One of the more irritating resources to deal with is the player data resource, or the NpïL resource. Before we get into how this resource can be read, we need to first understand some of the complexities and issues with it.
- For whatever reason AmbrosiaSW never migrated this particular file to flattened ndat files. This means that the actual NpïL resource is inside a true resource fork. This is illustrated by the fact that when we drop the file in to a hex editor, we see no content at all, despite Finder reporting an 87KB file size.
This is a problem as none of the Cocoa or POSIX API’s can open or read from the resource fork data. The only API’s that can are the Carbon API’s. Thankfully the macOS virtual file system provides us a “hack” to read the resource fork as a data fork (though who knows for how much longer). We’ll take a look at this later.
- The second issue is that the resource data itself is encrypted. There is a document online covering the actual data layout of the pilot resources (I believe by guy). However I can not currently find it, so not able to link to it. I have attached two screenshots beneath of the code structures representing the two NpïL resources.
Yep, both resources have a different format internally, which means they can’t be read with exactly the same process. This is rather annoying. On top of that, if we parse the resources using these structures the data is completely garbled and insane due to the encryption. We’ll get into the decryption in a bit.
The Contents of a Pilot File
Nothing on modern macOS will allow us to look at the contents of a resource fork easily. It just doesn’t exist. The API’s required were deprecated 10 years ago, and do not function correctly under 64-bit.
This means that liberal use of SheepShaver (a PPC emulator, for Classic Mac OS) is required. As you can see this is not easy to actually look at… even unencrypted binary data is not easy to look at, but this is especially not easy to look at. As far as I can tell there is zero documentation about this encryption online, or at least zero surviving documentation. This means that the only way to deal with it was by reverse engineering EV Nova itself.
This is a Hopper Disassembler, and it is used for investigating the contents of application binaries. I’m using it to reverse engineer parts of EV Nova itself. In addition to finding the _LoadPilotData routine, I also ran it through Hopper’s decompiler. The result of which can be found here.
The actual encryption algorithm is listed below. Being the result of a disassembly and decompilation, it is annoyingly cryptic and difficult to follow.
To be fair, I’ve written the code in much the same way many of the original variables are gone thanks to the original compiling of EV Nova. That’s fine, we don’t technically need them, they just make it easy to read.
The 3 arguments in the function are as follows:
- arg0: The data to encrypt/decrypt
- arg1: The size of the data to encrypt/decrypt
- arg2: The encryption key (which is hex value 0xb36a210f)
So once you have all of this sorted out and resolved you are ready to read the contents of a Pilot File!
Not quite. Pilot resources aren’t in ndat files for some reason.
The ResourceFork under modern macOS
There is something fundamentally wrong with this, and it honestly surprised me. Why were pilot files not migrated to ndat files? My assumption is due to the existing pilot files out in the world and the desire to not render them dead and forgotten. That said I’m not sure that argument would have been valid as the same resource loading API’s are used for both ndat and pilot files alike.
As I mentioned earlier it is impossible to load a resource file using modern none deprecated API’s. Carbon provides the only way to do this, and it is 32-bit only. It will not work under 64-bit.
However the macOS virtual file system does provide a means of reading resource fork data into a separate data file, or via a pipe.
By appending the /..namedfork/rsrc to a file, you can read its ResourceFork.
Let’s return to Hex Fiend, and attempt to load the pilot file again, but using this command instead.
It all worked successfully. Armed with all these tools, it is possible to fully read and handle pilot files from the original EV Nova engine.