Re: An intro palette fix for Diamond/Jade, as well as a title screen crash fix for Diamond!

Discuss anything related to hacking ROMs of the Telefang games here.
Post Reply
Blaziken257
Posts: 983
Joined: Fri Dec 22, 2006 11:52 am

Re: An intro palette fix for Diamond/Jade, as well as a title screen crash fix for Diamond!

Post by Blaziken257 »

Yes, I have another bug-fixing patch now... this time, it fixes three bugs in Diamond and one in Jade. It fixes the palette in the intro for both games, and in Diamond, it also fixes the crashing that occurs after the Game Over screen, as well as pressing A+B+Select+Start. (Jade doesn't have the latter two bugs, so fixing them was not necessary.)

All of these bugs were due to the same cause: The Smilesoft, Comic BomBom, and Natsume logos were disabled in the bootlegs, skipping straight to the title screen; therefore, the patch restores the logos to accomplish the bug fixes. (I had long suspected that disabling the logos was responsible for the game over and the A+B+Select+Start crashes, but I couldn't prove it until now.) Removing the logos was obviously done to remove any credit given to them (which is sort of a good thing, as the most of the naive people who bought the bootlegs would think they were responsible for the awful translation and glitches). However, the way it was removed was incredibly sloppy.

First of all, the way the logo screens were disabled (they aren't actually removed, just made inaccessible) are different between Diamond and Jade, suggesting the bootleggers were disorganized. Yes, it's inconsistent, and that's why Diamond has more bugs than Jade does. I'll go over Diamond's method first.

First, here is code for Power, Speed, and Jade:

Code: Select all

ROM0:1C14 3E 02            ld   a,02           ;Switch bank to 02 (0x8000-0xBFFF in ROM is copied to 0x4000-0x7FFF in RAM)
ROM0:1C16 EA 23 C4         ld   (C423),a
ROM0:1C19 D7               rst  10
ROM0:1C1A C3 00 53         jp   5300           ;Load Smilesoft/Comic BomBom/Natsume screens
Basically, as you can see, it switches ROM bank to 0x02, then jumps to 0x5300 (which is 0x9300 from ROM since we're in bank 2) to load the Smilesoft/Comic BomBom/Natsume screens. (Jade uses the same code, but doesn't load the screens due to code modification elsewhere.)

Code: Select all

ROM0:1C14 EA E1 C3         ld   (C3E1),a       ;(C3E1) has an effect on the screen that shows up.
                                               ;However, due to this code, (C3E1) = 0x14 after exiting Game Over screen!!
                                               ;Notice that the last byte of this address (0x1C14) is also 0x14 -- this
                                               ;is not a coincidence! (See code at 0x1BE2 to see why -- clearly the
                                               ;testers hadn't intended for the code there to jump to here.)
ROM0:1C17 3E 01            ld   a,01
ROM0:1C19 EA E0 C3         ld   (C3E0),a       ;Not completely sure what (C3E0) does or why Diamond needs to set it to 01
ROM0:1C1C C9               ret                 ;Return to function caller; this effectively skips the logo screens at the
                                               ;beginning
Most important here is the "ret" instruction at the bottom. Instead of jumping to 0x9300, instead the game returns to the location that called this function in the first place. Effectively, this skips loading the logos. You'll also see that RAM address 0xC3E1 gets written by some value (it varies depending on where the program was before this spot), and RAM address 0xC3E0 gets the value of 01 for some reason (why, I don't know). Power, Speed, and Jade don't do this, as you can see. In particular, writing to 0xC3E1 is what causes the crash after the Game Over screen in the first place; it gets the value of 0x14 and causes bugs that I'll describe later.

It turns out that replacing this section of code with the code in Power/Speed/Jade (which is what the Diamond patch at the bottom of this post does) is enough to restore the logos and fix all three bugs! I'll explain why this is later.

Now let's look at what Jade does.

Here is code from Power, Speed, and Diamond:

Code: Select all

ROM2:5300 FA E1 C3         ld   a,(C3E1)       ;Used for finding the right pointer in a pointer table
ROM2:5303 21 0A 53         ld   hl,530A        ;0x530A (0x930A in ROM) is where some pointer table starts
ROM2:5306 CD 97 0E         call 0E97           ;Get pointer to determine where program should go next
ROM2:5309 E9               jp   hl             ;Jump to 0x93BD for Smilesoft screen; 0x94C5 for title screen
Basically, it grabs a pointer to determine where the program should go next after each screen, and which pointer it grabs depends on the current value of 0xC3E1 in RAM.

This is the code for Jade:

Code: Select all

ROM2:5300 FA E1 C3         ld   a,(C3E1)       ;Used for finding the right pointer in a pointer table
ROM2:5303 C3 E0 7F         jp   7FE0           ;Hijack original routine by adding additional code
ROM2:5306 CD 97 0E         call 0E97           ;Get pointer to determine where program should go next
ROM2:5309 E9               jp   hl             ;Jump to 0x93BD for Smilesoft screen; 0x94C5 for title screen

ROM2:7FE0 FE 02            cp   a,02
ROM2:7FE2 20 05            jr   nz,7FE9        ;If (C3E1) != 0x02 then do not change it
ROM2:7FE4 3E 15            ld   a,15           ;Otherwise, if (C3E1) == 0x02, then change it to 0x15
ROM2:7FE6 EA E1 C3         ld   (C3E1),a       ;Set (C3E1) to 0x15;
                                               ;This is what skips the Smilesoft/Natsume/Comic BomBom logos in
                                               ;Jade because the bootleggers don't want to give them any credit
ROM2:7FE9 21 0A 53         ld   hl,530A        ;0x530A (0x930A in ROM) is where some pointer table starts
ROM2:7FEC C3 06 53         jp   5306           ;Go back
At ROM2:5303 (this is 0x9303 in ROM, for those confused about ROM banks), the bootleggers jumped to a previously unused space in ROM to add additional code. Basically, if 0xC3E1 in RAM is 0x02, it changes to 0x15. This seems to be enough to disable the logos. Then it jumps back to 0x5306 (in ROM: 0x9306) to go back to where it was before.

As before, replacing 0x9303-0x9305 with the original Japanese code will load the title screen, and consequently fix the colors in the intro. This is what the Jade patch at the bottom of this post does!

Now why does disabling the logo screens cause so many bugs? Let's start with the glitchy color palettes in the intro, which is the least obvious one.

It turns out that since the logos are skipped in the bootlegs, it also skips writing to a byte in RAM that is later necessary to use the proper colors.

First, when the game starts up, this code gets executed, in ALL versions.

Code: Select all

ROM0:159F AF               xor  a              ;XOR a with itself. This makes a = 0.
ROM0:15A0 22               ldi  (hl),a         ;hl = DD06, so at startup, (DD06) = 0 in ALL versions
Pretty self-explanatory; RAM address 0xDD06 = 0 when the game starts up.

Now, this code is executed as long as the Smilesoft screen appears:

Code: Select all

ROM2:53D0 3E 01            ld   a,01           
ROM2:53D2 EA 06 DD         ld   (DD06),a       ;Set (DD06) to 1; this only happens on the Smilesoft screen, so this
                                               ;gets skipped in bootlegs
When the Smilesoft screen appears, 0xDD06 = 1. However, the bootlegs skip this screen, so 0xDD06 doesn't change to 1!

Let's look at more code:

Code: Select all

ROM0:0B1A F5               push af             ;Save current value of a for later (there should be a pop af later on)
ROM0:0B1B FA 06 DD         ld   a,(DD06)       ;Read from (DD06)
ROM0:0B1E B7               or   a              ;Is (DD06) == 0?
ROM0:0B1F CA 9E 0B         jp   z,0B9E         ;If so, then skip routine the generates palettes
It turns out that in order for the game to generate the right colors for most screens, 0xDD06 has to be anything other than 0. Since the bootlegs skip the Smilesoft screen, though, 0xDD06 is still 00! Oops. That's why the palettes in the intro are glitchy.
Spoiler!
Here is the code to generate the colors for various screens like the intro. I don't know much about what's going on, but I do know that the scene for Shigeki is around 0x240BF in ROM, where the colors are in groups of 2 bytes. The lower 6 bits of the first byte seem to determine the length (in tiles), and the upper 2 bits do something different, but I didn't spend much time figuring out what. The second byte determines the palette (it should always be from 0-7). If anybody wants to look into this code, be my guest.

Code: Select all

ROM0:0BC8 13               inc  de
ROM0:0BC9 1A               ld   a,(de)
ROM0:0BCA FE FF            cp   a,FF
ROM0:0BCC CA 30 0C         jp   z,0C30
ROM0:0BCF 1A               ld   a,(de)
ROM0:0BD0 E6 C0            and  a,C0
ROM0:0BD2 FE C0            cp   a,C0
ROM0:0BD4 CA 1B 0C         jp   z,0C1B
ROM0:0BD7 FE 80            cp   a,80
ROM0:0BD9 CA 06 0C         jp   z,0C06
ROM0:0BDC FE 40            cp   a,40
ROM0:0BDE CA F2 0B         jp   z,0BF2
ROM0:0BE1 C5               push bc
ROM0:0BE2 1A               ld   a,(de)
ROM0:0BE3 3C               inc  a
ROM0:0BE4 47               ld   b,a
ROM0:0BE5 13               inc  de
ROM0:0BE6 1A               ld   a,(de)
ROM0:0BE7 CD B3 09         call 09B3
ROM0:0BEA 05               dec  b
ROM0:0BEB C2 E5 0B         jp   nz,0BE5
ROM0:0BEE C1               pop  bc
ROM0:0BEF C3 C8 0B         jp   0BC8
ROM0:0BF2 C5               push bc
ROM0:0BF3 1A               ld   a,(de)
ROM0:0BF4 E6 3F            and  a,3F
ROM0:0BF6 C6 02            add  a,02
ROM0:0BF8 47               ld   b,a
ROM0:0BF9 13               inc  de
ROM0:0BFA 1A               ld   a,(de)
ROM0:0BFB CD B3 09         call 09B3
ROM0:0BFE 05               dec  b
ROM0:0BFF C2 FB 0B         jp   nz,0BFB
ROM0:0C02 C1               pop  bc
ROM0:0C03 C3 C8 0B         jp   0BC8
ROM0:0C06 C5               push bc
ROM0:0C07 1A               ld   a,(de)
ROM0:0C08 E6 3F            and  a,3F
ROM0:0C0A C6 02            add  a,02
ROM0:0C0C 47               ld   b,a
ROM0:0C0D 13               inc  de
ROM0:0C0E 1A               ld   a,(de)
ROM0:0C0F CD B3 09         call 09B3
ROM0:0C12 3C               inc  a
ROM0:0C13 05               dec  b
ROM0:0C14 C2 0F 0C         jp   nz,0C0F
ROM0:0C17 C1               pop  bc
ROM0:0C18 C3 C8 0B         jp   0BC8
ROM0:0C1B C5               push bc
ROM0:0C1C 1A               ld   a,(de)
ROM0:0C1D E6 3F            and  a,3F
ROM0:0C1F C6 02            add  a,02
ROM0:0C21 47               ld   b,a
ROM0:0C22 13               inc  de
ROM0:0C23 1A               ld   a,(de)
ROM0:0C24 CD B3 09         call 09B3
ROM0:0C27 3D               dec  a
ROM0:0C28 05               dec  b
ROM0:0C29 C2 24 0C         jp   nz,0C24
ROM0:0C2C C1               pop  bc
ROM0:0C2D C3 C8 0B         jp   0BC8

ROM0:09B3 F3               di   
ROM0:09B4 F5               push af
ROM0:09B5 F0 41            ld   a,(ff00+41)
ROM0:09B7 E6 02            and  a,02
ROM0:09B9 20 FA            jr   nz,09B5
ROM0:09BB F1               pop  af
ROM0:09BC 22               ldi  (hl),a
ROM0:09BD FB               ei   
ROM0:09BE C9               ret
Fortunately, though, 0xDD06 also gets set to 1 when loading the D-Shot screen (right after the title screen). Naturally, even the bootlegs have this screen, so 0xDD06 gets set to 1 once and for all, and the D-Shot screen (and pretty much everything after it) loads the proper colors.

Code: Select all

ROM4:4089 3E 01            ld   a,01
ROM4:408B EA 06 DD         ld   (DD06),a       ;Set (DD06) to 1; this happens on the D-Shot screen, so even
                                               ;bootlegs reach this code
See?

It also turns out that if you get a Game Over in Jade, the intro will load the proper colors afterwards. I didn't know that before looking at this code, but you can see for yourself.

Now as for crashing after the Game Over screen: This is more complicated...

Let's look at the code here, which gets executed very frequently throughout the game (this is identical in all versions).

Code: Select all

ROM0:1BE2 FA E0 C3         ld   a,(C3E0)       ;Get value from (C3E0)
                                               ;0x0E on Game Over screen (all versions)
                                               ;0x00 just after exiting Game Over screen (all versions)
ROM0:1BE5 21 F4 1B         ld   hl,1BF4        ;0x1BF4 = Base pointer
ROM0:1BE8 16 00            ld   d,00
ROM0:1BEA 5F               ld   e,a            
ROM0:1BEB CB 23            sla  e              ;Multiply value from (C3E0) by 2
ROM0:1BED CB 12            rl   d
ROM0:1BEF 19               add  hl,de          ;Add to pointer to get another pointer
                                               ;Just after exiting Game Over screen: 0x1BF4 + 0 * 2 = 0x1BF4
ROM0:1BF0 2A               ldi  a,(hl)         ;Read little endian pointer
ROM0:1BF1 66               ld   h,(hl)
ROM0:1BF2 6F               ld   l,a            ;0x1BF4 returns pointer of 0x1C14
ROM0:1BF3 E9               jp   hl             ;Jump to pointer
The code that gets executed afterwards is dependent on the value of 0xC3E0 in RAM. At the Game Over screen, this is 0x0E. But after, it is 0x00, which is the important part. The program then reads from a pointer table, where the pointer determines the code to execute next. The pointer table starts at 0x1BF4, and after the Game Over screen, since 0xC3E0 is 00, it reads at 0x1BF4 itself. You can see that the values here are 14 1C, which, due to little endianness, results in a pointer of 0x1C14. The game, thus, jumps to 0x1C14 in ROM to execute the next code; you can see this near the beginning of this post.

Note that, at address 0x1BF0, the lower byte (0x14) gets assigned to register a, which causes problems for Diamond once it goes to 0x1C14. Scroll up to the second code block and look at it -- since a is 0x14, this gets written to 0xC3E1!! (As you can see, only Diamond writes to 0xC3E1 here.) This causes problems, as you will soon see...

Code: Select all

ROM2:493F FA E1 C3         ld   a,(C3E1)       ;(C3E1) = 14 in Diamond after Game Over screen; 0 in everything else!!
ROM2:4942 21 49 49         ld   hl,4949        ;Base pointer: 0x8949 in ROM
ROM2:4945 CD 97 0E         call 0E97           ;In Power/Speed/Jade: Reads from ROM address 0x8949 + 2*0x0 = 0x8949.
                                               ;  Reads pointer at 0x8949 and returns 0x496F; this translates to 0x896F in ROM
                                               ;  (since we are in ROM bank 2).
                                               ;In Diamond:          Reads from ROM address 0x8949 + 2*0x14 = 0x8971.
                                               ;  Reads pointer at 0x8971 and returns 0xCD00;
                                               ;  this translates to 0xCD00 in RAM!!!
ROM2:4948 E9               jp   hl             ;In Power/Speed/Jade: Program jumps to 0x896F to load title screen.
                                               ;In Diamond:          Program jumps to 0xCD00 and eventually crashes!!!
Since you see the "call 0E97" instruction a few times in here, here's the code for that. Effectively, using an address for a pointer table, and an index, you get the pointer:

Code: Select all

ROM0:0E97 06 00            ld   b,00           
ROM0:0E99 4F               ld   c,a            ;Get value from (C3E1)
ROM0:0E9A CB 21            sla  c              ;Multiply by 2
ROM0:0E9C CB 10            rl   b
ROM0:0E9E 09               add  hl,bc          ;Add to pointer from before
                                               ;When called from 0x9306:
                                               ;  Smilesoft screen: 0x930A + 2*0x2 = 0x930E;
                                               ;  title screen: 0x930A + 2*0x15 = 0x9334
                                               ;When called from 0x8945:
                                               ;  Power/Speed/Jade: 0x8949 + 2*0x0 = 0x8949; Diamond: 0x8949 + 2*0x14 = 0x8971
ROM0:0E9F 2A               ldi  a,(hl)         ;Read little-endian pointer
ROM0:0EA0 66               ld   h,(hl)         ;
ROM0:0EA1 6F               ld   l,a            ;When called from 0x9306:
                                               ;  Smilesoft screen: 0x930E returns 0x93BD; Title screen: 0x9334 returns 0x94C5
                                               ;When called from 0x8945:
                                               ;  Power/Speed/Jade: 0x8949 returns 0x896F;
                                               ;  Diamond: 0x8971 returns 0xCD00 in RAM!!
ROM0:0EA2 C9               ret                 ;Return
As you can see, eventually the program reaches 0x89EF in ROM, and in Diamond, 0xC3E1 is still 0x14!! This code is supposed to read from a pointer table at 0x8949, and grab the right pointer to determine where the program should jump to afterwards. In Power, Speed, and Jade, 0xC3E1 is 0, so the pointer would be at 0x8949 + 2*0, which is 0x8949. The values in this address are 6F 49, which translates to 0x896F in ROM. So, in these three versions, the program will jump to 0x896F, and everything is OK.

But in Diamond, the pointer is at 0x8949 + 2*0x14, which is... 0x8971. I'm not sure if this is even meant to be a pointer, but in any case, the values are 00 CD (at least in Diamond -- in Power, it's 09 CD -- this difference is strange in and of itself, but that's beside the point), which translates to 0xCD00 in RAM. So, the program will jump here, even though it's not supposed to. There are lots of "nop" instructions (which do nothing), but eventually the game will crash since it will come across some invalid instruction sooner or later (which depends on the current values in RAM).

As for A+B+Select+Start? I haven't really looked in great detail why this occurs, but since restoring the logos fixes this bug, it's probably related to the Game Over screen glitch. One thing is for certain: Like the other bugs, it's due to the removal of the company logos.

This was fun to reverse engineer, and I hope you enjoy the small but useful patches. It's amazing how the bootleggers care so much about removing any traces of the original creators of the game, and yet do it so sloppily that it causes bugs not originally present. Obviously they just cared about making money and not about the quality of the end product. But that's obvious, right?

One final thing: Try the GameShark code 01xx06DD. Replace xx with 00 in the Japanese games, or xx with 01 in the non-patched bootleg games, then watch the intro. See anything interesting? (Actually, replacing xx with 00 is fun to try in both games, in various screens, not just the intro!
Post Reply