Search code examples
c#bytebinaryfilescilbinarywriter

8 bytes of data being written to file by an empty method


I've been messing around with the save code of a game that resides in a dll and have discovered something interesting.

I'm using ILSpy with Reflexil to read and alter the code of the dll.

Okay, I have the following snippet of code (as shown by ILSpy):

stream.Write(this.V.Count); //this is signed 4-byte integer, it equals 1 in this case (10 00 00 00)
using (List<StatModifier>.Enumerator enumerator = this.V.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        StatModifier current = enumerator.Current;
        current.Write(stream);
    }

}

There is some code prior to this that writes to the same BinaryWriter, but I'll come to that later, since it isn't relevant to this matter.

current.Write(stream); is a call to this method:

public virtual void Write(BinaryWriter stream)
{
}

It's an empty method (I deleted it's contents using Reflexil). Only IL code in it is this single command (as displayed in ILSpy):

     Offset  OpCode   Operand
>0   0       ret

The code that runs next (after the method call) is cut short with ret command so it doesn't write anything else to the binary. The game code then closes the file properly (stream.Close();). This isn't really relevant for the problem, but I know you'll ask. If anyone requests I can add the before and after code, but I don't see the point in doing so.

The way I was testing:

  1. I created a save file with a clean game code so I could load it.
  2. I copied it to another folder.
  3. I altered the code to test it.
  4. I copied the original back.
  5. I have entered and exited the game (which executes this code - code that saves the game to the file).
  6. I inspected the file with hex editor.
  7. Repeat 3-6.

This loaded the save file, entered the game and then, upon exit, overwritten it using the altered code. I know which part of the game aspect this part of the code saves and I know for a fact that it is left unchanged during my short enter and exit session. This way everything is kept constant except the save code.

When I start the game with this code and make it run, it creates this binary file. That code is already altered so that most of the usually saved data is removed so it's easier to look for changes:

74 74 70 00 18 05 00 00    00 9A 99 19 3F 00 00 00
00 00 00 00 00 00 00 80    3F 00 00 80 3F 00 00 80
3F 01 01 00 00 00 9A 99    19 3F 9A 99 19 3F 

The first part of the binary was written by the code I omitted at the beginning since it spanned through several classes and method calls, but I don't think that it is of any relevance here. This is the important part:

01 00 00 00 9A 99    19 3F 9A 99 19 3F

This here are the last 12 bytes. The first four represent a signed 4-byte integer from the beginning of the first code snippet (this.V.Count).

And the last eight are the mysterious ones. I have no idea where they came from. So played some more. I changed the first snippet to this:

stream.Write(this.V.Count); //this is signed 4-byte integer, it equals 1 in this case (10 00 00 00)
using (List<StatModifier>.Enumerator enumerator = this.V.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        StatModifier current = enumerator.Current;
    }

}

As you can see, the current.Write(stream) is no longer called.

When I ran the game again, I got this output:

7C 74 70 00 18 05 00 00    00 9A 99 19 3F 00 00 00
00 00 00 00 00 00 00 80    3F 00 00 80 3F 00 00 80
3F 01 01 00 00 00

Did you notice anything missing? The last 8 bytes from the first run are gone. And the only change was the removal of the empty method call.

I first noticed this when I was looking at a complete save file trying to follow its creation in the source code. I just couldn't find where those 8-bytes came from so I ended up altering the code, removing this and that to see what I was missing. It turns out I wasn't really missing anything. So this happened in the completely clean code as well.

If someone is really interested I can tell them which game is this about and send them the relevant files so they can try it for themselves.

So, does anyone know of the origin of these bytes? How is this possible?

EDIT to whoever voted to close because of unclear: In what universe exactly is what I'm asking unclear?


Solution

  • The function is virtual, the implementation that is actually run is in a subclass derived from StatModifier, you are looking at the wrong class