Comment by amiga386
6 hours ago
So if I understand this right:
* common.lin contains filenames, so that filename-expansion code in the game can work. But the offsets and sizes associated with the files are garbage
* <filename>.lin contains a stream of every byte read from every file, while loading the level <filename>. The stream is then compressed in 16k chunks by zlib.
* There is no indication in that stream of which real file was being read, nor the length of each read, nor what seeking was done (if any). All that metadata is gone.
* The only way to recover this metadata is to run the game code and log the exact sequence of file opens, seeks, reads.
* Alternatively, extract all that Unreal object loader code from the game and reimplement it yourself, so that you can let the contents of the stream drive the correct reading of the stream. The code should be deterministic.
This sounds pretty hellish for the game developers, and I bet the debug versions of their game _ignored_ <filename>.lin and used the real source files, but _wrote_ <filename>.lin immediately after every load... any change to the Unreal objects could alter how they were read, and if the data streamed didn't perfectly match up with what was in the real files, you'd be toast.
It reminds me of the extreme optimisation that Farbrausch did for .kkrieger -- they built a single binary, then ran and played it under instrumentation, and _any_ code path that wasn't taken was deleted from the binary to make it smaller. They forgot to take any damage in that playthrough, so all the code that applies damage to the player was deleted. Oops!
About .kkrieger's trimming, I had only heard that they forgot to press up on the main menu so it doesn't work, not about the damage thing.
Unreal has gotten better since then, but you still need the actual game code to load the asset correctly. It’s a major pain in the ecosystem.
Could you explain a bit more about that code path optimisation? Why wouldn’t the compiler eliminate dead code? It seems like a very haphazard blunt force optimisation method.
The compiler can’t determine which code paths are never used in practice at runtime.
I see. It sounds like it would be a source of countless headaches, and I don’t think I’d ever want to do something that risks breaking the program like that, but I guess that’s why I’m not a game programmer.
2 replies →