Avevo risposto sta cosa in un thread, ma poi ho scritto cosi tanto che mi sono detto "perchè non farne una guida" e cosi ecco che riposto qui quello che ho scritto...
Il tutto nasce da sto codice postato:
C++ Programming
Da haste Speed
DWORD ADR_OFFSET_PLAYERSPEED = 0x896338; //UD 26.09
switch(cPlayer.CH_PlayerSpeed)
{
case 0: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96);break;
case 1: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*2);break;
case 2: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*3);break;
case 3: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*4);break;
case 4: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*5);break;
case 5: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*6);break;
case 6: WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96*7);break;
}
Ihr macht das soo unnötig kompliziert, das lässt sich in einer einzigen Zeile schreiben:
*reinterpret_cast<double *>(ADR_OFFSET_PLAYERSPEED) = (96.0 * (cPlayer.CH_PlayerSpeed - 1));
Per non farci distrarre dal numero di righe vediamone una sola, la prima, le altre fanno la stessa cosa:
C++ Programming
WriteMEM<double>((void*)ADR_OFFSET_PLAYERSPEED,96);
e per semplificarti la vita vediamola cosi:
C++ Programming
WriteMEM((double*)ADR_OFFSET_PLAYERSPEED,96);
questa altro non è che questa:
C++ Programming
(double*)ADR_OFFSET_PLAYERSPEED=96;
E quindi mi direte, ma WriteMEM cosa fa?
semplice, può succedere che alcune aree di memoria di warrock vengano "protette" settandole in lettura, che succede, che se qualcuno (tu) tenti di scriverci sopra, o non funziona o addirittura crasha il gioco per via dell'eccezione che viene generata.
Allora fai una funzione come WriteMEM che fa una cosa del tipo:
C++ Programming
void WriteMem(void *addy, const int value)
{
DWORD dwback;
VirtualProtect(addy, 4, PAGE_READWRITE, &dwback); // setta la memoria in lettura/scrittura
*(reinterpret_cast<type *const>(addy)) = value; // sovrascrive la memoria
VirtualProtect(addy, 4, dwback, &dwback); // risetta la memoria cosi com'era prima
}
che fa? semplice, leggete i commenti, prima cambia lo stato della memoria che ci interssa in Lettura/scrittura, a questo punto possiamo cambiare il valore della memoria (es lo speed), quindi rimettiamo il settaggio cosi com'era prima (ad es. in sola lettura)
Perchè lo rimettiamo com'era? perchè può essere che warrock o hs controlli che la memoria sia rimasta protetta cosi come se l'aspetta altrimenti può sospettare la presenza di una hack
Detto questo sorge un problema, notiamo sta riga:
C++ Programming
void WriteMem(void *addy, const int value)
notate che c'è scritto
int value
ovvero la funzione si aspetta un int... ma se vogliamo passargli un float? o un double? o un byte?
allora qui vengono in aiuto i
templates del C++ che ci permettono di scrivere una funzione GENERICA che può accettare piu di un tipo di parametro di passaggio e comportarsi di conseguenza,
la nostra funzione potrebbe diventare:
C++ Programming
template <typename type>
void WriteProtectedMem(void *addy, const type value)
{
DWORD dwback;
VirtualProtect(addy, 4, PAGE_READWRITE, &dwback);
*(reinterpret_cast<type *const>(addy)) = value;
VirtualProtect(addy, 4, dwback, &dwback);
}
che è successo? vediamo la riga di prima come è diventata:
C++ Programming
void WriteProtectedMem(void *addy, const type value)
ora c'è quel
type value
e sotto nel codice abbiamo questo:
C++ Programming
*(reinterpret_cast<type *const>(addy)) = value;
che vuol dire tutto cio? semplice, vuol dire che la nostra funzione ora si arrangia da sola, se gli passiamo un valore byte la riga è come se diventasse cosi:
C++ Programming
*(reinterpret_cast<BYTE *const>(addy)) = value;
se gli passiamo un float cosi:
C++ Programming
*(reinterpret_cast<float *const>(addy)) = value;
ovvero dove c'era scritto "type" dobbiamo immaginare di metterci il tipo che passiamo al momento in cui andiamo a chiamare la funzione (in realtà in compilazione, ma sono dettagli
)
Ovvero riassumendo, invece di fare tante funzioni quanti sono i tipi che ci interessano, ad esempio:
WriteMEM_byte(void *addy,const byte value)
...
WriteMEM_int(void *addy,const int value)
...
WriteMEM_float(void *addy,const float value)
...
ne abbiamo una sola che fa tutto
il fatto che sia scritta cosi :
WriteMEM<double>(
serve solo a specificare in modo esplicito il tipo che vogliamo usare per la chiamata, anche se in realtà potremmo tralasciarlo perchè il compilatore è cosi intelligente da capirlo da solo.
Oppure possiamo usarlo per forzare il tipo da usare, a nostro rischio e pericolo.
tutto chiaro?
un ultima cosa, se poi ci si domanda il senso di questa:
C++ Programming
*reinterpret_cast<double *>(ADR_OFFSET_PLAYERSPEED) = (96.0 * (cPlayer.CH_PlayerSpeed - 1));
ha voluto solo semplicemente (e giustamente) presentare un modo alternativo, migliore, piu pulito e "ristretto" di fare la stessa cosa in una sola riga.
Ovvero, sopra ha usato uno switch per dire che a seconda di quanto vale la variabile cPlayer.CH_PlayerSpeed (selezionabile da menu) calcola quanto deve scrivere in memoria per ottenere la velocità
Nella riga sotto alternativa, lo switch non lo usa piu e scrive in memoria semplicemente il valore calcolato al volo ottenuto moltiplicando 96 per la varibile di prima (cPlayer.CH_PlayerSpeed)
questo:
C++ Programming
*reinterpret_cast<double *>(ADR_OFFSET_PLAYERSPEED)= ...
altro non è che la versione C++ di questo che conosciamo già :
C++ Programming
*(double)(ADR_OFFSET_PLAYERSPEED)= ...
alla prossima guida
digger