C#, Code, Diablo 2

Understanding the diablo 2 save file format / Part 2

This time , we’ll be taking a look at how to read in the statistics bit of the d2 save file. First of all , we’ll need to establish how we will be reading in this part , I suggest copying this part in a separate bit/bool array for easier reading seeing as this is not byte aligned anymore.

First, we check out the length of the statsheader by looking at the location of the skills header . We also know for sure that the statsheader starts at 767 due to the fact that all files are formatted identical up untill this point.

int statstart = 767;

for (int i = 765; i < fileSize; i++)
{
    if (fileStream[i] == 'i')
    {
        if (fileStream[i + 1] == 'f')
        {
            skillHeaderStart = i;
        }
    }
}
int statlength = skillHeaderStart - statstart;

Now , let’s put the statsblock in an array of bools for easier reading ( so we dont have to skip over to another byte everytime )

List statBitStream = new List();

for (var i = 0; i < statlength; i++) // Getting the statlist in a bit array
{
  for (var j = 0; j < 8; j++)
  {
    var bit = GetBit(fileStream[statstart + i], j);
    statBitStream.Add(bit);
  }
}

private static bool GetBit(byte b, int position) // position in range 0-7
{
 return (b & (1 << position)) != 0;
}

Now that we have gotten individual bits in an array , let’s try reading them , and converting our values. The values always have an “id” part and a “value” part , the id part always consists of 9 bits and contains values between 0 and 15 ( meaning there are at maximum only 16 stats contained in this block ) . The size value part’s size however , depends on the particular id.

Now , for id’s and their values’ size :

private int StatIdToBitCount(int id)
        {

            int bitCount;
            switch (id)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                    bitCount = 10;
                    break;
                case 5:
                    bitCount = 8;
                    break;
                case 6:
                case 7:
                case 8:
                case 9:
                case 10:
                case 11:
                    bitCount = 21;
                    break;
                case 12:
                    bitCount = 7;
                    break;
                case 13:
                    bitCount = 32;
                    break;
                case 14:
                case 15:
                    bitCount = 25;
                    break;
                default:
                    // UNKNOWN BITCOUNT
                    bitCount = 1;
                    break;
            }
            return bitCount;
        }

With this out of the way , our program will recognise the amount of bits he has to read for the value of our particular stat. You will notice i did some weird shenanigans to properly read in the values ( namely multiplying the bits times 2^x depending on the bit position )

int streamIndex = 0;
bool doLoop = true;

while (doLoop)
{
    var id = 0;
    if (streamIndex + 9 > statBitStream.Count()) // stop when end of stream is reached;
    {
        doLoop = false;
        break;
    }

    for (int j = 0; j  statBitStream.Count() -StatIdToBitCount(id)) 
// stop when end of stream is reached;
    {
        doLoop = false;
        break;
    }

    uint valueForStat = 0;

    for (int d = 0; d < StatIdToBitCount(id); d++) // Add bits to the valueforstat
    {
        int addedvalue = 1;

        if (d == 0)
        {
            if (statBitStream[streamIndex + d]) { addedvalue = 1; }
            else
            {
                addedvalue = 0;
            }
        }
        else
        {
         for (int i = 0; i  statBitStream.Count()) 
// stop when end of stream is reached;
        {
        doLoop = false;
        }
}

This post is mainly a big wall of code , it may be rather ugly to read . But I do find it important for people so they can see how they would go about translating the information about a binary file into actual code that reads this.

So , that was it for now , next post , and probably the last about the d2s file , will be about manipulating the checksum so you can actually save your file and play your manipulated save in the game.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s