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


Advanced User Tutorial #2 - Additions To The Shiny Hack

Welcome back, hackers! With the release of the shiny hack, originally made for zelīs awesome Shiny Gold Hack, my laboratory got some more attention. Therefore I decided to continue the Advanced Userīs Lessons. But anyway, back to topic...

While the shiny hack was very nice, it had two major bugs. (Thanks to people referring to the second one, it was a little mistake. Iīve had overseen a possibility)

  1. The generated Shiny had always the same nature and gender
  2. If you loose against shiny Gyarados (or any other shiny) the shiny flag doesnīt get cleared and every pokémon youīre encountering in the future will be a shiny

Today, weīll fix that. Working again on Fire Red US. Hopefully you didnīt forget anything in the meantime. ;-)

First issue: The Random

Letīs remember the whole shiny condition:

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

If this line doesnīt make any sense to you, read Advanced User Lesson #1.
Do you remember me saying something about (PID XOR OTrainerIDs) >> 0x10) and (PID XOR OTrainerIDs) AND 0xFFFF) may not differ more than eight? Letīs follow this thought; if we introduce our Random it would be the following:

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

Now the complicated step: For which cases of Rand do we get a 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

The first case and our old solution. It returns 0. And now the following:
If the upper 2 Bytes and the lower 2 Bytes donīt differ more than eight, we have a shiny. So we can say:

(((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

Remember I replaced PID with (OTrainerIDs XOR (Rand Or (Rand << 10))). Rand# is an unsigned 16bit denom. (I chose 0xD532 for it) So setting the The Xkey to Rand# OR (Rand# << 10) like in the example above returns also 0. The following XOR OTrainerIDs is introduced to get rid of the first OTrainerIDs.
Thatīs the theory. The next thing is: How can there exist any īrandomī within the game? The answer is short and easy - there is no random. Well no īrealī random. To understand that, weīll have a closer look to the RNG (random numbers generator). It is always located at 0x03005000. So open the VBA and itīs memviewer and ratify youīre seeing 4 bytes changing rapidly.
Why isnīt that a īrealī random? Letīs have a look to the routine responsible for that.

Open VBA-SDL-H, load your game and press F11 to stop the execution. Stick a bpw to 03005000 for 4 bytes. Type c to continue. The game should break immediately. For me, itīs:

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

Did you notice my 4 byte expression? You should have another value because weīre dealing with īrandomī numbers. :-P Letīs explore the whole routine.

08044ec8  4a04 ldr r2, [$08044edc] (=$03005000)  // Load adress
08044eca  6811 ldr r1, [r2, #0x0] // Load *adress (value at 0x03005000)
08044ecc  4804 ldr r0, [$08044ee0] (=$41c64e6d)  // Load RNG polynomial
08044ece  4348 mul r0, r1         // New value = Old Value * 0x41c64e6d
08044ed0  4904 ldr r1, [$08044ee4] (=$00006073)  // Load another value
08044ed2  1840 add r0, r0, r1     // Add it to our īrandomī value
08044ed4  6010 str r0, [r2, #0x0] // Stores back īrandomī (32 bits)
08044ed6  0c00 lsr r0, r0, #0x10  // Return only the upper 16bits
08044ed8  4770 bx lr              // Quit function

Our random just depends on some multiplications within the RAM and the time. Therefore, it isnīt a real random, but itīs kinda near to it. :-P
Much more important is the function itself. If you call 0x08044ec8 from anywhere in the ROM, youīll get returned a random number in r0. With this knowledge weīre willing to extend our īshiny checkī routine we extracted in Advanced User Lesson #1. I located mine at 0x71b700 in the free space of the ROM.

0871b700  bc02 pop {r1}                         // get the saved value
0871b702  b524 push {lr, r2, r5}                // put the return address onto the stack, save values
0871b704  bc02 pop {r1}                         // reload it to r1
0871b706  3108 add r1, #8                       // adjust the return adress by 8. (for skipping 
                                                   the target-adressī data)
0871b708  b402 push {r1}                        // save return address again
0871b70a  4812 ldr r0, [$0871b754] (=$08044ec9)
0871b70c  f000 bl $0871b758
0871b70e  f824 blh $0048                        // continue execution from r0, save return adress 
                                                   in lr
0871b710  0405 lsl r5, r0, #0x10                // shift left the rnd
0871b712  1945 add r5, r5, r0                   // double the halfword to a word
0871b714  490e ldr r1, [$0871b750] (=$02022000) // load flag position
0871b716  6809 ldr r1, [r1, #0x0]               // load the value @ 02022000 to r1
0871b718  2901 cmp r1, #0x1
0871b71a  d00b beq $0871b734                    // is it a shiny? so jump to the shiny handler
0871b71c  7821 ldrb r1, [r4, #0x0]              // load the byte @ r4 (=03007cf8) to r1
0871b71e  7860 ldrb r0, [r4, #0x1]              // load the byte @ r4+1 (=03007cf9) to r0
0871b720  0200 lsl r0, r0, #0x08                // the byte in r0 is shifted left by 1 byte (=8bit)
0871b722  1809 add r1, r1, r0                   // r1 and r0 are added. we get a halfword r0+r1
0871b724  78a0 ldrb r0, [r4, #0x2]              // load the byte @ r4+2 (=03007cfa) to r0
0871b726  0410 lsl r0, r0, #0x10                // r0 is shifted left by 2 bytes again
0871b728  1809 add r1, r1, r0                   // r0 is added to r1
0871b72a  78e0 ldrb r0, [r4, #0x3]              // load the byte @ r4+3 (=03007cfb) to r0
0871b72c  0600 lsl r0, r0, #0x18                // r0 is left-shifted by 3 bytes
0871b72e  1809 add r1, r1, r0                   // and added to r1
0871b730  6079 str r1, [r7, #0x4]               // and finally the OTrainerIDs are stored at r7+4 
                                                   (=0202402c) 
0871b732  e00c b $0871b74e                      // skip the following lines. theyīre used for shinys
0871b734  7821 ldrb r1, [r4, #0x0]              // load the byte @ r4 (=03007cf8) to r1
0871b736  7860 ldrb r0, [r4, #0x1]              // load the byte @ r4+1 (=03007cf9) to r0
0871b738  0200 lsl r0, r0, #0x08                // the byte in r0 is shifted left by 1 byte (=8bit)
0871b73a  1809 add r1, r1, r0                   // r1 and r0 are added. we get a halfword r0+r1
0871b73c  78a0 ldrb r0, [r4, #0x2]              // load the byte @ r4+2 (=03007cfa) to r0
0871b73e  0410 lsl r0, r0, #0x10                // r0 is left-shifted by 2 bytes again
0871b740  1809 add r1, r1, r0                   // r0 is added to r1
0871b742  78e0 ldrb r0, [r4, #0x3]              // load the byte @ r4+3 (=03007cfb) to r0
0871b744  0600 lsl r0, r0, #0x18                // r0 is left-shifted by 3 bytes
0871b746  1809 add r1, r1, r0                   // and added to r1
0871b748  6079 str r1, [r7, #0x4]               // and finally the OTrainerIDs are stored at 
                                                   r7+4 (=0202402c)
0871b74a  4069 eor r1, r5                       // PID is set to OTRainerIDs XOR rnd
0871b74c  6039 str r1, [r7, #0x0]               // and hereīs our patch. the PID is overwritten 
                                                   by the OTrainerIDs
0871b74e  bd24 pop {pc, r5, r2}                 // return & exit sub, load saved values
0871b750  0020                                  // data1
0871b752  0202                                  // data2
0871b754  4ec9                                  // data3
0871b756  0804                                  // data4
0871b758  4700 bx r0                            // goto r0

And thatīs it. We introduced random. The encountering shinys will variate.

Donīt copy the routine into your ROM, since we arenīt finished yet. Weīll have to solve the other problem: If you loose against a shiny, all encountering pokemon will be shiny.

Therefore, I have a simple idea. Letīs say, the routine always sets the shiny flag to 0, once it had been read. So we extend our routine again.

Second issue: The infinity of shinys

0871b700  bc02 pop {r1}                         // get the saved value
0871b702  b52d push {lr, r0, r2, r3, r5}        // put the return address onto the stack, save values
0871b704  9904 ldr r1, [sp, #0xc]               // reload it to r1
0871b706  3108 add r1, #8                       // adjust the return adress by 8. (for skipping 
                                                   the target-adressī data)
0871b708  9104 str r1, [sp, #0xc]                        // save return address again
0871b70a  4813 ldr r0, [$0871b758] (=$08044ec9)
0871b70c  f000 bl $0871b75c
0871b70e  f826 blh $004c                        // continue execution from r0, save return adress 
                                                   in lr
0871b710  0405 lsl r5, r0, #0x10                // shift left the rnd
0871b712  1945 add r5, r5, r0                   // double the halfword to a word
0871b714  4a0f ldr r2, [$0871b754] (=$02022000) // load flag position
0871b716  6811 ldr r1, [r2, #0x0]               // load the value @ 02022000 to r1
0871b718  2300 mov r3, #0x00                    // set r3 to 0
0871b71a  6013 str r3, [r2, #0x0]               // unset shiny flag
0871b71c  2901 cmp r1, #0x1
0871b71e  d00b beq $0871b738                    // is it a shiny? so jump to the shiny handler
0871b720  7821 ldrb r1, [r4, #0x0]              // load the byte @ r4 (=03007cf8) to r1
0871b722  7860 ldrb r0, [r4, #0x1]              // load the byte @ r4+1 (=03007cf9) to r0
0871b724  0200 lsl r0, r0, #0x08                // the byte in r0 is shifted left by 1 byte (=8bit)
0871b726  1809 add r1, r1, r0                   // r1 and r0 are added. we get a halfword r0+r1
0871b728  78a0 ldrb r0, [r4, #0x2]              // load the byte @ r4+2 (=03007cfa) to r0
0871b72a  0400 lsl r0, r0, #0x10                // r0 is shifted left by 2 bytes again
0871b72c  1809 add r1, r1, r0                   // r0 is added to r1
0871b72e  78e0 ldrb r0, [r4, #0x3]              // load the byte @ r4+3 (=03007cfb) to r0
0871b730  0600 lsl r0, r0, #0x18                // r0 is left-shifted by 3 bytes
0871b732  1809 add r1, r1, r0                   // and added to r1
0871b734  6079 str r1, [r7, #0x4]               // and finally the OTrainerIDs are stored at r7+4 
                                                   (=0202402c) 
0871b736  e00c b $0871b752                      // skip the following lines. theyīre used for shinys
0871b738  7821 ldrb r1, [r4, #0x0]              // load the byte @ r4 (=03007cf8) to r1
0871b73a  7860 ldrb r0, [r4, #0x1]              // load the byte @ r4+1 (=03007cf9) to r0
0871b73c  0200 lsl r0, r0, #0x08                // the byte in r0 is shifted left by 1 byte (=8bit)
0871b73e  1809 add r1, r1, r0                   // r1 and r0 are added. we get a halfword r0+r1
0871b740  78a0 ldrb r0, [r4, #0x2]              // load the byte @ r4+2 (=03007cfa) to r0
0871b742  0400 lsl r0, r0, #0x10                // r0 is left-shifted by 2 bytes again
0871b744  1809 add r1, r1, r0                   // r0 is added to r1
0871b746  78e0 ldrb r0, [r4, #0x3]              // load the byte @ r4+3 (=03007cfb) to r0
0871b748  0600 lsl r0, r0, #0x18                // r0 is left-shifted by 3 bytes
0871b74a  1809 add r1, r1, r0                   // and added to r1
0871b74c  6079 str r1, [r7, #0x4]               // and finally the OTrainerIDs are stored at 
                                                   r7+4 (=0202402c)
0871b74e  4069 eor r1, r5                       // PID is set to OTRainerIDs XOR rnd
0871b750  6039 str r1, [r7, #0x0]               // and hereīs our patch. the PID is overwritten 
                                                   by the OTrainerIDs
0871b752  bd2d pop {pc, r0, r5, r3, r2}         // return & exit sub, load saved values
0871b754  2000                                  // data1
0871b756  0202                                  // data2
0871b758  4ec9                                  // data3
0871b75a  0804                                  // data4
0871b75c  4700 bx r0                            // goto r0

Neat. That solves the second problem too. So weīre finished for this time, I hope you enjoy the īnewī 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