This part was mostly done by those who started the project before I joined. Using a program called Atlas, they had written scripts to extract and insert all the cutscene text an a major portion of the text in the main executable. Building on that, I was able to extract most of the rest of the text from the executable and the file with the monster data. There were a few lines in the map data files as well for the name of each floor, but they were in a weird format so I just edited them by hand.
The game's executable is an LZ91 self-decompressing exe, which is not unusual but the commonly found version of UNLZEXE would not decompress it. Thankfully, Esperknight had previously found an updated version of the program that worked to give an uncompressed version we could edit. The uncompressed version would load identically to the compressed one, but would freeze at the start. I went back to the original file and found that after decompressing and updating the segments on all the far jumps, there is one additional jump that it changes that does not get changed in the uncompressed version. Once I was aware of that, I just changed it manually in the uncompressed version to the correct address.
The game actually already has separate subroutines for full-width and half-width text output, and since they take the same parameters it is simply a matter of changing the calls from one to the other. The full-width routine supports the PC-98's mirrored half-width Shift-JIS at 8540h, so I kept that in use for the cutscenes and lines that needed to use certain characters like the heart or the pound sign and just hacked it to only advance a half-space. Since that kept the cutscene scripts at 2 bytes per character, they ended up overflowing the alotted space of 1C00h bytes, requiring me to work out a method of splitting them and calling the split script from inside the cutscene. There are also two lines in SLR.EXE that combined both text and the pound sign, so I had to keep that text full-width and manually set it to mirror half-width after insertion.
The data for the cutscenes are split into two files apiece. Sxxx contains the scripting for the cutscene, while Xxxx contains the actual dialog. Each dialog character is stored as (JIS-2000h) in LSB order. Since the translated dialog often went beyond the 1C00h byte limit for what the game loads, I had to split the larger files in two. I first identified which line of dialog in the Xxxx file crosses the boundary line, then found the command that points to that line in the Sxxx script. I then had to make sure there werent any jumps in the script that crossed that point. If there were, I had to track back until I found a point in the script that worked. I wrote a program to automatically split the files at the given point and update any pointers automatically. There were a few scripts that did not have a valid split point, such as one with a jump at the very beginning to a subroutine at the very end, so for those I manually copied those sections from one to the other and adjusted the pointers by hand. Once I had the split files, I took one of the unused script codes and wrote an ASM hack to have it take the parameters and load the next script file.
This file contains the data for all the monster encounters in the game, for some reason, it stores the monster names, and just the names, with the bytes inverted. Thankfully, I was able to make a table for Atlas to automatically flip those on extraction/insertion.
M1.SLR - M4.SLR contain the dungeon map data for each chapter, including the floor names displayed. The names are Shift-JIS characters in LSB format and get flipped when the game reads them. I could replace them with single-byte ASCII, but it still gets loaded two bytes at a time and flipped, so I had to format them accordingly, so "Dungeon1" is stored as "uDgnoe1n". Since there were just 4 files with 5-8 names apiece, I went ahead and edited them by hand.
Details on the AIC image format can be found on Kirinn's github.
The original game has the credits hard coded in the executable, but I had the idea to try putting it in a cutscene file instead to free up extra space that could be used for text that overflows its original memory area. Since the credits show immediately after the final cutscene, X700.SLR remains loaded in memory. I was able to tack the cutscene data on to the end of the file, and then just updated the pointers to the new location.
Secret Shop: In searching through the game's data, I came across text for an additional shop that is not accessible in the released game. Called the すけべぇ屋, or "Adult Shop", it appears to be fully functional apart from missing graphics, but the coordinates to click on it are set beyond the bounds of the screen. I assume is that it was intended to be the building in the upper right of the town screen, around coordinates (240-280,90-c0). In the shop, you can speak to two ladies in racy outfits, Stella and Dorothy. The ladies are missing dialog portraits, and when you speak to one of them they remark on Gourry's attractiveness. Then it tries two display one of two CGs depending on who you spoke two, U98.AIC or U99.AIC. These files are not present on the disk, but the "U" prefix matches that of the hot spring images, so given that and the context these would probably be suggestive or pinup images of Stella and Dorothy. After that, Gourry panics and runs away with Lina chasing after. The 3rd party member makes a comment and leaves the party, and the ladies shout that Gourry has dropped his wallet, but he is already gone and the party's money is reduced to 0.
Bugged Cutscene 180: One of the cutscene scripts appears to contain mistakes in the original. It's possible this is due to a bad dump, but I checked a couple different sets of disk images and they all appear to have it. In cutscene script S180, Naga repeats the same line in three different places, and there are unused lines that seem to fit where the second and third ones are used. Line 0Eh (0c 08 0e) is called at 86h, 114h, 180h. The command at 114h should address unused line 2Ch. The command at 180h would point to unused line 3Dh, and should then be followed by unused line 3Eh, probably with Lina as the speaker. I have corrected this in the translated script.
Strange Cutscene S361: This cutscene is an oddity. It occurs immediately following the previous cutscene, but adds nothing to the story, consisting of a single image of characters staring blankly at each other with lines of "....." before fading to white. The script has several subroutines to adjust the palette, but only the very last one is used. The unused ones would have gradually faded the palette to white, but in the end this is accomplished with a single script command. Those leftover commands and the seeming pointlessness and oddity of the scene itself give me the feeling that this is a placeholder cutscene with leftover scripting code from before the single-command fade-to-white was implemented, and possibly wasn't even intended to be kept in the final game.
Bytecode | Parameters | Function | Notes |
---|---|---|---|
01 | xx 00 | Load music | load music file MUS\K(xx/0a)(xx%0a).M |
02 | xx | Play/stop music? | |
03 | xx | ? | |
04 | xx | Play SFX | Plays sound effect xx. |
05 | rr gg bb xx | Flash effect | Strobes palette slot xx to color rr gg bb |
06 | (xx yy) 6 times | Party check jump | Checks the current 3rd party member and jumps to the address in the parameters at (character ID-2). If there is no party member in the 3rd slot, it skips to the next command in the script. |
07 | none | Blank | |
08 | xx | Load image | Display image V1-x0. |
09 | xx yy | Jump | Jumps to xxyy in the script. |
0A | ? | ? | |
0B | xx yy aa bb | Gosub | Sets the return address at [bp-4] to aabb before jumping to xxyy |
0C | xx yy | Text | Displays dialog line yy with speaker nametag xx. Text does not autowrap, but does automatically break for input after 4 lines |
0D | none | Fade to white | |
0E | none | Fade to black | |
0F | xx rr gg bb | Set palette | Sets the color in palette slot xx to rr gg bb. |
10 | none | Blank | |
11 | xx | Transition image | Fade in V1-x0.AIC on top of the current image. |
12 | xx yy | Screen shake | |
13 | none | Blank | |
14 | none | Return | Jumps to the position set in [bp-4] by the 0B Gosub command. |
15 | none | Blank | |
16 | xx yy | Wait | Waits for xxyy frames before continuing to the next command. |
17 | none/0x 0y 0z | Blank/Load script | Originally a blank command. This code is repurposed in the patch to load a script file. It take 3 parameters, 0x, 0y, and 0z, loads files S/Xxyz.SLR, then jumps to the start of the script. |
18 | none | Special blank | Linked to the same address as the other blank commands, but 18 is checked for specifically for some reason. |
19 | xx | End cutscene | Ends the cutscene, leaving xx. |
00 = Lina
01 = Gourry
02 = Zelgadis
03 = Amelia
04 = Sylphiel
05 = Philionel
06 = Mina
07 = Lemmy
08 = Naga
09 = Kim
0A = Marine
0B = Junon
0C = Lass
non party members
0D = Gambling Den Lady
0E = Skeleton
0F = Lesser Demon
10 = Mysterious Girl
11 = Beastman Menuba
12 = Beastman Menukoo
13 = Host
14 = Audience
15 = Lundgren
16 =
17 = Bandit A
18 = Bandit B
19 = Bandits A & B
1A = Golem
1B = Lizardman A
1C = Lizardman B
1D = King Lizard
1E = Villager A
1F = Villager B
20 = Villager C
21 = Hermit
22 = Everyone
23 = Count
24 = Lina & Naga
25 = Gourry & Junon
26 = Lina & Lass
27 = Mine Worker
28 = Naga & Lass
29 = Monster
2A = Cultist A
2B = Cultist B
2C = Cultist C
2D = Man of 1,000 Names