[Tutorial] - JAP Games Font Size Hacking ( Nakoruru - SEGAGAGA - Vermillion Desert - Puyo Puyo 4 )

Moderators: pcwzrd13, deluxux, VasiliyRS

User avatar
VincentNL
Anarki
Posts: 91
Contact:

[Tutorial] - JAP Games Font Size Hacking ( Nakoruru - SEGAGAGA - Vermillion Desert - Puyo Puyo 4 )

Post#1 » Mon Apr 11, 2022 3:33 pm

Thanking everyone in DC scene who helped me out in the past, I'd love to take this chance to give back to the community! :)

Special credits to Nanashi for introducing and teaching VFW concept / SH4 assemblers, Exant for bitflag ops, Mr.Neo, Ian Micheal and Derek for providing help on DC tools, sharing their HW and RE knowledge!

This tutorial is intended exclusively for educational purposes, it does not have any intention to damage SEGA and/or any other third party developers who owns the rights.
If this does cause any issue, please feel free to remove it anytime.



Preface:

Anyone getting into JP games translation will sooner or later get into a common roadblock.
Let's say our game letters are usually 24x24 px in size ( Wide ), but we need 12x24 px ( Half-width) to efficiently use western characters and optimize text area.

Image



--------------------


How does this method work?

It is based on the assumption that our game adds one letter at a time.

Image

Our target is tracking a RAM variable (character count or offset), leading us to text-drawing function parameters.
In the end we'll alter original function to reduce distance between each letter:


Image

This method advantages are:

  • You can draw a smaller font than the original without resizing the texture
  • Words can use any custom size, smaller than wide.
  • It's variable font width-ready


--------------------

Click Here for "NAKORURU - Ano Hito Kara no Okurimono (JP)"

Click Here for "SEGAGAGA (JP)"

Click Here for "VERMILLION DESERT (JP)"

Click Here for "PUYO PUYO 4 (JP)"



What you will need:

  • Ghidra 10.0.2 or higher
  • Basic SH-4 asm knowledge
  • Demul 0.7
  • Cheat Engine 7.2


--------------------

DC Font Width Size Hacking Tutorial by VincentNL:

    Index

  • PART 1-1: SETUP - Ghidra
  • PART 1-2: SETUP - Cheat Engine
  • PART 2-1: SEARCH RAM VARIABLE
  • PART 2-2: LOCK THE VARIABLE
  • PART 2-3: LOCATE PARAMETERS
  • PART 2-4: WREAK HAVOC!
  • PART 3: BREAKPOINTS
  • PART 4: GHIDRA!
  • PART 5: HACKING!
  • PART 6: TESTING!


-------------


  • PART 1-1: SETUP - Ghidra


1) Let's open Ghidra and drag game executable (1ST_READ.bin, in this case) in the program window.

2) Choose language processor as SuperH4 32 LE, click on OPTIONS button to specify base address 8C010000

Image

3) Once Ghidra loads the file, click on "Auto Analyze".

Image



  • PART 1-2: SETUP - Cheat Engine


1) Let's open Demul and load the game.

2) Open Cheat Engine, and click on "Open Process" icon:

3) Select Demul with game name, and click Open button:

Image


--------------

  • PART 2-1: SEARCH RAM VARIABLE


1) Choose any part of the game where text is rendered one letter at a time:

Image

2) Make a savestate before text is shown

3) Switch to Cheat Engine window when text start to show up,If done correctly, Demul will freeze.

4) Let's search for an "Unknown inital value", and hit "First Scan" button:

Image

5) Go back to Demul, and let it continue to draw a few more letters then go back to Cheat Engine.

6) From this time on we will choose "Increased Value" and hit "Next Scan":

Image

7) Now, repeat steps 5-6 until you get a number of results below 500.
Please note, the "Next Scan" is relative to the number of text words rendered!
In case you have exhausted all words and results are still over 500, you can reload the savestate
but you will have to choose "Decreased value" since the value has reset to a lower number.

8) Select results over 0x2c010000 range and ignore those before. Right-click "Add selected..." to put them into Cheat Engine main window.

Why ignore the rest?

Demul allocates executable program memory at 0x2c010000 instead of 0x8c010000!
So yeah, keep this in mind when looking at 0x2c.. in Cheat Engine. Those will be 0x8c.. in Ghidra!

Image


  • PART 2-2: LOCK THE VARIABLE


Ok now that we have a range of variables, we need to find out which one is handling text offset.

1) On Demul, wait until a couple of letters are rendered in game, then jump to Cheat Engine:

2) Lock all values in main window, by selecting them all and press spacebar. A red X will show up near each address.

3) Reload your savestate



  • PART 2-3: LOCATE PARAMETERS


*Case A*

If you are lucky, one of those variables will cause text to clutter on the very same spot.
enable/disable locked values until you figure which one is our target, then proceed on this guide step 2-4.

*Case B*

No changes found. We will have to start looking into those address and repeat the process until we find the correct parameters.
Usually RAM values are stored outside of game executable memory, if you check in Ghidra 0x8c0da3dc is where game executable ends.
So let's choose the next one: 0x2C4176D8




  • PART 2-4: WREAK HAVOC!


1) Right click on the value and choose "Browse this memory region":

Image

Usually this present as a set of values, short, long, byte or floats arrays.

Image

2) Now, start messing with them during text rendering will lead you to actually reverse engineer their purpose.
Remember, our target is to have all letters clutter in the same offset! So yeah just "00" each parameter or lock them with "0" value.

Image

Bingo!

We can notice that altering the address 0x2C4176E8 value, affects space between each letter.
The more we reverse engineer now, the easier will be to read the original function in Ghidra.
(keep note of every parameter effect / memory address)


--------------

  • PART 3: BREAKPOINTS



1) First of all we know 0x8C4176E8 (0x2C4176E8 in Cheat Engine), is a RAM parameter produced by a function inside game executable.

2) We need to track down WHICH function is writing to 0x8C4176E8 and we'll need a breakpoint.

3) Reload the game when that value has not been written yet. (at the beginning of scene)

4) Immediately jump in Cheat Engine Memory window, right click on the value then, --> data breakpoint / break on Write:


Image

5) Now, go back in Demul until game will stop and write down the address in EBX register:

Image

--------------------

  • PART 4: GHIDRA


You can use any similar tool, but Ghidra is my personal fave. It's free and perfect for the purpose.

1) Go to the address found in EBX register:

Image

Cheat Engine debugs the emulator layer, so it won't work like a proper debugger. But that's fine.

1) That address is a long value, accessed by multiple functions so let's inspect them to find a 0x8C4176E8 write.
(double click on more)

Image

2) Let's follow the first write

3) scroll down the function carefully until you notice a write to 0x8C4176E8:
Image

4) Label it (click on address in red, and press L) as "ram_character_spacing" so it's easier to refer.


--------------

  • PART 5: HACKING!


Let's inspect how this works:

Code: Select all

8c019388 c2 62           mov.l      @r12=>DAT_8c0637d4,r2                            = 0000002Ch
8c01938a 2a 1d           mov.l      r2,@(0x28,r13)=>ram_character_spacing


Basically whatever we found in EBX register, were an actual value that gets copied in "r2".
What interest us is that r2 will be finally passed to r13 for being written.

It would be ideal to rewrite the function since it may have different use of that same parameter, but it might not be necessary.
We'll just check the instructions below:

Image

1) As you can see r2 parameter isn't reused until 0x8c01939e, where a new function is stored having a different purpose.
So yeah we may proceed to change r2 value (character spacing), to our likings.

2) We'll use 0x10 as size, so: mov #0x10,r2.
Right click on 0x8c019388 in Ghidra and click on "Patch instruction", we will change:

Code: Select all

mov.l @r12,r2 --> mov #0x10,r2


--------------

  • PART 6: TESTING!


You can now choose to rebuild your game or test it live.
To preview changes, just go back to Cheat Engine at 0x8c01938a and replace 2A1D --> 10E2

Boom!


Please note this method will help you to track down progressive text rendering.
Even if it won't magically patch all different text functions of the game, it should provide a huge boost in RE hopefully making your life easier.



Image
Last edited by VincentNL on Wed Aug 03, 2022 3:22 pm, edited 16 times in total.
You can read my latest reversing / hacking endeavours at:
https://www.patreon.com/VincentNL

User avatar
ateam
killer
Posts: 265

Re: [Tutorial] - JAP Games Font Size Hacking

Post#2 » Mon Apr 11, 2022 4:48 pm

Incredibly awesome, and thank you so much for taking the time last week to show me the magic of Cheat Engine for Dreamcast hacking/debugging!

I just want to put this out there to anyone who sees this and thinks my Nakoruru translation patch can now easily have a modified font width:

It's far more complicated than that :lol:

This game features about a dozen text-rendering routines, most of which include logic for built-in font scaling (an annoyance, to say the least). However, Vincent's assistance has helped me tremendously and has at least let me find the functions that I need to modify, although I have now determined I will need some new code in order to work around the limited space of modifying the original functions.

Just as one example, the function Vincent showed where mov.l @r12,r2 could be safely changed to mov #0x10,r2 is the exception and not the rule, unfortunately. The rest of the functions (most, if not all), will re-use the same register for tile width, height, and scale. This is by design and was intended to conform to the game's original auto-scaling feature with small, normal, and large text.

In the below example of what I'm referring to, I have a few memory addresses labeled (e.g., ram_char_space_value). As you can see, #1 is setting the tile width (0x16 = 22), and #2 shows the tile spacing, as well as the proportional height/width for drawing the tile...

Image

My goal is to solve these with jumps/branches that will execute my modified instructions and then return back to the function.

Anyway, thank you again, my friend! This has proven to be invaluable for me. My arsenal has been lxdream-nitro (for its debugger), Ghidra (for the obvious), and Flycast's tracelog dumping -- and now I can add in Cheat Engine for RAM searching and on-the-fly modification! A thing of beauty, indeed.
Find me on...

DreamcastForever.com
GitHub
Reddit
SegaXtreme
Twitter
YouTube
• Discord: derek (ateam)#4459

User avatar
VincentNL
Anarki
Posts: 91
Contact:

Re: [Tutorial] - JAP Games Font Size Hacking

Post#3 » Tue Apr 12, 2022 4:26 am

Yeah that falls in the "function rewrite" category, but there are a few tricks to avoid major rewrite if you just need to add a single instruction! ;)

1) Search for repeating instructions ending with rts, below the address you need to change within bra range

Image

2) Fill with nops (09 00) the area you want to clear

3) Now we want to let the function resume at the other address, so at the end let's bra over there.

4) Rearrange your function, and add the instruction you need!
(Unless it's finalized, keep a few extra nops in the empty area, just in case you'll want to add more instructions later on)



It really takes a minute or two:


Image
You can read my latest reversing / hacking endeavours at:
https://www.patreon.com/VincentNL

User avatar
ateam
killer
Posts: 265

Re: [Tutorial] - JAP Games Font Size Hacking

Post#4 » Tue Apr 12, 2022 8:41 am

VincentNL wrote:Yeah that falls in the "function rewrite" category, but there are a few tricks to avoid major rewrite if you just need to add a single instruction! ;)


Awesome! Very easy logic to follow. Now time to see how many of the functions can be given this same treatment. Will report back.
Find me on...

DreamcastForever.com
GitHub
Reddit
SegaXtreme
Twitter
YouTube
• Discord: derek (ateam)#4459

User avatar
VincentNL
Anarki
Posts: 91
Contact:

Re: [Tutorial] - JAP Games Font Size Hacking

Post#5 » Tue Apr 12, 2022 10:10 am

"SEGAGAGA (JP)"


  • PART 2-2: LOCK THE VARIABLE

Variable found at 0x8CAC5E58

Image


  • PART 2-3: LOCATE PARAMETERS + PART 2-4:WREAK HAVOC


Offset parameter at 0x8CAC5E60, not directly expressed as distance per letter.
Messing with it alters the whole text offset.

Image


---------
  • PART 3: BREAKPOINTS

Setting up a "breakpoint on Write" at offset parameter gives EBX: 0x8C12FA00, let's check it in Ghidra!

Looks like another set of parameters, by messing with them in Cheat Engine turns out they are offset floats/short values.

Image


  • PART 4: GHIDRA


They all lead to FUN_8c0ae8e2, so we need to check all of those instructions, using H_offset modifiying registers to RE how that works.
Does this look any familiar? :D

Image

----------------

  • PART 5: HACKING!

Let's inspect how this work:

By a using a sheer amount of instructions starting from 0x8c0af066, the function creates a 0x18 multiplier ( 24 in decimals) that put every new letter, 24 pixels after the previous one.

Code: Select all

         8c0af05c 41 85           mov.w      @(0x2,r4),r0=>TEXT_H_OFFSET                      = 15Ch
         8c0af05e 2c 30           add        r2,r0
         8c0af060 f6 52           mov.l      @(0x18,r15)=>local_34,r2
         8c0af062 0d 64           extu.w     r0,r4
         8c0af064 f6 85           mov.w      @(0xc,r15)=>local_40,r0
         8c0af066 03 61           mov        r0,r1
         8c0af068 00 40           shll       r0
         8c0af06a 1c 30           add        r1,r0
         8c0af06c 08 40           shll2      r0
         8c0af06e 00 40           shll       r0
         8c0af070 21 62           mov.w      @r2,r2
         8c0af072 35 d1           mov.l      ->thunk_FUN_8c023b48,r1                   
         8c0af074 2c 30           add        r2,r0
         8c0af076 0d 60           extu.w     r0,r0
         8c0af078 28 40           shll16     r0
         8c0af07a 0b 41           jsr        @r1=>thunk_FUN_8c023b48               
         8c0af07c 0b 24           _or        r0,r4


Now since it's an hassle to customize, we want to simplify and optimize the function:

1) Create our own multiplier using 2 empty bytes below the function
2) Replace all those shll and add, with muls.w and sts.

Code: Select all

         8c0af05c 41 85           mov.w      @(0x2,r4),r0=>TEXT_H_OFFSET       
         8c0af05e 2c 30           add        r2,r0
         8c0af060 f6 52           mov.l      @(0x18,r15)=>local_34,r2
         8c0af062 0d 64           extu.w     r0,r4
         8c0af064 f6 85           mov.w      @(0xc,r15)=>local_40,r0
 -->     8c0af066 6e 91           mov.w      PIXEL_DISTANCE,r1                                = 0005h
 -->     8c0af068 1f 20           muls.w     r1,r0
 -->     8c0af06a 1a 00           sts        MACL,r0
 -->     8c0af06c 09 00           nop
 -->     8c0af06e 09 00           nop
         8c0af070 21 62           mov.w      @r2,r2
         8c0af072 35 d1           mov.l      ->thunk_FUN_8c023b48,r1                           
         8c0af074 2c 30           add        r2,r0
         8c0af076 0d 60           extu.w     r0,r0
         8c0af078 28 40           shll16     r0
         8c0af07a 0b 41           jsr        @r1=>thunk_FUN_8c023b48                         
         8c0af07c 0b 24           _or        r0,r4



---------

  • PART 6: TESTING!


There we go!

Anyone interested on SGGG stranslation, feel free to use the code.

Image
Last edited by VincentNL on Fri Apr 15, 2022 4:37 pm, edited 1 time in total.
You can read my latest reversing / hacking endeavours at:
https://www.patreon.com/VincentNL

User avatar
yzb
Developer
Posts: 122

Re: [Tutorial] - JAP Games Font Size Hacking ( Nakoruru | SEGAGAGA )

Post#6 » Thu Apr 14, 2022 6:58 am

Very good tutorial

However, it should be noted that in a very few cases, the MACL register may have another purpose, which can only be known in context
If it's only a few multiplication runs, it's easier to use a shift

For example, X5
mov r0,r1
shll2 r0
add r1,r0


For example, X6
mov r0,r1
shll2 r0
shll r1
add r1,r0

User avatar
VincentNL
Anarki
Posts: 91
Contact:

Re: [Tutorial] - JAP Games Font Size Hacking ( Nakoruru | SEGAGAGA )

Post#7 » Thu Apr 14, 2022 10:01 am

"VERMILLION DESERT (JP)"

Decided to make this one since Cheat Engine breakpoints are not usable here, so it's a slightly different procedure.

  • PART 2-2: LOCK THE VARIABLE

Variable found at 8C8E88B0

Image

------------

  • PART 2-3: LOCATE PARAMETERS + PART 2-4:WREAK HAVOC


Offset parameter at 0x8C8E88C4, affects distance per letter ( 0x18 = 24px ):

Image


---------
  • PART 3: BREAKPOINTS

Setting up a "breakpoint on Write" at offset parameter gives an unusable EBX.
Parameters are relocating at each new text box, looking for 0x8C8E88C4 doesn't yield any result in Ghidra.

Do we need a emulator with debugger? Shall we give up?

Nope!

We have a series of values in parameters. As we know distance value is 0x18 and there's a fixed float 0x0AD723BF (-0.64) which is consistent between textboxes.

Image

How about we search for these in Ghidra to track the original function?
0x18 could lead up to a million of search results, so let's look for that float instead.


---------------

  • PART 4: GHIDRA


By searching for the float we only have 4 results! That's definitely good.
Now let's check which function calls them, one by one:

Image


FUN_0x8c017b2, use the float on r0.
Does that 0x18 in r5 look any familiar to you? :D

Image


----------------

  • PART 5: HACKING!

Not much to say here, we can get away by changing mov instruction:

Code: Select all

                  8c0187c0 18 e5           mov        #0x18,r5

into:

Code: Select all

                  8c0187c0 0c e5           mov        #0x0c,r5



---------

  • PART 6: TESTING!

There we go!
Half-width for Vermillion Desert!

Image
You can read my latest reversing / hacking endeavours at:
https://www.patreon.com/VincentNL

User avatar
VincentNL
Anarki
Posts: 91
Contact:

Re: [Tutorial] - JAP Games Font Size Hacking ( Nakoruru - SEGAGAGA - Vermillion Desert - Puyo Puyo 4)

Post#8 » Tue Aug 02, 2022 2:20 pm

"Puyo Puyo 4 (JP)"

Tutorial for one of my fave DC games :)


  • PART 2-2: LOCK THE VARIABLE

Variable found at 0x8CA36F68

Image



  • PART 2-3: LOCATE PARAMETERS + PART 2-4:WREAK HAVOC


Offsets parameters at 0x8C8E88C4, please note these are dynamic floats stored into RAM.
They are being constantly updated to determine current letter offset.

Image

  • PART 3: BREAKPOINTS

Setting up a "breakpoint on Write" at offset parameter and stepping into, leads to an instruction located at: 0x8C0CB174



  • PART 4: GHIDRA


The instruction we have found belongs to FUN8c0ca540, which is a pretty long one.
According its several branches, we are dealing with control codes specific parameters.
What we want to do now, is to track down which float register is used to assign offset and modify it.
The easiest way is to nop (0x0900) those in code section and test in game.

Image


By nulling nearby instruction 0x8c0cb16c reveals our target being fr2

Image

let's label 0x8c0cb16c as text_h_offset and move on!


  • PART 5: HACKING!


First of all, whatever value fr2 is holding, on a basic level we just want to reduce the spacing between letters, right?
Assuming 24px being a full-tile size, first instruction that comes to mind is fadd with a -12.0 float value (0x000040C1).

Now that we have a plan, let's find a suitable space which is often a 0x0900 0900 0900 0900... area.
Luckily PP4 has alot of 0x0009000900090009 (they are not nop, just dummy!)
and the closest one is within bra and mov ranges:

Image

It's really alot of empty space for what we need to do but we're experimenting, so any optimization will come later on:

1) Let's change that dummy area to an usable nop area. [0009 --> 0900]

2) last two instructions will allow us to get back to the original function offset right after the bra jump:

Code: Select all

      8c0cb33c 18 af          bra       LAB_8c0cb170
      8c0cb33e 09 00          nop



3) There's more empty space at 0x8c0cb0350 so let's add our new CUSTOM DISTANCE float (0x000040C1) there.
Image

4) Go back to the text_h_offset , copy those 2 instructions at the end of our new function:

Image
Image
To better explain, since we need 2 instructions to jump from the original position 0x8c0cb16c to our new code position 0x8c0cb324,
we still need to execute them for the game to work properly! So we'll move those near the end of our new code!

5) Cool. We need to verify if we made any mistakes and the game crashes:

Copy/paste as byte string the whole part we have altered in Ghidra (0x8c0cb16c to 0x8c0cb0354) into Demul RAM.
In our case at 0x2c0cb16c. (8c010000--> demul = 2c010000).
Be sure to enable writing in case Demul RAM is locked!

6) Let's make a save state before the scene loads with our new code already pasted in RAM.

7) Instruction time!

Code: Select all

      8c0cb324 0a c7          mova      CUSTOM_DISTANCE,r0          = -12.0
      8c0cb326 08 fb          fmov.s    @r0=>CUSTOM_DISTANCE,fr11   = -12.0
      8c0cb328 b0 f2          fadd      fr11,fr2
      8c0cb32a 09 00          nop
      8c0cb32c 09 00          nop
      8c0cb32e 09 00          nop
      8c0cb330 09 00          nop
      8c0cb332 09 00          nop
      8c0cb334 09 00          nop
      8c0cb336 09 00          nop
      8c0cb338 2a f1          fmov.s    fr2,@r1
      8c0cb33a e2 63          mov.l     @r14,r3
      8c0cb33c 18 af          bra       LAB_8c0cb170   # return to original function offset
      8c0cb33e 09 00          nop


Basically we read our CUSTOM_DISTANCE float address in r0, get the float in fr11 (unused)
and at last, simply add fr11 value to fr2.



  • PART 6: TESTING!

There we go!
Half-width for Puyo Puyo 4!

Image
Last edited by VincentNL on Fri Aug 05, 2022 11:52 am, edited 15 times in total.
You can read my latest reversing / hacking endeavours at:
https://www.patreon.com/VincentNL

User avatar
fafadou
1300
Posts: 1336

Re: [Tutorial] - JAP Games Font Size Hacking ( Nakoruru - SEGAGAGA - Vermillion Desert - Puyo Puyo 4 )

Post#9 » Wed Aug 03, 2022 6:23 am

Incredible topic dear Vincent !

colgate
letterbomb
Posts: 153

Re: [Tutorial] - JAP Games Font Size Hacking ( Nakoruru - SEGAGAGA - Vermillion Desert - Puyo Puyo 4 )

Post#10 » Wed Aug 03, 2022 10:52 am

Awesome!

  • Similar Topics
    Replies
    Views
    Last post

Return to “Modifications”

Who is online

Users browsing this forum: No registered users