coop-deluxe / sm64coopdx

An official continuation of https://github.com/djoslin0/sm64ex-coop on sm64coopdx for the enhancements and progress it already has.
https://sm64coopdx.com
403 stars 71 forks source link

[Request] Load assets from disk #372

Open Erotemic opened 1 month ago

Erotemic commented 1 month ago

I'm interested in doing some development on this repo in order to support my randomized assets project. The issue I'm having right now is that sm64coopdx seems to rely on extracting assets from an official ROM at runtime. However, I want to supply my own assets. I attempted a workaround where I compiled a sm64 ROM with my custom assets and tried to give it to sm64coopdx, but it strictly checks for hash equality to the us ROM.

Link to asset generation project: https://github.com/Erotemic/sm64-random-assets

Is there a reasonably simple development path to supporting loading pre-extracted (or generated) assets from png, aiff, m64, and bin files on disk instead of from the ROM? I imagine if there is some construct to get a particular asset from the ROM, a little polymorphic magic (although perhaps that is difficult in C?) can be used to pre-extracted assets from disk?

I originally posted this on discord, but I'd like to have an asynchronous development discussion and figure out the technical details behind accomplishing this.

Cooliokid956 commented 1 month ago

you'd need to reverse the rom extraction commits

Erotemic commented 1 month ago

Here is what I have discovered so far. If you search fd asset in the repo you see these files:

assets/
assets.json
extract_assets.py
sound/samples_assets.c
sound/sequences_assets.c
src/pc/rom_assets.c
src/pc/rom_assets.h
text/us/courses_assets.inc.c
text/us/dialog_assets.inc.c
tools/gen_asset_list.cpp

And src/pc/rom_assets.c seems like it is doing a lot of the major work. It looks like it is seeking to a location in the ROM, reading the bytes and then directly placing them into memory somewhere. This seems to be a lot more challenging to extend than I was originally hoping. If I wanted to re-use this logic I think I would need to ensure the generated assets I use are the exact same size, which might be difficult if there is compression.

Does anyone know if there might be an easier way to hook into assets read from disk?

I'm trying to find a good place to start tackling this, and I'm hoping someone can help me get my bearings.

Erotemic commented 1 month ago

you'd need to reverse the rom extraction commits

Is it that simple? Are these a set of commits I can revert to enable the original asset generation which I believe is baked into the executable?

I see:

but I imagine there are other commits that impact those files, and there might be conflicts that need to be resolved.

robertkirkman commented 1 month ago

@Erotemic Do you think you might be able to implement an exporter in your random asset generator that exports all of your random assets packed inside of a dummy ROM that is empty/nonessential except for all the addresses that ROM extractors read in order to extract assets from the vanilla US ROM? You could probably isolate and import the code from https://github.com/n64decomp/sm64.git that's used to convert matching assets into a matching ROM in order to do it more easily than if you had to do it completely from scratch.

If you end up figuring out how to make something like that, maybe your generator would become more easily compatible with all forks of the US game by just disabling the ROM checksum verification, rather than making a bunch of really different changes for every different fork of the game.

Erotemic commented 1 month ago

I can build a ROM that includes the random assets. Both the sm64 and sm64-port repo work fine with my asset generator on Linux (on MinGW there is an issue).

If you run my code as:

   sudo apt install -y binutils-mips-linux-gnu build-essential git libcapstone-dev pkgconf python3

   # Build and run the ROM in an emulator (m64py)
   export ASSET_CONFIG='
       png: generate
       aiff: generate
       m64: generate
       bin: generate
   '
   export NUM_CPUS=all
   export TARGET=sm64
   ./build.sh

This produces a ROM, which contains no copyrighted assets, which I've uploaded here if you want to take a look sm64-random-assets.us.zip (you may need to unzip it)

I have tried to use this by disabling the hash check, but when I drag and drop it into sm64coopdx I get a segfault.

robertkirkman commented 1 month ago

Most likely your random assets are not in exactly the memory addresses of the ROM that the US ROM has them at. When you run the normal decomp build process but change some things, it's like you create another variant ROM that is similar to the EU, JP, US and SH versions but not identical to any specific one any longer. I think there's probably a way to insert custom assets into the exact memory addresses they are supposed to be in the US ROM, but I haven't set up a whole system for doing that. If I end up making a demonstration of it that seems helpful, I'll show it to you. These are my favorite hex editors, which might help you with manually writing and comparing binary files byte by byte.

https://linux.die.net/man/1/hexedit https://github.com/WerWolv/ImHex

Erotemic commented 1 month ago

I expect you're right about it placing the assets. One thing I've noticed is that the randomly generated assets can be much larger than the originals (you can't runtime length compress random data with png as well as structured data). This actually caused an issue when I put the ROM on my everdrive and tried to put it on the N64. The texture size blew out memory!

To diff binaries I typically just use :%!xxd in vim. I can take a look at ImHex if that makes navigating larger files easier.

I would be interested in any code that would help put data in the correct memory locations, but I also think I need to ensure the random assets are no larger than the original ones. I believe they are stored in some compressed format in the ROM (otherwise there would be no memory difference between the random and original assets), so getting this right may be tricky.

theclashingfritz commented 1 month ago

We originally had an idea to have the game extract and dump the assets into a compressed (and more robust) file. But it hasn't been implemented. Your idea seems to line up with the idea of that (and may be a good reason to support it).

Erotemic commented 1 month ago

It would help if we can break down what needs to be done into smaller pieces.

  1. What file formats should the data be in, I assume the same .png, .aiff, .m64, .bin files extractable from the original ROM.
  2. How do we need to decode those in C? Are there libraries that can help us read these file formats?
  3. Where do we store the data once it is read and decoded?
  4. Anything else I'm missing?
theclashingfritz commented 1 month ago

The formats are likely going to be in what the engine natively accepts-Likely packed into a new format (A container) and not loose because having them loose will just cause confusion. (And make mods more file hefty then they already are.)

This also means we won't need anything special for encoding or decoding them.

The original idea was too read all of the data from all the language ROMs are pack them into one file. (Allowing you play multiple versions without ROM swapping in theory). But we didn't go through with it.

theclashingfritz commented 1 month ago

The only thing that will have more complicated issues. Is sounds and music (probably the best reason to even load from disk at this point too) Sounds are very specifically made and coded and while just swapping the audio data is easy. The other data is tough and hard to work with. (There is a big lack of existing tools) I'm not sure how that could be done easily.