This is a guide to the Secret of Evermore SRAM, the section of memory in the Secret of Evermore cartridge used to save your progress.
SRAM Basics
Secret of Evermore, like most SNES games, used an SRAM of 0x2000
(8 KB) bytes to store its information. Up to four games could be save. Each game used 0x331
bytes of SRAM. This means 0xCC4
bytes of SRAM are reserved for the game data, with 0x133C
bytes used for other things (some unused). In this document, we are only interested in the 0xCC4
bytes of save game data.
Not all of the information contained in the save game data is known. Most of what is undocumented is related to quest progress and opened or already found treasure. The only major items of interest that I've yet to find are the save location, and the rare quest items.
Here is a short listing of what is known in no particular order. The sanity (aka checksum) data, the known alchemys and their levels, the boy's and dog's stats (name, experience, level, HP, and MP), the money (talons, jewels, gold coins, and credits), and the inventory (alchemy ingredients, items, armors, weapons, helmets, gauntlets, collars, trade goods, and charms).
The first game starts at 0x2
in the SRAM, and after its 0x331
bytes, the second game starts and so on for the four games.
SRAM Offsets
This information is presented in no particular order. Similar information will be grouped together.
When an offset is listed, this offset is relative to the start of the game data. For example, if I said the boy's name is at offset 0x26
, this would be 0x28 (0x2 + 0x26)
from the start of SRAM for game 1, and 0x359 (0x2 + 0x331 + 0x26)
, and so on.
Data in the SRAM is stored in little endian format, the format used by the SNES processor. This means words are stored with their least significant byte first. For instance, 400 is 0x190
in hex, which is two bytes 0x01
and 0x90
. In little endian, this would be 0x9001 [90 01]
. Mostly, this just means reverse the bytes. There are plenty of endian guides on the web if you need a more in-depth explanation.
Group | Offset | Length | Description |
---|---|---|---|
Sanity Data | 0x000 | 0x02 | The Sanity / Checksum Word |
Boy's Stats | 0x026 | 0x22 | Name |
Boy's Stats | 0x09D | Level | |
Boy's Stats | 0x06E | 0x02 | Current HP |
Boy's Stats | 0x08E | 0x02 | Max HP |
Boy's Stats | 0x09A | 0x03 | Experience |
Dog's Stats | 0x04A | 0x22 | Name |
Dog's Stats | 0x0DE | Level | |
Dog's Stats | 0x0AF | 0x02 | Current HP |
Dog's Stats | 0x0CF | 0x02 | Max HP |
Dog's Stats | 0x0DB | 0x03 | Experience |
Money Types | 0x0FC | 0x03 | Talons |
Money Types | 0x0FF | 0x03 | Jewels |
Money Types | 0x102 | 0x03 | Gold Coins |
Money Types | 0x105 | 0x03 | Credits |
Weapon Inventory | 0x279 | Bit 1 | Bone Crusher |
Weapon Inventory | 0x279 | Bit 2 | Gladiator Sword |
Weapon Inventory | 0x279 | Bit 3 | Crusader Sword |
Weapon Inventory | 0x279 | Bit 4 | Neutron Blade |
Weapon Inventory | 0x279 | Bit 5 | Spider's Claw |
Weapon Inventory | 0x279 | Bit 6 | Bronze Axe |
Weapon Inventory | 0x279 | Bit 7 | Knight Basher |
Weapon Inventory | 0x27A | Bit 0 | Atom Smasher |
Weapon Inventory | 0x27A | Bit 1 | Horn Spear |
Weapon Inventory | 0x27A | Bit 2 | Bronze Spear |
Weapon Inventory | 0x27A | Bit 3 | Lance |
Weapon Inventory | 0x27A | Bit 4 | Laser Lance |
Weapon Inventory | 0x27A | Bit 5 | Bazooka |
Weapon Levels | 0x115 | 0x01 | Minor: Bone Crusher |
Weapon Levels | 0x116 | 0x01 | Major: Bone Crusher |
Weapon Levels | 0x117 | 0x01 | Minor: Gladiator Sword |
Weapon Levels | 0x118 | 0x01 | Major: Gladiator Sword |
Weapon Levels | 0x119 | 0x01 | Minor: Crusader Sword |
Weapon Levels | 0x11A | 0x01 | Major: Crusader Sword |
Weapon Levels | 0x11B | 0x01 | Minor: Neutron Sword |
Weapon Levels | 0x11C | 0x01 | Major: Neutron Sword |
Weapon Levels | 0x11D | 0x01 | Minor: Spider's Claw |
Weapon Levels | 0x11E | 0x01 | Major: Spider's Claw |
Weapon Levels | 0x11F | 0x01 | Minor: Bronze Axe |
Weapon Levels | 0x120 | 0x01 | Major: Bronze Axe |
Weapon Levels | 0x121 | 0x01 | Minor: Knight Basher |
Weapon Levels | 0x122 | 0x01 | Major: Knight Basher |
Weapon Levels | 0x123 | 0x01 | Minor: Atom Smasher |
Weapon Levels | 0x124 | 0x01 | Major: Atom Smasher |
Weapon Levels | 0x125 | 0x01 | Minor: Horn Spear |
Weapon Levels | 0x126 | 0x01 | Major: Horn Spear |
Weapon Levels | 0x127 | 0x01 | Minor: Bronze Spear |
Weapon Levels | 0x128 | 0x01 | Major: Bronze Spear |
Weapon Levels | 0x129 | 0x01 | Minor: Lance |
Weapon Levels | 0x12A | 0x01 | Major: Lance |
Weapon Levels | 0x12A | 0x01 | Minor: Laser Lance |
Weapon Levels | 0x12B | 0x01 | Major: Laser Lance |
Weapon Levels | 0x13D | 0x01 | Minor: Dog's Attack Level |
Weapon Levels | 0x13E | 0x01 | Major: Dog's Attack Level |
Alchemy Ingredients | 0x289 | 0x01 | Wax |
Alchemy Ingredients | 0x28A | 0x01 | Water |
Alchemy Ingredients | 0x28B | 0x01 | Vinegar |
Alchemy Ingredients | 0x28C | 0x01 | Root |
Alchemy Ingredients | 0x28D | 0x01 | Oil |
Alchemy Ingredients | 0x28E | 0x01 | Mushroom |
Alchemy Ingredients | 0x28F | 0x01 | Mud Pepper |
Alchemy Ingredients | 0x290 | 0x01 | Meteorite |
Alchemy Ingredients | 0x291 | 0x01 | Limestone |
Alchemy Ingredients | 0x292 | 0x01 | Iron |
Alchemy Ingredients | 0x293 | 0x01 | Gunpowder |
Alchemy Ingredients | 0x294 | 0x01 | Grease |
Alchemy Ingredients | 0x295 | 0x01 | Feather |
Alchemy Ingredients | 0x296 | 0x01 | Ethanol |
Alchemy Ingredients | 0x297 | 0x01 | Dry Ice |
Alchemy Ingredients | 0x298 | 0x01 | Crystal |
Alchemy Ingredients | 0x299 | 0x01 | Clay |
Alchemy Ingredients | 0x29A | 0x01 | Brimstone |
Alchemy Ingredients | 0x29B | 0x01 | Bone |
Alchemy Ingredients | 0x29C | 0x01 | Atlas Medallion |
Alchemy Ingredients | 0x29D | 0x01 | Ash |
Alchemy Ingredients | 0x29E | 0x01 | Acorn |
Alchemy Inventory | 0x1F7 | Bit 0 | Acid Rain |
Alchemy Inventory | 0x1F7 | Bit 1 | Atlas |
Alchemy Inventory | 0x1F7 | Bit 2 | Barrier |
Alchemy Inventory | 0x1F7 | Bit 3 | Call Up |
Alchemy Inventory | 0x1F7 | Bit 4 | Corrosion |
Alchemy Inventory | 0x1F7 | Bit 5 | Crush |
Alchemy Inventory | 0x1F7 | Bit 6 | Cure |
Alchemy Inventory | 0x1F7 | Bit 7 | Defend |
Alchemy Inventory | 0x1F8 | Bit 0 | Double Drain |
Alchemy Inventory | 0x1F8 | Bit 1 | Drain |
Alchemy Inventory | 0x1F8 | Bit 2 | Energize |
Alchemy Inventory | 0x1F8 | Bit 3 | Escape |
Alchemy Inventory | 0x1F8 | Bit 4 | Explosion |
Alchemy Inventory | 0x1F8 | Bit 5 | Fireball |
Alchemy Inventory | 0x1F8 | Bit 6 | Fire Power |
Alchemy Inventory | 0x1F8 | Bit 7 | Flash |
Alchemy Inventory | 0x1F9 | Bit 0 | Force Field |
Alchemy Inventory | 0x1F9 | Bit 1 | Hard Ball |
Alchemy Inventory | 0x1F9 | Bit 2 | Heal |
Alchemy Inventory | 0x1F9 | Bit 3 | Lance |
Alchemy Inventory | 0x1F9 | Bit 4 | Laser (Dummied Alchemy) |
Alchemy Inventory | 0x1F9 | Bit 5 | Levitate |
Alchemy Inventory | 0x1F9 | Bit 6 | Lightning Storm |
Alchemy Inventory | 0x1F9 | Bit 7 | Miracle Cure |
Alchemy Inventory | 0x1FA | Bit 0 | Nitro |
Alchemy Inventory | 0x1FA | Bit 1 | One Up |
Alchemy Inventory | 0x1FA | Bit 2 | Reflect |
Alchemy Inventory | 0x1FA | Bit 3 | Regrowth |
Alchemy Inventory | 0x1FA | Bit 4 | Revealer |
Alchemy Inventory | 0x1FA | Bit 5 | Revive |
Alchemy Inventory | 0x1FA | Bit 6 | Slow Burn |
Alchemy Inventory | 0x1FA | Bit 7 | Speed |
Alchemy Inventory | 0x1FB | Bit 0 | Sting |
Alchemy Inventory | 0x1FB | Bit 1 | Stop |
Alchemy Inventory | 0x1FB | Bit 2 | Super Heal |
Alchemy Levels | 0x155 | 0x02 | Minor: Acid Rain |
Alchemy Levels | 0x157 | 0x02 | Minor: Atlas |
Alchemy Levels | 0x159 | 0x02 | Minor: Barrier |
Alchemy Levels | 0x15B | 0x02 | Minor: Call Up |
Alchemy Levels | 0x15D | 0x02 | Minor: Corrosion |
Alchemy Levels | 0x15F | 0x02 | Minor: Crush |
Alchemy Levels | 0x161 | 0x02 | Minor: Cure |
Alchemy Levels | 0x163 | 0x02 | Minor: Defend |
Alchemy Levels | 0x165 | 0x02 | Minor: Double Drain |
Alchemy Levels | 0x167 | 0x02 | Minor: Drain |
Alchemy Levels | 0x169 | 0x02 | Minor: Energize |
Alchemy Levels | 0x16B | 0x02 | Minor: Escape |
Alchemy Levels | 0x16D | 0x02 | Minor: Explosion |
Alchemy Levels | 0x16F | 0x02 | Minor: Fireball |
Alchemy Levels | 0x171 | 0x02 | Minor: Fire Power |
Alchemy Levels | 0x173 | 0x02 | Minor: Flash |
Alchemy Levels | 0x175 | 0x02 | Minor: Force Field |
Alchemy Levels | 0x177 | 0x02 | Minor: Hard Ball |
Alchemy Levels | 0x179 | 0x02 | Minor: Heal |
Alchemy Levels | 0x17B | 0x02 | Minor: Lance |
Alchemy Levels | 0x17D | 0x02 | Minor: Laser (Dummied Alchemy) |
Alchemy Levels | 0x17F | 0x02 | Minor: Levitate |
Alchemy Levels | 0x181 | 0x02 | Minor: Lightning Storm |
Alchemy Levels | 0x183 | 0x02 | Minor: Miracle Cure |
Alchemy Levels | 0x185 | 0x02 | Minor: Nitro |
Alchemy Levels | 0x187 | 0x02 | Minor: One Up |
Alchemy Levels | 0x189 | 0x02 | Minor: Reflect |
Alchemy Levels | 0x18B | 0x02 | Minor: Regrowth |
Alchemy Levels | 0x18D | 0x02 | Minor: Revealer |
Alchemy Levels | 0x18F | 0x02 | Minor: Revive |
Alchemy Levels | 0x191 | 0x02 | Minor: Slow Burn |
Alchemy Levels | 0x193 | 0x02 | Minor: Speed |
Alchemy Levels | 0x195 | 0x02 | Minor: Sting |
Alchemy Levels | 0x197 | 0x02 | Minor: Stop |
Alchemy Levels | 0x199 | 0x02 | Minor: Super Heal |
Alchemy Levels | 0x19B | 0x02 | Major: Acid Rain |
Alchemy Levels | 0x19D | 0x02 | Major: Atlas |
Alchemy Levels | 0x19F | 0x02 | Major: Barrier |
Alchemy Levels | 0x1A1 | 0x02 | Major: Call Up |
Alchemy Levels | 0x1A3 | 0x02 | Major: Corrosion |
Alchemy Levels | 0x1A5 | 0x02 | Major: Crush |
Alchemy Levels | 0x1A7 | 0x02 | Major: Cure |
Alchemy Levels | 0x1A9 | 0x02 | Major: Defend |
Alchemy Levels | 0x1AB | 0x02 | Major: Double Drain |
Alchemy Levels | 0x1AD | 0x02 | Major: Drain |
Alchemy Levels | 0x1AF | 0x02 | Major: Energize |
Alchemy Levels | 0x1B1 | 0x02 | Major: Escape |
Alchemy Levels | 0x1B3 | 0x02 | Major: Explosion |
Alchemy Levels | 0x1B5 | 0x02 | Major: Fireball |
Alchemy Levels | 0x1B7 | 0x02 | Major: Fire Power |
Alchemy Levels | 0x1B9 | 0x02 | Major: Flash |
Alchemy Levels | 0x1BB | 0x02 | Major: Force Field |
Alchemy Levels | 0x1BD | 0x02 | Major: Hard Ball |
Alchemy Levels | 0x1BF | 0x02 | Major: Heal |
Alchemy Levels | 0x1C1 | 0x02 | Major: Lance |
Alchemy Levels | 0x1C3 | 0x02 | Major: Laser (Dummied Alchemy) |
Alchemy Levels | 0x1C5 | 0x02 | Major: Levitate |
Alchemy Levels | 0x1C7 | 0x02 | Major: Lightning Storm |
Alchemy Levels | 0x1C9 | 0x02 | Major: Miracle Cure |
Alchemy Levels | 0x1CB | 0x02 | Major: Nitro |
Alchemy Levels | 0x1CD | 0x02 | Major: One Up |
Alchemy Levels | 0x1CF | 0x02 | Major: Reflect |
Alchemy Levels | 0x1D1 | 0x02 | Major: Regrowth |
Alchemy Levels | 0x1D3 | 0x02 | Major: Revealer |
Alchemy Levels | 0x1D5 | 0x02 | Major: Revive |
Alchemy Levels | 0x1D7 | 0x02 | Major: Slow Burn |
Alchemy Levels | 0x1D9 | 0x02 | Major: Speed |
Alchemy Levels | 0x1DB | 0x02 | Major: Sting |
Alchemy Levels | 0x1DD | 0x02 | Major: Stop |
Alchemy Levels | 0x1DF | 0x02 | Major: Super Heal |
Inventory Items | 0x29F | 0x01 | Petal |
Inventory Items | 0x2A0 | 0x01 | Nectar |
Inventory Items | 0x2A1 | 0x01 | Honey |
Inventory Items | 0x2A2 | 0x01 | Dog Biscuit |
Inventory Items | 0x2A3 | 0x01 | Wings |
Inventory Items | 0x2A4 | 0x01 | Essence |
Inventory Items | 0x2A5 | 0x01 | Pixie Dust |
Inventory Items | 0x2A6 | 0x01 | Call Bead |
Inventory Items | 0x2A7 | 0x01 | Grass Vest |
Inventory Items | 0x2A8 | 0x01 | Shell Plate |
Inventory Items | 0x2A9 | 0x01 | Dino Skin |
Inventory Items | 0x2AA | 0x01 | Bronze Armor |
Inventory Items | 0x2AB | 0x01 | Stone Vest |
Inventory Items | 0x2AC | 0x01 | Centurion Cape |
Inventory Items | 0x2AD | 0x01 | Silver Mail |
Inventory Items | 0x2AE | 0x01 | Gold Plated Vest |
Inventory Items | 0x2AF | 0x01 | Shining Armor |
Inventory Items | 0x2B0 | 0x01 | Magna Mail |
Inventory Items | 0x2B1 | 0x01 | Titanium Vest |
Inventory Items | 0x2B2 | 0x01 | Virtual Vest |
Inventory Items | 0x2B3 | 0x01 | Grass Hat |
Inventory Items | 0x2B4 | 0x01 | Shell Hat |
Inventory Items | 0x2B5 | 0x01 | Dino Helm |
Inventory Items | 0x2B6 | 0x01 | Bronze Helmet |
Inventory Items | 0x2B7 | 0x01 | Obsidian Helm |
Inventory Items | 0x2B8 | 0x01 | Centurion Helm |
Inventory Items | 0x2B9 | 0x01 | Titan's Crown |
Inventory Items | 0x2BA | 0x01 | Dragon Helm |
Inventory Items | 0x2BB | 0x01 | Knight's Helm |
Inventory Items | 0x2BC | 0x01 | Lightning Helm |
Inventory Items | 0x2BD | 0x01 | Old Reliable |
Inventory Items | 0x2BE | 0x01 | Brainstorm |
Inventory Items | 0x2BF | 0x01 | Vine Bracelet |
Inventory Items | 0x2C0 | 0x01 | Mammoth Guard |
Inventory Items | 0x2C1 | 0x01 | Claw Guard |
Inventory Items | 0x2C2 | 0x01 | Serpent Bracer |
Inventory Items | 0x2C3 | 0x01 | Bronze Gauntlet |
Inventory Items | 0x2C4 | 0x01 | Gloves of Ra |
Inventory Items | 0x2C5 | 0x01 | Iron Bracer |
Inventory Items | 0x2C6 | 0x01 | Magician's Ring |
Inventory Items | 0x2C7 | 0x01 | Dragon's Claw |
Inventory Items | 0x2C8 | 0x01 | Cyberglove |
Inventory Items | 0x2C9 | 0x01 | Protector Ring |
Inventory Items | 0x2CA | 0x01 | Virtual Glove |
Inventory Items | 0x2CB | 0x01 | Leather Collar |
Inventory Items | 0x2CC | 0x01 | Spiky Collar |
Inventory Items | 0x2CD | 0x01 | Defender Collar |
Inventory Items | 0x2CE | 0x01 | Spot's Collar |
Inventory Items | 0x2CF | 0x01 | Thunderball |
Inventory Items | 0x2D0 | 0x01 | Particle Bomb |
Inventory Items | 0x2D1 | 0x01 | Cryo-Blast |
Inventory Items | 0x315 | 0x01 | Annihilation Amulet |
Inventory Items | 0x316 | 0x01 | Beads |
Inventory Items | 0x317 | 0x01 | Ceramic Pot |
Inventory Items | 0x318 | 0x01 | Chicken |
Inventory Items | 0x319 | 0x01 | Golden Jackal |
Inventory Items | 0x31A | 0x01 | Jeweled Scarab |
Inventory Items | 0x31B | 0x01 | Limestone Tablet |
Inventory Items | 0x31C | 0x01 | Perfume |
Inventory Items | 0x31D | 0x01 | Rice |
Inventory Items | 0x31E | 0x01 | Spice |
Inventory Items | 0x31F | 0x01 | Souvenir Spoon |
Inventory Items | 0x320 | 0x01 | Tapestry |
Inventory Items | 0x321 | 0x01 | Ticket for Exhibition |
Charms | 0x200 | Bit 5 | Armor Polish |
Charms | 0x200 | Bit 6 | Chocobo Egg |
Charms | 0x200 | Bit 7 | Insect Incense |
Charms | 0x201 | Bit 0 | Jade Disk |
Charms | 0x201 | Bit 1 | Jaguar Ring |
Charms | 0x201 | Bit 2 | Magic Gourd |
Charms | 0x201 | Bit 3 | Moxa Stick |
Charms | 0x201 | Bit 4 | Oracle Bone |
Charms | 0x201 | Bit 5 | Ruby Heart |
Charms | 0x201 | Bit 6 | Silver Sheath |
Charms | 0x201 | Bit 7 | Staff of Life |
Charms | 0x202 | Bit 0 | Sun Stone |
Charms | 0x202 | Bit 1 | Thug's Cloak |
Charms | 0x202 | Bit 2 | Wizard's Coin |
Names
The boy and dog's names have storage space for 34 characters (null terminated). However the game limits you to 15 character names. Using longer names is untested.
The name is ASCII encoded, and names can consist of the entire alphabet (upper and lowercase), the numbers 0-9, and the characters , (comma), . (period), ! (exclamation point), ' (apostrophe), \ (backslash), - (hyphen), & (ampersand), # (pound), and (space). This is the alphabet the game limits you to using. Using characters outside this alphabet is untested.
The German and Spanish versions have slightly different alphabets for the names. In the Spanish version, # is replaced by the Γ±. It is encoded in the name with the value 0xD7
, not the unicode value 0x00F1
.
In the German version, the numbers 0, and 4-9 are replaced by the capital and lower case vowels with umlauts and the eszett Γ character. Their values are encoded as follows:
Character | Code |
---|---|
Γ | 0xCB |
Γ | 0xDB |
Γ | 0xDF |
Γ€ | 0xE3 |
ΓΆ | 0xEF |
ΓΌ | 0xF3 |
Γ | 0xC6 |
The byte immediately after the character's name must be the null terminator (00). If there is no terminator, the game will crash on load.
Levels
The valid range for levels are 1-99.
Health Points
The valid range for HP is 0-999, though the max should probably be at least 1.
Experience
The valid range for experience is 0-7562471. The high value is the dog's level 99 experience. Anything more than that is pointless.
Money Types
The next offsets are for the money types in the game. Their valid range is 0-16,777,215. The high value is the max value for an unsigned 24-bit number.
Weapon Inventory
The next offsets are for the weapon inventory. There is a single bit for each weapon. The first value is the offset in the SRAM, and the second is the bit that controls it. A set bit (1) means you have the weapon, and a clear bit means you do not have it.
Bit number start at 0 and go through 7 and run right to left. For example, in the byte with bit pattern 10010010, bit 0 is clear and bit 7 is set.
Weapon Levels
The next set of offsets are for the weapon levels. The first offset is for the major level (1-3), and the second is for the minor (power up progression).
The minor level ranges from 0-255. The status screen only shows values from 0-99. To convert between the actual value and the shown value, just divide the minor level by 2.56 and ignore the decimal places.
Alchemy Ingredients
The next set of offsets are for the alchemy ingredients. Their valid range is from 0-99.
Alchemy Inventory
The next offsets are for the alchemy inventory. Just like the weapon inventory, the first value is the offset and the second is the bit that controls it. A set bit means you know the alchemy, and a clear bit means you don't.
Alchemy Levels
The next set of offsets are for the alchemy levels. The first offset is their major level (0-9), and the second is their minor level (power-up progress). The minor level can range from 0-99. For some reason, both of these offsets are 2 bytes, but since the values never exceed 255, you can just ignore their second byte and treat them like a single byte value.
Inventory Items
The next offsets are for the inventory items, which include the common items, equipment, and trade goods. In other words, anything that can be bought, sold, or traded.
Most items have a valid range of 0-6, but a few can be up to 99. These include call beads, bazooka ammunition, and all the trade goods.
Charms
The final offsets are for the charms. They are just like the weapon and alchemy inventories. The first value is the offset, and the second is the controlling bit.
Unknown
The remaining values in the SRAM are unknown. Please update this guide if there you figure them out.
The Sanity Algorithm
As described earlier, Secret of Evermore uses two bytes of sanity data to ensure the SRAM is still being saved by the battery. This section describes that algorithm.
The following is the actual algorithm in the US version, obtained using Geiger's Snes9x debugger. I have commented each line.
$8D/B469 A0 2F 03 LDY #$032F ; load 0x32F into counter
$8D/B46C 22 78 B4 8D JSL $8DB478[$8D:B478] ; jump to subroutine
$8D/B478 A9 3F 04 LDA #$043F ; a = 0x43F
$8D/B47B E2 20 SEP #$20 ; use 8-bit accumulator
$8D/B47D 18 CLC ; clear carry
$8D/B47E 7D 00 00 ADC $0000,x[$30:6666] ; add data[offset]
$8D/B481 88 DEY ; y = y - 1
$8D/B482 F0 0B BEQ $0B [$B48F] ; branch if y = 0
$8D/B484 E8 INX ; x = x + 1
$8D/B485 C2 20 REP #$20 ; use 16-bit accumulator
$8D/B487 0A ASL A ; shift left
$8D/B488 E2 20 SEP #$20 ; use 8-bit accumulator
$8D/B48A 7D 00 00 ADC $0000,x[$30:6667] ; add data[offset]
$8D/B48D 80 F2 BRA $F2 [$B481] ; branch always
$8D/B48F C2 20 REP #$20 ; use 16-bit accumulator
$8D/B491 6B RTL ; return from subroutine
The algorithm is fairly straightforward. First, we take an accumulator and initialize it with 0x043F. The first byte of game data is added to the lower byte of our accumulator. If a carry results, we ignore it.
Next we start a loop. We shift the accumulator left by 1. The shifted bit is placed in the carry. Then we add the next byte of data to the lower byte of our accumulator. The carry is also added here and reset to 0. If our addition results in a carry, it will be placed in the carry. This process is repeated for every byte of game data. Note that the carry from our addition will be ignored due to the shift that always occurs following the next iteration of the loop.
In the European versions, the algorithm is only slightly different. Change the initial value 0x43F
to 0x16FF
. This is the only difference.
Here is the C++ routine from soesrame I wrote (based on C source from phonymike).
quint16 SRAMFile::checksum(int game) const {
quint32 checksum = 0x43F;
unsigned char temp = checksum + sram[SRAM_GAME_OFFSET + 2 + game * SRAM_GAME_SIZE];
for(int i = 3; i < SRAM_GAME_SIZE; ++i){
checksum &= 0xFF00;
checksum |= temp;
checksum <<= 1;
if(checksum > 0xFFFF){
checksum -= 0xFFFF;
}
temp = checksum + sram[SRAM_GAME_OFFSET + i + game * SRAM_GAME_SIZE];
}
return static_cast<quint16>(checksum);
}
To clear up any confusion, a quint16 is a 16-bit unsigned integer and a quint32 is an unsigned 32-bit integer. These are types defined by the Qt library which is used by soesrame.
When you edit the SRAM by hand, the checksum will be wrong. To get around this, you can either fix the checksum, or have the game skip the sanity check. The latter can be accomplished by using a game gene code. DDA4-17E1
will do this. If you try to skip the sanity check with a game genie code, and have invalid game data in any of the four slots, the game is quite likely to crash.
You can get the code for a C program which will fix the sanity data for an SRAM file, as well as a Windows & Mac OS X binary here: soe-sanity.7z. For other platforms, you will need to compile it yourself. It is written in standard ANSI C, so it should run anywhere.
If you are compiling on a big endian platform, you will need to define BYTE_ORDER_BIGENDIAN
at compile time. PowerPC and Sparc machines are probably the most common big endian platforms. Intel machines (x86) are little endian.
gcc -o soe-sanity soe-sanity.c
Will build the program using gcc on a little endian platform.
gcc -DBYTE_ORDER_BIGENDIAN -o soe-sanity soe-sanity.c
Will build the program using gcc on a big endian machine. I don't have much experience with compilers other than gcc, so you'll have to figure it out yourself if you're using something else.
The program has a simple interface. Specify the sram filename, the game that needs fixing, and the version of the game (us or europe). It defaults to game 1 and the US version.
Examples:
soe-sanity "Secret of Evermore (U).srm"
soe-sanity "Secret of Evermore (G).srm" --game 2 --version europe
The first one fixes the checksum for the first game using the US algorithm. The second one fixes the checksum for the second game using the European algorithm.
soesrame - Secret of Evermore SRAM Editor
For those that don't want to play around with the SRAM directly, I have written a program that edits all the values documented here. It's also nice in that it keeps the values within their valid ranges, and handles the sanity data for you.
You can find it at http://games.technoplaza.net/soesrame/. There are binaries for Windows, Mac OS X, and Linux. It should work on any platform that supports Qt, so it should work on any unice with X Windows. The full source is available as well.
Credits & Acknowledgements
I want to thank phonymike for discovering and documenting the checksum algorithm used by Secret of Evermore. He was also very helpful in answering questions I had about his code.
Thanks to Geiger for his debugging Snes9x version and the entire Snes9x team for Snes9x. Without it, it would have been very difficult to find the SRAM offsets.
Written by John David Ratliff (webmaster at technoplaza dot net / http://www.technoplaza.net/feedback.php)
The most recent version of this guide can always be found at http://games.technoplaza.net/soesrame/sram-doc.txt
Copyright (C) 2006-2008 emuWorks (http://games.technoplaza.net/)
This document is Copyright (C) 2006 emuWorks (http://games.technoplaza.net/). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at http://www.gnu.org/licenses/fdl.html
Basically, it is free documentation in much the same way software under the GNU General Public License is free software. You can modify it, redistribute it, sell it, publish it, etc.