Roger Quartermain and the lost script of the Heisei era.

Roger is happy. He bought a cheap PC-Engine CDRom game called “Nishimura Kyotaro Mystery.: Hokutosei No Onna”. It seems to be a detective game like J.B. Harold Murder Club, Jake Hunter, Ace Attorney, Le Manoir de Mortevielle or Maupiti Island. It was published by Naxat (now Kaga Create) in 1990.
He remembers that some games have a language option. For example, the japanese version of J.B Harold Murder Club can be played in english. Unfortunately such option was nowhere to be found in Hokutosei No Onna… Nevermind! Roger Quartermain grabs his boots, his hat and jumps into the game dark bowels!
The introduction seems to be about some dude stabbed in his room, 2 girls going to a train station and another guy who is apparently stalking them.

Nishimura-0000Nishimura-0013
Nishimura-0004Nishimura-0005
Nishimura-0006Nishimura-0007

As it’s a CDRom game, it must use the BIOS function for displaying text. Roger took his old notes and search for any text related routine. Here it is! ex_fnt located at $e060. He draw his favorite emulator and set a breakpoint to it.
debugger00It fires just after the introduction when what seems to be the savegame selection screen appears.

Nishimura-0008debugger01

Roger is not really interested in the ex_fnt routine but more in the calling environment. So he sent breakpoints to the potential rts. A gentle pression to the R key sends him to $f1e2. Another push to the S key brings him the the caller.

debugger02

Here it is at $566b.
debugger03
According to the notes, the shift-jis is loaded from $41, $42 to $f8 and $f9. The logical next step is to put a write breakpoint to $41 and $42.
debugger04
The write breakpoint is triggered at $5b20. The code is pretty simple.

5b1c: ldy #$00
      lda ($3c), Y
5b20: sta $41
      iny
      lda ($3c), Y
      iny
      sta $42

The next step is to search where the $3c pointer. Once again Roger has to set a new write breakpoint at $3c and $3d.

5a18: lda #$90
      bra $5a22
      lda #$40
      bra $5a22
      lda #$80
5a22: sta $c0
5a24: sty $3d
      cpy #$00
      beq $5a2f
      stx $3c
      smb5 $c0
      rts

Unfortunately the caller is not that obvious to discover. But the good news is that the text is not compressed. By keeping track of the values stored at $41 and $42, Roger manages to extract the string

40 81 40 81 40 81 C7 82 CC 82 54 8B E4 88 59 8C 96 8E C5 82 6E 8E DF 82 DC 82 B7 82 A9 82 48 81 0D 00 FF 00

The endiannes must be swapped. He hopefully has some handsome perl script at hand swap_endian.pl. The last 4 characters looks like some control code. His long time experience enables him to deduce that 00 0D is some kind of newline code and 00 FF may be for end of text. Here’s the string.

   どの亀井刑事で始めますか?

But this doesn’t tell Roger where the $3c pointer is set. Roger knows that on CDRom systems, the data is first transfered to RAM before being executed. This means that the code can be self-modifiable. Unluckily this is the case here. Roger has to set a breakpoint where the jump is done and run it until he finally reaches the pointer initialization code. Time passes and he ends up at $724d

724d: ldx #$fc
      ldy #$74
      jmp $5a18

He restarts the game with only the write breakpoint at $41 and $42. As expected he ends up at the same code as before for the first sentence. The next run lands at a different location acb4.

acae: lda $4f
      asl A
      tay
      lda ($50), Y
acb4: sta $41
      iny
      lda ($50), Y
      beq $acd8
      sta $42
      lda $2922
      sta $3f
      lda $2923
      sta $40
      lda #$01
      jsr $5655

The pointer $50 is initialized at ac92.

ac8a: tay
      lda $312a, Y
      asl
ac8e: tay
ac90: lda ($9f), Y
      sta $50
      iny
      lda ($9f), Y
      sta $51

So the pointer is set up from a table. He gets the values

  • b1b6
  • bdb6
  • cdb6
  • d9b6
  • e7b6

Hopefully the indices for this pointers are 2,3,4,5,6. Roger searchs in the iso for b1 b6 bd b6 cb b6 d9 b6 e7 b6. He ends up at the offset $11f49. The place is filled with what looks like pointers. Some bytes after the string he recognises byte swapped shit-jis values. It starts at $11f53. So The pointer soup must contain some value equals to $53Xf. He scrolls up and finds $53af at $11d45. Tadam, he has his first table and string bloc. As all the pointers are in increasing order, Roger deduces that the option strings are stored from $11f53 to $126f4. But just after this, he notices some other shift-jis symbols. It goes from $126f5 to $132f5.
Once the options are display, he jumps to another screen where some grumpy old cop is talking to you.
Nishimura-0009
Once again, he set his breakpoints to $41 and $42. But then he remembers $5a18. It starts with

5a18: lda #$90
      bra $5a22
5a1c: lda #$40
      bra $5a22
5a20: lda #$80
5a22: sta $c0

So he can get here from $5a18, $5a1c or $5a20. This gives him 3 more breakpoints. $5a20 is the one and as expected it comes from RAM initialized code ($59ab). He prepares for a painful ride. And painful it is. Nevertheless, he finds out that that $5a20 is reached from $6c5e.

6c5e: ldy $a5
      lda ($a8), Y
      sta $00
      iny
      lda ($a8), Y
      sta $01
      iny
      sty $a5
      and $00
      cmp #$ff
      beq $6c79
      ldx $00
      ldy $01
      jmp $5a20

The first sentence of the grumpy cop comes from $89c9 (iso offset $1339c9). This address comes from a table pointed by $a8. It’s setup at $707e.

707e: lda #$00
      sta $00
      lda #$80
      sta $01
      lda ($00), Y
      sta $a8
      iny
      lda ($00), Y
      sta $a9
      stz $a5
      stz $a6
      lda #$03
      jmp $619c

The pointer is store at $13308c. Roger is perplex. It’s not just a simple pointer table. There is some extra data. He needs to figure out how it works but he is tired and there’s Night of the Creeps on TV.
He streches, closes the emulator and his laptop and prepares himself for 90 minutes of pure 80’s badassery.

Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

What is 7 + 14 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)