The following information is pertaining to Estpolis Denki aka エストポリス伝記 aka 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:
// 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. SRAM Checksum traced and described by KingMike.