In regards to the extra data at the end of the Prt file, it is not loaded or used by Outpost 2. We have found extra unused data. This is very similar to the tile group data at the end of map files. The game never tries to load it. There is no code to analyse how the data is loaded or used, because it isn't loaded or used.
I spent some time examining the code we were looking at in OllyDbg. Turns out the code at the end of the Prt loading method corresponds to the
UnknownContainer struct array, defined in Animations.h in the OP2Utility project. That particular code corresponds to data that is already being loaded, we just don't yet know what to do with that part yet.
The Prt loading method in Outpost 2 closes the stream and ends after loading the animation array. It never touches the data after that.
The only way to determine what that data means is to analyse the data itself.
In terms of the structure of the
UnknownContainer, I strongly believe it represents a
Rect. More specifically, I believe it represents two
Point objects.
The reason for this conclusion is the peculiar and somewhat nonsensical stream error checks around that code, where it reads the first 4 bytes, and then conditionally loads the next 4 bytes, provided there were no errors from reading the first 4 bytes. As the only expected error might be end of file, it could have just tried to read all 8 bytes together.
This error checking behavior is consistent for other parts of the code that process Point structs. In particular, the
clipRect struct exhibits the exact same checks. My guess is, the game's code had an inline method which read the x and y fields of the Point object separately, and returned early if there was a failure. It may have even returned an error code to indicate success or failure. Though the problem with returning error codes is they are inconvenient to check, and make a mess of the control flow of the program. As such, they are frequently ignored. That may explain the pattern in the code, which seems to be repeated for each
Point or
Rect that is loaded.
Possible example code:
/* Returns true if read was successful */
bool StreamIO::Read(void* buffer, std::size_t size);
/* Returns true if read was successful */
inline bool StreamIO::Read(Point& p) {
/* Note: The && is short-circuiting */
/* It only evaluates the right hand side if the left hand side is true */
return Read(p.x) && Read(p.y);
}
/* Returns true if read was successful */
/* ... or get lazy, not check things, and return void */
inline void StreamIO::Read(Rect& rect) {
Read(rect.p1); /* Ignore return value */
Read(rect.p2); /* Ignore return value */
}
The
Rect class doesn't seem to use the same short-circuiting behaviour. If one point fails to load, it still tries to load the second point.