SearchForCheats.de.vu » Tutorials » Advanced User Tutorial #2 - Additions To The Shiny Hack


Tutorial avanzato #2 - Aggiunte alla Shiny Hack

This tutorial was translated by the User HackMew. The original version can be found in the tutorial-section.
If you want to provide your support to this website by translating this tutorial to another language, please contact me. (Details can be found in the imprint)
Thanks!

Ben ritrovati, hacker! Con il rilascio della shiny hack, creata originariamente per la stupenda hack Shiny Gold opera di zel, il mio laboratorio ha ricevuto più attenzione. Ho deciso quindi di continuare con le lezioni per utenti esperti. Comunque, tornando all´argomento...

Sebbene la shiny hack non fosse affatto male, presentava due difetti principali. (Ringrazio le persone che mi hanno informato del secondo, era una piccola svista. Non avevo tenuto conto di una possibilità):

  1. - lo shiny generato aveva sempre la stessa natura e lo stesso sesso
  2. - se perdevi contro il Gyarados shiny (o qualsiasi altro shiny), lo shiny flag non veniva azzerato e ogni incontro successivo sarebbe stato shiny

Oggi sistemeremo le cose, lavorando ancora una volta con la versione americana di Pokémon Rosso Fuoco. Con un po´ di fortuna non
avrai dimenticato nulla nel frattempo. ;-)

Primo problema: la casualità

Ricordiamo l´intera condizione shiny:

((PID XOR OTrainerIDs) >> 0x10) XOR ((PID XOR OTrainerIDs) AND 0xFFFF) < 8

Se questa linea non ti dice nulla, leggiti il Tutorial avanzato #1.
Ti ricordi del fatto che (PID XOR OTrainerIDs) >> 0x10) e (PID XOR OTrainerIDs) AND 0xFFFF) non possono differire molto dal numero otto? Seguiamo questa riflessione; se introduciamo la casualità questo qui sotto sarebbe il risultato:

((Rand XOR OTrainerIDs) >> 0x10) XOR ((Rand XOR OTrainerIDs) AND 0xFFFF) < 8

Ora la parte complicata: in quali casi otteniamo uno shiny?

((OTrainerIDs XOR OTrainerIDs) >> 0x10) XOR ((OTrainerIDs XOR OTrainerIDs) AND 0xFFFF) < 8
(0x0 >> 0x10) XOR (0x0 AND 0xFFFF) < 0x8
0 XOR 0 < 0x8
0 < 8

La nostra vecchia soluzione. Restituisce 0. Ora, se i 2 byte più significativi e quelli meno significativi non differiscono molto da otto, abbiamo uno shiny. Così possiamo dire:

(((OTrainerIDs XOR (Rand Or (Rand << 10))) XOR OTrainerIDs) >> 0x10) XOR ((OTrainerIDs XOR (Rand Or (Rand << 10))) AND 0xFFFF) < 8
(((0x65A9DC70 XOR (Rand Or (Rand << 10))) XOR 0x65A9DC70) >> 0x10) XOR (((0x65A9DC70 XOR (Rand Or (Rand << 10))) XOR 0x65A9DC70) AND 0xFFFF) < 8
(((0x65A9DC70 XOR (0xD523 Or (0xD523 << 10))) XOR 0x65A9DC70) >> 0x10) XOR (((0x65A9DC70 XOR (0xD523 Or (0xD523 << 10))) XOR 0x65A9DC70) AND 0xFFFF) < 8
(((0x65A9DC70 XOR 0xD523D523) XOR 0x65A9DC70) >> 0x10) XOR (((0x65A9DC70 XOR 0xD523D523) XOR 0x65A9DC70) AND 0xFFFF) < 8
(0xD523D523 >> 0x10) XOR (0xD523D523 AND 0xFFFF) < 8
0xD523 XOR 0xD523 < 8
0 < 8

Ricorda che ho sostituito il PID con (OTrainerIDs XOR (Rand Or (Rand << 10))). Rand# un denominatore a 16bit non segnati. (Io ho scelto 0xD523 come valore). Perciò, impostando la Xkey come Rand# OR (Rand# << 10) come nell´esempio sopra riportato, ciò che viene restituito è 0. Il successivo XOR OTrainerIDs è stato introdotto per sbarazzarsi del primo OTrainerIDs.
Questa è la teoria. E adesso: come può esistere un numero casuale all´interno del gioco? La risposta è semplice - non ce ne sono. O meglio, non esistono numeri casuali ´veri´. Per capire meglio il concetto, daremo un´occhiata più da vicino al RNG (Random Numbers Generator, Generatore di Numeri Casuali). È sempre posizionato all´indirizzo 0x03005000. Apri quindi il VBA ed il visualizzatore di memoria (Memory viewer) e ti accorgerai di 4 byte che cambiano rapidamente in quella posizione.
Perché quindi non si tratta di ´veri´ numeri casuali? Andiamo a vedere la routine che ne è responsabile.

Apri VBA-SDL-H, carica il gioco e premi F11 per fermarne l´esecuzione. Posiziona un bpw su 03005000 per 4 byte. Digita c per continuare. Il gioco dovrebbe fermarsi immediatamente. Nel mio caso:

Breakpoint (on write) address 03005000 old:77a8a7b8 new:c639d9cb
R00=c639d9cb R04=04000006 R08=00000000 R12=00000273
R01=00006073 R05=030030f0 R09=00000000 R13=03007e14
R02=03005000 R06=030030e4 R10=00000000 R14=0800078d
R03=00000000 R07=030030f0 R11=00000000 R15=08044ed8
CPSR=8000003f (N.....T Mode: 1f)
08044ed6  0c00 lsr r0, r0, #0x10

Hai notato la mia espressione a 4 byte? Tu dovresti avere un altro valore visto che ci stiamo occupando di numeri ´casuali´. :-P
Esploriamo l´intera routine.

08044ec8  4a04 ldr r2, [$08044edc] (=$03005000)  // carica indirizzo
08044eca  6811 ldr r1, [r2, #0x0] // carica *indirizzo (valore 0x03005000)
08044ecc  4804 ldr r0, [$08044ee0] (=$41c64e6d)  // carica l´RNG polinomiale
08044ece  4348 mul r0, r1         // nuovo valore = vecchio valore * 0x41c64e6d
08044ed0  4904 ldr r1, [$08044ee4] (=$00006073)  // carica un altro valore
08044ed2  1840 add r0, r0, r1     // sommalo con il nostro numero ´casuale´
08044ed4  6010 str r0, [r2, #0x0] // salva nuovamente il numero ´casuale´ (32 bit)
08044ed6  0c00 lsr r0, r0, #0x10  // restituisci solo i 16bit più significativi
08044ed8  4770 bx lr              // termina la funzione

Il nostro numero casuale dipende solamente da alcune moltiplicazioni fra la RAM e il tempo. Quindi non si tratta di un vero numero casuale, ma vi è piuttosto vicino. :-P
Di gran lunga più importante è la funzione stessa. Se chiami 0x08044ec8 da qualsiasi punto all´interno della ROM, otterrai un numero casuale depositato in r0. Con la conoscenza di ciò andremo a estendere la nostra routine ´shiny check´ che abbiamo estratto nel primo tutorial. La mia è localizzata all´offset 0x71b700 nello spazio libero della ROM.

0871b700  bc02 pop {r1}                         // ottieni il valore salvato
0871b702  b524 push {lr, r2, r5}                // metti nello stack l´indirizzo di ritorno, salva
0871b704  bc02 pop {r1}                         // ricaricalo su r1
0871b706  3108 add r1, #4                       // aggiusta l´indirizzo di ritorno di 8. (per saltare 
                                                   l´indirizzo-bersaglio)
0871b708  b402 push {r1}                        // salva di nuovo l´indirizzo di ritorno
0871b70a  4812 ldr r0, [$0871b754] (=$08044ec8)
0871b70c  f000 bl $0871b74c
0871b70e  f824 blh $0048                        // continua l´esecuzione da r0, salva l´indirizzo di 
                                                   ritorno in lr
0871b710  0405 lsl r5, r0, #0x10                // shifta a sinistra il rnd
0871b712  1945 add r5, r5, r0                   // raddoppia la semiparola ad una parola
0871b714  490e ldr r1, [$0871b750] (=$02022000) // carica la posizione del flag
0871b716  6809 ldr r1, [r1, #0x0]               // carica il valore @ 02022000 su r1
0871b718  2901 cmp r1, #0x1
0871b71a  d00b beq $0871b734                    // è uno shiny? allora salta al gestore shiny
0871b71c  7821 ldrb r1, [r4, #0x0]              // carica il @ r4 (=03007cf8) su r1
0871b71e  7860 ldrb r0, [r4, #0x1]              // carica il byte @ r4+1 (=03007cf9) su r0
0871b720  0200 lsl r0, r0, #0x08                // il byte in r0 è shiftato a sinis. di 1 byte (=8bit)
0871b722  1809 add r1, r1, r0                   // r1 e r0 sono sommati. otteniamo la semiparola r0+r1
0871b724  78a0 ldrb r0, [r4, #0x2]              // carica il byte @ r4+2 (=03007cfa) su r0
0871b726  0400 lsl r0, r0, #0x10                // r0 è shiftato nuovamente a sinistra di 2 byte
0871b728  1809 add r1, r1, r0                   // r0 è sommato a r1
0871b72a  78e0 ldrb r0, [r4, #0x3]              // carica il byte @ r4+3 (=03007cfb) su r0
0871b72c  0600 lsl r0, r0, #0x18                // r0 è shiftato a sinistra di 3 byte
0871b72e  1809 add r1, r1, r0                   // e sommato con r1
0871b730  6079 str r1, [r7, #0x4]               // infine l´OTrainerIDs è salvato su r7+4 
                                                   (=0202402c) 
0871b732  e00c b $0871b74e                      // salta le linee seguenti. sono usate per gli shiny
0871b734  7821 ldrb r1, [r4, #0x0]              // carica il byte @ r4 (=03007cf8) su r1
0871b736  7860 ldrb r0, [r4, #0x1]              // carica il byte @ r4+1 (=03007cf9) su r0
0871b738  0200 lsl r0, r0, #0x08                // il byte in r0 è shiftato a sinis. di 1 byte (=8bit)
0871b73a  1809 add r1, r1, r0                   // r1 e r0 sono sommati. otteniamo la semiparola r0+r1
0871b73c  78a0 ldrb r0, [r4, #0x2]              // carica il byte @ r4+2 (=03007cfa) su r0
0871b73e  0400 lsl r0, r0, #0x10                // r0 è shiftato nuovamente a sinistra di 2 byte
0871b740  1809 add r1, r1, r0                   // r0 è sommato a r1
0871b742  78e0 ldrb r0, [r4, #0x3]              // carica il byte @ r4+3 (=03007cfb) su r0
0871b744  0600 lsl r0, r0, #0x18                // r0 è shiftato a sinistra di 3 byte
0871b746  1809 add r1, r1, r0                   // e sommato con r1
0871b748  6079 str r1, [r7, #0x4]               // infine l´OTrainerIDs è salvato su 
                                                   r7+4 (=0202402c)
0871b74a  4069 eor r1, r5                       // il PID è impostato come OTRainerIDs XOR rnd
0871b74c  6039 str r1, [r7, #0x0]               // e qui c´è la nostra patch. il PID è sovrascritto 
                                                   dall´OTrainerIDs
0871b74e  bd24 pop {pc, r5, r2}                 // ritorna ed esci dalla sub, carica i valori salvati
0871b750  0020                                  // data1
0871b752  0202                                  // data2
0871b754  4ec8                                  // data3
0871b756  0804                                  // data4
0871b758  4787 bx r0                            // vai a r0

Ecco tutto. Abbiamo introdotto la casualità. D´ora in poi gli incontri degli shiny varieranno e non saranno più fissi.

Non inserire ancora la routine nella tua ROM dato che non abbiamo ancora finito. Abbiamo da risolvere ancora un problema: se perdi contro un Pokémon shiny, qualunque altro Pokémon risulta poi shiny.

Un´idea semplice: diciamo che la routine resetta lo shiny flag a 0, dopo essere stato letto. Quindi estenderemo nuovamente la routine.

Secondo problema: l´infinità di shiny

0871b700  bc02 pop {r1}                         // ottieni il valore salvato
0871b702  b52d push {lr, r0, r2, r3, r5}            // metti nello stack l´indirizzo di ritorno, salva
0871b704  9904 ldr r1, [sp, #0xc]               // ricaricalo su r1
0871b706  3108 add r1, #4                       // aggiusta l´indirizzo di ritorno di 8. (per saltare 
                                                   l´indirizzo-bersaglio)
0871b708  9104 sdr r1, [sp, #0xc]               // salva di nuovo l´indirizzo di ritorno
0871b70a  4813 ldr r0, [$0871b758] (=$08044ec9)
0871b70c  f000 bl $0871b75c
0871b70e  f826 blh $004c                        // continua l´esecuzione da r0, salva l´indirizzo di 
                                                   ritorno in lr
0871b710  0405 lsl r5, r0, #0x10                // shifta a sinistra il rnd
0871b712  1945 add r5, r5, r0                   // raddoppia la semiparola ad una parola
0871b714  4a0f ldr r2, [$0871b754] (=$02022000) // carica la posizione del flag
0871b716  6811 ldr r1, [r2, #0x0]               // carica il valore @ 02022000 su r1
0871b718  2300 mov r3, #0x00                    // imposta r3 a 0
0871b71a  6013 str r3, [r2, #0x0]               // azzera lo shiny flag
0871b71c  2901 cmp r1, #0x1
0871b71e  d00b beq $0871b738                    // è uno shiny? allora salta al gestore shiny
0871b720  7821 ldrb r1, [r4, #0x0]              // carica il @ r4 (=03007cf8) su r1
0871b722  7860 ldrb r0, [r4, #0x1]              // carica il byte @ r4+1 (=03007cf9) su r0
0871b724  0200 lsl r0, r0, #0x08                // il byte in r0 è shiftato a sinis. di 1 byte (=8bit)
0871b726  1809 add r1, r1, r0                   // r1 e r0 sono sommati. otteniamo la semiparola r0+r1
0871b728  78a0 ldrb r0, [r4, #0x2]              // carica il byte @ r4+2 (=03007cfa) su r0
0871b72a  0400 lsl r0, r0, #0x10                // r0 è shiftato nuovamente a sinistra di 2 byte
0871b72c  1809 add r1, r1, r0                   // r0 è sommato a r1
0871b72e  78e0 ldrb r0, [r4, #0x3]              // carica il byte @ r4+3 (=03007cfb) su r0
0871b730  0600 lsl r0, r0, #0x18                // r0 è shiftato a sinistra di 3 byte
0871b732  1809 add r1, r1, r0                   // e sommato con r1
0871b734  6079 str r1, [r7, #0x4]               // infine l´OTrainerIDs è salvato su r7+4 
                                                   (=0202402c) 
0871b736  e00c b $0871b752                      // salta le linee seguenti. sono usate per gli shiny
0871b738  7821 ldrb r1, [r4, #0x0]              // carica il byte @ r4 (=03007cf8) su r1
0871b73a  7860 ldrb r0, [r4, #0x1]              // carica il byte @ r4+1 (=03007cf9) su r0
0871b73c  0200 lsl r0, r0, #0x08                // il byte in r0 è shiftato a sinis. di 1 byte (=8bit)
0871b73e  1809 add r1, r1, r0                   // r1 e r0 sono sommati. otteniamo la semiparola r0+r1
0871b740  78a0 ldrb r0, [r4, #0x2]              // carica il byte @ r4+2 (=03007cfa) su r0
0871b742  0400 lsl r0, r0, #0x10                // r0 è shiftato nuovamente a sinistra di 2 byte
0871b744  1809 add r1, r1, r0                   // r0 è sommato a r1
0871b746  78e0 ldrb r0, [r4, #0x3]              // carica il byte @ r4+3 (=03007cfb) su r0
0871b748  0600 lsl r0, r0, #0x18                // r0 è shiftato a sinistra di 3 byte
0871b74a  1809 add r1, r1, r0                   // e sommato con r1
0871b74c  6079 str r1, [r7, #0x4]               // infine l´OTrainerIDs è salvato su 
                                                   r7+4 (=0202402c)
0871b74e  4069 eor r1, r5                       // il PID è impostato come OTRainerIDs XOR rnd
0871b750  6039 str r1, [r7, #0x0]               // e qui c´è la nostra patch. il PID è sovrascritto 
                                                   dall´OTrainerIDs
0871b752  bd2d pop {pc, r0, r5, r3, r2}         // ritorna ed esci dalla sub, carica i valori salvati
0871b754  2000                                  // data1
0871b756  0202                                  // data2
0871b758  4ec9                                  // data3
0871b75a  0804                                  // data4
0871b75c  4700 bx r0                            // vai a r0

Fantastico. Questo risolve anche il secondo problema. Abbiamo finito, spero che apprezzerai la ´nuova´ shiny-hack!

"Pokémon" ist ein eingetragenes Warenzeichen der Firma Nintendo
"Action Replay" ist ein eingetragenes Warenzeichen von Datel Interact.
© www.SearchForCheats.de.vu by Mastermind_X
© 2006 - 2008