SFC Development Wiki
🌝
Browse Tags
New Document
Editing Lufia & The Fortress of Doom
Title
Slug
Original Slug (Reference Only)
Redirects
Image
Excerpt
Content
Write
The following information is pertaining to [Estpolis Denki](http://superfamicom.org/info/lufia-the-fortress-of-doom/) aka [エストポリス伝記](http://superfamicom.org/info/lufia-the-fortress-of-doom/) aka [Lufia & The Fortress of Doom](http://superfamicom.org/info/lufia-the-fortress-of-doom/). ## SRAM ### SRAM Checksum Algorithm To calculate the checksum for each save slot, you start with the first offset in your save slot, and add up the little endian 16-bit numbers. So, say for SLOT00, your first 16 bytes looked like this: 46 69 6C 65 30 30 E8 63 01 04 E5 E5 E5 E5 E5 E5 So, your first four bytes: "`46 69 6c 65`", are the HEX equivalent of the word "`File`" (if you don't believe me, look up the ASCII character table, and find that hex values `0x41` - `0x5A` are upper case characters and hex `0x61 - 0x7A` are lower case characters of the alphabet (A-Z and a-z respectively)). Then, your next two bytes: "`30 30`", are the HEX equivalent of the printable ASCII characters 00 (the ASCII table defines hex values `0x30 - 0x39` as the textual representations of the characters 0 - 9). Now we come to the little endian checksum part, which can be complicated, so please bear with me: Then, the next two bytes are what your checksum is currently calculated as. Since it is a 16 byte value, to get your checksum as a decimal number, you take the first number, "`0xE8`", and convert it to a decimal -- 232. Now, you take the second number, "`0x63`", and convert it to a decimal -- 99. But! Before you can add these two numbers together, you have multiply 99 by 256, since the number that exists there would actually be `0x6300`. Anyway, 99 times 256 is 25344. Then you add 232 to that, and you get: 25576. That is your checksum value. Why did I show you that complicated method? Because, we're dealing with little endian encoding. That means that "0x63" is your high order byte, and "`0xE8`" is your low order byte. The 16 bit value that is your checksum is not `0xE863`, but it is actually 0x63E8. This little tidbit of information becomes important when we start going through the save slot to compute the checksum. Now, if we were to start computing the checksum for this save slot, we would begin after the checksum bytes with the first "`0x01`". This is the low order byte, so the decimal equivalent here is 1. Now, we take the high order byte, which is "0x04". Remember, this is just like saying "0x0400" for the purposes of computing a checksum. So, convert the byte to a decimal -- in this case, 4, and then multiply it by 256 -- 1024; before adding back in 1. So, your first value in your checksum is 1025. You will continue in this manner until you reach the end of that save slot. Except, there is a caveat: what happens when your checksum value goes over `0xFFFF` (the biggest 16-bit number, aka. 65535). Well, your counter technically rolls back over to 0, and starts counting up again -- and you just throw away the extra. So you've added up your save slot and you find out that it still doesn't match the two bytes at the end of "File00"? Well, that's why the assembler code up above was important -- it not only told us how the checksum algorithm works, but it also told us the base value to begin the checksum calculation off of. In this case, it is 6502. You can either start with this value from the onset, or you can add it on as the last step in the process, but regardless, the value this yields will be the two bytes that get stored away as your checksum for this save slot. Then you just have to rinse and repeat this process for the next two save slots! (by the way, 6502 is a decimal value -- not in hex). ~~~ $00/9429 A0 FC 03 LDY #$03FC A:0070 X:616E Y:0000 ; load Y immediate with value 0x03FC $00/942C A6 1F LDX $1F [$00:001F] A:0070 X:616E Y:03FC ; load X with value at address 0x1F $00/942E A9 02 65 LDA #$6502 A:0070 X:0000 Y:03FC ; load Accumulator with 0x6502 $00/9431 18 CLC A:6502 X:0000 Y:03FC ; clear the carry flag $00/9432 7D 08 00 ADC $0008,x[$70:0008] A:6502 X:0000 Y:03FC ; add with carry starting at address 0x0008 $00/9435 E8 INX A:4AE7 X:0000 Y:03FC ; increment the X register $00/9436 E8 INX A:4AE7 X:0001 Y:03FC ; increment the X register (again) $00/9437 88 DEY A:4AE7 X:0002 Y:03FC ; decrement the Y register $00/9438 D0 F7 BNE $F7 [$9431] A:4AE7 X:0002 Y:03FB ; repeat until Y hits 0 (branch not equal) $00/943A AA TAX A:592C X:07F8 Y:0000 ; transfer the accumulator into X $00/943B 7A PLY A:592C X:592C Y:0000 ; pull the Y register off of the stack $00/943C AB PLB A:592C X:592C Y:0000 ; pulls a byte off of stack into data bank $00/943D 28 PLP A:592C X:592C Y:0000 ; pull processor status off of the stack $00/943E 60 RTS A:592C X:592C Y:0000 ; return from subroutine ~~~ Below is a C++ program that accurately handles the checksum algorithm described above: ```c // File name: main.cpp // Author: Vegetaman // Date: February 24, 2011 // Purpose: Lufia Checksum #include <iostream> #include <fstream> #define SIZE_OF_SRAM 0x2000 // SRAM is 8K large #define LUFIA_SRAM_FILE "C:\\Lufia.srm" // SRAM file location #define HALF_OF_2K 0x03FC // half of 2048 - 8 #define FILE00_OFFSET 0x0008 // 8 bytes in #define FILE01_OFFSET 0x0808 // 2K + 8 bytes in #define FILE02_OFFSET 0x1008 // 4K + 8 bytes in #define COUNTER_ROLLOVER_VALUE 0xFFFF // max 16-bit value using namespace std; int main(int argc, char *argv[]){ FILE *filePtr; // file pointer unsigned char ArrayOfSRAM[SIZE_OF_SRAM]; // array of char or bytes unsigned short accumulator; // is a 16-bit unsigned integer unsigned short register_x; // will be just like the register X unsigned short register_y; // will be just like the register Y filePtr = fopen(LUFIA_SRAM_FILE, "r"); // open file Lufia.srm -- read only // NOTE: I am reading the SRAM into an array so I don't have to do all of my // operations from the file, which would be really slow and wasteful... fread(ArrayOfSRAM, sizeof(char), SIZE_OF_SRAM, filePtr); // load the array fclose(filePtr); // always close your file handle // begin the routine of calculating the first 16-bit little endian checksum register_x = FILE00_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0){ // while Y does not equal 0 (will run 1020 times) accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // some code to throw the checksum up on the console so that it can be seen cout << "Checksum for FILE00: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // prepare to calculate the second little endian 16-bit checksum register_x = FILE01_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0){ // while Y does not equal 0 (will run 1020 times) accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // some code to throw the checksum up on the console so that it can be seen cout << "Checksum for FILE01: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // prepare to calculate the third and final little endian 16-bit checksum register_x = FILE02_OFFSET; // load the first offset into register X register_y = HALF_OF_2K; // load 0x03FC into register Y accumulator = 0x6502; // load the accumulator with the base value 6502 while(register_y != 0){ // while Y does not equal 0 (will run 1020 times) accumulator += ArrayOfSRAM[register_x]; // get the first/low byte register_x++; // increment X accumulator += ArrayOfSRAM[register_x] << 8; // get the second/high byte register_x++; // increment X accumulator &= 0xFFFF; // discarding the carry flag register_y--; // decrement Y } // now throw the last checksum up on the console so it can also be seen cout << "Checksum for FILE02: " << dec << accumulator << endl; cout << "Little Endian HEX: " << hex << (accumulator & 0xFF) << " "; cout << hex << ((accumulator >> 8) & 0xFF) << endl << endl; // now, hold the program up until the user is done reading... cout << "Press ENTER to continue..."; // prompt user getchar(); // wait for "ENTER" key return 0; // exit with success } ``` ### SRAM Structure Save slot 0 is at SRAM `0x0000`, spot 1 is at `0x0800`, spot 2 is at `0x1000` and there is extra space at `0x1800`. On an interesting note though, where the fourth save slot would be, the SRAM file I was working with says "Estpolis Biography Neverland Co.". A perfect 32 bytes starting at SRAM location `0x1800`. | Offset | Description | |--------|-------------| | `0x000 - 0x005` | "FILE0?" where 0 is the ? for this file location in SRAM. | `0x006 - 0x007` | The 16 bit little endian checksum. | `0x113 - 0x116` | 5 bytes for the Hero's name plus a null terminator (0x00). | `0x119 - 0x11E` | 5 bytes for the Hero's name a second time plus a 0x00. | `0x11F - 0x124` | 5 bytes for Lufia's name (once she joins) plus a 0x00. | `0x125 - 0x12A` | 5 bytes for Aguro's name (once he joins) plus a 0x00. | `0x12B - 0x12F` | 5 bytes for Jerin's name (once she joins) plus a 0x00. | `0x131` | This byte says how many party members you currently have. | `0x132 - 0x134` | Three bytes that contain the amount of gold your party has. | `0x13E - 0x1B5` | 120 bytes that hold the item information. | `0x1B6` | Hero's Level | `0x1B7` | Lufia's Level | `0x1B8` | Aguro's Level | `0x1B9` | Jerin's Level | `0x1C6 - 0x1C7` | Hero's HP | `0x1C8 - 0x1C9` | Lufia's HP | `0x1CA - 0x1CB` | Aguro's HP | `0x1CC - 0x1CC` | Jerin's HP | `0x1CE - 0x1CF` | Hero's MP | `0x1D0 - 0x1D1` | Lufia's MP | `0x1D2 - 0x1D2` | Aguro's MP | `0x1D3 - 0x1D4` | Jerin's MP | `0x1D6 - 0x1F5` | Hero's Magic Spells (32 bytes for 32 spells) | `0x1F6 - 0x215` | Lufia's Magic Spells (32 bytes for 32 spells) | `0x216 - 0x235` | Jerin's Magic Spells (32 bytes for 32 spells) | `0x307` | Hero's Equipped Weapon | `0x308` | Lufia's Equipped Weapon | `0x309` | Aguro's Equipped Weapon | `0x30A` | Jerin's Equipped Weapon | `0x30B` | Hero's Equipped Armor | `0x30C` | Lufia's Equipped Armor | `0x30D` | Aguro's Equipped Armor | `0x30E` | Jerin's Equipped Armor | `0x30F` | Hero's Equipped Shield | `0x310` | Lufia's Equipped Shield | `0x311` | Aguro's Equipped Shield | `0x312` | Jerin's Equipped Shield | `0x313` | Hero's Equipped Helm | `0x314` | Lufia's Equipped Helm | `0x315` | Aguro's Equipped Helm | `0x316` | Jerin's Equipped Helm | `0x317` | Hero's Equipped Shoes | `0x318` | Lufia's Equipped Shoes | `0x319` | Aguro's Equipped Shoes | `0x31A` | Jerin's Equipped Shoes | `0x31B` | Hero's Equipped Ring | `0x31C` | Lufia's Equipped Ring | `0x31D` | Aguro's Equipped Ring | `0x31E` | Jerin's Equipped Ring #### Items You get a maximum of 5 pages of items, with 12 items a page, for a maximum of 60 items. The way the data is stored is that the first byte of the pair identifies what the item is, while the second byte of the pair identifies the quantity of that item that you have. | Value | Item Name | |-------|-----------| | `00` | Empty Slot | `01` | Knife | `02` | Club | `03` | Mace | `04` | Dagger | `05` | Long Knife | `06` | Short Sword | `07` | Rod | `08` | Gladius | `09` | Glass Robe | `0A` | Brone Sword | `0B` | Staff | `0C` | Scimitar | `0D` | Rapier | `0E` | Long Sword | `0F` | Long Staff | `10` | Axe | `11` | Spear | `12` | Morning Star | `13` | Catwhip | `14` | Battle Axe | `15` | Hammer Rod | `16` | Trident | `17` | Silver Rod | `18` | Silver Sword | `19` | Buster Sword | `1A` | Zircon Rod | `1B` | Great Axe | `1C` | Grand Blade | `1D` | Zircon Axe | `1E` | Zircon Sword | `1F` | Broad Sword (cursed) | `20` | Broad Rod (cursed) | `21` | Luck Blade (cursed) | `22` | Gloom Pick (cursed) | `23` | Dual Blade | `24` | Dress | `25` | Cloth | `26` | Cloth Armor | `27` | Robe | `28` | Tan Armor | `29` | Tan Robe | `2A` | Light Armor | `2B` | Light Robe | `2C` | Chain Mail | `2D` | Chain Cloth | `2E` | Plate Cloth | `2F` | Brone Armor | `30` | Quilted Silk | `31` | Half Mail | `32` | Brone Robe | `33` | Silver Armor | `34` | Silver Robe | `35` | Plate Mail | `36` | Zircon Robe | `37` | Zircon Armor | `38` | Clear Silk | `39` | Bracelet | `3A` | Tan Shield | `3B` | Wood Shield | `3C` | Buckler | `3D` | Wood Wrist | `3E` | Kite Shield | `3F` | Round Shield | `40` | Round Wrist | `41` | Brone Shield | `42` | Tower Shield | `43` | Large Shield | `44` | Silver Wrist | `45` | Silver Plate | `46` | Zircon Wrist | `47` | Zircon Plate | `48` | Cloth Helm | `49` | Tan Helm | `4A` | Hair Band | `4B` | Wood Helm | `4C` | Glass Cap | `4D` | Brone Helm | `4E` | Red Beret | `4F` | Iron Helm | `50` | Plate Cap | `51` | Plate Helm | `52` | Glass Beret | `53` | Silver Helm | `54` | Sakret | `55` | Zircon Beret | `56` | Zircon Helm | `57` | Sandal | `58` | Cloth Shoes | `59` | Tan Shoes | `5A` | Spike Shoes | `5B` | Heeled Shoes | `5C` | Wind Shoes | `5D` | Wind Heels | `5E` | Knife Shoes | `5F` | Needle Heels | `60` | Sonic Shoes | `61` | Sonic Heels | `62` | Sword Shoes | `63` | Cat Heels | `64` | Mach Shoes | `65` | Mach Heels | `66` | Power Ring | `67` | HiPower Ring | `68` | Daze Ring | `69` | Hi Daze Ring | `6A` | Mind Ring | `6B` | Sonic Ring | `6C` | Mach Ring | `6D` | Undead Ring | `6E` | Ghost Ring | `6F` | Dragon Ring | `70` | Sea Ring | `71` | Fly Ring | `72` | Water Ring | `73` | Fire Ring | `74` | Ice Ring | `75` | Electro Ring | `76` | Flash Ring | `77` | Flame Ring | `78` | Water Ring | `79` | Blast Ring | `7A` | Frost Ring | `7B` | Might Armor | `7C` | Might Shield | `7D` | Might Helmet | `7E` | Gloom Ring | `7F` | Gloom Voice | `80` | Dummy | `81` | Brone Breast | `82` | Carbo Sword | `83` | Carbo Plate | `84` | Carbo Shield | `85` | Carbo Helm | `86` | Carbo Cap | `87` | Gloom Guard | `88` | Diamond Ring | `89` | Engage Ring | `8A` | Monster Ring | `8B` | Blue Ring | `8C` | Yellow Ring | `8D` | Red Ring | `8E` | Purple Ring | `8F` | Green Ring | `90` | White Ring | `91` | Black Ring | `92` | Heavy Ring | `93` | Wave Ring | `94` | Potion | `95` | Hi Potion | `96` | Ex Potion | `97` | Hi Magic | `98` | Ex Magic | `99` | Antidote | `9A` | Sweet Water | `9B` | Foul Water | `9C` | Awaken | `9D` | Stone Cure | `9E` | Mystery Pin | `9F` | Shriek | `A0` | Swing Wing | `A1` | Magic Guard | `A2` | Power Gourd | `A3` | Mind Gourd | `A4` | Power Potion | `A5` | Spell Potion | `A6` | Speed Potion | `A7` | Mind Potion | `A8` | Great Potion | `A9` | Float | `AA` | Smoke Ball | `AB` | Arror | `AC` | Mid Arrow | `AD` | Big Arrow | `AE` | Arrows | `AF` | Hi Arrows | `B0` | Ex Arrows | `B1` | Dragon Arrows | `B2` | Sleep Arrow | `B3` | Puzzle Arrow | `B4` | Stun Arrow | `B5` | Gloom Arrow | `B6` | Bomb | `B7` | Hi Bomb | `B8` | Ex Bomb | `B9` | Miracle | `BA` | Revive | `BB` | Pear Cider | `BC` | Sour Cider | `BD` | Lime Cider | `BE` | Plum Cider | `BF` | Apple Cider | `C0` | Hair Band | `C1` | Brooch | `C2` | Earring | `C3` | Necklace | `C4` | Stuffed Bear | `C5` | Stuffed Dog | `C6` | Stuffed Pig | `C7` | Emerald | `C8` | Opal | `C9` | Goblet | `CA` | Ear Tip | `CB` | Empty Bottle | `CC` | Gown | `CD` | Ribbon | `CE` | Fry Pan | `CF` | Small Knife | `D0` | Pot | `D1` | Chop Block | `D2` | Apron | `D3` | Dragon Egg | `D4` | Crown | `D5` | Secret Map | `D6` | Miracle Gem | `D7` | Silver Wick | `D8` | Royal Statue | `D9` | Silver Tarot | `DA` | Golden Pawn | `DB` | Crown Jewels | `DC` | Wind Flute | `DD` | Escape | `DE` | Magic Jar | `DF` | Dragon Tooth | `E0` | Grilled Newt | `E1` | Poison Pin | `E2` | Might Sword | `E3` | Straw Doll | `E4` | Long Nail | `E5` | Bomb | `E6` | Alumina | `E7` | Power Oil | `E8` | Elven Bow | `E9` | Artea's Bow | `EA` | Might Bow | `EB` | Dummy | `EC` | Dummy | `ED` | Dummy | `EE` | Dummy | `EF` | Free Door | `F0` | Sheran Key | `F1` | Letter | `F2` | Dais Key | `F3` | Shrine Key | `F4` | Pirate Key | `F5` | Light Key | `F6` | Oil Key | `F7` | Green Jade | `F8` | Red Sapphire | `F9` | Blue Jade | `FA` | Purple Newt | `FB` | Glasdar Key | `FC` | Magic Flavor | `FD` | Fairy Kiss | `FE` | Not Used | `FF` | Not Used Note that there is some oddity in this list, such as two items named "Bomb", and at least 5 "Dummy" items, and then there's "Free Door" on top of that. Not only that, but several of these items, such as "Sheran Key", go into your Scenario page, not your item list. But, I digress -- you can add them in to your SRAM file anyway. #### Magic You have a maximum amount of space for spells of 32 bytes. However, the way that magic works is that the spell listing must end with a call to `0x00` for "END OF LIST". Otherwise, if you use up all 32 slots in Hero's Magic list, he will also have every spell of Lufia's in his spell casting ability as well! And likewise, if you fill up all 32 of Lufia's spells, you can spill over into Jerin's territory. And if you fill up Jerin's... Well, you'll probably crash something, but just beware if you go to edit the game in this manner. Though with this method, you can actually give your Hero EVERY spell in the game when the team consists of only you and Lufia! Also, notice that there is no space reserved for Aguro to have magic, so you cannot just give him some spells to let him cast away. | Value | Description | |-------|-------------| | `00` | END OF LIST | `01` | Flash | `02` | Bolt | `03` | Thunder | `04` | Spark | `05` | Flame | `06` | vulcan | `07` | Dew | `08` | Water | `09` | Flood | `0A` | Bang | `0B` | Blast | `0C` | Sunder | `0D` | Frost | `0E` | Blizzard | `0F` | Glacier | `10` | Perish | `11` | Succumb | `12` | Drowsy | `13` | Fright | `14` | Drain | `15` | Dread | `16` | Deflect | `17` | Bounce | `18` | Absorb | `19` | Fake | `1A` | Trick | `1B` | Confuse | `1C` | Bravery | `1D` | Courage | `1E` | Shield | `1F` | Protect | `20` | Mirror | `21` | Statue | `22` | Strong | `23` | Stronger | `24` | Champion | `25` | Boost | `26` | Valor | `27` | Poison | `28` | Stun | `29` | Dead | `2A` | Rally | `2B` | Stone | `2C` | Waken | `2D` | Warp | `2E` | Escape | `2F` | Float | `30` | Elf | `31` | Defake | `32` | Figual | `33` | Paraiz | `34` | Elegion | `35` | Elegi | `36` | Elegio | `37` | Absobl In case you do not recognize some of the last magic spells on that list, that is because they must have been in there for test purposes. Without going too much off topic, here is what it appears that these extra magic spells do: - Defake - Agility Down - Figual - Confuse - Paraiz - Paralyze - Elegion - Thunder Spell (all enemies) - Elegi - Flash Spell (all enemies) - Elegio - Bolt Spell (all enemies) - Absobl - Absorb Magic The plus of some of these spells is that they cost only 1 or no MP at all to cast, meaning you can give them to your low level party and completely rule the entire game -- never mind the great items you could give yourself as well. ## Dictionary Reference Key The dictionary is located at `0x054E19 - 0x0553CC`. Ì, .z .& .îone.. a fake ..?. .. rich ‡ gemstone.mines. Ì .o ran .‹..` ago. Can't read some of those characters? Well, that's because the stuff that doesn't make sense has to do with punctuation and pointers to other words that get put in place. The statement that should be making is this: But, why would anyone make a fake ruby? Medan was rich in gemstone mines. But they ran out years ago. A few words are there that you can make out (between some towns person and the Princess in Medan). Such as ",", "one", "a fake", "?", "rich", "gemstone mines.", "ran", "ago.". But what about the questionable things you can't see. Let's take the first part, the "But, why would anyone make a fake ruby, which looks like this, and compare it against the dictionary file of the game (`0x54A50`): cc 2c 20 0c 7a 20 0c 26 20 0c ee 6f 6e 65 05 0c 7f 20 61 20 66 61 6b 65 20 0c 19 3f Ì, .z .& .îone.. a fake ..?. cc -> refers to an upper case "But" 2c -> comma (",") 20 -> blank space 0c 7a -> refers to a lower case (0c is "lower case"/0d is "upper case") "why" 20 -> blank space 0c 26 -> refers to a lower case "would" 20 -> blank space 0c ee -> refers to a lower case "any" 6f -> letter "o" 6e -> letter "n" 65 -> letter "e" 05 -> line/carriage return 0c 7f -> refers to a lower case "make" 20 -> blank space 61 -> letter "a" 20 -> blank space 66 -> "f" 61 -> "a" 6b -> "k" 65 -> "e" 20 -> blank space 0c 19 -> refers to a lower case "ruby" 3f -> "?" But, why would anyone make a fake ruby? After that, there is a "`04 A0`" code which I can only assume is some sort of "close textbox" and "open new text box" (and possibly a character sprite/on screen location to hook it to). But then we get to the second phrase: 0b 08 20 a0 20 72 69 63 68 20 87 20 67 65 6d 73 74 6f 6e 65 05 6d 69 6e 65 73 2e 20 cc 20 0c 6f 20 72 61 6e 20 0c 8b 05 0c 60 20 61 67 6f 2e .. rich ‡ gemstone.mines. Ì .o ran .‹..` ago. 0b 08 -> dictionary for the town name "Medan" 20 -> blank space a0 -> lower case "was" (notice it is not referenced by any 0c/0d type calls) 20 -> blank space 72 -> "r" 69 -> "i" 63 -> "c" 68 -> "h" 20 -> blank space 87 -> lower case "in" 20 -> blank space 67 65 6d 73 74 6f 6e 65 -> letters for "gemstone" 05 -> carriage/line return 6d 69 6e 65 73 -> letters for "mines" 2e -> "." 20 -> blank space cc -> upper case "But" (as 8c refers to lower case) 20 -> blank space 0c 6f -> lower case "they" 20 -> blank space 72 61 6e -> letters for "ran" 20 -> blank space 0c 8b -> lower case "out" 05 -> carriage/line return 0c 60 -> lower case "years" 20 -> blank space 61 67 6f -> letters for "ago" 2e -> "." Medan was rich in gemstone mines. But they ran out years ago. Then there's more words after that, because she's long winded, but you get the idea... It took me almost 8 hours of trial and error to figure out how the dictionary words were stored, as well as what numbers called them (which I figured out by comparing the dictionary against known phrases). The upper case/lower case was a bit harder to figure out as well. There's still some extra data that doesn't make sense yet. Also, there's a lot of words that are in the dictionary but they don't both to make a call to use them (like "one" in the above example"). There must be a reason for this, but I have yet to figure it out. Some things are a little more hidden, such as a character is referenced by "07 0X" with Hero being "`07 00`" up to Gades as "`07 0b`". Also, while to get lowercase/uppercase for the one set of dictionary words it seems to be dependent on the "0c" vs. "0d" call, the other section of dictionary words has two memory addresses that are separate for lower case or upper case (though the dictionary only exists once, it just loops back on itself I guess). For example, in the non-0c/0d dictionary, the word "there" is referenced by "`8e`", while "There" is referenced by "`ce`". Meaning they're exactly `0x40` difference. Now, for the dictionaries themselves, the character names start around "`0xe800`", the town names around "`0xe850`", there's one of 16 words (such as ('s) and ("Welcome")). These start at "`0x54a50`". Then the next dictionary (the one that has double calls to it, for being lower case/upper case) starts at "`0x54ac0`" (it contains 80 items, which makes for 160 words between upper/lower case). Then the main dictionary (called by 0c/0d for lower/upper case) starts near "`0x54c10`". Now, there's two types of dictionary calls here. Early on names: 0xe850: 8f e8 95 e8 9b e8 8f e8 refers to memory location "0xe88f", which contains the name "Alekia" 95 e8 refers to memory location "0xe895", which contains the name "Chatam" 9b e8 refers to memory location "0xe89b", which contains the name "Sheran" These places are called by text boxes by "`0b xx`", where "`0b 01`" is Alekia, "`0b 02`" is Chatam, "`0b 03`" is Sheran and so on (there's an extra space '.' in this list which makes the names start at 1 instead of 0). Later on words: 0x54ac0: 42 cb 45 cb 48 cb Here we have a bit of a problem, as memory location "`0xcb42`" is way back in the program and nowhere near what we want. So all of these later dictionary entries need to have the hex value "`0x48000`" added to them so that they point to the proper place. The unique item grabber program I made for Diablo I uses a pointer offset like this too, so it is not all that uncommon for larger programs, especially later on in the data when dealing with 16 bit little endian pointers. 42 cb + 48000H = "0x54b42" which contains the word "the" 45 cb + 48000H = "0x54b45" which contains the word "you" 48 cb + 48000H = "0x54b48" which contains the word "to" In case you didn't notice, the pointer to the next word tells you where to stop. At least, that's my guess... ### Important Codes | Values | Description | |-----------|-------------| | `04` | Text Box Close (?) | `05` | Line Return | `07` | Character Name Call | `0B` | Place Name Call | `0C` | Lower Case Dictionary | `0D` | Upper Case Dictionary | `20` | Blank Space (" ") | `2E` | Period (.) | `2B` | Double Period (..) | `20 - 7F` | Reserved Character Symbols | `00 - 0F` | Reserved Flag Calls and Specials | `80 - A9` | New Textbox (?) (Tie to Character/Position?) | `??` | Time to Wait Between Boxes (?) | Value | Name | |---------|------| | `07 00` | Hero | `07 01` | Lufia | `07 02` | Aguro | `07 03` | Jerin | `07 04` | Maxim | `07 05` | Selan | `07 06` | Guy | `07 07` | Artea | `07 08` | Daos | `07 09` | Erim | `07 0A` | Amon | `07 0B` | Gades | Values | Name | |---------|------| | `1C E8` | Lufia | `21 E8` | Aguro | `26 E8` | Jerin | `2B E8` | Maxim | `30 E8` | Selan | `35 E8` | Guy | `38 E8` | Artea | `3D E8` | Daos | `41 E8` | Erim | `45 E8` | Amon | `49 E8` | Gades | `4E E8` | <- End of List | Value A | Value B | Names | |---------|---------|-------| | `00` | `8E E8` | (00) | `01` | `8F E8` | Alekia | `02` | `95 E8` | Chatam | `03` | `9B E8` | Sheran | `04` | `A1 E8` | Treck | `05` | `A6 E8` | Lorbenia | `06` | `AE E8` | Grenoble | `07` | `B6 E8` | Kirof | `08` | `BB E8` | Medan | `09` | `C0 E8` | Surinagal | `0A` | `C9 E8` | Belgen | `0B` | `CF E8` | Jenoba | `0C` | `D5 E8` | Ruan | `0D` | `D9 E8` | Ranqs | `0E` | `DE E8` | Odel | `0F` | `E2 E8` | Lyden | `10` | `E7 E8` | Arus | `11` | `EB E8` | Platina | `12` | `F2 E8` | Carbis | `13` | `F8 E8` | Bakku | `14` | `FD E8` | Linze | `15` | `02 E9` | Marse | `16` | `07 E9` | Herat | `17` | `0C E9` | Soshette | `18` | `14 E9` | Epro | `19` | `18 E9` | Arubus | `1A` | `1E E9` | Frederia | `1B` | `26 E9` | Forfeit | `1C` | `2D E9` | Makao | `1D` | `32 E9` | Elfrea | `1E` | `38 E9` | Elfrea | `XX` | `3E E9` | <- End of List Offset: 048000 (hex) | Values | Link # | Dictionary | |--------|---------|------------| | `10` | `72 CA` | 's | `11` | `74 CA` | ed | `12` | `76 CA` | ing | `13` | `79 CA` | I'm | `14` | `7C CA` | I'll | `15` | `80 CA` | I've | `16` | `84 CA` | Alumina | `17` | `8B CA` | Sinistral | `18` | `94 CA` | Dual | `19` | `98 CA` | Falcon | `1A` | `9E CA` | Glasdar | `1B` | `A5 CA` | Welcome | `1C` | `AC CA` | Raile | `1D` | `B1 CA` | Lilah | `1E` | `B6 CA` | Reyna | `1F` | `BB CA` | Shaia | `XX` | `C0 CA` | <- End of List | LC | UC | Link # | Dictionary | |------|------|---------|------------| | `80` | `C0` | `42 CB` | the | `81` | `C1` | `45 CB` | you | `82` | `C2` | `48 CB` | to | `83` | `C3` | `4A CB` | it | `84` | `C4` | `4C CB` | of | `85` | `C5` | `4E CB` | that | `86` | `C6` | `52 CB` | is | `87` | `C7` | `54 CB` | in | `88` | `C8` | `56 CB` | and | `89` | `C9` | `59 CB` | what | `8A` | `CA` | `5D CB` | this | `8B` | `CB` | `61 CB` | go | `8C` | `CC` | `63 CB` | but | `8D` | `CD` | `66 CB` | are | `8E` | `CE` | `69 CB` | there | `8F` | `CF` | `6E CB` | no | `90` | `D0` | `70 CB` | be | `91` | `D1` | `72 CB` | we | `92` | `D2` | `74 CB` | so | `93` | `D3` | `76 CB` | do | `94` | `D4` | `78 CB` | for | `95` | `D5` | `7B CB` | have | `96` | `D6` | `7F CB` | can | `97` | `D7` | `82 CB` | me | `98` | `D8` | `84 CB` | know | `99` | `D9` | `88 CB` | don't | `9A` | `DA` | `8D CB` | he | `9B` | `DB` | `8F CB` | if | `9C` | `DC` | `91 CB` | my | `9D` | `DD` | `93 CB` | here | `9E` | `DE` | `97 CB` | yes | `9F` | `DF` | `9A CB` | on | `A0` | `E0` | `9C CB` | was | `A1` | `E1` | `9F CB` | island | `A2` | `E2` | `A5 CB` | with | `A3` | `E3` | `A9 CB` | about | `A4` | `E4` | `AE CB` | your | `A5` | `E5` | `B2 CB` | come | `A6` | `E6` | `B6 CB` | get | `A7` | `E7` | `B9 CB` | see | `A8` | `E8` | `BC CB` | can't | `A9` | `E9` | `C1 CB` | will | `AA` | `EA` | `C5 CB` | right | `AB` | `EB` | `CA CB` | now | `AC` | `EC` | `CD CB` | let | `AD` | `ED` | `D0 CB` | ok | `AE` | `EE` | `D2 CB` | at | `AF` | `EF` | `D4 CB` | take | `B0` | `F0` | `D8 CB` | just | `B1` | `F1` | `DC CB` | up | `B2` | `F2` | `DE CB` | really | `B3` | `F3` | `E4 CB` | please | `B4` | `F4` | `EA CB` | well | `B5` | `F5` | `EE CB` | not | `B6` | `F6` | `F1 CB` | all | `B7` | `F7` | `F4 CB` | you're | `B8` | `F8` | `FA CB` | good | `B9` | `F9` | `FE CB` | want | `BA` | `FA` | `02 CC` | four | `BB` | `FB` | `06 CC` | tower | `BC` | `FC` | `0B CC` | as | `BD` | `FD` | `0D CC` | from | `BE` | `FE` | `11 CC` | back | `BF` | `FF` | `15 CC` | by | `XX` | `XX` | `17 CC` | <- End of List "0c" -> lc "0d" -> uc | Value | Link # | Dictionary | |-------|---------|------------| | `00` | `19 CE` | something | `01` | `22 CE` | monsters | `02` | `2A CE` | professor | `03` | `33 CE` | doom | `04` | `37 CE` | pieces | `05` | `3D CE` | lorbenia | `06` | `45 CE` | vibration | `07` | `4E CE` | basement | `08` | `56 CE` | village | `09` | `5D CE` | going | `0A` | `62 CE` | understand | `0B` | `6C CE` | around | `0C` | `72 CE` | you'll | `0D` | `78 CE` | should | `0E` | `7E CE` | dangerous | `0F` | `87 CE` | think | `10` | `8C CE` | people | `11` | `92 CE` | cave | `12` | `96 CE` | find | `13` | `9A CE` | castle | `14` | `A0 CE` | again | `15` | `A5 CE` | gold | `16` | `A9 CE` | won't | `17` | `AE CE` | power | `18` | `B3 CE` | north | `19` | `B8 CE` | ruby | `1A` | `BC CE` | blade | `1B` | `C1 CE` | return | `1C` | `C7 CE` | battle | `1D` | `CD CE` | where | `1E` | `D2 CE` | when | `1F` | `D6 CE` | you've | `20` | `DC CE` | together | `21` | `E4 CE` | through | `22` | `EB CE` | still | `23` | `F0 CE` | doesn't | `24` | `F7 CE` | sapphires | `25` | `00 CF` | yourself | `26` | `08 CF` | would | `27` | `0D CF` | anything | `28` | `15 CF` | never | `29` | `1A CF` | because | `2A` | `21 CF` | didn't | `2B` | `27 CF` | been | `2C` | `2B CF` | nothing | `2D` | `32 CF` | lever | `2E` | `37 CF` | hello | `2F` | `3C CF` | alright | `30` | `43 CF` | thanks | `31` | `49 CF` | little | `32` | `4F CF` | descendant | `33` | `59 CF` | course | `34` | `5F CF` | only | `35` | `63 CF` | before | `36` | `69 CF` | town | `37` | `6D CF` | some | `38` | `71 CF` | remember | `39` | `79 CF` | kingdom | `3A` | `80 CF` | recently | `3B` | `88 CF` | great | `3C` | `8D CF` | wish | `3D` | `91 CF` | strong | `3E` | `97 CF` | without | `3F` | `9E CF` | treasure | `40` | `A6 CF` | time | `41` | `AA CF` | thought | `42` | `B1 CF` | strange | `43` | `B8 CF` | looking | `44` | `BF CF` | heard | `45` | `C4 CF` | over | `46` | `C8 CF` | today | `47` | `CD CF` | tell | `48` | `D1 CF` | look | `49` | `D5 CF` | like | `4A` | `D9 CF` | believe | `4B` | `E0 CF` | restored | `4C` | `E8 CF` | help | `4D` | `EC CF` | father | `4E` | `F2 CF` | always | `4F` | `F8 CF` | wonder | `50` | `FE CF` | must | `51` | `02 D0` | matter | `52` | `08 D0` | king | `53` | `0C D0` | everything | `54` | `16 D0` | country | `55` | `1D D0` | we're | `56` | `22 D0` | sorry | `57` | `27 D0` | night | `58` | `2C D0` | need | `59` | `30 D0` | happened | `5A` | `38 D0` | found | `5B` | `3D D0` | everyone | `5C` | `45 D0` | shop | `5D` | `49 D0` | even | `5E` | `4D D0` | thank | `5F` | `52 D0` | someone | `60` | `59 D0` | years | `61` | `5E D0` | world | `62` | `63 D0` | vibrations | `63` | `6D D0` | true | `64` | `71 D0` | saying | `65` | `77 D0` | returned | `66` | `7F D0` | princess | `67` | `87 D0` | isn't | `68` | `8C D0` | aren't | `69` | `92 D0` | worried | `6A` | `99 D0` | they're | `6B` | `A0 D0` | destroy | `6C` | `A7 D0` | one | `6D` | `AA D0` | lately | `6E` | `B0 D0` | wouldn't | `6F` | `B8 D0` | they | `70` | `BC D0` | said | `71` | `C0 D0` | leave | `72` | `C5 D0` | couldn't | `73` | `CD D0` | things | `74` | `D3 D0` | sure | `75` | `D7 D0` | many | `76` | `DB D0` | enough | `77` | `E1 D0` | hope | `78` | `E5 D0` | give | `79` | `E9 D0` | too | `7A` | `EC D0` | why | `7B` | `EF D0` | who | `7C` | `F2 D0` | then | `7D` | `F6 D0` | stay | `7E` | `FA D0` | rubies | `7F` | `00 D1` | make | `80` | `04 D1` | maberia | `81` | `0B D1` | long | `82` | `0F D1` | cinnamon | `83` | `17 D1` | careful | `84` | `1E D1` | attacked | `85` | `26 D1` | anyway | `86` | `2C D1` | she | `87` | `2F D1` | more | `88` | `33 D1` | three | `89` | `38 D1` | these | `8A` | `3D D1` | south | `8B` | `42 D1` | out | `8C` | `45 D1` | first | `8D` | `4A D1` | after | `8E` | `4F D1` | sacrifice | `8F` | `58 D1` | reward | `90` | `5E D1` | man | `91` | `61 D1` | how | `92` | `64 D1` | fine | `93` | `68 D1` | cooper | `94` | `6E D1` | knights | `95` | `75 D1` | west | `96` | `79 D1` | stop | `97` | `7D D1` | magic | `98` | `82 D1` | level | `99` | `87 D1` | could | `9A` | `8C D1` | ahead | `9B` | `91 D1` | used | `9C` | `95 D1` | him | `9D` | `98 D1` | called | `9E` | `9E D1` | wrong | `9F` | `A3 D1` | we'll | `A0` | `A8 D1` | place | `A1` | `AD D1` | floor | `A2` | `B2 D1` | bring | `A3` | `B7 D1` | brant | `A4` | `BC D1` | such | `A5` | `C0 D1` | ship | `A6` | `C4 D1` | mean | `A7` | `C8 D1` | islands | `A8` | `CF D1` | into | `A9` | `D3 D1` | came | `AA` | `D7 D1` | already | `AB` | `DE D1` | wonderful | `AC` | `E7 D1` | southeast | `AD` | `F0 D1` | northwest | `AE` | `F9 D1` | commander | `AF` | `02 D2` | apologize | `B0` | `0B D2` | whatever | `B1` | `13 D2` | wanted | `B2` | `19 D2` | them | `B3` | `1D D2` | switch | `B4` | `23 D2` | sapphire | `B5` | `2B D2` | might | `B6` | `30 D2` | later | `B7` | `35 D2` | forest | `B8` | `3B D2` | fight | `B9` | `40 D2` | city | `BA` | `44 D2` | bridge | `BB` | `4A D2` | way | `BC` | `4D D2` | got | `BD` | `50 D2` | old | `BE` | `53 D2` | mark | `BF` | `57 D2` | feel | `C0` | `5B D2` | down | `C1` | `5F D2` | did | `C2` | `62 D2` | we've | `C3` | `67 D2` | small | `C4` | `6C D2` | seems | `C5` | `71 D2` | other | `C6` | `76 D2` | light | `C7` | `7B D2` | haven't | `C8` | `82 D2` | has | `C9` | `85 D2` | forgive | `CA` | `8C D2` | were | `CB` | `90 D2` | spirit | `CC` | `96 D2` | shrine | `CD` | `9C D2` | happen | `CE` | `A2 D2` | under | `CF` | `A7 D2` | things | `D0` | `AC D2` | supposed | `D1` | `B4 D2` | spiritual | `D2` | `BD D2` | southwest | `D3` | `C6 D2` | shouldn't | `D4` | `CF D2` | possible | `D5` | `D7 D2` | medicine | `D6` | `DF D2` | left | `D7` | `E3 D2` | knows | `D8` | `E8 D2` | important | `D9` | `F1 D2` | care | `DA` | `F5 D2` | away | `DB` | `F9 D2` | alone | `DC` | `FE D2` | surrounded | `DD` | `08 D3` | say | `DE` | `0B D3` | repair | `DF` | `11 D3` | problem | `E0` | `18 D3` | much | `E1` | `1C D3` | memory | `E2` | `22 D3` | girlfriend | `E3` | `2C D3` | girl | `E4` | `30 D3` | gets | `E5` | `34 D3` | defeat | `E6` | `3A D3` | better | `E7` | `40 D3` | anytime | `E8` | `47 D3` | young | `E9` | `4C D3` | raise | `EA` | `51 D3` | piron | `EB` | `56 D3` | yeah | `EC` | `5A D3` | wait | `ED` | `5E D3` | best | `EE` | `62 D3` | any | `EF` | `65 D3` | tunnel | `F0` | `6B D3` | second | `F1` | `71 D3` | order | `F2` | `76 D3` | mountain | `F3` | `7E D3` | memories | `F4` | `86 D3` | makes | `F5` | `8B D3` | made | `F6` | `8F D3` | live | `F7` | `93 D3` | kill | `F8` | `97 D3` | items | `F9` | `9C D3` | information | `FA` | `A7 D3` | hey | `FB` | `AA D3` | her | `FC` | `AD D3` | grief | `FD` | `B2 D3` | disappeared | `FE` | `BD D3` | daughter | `FF` | `C5 D3` | business | `XX` | `CD D3` | <- end of list _SRAM details algorithm description, C++ checksum program and text dictionary compression by [Vegetaman](https://sites.google.com/site/vegetaman/home). SRAM Checksum traced and described by [KingMike](http://kingmike.emuxhaven.net/)._
Preview
Tags
Delete
Drop files here or click to upload.