Save State Hacking Guide by taleden

Version: 1.0 | Updated: 01/28/04 | Printable Version

Shining Force II Save State Hacking Addendum -- v1.0
by Taleden <email omitted lest another address be ruined by spam>



I. Intro
  A. Inspiration & Introduction
  B. License
  C. Revision History
  D. Intro to Hex Editing
II. Offsets
  A. Joined Members
    1. Gens/DGen/Genecyst save-state offsets
    2. Gens SRAM offsets
    3. DGen SRAM offsets
  B. Active Members
    1. Gens/DGen/Genecyst save-state offsets
    2. Gens SRAM offsets
    3. DGen SRAM offsets
  C. Difficulty
    1. Gens/DGen/Genecyst save-state offsets    
    2. Gens SRAM offsets
    3. DGen SRAM offsets
  D. Technical Discussion
III. Quick Lookup


[I] Intro

This section just has some introductory information; if you just need the
offsets and values, skip to section II.

[I.A] Inspiration

A few weeks ago, I sat down to play through Shining Force again; it had been
some time, and I was itching for some of that awesome tactical combat from my
youth.  Unfortunately my Genesis console stopped accepting my Shining Force
cart long ago, so I got the latest copy of a few emulators (namely, as of this
writing, Gens 2.11 and DGen 1.21) and sat down to play.  Around the middle of
Chapter 4, just after Pao I had moved off and I was preparing for the battle
with General Elliott, I realized that I had forgotten to get Kokichi in the
last chapter!  Unwilling to play out the rest of the game without my favorite
character, I figured I'd just go back to an old save-state and re-do a few
battles, no big deal.. but then I realized that I didn't have any old save-
states, and had already saved over the SRAM image, too.  I was at a loss.

So, I started looking around the net for information on save state hacking,
hoping to just add Kokichi to my Force and continue on.  I came across
Thundergod's SF1 guide, as well as Stufff's SF2 guide, which provide the
offsets to modify character traits, but neither of them had any mention of how
to change which characters were members of the Force.  I also picked up a copy
of GenEdit (a generic Genesis save-game editor) which supported SF1, and it
even allowed you to change which members were active (that is, part of the 12-
strong group that goes into battle) - I was able to activate Kokichi and sure
enough he appeared on the field when I went into battle, but he was still not
technically "part of the Force" as far as the game was concerned, so I couldn't
transfer items to him, or replace him with other people, etc, except during
battle, which was rather annoying.

All of this led up to some toying with save states and hex editors to determine
for myself where the joined-member data was stored, so I could add Kokichi
properly and continue my game.  The offsets weren't too hard to find in SF1, so
I did the same for SF2, and then figured out the active-member data storage and
SF2's difficulty setting storage, and now offer it all as an addendum to the
current save-state hacking guides (Thundergod's and Stufff's).

Note that I have released two versions of this guide: one of them is for SF1,
and the other is for SF2.  I originally wrote a single guide that covered both
games, but when I decided to submit to GameFAQs I discovered that they won't
accept guides that cover multiple games, so I split it into two versions.  If
you read both you'll notice they are almost identical (right down to this
paragraph), except of course for the actual addresses and places where the two
games differ.  I apologize for being repetetive; blame GameFAQs.  :)

[I.B] License

This document is released into the public domain, and may be freely copied and
redistributed in any form, electronic or otherwise.  However, it may not be
used, in whole or in part, for any commercial gain; no money may be charged for
its distribution except for the cost of the media it is distributed on, if
applicable.  This document may be duplicated in whole or in part, or used as
a reference for the creation of subsequent documents, provided that appropriate
credit is given to the author (Taleden) for the use of any information provided
by this document.  All copyrights and trademarks mentioned herein are the
property of their respective owners, unless otherwise stated.

[I.C] Revision History

1.0 - initial release

[I.D] Intro to Hex Editing

The Thundergod and Stufff guides each provide a decent intro to hex editing
in general, so mine will be brief.

First, a comment on notation: hex editing involves the hexadecimal number
system, which recognizes sixteen digits (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c,
d, e, f).  The decimal system recognizes ten digits, but they are the same
first ten digits that hexadecimal uses, so there is a possibility for
confusion: if I say 63, do I mean the decimal number sixty-three, or the
hexadecimal number 63 (which, in decimal, is 6*16+3=99)?  On top of that, we
have binary, which has only the symbols 0 and 1 - symbols which now appear in
three number systems with which we are concerned, making the number "101"
triply ambiguous.  So, to avoid this confusion, I will always refer to
hexadecimal values with the prefix "0x", decimal numbers with no prefix, and
binary numbers with the prefix "x".  Although there are of course exceptions,
generally speaking these are the conventional ways to differentiate between
these number systems.  So, as an example, "63" means decimal sixty-three;
"0x63" means decimal ninety-nine; "x63" means nothing, because neither 6 nor 3
is a valid binary digit; "0x11" means decimal seventeen (1*16+1); "x11" means
decimal three (1*2+1); and so on.

Next, a comment on hexadecimal and binary: computer data is, technically
speaking, stored in binary, but it is very cumbersome to try to look at it in
this form, which is why we use hex editors and not binary editors.  Hexadecimal
is a good alternative (as opposed to, say, decimal) because it lines up very
neatly with binary: four binary digits in sequence correspond to exactly one
hexadecimal digit.  For example, the binary value x1101 is the hexadecimal
digit 0xD (or decimal thirteen); the binary string x001101110001 is 0x371;
notice how neatly they line up: each group of four binary digits becomes a
single hexadecimal digit; x0011 -> 0x3, x0111 -> 0x7, x0001 -> 0x1.

Building on this, I can now discuss the concept of a 'bitvector'.  Suppose you
were designing a game, and you had a bunch of boolean flags that you wanted to
store; for example, has the Hero talked to this person yet?  Has the Hero
been to this town yet?  These true/false values determine the exact state of
the game, and must be stored when the game is saved.  However, it is generally
not possible to save data in sizes less than one 'byte', which is eight binary
bits or two hexadecimal digits; the simple method would be to just store each
flag as one byte, either 0x00 for false or 0x01 for true.  But this is a huge
waste of storage space, which (especially on a game console) might be in short
supply.  A much better method would be to store each flag as a single binary
bit, but since a single bit can't be stored independently of a full eight-bit
byte, a 'bitvector' is created.  In general, a 'bitvector' is just a bunch of
bits lumped together into a number.  In our case, we're only interested in the
8-bit variety of bitvector, which is enough to store as a single byte.  Each
bit of the byte represents one of the flags; this way, no space is wasted.

For example: from among eight flags, suppose we wanted the second and third to
be 'true' and the rest to be 'false'; stringing them all together, we would
have the binary sequence x01100000, which would then be stored as the hex byte
0x60.  Now suppose we wanted to turn the first flag on; the new sequence is
x11100000, which is hex 0xE0.  As you can see, changing just one bit of the
bitvector made a big difference - the hex value jumped from 0x60 to 0xE0.  This
is because we changed the 'most-significant' bit, which for our purposes always
means the left-most bit.


[II] Offsets

[II.A] Joined Members

Since there are 30 characters in the game (including the Hero), four bytes
(32 bits) are used to store the member-has-joined flags, in the form of a
bitvector.  The ordering is identical to the ordering of the members list that
you can see in the game, when you have acquired everyone: Sarah, then Chester,
then Jaha, etc.  Specifically, in order of first to last byte, and most to
least significant bit in each byte, the flags are:

1st byte: Hero,    Sarah,   Chester, Jaha,   Kazin,   Slade,  Kiwi,   Peter
2nd byte: May,     Gerhalt, Luke,    Rohde,  Rick,    Elric,  Eric,   Karna
3rd byte: Randolf, Tyrin,   Janet,   Higins, Skreech, Taya,   Frayja, Jaro
4th byte: Gyan,    Sheela,  Zynk,    Chaz,   Lemon,   Claude, N/A,    N/A

Final two bits are junk; the game doesn't expect them to ever be true, so they
are ignored.

[II.A.1] Gens/DGen/Genecyst save-state offsets

Although each emulator has its own save-state file format, the data we're
interested in appears to be in exactly the same place in save-states created
by Gens, DGen or Genecyst, and may be the same for other emulators as well.
The member-has-joined data begins at offset 0x11AFE and occupies the four
bytes up to and including 0x11B01.

For example, in your first battle you have the Hero, Sarah and Chester
available to you.  In this case, the bitvectors for the four bytes would be:

  x1110 0000
  x0000 0000
  x0000 0000
  x0000 0000

I've added spaces in the middle to make it easier to see the mapping to the
hex data which would appear in the save-state file, starting at offset

  0xE0 0x00 0x00 0x00

If you wanted to add Rick (who, looking back up at the table above, is the
fifth bit in the second byte), the hex data would become:

  0xE0 0x08 0x00 0x00

[II.A.2] Gens SRAM offsets

SRAM refers to save-ram, which is the file an emulator creates to simulate
the battery-backup memory on a physical genesis cartridge.  If you save your
game with the priest and then turn off the emulator, it is this file you would
modify before restarting the emulator and continuing your game from the
modified SRAM.  Unfortunately, different emulators also appear to store SRAM
differently, and in this case (unlike for save-states) the offsets for Gens-
and DGen-created SRAM files are not the same.

The member-has-joined bitvector bytes are basically the same as in the save-
state, except that instead of being four consecutive bytes, in the Gens SRAM
file they are stored at every other byte: the first is at 0x1DBD, the second at
0x1DBF, and so on to the fourth at 0x1DC3.  The bytes in between appear to be
ignored; I've never seen them with any value besides 0x00.

Note however that these offsets refer to the game saved in slot 1!  I have not
done any testing to determine the corresponding offsets for the save game in
slot 2, but I imagine they are at the same positions relative to the member
data (that is, the difference between the location of the Hero's name in game 1
and the offsets listed here is likely the same as the difference between the
Hero's name in game 2 and the corresponding offsets there).
HOWEVER!  In SF2, these flags do not appear to be the end of the story when
it comes to storing the members who have joined the Force.  If you modify the
bitvectors, either to add or remove a member, SF2 will reject the change and
erase the save record in that slot.  For more on this, see section II.D
"Technical Discussion" - the short of it is, you can use these offsets to
look at an SRAM file and determine who has joined the team, but you cannot
modify them in the SRAM file - you have to do it in a save-state file instead
(and then you can just save your game to create the corresponding SRAM file).

[II.A.3] DGen SRAM offsets

DGen's SRAM files appear to differ from Gens' by only one byte; whereas the
Gens offset for member-has-joined bitvectors is 0x345, the DGen SRAM offset is
0x344.  But in both cases, the bitvectors are stored at every other byte - from
0x344 to 0x34A.  You cannot modify the roster in an SF2 SRAM file; see the note
in the previous section.

[II.B] Active Members

In SF2 (as opposed to SF1), the developers seem to have realized the
foolishness of the list system, because active members are stored as a
bitvector this time.  The bits are exactly the same as they were for the
member-has-joined flags.

[II.B.1] Gens/DGen/Genecyst save-state offsets

In save-state files, the bitvectors begin at offset 0x11B02 (right after the
member-has-joined bitvectors), through offset 0x11B05.

[II.B.2] Gens SRAM offsets

Again, the SRAM bitvectors are stored every other byte; for Gens SRAM files,
they begin at offset 0x1DC5 (through 0x1DCB).  Unlike changing the roster
itself, changing which members are part of the active force DOES NOT invalidate
your save game; the emulator will happily load it up with the new active-
members list.  However, making members active who are not part of your Force
may still invalidate the save game, I haven't tested it.

[II.B.3] DGen SRAM offsets

The DGen SRAM bitvectors begin at offset 0x1DC4, through 0x1DCA.

[II.C] Difficulty

SF2 introduced the concept of a difficulty setting, which of course must be
saved with your game, and can therefore be changed.  In all cases, the
difficulty is stored as one byte with a value between 0 (0x00) and 3 (0x03),
with 0 (0x00) being 'Normal' and 3 (0x03) being 'Ouch' - however, the middle
two difficulty settings are stored backwards; 1 (0x01) is 'Super', and 2 (0x02)
is 'Hard'.

[II.C.1] Gens/DGen/Genecyst save-state offsets

In the save-state file, the difficulty is stored at offset 0x11B07.

[II.C.2] Gens SRAM offsets

In Gens' SRAM file, the difficulty is stored at offset 0x1DCF.

[II.C.3] DGen SRAM offsets

In DGen's SRAM file, the difficulty is stored at offset 0x1DCE.

[II.D] Technical Discussion

As mentioned earlier, SF2 rejects any change to the member-has-joined
bitvectors if you make them in the SRAM file and then try to re-load the game;
changing the save-state seems to work fine.

It could be that the game is actually designed with checksums, and that
changing the member roster without changing the checksum accordingly trips an
error that the save data has become corrupt, and so the game rejects it.  It
could also be that the game compares the member roster to the game state flags
and detects when a member is present who could not possibly be, or the other
way around (i.e. if SLADE was on the team but you had not yet fought battle 1;
or if SARAH was somehow absent from the team, but CHESTER was present).

I did some testing, comparing SRAM files from the very beginning of the game
up through the point where JAHA joins, to see if I could determine which other
bytes were changing, but I wasn't able to detect a pattern.  I may do some more
tinkering, but if anyone else is able to figure out how it works, please post
your results.


[III] Quick Lookup

This guide is written (in the spirit of the other save state hacking guides)
toward a relativly novice user, so the meat of it (the actual offsets and
values) are jumbled up in all kinds of discussion, which is sometimes annoying.
If you already know how this all works and just need the numbers, here they
are, in a more concise format.

-Joined Members: stored as a 4-byte bitvector
  -Bitvector byte 1: Hero, Sarah, Chester, Jaha, Kazin, Slade, Kiwi, Peter
  -Bitvector byte 2: May, Gerhalt, Luke, Rohde, Rick, Elric, Eric, Karna
  -Bitvector byte 3: Randolf, Tyrin, Janet, Higins, Skreech, Taya, Frayja, Jaro
  -Bitvector byte 4: Gyan, Sheela, Zynk, Chaz, Lemon, Claude, N/A, N/A
  -Savestate offset: 0x11AFE - 0x11B01
  -Gens SRAM offset: 0x1DBD - 0x1DC3 (every other byte - CANNOT CHANGE)
  -DGen SRAM offset: 0x1DBC - 0x1DC2 (every other byte - CANNOT CHANGE)

 -Active Members: stored as a 4-byte bitvector (same bits as Joined Members)
  -Savestate offset: 0x11B02 - 0x11B05
  -Gens SRAM offset: 0x1DC5 - 0x1DCB (every other byte)
  -DGen SRAM offset: 0x1DC4 - 0x1DCA (every other byte)

 -Difficulty: stored as a single byte
  -Normal=0x00 , Hard=0x02 , Super=0x01 , Ouch=0x03
  -Savestate offset: 0x11B07
  -Gens SRAM offset: 0x1DCF
  -DGen SRAM offset: 0x1DCE