News: 11 March 2016 - Forum Rules

Author Topic: Inindo SNES JRPG ROM/SRAM maps + disassembler  (Read 6017 times)


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Inindo SNES JRPG ROM/SRAM maps + disassembler
« on: September 05, 2018, 12:02:08 am »
I've been working since about 2012 on mapping out the content of the ROM and SRAM for this SNES game.

I wrote a simple tool to display/decode/edit the content and my attempts to decode and understand every last byte are ongoing.

An example of funny stuff I've managed to do when bored:

Exp Needed: 1 good fight

I'll probably get around to adding the maps I have constructed so far to the datacrystal wiki at some point in the near future.

A few images of the tool:

I've improved a lot about the maps/tool recently. I write in c++ and I've been thinking I should create a github account dedicated to the source and utility functions that I can release under simplified BSD or maybe something like WTFPL. Unfortunately I'm not interested in releasing my GUI framework or tons of other code the tool depends upon so the version I release would be greatly simplified. It would be with no real UI but rather just the portions used to actually access or modify the data.

As for why anyone would ever get so mad about hacking a ROM that they spend several years to chew it apart byte by byte? Well I'm sure I have some company here that might understand...

Mostly it's been an on again off again type of project.
« Last Edit: June 04, 2020, 04:36:30 am by Naruse »


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #1 on: September 21, 2018, 12:09:29 pm »
If the wiki were working...

It might be easier to list things I know by simply posting the structures rather than things I don't know.

That said there are a few key elements missing from my maps that I know are missing (known unknowns) and likely far more data remains that is entirely unidentified (unknown unknowns.)

Things I know are missing include:
  • Sprite tables (sprite image offsets, sizes, frames and palettes.)
  • Map tile data including the world map (nihon), dungeons and other locations (tilesets are known.)
  • Overlay mapping data (RAM addresses to which different overlay segments are copied.)
  • A detailed understanding of the VM and sysdep ops the game is implemented with.
  • Complete picture of the disassembly: most core functions are understood but the map is 30% at best.
  • Any knowledge of actual (code) AI routines for NPCs or Daimyos: related data is understood purely through trial & error and is likely incorrect.
  • Many more.

I have seen complete tilemaps + sprite maps for the dungeons in this game which makes me wonder whether this was accomplished through naive brute-force hackery (screen grabs?) or whether someone out there already understands the location and functionality associated with that data.

I have also seen a few very primitive graphical hacks and string hacks (translations or changes) but nothing detailed enough to involve changes to the game code itself.

Like other KOEI titles of the era this game is implemented in a VM (virtual machine) language. The VM seems to match the one used in SNES Gemfire although I haven't studied it in any significant detail yet. Due to this fact I suspect there may be significant overlap in the engine used for other KOEI titles both on SNES and for other platforms.

I have seen some documentation of efforts associated with reversing and disassembling this VM for NES titles but the data provided does not match the SNES VM implementation directly. The ops nonetheless are aligned to the same values: in other words the NES op does not do exactly the same as the SNES op, but both have the same opcode index and both do roughly the same thing just in a different way.

Much of the string processing appears to be accomplished with C language primitives like printf implemented in this VM.

Both the VM implementation and the unique aspects of this game including turn-based NPC AI and Daimyo AI make it an interesting title to examine in detail.

The game itself is not all that special: really standard JRPG fare for the most part although somewhat cutting edge at the time of its initial release. Many risks were taken and not all of them paid off. This title was somewhat of a "flop" due to those elements which were left imperfect upon release.

Being armed with the knowledge of how the game functions internally and which data is at play may potentially improve gameplay for anyone with a deep interest in the game such as speed runners or similar.
« Last Edit: September 21, 2018, 12:19:26 pm by Naruse »


  • Full Member
  • ***
  • Posts: 225
  • Modder
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #2 on: September 22, 2018, 12:36:39 pm »
Great job !, Hope to see more projects for SNES  :thumbsup:


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #3 on: September 23, 2018, 08:11:31 am »
The tool is now available here:
edit: I'll have to include (?) as much source as I can in the next update, anyone with any questions just ask me here.

This will allow you to view (print out) some of the static data from the ROM like the overlay segment index table, spells, monsters, generals and items, item lists (stores) and equipment stats.

It also does a (currently a bit poor) job of displaying all the SRAM data from a save file you load and allows you to edit character stats, province stats (soldiers, gold, rice, ruler) and the game flags and inventories.

There is a lot missing from the GUI like all the additional SRAM data such as bingo chips, pawn contents, party status (members, lineup, status flags), the current game mode, location flags (location of NPCs, whether you've visited a location yet) and a lot more.

It also contains a tile and palette browser as well as a decoded bitmap database. You can export all known bitmaps as .bmp format files but it isn't able to handle managing the palette + tile locations or therefore replacement of bitmaps by importing from file. There are plenty of tools available to do those things though.

So this allows for quite a bit of experimentation and aids in reverse-engineering the ROM and SRAM content.

  • You can print a diff of save 1 & 2 including all data or only data from the unknown arrays.
  • You can set your own player character static "character index" (for Hero) to any available character and play through the game as that character.
  • You can also set the character you replace (Tenkai?) to character index 0 (Hero) and meet Our Hero on his... your... uh... quest...
  • You can save ASAP after game start and set up provinces with varied gold/food/soldiers/arms/training and watch how a different scenario plays out based upon the initial settings.

Not all the parameters are configured to allow editing although you could place every character in the game at the inn in Ise if they were.

Since the tool is so "incomplete" at this point I haven't bothered to include documentation with it. That's because the GUI layout and functions are so likely to change. I'll probably put most of what is in this post as well as a lot more information in the distribution at some point.

A couple more helpful tips (as if the post isn't long enough already):
On the "characters" screen a character's health is displayed as white text if it is full. If not it ranges from green (near full) to red (near zero) to black with a red strike-through (dead = zero.)

Replacement characters (when a character dies) with indices that don't match the initial ones are printed with an underline.

If you kill too many characters the game can crash. It's likely a bug in the character replacement / NPC AI code in the game. Sometimes it will work fine and they'll just end up perma-forever-dead (AKA, dead non-zombies) without a matching replacement character available. It's also possible to bring a previously dead character back to life using this tool, so if you ever trigger an in-game crash you can fix that... if you can figure out how you caused it in the first place.

Since "Ninjutsu training" sets your spell flags rather than naturally earning them as you level up it means playing as other characters either renders this reward useless (warrior) or provides spells at odd times (wizard or sage.)

It is potentially possible that a hack could one day be produced for this title that changes the game from "path of the ninja" to "path of the samurai" where you learn about bushido and sword techniques while attempting to track down and kill off those pesky Iga clansmen who could replace various characters or bosses.

The engine used in the game does have a lot of potential... it would be interesting to create a re-make using a similar engine with modern code / platform too as many games have today. All just a question of interest. Might be different if I started this project in 1995 :)
« Last Edit: May 24, 2020, 12:09:09 am by Naruse »


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #4 on: January 13, 2020, 08:51:21 pm »
I'm replying publicly, this was also posted on and I'll cross-link this thread from there also.

Are you, by chance, familiar with The Birdman? He was a speedrunning of Inindo that discovered some kind of tech or RNG manipulation that led to an insane time.

His video, however, was lost.

He said he was working with someone on stuff for the game and you seem to be active around that same time and the most knowledgeable person I have found. Please, if you could provide any kind of info on someone asking about RNG manip or speedrunning in general, I'd appreciate it!

I'm not sure, but I have written a tool that can provide complete disassembly of the inindo VM used in many Koei games from the period. I also have the diassembly of the RNG, so exploring ways to influence it is possible. It's a standard C-lib RNG in 32-bit using the 32-bit opcodes from the Koei VM.

For example:

I also know a lot about the savegame / SRM / "state" structure which is maintained at 0x7ef000. This matches 100% to the S-RAM, it's simply copied across and the 16-bit checksum is set along with a "KOEI" string and 226 bytes of padding (available space). There is a "start game" state stored in the ROM that is initially copied on a new game by the startup/loader overlay code.

I was recently (last month) working on updating my inindo-edit project that I'd publish all the source-code with. So anyone can use the snes+koei disassembler and all the tools I have available along with the structure of the inindo rom and s-ram.

Here's my S-RAM struct:

Here's the output from the current version of my disassembler and the associated functions/data database:
(seems this isn't secure, so file might disappear again.)
(slightly better solution:)
(updating inindo_edit would be best obviously since it should never disappear, and you could run the disassembler yourself on any version of any compatible rom.)

You'll find the C-lib rand() function at inindo/koeicode/root.asm ln 3094

Code: [Select]
$7e35ca call root.vm_start             ; 20254b
$7e35cf long l = root.rand_state       ; b7102053
$7e35d3 long r = #$41c64e6d            ; b7196d4ec641
$7e35d9 long l *= r                    ; b701
$7e35db long r = #$3039                ; b71939300000
$7e35e1 long l += r                    ; b703
$7e35e3 long root.rand_state = l       ; b7122053
$7e35e7 long l = root.rand_state       ; b7102053
$7e35eb long r = #$10                  ; b71910000000
$7e35f1 long l >>= r (signed)          ; b720
$7e35f3 long no operation              ; b727
$7e35f5 r = #$7fff                     ; 8cff7f
$7e35f8 l &= r                         ; da
$7e35f9 return                         ; cf

$7e35fa call root.vm_start             ; 20254b
$7e35ff l = arg1                       ; 0c
$7e3600 long l = zero extend           ; b726
$7e3602 long root.rand_state = l       ; b7122053
$7e3606 return                         ; cf

$7e3607 call root.vm_start             ; 20254b
$7e360c l = arg1                       ; 0c
$7e360d if (l) goto _l3612             ; d71236
$7e3610 l = #0                         ; 40
$7e3611 return                         ; cf
$7e3612 call root.rand                 ; acca35
$7e3615 r = arg1                       ; 1c
$7e3616 l %= (u)r                      ; ba
$7e3617 return                         ; cf

This is the standard 80s ANSI-C LCG:
Multiplier = 1103515245
Increment = 12345
"ANSI C: Watcom, Digital Mars, CodeWarrior, IBM VisualAge C/C++ [11] C90, C99, C11: Suggestion in the ISO/IEC 9899,[12] C18"

From C89:
Code: [Select]
        static unsigned long int next = 1;

         int rand(void)   /* RAND_MAX assumed to be 32767 */
                  next = next * 1103515245 + 12345;
                  return (unsigned int)(next/65536) % 32768;

         void srand(unsigned int seed)
                  next = seed;

So it is easy to conclude that Koei's C-runtime implementation is based upon a reference implementation of ANSI C89 / ISO C90.

Although it's somewhat less on-topic to this thread I suppose I should also answer the other bit of the question too.

To "Professor_Palmer";

Regarding your question about "someone asking about", as I said I'm not sure. I have had a couple offers of help however and I have shared my sourcecode with one person.

That person also mentioned this "The Birdman" individual and the existing speedrun or something very similar. The name seems oddly familiar to me although I can't put my finger on it exactly.

They were working on their own disassembly, mostly on other Koei games and although I did share my source at the time with them I have sincere doubts it would have amounted to much of any sort of significance in such a short length of time.

The same work I've done has been done by others in the past. I'm really only repeating and "reinventing the wheel" because that earlier work was never published in an accessible format. I believe the earlier works date back to around 2008 where a nearly full reversing was completed by someone who was even able to render complete dungeon and other maps including items and additional data, as far as I can tell 100% complete in scope.

I believe that my implementation is superior in several ways, although suffers from several major gaps that were filled by the other work. I suppose this is due to different aims and focus.

So that's a very wordy way to state that quite simply there is a lot of reason to believe that there are dozens or many more people with all the ability required to produce such things. I also believe with some certainty that existing unpublished work remains discarded by the authors and buried, and may never be dug up again by anyone.

So to track down a particular author who wishes to remain anonymous, or any specific details of their particular work or knowledge without them having published it themselves, voluntarily, is quite simply impossible.
« Last Edit: May 26, 2020, 04:55:34 pm by Naruse »


  • Jr. Member
  • **
  • Posts: 3
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #5 on: February 21, 2020, 09:57:21 am »
 I'm from Brazil and I made a modified version of this game for Portuguese where I made some changes to the game.
 I was interested in your project.
 How I can help you in this project?


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps/tools
« Reply #6 on: May 23, 2020, 02:19:13 pm »
I'm from Brazil and I made a modified version of this game for Portuguese where I made some changes to the game.
 I was interested in your project.
 How I can help you in this project?

I'm not sure how you might help with direct involvement, but I've seen your modifications (including tiles and palettes! not sure if that was yours?) and I wish I'd been able to provide the data I have now back then!

You can get a rough idea of part of what I know from this (partial) map:
The readout from the disassembler which uses the above map:
For translations, knowing the format of text and indexes is important:
For anyone wanting to modify the tile data, "tile segment" map:
Here's the current version of my state/SRAM map with some progress:
I'm sure you have similar maps already.

What I'm aiming to do is write fully automated general-purpose tools that can perform the disassembly and other processes required to allow for study of the data.

At first I'd started out by writing the inindo_edit tool to replace a hex editor... and that grew eventually to the point where I'd need to use the bitmap data to display the map of japan on the provinces page. That threw me into a frustrating catch-22 situation with regard to copyright and derivatives vs. educational "fair use". I've mentioned already that all the work I'm doing is "reinventing the wheel" and recreating ancient artifacts that have been buried due to these copyright issues.

I have no doubt that there is a limit to fair use as there is a limit to documentation of observable fact for the purpose of education and commentary vs. the point where that documentation is itself a derivative work. Walking this fine line with our laws the way they are currently is precarious: an author relies upon good faith.

So that is really the core issue at play and the source of great headaches and burdens associated with these sorts of things. Ultimately as we've seen numerous times in the past (with Inindo for SNES!) previous authors have simply given up and walked away from anything remotely related to the games or their authors and descendant works under undue and unreasonable threats.

May 25, 2020, 09:28:21 pm - (Auto Merged - Double Posts are not allowed before 7 days.)
With the r4 update of the tool, some real help is now possible. There is a large amount of reversing effort that still needs to be undertaken to map out all the data. I've felt hesitant to include my c++ source files containing partially completed maps both for reasons of copyright concern (what's documentation vs. derivative?) and primarily due to their incomplete nature (I'd like to avoid leaving a trail of moldy breadcrumbs for future delvers.)

You can grab the tool and use it on your data to produce the disassembly outputs. From there you can look into filling in gaps regarding unnamed or unknown functions and data addresses or correct mistakes that I've made in my existing work.

I've also included the sourcecode of my disassemblers albeit in a somewhat impractical format to put to immediate use. Anyone with a fascination for any of the VM games can test using the disassemblers on those games, and I'd be happy to help with getting you started. To my knowledge Gemfire is essentially a duplicate of Inindo with the data and code replaced... so it follows a near identical structure. If the inindo_edit tool didn't have such strict safety checks it could also print several parts of the data from Gemfire. There are also likely to be other games with which it is compatible, and a whole group of games including NES and SNES titles worked based upon the same or similar virtual machine and data structures.

Without a fully populated disassembler database or full sourcecode I'm still limited to doing most of the work on my own. The reversing however is a hefty part of that, so if anyone would like to take up something like the castle "k" AI segment or similar that would be great as it would save me having to do so on my own. Since I do have data associated with each segment, be sure to ask me about it and I'm comfortable to post that soon-to-be-obsolete code on pastebin. There, it's safe to say it likely won't be dug up and lead people in the wrong directions ten years or more from now.

Remember the eventual goal is to automatically generate a majority of this data using generic methods. My string searching tool can already handle (?) almost all of the known string data, so simply knowing where the start and end of a block of text exists is enough to automatically generate the C-struct sourcecode.

Damn, I forgot to put this handy file in: Notepad++ SNES Koei VM "pretty" UDL

"Pretty" is in quotes for a whole rainbow of reasons:
« Last Edit: May 25, 2020, 10:03:40 pm by Naruse »


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #7 on: June 04, 2020, 04:58:40 am »
I recall posts from many years ago from several sources:
1) someone had written code for image decompression from SNES and other games (inindo + gemfire images)
2) someone had written a disassembler and reverse engineered the map data format including item locations (published full level maps as PNG + additional annotation)
3) someone posted somewhere about their experience on the Koei US team who claimed to be the author of the compression routine and responsible for packing the bitmap data (?) in a thread about unique data compression algorithms

#1: I've reproduced this work already.
The bitmaps are really only interesting to me from a functional perspective. Note that there are possibly hidden bitmaps packed between other chunks of data. This is one reason I've been working to reach 100% data identification. The large "unk" chunks in the tile segment may be level data, arrays of structures such as sprite data + tilemaps or something else (compressed images or other data?) I know the goal was to reach <8mbit and it's entirely possible due to this goal being met without compressing further chunks that they either simply gave up or had some fun with spare bytes :)

#2: I've also reproduced a functional disassembler.
I haven't finished reverse engineering all these areas (no level data, sprite tables, background tile maps, ...) Many of the comments I vaguely recall reading about the code back then now are finally beginning to make sense while reading the disassembly.

#3: The algorithm is a simple LZ77 designed for fast in-place decompression requiring no additional memory and using tables to reduce code size (?). It looks as if the algorithm was written directly into the space from the Japanese ROM where the breakpoint function existed. This is based upon the debug break vector pointing mid-instruction in the compression algorithm. Some of the data in the tables appears unused and may be experimental or for future application (features of the compressor that were not used on the Inindo tile data.)

I'm almost sure I'm mixing things up and I have no specific idea of the exact time frames involved. Digging up some of this stuff has become difficult as large portions of it were deleted at the source and ceased being published. I don't have much experience as a digital archeologist, so help digging would be great! Additional information from ancient sages would also be much appreciated.

A bit more work reading strings directly from the ROM (some remain hard-coded):

I've improved the function references output a bit:

Code: [Select]
009afe bios.ovl_1e00_1()
    009a7a bios.so1b_ovl_memcpy_1()
    009a92 bios.so1c_ovl_memcpy_2()
    009ab1 bios.so1d_ovl_load_pal()
    009ac0 bios.so1e_ovl_vw01b()
    009ad3 bios.so1f_ovl_so09_mul()
    009ae4 bios.so20_ovl_so0b()
    009af3 bios.so21_ovl_so0d()
    009f3e bios.so2c_alloc_dma2a_ovl9()
    009f4d bios.so2d_alloc_dma2b()
    00b11c bios.so33()
    00b203 bios.so35()
    00b367 bios.so36()
    00b5a9 bios.so43()
    00b769 bios.so40_audio5()
009b3d bios.get_free_1e00_slot()
    0099c8 bios.so18_allocate_ovl()

I've always thought the table at 1e00 looked a lot like a malloc()/free() heap table... Getting the bios code cleared up and understood in more detail should make understanding the game implementation much easier. The v_write/vw functions will probably help a lot to get many of the sysops named accurately. For example:

Code: [Select]
008cba bios.VRAM_write_6()
009323 bios.so0b_load_tiles()
00935f bios.so0c_vw681()
00938a bios.so0d_load_tiles_b()
0093b5 bios.so0e_vw681b()

It might be interesting to take this output and render it as a call tree in some way... I've never found such visualizations help me much to understand code although a lot of people like them.

I have a lot of speculation and info scattered about, a lot of it I'm not certain so I generally prefer not to include any of that stuff. Some though I've been trying to copy across into the published documents and outputs:
Code: [Select]
        i - initialization
        m - main game
                mm - indoors - mon masu
                n - outdoor - nihon
                d - dungeon
                s - combat - sentou
                        sen 1
                        sen 2
                                ssen 1, 2, 3
                        sen 3
                        sssen 1, 2, 3, 4, 5
                k - end of month - k/castle
                j - ? - ?
                km - conversation text - kaiwa machi
                c - menus - control
                cs - use item
                a - ai
        h - warfare - heika
                        h i, e, u, c, k
        opening, ending

A great example of that speculation is trying to work out what "mm" means in English, Japanese or any language at all if any.

mm - monma: 間門
= passing into the gateway?

Then I'm not sure where I got "su" from as I didn't write any notes... but possibly:

ms - monma sakui: 門間作為
= indoor action/commit/intent?

km - kaiwa machi: 会話町
= town conversation?

I should really look into the j- segments/overlays more because having zero clue what "j" means has been bugging me for years.
« Last Edit: June 08, 2020, 03:29:48 am by Naruse »


  • Jr. Member
  • **
  • Posts: 3
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #8 on: July 03, 2020, 01:04:28 pm »
Congratulations on your work, I know it is not a very easy task. I don't have much experience in programming, I only do small modifications and by pure coincidence I had the opportunity to play Inindo when I was younger and I was enchanted with this game, and as I had knowledge of basic tools I realized a great dream that was to make the translation of this game into my language, which is Portuguese. During this process I discovered that it was possible to give a slightly different look to the game and then decided to make changes to the graphics, which is also something very simple but that makes a big difference since games from that time used palette changes to create new characters and if I changed the palettes I would create not a new character, but all of them. And today analyzing the possibilities we can see that this game serves as a great study because using reverse engineering would give the possibility to recreate events, change attributes of items creating even new items, Insert the two songs taken from the Japanese version in the American conversion of the game, and also another event removed is the one present in the version of Pc 98 that involves that girls' house, and the most impressive is to create a tool that could randomize the whole game, with the possibility of customizing this randomizer by exchanging enemy dops, items in stores, changing the location of enemies and characters among other options, as well as in games like Zelda Alttp and Super Metroid for example, creating a new experience with each new gameplay. Or maybe something more greedy like a remake of that game, have you thought about that possibility?


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #9 on: July 27, 2020, 11:11:54 am »
... various customizations or additions ... Or maybe something more greedy like a remake of that game, have you thought about that possibility?

Yes, since the game is segment-based as it is and relies upon dynamic loading of segments a complete reverse would allow a disassembly to the point where the assembly could be re-assembled and functional. From there a de-compilation would be possible to generate C source code. A major problem in making modifications is adapting the size of the various segments. Some space in RAM remains unused but it would require an extremely detailed analysis to safely expand the size of an overlay chunk to add items, monsters or similar.

The purpose of the virtual machine used by Koei seems to have been to allow the games to be written in C language and portable across a variety of targets. This is much like the original intention for the Java language - once the bios and virtual machine are ported any game should be able to be compiled for the target platform. A second reason appears to have been to minimize the size of the machine language code generated from the source. The virtual machine is the main reason Inindo runs so slowly on SNES - it doesn't even use fastrom and not only uses a virtual machine, but executes from chunks dynamically copied to RAM!

One possible goal might be to attempt to modify the code to allow it to execute directly from ROM using long/far addressing where required. Without a functional disassembly or de-compilation however this work is at an extreme level of difficulty and extremely labor intensive. Unfortunately all of these customizations beyond minor in-place edits are far beyond my level of skill or interest.

I forgot to mention: one tile-based mod I've made is to replace the tea logo with the sakaya (brewery / pub / liquor store.) That was one of the censorship modifications made for the US version - the usual removal of sexual / alcohol / drug references found offensive by US culture - gratuitous psychopathic violence is a core value! Chop them to bits, just don't kiss anyone!

(That information along with much of the other info I'm aware of from memory was I believe published by the author of the dungeon and map of japan .PNG files.)

re: updates - the "j" segment is "mapping", I believe "Jo" = "castle" although the same code is I think used for dungeons, castles/towns and the overworld. I finally recall reading about this segment which I believe was the primary target of the author of the older disassembler + maps. I've primarily focused on other parts such as mapping out all data in the ROM + SRAM and the ability to decode it.
« Last Edit: July 27, 2020, 11:33:05 am by Naruse »


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #10 on: August 04, 2020, 12:24:25 am »
Insert the two songs taken from the Japanese version in the American conversion of the game,

With regard to this I've identified the code and data associated with the SPC:
Code: [Select]
$7e4a9f call root.vm_start             ; 20254b
$7e4aa4 push #$22 (set_screen_1)       ; 8d22
$7e4aa6 call root.sysop +2             ; e9294b02
$7e4aaa push root.spc[2].page          ; aa6a5b
$7e4aad push root.spc[2].addr          ; aa6c5b
$7e4ab0 push #$41 (audio6)             ; 8d41
$7e4ab2 call root.sysop +6             ; e9294b06
$7e4ab6 push #$23 (set_screen_2)       ; 8d23
$7e4ab8 call root.sysop +2             ; e9294b02
$7e4abc push #1                        ; 61
$7e4abd call root.so10n +2             ; e9b44402
$7e4ac1 push #$22 (set_screen_1)       ; 8d22
$7e4ac3 call root.sysop +2             ; e9294b02
$7e4ac7 push root.spc[1].page          ; aa665b
$7e4aca push root.spc[1].addr          ; aa685b
$7e4acd push #$41 (audio6)             ; 8d41
$7e4acf call root.sysop +6             ; e9294b06
$7e4ad3 push #$23 (set_screen_2)       ; 8d23
$7e4ad5 call root.sysop +2             ; e9294b02
$7e4ad9 return                         ; cf

$7e4ada call root.vm_start             ; 20254b
$7e4adf push #$22 (set_screen_1)       ; 8d22
$7e4ae1 call root.sysop +2             ; e9294b02
$7e4ae5 l = (u8)FP.arg1                ; a00b00
$7e4ae8 switch (contiguous)            ; d500000400034bf74a0a4b134b1c4b
; 0 to 3, 4 cases: 0=$4af7 1=$4b0a 2=$4b13 3=$4b1c default=$4b03
case 0:
$7e4af7 push root.spc[0].page          ; aa625b
$7e4afa push root.spc[0].addr          ; aa645b
$7e4afd push #$41 (audio6)             ; 8d41
$7e4aff call root.sysop +6             ; e9294b06
$7e4b03 push #$23 (set_screen_2)       ; 8d23
$7e4b05 call root.sysop +2             ; e9294b02
$7e4b09 return                         ; cf
case 1:
$7e4b0a push root.spc[4].page          ; aa725b
$7e4b0d push root.spc[4].addr          ; aa745b
$7e4b10 goto _l4afd                    ; d6fd4a
case 2:
$7e4b13 push root.spc[5].page          ; aa765b
$7e4b16 push root.spc[5].addr          ; aa785b
$7e4b19 goto _l4afd                    ; d6fd4a
case 3:
$7e4b1c push root.spc[3].page          ; aa6e5b
$7e4b1f push root.spc[3].addr          ; aa705b
$7e4b22 goto _l4afd                    ; d6fd4a

The SPC data block index/pointers appear at the very end of the root segment. The SPC blocks themselves were as I thought part of the unidentified data in the tile data region:
Code: [Select]
// SPC data blocks
// 10/8000 (80000)
u08 spc0[18454];
// 10/c816 (84816)
u08 spc1[36396];
// 11/d642 (8d642)
u08 spc2[3082];
// 11/e24c (8e24c)
u08 spc3[6286];
// 11/fada (8fada)
u08 spc4[12208];
// 12/aa8a (92a8a)
u08 spc5[9992];

So it should be possible to expand the ROM and fit the additional data blocks at higher addresses beyond the 8 mbit mark 20/8000 (>ffffff) and then expand the root.set_scene# functions and index array to transmit the data to the sound controller.

This all involves the usual hackery to try to shift functions around and fit data within the bounds of the existing segments. I'd assume it would be quite difficult, but it should be possible to use some SMC (self-modifying code) hacks to simply re-write the target address dynamically at the caller and eliminate the switch statement entirely (for example using some available 32-bit word in the low memory region). Sysop $41 "audio6" seems to be "transmit SPC data block".

I've finally named every function in the root segment and although there are many functions that aren't clearly identified or "correctly named" I'm hoping this should speed up the reversing process quite a lot. Someone with more familiarity with the C standard library should probably be able to notice my errors in "root" and fix them trivially.


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #11 on: August 27, 2020, 06:04:16 pm »
I should upload a new version of the disassembler that includes a few improvements I've made. It now supports passing configuration/flags in the database ("pretty" output mode, local/absolute address, hex dump) and I've added the ability to output c-strings where data is marked as char type vs. u/i08/16.

I've also implemented some improvements to function reference tracing. Ideally it would be possible to check the last pushed stack value for sysop calls and alias those as "virtual" indexed sysops rather than references to the sysop dispatch function. It's still nice with the improvements I've made seeing exactly what calls what, excluding sysop references.

For example:
Code: [Select]
data {
7e8800 = code;
7ea8c3 = native;
7ea8c3 = unk1;
7eaaf0 = unk1b;
7eaaf8 = unk1c;
7eab00 = unk1d;
7eab08 = u08,music_index;
7eab1a = u08,gameflag_index;
7eab2b = char,opened_treasure;
7eab4a = char,already_taken;
7eab6d = char,found_the;
7eab80 = char,is_inside;
7eab8f = char,was_taken;
7eab9d = char,didnt_open;
7eabb3 = char,opened_door;
7eabc7 = unk3;
7eacb6 = unk4a;
7eacc8 = unk4b;
7eace8 = char,nothing_happened;
7eacfa = unk5;

7eb022 = u08,e_current_dungeon;

Regarding "missing", music: it seems the table in dovl ("music_index") contains 0x09 for all dungeons except 0x11 = 0x0a. These are sub-song indices into SPC data chunks ("modules") that are loaded in addition to the initial bios/player code. Some sub-songs in some chunks/banks/modules contain references to samples that don't exist; in which case the plain square wave is used.

Code: [Select]
// SPC data blocks
// 10/8000 (80000)
// main game sequences (21 sub-songs 0x01 to 0x15)
u08 spc0[18454];
// 10/c816 (84816)
u08 spc1[36396];
// 11/d642 (8d642)
// initial spc "bios" program (?)
u08 spc2[3082];
// 11/e24c (8e24c)
// unused, 2 sub-songs (?)
u08 spc3[6286];
// 11/fada (8fada)
// battle
u08 spc4[12208];
// 12/aa8a (92a8a)
// opening, (at least 4 subsongs 0x01 to 0x05 ... ?)
u08 spc5[9992];

So since we know there are 21 sub-songs (zero is "stop") from 0x01 to 0x15 in the main game SPC bank, we can select any song to play for any dungeon by simply changing the entries in the dungeon song index table.

The table is at absolute 0x22604 with 18 bytes. I haven't worked out how the dovl functions translate from the apparently 0 to 31 dungeon index to the table index so you'll need to experiment to find out. All the music does apparently exist unmodified in the US translation. At worst it seems some of the sample content has been trimmed from sub-songs that were unused in both games. I'd need to double check the Japanese version to verify whether any are actually removed rather than simply unused.

This was easy to work out once I identified each of the audio sysops:
Code: [Select]
b646 = long_ptr_increment;
b65e = audio_send;
b71f = so3c_audio_play;
b731 = so3d_audio_clear;
b740 = so3e_audio_get_status;
b74d = so3f_audio_sfx;
b765 = so40_audio_loadbank_indirect;
b770 = so41_audio_loadbank;

Here's an updated map, although note it may not work correctly in the previous version.

More info by guessing on what the main bank songs are:
Code: [Select]
enum class main_songs_t
new_game = 0x01,
danger1l = 0x02,
danger2l = 0x03,
victory1 = 0x04,
weird1l  = 0x05,
death    = 0x06,
victory2 = 0x07,
weird2   = 0x08,
dungeon1 = 0x09,
azuchi1  = 0x0a,
ferry    = 0x0b,
indoors  = 0x0c,
castle   = 0x0d,
bingo    = 0x0e,
nihon1   = 0x0f,
nihon2   = 0x10,
nihon3   = 0x11,
town     = 0x12,
village  = 0x13,
training = 0x14,
azuchi2  = 0x15,

enum class battle_songs_t
victory1 = 0x01,
victory2 = 0x02,
defeat1  = 0x03,
combat0  = 0x04,
combat1  = 0x05,
combat2  = 0x06,
combat3  = 0x07,
battle1  = 0x08,
defeat   = 0x09,
twotones = 0x0a,

enum class opening_songs_t
title  = 0x01,
attack = 0x02,
iga    = 0x03,
unk1   = 0x04,
alarm  = 0x05,

I suspect "weird2 = 0x08" might be the tengu forest main, with "weird_loop1 = 0x05" being the tengu encounter/cutscene music. I'm not sure if "happy = 0x0b" is used but I think nihon1 to 3 are all used for overworld travel during different parts of the game.

I'll need to trudge through all the sequences and try to track down where they're used if anywhere. It would be nice to dump the output using an SPC replayer and upload all the tracks. Some people more familiar with the game might be able to help identify all the songs.

I still need to identify the sub-songs from chunks spc1, 2 and 3, although I suspect 2 is the loader code and shouldn't do anything when told to play. It seems 3 has at least two sub-songs and I haven't experimented with loading 1 yet.

More music stuff from novl, the "overworld" segment:
Code: [Select]
$7e968c call root.vm_start             ; 20254b
$7e9691 l = (u8)state.flag10hiei       ; a50eff
$7e9694 r = #2                         ; 52
$7e9695 l != r                         ; c1
$7e9696 if (!l) goto _lendgame         ; d8aa96
$7e9699 l = (u8)    ; a51df0
$7e969c r = #1                         ; 51
$7e969d l == r                         ; c0
$7e969e if (!l) goto _ltogether        ; d8a596
$7e96a1 l = #15                        ; 4f
$7e96a2 goto _lplaymusic               ; d6ac96
$7e96a5 l = #$10                       ; 8910
$7e96a7 goto _lplaymusic               ; d6ac96
$7e96aa l = #$11                       ; 8911
$7e96ac push l                         ; b3
$7e96ad call root.aud_playsong +2      ; e9563602
$7e96b1 return                         ; cf

We can see the code in pseudo-C would be:
Code: [Select]
int song_index;
if (state.flag10hiei == 2) {
// endgame
song_index = 0x11;
} else if ( != 1) {
// traveling together
song_index = 0x10;
} else {
// alone
song_index = 0x0f;

So that verifies how the overworld music tracks are selected.

Or this one is cool too. This demonstrates how things start to come together very quickly once a certain percentage of the binary is understood. Rather than stumbling blind in the dark it's more like spotting black patches in a field of white and gray.

Code: [Select]
$01f52c $7e94cc call root.vm_start            ; 20254b
SP += 2
$01f531 $7e94d1 l = #0                        ; 40
$01f532 $7e94d2 var1 = l                      ; 2b
$01f533 $7e94d3 push #7                       ; 67
$01f534 $7e94d4 call root.aud_sfx_play +2     ; e9a23602
$01f538 $7e94d8 push #3                       ; 63
$01f539 $7e94d9 call root.sleep +2            ; e9b44402
$01f53d $7e94dd push #0                       ; 60
$01f53e $7e94de call root.aud_sfx_stop +2     ; e9b23602
$01f542 $7e94e2 l = var1                      ; 0b
$01f543 $7e94e3 l++                           ; d0
$01f544 $7e94e4 var1 = l                      ; 2b
$01f545 $7e94e5 l = var1                      ; 0b
$01f546 $7e94e6 r = #4                        ; 54
$01f547 $7e94e7 l < r (u)                     ; c6
$01f548 $7e94e8 if (l) goto _l94d3            ; d7d394
$01f54b $7e94eb return

This function plays the enter/exit sound effect one or more times. Pseudo-C:
Code: [Select]
for (int i = 0; i < 4; i++) {

Since the "4" threshold is copied with the $5x instruction which takes a nibble (4bit) sized immediate value defined according to the opcode; we can change this function to make it play the sound anywhere between one and fifteen times.

RAM: 7e94e654
ROM: 01f54654
label: "novl: number of times to play enter/exit sound"

The other instances of the $6x instruction push the argument for the functions. So we can change the sound played (anything equal or below #15) and the NMI sleep period between them. In addition the sfx_stop() is passed an argument although it never uses it. That may have been accidental and didn't create a bug since in this VM it's legal to pass up to the max number of function arguments whether they're used or not.
« Last Edit: September 02, 2020, 03:21:36 am by Naruse »


  • Jr. Member
  • **
  • Posts: 11
    • View Profile
Re: Inindo SNES JRPG ROM/SRAM maps + disassembler
« Reply #12 on: September 12, 2020, 01:42:40 pm »

I've identified the format of the generals list. I've also found a character "sex/sexuality" using two bits in the SRAM character state data. In the screenshot you can see I've added an overlay display of daimyo actions and a readout for the generals list.

The list is a fixed hierarchy. Each general has an index in the array (104) which holds the subordinate, or -1 (255) for none to end the list. The province state contains the general (daimyo) in charge.

So say you have Mikawa = Tokugawa Ieyasu (8). Then at subordinate[8] = Ishikawa Kazumasa (54) and so on until subordinate[101] = -1.

In C:
Code: [Select]
int generals = 0;
for (int general = province[].daimyo; general != 255; general = subordinate[general]) {

I'm hoping that as I look further into the "k" segment (province AI?) I may be able to identify some of the last remaining large chunk of 828 unknown SRAM bytes. "k" may be 国王 "kokuou" = "region/country's ruler" = "king".