[GUIA] Strings en goldsrc (STRING, ALLOC_STRING, MAKE_STRING)
#1
En este Tutorial les explicare como son manipulados los Strings en goldsrc, a partir de las premisas que describiré.

Antes de empezar, se indica que para entender ciertos conceptos hay que familiarizarse primero con el proceso de compilacion de códigos en alto nivel a bajo nivel (C/C++ -> machine code) y materia de punteros en C/C++.

Todos aquellos quienes han visto codigos en SDKs han notado esta particular forma de manipular strings, pero suelen confundir porque uno no capta cuando utilizar MAKE_STRING y ALLOC_STRING por ejemplo. Todo esto sirve al momento de programar módulos también.

Estan definidos de la siguiente forma, no es necesario que lo entiendan a la primera.

Código PHP:
#define STRING(offset) reinterpret_cast<const char *>(gpGlobals->pStringBase + (uintp)offset)
#define MAKE_STRING(str) (reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(STRING(0)))
#define ALLOC_STRING (*g_engfuncs.pfnAllocString) 

Les daré una breve introduccion de cada una de estas 3 funciones.

MAKE_STRING lo que hace principalmente es obtener la direccion de memoria de un String que esta dentro del programa.

Código PHP:
    if (m_pActiveItem && !pev->viewmodel)
    {
        switch (
m_pActiveItem->m_iId)
        {
            case 
WEAPON_AWPpev->viewmodel MAKE_STRING("models/v_awp.mdl"); break;
            case 
WEAPON_G3SG1pev->viewmodel MAKE_STRING("models/v_g3sg1.mdl"); break;
            case 
WEAPON_SCOUTpev->viewmodel MAKE_STRING("models/v_scout.mdl"); break;
            case 
WEAPON_SG550pev->viewmodel MAKE_STRING("models/v_sg550.mdl"); break;
        }
    } 

Código PHP:
    pev->classname MAKE_STRING("grenade"); 

Código PHP:
CBaseEntity *CBaseEntity::Create(char *szName, const Vector &vecOrigin, const Vector &vecAnglesedict_t *pentOwner)
{
    
edict_t *pent CREATE_NAMED_ENTITY(MAKE_STRING(szName));
    
// ...


Código PHP:
    switch (m_iType)
    {
        case 
44pEntity CBaseEntity::Create("item_battery"pev->originpev->angles); break;
        case 
42pEntity CBaseEntity::Create("item_antidote"pev->originpev->angles); break;
        case 
43pEntity CBaseEntity::Create("item_security"pev->originpev->angles); break;
        case 
45pEntity CBaseEntity::Create("item_suit"pev->originpev->angles); break;
    } 

Código PHP:
    CBaseEntity *pExplosion CBaseEntity::Create("env_explosion"centeranglespOwner); 

La particularidad de estos Strings, es que estan puestos en bruto.

¿Como funciona esto? Cuando yo compilo un codigo cualquiera, con un String escrito directamente en el código mencionado, este al momento de ser compilado y transformado a un lenguaje de bajo nivel para ser ejecutado por un sistema operativo, se convierte en un trozo de memoria estatico dentro del codigo que siempre esta en la misma posición de memoria, ya que así quedo al momento de ser compilado. Usted puede abrir un HEX Editor en un programa compilado (un dll, un exe, un so) y podrá percatarse que hay textos muy bonitos puestos a lo largo del programa.

MAKE_STRING hace uso de esa característica, haciendo un casteo inteligente y transformando este String en una dirección de memoria, para que al momento de transportar este dato sea a partir de un puntero, en vez de un tamaño no-definido de bytes que harian mas tedioso el trabajo. Es por eso que, existe el pev_viewmodel2 y pev_weaponmodel2 ya que estos son datos enteros, se mueven con valores discretos, no guardan cadenas de carácteres.

¿Y como se hace para saber que tan "largo" es un string si yo siempre manejo simplemente su dirección en memoria? Siempre hay que acordarse que un String, aquí y en la quebrada del ají son "cadenas de caracteres" que siempre SIEMPRE va a terminar con un 0 al final (0, '\0' o EOS), donde se ve involucrado la funcion strlen de C/C++; su principal función es iterar caracter-por-caracter hasta encontrar un 0

Código PHP:
size_t strlen(const char *s) {
    
size_t i;
    for (
0s[i] != '\0'i++) ; // magia
    
return i;


STRING hace lo inverso a MAKE_STRING. A partir de una direccion, este ubica el String en memoria convirtiendolo a char*. Es la manera de desempaquetar el String obtenido a partir de 1 sola direccion de memoria. De esta forma, se transportan strings con facilidad entre el Engine y el Gamedll.

Tienen infinidades de ejemplos de STRING en el SDK donde estan asociados directamente a "pevs" que guardan Strings, que ahora deberían entender con lo que les expliqué sobre MAKE_STRING.

Ahora, deglosemos el código.

Código PHP:
#define STRING(offset) reinterpret_cast<const char *>(gpGlobals->pStringBase + (uintp)offset)
#define MAKE_STRING(str) (reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(STRING(0))) 

reinterpret_cast<uintp>(str) lo que hace es obtener la direccion del String puesto. El casteo que se realiza es de char* a unsigned int* siendo el tipo de dato apropiado para guardar una direccion de memoria.

Es bueno tocar este tema. Para entrar más en el asunto, un offset lo pueden relacionar con un delta, una distancia, entre 2 puntos específicos. Es por eso que cuando ustedes buscan un "offset" para (por ejemplo) editar el team (m_iTeam = 114) utilizando set_pdata_int, en lo muy profundo, obtienen la direccion de la clase (CBasePlayer) y le suman 114 (x 4), lo castean a int, y voilá, tienen el team. Si, no es tan simple como eso, podría ser otro tutorial inclusive.

Lo que ocurre con MAKE_STRING y STRING es algo parecido. En MAKE_STRING se ve que hay una diferencia entre 2 valores

reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(STRING(0))
reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(reinterpret_cast<const char *>(gpGlobals->pStringBase + (uintp)0))
reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(reinterpret_cast<const char *>(gpGlobals->pStringBase))

uintp - uintp = resta de punteros

¿Porque se calculan los Strings utilizando como punto de partida gpGlobals->pStringBase?

gpGlobals->pStringBase esta definido en el Engine. Lo que guarda esto, es mas ni menos que una tabla de strings que el engine guarda. O de una manera mas bonita, Allocated Strings Table.

ALLOC_STRING es la forma de guardar Strings que no estan en memoria estática, mas bien estan guardados en el Stack.

Si yo hago por ejemplo lo siguiente:

Código PHP:
uintp generateString(void)
{
    
char str[64];
    
sprintf(str"Hola, este es mi string de %u bytes\n", (sizeof str)/(sizeof char)); // igual utiliza menos espacio xdxd
    
return MAKE_STRING(str);


Sería invalido. ¿Porque? Bueno, la memoria de lo que guarda str desaparecerá al momento que la funcion termine su ejecucion, y la direccion de memoria que cederá MAKE_STRING será invalida, apuntará a cualquier otra cosa en medio de la ejecucion del programa y la creación de más memoria en el stack. (stack es la memoria de variables creadas en medio de la ejecucion)

Es por eso que fue creado ALLOC_STRING, lo que hace esta funcion es guardar el string en una tabla en el engine (a la cual se puede acceder mediante el pointer gpGlobals->pStringBase) donde uno puede guardar Strings que son creados en medio de la ejecucion del programa, cosa que difiere del uso de MAKE_STRING el cual es orientado a Strings que estan dentro del código del programa.

Un ejemplo:

Código PHP:
void CC4::KeyValue(KeyValueData *pkvd)
{
    if (
FStrEq(pkvd->szKeyName"detonatedelay"))
    {
        
pev->speed atof(pkvd->szValue);
        
pkvd->fHandled TRUE;
    }
    else if (
FStrEq(pkvd->szKeyName"detonatetarget"))
    {
        
pev->noise1 ALLOC_STRING(pkvd->szValue); // *
        
pkvd->fHandled TRUE;
    }
    else if (
FStrEq(pkvd->szKeyName"defusetarget"))
    {
        
pev->target ALLOC_STRING(pkvd->szValue); // *
        
pkvd->fHandled TRUE;
    }
    else
        
CBaseEntity::KeyValue(pkvd);


pkvd es parte del Stack, ya que es memoria de los parámetros de las funciones. Al terminar la funcion, esa memoria desaparece, es por eso que aquí utilizamos ALLOC_STRING para hacer una copia de este String, guardarlo en la tabla, y quedarnos con su direccion.



Se llega a la conclusion que ALLOC_STRING y MAKE_STRING sacan una distancia desde el mismo punto, cosa que STRING convierta de puntero a string ya sea a Strings estaticos guardados por MAKE_STRING o Strings guardados en la tabla mencionada mediante ALLOC_STRING.

Esta tabla da una abstraccion a lo mencionado. Las direcciones de memorias utilizadas obviamente son falsas.

Cita:0x0000 [START ENGINE BLOCK]
0x1234 [MEMDIR gpGlobals->pStringBase]
0x1250 [MEMDIR "trivago"]
0x1260 [MEMDIR "pajarussel"]
0xAAAA [END ENGINE BLOCK]

0xAAAB [START GAMEDLL BLOCK]
0xBBB0 [MEMDIR "env_explosion"]
0xBBC0 [MEMDIR "item_battery"]
0xBBB0 [MEMDIR "grenade"]
0xBBB0 [MEMDIR "models/v_sg550.mdl"]
0xFFFF [END GAMEDLL BLOCK]

Viendo esto y tomando en cuenta el ejemplo sin terminar anteriormente

Código PHP:
uintp ptr MAKE_STRING("models/v_sg550.mdl"

Código PHP:
#define STRING(offset) reinterpret_cast<const char *>(gpGlobals->pStringBase + (uintp)offset)
#define MAKE_STRING(str) (reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(STRING(0))) 

ptr vendría a ser:

(reinterpret_cast<uintp>(str) - reinterpret_cast<uintp>(reinterpret_cast<const char *>(gpGlobals->pStringBase)))
(0xBBB0 - 0x1234)
0xA97C

Este OFFSET (que es un valor numero que solo menciona una distancia) si lo paso por STRING:

reinterpret_cast<const char *>(gpGlobals->pStringBase + (uintp)offset)
reinterpret_cast<const char *>(gpGlobals->pStringBase + 0xA97C)
reinterpret_cast<const char *>(0x1234 + 0xA97C)
reinterpret_cast<const char *>(0xBBB0)
"models/v_sg550.mdl"

(Aunque realmente es 'm' ya que apunta a la primera casilla en memoria de la direccion específica)

La misma aritmetica se aplica con los Strings falsos que inventé ("trivago" y "pajarussel").

Estoy atento a sus comentarios, y a los superhumanos que me corregirán la ortografía.
Responder
#2
Entendi gran parte. Al final se va poniendo cada vez mas jodido (Para mi) y medio que me costo. Un poco de google y creo que lo voy a entender del todo.

EDIT: Esta muy bueno.
Responder
#3
Bonita guía.
No conocía la función de STRING. Si no entendí mal, se puede acceder a STRING desde AMXX con EngFunc_SzFromIndex, es correcto?
(16/05/2020, 06:31 PM)Ipolito escribió: Ahre que solo estafe a 1

(12/05/2020, 06:37 PM)Neeeeeeeeeel.- escribió: El puto compilador de AM está más bugueado que la concha de la lora

(09/05/2020, 02:29 PM)Nube. escribió: que concha le hicieron al foro?

(13/05/2019, 08:27 PM)Niper.-. escribió: Yo siempre quise ser un hacker hackero profesional del 1.6 conter strike 1.6 no steam y counter strike 1.6 steam version 1.2.65. El problema es que no sabía como hackear entonces aprendi a hackear mediante hackeos como hackee mi primera vez hackeando un sistema de hackers para yo despues hackear a ese hacker que me queria hackear pero yo lo hackee primero por ende el hacker quedo re hackeado por mi que seria un hacker. Entonces así fui como me hice hacker. Gracias por leer.

(27/04/2020, 02:13 PM)Neeeeeeeeeel.- escribió: El foro será actualizado tan pronto encuentren la cura para el coronavirus

(07/04/2020, 11:22 PM)Hypnotize escribió: aparte kikizon es marica

(26/03/2020, 04:23 AM)Chema escribió: +REP de que pendejo? ni tienes 100 posts.

(20/11/2018, 09:32 AM)Neeeeeeeeeel.- escribió: yo porque soy muy enfermo del styling

(13/02/2017, 07:41 PM)Heber[$]ource escribió: es nuvpawn, nos esta ddoseando con su sega genesis

(10/05/2019, 08:23 PM)Niper.-. escribió: Trap no son tus putas trap es mi equipo tumbando el foro de amxmodx hey tumbando el foro hey tumbando el foroooo ya saben todos nos llevamos todos los pluginsss..

(18/02/2019, 03:08 PM)Neeeeeeeeeel.- escribió: Shit

(28/02/2015, 03:27 AM)sasske escribió: Vete al diablo detallista de mierda :ohgodwhy;

(26/12/2018, 02:45 PM)[R]ak escribió: yo solo te veo en whatsapp agarrando culos.. nose..

(09/06/2017, 12:59 AM)kikizon2 escribió: Quiero que leas las reglas, y si fuese posible que saltaras de un 10mo piso, pls.

(21/04/2017, 01:27 AM)Chamo. escribió: Cuanto hay que pagar para estar en tu firma TrolleyesThats what she said?

(20/04/2017, 11:56 PM)matrix123 escribió: Patrocinas rehlds? Es de lo único que hablas, mañana seguro te llegan remeras de rehlds, mandame una Whatever

(26/04/2017, 08:47 PM)Dieguito escribió: al que me salga con latinservers le meto un sugisaki por el c***

(26/02/2017, 08:02 PM)meTaLiCroSS escribió: La misma aritmetica se aplica con los Strings falsos que inventé ("trivago" y "pajarussel").

(06/01/2015, 10:47 PM)meTaLiCroSS escribió:
(06/01/2015, 10:40 PM)wiD escribió: este chileno culiau :whatever;
¿Que?

(29/04/2016, 11:44 PM)wiD escribió: Te juro que si te tengo enfrente primero te piso con el auto y despues te meo en la boca.

(16/07/2014, 06:38 PM)wiD escribió: Mongito dame su name que quiero un chip para pegar todo cocow

(18/03/2015, 02:04 PM)ErikMav94 escribió:
(18/03/2015, 02:03 PM)mongito100 escribió: Son todos putos
escribís como el orto :trolleyes;

[Anti] escribió:Olle tranquilo cerebrito

(03/04/2015, 12:16 AM)Treki escribió:
(03/04/2015, 12:11 AM)Sugisaki escribió: cual plug?, el arkshine o el de arkshine?

Fixed*

(09/03/2015, 09:49 PM)Moroha escribió: Suspendieron al abaricioso platero de kikizon :trolleyes;

(26/05/2014, 08:31 PM)ivan escribió: hola quiero hacer combo para el party uso el de rank

(22/08/2014, 11:41 PM)Store.gh escribió: Join team de jeropito? A vos t hace falta un pito..

(26/02/2015, 01:01 AM)meTaLiCroSS escribió:
(26/02/2015, 12:54 AM)sasske escribió: Salida del horno nueva versión, que obsesionado estoy :3
PD: Me olvide de darle los créditos a metal xD, pobre metal xd
Da igual, dejalo como una enseñanza jejej no te llorare por unos creditos ehhmm hud...

(18/01/2015, 12:31 AM)[R]ak escribió: no me critican.. solo dicen boludeces

(05/01/2015, 12:43 AM)roccoxx escribió: Sentado en dust pensando como fui tan gil ♪
[Imagen: attachment.php?thumbnail=1750]
Responder
#4
(26/02/2017, 10:15 PM)Mario AR. escribió: Bonita guía.
No conocía la función de STRING. Si no entendí mal, se puede acceder a STRING desde AMXX con EngFunc_SzFromIndex, es correcto?

SzFromIndex es el equivalente de STRING efectivamente
Responder
#5
Nice, excelente guía

(26/02/2017, 08:02 PM)meTaLiCroSS escribió: La misma aritmetica se aplica con los Strings falsos que inventé ("trivago" y "pajarussel").

RoflmaoRoflmao
[Imagen: 76561198095047868.png]
Responder
#6
Quizás comente algo tarde, pero no tenia acceso a internet.

Muy buena guía Metal, y si, efectivamente algunos del conjunto que lee el/los SKDs quizás no entiende esto (claramente yo), así que nuevamente te doy las gracias por tomarte la molestia de publicar esto, ahora si podre entender mejor esto.

Nothingdohere
Responder
#7
(03/03/2017, 12:52 AM)Chamo. escribió: Quizás comente algo tarde, pero no tenia acceso a internet.

Muy buena guía Metal, y si, efectivamente algunos del conjunto que lee el/los SKDs quizás no entiende esto (claramente yo), así que nuevamente te doy las gracias por tomarte la molestia de publicar esto, ahora si podre entender mejor esto.

Nothingdohere

Espero te haya servido, gracias a ti por leer
Responder
#8
Que buenos estos tipos de aportes metalicross, excelente como los demás.
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)