Guynarock II Technical Data: SGS.COM v.1.00

Half-Width Text Hack

Since SGS uses the PC-98's Text VRAM to display text, the text is stored in JIS x 0208 rather than the more common Shift-JIS format. This means the it won't be readable as you'd expect in the debug window or most hex editors. While this format has encoding for half-width roman and kana characters, the text display subroutines in the engine were written assuming it will only be using full-width characters, grabbing each one from memory two bytes at a time. With a little assembly hacking, we can reverse those assumptions so that it outputs each byte as a single half-width character. This means we can feed it plain ASCII taking up half the space, and since it is the output step we are changing it can still interpret the two-byte control codes in the text. Of course, this makes full width and half-width mutually exclusive, and since it is still reading the script two bytes at a time, length and control code placement have to be aligned on even numbered bytes.

The ASM code in the original SGS.COM is as follows:

      2c20        sub al,20
      247f        and al,7f
      ab          stosw
      0c80        or al,80
      ab          stosw
This code is repeated in three places: 3FA (to display menu text), 4CF(to display dialog text), and EE0 (the wait prompt). Replace the first two instances with the following for half-width text:

      aa          stosb
      32c0        xor al,al
      aa          stosb
      86e0        xchg al,ah
      ab          stosw
      90          nop

Game Files

Guynarock II uses version 1.00 of Silence/Sogna's SGS game engine. SGS.COM is the main executable, with the games script stored in .SIL files, music as .M, and images and animations compressed to their proprietary SZH format. The three save files do not have a file extension. When the BAT file is ran, PMD.COM is ran first to load the music drivers, then ESET.COM is ran on the file GASE.EFC to load the sfx into memory, then SGS.COM is launched.

SIL format

The SIL files contain a series of opcode instructions interpreted by the engine and comprise all the text and game logic. In order to extract the text and insert the translation, it was not necessary to figure out exactly what each code does. I simply needed to know the size of each command and its parameters to be able to divide them up, and then I could focus on the instructions directly related to text and pointers.

Opcode Parameters Function Notes
00 ** Menu block Displays the menu. See the Menu Format section for details.
01 xx yy aa bb Set menu parameters Set the menu text parameters at [18db]. The menu coordinates column xx and row yy, height = aa and width = bb.
02 ** 00 Text block Displays text in the dialog box, terminated in 00. See the Text Format section for details.
03 xx yy aa bb Set dialog box parameters Set dialog box coordinates, column xx, row yy, width aa, height bb. Calculates position for wait prompt indicator.
04 xx yy Set text display speed modifier Sets text display speed modifier at [18E7] to yyxx. This is the portion of the delay that is ignored if the button is held down while text is being displayed. Lower number means faster text.
05 xx yy Jump Jumps to position yyxx in the script by setting SI to 4000+yyxx.
06 ** 00 Load SIL Writes the filename ** to [18F4] and loads the **.SIL file
07 xx ** 00 Load SZH Writes the filename ** to 1910 and loads the **.SZH file to slot xx(00-04). Decompresses the file to [5000+(xx*1000)].
08 xx Draw image Draws the image loaded to [5000+(xx*1000)] to the screen
09 xx yy Scene transition Fade in/out, xx is the image slot, yy is the fade type(00=fade in from black,01=fade out to black,02=fade out to white, 03=fade in from white).
0A xx ??? Set [1B93] to xx. Value is checked by the Scene Transition fade effect and must be set to 00.
0B none ??? unused?
0C xx yy zzzz ww(aa bb cc dd,...) Set palette effects Loads the palette in slot xx, yy is the palette effect type(00 = none,01 = fade,02 = flash), zzzz is the timer for the effect, ww is the number of colors affected, then for each color aa=the palette slot, bb cc dd is the secondary color
0D xx yy Set animation speed Set [1B61] to yyxx, lower number means animation/image scrolling is faster
0E none Stop music Stops playing music.
0F ** 00 Load M Writes the filename ** to [1930] and loads the **.M file.
10 none Play music Plays the music file loaded.
11 xx Play SFX Plays sound effect number xx.
12 xx yy aa bb Jump if equal If the value at [1A40+xx] equals yy, jump to 4000+bbaa.
13 xx yy aa bb Jump if not equal If the value at [1A40+xx] does NOT equal yy, jump to 4000+bbaa.
14 xx Load variable Copies the value at [1A40+xx] to [1B40].
15 xx aa bb Jump if not equal to [1B40] If the value at [1B40] is not equal to xx, jump to 4000+bbaa.
16 xx yy Set variable Sets the value at [1A40+xx] to yy.
17 xx yy Set skip position Sets [1B8D] to 4000+yyxx and [1B8F] to FF. This is the address it jumps to if you press a key during the intro.
18 none Dialog wait prompt Displays a flashing triangle in the bottom right of the dialog box and waits for the user to press a key.
19 xx yy Wait timer? Sets [1B5B] to (yyxx*3E8)/682 and loops through checking specific memory address.
1A none ??? unused?
1B xx ** 00 Load error message Copy error message text ** to [1950], [19A0], or [19F0] for xx = 00, 01, or 02.
1C xx yy Set image position Sets the postion to draw images at [18D9] to yyxx.
1D xx yy Set palette fade speed Sets [1BA3] to yyxx. Lower number means faster fade effect
1E xx yy zz aa bb Clear draw Sets the graphics VRAM data to FF, drawing a black box starting at memory position yyxx in the graphics plane, width of zz*8, height of bbaa.
1F xx yy aa bb Display scrolling image Display loaded SCR image in slot xx, yy = ??, bbaa is the height of the visible section of the image as it scrolls
20 ** 00 Load game Loads the save file ** to [1910].
21 ** 00 Save game Saves the game to file **.
22 xx yy Set flag Sets bit flags in variable xx by performing a bitwise OR on value at [1A40+xx] with yy.
23 xx yy ??? Unused.
24 none ??? Unused.
25 none Clear menu Clears the menu box
26 xx ** 00 Load nametag Loads the nametag text ** into [1DCF+(xx*21)]. See the Nametag Format section for details.
27 xx yy Set text display speed base Sets the base text display speed [18E9] to yyxx. This is the portion of the delay that waits regardless of whether the button is held down or not. Lower number means faster text.
28 xx ??? Unused. Sets [1B9E] to xx
29 xx ??? Sets [1B5F] to xx.
2A xx yy aa bb Display animation frame Draws frame yy from the ANM loaded in slot xx. This is used to play the animation frame-by-frame so that other commands can be executed between frames.
2B xx yy zz Initialize palette Sets each color in the palette to xx yy zz
2C xx Loop animation Display ANM in slot xx and loop continuously.
2D none ??? Sets [1B95] to 00.
2E none Clear palette effects Sets [1B9F] to 00, set [1BA1] to FF.
2F none Set wait prompt Manually sets the dialog box wait prompt.
30 none Clear wait prompt Manually clears the dialog box wait prompt.
31 xx yy zz aa bb cc Draw box Draws a box at position yyxx, width 8*zz, height bbaa, color cc, used to flash white between images during the flashback sequence
32 none Clear palette effects Sets [1B9F] and [1BA1] to 00.

Opcode "00" is the instruction to display the game's ADV-style text menu. It is followed by at least two bytes, first is the variable that will be used to store the menu option selected(usually 00, added to 1A40 and saved to [1B72]) and then the number of top-level options in the menu. It is then followed by a block for each menu option each menu option. Each block consists of the following: a byte that is 1+the number of suboptions to display when selected, then a byte for the variable id to use as a flag that must be set for the option to be shown (00 means it is always shown), then the text for the menu option, terminated by 00. If the number of suboptions is greater than 0, they each come sequentialy in the format of flag byte, text, 00, before proceeding to the next top-level option.

Take the menu for the first scene for example:

    00 00 05 01 00 4C 4F 4F 4B 00 01 00 54 41 4C 4B 
    00 01 00 54 48 49 4E 4B 20 00 02 0D 4D 4F 56 45 
    00 00 43 6F 72 72 69 64 6F 72 00 04 00 53 41 56 
    45 00 00 44 41 54 41 20 31 00 00 44 41 54 41 20 
    32 00 00 44 41 54 41 20 33 00 14 00 15 0A 96 01 
    14 0A 15 00 15 01 02 21 30 00

The first three bytes, 00 00 05, tell us this is a menu, using variable 00, with 5 options. The next block, 01 00 4C 4F 4F 4B 00, tells us the first option has no sub options, is always visible, and displays text 4C 4F 4F 4B, or "LOOK". Likewise for the second and third blocks. The fourth is 02 0D 4D 4F 56 45 00, so this menu will have one sub option and will be hidden if the value variable 0D is 00, and the text is "MOVE". It is immediately followed by the sub option block, 00 43 6F 72 72 69 64 6F 72 00, so always visible, text "Corridor". The last option has 3 suboptions, all visible.

When a top-level menu is selected, the number of the option is multiplied by 0A and stored in the room variable. If that option has suboptions, those are displayed. When a suboption is selected, the number of that option is added to the room variable to determine the final value. For example, if the first option is selected and there are no suboptions, the room variable will be set to 0A. If the fourth option, first suboption is selected, it will be (04*0A)+01, or 29. This means that if you try to put more than nine suboptions in a single submenu, you run the risk of options overlapping other submenus. You should be able to get around that by using hidden menu items, as they are included in calculating the value even when they are not displayed.

Text Format

Opcode "02" indicates the start of a text block. It takes no parameters besides the text string terminated by 00. The text box is output to the dialog box based on parameters set in memory and will automatically wrap at the end of the line and scroll if there are more lines than fit. The dialog box is seventy-two half-width spaces wide and five rows tall. When reading the text, the game checks for the following special control codes:

Hex ASCII JIS x 0208 Function Notes
21 30 !0 Clear text Clears the dialog box and returns the cursor to the top right position.
21 64 !o Line break Moves the cursor to the start of the next line.
21 74 !t Wait Pauses the text output briefly.
21 73 !s Nametag Inserts a character nametag. This command is followed by a fullwidth roman letter corresponding to the nametag slot, with %A (!s#A in ASCII) being the first slot. See Nametag Format for details.
21 70 !p Quick text mode This command controls the setting to print the text instantaneously, instead of character-by-character like a typewriter. The nametags each begin with $1 (!p#1) and end with $0 (!p#0) so that the full tag is printed at once.
21 77 !w Change text color Changes the color of text. Followed by a fullwidth number from 0 to 7. The nametags use @4 (!w#4) to change to green and then @7 (!w#7) to change back to white. The only control character usable in Menu commands.

The text color command uses the fullwidth numbers for simplicity sake, but actually only looks at the last three bits, corresponding to the colors on the following table:

Code Color
0 Black
1 Blue
2 Red
3 Magenta
4 Green
5 Cyan
6 Yellow
7 White

The text commands are formatted conservatively in the original, with a separate command for each line of text and rarely going over 4 lines before clearing the dialog box with it's own separate command. In practice it is more permissive, automatically wrapping to the next line when the end is reached. If a new line is started when the cursor is already on the bottom, it will delete the top line, move the rest of the text up a row, and continue from the start of the bottom row. Thus a single command can print multiple lines by simply running over or using the Line Break code.

Nametag Format

Opcode "26" sets the text for a nametag which is saved in memory to be called with the (!s) control code. The byte that follows it is the slot to save it in and will correspond to the letter used to call it: 00 = (#A), 01 = (#B), etc. The text data of the tags follows and then a 00 byte ends the command. The slots are thirthy-three bytes long, designed for twelve bytes worth of printable characters (six full-width or twelve half-width) leaving ten for non-printing codes and one for the terminating 00. As used, they all begin with code to turn on quick text mode, change the color to green, the text of the name, change the color back to white, then turn off quick text mode. The exception is slot #S, which omits the color and is just blank spaces, used to indent a new line when the name is not needed. While designed around these expectations, there is nothing that would prevent you from inserting a longer nametag that saves across multiple slots, as it will keep going until it hits the 00.

The slots used in the Guynarock II are #S for blank space, #E for Elly, #I for Io, #R for Remi, #L for Rui, #M for Meg, #B for Vykal, and #C for the Chief.

SZH Files

SZH files are a compression format for the three different types of graphics, MSV for static images, SCR for large images that scroll, and ANM for animations. Opcode "07" loads the file to memory position 1f0f:8000, then decompresses it to slot 00 at 5000:0000, slot 01 at 6000:0000, etc. Opcodes 08, 1F, 2A, or 2C are then used to display the image loaded into the given slot.

MSV files

Once decompressed, the MSV file format consists of a short header containing the width and height of the image, the image data further compressed with run-length encoding, and a footer with an indicator for the filetype, the palette information, and ending in "RGB". The header is two bytes for width divided by 8, as it is handled 8 pixels per byte, then two bytes for the height. The pixel data is then stored by 8-pixel column, plane-by-plane. So it would be the first column of plane 0, then the first column of plane 1, plane 2, plane 3, then the second column of plane 0, etc. This data is run-length encoded within the column, with indicated by two matching bytes in a row. The next byte will be the number of bytes to repeat the data, or 00 which will add 0100 to the next byte for the length. For each byte after the first in each column, if it is not a run it is XOR'ed with the previous value to get the actual output. The footer has one byte indicating the image type (00 for MSV, 01 for ANM, 03 for SCR), then fourteen sets of three bytes for the RGB of each palette color 01-0E (color 00 is always 00 00 00 and 0F is always 0f 0f 0f), then three bytes for "RGB".

SCR files

SCR files have the same header and footer format as MSV, but the data pixel data is stored row-by-row instead of column-by-column. That way, it can just jump to the first column in the scroll window and copy a whole row at a time. The height of the sroll window is not part of the image data, but declared in the command to display it to screen.

ANM files

ANM files have a header from 00 to 7F that list sequentially the number of frames in the animation with the rest filled with 00. A five frame animation, for example, would start with "01 02 03 04 05 00 00..." Next it lists for bytes for each frame, xx yy aa bb, which are pointers to the data for that frame. The first two are copied to DS and the last two to SI, so that it points to (yyxx*10)+bbaa. Then it has blocks of data for each frame, which start with the width and height, followed by data four bytes at a time, one for each plane(?) by columns. The footer is then the same as the other formats.