snickerbockers / fp-assets

Extractor/archiver tool for Freedom Planet's Assets.dat file.
The Unlicense
5 stars 1 forks source link

Trouble trying to extract assets.dat from AVGN Adventures (3DS version) #2

Open leomontenegro6 opened 5 years ago

leomontenegro6 commented 5 years ago

I heard that the game "Angry Video Game Nerd Adventures" was created in the same engine than Freedom Planet (or similar). So after getting the 3DS version and extracting the romfs data, I managed to get a "assets.dat" file. Then, I tried using fp-assets to extract contents from that file, to see what happens.

I managed to install Pillow 2.7.0, then copied fp-assets.py and assets.bin to a same folder, and finally run the command below:

python fp-editor.py -x

However, it didn't worked as it should, and instead it returned me the following error:

Traceback (most recent call last):
  File "fp-assets.py", line 479, in <module>
    assets_dir_path=assets_dir_path)
  File "fp-assets.py", line 407, in extract_all_assets
    "img_%d_meta.txt" % index)
  File "fp-assets.py", line 132, in extract_img
    img_w = struct.unpack("<H", assets_file.read(2))[0]
struct.error: unpack requires a string argument of length 2

The tool could extract only the files "preload_data.bin" and "type_sizes.txt" and create five folders: "audio", "files", "fonts", "images" and "shaders". However, all folders are empty, which suggests me that it aborted the extraction the moment it tried to extract images.

Is there a way of extracting and remounting that file? Even if it's a workaround?

snickerbockers commented 5 years ago

The chowdren engine has a metadata block containing offsets to all assets. The location of this block is hard-coded into the game, as is the number of each type of assets. That means that there's no general-purpose way to dump Assets.dat because the script needs to also have the metadata block location and the number of assets hardcoded.

So in order to get this working with a different game, you'd have to replace the below metadata in the script with Angry Video Game Nerd's version:

OFFSETS_START=0x83fa
IMG_COUNT=16893
SOUND_COUNT=475
FONT_COUNT=1
SHADER_COUNT=37
FILE_COUNT=18
TYPE_SIZE_COUNT = 5

I was able to reverse-engineer this from the GoG release of the Linux version of Freedom Planet with relative ease because the devs forgot to strip the ELF symbols. I'd imagine the same is probably true for Angry Video Game Nerd's Linux release, so you should be able to find them in there.

So the steps to figure this out would be to find the function where it parses the Assets.dat file (I can't remember what it was called, but IIRC there was only one function that did it) then find the part where it seeks to the offset block, then from the disassembly figure out how much of each type of asset there is. Then you can change the above constants in the script and it should hopefully work.

Adrthegamedev commented 3 years ago

I have come to this issue yet again. I've got the linux version of a game (baba is you) and i've got the file where the elf symbols are stored (at least i think i do), all i need to do now is find that function, and i can't seem to find it. What i did find, however, is a mention of assets.dat in the file. If that's the function, my only question is how do i get the rest of the values? i can't see any obvious way to get the constants required for the extractor to work.

wasamasa commented 1 year ago

I've opened Baba is You in Ghidra, poked around a bit and found that the offsets are processed in an init_assets function (which can be found by analyzing the file, entering "offsets" in the symbol tree explorer and clicking around there). It decompiles to this:


/* init_assets(BaseFile&) */

void init_assets(BaseFile *param_1)

{
  long lVar1;

  assets_initialized = 1;
  lVar1 = 0;
  BaseFile::seek(param_1,0,0);
  BaseFile::read(param_1,image_offsets,0x31cc);
  do {
    lVar1 = lVar1 + 1;
  } while (lVar1 != 0xc73);
  BaseFile::read(param_1,sound_offsets,0x3d0);
  lVar1 = 0;
  do {
    lVar1 = lVar1 + 1;
  } while (lVar1 != 0xf4);
  lVar1 = 0;
  BaseFile::read(param_1,&font_offsets,0);
  BaseFile::read(param_1,shader_offsets,0x1d4);
  do {
    lVar1 = lVar1 + 1;
  } while (lVar1 != 0x75);
  BaseFile::read(param_1,&file_offsets,0);
  BaseFile::read(param_1,&platform_offsets,0);
  BaseFile::read(param_1,type_sizes,0x18);
  return;
}

From this I conclude, that the start offset in assets.dat is 0, there's 0x31cc image offsets, 0x3d0 sound offsets, 0 font offsets, 0x1d4 shader offsets, 0 file offsets, 0 platform offsets and 0x18 type sizes. No idea about those useless loops.

In case you don't feel like waiting for Ghidra to finish analyzing the game, you can use radare2 with the Ghidra plugin, enter visual mode with V, navigate to the function with _, press enter, switch to disassembly view with p, analyze function at cursor with :af, then disassemble with Ghidra with :pdg.