Save file structure

This forum is for discussions on how to edit what can not be edited through the txt files, needless to say this isn't about battle net hacking.

Moderators: Nefarius, Havvoric

1
100%
 
Total votes: 1

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Save file structure

Post by Jarulf » Mon Feb 24, 2003 3:13 pm

This post will outline the current layout of the save file (version 9.2). Please post notes information or other comments in the thread below. I will update this first post with information over time and due to input. Please note that part of the info comes fro information from the links given below by various posters.


Code: Select all

Summary of the various sections. Note the header's ID is actually a file ID, but you get the idea :)

[color=blue]ID, content[/color]
0xaa55aa55, header
"Woo!", Quest info
"WS", Waypoint info
0x7701, NPC info
"gf", Main stats info
"if", Skill info
"JM", Main itemlist
"JM", Corpse item list
"jf", Mercenary section (expansion char only)
"kf", Iron Golem list (expansion char only)

Note that individual items ALSO has the JM ID as do the Mercenary section if you have one. Confusing, yes :)


[color=blue]Header[/color]
+0000 file identifier, 0xaa55aa55
+0004 file version 92
+0008 file size
+000c checksum, d2fog.27f5
+0010 0 if [ptClient+428]b is 0, otherwise 1
+0014 character name, 16 bytes
+0024 flags from [ptClient+0a]b, bit 5 (of 0-7) is set by SAVE code if expansion game
+0025 acts done (see link in post below for more info)
(+0024 and +0025 is actually returned as a word from [ptClient+0a]w
+0026 empty filler to the 2 previous bytes, 0
+0027 empty filler to the 2 previous bytes, 0
+0028 character class from [ptClient+08]b, hopefully matches that of hUnit :)
+0029 hard coded to 0x10
+002a hard coded to 0x1e
+002b clvl from hUnit
+002c ? from [ptClient+454]dw
+0030 time
+0034 -1, function always return -1
+0038 32 2-byte values, supposedly skills assigned to function keys
+0078 8 2-byte values, supposedly skills assigned to mouse buttons
+0088 16 2-byte values (could be 8 4-byte values) set by d2common.283a, seems to relate to appearance of items/character
+00a8 act playing in normal difficulty
+00a9 act playing in nightmare difficulty
+00aa act playing in hell difficulty

Of the three values above, 2 will be 0 the other will be the actual value with bit 7 set to indicate which difficulty is saved

+00ab from [ptGame+78], should be initial seed of game
+00af start of mercenary info, much comes from 6fc71c10 (as ebx+xx), needs checking anyway:

+00af flags, only 00100000 seems to be in use set if 6fcad880 returns non 0
+00b3 ebx+00
+00b7 ebx+04, then subtract [d2common.2957+148], should be from mercenary txt
+00b9 ebx+08
+00b9 exp of mercenary from hUnit

+00bf to +014e empty, all 0. These bytes can be used for your own custom saved information. just make sure you load it properly too :)

That concludes the main header of the save file. Next follow various sections of information.

[color=blue]Quest info[/color]
+014f "Woo!"
+0153 version of block? 0x00000006
+0157 blocksize, 0x012a
+0159 quest info stuff (to come)

[color=blue]Waypoint info[/color]
+0279 "WS"
+027b version of block? 0x00000001
+027f block size 0x0050
+0281 waypoint info stuff (to come)

[color=blue]NPC info[/color]
+02c9 0x7701
+02cb block size, 0x0034
+02cd NPW info stuff (to come)

[color=blue]Main stats info[/color]
+02fd "gf"
+02ff bitfield indicating which of the 16 main stats follow
+0301 stats (dw) indicated by bitfield, from hUnit

The above section is of variable length

[color=blue]Skill info[/color]
+03xx+00 "if"
+03xx+02 base slvl (b) for 30 skills

[color=blue]Item info[/color]
+03xx+30 Start of item info

The item info is mostly made up of various lists of items. A list will usually contain a header that has a two byte "string", for example "JM" (I would say many of those strings you find are actually short for various programmer names) followed by a word indicating the number of items in the list to follow. Next follow each item itself and I think most (all?) of them will start of with the "JM" string (yes confusing since it is used in two different cases so to speak) followed by item specific data. Socketed items follow right after the item they are socketed into and are not counted in the list header for number of items in the list.

The various lists (of which not all need to be present) are:

[color=blue]Main itemlist[/color]
+00 "JM"
+02 number of items in list
+04 List of items

This includes all items your character have, including those in the stash.


[color=blue]Corpse item list[/color]
+00 "JM"
+02 number of corpses
+04 if coprse exists, 12 bytes not used
+10 if corpse exists, list of items

The game obviously have had the ability to save several corpses for a character. Currently, only one corpse is supported though. The "number of corpses" can only be set to 0 or 1 and can thus be thought of as a flag for the existance of a corpse. If the corpse exists there follow 4 dwords of random data. The first value is actually set from an uninitialized local variable and can thus be anything that happens to sit on the stack. The next two values are x and y coordinates (not sure if they actually make any sense though). None of the three values are read by the code by the way. I guess the first value was once which map the coprse was on or something. In any case, thsoe 12 bytes can be ignored. Next follows a normal list of items if a corpse existed.

[color=blue]Mercenary item list[/color]
+00 "jf" (only exists if expansion game regardless of if you have a mercenary or not)
+02 "JM" (only exists if you actually have a mercenary)
+04 number of items in list (only exists if you actuall have a mercenary)
+06 List of items (only exists if you actually have a mercenary)

In an expansion game you then get:

[color=blue]Iron Golem list[/color]
+00 "kf"
+02 0 or 1 (b), a 1 indictaing an Iron Golem exists
+03 if an Iron Golem eixts the item used to create it will follow here




Next to be added is detailed info on the data for each item saved in a list. It is a quite variable content depending on the item and also quite large. It will follow below. Note that the same function that compress an item for saving seems to be used to compress items for sending between server and client. It has the ability to mask many properties in such cases for non identified items for example when you gamble I would guess. I will not at all handle that here but only the case for actually saving an item.

Since items are saved in a very compressed way on bit levels (and not bytes). In addition, what actually is saved varies with the item type and so on. I will thus not give offsetts but rather the number of bits (in decimal notation) something is saved in. The order would be the one the game places them though, so disregarding the fact that some section might be missing for some items the order should NEVER be another than the one I list below.


[color=blue]Common item data[/color]
16 "JM"
32 flags [ptItemstats+18], ptItemstats is [hUnit+70]

Note, the flags are somewhat modified, some removed and some set by the save code but in much should be identical with the run time flags.

10 version of item, 0, 1 or 100. 100 are new expansion items, 1 are new classic items and 0 is old pre expansion items. Note that all items should be converted to new ones in a current game (I think) at least no item should remain as version 0. 

3 mode, from hUnit+0c

The mode of items seems to mostly be were the item actually is "located", as oposed to some action it performs. See the thread maping out the ptUnit (which I call hUnit in this thread) for more info. Although I can't imagine a situation were the item is in mode 3 (ground) or 5 (whatever that might be) the save file has a special case for them. In that case, the mode would be followed by 2 32 bit x and y coordinates (from d2common.2856 and 2859). For other items we instead have the three following entries.

4 body location [ptItemstats+40]. This tells were, if at all, the item is equiped

4 x coordinate in inventory (d2common.2856)

4 y coordinate in inventory (d2common.2859)

3 page number PLUS 1. The page tells where in the invenotry the item is. Example of different pages are stash cube, main invenotry on character and so on. Pages like trade screen and such should not matter although I am not sure what happens if the game tries to save your character while the trade screen is open, you might end up with items on that page too for example. Same with an item on the cursor.

32 item code

The 3 letter code each base item have. This is for  non ears only (flag 00010000 in the flag data above is set for ears). Ears does not save the code, instead they have:

 3 class of ear
 n*7 name of ear. This is the character name of the ear and is ended by a 0 value. The name is stored at [ptItemstats+44] (first letter) and the following at [ptItemstats+46] and onwards.

For the interested, in non saved structures, if the item is gold (which obviously never save as items), the ammount of gold is inserted after the item code. First comes a 1 bit indicating the size. A 1 indicate a 32 bit value a 0 indicate a 12 bit value. That 12/32 bit value then follows and specify the ammount of gold.

Compact save items will have no other data than the above one. Flag 00200000 is set on compact save items. Note that flag is set only upon save, not normally in the flag register. It will be cleared in the process of loading. The compact saving info of an item is fetched from the appropriate item.txt file upon saving and checked for.




[color=blue]Non compact save item data[/color]
3 number of socketed items
32 creation seed, from [hUnit+34]
7 ilvl, from [ptItemstats+28]
4 quality, from [ptItemstats+00]
1 flag for variable picture (itemtypes +27)
3 if flag=1, number indicating which picture to use
1 flag for autoprefix on item
11 if flag=1, info on which autoprefix

Next follows data depending on the quality of the item. 

Low quality
3 type of low quality property

Normal quality
No special data saved

Superior
3 type of superiorproperty

Magic
11 which prefix the item has
11 which suffix the item has

In both cases it is the entry in the appropriate affix table (were a 0 value indicate none). This applies to the autoprefix above as well.

Rare and Crafted
8 which prefix name the item has
8 which suffix name the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has

Each rare item can have up to 3 prefixes and 3 suffixes.

Set
12 which set (in setitems.txt) the item is part of

Unique
12 which unique (in unqiue.txt) the item is


...more to come

Last edited by Jarulf on Thu Mar 13, 2003 11:41 pm, edited 10 times in total.

Hammerman
Junior Member
Champion of the Light
Posts: 263
Joined: Sun Jun 02, 2002 2:08 am

Hand-picked

Re: Save file structure

Post by Hammerman » Mon Feb 24, 2003 4:48 pm

There was posted some time ago a link to a page detailing the format of the .d2s file (along with checksum info). I think it was http://www.xmission.com/~trevin/DiabloI ... ormat.html , I at least have that bookmarked.

By all means, post whatever notes you have. I started looking at the ptClient structure since your recent post about it, and from that the loading of the .d2s file. So if you have anything that could help me (or others) on it, please post it.

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Mon Feb 24, 2003 4:58 pm

[quote="Hammerman";p="71819"]There was posted some time ago a link to a page detailing the format of the .d2s file (along with checksum info). I think it was http://www.xmission.com/~trevin/DiabloI ... ormat.html , I at least have that bookmarked.

By all means, post whatever notes you have. I started looking at the ptClient structure since your recent post about it, and from that the loading of the .d2s file. So if you have anything that could help me (or others) on it, please post it.[/quote]

Ahh, good source. On a quick glance I found some errors or additions he was not aware of, but he has also looked in more details of sections I have not bothered at all with, so should help a lot. Thanks. Will try to see what I can compile myself of it. The code is slightly "messy" since it handle multiple versions so you need to trace through it, you can't just start looking at something that looks interesting since you might end up later realising it was for an older version.

User avatar
Paul Siramy
Retired staff
Principality
Posts: 2828
Joined: Sat May 25, 2002 2:39 pm
Location: La Garenne Colombes (near Paris)
France

Hand-picked

Re: Save file structure

Post by Paul Siramy » Mon Feb 24, 2003 7:58 pm

I have tried to decipher that .d2s file a loooooong time ago, I stopped when the 1.09 was released, because it inroduced a checksum in the savegame, so I couldn't edit my saveggame and test them anymore.

I think many datas will be redundant with the link Hammerman gave you, but I hope you'll still find something interesting : http://paul.siramy.free.fr/d2ref/eng/
Last edited by Paul Siramy on Mon Feb 24, 2003 8:05 pm, edited 1 time in total.

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Mon Feb 24, 2003 8:49 pm

[quote="Paul Siramy";p="71841"]I have tried to decipher that .d2s file a loooooong time ago, I stopped when the 1.09 was released, because it inroduced a checksum in the savegame, so I couldn't edit my saveggame and test them anymore.

I think many datas will be redundant with the link Hammerman gave you, but I hope you'll still find something interesting : http://paul.siramy.free.fr/d2ref/eng/[/quote]

Thanks. Some of the links to jamella and such seems to not work any more though.

As for the checksum, it is a simple little routine found in fog.dll:

Code: Select all

Exported fn(): Ordinal:27F5 - Ord:27F5h
:6FF52410 56                      push esi
:6FF52411 8B74240C                mov esi, dword ptr [esp+0C]
:6FF52415 33C0                    xor eax, eax
:6FF52417 33C9                    xor ecx, ecx
:6FF52419 85F6                    test esi, esi
:6FF5241B 7E1E                    jle 6FF5243B
:6FF5241D 53                      push ebx
:6FF5241E 57                      push edi
:6FF5241F 8B7C2410                mov edi, dword ptr [esp+10]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:6FF52437(C)
|
:6FF52423 33D2                    xor edx, edx
:6FF52425 33DB                    xor ebx, ebx
:6FF52427 8A1439                  mov dl, byte ptr [ecx+edi]
:6FF5242A 85C0                    test eax, eax
:6FF5242C 0F9CC3                  setl bl
:6FF5242F 03D3                    add edx, ebx
:6FF52431 41                      inc ecx
:6FF52432 3BCE                    cmp ecx, esi
:6FF52434 8D0442                  lea eax, dword ptr [edx+2*eax]
:6FF52437 7CEA                    jl 6FF52423
:6FF52439 5F                      pop edi
:6FF5243A 5B                      pop ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:6FF5241B(C)
|
:6FF5243B 5E                      pop esi
:6FF5243C C20800                  ret 0008
If I recall correctly (and from the look), the first parameter is a pointer to the first byte to checksum on so to speak, and the second parameter is the number of bytes to do it on.
Last edited by Jarulf on Mon Feb 24, 2003 8:50 pm, edited 1 time in total.

Hammerman
Junior Member
Champion of the Light
Posts: 263
Joined: Sun Jun 02, 2002 2:08 am

Hand-picked

Re: Save file structure

Post by Hammerman » Mon Feb 24, 2003 9:45 pm

[quote="Jarulf";p="71821"]The code is slightly "messy" since it handle multiple versions so you need to trace through it, you can't just start looking at something that looks interesting since you might end up later realising it was for an older version.[/quote]

I had thought of that, and am now looking at it from early part of the player joining process, so I can trace from loading the .d2s in memory to the end of the spawning process.

You wouldn't happen to have more (code related) notes than the checksum funtion, would you?

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Mon Feb 24, 2003 10:59 pm

[quote="Hammerman";p="71858"][quote="Jarulf";p="71821"]The code is slightly "messy" since it handle multiple versions so you need to trace through it, you can't just start looking at something that looks interesting since you might end up later realising it was for an older version.[/quote]

I had thought of that, and am now looking at it from early part of the player joining process, so I can trace from loading the .d2s in memory to the end of the spawning process.

You wouldn't happen to have more (code related) notes than the checksum funtion, would you?[/quote]

I have some. I have only looked through swiftly for various parts not specifically noting exactly what happens (mostly geting a general feel and such) and also not connecting the parts properly or noting parameters passed. I can go through it more in detail if you want (also, anticipating changes in 1.10 and don't want to spend to much time in 1.09).

I will try to put all info on the save fil in the first post (a la hUnit thread). Seems that with the links I got here, it would be possible to allready map most of it. I will try to not simply copy stuff though and mostly (and slowly when I have time :) ), put in it once I have looked at itmyself or verified it.

The main load file should be at 6fc80730. That is where the file is actually opened (and the .d2s is appended for example). Note that this function can be called from two different places and the functions calling are just in front of it. I have not bothered much since I have not cared about what happens before, I am happy when I found the actual file open since that is what I was interested in). It will later check the version (current version is 9.2 or 92). It branches for current version and older versions. Current version calls 6fc82a10 which can be seen as a "load version 92 save file"-. It sson calls 6fc81a70, which I believe was the one you reffered to in the other thread. It makes a whole bunch of sanity and error test on the file. It also seems to initiate things like the assigned skills to mouse buttons and function keys and such. The call to 6fc819c0, check characvter for death and hardcore, if you load expansion char to normal game and such (basically more checks).

Back to the load v92 again, it seems to do a whole bunch of stuffs seting up the client and such and eventually get back to loading the actual character (or that påart might be for not loading character, perhaps with errors no idea) arround 6fc82baf. It will basically load each section of the save file, one by one (the links above has more details about each section, I have only looked at some and mostly on the general layout of them (mostly in the saving of a char part).

Saving a char (current version only of course) seem to be 6fc7df30, which in various ways call the main save function at 6fc816d0. It will similary save each section of the save file in turn. the header for example will be saved at the call to 6fc80d10.

Well that is mostly what I have. I have checked some of the parts more, mostly to pinpoint what is saved were. I will add it to the first post here as explanation of the save file I know.

Hope it helped.

User avatar
Alkalund
Retired Admin
Throne
Posts: 7597
Joined: Sun May 26, 2002 5:54 pm
Location: Toronto, Ontario, Canada

Hand-picked

Re: Save file structure

Post by Alkalund » Tue Feb 25, 2003 3:38 am

This discussion is very instructive and the contents much valuable, I'm putting this as a sticky topic. Please continue :)

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Wed Feb 26, 2003 1:44 pm

I got a problem. Seems I am mixing up various save file versions for items :(

As far as I can see, the game (of course) only have one save file version the current. But need to handl all sort of versions for loading. Now as far as I could see, D2common.2a81 is used for saving. It matches quite well D2common.2a82 for loading. BUT loading current version seems to use D2common.2a83 which is different (well more simple and streamlined) for the inital part (the rest is in a code section I have not with me at the moment). Anyway, this seems strange to me. Anyone that has some clue? I compared to the links given in this thread, and 2a83 clearly match that one.

Would hate spending time on some older version :)

Hammerman
Junior Member
Champion of the Light
Posts: 263
Joined: Sun Jun 02, 2002 2:08 am

Hand-picked

Re: Save file structure

Post by Hammerman » Wed Feb 26, 2003 11:19 pm

Great stuff! Both the code pointers you gave, and the save file description :)

Only one comment, haven't looked into save code yet (currently in over my head with pathfinding code, bah):
Jarulf wrote:10 version of item, 0, 1 or 100. 100 are new items, 0 and 1 are older items from pre expansion.
Isn't it 100 = expansion, 1 = classic (current version), 0 = pre-1.08 items? I'm not sure though, the game seems to have some different numbers to indicate versions. Ie. at ptGame+7c is 1/0 for lod/classic. At +74 is 100/1? for lod/classic (not sure about that 1). This is what the item gets as version. In the affix spawning code, the part where it finds a random value between min-max (same code for all), it does:

if version 1 or above, rnd[max - min + 1]
else, rnd[max - min]

Seems weird to me.

Oh, and if you're still having trouble finding code for current item save version. Seems to me that it is at 6FC4D040, it passes in a pointer to the starting list (with the JM header). Haven't looked anything more at it, might even not be of any help :-|
Last edited by Hammerman on Wed Feb 26, 2003 11:19 pm, edited 1 time in total.

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Re: Save file structure

Post by Myhrginoc » Thu Feb 27, 2003 2:49 am

0 = legacy items <1.08
1 = d2classic 1.08+
100 = LoD

This is used in the affixes and items tables, so any item imported by a pre-1.08 character can continue to be supported. This feature still works, you can generate fully-equipped classic characters for 1.05-1.06 if you still have Jamella v3. (It makes quill rat drop testing in Hell diff a little more bearable, heh heh.) But other than legacy items and reading old character save files, there is nothing I know of that requires legacy support. Then you only need know that the game is D2C (1.08+) or LoD, so you don't need three states in a game structure as you do in an item structure or data tables.
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

User avatar
Paul Siramy
Retired staff
Principality
Posts: 2828
Joined: Sat May 25, 2002 2:39 pm
Location: La Garenne Colombes (near Paris)
France

Hand-picked

Re: Save file structure

Post by Paul Siramy » Thu Feb 27, 2003 4:32 am

Unknown list
+00 "kf"
+02 0 or 1 (b)
+03 if 1 an item follows
I bet this is the Item the Metal Golem is made from, since this golem is now saved.

Just to be sure, if you set the Player number of corpse to 2, does the function read the 2nd corpse datas (or at least skip it) ? That way we'll know if it's a Boolean or a number.

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Thu Feb 27, 2003 4:43 pm

>Isn't it 100 = expansion, 1 = classic (current version), 0 = pre-1.08
>items? I'm not sure though, the game seems to have some different
>numbers to indicate versions. Ie. at ptGame+7c is 1/0 for lod/classic. At
>+74 is 100/1? for lod/classic (not sure about that 1). This is what the
>item gets as version. In the affix spawning code, the part where it finds
>a random value between min-max (same code for all), it does:


Yeah, you are correct. I have just never bothered with the exact details, only ever looking at the latest version of things in the code-

>if version 1 or above, rnd[max - min + 1]
>else, rnd[max - min]
>
>Seems weird to me.

No, it is not weired. Since the old code had in several places buggy code to determine a random value within a range the code to read those old items (since they are saved as seeds only and need to be recreated upon load, that is imported to later versions), the bug needs to be recreated too :)


>Oh, and if you're still having trouble finding code for current item save
>version. Seems to me that it is at 6FC4D040, it passes in a pointer to the
>starting list (with the JM header). Haven't looked anything more at it,
>might even not be of any help


Yeah, the load code is in that area too. The problem is that I never get there in the save code on a quick glance :)

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Thu Feb 27, 2003 4:46 pm

Paul Siramy";p="72725" wrote:
Unknown list
+00 "kf"
+02 0 or 1 (b)
+03 if 1 an item follows
I bet this is the Item the Metal Golem is made from, since this golem is now saved.

Just to be sure, if you set the Player number of corpse to 2, does the function read the 2nd corpse datas (or at least skip it) ? That way we'll know if it's a Boolean or a number.
Ahh, yes, good observation. It should be the iron golem items indeed. Didn't think of it or that he was saved.

As for the number of bodies, yes it is indeed a number and not really a flag. Or rather the save code will only set 0 or 1. The load code WILL loop through the number of bodies but there is no code to actually read them correctly such as the x/y coordinates and so on. So it will most likely end up with a crash or or simply ignoring extra body data that you introduce in the save file.

I will update the information as soon as I get time. Note that editing a post does not mark the thread as having new post or content.
Last edited by Jarulf on Thu Feb 27, 2003 4:47 pm, edited 1 time in total.

User avatar
Alkalund
Retired Admin
Throne
Posts: 7597
Joined: Sun May 26, 2002 5:54 pm
Location: Toronto, Ontario, Canada

Hand-picked

Re: Save file structure

Post by Alkalund » Fri Feb 28, 2003 2:47 am

Jarulf";p="72862" wrote:Note that editing a post does not mark the thread as having new post or content.
This was the case before the forum update. Since the forum update, whenever a post in a topic is edited, or a new post is made, the topic is marked as 'unread', so people know there is new content in it ;)

User avatar
Brother Laz
Forum Legend
Dominion
Posts: 6715
Joined: Mon Jun 03, 2002 10:06 am
Location: Fallen Like Lightning

Hand-picked

Re: Save file structure

Post by Brother Laz » Fri Feb 28, 2003 11:08 am

And it even randomly marks post as unread even if nothing at all changed to them. :P

Anyway.........
19.may.2007 | Adun Tori Laz.
Median XL released!
Flesyht sa ruobhgien yht etah.


y dont u play the game the way its supposta be played? -SlothNathan

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Fri Feb 28, 2003 1:31 pm

Brother Laz";p="73104" wrote:And it even randomly marks post as unread even if nothing at all changed to them. :P

Anyway.........
Ohh, I have made some small changes to the first post and the info every now and then. As I said I was not aware it affected the status since for me, it is all messed up for all forums here it seems (no idea if it have anything to do with the fact that I had my computer run about 1 year back in date for various reasons).

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Sun Mar 16, 2003 6:54 pm

[quote=Jarulf";p="71793"]This post will outline the current layout of the save file (version 9.2). Please post notes information or other comments in the thread below. I will update this first post with information over time and due to input. Please note that part of the info comes fro information from the links given below by various posters.


Code: Select all

Summary of the various sections. Note the header's ID is actually a file ID, but you get the idea :)

[color=blue]ID, content[/color]
0xaa55aa55, header
"Woo!", Quest info
"WS", Waypoint info
0x7701, NPC info
"gf", Main stats info
"if", Skill info
"JM", Main itemlist
"JM", Corpse item list
"jf", Mercenary section (expansion char only)
"kf", Iron Golem list (expansion char only)

Note that individual items ALSO has the JM ID as do the Mercenary section if you have one. Confusing, yes :)


[color=blue]Header[/color]
+0000 file identifier, 0xaa55aa55
+0004 file version 92
+0008 file size
+000c checksum, d2fog.27f5
+0010 0 if [ptClient+428]b is 0, otherwise 1
+0014 character name, 16 bytes
+0024 flags from [ptClient+0a]b, bit 5 (of 0-7) is set by SAVE code if expansion game
+0025 acts done (see link in post below for more info)
(+0024 and +0025 is actually returned as a word from [ptClient+0a]w
+0026 empty filler to the 2 previous bytes, 0
+0027 empty filler to the 2 previous bytes, 0
+0028 character class from [ptClient+08]b, hopefully matches that of hUnit :)
+0029 hard coded to 0x10
+002a hard coded to 0x1e
+002b clvl from hUnit
+002c ? from [ptClient+454]dw
+0030 time
+0034 -1, function always return -1
+0038 32 2-byte values, supposedly skills assigned to function keys
+0078 8 2-byte values, supposedly skills assigned to mouse buttons
+0088 16 2-byte values (could be 8 4-byte values) set by d2common.283a, seems to relate to appearance of items/character
+00a8 act playing in normal difficulty
+00a9 act playing in nightmare difficulty
+00aa act playing in hell difficulty

Of the three values above, 2 will be 0 the other will be the actual value with bit 7 set to indicate which difficulty is saved

+00ab from [ptGame+78], should be initial seed of game
+00af start of mercenary info, much comes from 6fc71c10 (as ebx+xx), needs checking anyway:

+00af flags, only 00100000 seems to be in use set if 6fcad880 returns non 0
+00b3 ebx+00
+00b7 ebx+04, then subtract [d2common.2957+148], should be from mercenary txt
+00b9 ebx+08
+00b9 exp of mercenary from hUnit

+00bf to +014e empty, all 0. These bytes can be used for your own custom saved information. just make sure you load it properly too :)

That concludes the main header of the save file. Next follow various sections of information.

[color=blue]Quest info[/color]
+014f "Woo!"
+0153 version of block? 0x00000006
+0157 blocksize, 0x012a
+0159 quest info stuff (to come)

[color=blue]Waypoint info[/color]
+0279 "WS"
+027b version of block? 0x00000001
+027f block size 0x0050
+0281 waypoint info stuff (to come)

[color=blue]NPC info[/color]
+02c9 0x7701
+02cb block size, 0x0034
+02cd NPW info stuff (to come)

[color=blue]Main stats info[/color]
+02fd "gf"
+02ff bitfield indicating which of the 16 main stats follow
+0301 stats (dw) indicated by bitfield, from hUnit

The above section is of variable length

[color=blue]Skill info[/color]
+03xx+00 "if"
+03xx+02 base slvl (b) for 30 skills

[color=blue]Item info[/color]
+03xx+30 Start of item info

The item info is mostly made up of various lists of items. A list will usually contain a header that has a two byte "string", for example "JM" (I would say many of those strings you find are actually short for various programmer names) followed by a word indicating the number of items in the list to follow. Next follow each item itself and I think most (all?) of them will start of with the "JM" string (yes confusing since it is used in two different cases so to speak) followed by item specific data. Socketed items follow right after the item they are socketed into and are not counted in the list header for number of items in the list.

The various lists (of which not all need to be present) are:

[color=blue]Main itemlist[/color]
+00 "JM"
+02 number of items in list
+04 List of items

This includes all items your character have, including those in the stash.


[color=blue]Corpse item list[/color]
+00 "JM"
+02 number of corpses
+04 if coprse exists, 12 bytes not used
+10 if corpse exists, list of items

The game obviously have had the ability to save several corpses for a character. Currently, only one corpse is supported though. The "number of corpses" can only be set to 0 or 1 and can thus be thought of as a flag for the existance of a corpse. If the corpse exists there follow 4 dwords of random data. The first value is actually set from an uninitialized local variable and can thus be anything that happens to sit on the stack. The next two values are x and y coordinates (not sure if they actually make any sense though). None of the three values are read by the code by the way. I guess the first value was once which map the coprse was on or something. In any case, thsoe 12 bytes can be ignored. Next follows a normal list of items if a corpse existed.

[color=blue]Mercenary item list[/color]
+00 "jf" (only exists if expansion game regardless of if you have a mercenary or not)
+02 "JM" (only exists if you actually have a mercenary)
+04 number of items in list (only exists if you actuall have a mercenary)
+06 List of items (only exists if you actually have a mercenary)

In an expansion game you then get:

[color=blue]Iron Golem list[/color]
+00 "kf"
+02 0 or 1 (b), a 1 indictaing an Iron Golem exists
+03 if an Iron Golem eixts the item used to create it will follow here




Next to be added is detailed info on the data for each item saved in a list. It is a quite variable content depending on the item and also quite large. It will follow below. Note that the same function that compress an item for saving seems to be used to compress items for sending between server and client. It has the ability to mask many properties in such cases for non identified items for example when you gamble I would guess. I will not at all handle that here but only the case for actually saving an item.

Since items are saved in a very compressed way on bit levels (and not bytes). In addition, what actually is saved varies with the item type and so on. I will thus not give offsetts but rather the number of bits (in decimal notation) something is saved in. The order would be the one the game places them though, so disregarding the fact that some section might be missing for some items the order should NEVER be another than the one I list below.


[color=blue]Common item data[/color]
16 "JM"
32 flags [ptItemstats+18], ptItemstats is [hUnit+70]

Note, the flags are somewhat modified, some removed and some set by the save code but in much should be identical with the run time flags.

10 version of item, 0, 1 or 100. 100 are new expansion items, 1 are new classic items and 0 is old pre expansion items. Note that all items should be converted to new ones in a current game (I think) at least no item should remain as version 0. 

3 mode, from hUnit+0c

The mode of items seems to mostly be were the item actually is "located", as oposed to some action it performs. See the thread maping out the ptUnit (which I call hUnit in this thread) for more info. Although I can't imagine a situation were the item is in mode 3 (ground) or 5 (whatever that might be) the save file has a special case for them. In that case, the mode would be followed by 2 32 bit x and y coordinates (from d2common.2856 and 2859). For other items we instead have the three following entries.

4 body location [ptItemstats+40]. This tells were, if at all, the item is equiped

4 x coordinate in inventory (d2common.2856)

4 y coordinate in inventory (d2common.2859)

3 page number PLUS 1. The page tells where in the invenotry the item is. Example of different pages are stash cube, main invenotry on character and so on. Pages like trade screen and such should not matter although I am not sure what happens if the game tries to save your character while the trade screen is open, you might end up with items on that page too for example. Same with an item on the cursor.

32 item code

The 3 letter code each base item have. This is for  non ears only (flag 00010000 in the flag data above is set for ears). Ears does not save the code, instead they have:

 3 class of ear
 n*7 name of ear. This is the character name of the ear and is ended by a 0 value. The name is stored at [ptItemstats+44] (first letter) and the following at [ptItemstats+46] and onwards.

For the interested, in non saved structures, if the item is gold (which obviously never save as items), the ammount of gold is inserted after the item code. First comes a 1 bit indicating the size. A 1 indicate a 32 bit value a 0 indicate a 12 bit value. That 12/32 bit value then follows and specify the ammount of gold.

Compact save items will have no other data than the above one. Flag 00200000 is set on compact save items. Note that flag is set only upon save, not normally in the flag register. It will be cleared in the process of loading. The compact saving info of an item is fetched from the appropriate item.txt file upon saving and checked for.




[color=blue]Non compact save item data[/color]
3 number of socketed items
32 creation seed, from [hUnit+34]
7 ilvl, from [ptItemstats+28]
4 quality, from [ptItemstats+00]
1 flag for variable picture (itemtypes "varinvgfx")
3 if flag=1, number indicating which picture to use
1 flag for autoprefix on item
11 if flag=1, info on which autoprefix

Next follows data depending on the quality of the item. 

Low quality
3 type of low quality property

Normal quality
No special data saved

Superior
3 type of superiorproperty

Magic
11 which prefix the item has
11 which suffix the item has

In both cases it is the entry in the appropriate affix table (were a 0 value indicate none). This applies to the autoprefix above as well.

Rare and Crafted
8 which prefix name the item has
8 which suffix name the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has
1 flag indicating if a prefix follows
11 if flag=1, which prefix the item has
1 flag indicating if a suffix follows
11 if flag=1, which suffix the item has

Each rare item can have up to 3 prefixes and 3 suffixes.

Set
12 which set (in setitems.txt) the item is part of

Unique
12 which unique (in unqiue.txt) the item is


...more to come

[/quote]

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Re: Save file structure

Post by Myhrginoc » Mon Mar 17, 2003 1:22 am

Jarulf" wrote:Non compact save item data
3 number of socketed items
32 creation seed, from [hUnit+34]
7 ilvl, from [ptItemstats+28]
4 quality, from [ptItemstats+00]
1 flag for variable picture (itemtypes "varinvgfx")
3 if flag=1, number indicating which picture to use
1 flag for autoprefix on item
11 if flag=1, info on which autoprefix
So the highlighted line is what we have to change to get ilvl over 127? Now we need to find out where that is established when a character is created.
Last edited by Myhrginoc on Mon Mar 17, 2003 1:22 am, edited 1 time in total.
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Mon Mar 17, 2003 8:36 am

Myhrginoc";p="78532" wrote:
Jarulf" wrote:Non compact save item data
3 number of socketed items
32 creation seed, from [hUnit+34]
7 ilvl, from [ptItemstats+28]
4 quality, from [ptItemstats+00]
1 flag for variable picture (itemtypes "varinvgfx")
3 if flag=1, number indicating which picture to use
1 flag for autoprefix on item
11 if flag=1, info on which autoprefix
So the highlighted line is what we have to change to get ilvl over 127? Now we need to find out where that is established when a character is created.
Yes, that entry needs to be increased to more than 7 bits for the save file to handle values larger than 127. Also, the code seting this value for saving in the save file will cap the value at 99 as well, so that cap need to be removed. I can give you the code location if needed.

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Re: Save file structure

Post by Myhrginoc » Tue Mar 18, 2003 2:50 am

That would be a good addition to the Breaking the ilvl 99 Barrier thread. Thanks!
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

Jarulf
Junior Member
Champion of the Light
Posts: 346
Joined: Sun May 26, 2002 9:20 am

Hand-picked

Re: Save file structure

Post by Jarulf » Tue Mar 18, 2003 2:20 pm

[quote=Myhrginoc";p="78835"]That would be a good addition to the Breaking the ilvl 99 Barrier thread. Thanks![/quote]

Note that changing it would make old saves incompatible. One way would perhaps be to change the save version number. I think it is referenced (with compare for exact value, not just above or below a specific value and also used in switch statements) many times though so would require a bigger changes. An alternative is to use the "free" space, but there are so many items so it is not practical. Perhaps one can in some way make it possible to have a switch to read 7, alternatively more bits for the level. It must be in some way to not interfer with existing version values though. Hmmm. Tricky.

One can just forget about compatability backwards though and just set it to 8 of course.

User avatar
Myhrginoc
Retired Admin
Cherub
Posts: 12100
Joined: Sat May 25, 2002 7:28 am
Location: Percussion U
United States of America

Hand-picked

Re: Save file structure

Post by Myhrginoc » Wed Mar 19, 2003 2:41 am

This is no different than changing ItemStatCost SaveBits and SaveAdd, as far as importing other-version characters goes. Besides, it is bad form to mix characters between mods or in and out of vanilla. Bumping this value from 7 to 16 would handle full-range ilvl even for the new 65,000+ level mod, assuming all other ilvl factors are equally accounted for.
Do the right thing. It will gratify some people and astonish the rest.
~ Mark Twain
Run Diablo II in any version for mods: tutorial
The Terms of Service!! Know them, abide by them, and enjoy the forums at peace.
The Beginner's Guide v1.4: (MS Word | PDF) || Mod Running Scripts || TFW: Awakening

aurikan
Posts: 11
Joined: Thu Sep 19, 2002 3:10 am

Re: Save file structure

Post by aurikan » Fri Mar 28, 2003 12:22 pm

I've been looking into .d2s's as well. The referenced page above is quite good and contains a bunch of info that Jarulf hasn't got (to yet), most notably encoding of actual mods on items.

Just to regurgitate a bit of that here:

Runeword portion (iff flag 04000000(?)):
12 bits: Runeword Index
4 bits: Unknown

Personalization portion (iff flag 01000000(?)):
do {
7 bits: ascii character of personalization
} while bits read not 0
(essentially a null-terminated 7-bit-ascii string)

1: Unknown Bit (set on "ibk", apparently set on very little else)

Item Type driven data:

On Armor
10: Defense Rating + 10

On Weapons and Armor
8: Maximum Durability

On Items with Max Durability > 0:
8: Current Durability

Socket Portion (iff flag 00000800 (?)):
4: Total Sockets

On Tomes:
5: Unknown

On Stackable Items:
9: Quantity

On Set Items:
5: Number of modifier lists (quirky behavior I don't understand yet)

The next portion in the item field is the list of modifiers. Each modifier is contained by a 9 bit identifier and zero or more variable-width fields. The modifier 0x1ff (that is, 9 1's) indicates the end of a modifier list. Most items have 1 modifier list; set items apparently have multiple.

A relatively full listing of modifiers and field widths can be found at http://www.xmission.com/~trevin/DiabloI ... rties.html

I'm not sure if there is somewhere in the mpq to find a better and/or more complete listing.

As for checksums, the algorithm is quite clearly described in C code on the ShadowMaster site and the referenced site above. I have also written the routine in perl:

Code: Select all

#calculate correct checksum for an arbitrary file
#remember of course that this checksum is stored in "apparent" reverse
#order due to endianness
#on unix, run with "perl script.pl < filename".  On windows it might be a
#bit tougher - try "type filename | perl script.pl"

$/=\1;while(<>){$c=($c<<1)+($c>>31)+($x>>2==3?0:ord);$x++}printf"%x",$c

# :-P
OK, now onto my question:

I've been analysing the data of items I picked up in Seven Lances. FoxBat has enabled some of those cut-from-d2 items such as the spleen, quill, and jawbone. However, I can't seem to make full sense of them:

The item dump for a spleen I received in a2Hell is (in hex)
4a 4d 10 00 80 00 64 00 72 32 07 57 06 02 08 ed a3 4b aa 10 86 ff

4a 4d is the header.
10 00 80 00 is the "flags".
The version and position data is in 64 00 72 02 (lower half of that last one)
The name is in 30 07 57 06 02 (upper half of first, lower half of last)

Now it gets more confusing. Here's the binary stream of the rest of it, split into fields:

Code: Select all

[000][0 0001 0000 1011 0111 1100 0101 1101 001][0 0101 01][01 00][0][0] [1][000 0110 000][1 1111 1111]
The first field is the number of sockets, 0
The second is the UID, we don't care about that
The third is the ilvl, 84. Seems reasonable.
Fourth is quality, 2 = normal. The item does show as white in inv, so makes sense
Fifth is picture field, no unique picture
Sixth is autoprefix, no unique autoprefix
Seventh is the listed "unknown but almost always zero" field. It isn't zero!
Next is 10 bits that I can't for the life of me figure out. Anyone want to hazard a guess?
Finally, is the mod list. The item has zero mods in-game, so the first 9 bits are all 1 to indicate termination.

Any help on this one? Or should I just catch the spleen mod code and skip over the bits?

Isolde
Junior Member
Champion of the Light
Posts: 317
Joined: Thu Jun 27, 2002 12:38 pm
Location: California

Re: Save file structure

Post by Isolde » Fri Mar 28, 2003 7:35 pm

Seventh is the listed "unknown but almost always zero" field. It isn't zero!
hint: for body parts, it needs to store what monster the body part came from.... "fallen spleen", etc.


also, all the so-called magic properties are simply stats from itemstatcost.txt. so if you're writing a save game editor, it'd be good if you read the properties from that file so it'll work with mods.
Last edited by Isolde on Fri Mar 28, 2003 7:38 pm, edited 2 times in total.

Return to “Code Editing”