Rest in peace, Eye of Peace Picture Viewer...

Posted by Administrator.

So, I've discovered a severe bug in The Peacenet that prevents you from starting the game. No, it's not the one that @kaylinDevelopment reported on the forum. This one's a different one. One that I simply can't fix without going the nuclear route.

Some context

For some context, let's look at one of the programs in the game. It's called the Picture Viewer and it allows you to load picture files and look at them in-game. These files are stored on your in-game filesystem. The idea being that you would find some pictures on NPC systems that could be useful in missions, etc. You could download them to your system and view them. All good, right? Well, it does work. But, there's another part of the game responsible for making sure those picture files can exist in the first place. That is where the bug lies.

That component is called, although a misnomer, the Image Loader. Actually it serves the purpose of loading AND saving images to the in-game filesystem, but it was originally implemented to solely load images. Its original loading code works fine but, the saving code does not.

In order to save an image, the game does these things:

  • It gets given a pointer to an Unreal Engine texture object (a UTexture2D*).
  • It then sets the compression and mip-generation settings of this texture.
  • Then it determines the pixel width and height of the texture using the mip map.
  • Then, it allocates (width * height) * 4 bytes of memory to hold the pixel data it's about to read. (Pixels are 4 bytes long, each byte representing a different color channel - red, green, blue and alpha.)
  • Then it reads this pixel data into that buffer.
  • Then using some magic Unreal API calls, it encodes that buffer into a .png file in ANOTHER buffer.
  • This other buffer gets saved to the in-game filesystem.

This works perfectly fine for saving images in the Unreal editor, which is where I primarily test the game. But, when you compile the game as a shipping build, i.e, without the Editor, you get a compiler error trying to set the mip gen settings of the texture!

InTexture->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps; // <-- Error: "MipGenSettings" is not a member of "UTexture2D."

Why's that?

Well, let's look inside the header for UTexture2D.

/** Per asset specific setting to define the mip-map generation properties like sharpening and kernel size. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=LevelOfDetail)
TEnumAsByte<enum TextureMipGenSettings> MipGenSettings;

Well, it's obviously there! It's in UTexture.h. Line 492. What the heck? Let's keep looking. Well, about a hundred lines up in the file, there's this:

#if WITH_EDITORONLY_DATA

This is a C++ preprocessor directive that is used to tell the compiler to ONLY compile this section of the code if the specified preprocessor constant is defined, i.e #define EDITORONLY_DATA. This constant isn't defined in shipping builds, so, the MipGenSettings variable is never compiled by C++! No wonder we can't set it. It really isn't there!

But what's this mean?

Well, I originally thought it wouldn't mean much. Actually a lot of textures in the game do save just fine as .png's without that variable being set. But that variable being set to the specific value actually determines how the pixel data is read. My code expects the pixel data to be in a specific format, and it's that setting that makes damn sure it is.

So, if the texture isn't already set to that mip gen setting, then the game's code gets a little cranky and gets a read access violation trying to read out the pixel data.

How can you fix that?

Short answer is... you can't. At least not feasibly. You'd need to make sure EVERY texture that's sent to that function has that setting set by the person who imported the texture. I couldn't tell you how to set it in the Unreal editor and it'd be a pain for me to do it for every texture already in the game! (There's at least a hundred just for UI.)

So what do you do then?

Well, images aren't the focus of the game. The focus is hacking. We can afford to take this code right outta the game completely and we'd be fine. So that's what we can do.

For scenarios where we do need images to be stored on the filesystem (i.e, wallpapers), we can just store asset references in the file, and when you attempt to read the file as text, the game throws an error like cat: cannot read file. This is a game, mind you.

Consequences?

Unfortunately, this does mean that the Picture Viewer will get removed, and that desktop wallpapers are limited to what we put in the game. But it does mean the game actually functions. So that's more important, right? It also means that, since I have to implement this and it'll take time, I may or may not have to delay the alpha release a few weeks so I also have time to implement hacking gameplay.

So yeah, just a few schedule changes. Those are the real consequences.