mirror of
https://github.com/Xeeynamo/sotn-decomp.git
synced 2024-11-26 22:40:33 +00:00
New asset manager (#1343)
This aims to deprecate all the Splat tools in `tools/splat_ext` in favour of a more centralised asset manager. This brings the following advantages: * Much faster extraction * Faster build * Automatically define `static` symbols or unique names whenever `static` is not possible * Allow to embed assets into the output binary * Drastically simplify `Makefile` by removing all the asset build rules * Avoid situations where it is not possible to extract and build assets that is not 4-byte aligned This is achieved by having the splat YAML targeting a normal C file as data and have an external tool to take care of the following: 1. Extract asset files straight from the overlay binary file into human readable file in `assets/st/STAGE_NAME` 2. Build assets as header files that go into `src/st/STAGE_NAME` to just include them from any C file This requires each stage header to have the following new format: please see `src/st/nz0/header.c` Built assets in `src/st` are ignored by Git. As for now, for simplicity sake, the steps `make extract_assets` and `make build_assets` are just executed within `make extract` exclusively for the US version. I plan to auto-generate files such as `src/st/nz0/tile_data.c`. For a first iteration I am aiming to handle the following: * [X] Extract rooms: `assets/st/*/rooms.json` * [X] Extract room layers: `assets/st/*/entity_layouts.json` * [X] Extract tilemap data: `assets/st/*/tilemap_*.bin` * [X] Extract tilemap definitions: `assets/st/*/tiledef_*.json` * [X] Extract sprites: `assets/st/*/sprites.json` * [x] Extract entity layouts * [X] Build rooms: `src/st/*/rooms.h` * [X] Build room layers: `src/st/*/layers.h` * [X] Build tilemap data: `src/st/*/tilemap_*.h` * [X] Build tilemap definitions: `src/st/*/tiledef_*.h` * [x] Build sprites (aka `g_SpriteBanks`) * [x] Build entity layouts * [x] Allow the tool to suggest how to adjust the Splat config for each overlay I want the tool to cover the following stages: * [x] CEN * [x] DRE * ~MAD~ I do not think this can be done, it is way too different from the other overlays * [x] NO3 * [x] NP3 * [X] NZ0 * [x] ST0 * [X] WRP * [x] RWRP * ~WRP (PSP)~ Maybe in a follow-up PR For a later iteration I plan on extracting and build: * Entity GFX thingie * The CLUT thingie in the header * Uncompressed GFX data * Cutscene data * Blueprints * The `src/config_us.h` thingie --------- Co-authored-by: Josh Lory <josh.lory@outlook.com>
This commit is contained in:
parent
f7882814b0
commit
de46f99e2e
75
.github/workflows/picci.yaml
vendored
75
.github/workflows/picci.yaml
vendored
@ -7,19 +7,49 @@ on:
|
||||
paths:
|
||||
- 'include/**'
|
||||
- 'src/**'
|
||||
- 'tools/sotn-assets/**'
|
||||
- 'cmake/**'
|
||||
- 'CMakeLists*'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'include/**'
|
||||
- 'src/**'
|
||||
- 'tools/sotn-assets/**'
|
||||
- 'cmake/**'
|
||||
- 'CMakeLists*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
extract-assets:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone main repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- name: Get dependencies
|
||||
uses: actions/cache@v4
|
||||
id: get-dependencies
|
||||
with:
|
||||
path: 'disks/dependencies'
|
||||
key: sotn-us-deps
|
||||
- name: Setting up dependencies
|
||||
working-directory: disks
|
||||
run: cat dependencies/* | tar -zxf -
|
||||
- name: Extract dependencies
|
||||
run: make extract_disk
|
||||
- name: Extract game assets
|
||||
run: make -j extract_assets
|
||||
- name: Export assets
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: assets
|
||||
path: assets
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
needs: extract-assets
|
||||
steps:
|
||||
- name: Install requirements
|
||||
run: sudo apt-get update && sudo apt-get install build-essential libsdl2-dev
|
||||
@ -28,6 +58,13 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- name: Get assets
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: assets
|
||||
path: assets
|
||||
- name: Build assets
|
||||
run: make -j build_assets
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/pc
|
||||
@ -35,14 +72,27 @@ jobs:
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
needs: extract-assets
|
||||
steps:
|
||||
- name: Install requirements
|
||||
run: brew install SDL2
|
||||
- name: Install Go
|
||||
run: |
|
||||
curl -L -o go.tar.gz https://go.dev/dl/go1.22.4.darwin-amd64.tar.gz
|
||||
tar -C ~ -xzf go.tar.gz
|
||||
rm go.tar.gz
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- name: Get assets
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: assets
|
||||
path: assets
|
||||
- name: Build assets
|
||||
run: make -j build_assets
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/pc
|
||||
@ -50,6 +100,7 @@ jobs:
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
needs: extract-assets
|
||||
steps:
|
||||
- name: Install requirements
|
||||
run: |
|
||||
@ -58,6 +109,22 @@ jobs:
|
||||
7z x C:\temp-sdl2\SDL2-devel-2.28.5-VC.zip -oC:\temp-sdl2
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- name: Get assets
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: assets
|
||||
path: assets
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '^1.22.4'
|
||||
- name: Build assets
|
||||
run: |
|
||||
cd tools/sotn-assets; go install; cd -
|
||||
$SOTN_ASSETS = (Get-Command sotn-assets).Source
|
||||
& make -j build_assets SOTNASSETS=$SOTN_ASSETS
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/pc -DCMAKE_BUILD_TYPE=Release -DSDL2_PATH=C:\temp-sdl2\SDL2-2.28.5
|
||||
@ -65,6 +132,7 @@ jobs:
|
||||
|
||||
build-linux-lle:
|
||||
runs-on: ubuntu-latest
|
||||
needs: extract-assets
|
||||
steps:
|
||||
- name: Install requirements
|
||||
run: sudo apt-get update && sudo apt-get install build-essential libsdl2-dev
|
||||
@ -73,6 +141,13 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
submodules: false
|
||||
- name: Get assets
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: assets
|
||||
path: assets
|
||||
- name: Build assets
|
||||
run: make -j build_assets
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/pc -DWANT_LIBSND_LLE=1
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,6 +18,7 @@ expected/
|
||||
disks/
|
||||
|
||||
.vscode/settings.json
|
||||
.idea/
|
||||
tools/go
|
||||
|
||||
!tools/sotn-debugmodule/sotn-debugmodule.ld
|
||||
|
@ -220,10 +220,16 @@ set(SOURCE_FILES_STAGE_SEL
|
||||
|
||||
set(SOURCE_FILES_STAGE_WRP
|
||||
src/pc/stages/stage_wrp.c
|
||||
src/st/wrp/6FD0.c
|
||||
src/st/wrp/d_1b8.c
|
||||
src/st/wrp/e_laydef.c
|
||||
src/st/wrp/d_3e4.c
|
||||
src/st/wrp/st_debug.c
|
||||
src/st/wrp/e_breakable.c
|
||||
src/st/wrp/d_608.c
|
||||
src/st/wrp/rooms.c
|
||||
src/st/wrp/e_layout.c
|
||||
src/st/wrp/tile_data.c
|
||||
src/st/wrp/sprites.c
|
||||
src/st/wrp/warp.c
|
||||
src/st/wrp/st_update.c
|
||||
src/st/wrp/collision.c
|
||||
|
52
Makefile
52
Makefile
@ -67,6 +67,7 @@ MASPSX := $(PYTHON) $(MASPSX_APP) --expand-div --aspsx-version=2.34
|
||||
GO := $(HOME)/go/bin/go
|
||||
GOPATH := $(HOME)/go
|
||||
SOTNDISK := $(GOPATH)/bin/sotn-disk
|
||||
SOTNASSETS := $(GOPATH)/bin/sotn-assets
|
||||
GFXSTAGE := $(PYTHON) $(TOOLS_DIR)/gfxstage.py
|
||||
PNG2S := $(PYTHON) $(TOOLS_DIR)/png2s.py
|
||||
ICONV := iconv --from-code=UTF-8 --to-code=Shift-JIS
|
||||
@ -81,11 +82,21 @@ define list_src_files
|
||||
$(foreach dir,$(SRC_DIR)/$(1)/psxsdk,$(wildcard $(dir)/**.c))
|
||||
$(foreach dir,$(ASSETS_DIR)/$(1),$(wildcard $(dir)/**))
|
||||
endef
|
||||
define list_st_src_files
|
||||
$(foreach dir,$(ASM_DIR)/$(1),$(wildcard $(dir)/**.s))
|
||||
$(foreach dir,$(ASM_DIR)/$(1)/data,$(wildcard $(dir)/**.s))
|
||||
$(foreach dir,$(SRC_DIR)/$(1),$(wildcard $(dir)/**.c))
|
||||
$(foreach dir,$(ASSETS_DIR)/$(1),$(wildcard $(dir)/D_8018*.bin))
|
||||
endef
|
||||
|
||||
define list_o_files
|
||||
$(foreach file,$(call list_src_files,$(1)),$(BUILD_DIR)/$(file).o)
|
||||
endef
|
||||
|
||||
define list_st_o_files
|
||||
$(foreach file,$(call list_st_src_files,$(1)),$(BUILD_DIR)/$(file).o)
|
||||
endef
|
||||
|
||||
define list_shared_src_files
|
||||
$(foreach dir,$(SRC_DIR)/$(1),$(wildcard $(dir)/*.c))
|
||||
endef
|
||||
@ -104,6 +115,7 @@ define link
|
||||
endef
|
||||
|
||||
SOTNDISK_SOURCES := $(shell find tools/sotn-disk -name '*.go')
|
||||
SOTNASSETS_SOURCES := $(shell find tools/sotn-assets -name '*.go')
|
||||
|
||||
CHECK_FILES := $(shell cut -d' ' -f3 config/check.$(VERSION).sha)
|
||||
|
||||
@ -191,6 +203,7 @@ dra: $(BUILD_DIR)/DRA.BIN
|
||||
$(BUILD_DIR)/DRA.BIN: $(BUILD_DIR)/$(DRA).elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
$(BUILD_DIR)/$(DRA).elf: $(call list_o_files,dra)
|
||||
echo $(call list_o_files,dra)
|
||||
$(call link,dra,$@)
|
||||
|
||||
ric: $(BUILD_DIR)/RIC.BIN
|
||||
@ -291,7 +304,9 @@ $(BUILD_DIR)/stmad.elf: $$(call list_o_files,st/mad) $$(call list_shared_o_files
|
||||
-T $(CONFIG_DIR)/undefined_syms.beta.txt \
|
||||
-T $(CONFIG_DIR)/undefined_syms_auto.stmad.txt \
|
||||
-T $(CONFIG_DIR)/undefined_funcs_auto.stmad.txt
|
||||
$(BUILD_DIR)/st%.elf: $$(call list_o_files,st/$$*) $$(call list_shared_o_files,st)
|
||||
$(BUILD_DIR)/stsel.elf: $$(call list_o_files,st/sel) $$(call list_shared_o_files,st)
|
||||
$(call link,stsel,$@)
|
||||
$(BUILD_DIR)/st%.elf: $$(call list_st_o_files,st/$$*) $$(call list_shared_o_files,st)
|
||||
$(call link,st$*,$@)
|
||||
|
||||
# Weapon overlays
|
||||
@ -425,8 +440,8 @@ update-dependencies: $(ASMDIFFER_APP) $(M2CTX_APP) $(M2C_APP) $(MASPSX_APP) $(SA
|
||||
cd $(SATURN_SPLITTER_DIR)/rust-dis && cargo build --release
|
||||
cd $(SATURN_SPLITTER_DIR)/adpcm-extract && cargo build --release
|
||||
pip3 install -r $(TOOLS_DIR)/requirements-python.txt
|
||||
$(GO) install github.com/xeeynamo/sotn-decomp/tools/gfxsotn@latest
|
||||
$(GO) install github.com/xeeynamo/sotn-decomp/tools/sotn-disk@latest
|
||||
rm $(SOTNDISK) && make $(SOTNDISK) || true
|
||||
rm $(SOTNASSETS) && make $(SOTNASSETS) || true
|
||||
git clean -fd bin/
|
||||
|
||||
bin/%: bin/%.tar.gz
|
||||
@ -449,11 +464,13 @@ $(MASPSX_APP):
|
||||
git submodule init $(MASPSX_DIR)
|
||||
git submodule update $(MASPSX_DIR)
|
||||
$(GO):
|
||||
curl -L -o go1.19.7.linux-amd64.tar.gz https://go.dev/dl/go1.19.7.linux-amd64.tar.gz
|
||||
tar -C $(HOME) -xzf go1.19.7.linux-amd64.tar.gz
|
||||
rm go1.19.7.linux-amd64.tar.gz
|
||||
curl -L -o go1.22.4.linux-amd64.tar.gz https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
|
||||
tar -C $(HOME) -xzf go1.22.4.linux-amd64.tar.gz
|
||||
rm go1.22.4.linux-amd64.tar.gz
|
||||
$(SOTNDISK): $(GO) $(SOTNDISK_SOURCES)
|
||||
cd tools/sotn-disk; $(GO) install
|
||||
$(SOTNASSETS): $(GO) $(SOTNASSETS_SOURCES)
|
||||
cd tools/sotn-assets; $(GO) install
|
||||
|
||||
$(BUILD_DIR)/%.s.o: %.s
|
||||
mkdir -p $(dir $@)
|
||||
@ -463,29 +480,18 @@ $(BUILD_DIR)/%.c.o: %.c $(MASPSX_APP) $(CC1PSX)
|
||||
$(CPP) $(CPP_FLAGS) -lang-c $< | $(SOTNSTR) | $(ICONV) | $(CC) $(CC_FLAGS) $(PSXCC_FLAGS) | $(MASPSX) | $(AS) $(AS_FLAGS) -o $@
|
||||
|
||||
# Handles assets
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.layoutobj.json.o: $(ASSETS_DIR)/%.layoutobj.json
|
||||
./tools/splat_ext/layoutobj.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.bin
|
||||
$(LD) -r -b binary -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.bin
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.roomdef.json.o: $(ASSETS_DIR)/%.roomdef.json
|
||||
./tools/splat_ext/roomdef.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.bin
|
||||
$(LD) -r -b binary -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.bin
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.layers.json.o: $(ASSETS_DIR)/%.layers.json
|
||||
./tools/splat_ext/layers.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.tiledef.json.o: $(ASSETS_DIR)/%.tiledef.json
|
||||
./tools/splat_ext/tiledef.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.spritesheet.json.o: $(ASSETS_DIR)/%.spritesheet.json
|
||||
./tools/splat_ext/spritesheet.py encode $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.animset.json.o: $(ASSETS_DIR)/%.animset.json
|
||||
./tools/splat_ext/animset.py gen-asm $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.json.o: $(ASSETS_DIR)/%.json
|
||||
./tools/splat_ext/assets.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.tilelayout.bin.o: $(ASSETS_DIR)/%.tilelayout.bin
|
||||
$(LD) -r -b binary -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(ASSETS_DIR)/$*.tilelayout.bin
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/dra/%.json.o: $(ASSETS_DIR)/dra/%.json
|
||||
./tools/splat_ext/assets.py $< $(BUILD_DIR)/$(ASSETS_DIR)/dra/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/dra/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/dra/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/ric/%.json.o: $(ASSETS_DIR)/ric/%.json
|
||||
./tools/splat_ext/assets.py $< $(BUILD_DIR)/$(ASSETS_DIR)/ric/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/ric/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/ric/$*.s
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.bin.o: $(ASSETS_DIR)/%.bin
|
||||
$(LD) -r -b binary -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $<
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.dec.o: $(ASSETS_DIR)/%.dec
|
||||
|
@ -12,6 +12,8 @@ PSX_BASE_SYMS := $(CONFIG_DIR)/symbols.$(VERSION).txt
|
||||
|
||||
extract_us: $(addprefix $(BUILD_DIR)/,$(addsuffix .ld,$(PSX_US_TARGETS)))
|
||||
$(PNG2S) bdecode config/gfx.game.json disks/us assets/game
|
||||
make extract_assets
|
||||
make build_assets
|
||||
extract_hd: $(addprefix $(BUILD_DIR)/,$(addsuffix .ld,$(PSX_HD_TARGETS)))
|
||||
|
||||
extract_disk_us: extract_disk_psxus
|
||||
@ -46,6 +48,25 @@ $(BUILD_DIR)/weapon.ld: $(CONFIG_DIR)/splat.$(VERSION).weapon.yaml $(PSX_BASE_SY
|
||||
$(SPLAT) $<
|
||||
touch $@
|
||||
|
||||
extract_assets: $(SOTNASSETS)
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/CEN/CEN.BIN -o assets/st/cen
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/DRE/DRE.BIN -o assets/st/dre
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/NO3/NO3.BIN -o assets/st/no3
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/NP3/NP3.BIN -o assets/st/np3
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/NZ0/NZ0.BIN -o assets/st/nz0
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/ST0/ST0.BIN -o assets/st/st0
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/WRP/WRP.BIN -o assets/st/wrp
|
||||
$(SOTNASSETS) extract -stage_ovl disks/$(VERSION)/ST/RWRP/RWRP.BIN -o assets/st/rwrp
|
||||
build_assets: $(SOTNASSETS)
|
||||
$(SOTNASSETS) build_all -i assets/st/cen -o src/st/cen/
|
||||
$(SOTNASSETS) build_all -i assets/st/dre -o src/st/dre/
|
||||
$(SOTNASSETS) build_all -i assets/st/no3 -o src/st/no3/
|
||||
$(SOTNASSETS) build_all -i assets/st/np3 -o src/st/np3/
|
||||
$(SOTNASSETS) build_all -i assets/st/nz0 -o src/st/nz0/
|
||||
$(SOTNASSETS) build_all -i assets/st/st0 -o src/st/st0/
|
||||
$(SOTNASSETS) build_all -i assets/st/wrp -o src/st/wrp/
|
||||
$(SOTNASSETS) build_all -i assets/st/rwrp -o src/st/rwrp/
|
||||
|
||||
$(BUILD_DIR)/assets/dra/memcard_%.png.o: assets/dra/memcard_%.png
|
||||
mkdir -p $(dir $@)
|
||||
$(PNG2S) encode $< \
|
||||
|
@ -34,21 +34,22 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0xDC, layers, rooms]
|
||||
- [0xDC, .data, header] # layers
|
||||
- [0x134, data]
|
||||
- [0x1EC, .data, e_laydef] # layout entries header
|
||||
- [0x394, data]
|
||||
- [0x7E4, .data, collision]
|
||||
- [0xBA4, .data, e_red_door]
|
||||
- [0xBBC, data]
|
||||
- [0x10AC, .data, entity_relic_orb]
|
||||
- [0x1110, data]
|
||||
- [0x12D4, roomdef, g_Rooms]
|
||||
- [0x1300, data]
|
||||
- [0x69EC, tilelayout, D_801869EC]
|
||||
- [0x6DEC, tilelayout, D_80186DEC]
|
||||
- [0x7FEC, tilelayout, D_80187FEC]
|
||||
- [0x81EC, tiledef, t_801884EC, D_801884EC]
|
||||
- [0x85FC, tiledef, t_8018C5FC, D_8018C5FC]
|
||||
- [0xC60C, data]
|
||||
- [0x12D4, .data, rooms]
|
||||
- [0x1300, .data, e_layout] # layout entries data
|
||||
- [0x13F0, data]
|
||||
- [0x69EC, .data, tile_data] # tile data
|
||||
- [0x81EC, .data, tile_data] # tile definitions
|
||||
- [0xC60C, .data, sprites]
|
||||
- [0xD414, rodata]
|
||||
- [0xD42C, .rodata, holyglassescutscene]
|
||||
- [0xD4B0, .rodata, F890]
|
||||
- [0xD4D8, .rodata, e_red_door] # EntityRedDoor
|
||||
|
@ -34,8 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0xE8, layers, rooms]
|
||||
- [0xE8, .data, header] # layers
|
||||
- [0x128, data]
|
||||
- [0x220, .data, e_laydef] # layout entries header
|
||||
- [0x3C8, data]
|
||||
- [0x590, data]
|
||||
- [0x610, data]
|
||||
- [0x630, data]
|
||||
@ -50,14 +52,13 @@ segments:
|
||||
- [0xD80, data]
|
||||
- [0x1270, .data, entity_relic_orb]
|
||||
- [0x12D4, data]
|
||||
- [0x1498, roomdef, g_Rooms]
|
||||
- [0x1498, .data, rooms]
|
||||
- [0x14AC, data]
|
||||
- [0x1608, .data, e_layout] # layout entries data
|
||||
- [0x16C0, data]
|
||||
- [0xB548, tilelayout, D_8018B548]
|
||||
- [0xB948, tilelayout, D_8018B948]
|
||||
- [0xBD48, tiledef, t_8018C148, D_8018C148]
|
||||
- [0xC158, tiledef, t_80190158, D_80190158]
|
||||
- [0x10168, data]
|
||||
- [0xB548, .data, tile_data] # tile data
|
||||
- [0xBD48, .data, tile_data] # tile definitions
|
||||
- [0x10168, .data, sprites]
|
||||
- [0x1171C, .rodata, 11A64]
|
||||
- [0x11728, .rodata, succubus] # EntitySuccubus
|
||||
- [0x11808, .rodata, succubuscutscene]
|
||||
|
@ -43,7 +43,7 @@ segments:
|
||||
- [0xE40, data]
|
||||
- [0xEF8, .data, entity_relic_orb]
|
||||
- [0xF5C, data]
|
||||
- [0x1130, roomdef, g_Rooms]
|
||||
- [0x1130, data, g_Rooms]
|
||||
- [0x11D4, data] # assets/st/mad/0.objlayout
|
||||
# - [0x11E8, data] # assets/st/mad/1.objlayout
|
||||
# - [0x1206, data] # assets/st/mad/2.objlayout
|
||||
|
@ -34,9 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0x1C4, layers, rooms]
|
||||
- [0x1C4, .data, header] # layers
|
||||
- [0x5A4, data]
|
||||
- [0x920, data]
|
||||
- [0x77C, .data, e_laydef] # layout entries header
|
||||
- [0x924, data]
|
||||
- [0xAAC, data]
|
||||
- [0xDC0, data]
|
||||
- [0xDD0, data]
|
||||
@ -57,56 +58,12 @@ segments:
|
||||
- [0x3AC0, data]
|
||||
- [0x3AF0, data]
|
||||
- [0x3B30, data]
|
||||
- [0x3CC4, roomdef, g_Rooms]
|
||||
- [0x3DC8, data]
|
||||
- [0x3CC4, .data, rooms]
|
||||
- [0x3DC8, .data, e_layout] # layout entries data
|
||||
- [0x4CE0, data]
|
||||
- [0x18838, tilelayout, D_80198838]
|
||||
- [0x19438, tilelayout, D_80199438]
|
||||
- [0x19638, tilelayout, D_80199638]
|
||||
- [0x19A38, tilelayout, D_80199A38]
|
||||
- [0x19E38, tilelayout, D_80199E38]
|
||||
- [0x1AC38, tilelayout, D_8019AC38]
|
||||
- [0x1AE38, tilelayout, D_8019AE38]
|
||||
- [0x1B038, tilelayout, D_8019B038]
|
||||
- [0x1B238, tilelayout, D_8019B238]
|
||||
- [0x1B638, tilelayout, D_8019B638]
|
||||
- [0x1BA38, tilelayout, D_8019BA38]
|
||||
- [0x1C238, tilelayout, D_8019C238]
|
||||
- [0x1CA38, tilelayout, D_8019CA38]
|
||||
- [0x1CC38, tilelayout, D_8019CC38]
|
||||
- [0x1CE38, tilelayout, D_8019CE38]
|
||||
- [0x1DA38, tilelayout, D_8019DA38]
|
||||
- [0x1E638, tilelayout, D_8019E638]
|
||||
- [0x1E838, tilelayout, D_8019E838]
|
||||
- [0x1EA38, tilelayout, D_8019EA38]
|
||||
- [0x1F638, tilelayout, D_8019F638]
|
||||
- [0x1F838, tilelayout, D_8019F838]
|
||||
- [0x1FA38, tilelayout, D_8019FA38]
|
||||
- [0x1FC38, tilelayout, D_8019FC38]
|
||||
- [0x20038, tilelayout, D_801A0038]
|
||||
- [0x20238, tilelayout, D_801A0238]
|
||||
- [0x20838, tilelayout, D_801A0838]
|
||||
- [0x20E38, tilelayout, D_801A0E38]
|
||||
- [0x21038, tilelayout, D_801A1038]
|
||||
- [0x21238, tilelayout, D_801A1238]
|
||||
- [0x21438, tilelayout, D_801A1438]
|
||||
- [0x21638, tilelayout, D_801A1638]
|
||||
- [0x22238, tilelayout, D_801A2238]
|
||||
- [0x22E38, tilelayout, D_801A2E38]
|
||||
- [0x23038, tilelayout, D_801A3038]
|
||||
- [0x23238, tilelayout, D_801A3238]
|
||||
- [0x23438, tilelayout, D_801A3438]
|
||||
- [0x23638, tilelayout, D_801A3638]
|
||||
- [0x25A38, tilelayout, D_801A5A38]
|
||||
- [0x25C38, tilelayout, D_801A5C38]
|
||||
- [0x25E38, tilelayout, D_801A5E38]
|
||||
- [0x26038, tilelayout, D_801A6038]
|
||||
- [0x26238, tilelayout, D_801A6238]
|
||||
- [0x26438, tilelayout, D_801A6438]
|
||||
- [0x26638, tiledef, t_801A6A38, D_801A6A38]
|
||||
- [0x26A48, tiledef, t_801AAA48, D_801AAA48]
|
||||
- [0x2AA58, tiledef, t_801AEA58, D_801AEA58]
|
||||
- [0x2EA68, data]
|
||||
- [0x18838, .data, tile_data] # tile data
|
||||
- [0x26638, .data, tile_data] # tile definitions
|
||||
- [0x2EA68, .data, sprites]
|
||||
- [0x373E0, data]
|
||||
- [0x373E8, .rodata, 377D4] # EntityCastleDoor
|
||||
- [0x37400, .rodata, 377D4] # EntityStairwayPiece
|
||||
|
@ -34,8 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0x1D0, layers, rooms]
|
||||
- [0x1D0, .data, header] # layers
|
||||
- [0x558, data]
|
||||
- [0x728, .data, e_laydef] # layout entries header
|
||||
- [0x8D0, data]
|
||||
- [0xE50, data]
|
||||
- [0x10C0, data]
|
||||
- [0x1300, data]
|
||||
@ -60,51 +62,12 @@ segments:
|
||||
- [0x3580, data]
|
||||
- [0x3720, data]
|
||||
- [0x3760, data]
|
||||
- [0x3A7C, roomdef, g_Rooms]
|
||||
- [0x3B68, data]
|
||||
- [0x168F4, tilelayout, D_801968F4]
|
||||
- [0x174F4, tilelayout, D_801974F4]
|
||||
- [0x176F4, tilelayout, D_801976F4]
|
||||
- [0x17AF4, tilelayout, D_80197AF4]
|
||||
- [0x17EF4, tilelayout, D_80197EF4]
|
||||
- [0x18CF4, tilelayout, D_80198CF4]
|
||||
- [0x18EF4, tilelayout, D_80198EF4]
|
||||
- [0x190F4, tilelayout, D_801990F4]
|
||||
- [0x192F4, tilelayout, D_801992F4]
|
||||
- [0x196F4, tilelayout, D_801996F4]
|
||||
- [0x19AF4, tilelayout, D_80199AF4]
|
||||
- [0x1A2F4, tilelayout, D_8019A2F4]
|
||||
- [0x1AAF4, tilelayout, D_8019AAF4]
|
||||
- [0x1ACF4, tilelayout, D_8019ACF4]
|
||||
- [0x1AEF4, tilelayout, D_8019AEF4]
|
||||
- [0x1BAF4, tilelayout, D_8019BAF4]
|
||||
- [0x1C6F4, tilelayout, D_8019C6F4]
|
||||
- [0x1C8F4, tilelayout, D_8019C8F4]
|
||||
- [0x1CAF4, tilelayout, D_8019CAF4]
|
||||
- [0x1D6F4, tilelayout, D_8019D6F4]
|
||||
- [0x1D8F4, tilelayout, D_8019D8F4]
|
||||
- [0x1DAF4, tilelayout, D_8019DAF4]
|
||||
- [0x1DCF4, tilelayout, D_8019DCF4]
|
||||
- [0x1E0F4, tilelayout, D_8019E0F4]
|
||||
- [0x1E2F4, tilelayout, D_8019E2F4]
|
||||
- [0x1E8F4, tilelayout, D_8019E8F4]
|
||||
- [0x1EEF4, tilelayout, D_8019EEF4]
|
||||
- [0x1F0F4, tilelayout, D_8019F0F4]
|
||||
- [0x1F2F4, tilelayout, D_8019F2F4]
|
||||
- [0x1F4F4, tilelayout, D_8019F4F4]
|
||||
- [0x1F6F4, tilelayout, D_8019F6F4]
|
||||
- [0x202F4, tilelayout, D_801A02F4]
|
||||
- [0x20EF4, tilelayout, D_801A0EF4]
|
||||
- [0x210F4, tilelayout, D_801A10F4]
|
||||
- [0x212F4, tilelayout, D_801A12F4]
|
||||
- [0x214F4, tilelayout, D_801A14F4]
|
||||
- [0x216F4, tilelayout, D_801A16F4]
|
||||
- [0x218F4, tilelayout, D_801A18F4]
|
||||
- [0x21AF4, tilelayout, D_801A1AF4]
|
||||
- [0x21CF4, tiledef, t_801A20F4, D_801A20F4]
|
||||
- [0x22104, tiledef, t_801A6104, D_801A6104]
|
||||
- [0x26114, tiledef, t_801AA114, D_801AA114]
|
||||
- [0x2A124, data]
|
||||
- [0x3A7C, .data, rooms]
|
||||
- [0x3B68, .data, e_layout] # layout entries data
|
||||
- [0x49E0, data]
|
||||
- [0x168F4, .data, tile_data] # tile data
|
||||
- [0x21CF4, .data, tile_data] # tile definitions
|
||||
- [0x2A124, .data, sprites]
|
||||
- [0x31EA0, data]
|
||||
- [0x31EA8, .rodata, 3246C] # EntityStairwayPiece
|
||||
- [0x31EBC, .rodata, 365FC] # func_801B65FC
|
||||
|
@ -33,10 +33,11 @@ segments:
|
||||
align: 4
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, d_0]
|
||||
- [0x40, data]
|
||||
- [0x164, layers, rooms]
|
||||
- [0x0, .data, header]
|
||||
- [0x164, .data, header] # layers
|
||||
- [0x47C, data]
|
||||
- [0x8EC, .data, e_laydef] # layout entries header
|
||||
- [0xA94, data]
|
||||
- [0x15A0, .data, collision]
|
||||
- [0x1960, .data, e_red_door]
|
||||
- [0x1978, data]
|
||||
@ -50,46 +51,14 @@ segments:
|
||||
- [0x2220, data]
|
||||
- [0x2600, data]
|
||||
- [0x2710, data]
|
||||
- [0x272C, roomdef, g_Rooms]
|
||||
- [0x2830, data]
|
||||
- [0x2840, data]
|
||||
- [0x2850, data]
|
||||
- [0x16A5C, tilelayout, D_80196A5C]
|
||||
- [0x16C5C, tilelayout, D_80196C5C]
|
||||
- [0x16E5C, tilelayout, D_80196E5C]
|
||||
- [0x1725C, tilelayout, D_8019725C]
|
||||
- [0x17A5C, tilelayout, D_80197A5C]
|
||||
- [0x17C5C, tilelayout, D_80197C5C]
|
||||
- [0x17E5C, tilelayout, D_80197E5C]
|
||||
- [0x1805C, tilelayout, D_8019805C]
|
||||
- [0x1845C, tilelayout, D_8019845C]
|
||||
- [0x1865C, tilelayout, D_8019865C]
|
||||
- [0x1885C, tilelayout, D_8019885C]
|
||||
- [0x18A5C, tilelayout, D_80198A5C]
|
||||
- [0x1905C, tilelayout, D_8019905C]
|
||||
- [0x1925C, tilelayout, D_8019925C]
|
||||
- [0x19C5C, tilelayout, D_80199C5C]
|
||||
- [0x1A05C, tilelayout, D_8019A05C]
|
||||
- [0x1A45C, tilelayout, D_8019A45C]
|
||||
- [0x1AA5C, tilelayout, D_8019AA5C]
|
||||
- [0x1AE5C, tilelayout, D_8019AE5C]
|
||||
- [0x1BE5C, tilelayout, D_8019BE5C]
|
||||
- [0x1C65C, tilelayout, D_8019C65C]
|
||||
- [0x1C85C, tilelayout, D_8019C85C]
|
||||
- [0x1D45C, tilelayout, D_8019D45C]
|
||||
- [0x1D65C, tilelayout, D_8019D65C]
|
||||
- [0x1E25C, tilelayout, D_8019E25C]
|
||||
- [0x1EE5C, tilelayout, D_8019EE5C]
|
||||
- [0x1F25C, tilelayout, D_8019F25C]
|
||||
- [0x2005C, tilelayout, D_801A005C]
|
||||
- [0x2025C, tilelayout, D_801A025C]
|
||||
- [0x2045C, tilelayout, D_801A045C]
|
||||
- [0x2065C, tilelayout, D_801A065C]
|
||||
- [0x2085C, tilelayout, D_801A085C]
|
||||
- [0x20A5C, tiledef, t_801A0E5C, D_801A0E5C]
|
||||
- [0x20E6C, tiledef, t_801A4E6C, D_801A4E6C]
|
||||
- [0x24E7C, tiledef, t_801A6E7C, D_801A6E7C]
|
||||
- [0x26E8C, data]
|
||||
- [0x272C, .data, rooms]
|
||||
- [0x2830, data, D_80182830]
|
||||
- [0x2884, .data, e_layout] # layout entries data
|
||||
- [0x3B0C, data]
|
||||
- [0x16A5C, .data, tile_data] # tile data
|
||||
- [0x20A5C, .data, tile_data] # tile definitions
|
||||
- [0x26E8C, .data, sprites]
|
||||
- [0x3058C, rodata]
|
||||
- [0x305A4, .rodata, 30958] # func_801B3C38
|
||||
- [0x305B8, .rodata, bossfight] # EntityBossFightManager
|
||||
- [0x305E8, .rodata, bossfight] # EntityBossRoomBlock
|
||||
|
@ -34,7 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0xB8, data]
|
||||
- [0xB8, .data, header] # layers
|
||||
- [0x1B8, data]
|
||||
- [0x23C, .data, e_laydef] # layout entries header
|
||||
- [0x3E4, data]
|
||||
- [0x4E0, data]
|
||||
- [0x5E0, data]
|
||||
- [0x6BC, .data, collision]
|
||||
@ -43,8 +46,13 @@ segments:
|
||||
- [0xF84, .data, entity_relic_orb]
|
||||
- [0xFE8, data]
|
||||
- [0x10A0, data]
|
||||
- [0x11AC, roomdef, g_Rooms]
|
||||
- [0x1228, data]
|
||||
- [0x11AC, .data, rooms]
|
||||
- [0x1228, .data, e_layout] # layout entries data
|
||||
- [0x1420, data]
|
||||
- [0x1FC8, .data, tile_data] # tile data
|
||||
- [0x2BC8, .data, tile_data] # tile definitions
|
||||
- [0x6FE8, .data, sprites]
|
||||
- [0x8C48, data]
|
||||
- [0x8C88, rodata]
|
||||
- [0x8CC8, .rodata, e_red_door]
|
||||
- [0x8CE0, rodata]
|
||||
|
@ -34,8 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0x124, layers, rooms]
|
||||
- [0x124, .data, header] # layers
|
||||
- [0x1A4, data]
|
||||
- [0x314, .data, e_laydef] # layout entries header
|
||||
- [0x4BC, data]
|
||||
- [0x580, data]
|
||||
- [0x770, data]
|
||||
- [0x828, .data, draculacutscene] # "Richter, Dracula"
|
||||
@ -45,21 +47,19 @@ segments:
|
||||
- [0x1618, .data, collision]
|
||||
- [0x1978, .data, e_red_door]
|
||||
- [0x1990, data]
|
||||
- [0x2060, roomdef, g_Rooms]
|
||||
- [0x2060, .data, rooms]
|
||||
- [0x2084, data]
|
||||
- [0x26B8, .data, e_layout] # layout entries data
|
||||
- [0x29D8, data]
|
||||
#- [0x3D1C, cmp]
|
||||
- [0x487C, data]
|
||||
#- [0x17F80, cmp]
|
||||
- [0x187BC, data]
|
||||
#- [0x1A40C, cmp]
|
||||
- [0x1A750, data]
|
||||
- [0x1B2D0, tilelayout, D_8019B2D0]
|
||||
- [0x1C6D0, tilelayout, D_8019C6D0]
|
||||
- [0x1DAD0, tilelayout, D_8019DAD0]
|
||||
- [0x1E0D0, tilelayout, D_8019E0D0]
|
||||
- [0x1E2D0, tilelayout, D_8019E2D0]
|
||||
- [0x1E6D0, tiledef, t_801A26D0, D_801A26D0]
|
||||
- [0x226E0, data]
|
||||
- [0x1B2D0, .data, tile_data] # tile data
|
||||
- [0x1E6D0, .data, tile_data] # tile definitions
|
||||
- [0x226E0, .data, sprites]
|
||||
- [0x27984, .rodata, 27D64]
|
||||
- [0x279E8, .rodata, draculacutscene] # EntityDraculaCutscene
|
||||
- [0x27A74, .rodata, 2A218] # EntityCutscene
|
||||
|
@ -34,8 +34,10 @@ segments:
|
||||
subalign: 4
|
||||
subsegments:
|
||||
- [0x0, .data, header]
|
||||
- [0xB8, layers, rooms]
|
||||
- [0x1B8, .data, 6FD0]
|
||||
- [0xB8, .data, header] # layers
|
||||
- [0x1B8, .data, d_1b8] # entity gfx
|
||||
- [0x23C, .data, e_laydef] # layout entries header
|
||||
- [0x3E4, .data, d_3e4] # entity inits
|
||||
- [0x528, .data, st_debug]
|
||||
- [0x5A8, .data, e_breakable]
|
||||
- [0x608, .data, d_608]
|
||||
@ -48,34 +50,15 @@ segments:
|
||||
- [0xF84, .data, e_misc]
|
||||
- [0xFE8, .data, e_particles]
|
||||
- [0x1120, .data, e_room_fg]
|
||||
- [0x11AC, roomdef, g_Rooms]
|
||||
- [0x1228, layoutobj, D_80181228]
|
||||
- [0x1250, layoutobj, D_80181250]
|
||||
- [0x1278, layoutobj, D_80181278]
|
||||
- [0x12A0, layoutobj, D_801812A0]
|
||||
- [0x12C8, layoutobj, D_801812C8]
|
||||
- [0x12F0, layoutobj, D_801812F0]
|
||||
- [0x1304, layoutobj, D_80181304]
|
||||
- [0x1324, layoutobj, D_80181324]
|
||||
- [0x134C, layoutobj, D_8018134C]
|
||||
- [0x1374, layoutobj, D_80181374]
|
||||
- [0x139C, layoutobj, D_8018139C]
|
||||
- [0x13C4, layoutobj, D_801813C4]
|
||||
- [0x13EC, layoutobj, D_801813EC]
|
||||
- [0x1400, layoutobj, D_80181400]
|
||||
- [0x11AC, .data, rooms]
|
||||
- [0x1228, .data, e_layout] # layout entries data
|
||||
- [0x1420, cmp, D_80181420]
|
||||
- [0x1764, cmp, D_80181764]
|
||||
- [0x1D08, cmp, D_80181D08]
|
||||
- [0x1D68, tilelayout, D_80181D68]
|
||||
- [0x1F68, tilelayout, D_80181F68]
|
||||
- [0x2168, tilelayout, D_80182168]
|
||||
- [0x2368, tilelayout, D_80182368]
|
||||
- [0x2568, tilelayout, D_80182568]
|
||||
- [0x2768, tilelayout, D_80182768]
|
||||
- [0x2968, tiledef, t_80182D68, D_80182D68]
|
||||
- [0x2D78, tiledef, t_80186D78, D_80186D78]
|
||||
- [0x6D88, animset, D_80186D88]
|
||||
- [0x6E30, .rodata, 6FD0]
|
||||
- [0x1D68, .data, tile_data] # tile data
|
||||
- [0x2968, .data, tile_data] # tile definitions
|
||||
- [0x6D88, .data, sprites]
|
||||
- [0x6E30, .rodata, warp] # warp strings
|
||||
- [0x6E70, .rodata, warp] # EntityWarpRoom
|
||||
- [0x6E90, .rodata, warp] # EntityWarpSmallRocks
|
||||
- [0x6EA8, .rodata, e_red_door] # EntityRedDoor
|
||||
|
@ -1,5 +1,3 @@
|
||||
g_pStObjLayoutHorizontal = 0x801801EC;
|
||||
g_pStObjLayoutVertical = 0x801802C0;
|
||||
PfnEntityUpdates = 0x80180394;
|
||||
g_InitializeData0 = 0x80180410;
|
||||
g_InitializeEntityData0 = 0x8018041C;
|
||||
@ -28,7 +26,6 @@ g_UnkRecursPrim2Inds = 0x801811A4;
|
||||
g_ESoulStealOrbAngles = 0x801811C8;
|
||||
g_ESoulStealOrbSprt = 0x801811D8;
|
||||
g_ESoulStealOrbAnim = 0x80181238;
|
||||
g_Rooms = 0x801812D4;
|
||||
D_8018D4F0 = 0x8018D4F0;
|
||||
D_8018D4F8 = 0x8018D4F8;
|
||||
D_8018D500 = 0x8018D500;
|
||||
|
@ -1,6 +1,4 @@
|
||||
g_GfxBanks = 0x801801D0;
|
||||
g_pStObjLayoutHorizontal = 0x80180220;
|
||||
g_pStObjLayoutVertical = 0x801802F4;
|
||||
PfnEntityUpdates = 0x801803C8;
|
||||
g_eBreakableInit = 0x80180458;
|
||||
g_InitializeData0 = 0x80180464;
|
||||
@ -35,7 +33,6 @@ g_UnkRecursPrim2Inds = 0x80181368;
|
||||
g_ESoulStealOrbAngles = 0x8018138C;
|
||||
g_ESoulStealOrbSprt = 0x8018139C;
|
||||
g_ESoulStealOrbAnim = 0x801813FC;
|
||||
g_Rooms = 0x80181498;
|
||||
EntityUnkId11 = 0x80191A64;
|
||||
EntityBreakable = 0x80191D00;
|
||||
EntityBackgroundClouds = 0x80191E34;
|
||||
|
@ -1,6 +1,4 @@
|
||||
g_GfxBanks = 0x8018072C;
|
||||
g_pStObjLayoutHorizontal = 0x8018077C;
|
||||
g_pStObjLayoutVertical = 0x80180850;
|
||||
PfnEntityUpdates = 0x80180924;
|
||||
g_eBreakableInit = 0x80180AAC;
|
||||
g_InitializeData0 = 0x80180AB8;
|
||||
@ -35,7 +33,6 @@ g_UnkRecursPrim2Inds = 0x801826AC;
|
||||
g_ESoulStealOrbAngles = 0x801826D0;
|
||||
g_ESoulStealOrbSprt = 0x801826E0;
|
||||
g_ESoulStealOrbAnim = 0x80182740;
|
||||
g_Rooms = 0x80183CC4;
|
||||
EntityCavernDoorVase = 0x801B77D4;
|
||||
EntityUnkId12 = 0x801B78A8;
|
||||
EntityBreakable = 0x801B7A64;
|
||||
|
@ -1,6 +1,4 @@
|
||||
g_GfxBanks = 0x801806DC;
|
||||
g_pStObjLayoutHorizontal = 0x80180728;
|
||||
g_pStObjLayoutVertical = 0x801807FC;
|
||||
PfnEntityUpdates = 0x801808D0;
|
||||
g_eBreakableInit = 0x80180A3C;
|
||||
g_InitializeData0 = 0x80180A48;
|
||||
@ -36,7 +34,6 @@ g_ESoulStealOrbAngles = 0x8018205C;
|
||||
g_ESoulStealOrbSprt = 0x8018206C;
|
||||
g_ESoulStealOrbAnim = 0x801820CC;
|
||||
D_801828C8 = 0x801828C8;
|
||||
g_Rooms = 0x80183A7C;
|
||||
EntityBreakable = 0x801B26FC;
|
||||
EntityShuttingWindow = 0x801B2C20;
|
||||
EntityCastleDoor = 0x801B2F30;
|
||||
|
@ -1,7 +1,4 @@
|
||||
g_Cluts = 0x80180160;
|
||||
g_EntityGfxs = 0x80180888;
|
||||
g_pStObjLayoutHorizontal = 0x801808EC;
|
||||
g_pStObjLayoutVertical = 0x801809C0;
|
||||
PfnEntityUpdates = 0x80180A94;
|
||||
g_InitializeData0 = 0x80180BD4;
|
||||
g_InitializeEntityData0 = 0x80180BE0;
|
||||
@ -26,7 +23,6 @@ g_UnkRecursPrimVecOrder = 0x80181F40;
|
||||
g_UnkRecursPrim2Inds = 0x80181F60;
|
||||
g_eBlueDoorUV = 0x801826B8;
|
||||
g_eBlueDoorTiles = 0x801826D0;
|
||||
g_Rooms = 0x8018272C;
|
||||
EntityBreakable = 0x801B0EEC;
|
||||
EntityRedEyeBust = 0x801B11C0;
|
||||
EntityPurpleBrickScrollingBackground = 0x801B12E8;
|
||||
|
@ -1,7 +1,4 @@
|
||||
g_TileLayers = 0x80180168;
|
||||
g_GfxBanks = 0x801801EC;
|
||||
g_pStObjLayoutHorizontal = 0x8018023C;
|
||||
g_pStObjLayoutVertical = 0x80180310;
|
||||
PfnEntityUpdates = 0x801803E4;
|
||||
g_InitializeData0 = 0x8018044C;
|
||||
g_InitializeEntityData0 = 0x80180458;
|
||||
@ -29,7 +26,6 @@ g_UnkRecursPrim2Inds = 0x8018107C;
|
||||
g_ESoulStealOrbAngles = 0x801810A0;
|
||||
g_ESoulStealOrbSprt = 0x801810B0;
|
||||
g_ESoulStealOrbAnim = 0x80181110;
|
||||
g_Rooms = 0x801811AC;
|
||||
EntityBreakable = 0x8018908C;
|
||||
Random = 0x8018A168;
|
||||
EntityDamageDisplay = 0x8018B6B4;
|
||||
|
@ -1,6 +1,4 @@
|
||||
g_EntityGfxs = 0x801802C4;
|
||||
g_pStObjLayoutHorizontal = 0x80180314;
|
||||
g_pStObjLayoutVertical = 0x801803E8;
|
||||
PfnEntityUpdates = 0x801804BC;
|
||||
g_InitializeData0 = 0x80180580;
|
||||
g_InitializeEntityData0 = 0x8018058C;
|
||||
@ -27,17 +25,9 @@ g_UnkRecursPrim2Inds = 0x80181F34;
|
||||
g_ESoulStealOrbAngles = 0x80181F54;
|
||||
g_ESoulStealOrbSprt = 0x80181F64;
|
||||
g_ESoulStealOrbAnim = 0x80181FC4;
|
||||
g_Rooms = 0x80182060;
|
||||
D_80183D1C = 0x80183D1C;
|
||||
D_80197F80 = 0x80197F80;
|
||||
D_8019A40C = 0x8019A40C;
|
||||
D_801A26E0 = 0x801A26E0;
|
||||
D_801A4298 = 0x801A4298;
|
||||
D_801A6A68 = 0x801A6A68;
|
||||
D_801A6F90 = 0x801A6F90;
|
||||
D_801A7430 = 0x801A7430;
|
||||
D_801A7728 = 0x801A7728;
|
||||
D_801A77CC = 0x801A77CC;
|
||||
c_HeartPrizes = 0x801A7C84;
|
||||
EntityLockCamera = 0x801A7EB0;
|
||||
EntityDraculaCutscene = 0x801A9210;
|
||||
|
4
go.work
4
go.work
@ -1,5 +1,7 @@
|
||||
go 1.19
|
||||
go 1.22
|
||||
|
||||
use ./tools/gfxsotn
|
||||
|
||||
use ./tools/sotn-disk
|
||||
|
||||
use ./tools/sotn-assets
|
||||
|
@ -16,6 +16,19 @@ typedef struct {
|
||||
/* 0x8 */ u16 params;
|
||||
} LayoutEntity; // size = 0xA
|
||||
|
||||
typedef struct {
|
||||
u16* layout;
|
||||
TileDefinition* tileDef;
|
||||
u32 params;
|
||||
u16 zPriority;
|
||||
u8 unkE;
|
||||
u8 unkF;
|
||||
} MyLayer;
|
||||
typedef struct {
|
||||
MyLayer* fg;
|
||||
MyLayer* bg;
|
||||
} MyRoomDef;
|
||||
|
||||
#if defined(VERSION_PSP)
|
||||
// A horizontally ordered array with head and tail sigils in the 1st field
|
||||
extern LayoutEntity** g_pStObjLayoutHorizontal;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <string.h>
|
||||
#include "stage_loader.h"
|
||||
#include "sfx.h"
|
||||
#include "../../st/wrp/wrp.h"
|
||||
|
||||
void Update(void);
|
||||
void HitDetection(void);
|
||||
@ -11,35 +12,6 @@ void UpdateRoomPosition(void);
|
||||
void UpdateStageEntities(void);
|
||||
static void MyInitRoomEntities(s32 objLayoutId);
|
||||
|
||||
// TODO populate from assets/st/wrp/D_80186D88.animset.json
|
||||
static SpriteParts* D_80186D88[] = {NULL, NULL, NULL, NULL};
|
||||
static u_long* sprite_banks[] = {
|
||||
/* 0x040 */ 0x00000000,
|
||||
/* 0x044 */ D_80186D88,
|
||||
/* 0x048 */ 0x00000000,
|
||||
/* 0x04C */ 0x00000000,
|
||||
/* 0x050 */ 0x00000000,
|
||||
/* 0x054 */ 0x00000000,
|
||||
/* 0x058 */ 0x00000000,
|
||||
/* 0x05C */ 0x00000000,
|
||||
/* 0x060 */ 0x00000000,
|
||||
/* 0x064 */ 0x00000000,
|
||||
/* 0x068 */ 0x00000000,
|
||||
/* 0x06C */ 0x00000000,
|
||||
/* 0x070 */ 0x00000000,
|
||||
/* 0x074 */ 0x00000000,
|
||||
/* 0x078 */ 0x00000000,
|
||||
/* 0x07C */ 0x00000000,
|
||||
/* 0x080 */ 0x00000000,
|
||||
/* 0x084 */ 0x00000000,
|
||||
/* 0x088 */ 0x00000000,
|
||||
/* 0x08C */ 0x00000000,
|
||||
/* 0x090 */ 0x00000000,
|
||||
/* 0x094 */ 0x00000000,
|
||||
/* 0x098 */ 0x00000000,
|
||||
/* 0x09C */ 0x00000000,
|
||||
};
|
||||
|
||||
u32 D_80181420[2048];
|
||||
u32 D_80181764[2048];
|
||||
|
||||
@ -55,17 +27,19 @@ static void* clut_anims[] = {
|
||||
};
|
||||
|
||||
extern void* WRP_g_EntityGfxs[];
|
||||
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
#include "../../st/wrp/sprite_banks.h" // TODO OVL_EXPORT
|
||||
#include "../../st/wrp/layers.h" // TODO OVL_EXPORT
|
||||
static Overlay g_StageDesc = {
|
||||
Update,
|
||||
HitDetection,
|
||||
UpdateRoomPosition,
|
||||
MyInitRoomEntities,
|
||||
NULL, // set in InitStageWrp
|
||||
sprite_banks,
|
||||
OVL_EXPORT(rooms),
|
||||
spriteBanks,
|
||||
clut_anims,
|
||||
NULL,
|
||||
NULL, // set in InitStageWrp
|
||||
rooms_layers,
|
||||
WRP_g_EntityGfxs,
|
||||
UpdateStageEntities,
|
||||
NULL,
|
||||
@ -75,22 +49,6 @@ static Overlay g_StageDesc = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
LayoutEntity* D_80181228;
|
||||
LayoutEntity* D_80181250;
|
||||
LayoutEntity* D_801812A0;
|
||||
LayoutEntity* D_801812C8;
|
||||
LayoutEntity* D_80181278;
|
||||
LayoutEntity* D_801812F0;
|
||||
LayoutEntity* D_80181304;
|
||||
|
||||
LayoutEntity* D_80181324;
|
||||
LayoutEntity* D_8018134C;
|
||||
LayoutEntity* D_80181374;
|
||||
LayoutEntity* D_8018139C;
|
||||
LayoutEntity* D_801813C4;
|
||||
LayoutEntity* D_801813EC;
|
||||
LayoutEntity* D_80181400;
|
||||
|
||||
void InitStageWrp(Overlay* o) {
|
||||
LoadReset();
|
||||
|
||||
@ -121,136 +79,8 @@ void InitStageWrp(Overlay* o) {
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
D_80181228 = LoadObjLayout("assets/st/wrp/D_80181228.layoutobj.json");
|
||||
D_80181250 = LoadObjLayout("assets/st/wrp/D_80181250.layoutobj.json");
|
||||
D_801812A0 = LoadObjLayout("assets/st/wrp/D_801812A0.layoutobj.json");
|
||||
D_801812C8 = LoadObjLayout("assets/st/wrp/D_801812C8.layoutobj.json");
|
||||
D_80181278 = LoadObjLayout("assets/st/wrp/D_80181278.layoutobj.json");
|
||||
D_801812F0 = LoadObjLayout("assets/st/wrp/D_801812F0.layoutobj.json");
|
||||
D_80181304 = LoadObjLayout("assets/st/wrp/D_80181304.layoutobj.json");
|
||||
//
|
||||
|
||||
D_80181324 = LoadObjLayout("assets/st/wrp/D_80181324.layoutobj.json");
|
||||
D_8018134C = LoadObjLayout("assets/st/wrp/D_8018134C.layoutobj.json");
|
||||
D_80181374 = LoadObjLayout("assets/st/wrp/D_80181374.layoutobj.json");
|
||||
D_8018139C = LoadObjLayout("assets/st/wrp/D_8018139C.layoutobj.json");
|
||||
D_801813C4 = LoadObjLayout("assets/st/wrp/D_801813C4.layoutobj.json");
|
||||
D_801813EC = LoadObjLayout("assets/st/wrp/D_801813EC.layoutobj.json");
|
||||
D_80181400 = LoadObjLayout("assets/st/wrp/D_80181400.layoutobj.json");
|
||||
|
||||
int layoutId = 0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181228;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181250;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812A0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812C8;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181278;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_801812F0;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181304;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181304;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181304;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181304;
|
||||
g_pStObjLayoutHorizontal[layoutId++] = D_80181304;
|
||||
|
||||
layoutId = 0;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181324;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_8018134C;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_8018139C;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813C4;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181374;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_801813EC;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181400;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181400;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181400;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181400;
|
||||
g_pStObjLayoutVertical[layoutId++] = D_80181400;
|
||||
|
||||
sprite_banks[1] = LoadSpriteParts("assets/st/wrp/D_80186D88.animset.json");
|
||||
|
||||
g_StageDesc.tileLayers = LoadRoomsLayers("assets/st/wrp/rooms.layers.json");
|
||||
g_StageDesc.rooms = LoadRoomDefs("assets/st/wrp/g_Rooms.roomdef.json");
|
||||
memcpy(o, &g_StageDesc, sizeof(Overlay));
|
||||
}
|
||||
|
||||
|
8
src/st/.gitignore
vendored
Normal file
8
src/st/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
rooms.c
|
||||
sprites.c
|
||||
e_laydef.c
|
||||
e_layout.c
|
||||
layers.h
|
||||
sprite_banks.h
|
||||
tilemap_*.h
|
||||
tiledef_*.h
|
@ -4,6 +4,8 @@
|
||||
#include "common.h"
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) CEN_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0x00
|
||||
|
||||
// CEN Sound IDs
|
||||
|
@ -1,40 +1,27 @@
|
||||
#include "cen.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* D_8018019C[];
|
||||
void UpdateStageEntities();
|
||||
|
||||
static Overlay StageOverlay = {
|
||||
Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = NULL,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = D_8018019C,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
.unk2c = NULL,
|
||||
.unk30 = NULL,
|
||||
.unk34 = NULL,
|
||||
.unk38 = NULL,
|
||||
.StageEndCutScene = NULL,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_8018C754[];
|
||||
extern SpriteParts* D_8018C60C[];
|
||||
extern SpriteParts* D_8018CED8[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_8018C754, D_8018C60C, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, D_8018CED8, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_8018658C[0x80];
|
||||
extern u16* D_8018678C[0x80];
|
||||
@ -50,3 +37,5 @@ static u16** D_801800A0[] = {
|
||||
static void* Cluts[] = {
|
||||
D_801800A0,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
7
src/st/cen/tile_data.c
Normal file
7
src/st/cen/tile_data.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_069EC.h"
|
||||
#include "tilemap_06DEC.h"
|
||||
#include "tilemap_07FEC.h"
|
||||
#include "tiledef_085EC.h"
|
||||
#include "tiledef_0C5FC.h"
|
@ -1,6 +1,8 @@
|
||||
#include "stage.h"
|
||||
#define STAGE_DRE_H
|
||||
|
||||
#define OVL_EXPORT(x) DRE_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0xD3
|
||||
|
||||
typedef enum {
|
||||
|
@ -1,38 +1,27 @@
|
||||
#include "dre.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* g_GfxBanks[];
|
||||
void UpdateStageEntities();
|
||||
|
||||
static Overlay StageOverlay = {
|
||||
Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = NULL,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_GfxBanks,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
.unk2c = NULL,
|
||||
.unk30 = NULL,
|
||||
.unk34 = NULL,
|
||||
.unk38 = NULL,
|
||||
.StageEndCutScene = NULL,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_80190168[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_80190168, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_8018AEA8[0x50];
|
||||
extern u16* D_8018AF48[0x80];
|
||||
@ -53,3 +42,5 @@ static u16** Clut[] = {
|
||||
static void* Cluts[] = {
|
||||
Clut,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
6
src/st/dre/tile_data.c
Normal file
6
src/st/dre/tile_data.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_0B548.h"
|
||||
#include "tilemap_0B948.h"
|
||||
#include "tiledef_0C148.h"
|
||||
#include "tiledef_10158.h"
|
@ -8,7 +8,7 @@ extern GfxBank* g_pStTileset[];
|
||||
void UpdateStageEntities();
|
||||
void func_8018E1D4();
|
||||
|
||||
static Overlay StageOverlay = {
|
||||
static Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) MAD_##x
|
||||
|
||||
typedef enum {
|
||||
E_NONE,
|
||||
E_BREAKABLE,
|
||||
|
@ -1,44 +1,27 @@
|
||||
#include "no3.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* g_GfxBanks[];
|
||||
void UpdateStageEntities();
|
||||
|
||||
static AbbreviatedOverlay StageOverlay = {
|
||||
AbbreviatedOverlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = g_pStObjLayoutHorizontal,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_GfxBanks,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_801AEA68[];
|
||||
extern SpriteParts* D_801B009C[];
|
||||
extern SpriteParts* D_801AF754[];
|
||||
extern SpriteParts* D_801AFB3C[];
|
||||
extern SpriteParts* D_801B077C[];
|
||||
extern SpriteParts* D_801B0B80[];
|
||||
extern SpriteParts* D_801B57C0[];
|
||||
extern SpriteParts* D_801B5904[];
|
||||
extern SpriteParts* D_801B721C[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_801AEA68, D_801B009C, D_801AF754, D_801AFB3C, D_801B077C,
|
||||
D_801B0B80, D_801B57C0, D_801B5904, D_801B721C, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
};
|
||||
|
||||
extern u16* table[];
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_80198578[0x100];
|
||||
extern u16* D_801966B8[0x8C0];
|
||||
@ -100,3 +83,5 @@ static u16** Clut[] = {
|
||||
static void* Cluts[] = {
|
||||
Clut,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) NO3_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0x34
|
||||
|
||||
// NO3 Sound IDs
|
||||
|
48
src/st/no3/tile_data.c
Normal file
48
src/st/no3/tile_data.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_18838.h"
|
||||
#include "tilemap_19438.h"
|
||||
#include "tilemap_19638.h"
|
||||
#include "tilemap_19A38.h"
|
||||
#include "tilemap_19E38.h"
|
||||
#include "tilemap_1AC38.h"
|
||||
#include "tilemap_1AE38.h"
|
||||
#include "tilemap_1B038.h"
|
||||
#include "tilemap_1B238.h"
|
||||
#include "tilemap_1B638.h"
|
||||
#include "tilemap_1BA38.h"
|
||||
#include "tilemap_1C238.h"
|
||||
#include "tilemap_1CA38.h"
|
||||
#include "tilemap_1CC38.h"
|
||||
#include "tilemap_1CE38.h"
|
||||
#include "tilemap_1DA38.h"
|
||||
#include "tilemap_1E638.h"
|
||||
#include "tilemap_1E838.h"
|
||||
#include "tilemap_1EA38.h"
|
||||
#include "tilemap_1F638.h"
|
||||
#include "tilemap_1F838.h"
|
||||
#include "tilemap_1FA38.h"
|
||||
#include "tilemap_1FC38.h"
|
||||
#include "tilemap_20038.h"
|
||||
#include "tilemap_20238.h"
|
||||
#include "tilemap_20838.h"
|
||||
#include "tilemap_20E38.h"
|
||||
#include "tilemap_21038.h"
|
||||
#include "tilemap_21238.h"
|
||||
#include "tilemap_21438.h"
|
||||
#include "tilemap_21638.h"
|
||||
#include "tilemap_22238.h"
|
||||
#include "tilemap_22E38.h"
|
||||
#include "tilemap_23038.h"
|
||||
#include "tilemap_23238.h"
|
||||
#include "tilemap_23438.h"
|
||||
#include "tilemap_23638.h"
|
||||
#include "tilemap_25A38.h"
|
||||
#include "tilemap_25C38.h"
|
||||
#include "tilemap_25E38.h"
|
||||
#include "tilemap_26038.h"
|
||||
#include "tilemap_26238.h"
|
||||
#include "tilemap_26438.h"
|
||||
#include "tiledef_26A38.h"
|
||||
#include "tiledef_2AA48.h"
|
||||
#include "tiledef_2EA58.h"
|
@ -1,45 +1,27 @@
|
||||
#include "np3.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* g_GfxBanks[];
|
||||
void UpdateStageEntities();
|
||||
|
||||
static AbbreviatedOverlay StageOverlay = {
|
||||
static AbbreviatedOverlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = g_pStObjLayoutHorizontal,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_GfxBanks,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_801AA124[];
|
||||
extern SpriteParts* D_801AB758[];
|
||||
extern SpriteParts* D_801AAE10[];
|
||||
extern SpriteParts* D_801AB1F8[];
|
||||
extern SpriteParts* D_801ABE38[];
|
||||
extern SpriteParts* D_801AED34[];
|
||||
extern SpriteParts* D_801AC23C[];
|
||||
extern SpriteParts* D_801AD278[];
|
||||
extern SpriteParts* D_801AC380[];
|
||||
extern SpriteParts* D_801AEEF8[];
|
||||
extern SpriteParts* D_801B08B0[];
|
||||
extern SpriteParts* D_801B1B30[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_801AA124, D_801AB758, D_801AAE10, D_801AB1F8, D_801ABE38,
|
||||
D_801AED34, D_801AC23C, D_801AD278, D_801AC380, D_801AEEF8, D_801B08B0,
|
||||
D_801B1B30, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_801963B4[0x100];
|
||||
extern u16* D_80194914[0x8C0];
|
||||
@ -88,3 +70,5 @@ static u16** Clut[] = {
|
||||
static void* Cluts[] = {
|
||||
Clut,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) NP3_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0x34
|
||||
|
||||
typedef enum {
|
||||
|
44
src/st/np3/tile_data.c
Normal file
44
src/st/np3/tile_data.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_168F4.h"
|
||||
#include "tilemap_174F4.h"
|
||||
#include "tilemap_176F4.h"
|
||||
#include "tilemap_17AF4.h"
|
||||
#include "tilemap_17EF4.h"
|
||||
#include "tilemap_18CF4.h"
|
||||
#include "tilemap_18EF4.h"
|
||||
#include "tilemap_190F4.h"
|
||||
#include "tilemap_192F4.h"
|
||||
#include "tilemap_196F4.h"
|
||||
#include "tilemap_19AF4.h"
|
||||
#include "tilemap_1A2F4.h"
|
||||
#include "tilemap_1AAF4.h"
|
||||
#include "tilemap_1ACF4.h"
|
||||
#include "tilemap_1AEF4.h"
|
||||
#include "tilemap_1BAF4.h"
|
||||
#include "tilemap_1C6F4.h"
|
||||
#include "tilemap_1C8F4.h"
|
||||
#include "tilemap_1CAF4.h"
|
||||
#include "tilemap_1D6F4.h"
|
||||
#include "tilemap_1D8F4.h"
|
||||
#include "tilemap_1DAF4.h"
|
||||
#include "tilemap_1DCF4.h"
|
||||
#include "tilemap_1E0F4.h"
|
||||
#include "tilemap_1E2F4.h"
|
||||
#include "tilemap_1E8F4.h"
|
||||
#include "tilemap_1EEF4.h"
|
||||
#include "tilemap_1F0F4.h"
|
||||
#include "tilemap_1F2F4.h"
|
||||
#include "tilemap_1F4F4.h"
|
||||
#include "tilemap_1F6F4.h"
|
||||
#include "tilemap_202F4.h"
|
||||
#include "tilemap_20EF4.h"
|
||||
#include "tilemap_210F4.h"
|
||||
#include "tilemap_212F4.h"
|
||||
#include "tilemap_214F4.h"
|
||||
#include "tilemap_216F4.h"
|
||||
#include "tilemap_218F4.h"
|
||||
#include "tilemap_21AF4.h"
|
||||
#include "tiledef_220F4.h"
|
||||
#include "tiledef_26104.h"
|
||||
#include "tiledef_2A114.h"
|
@ -50,6 +50,7 @@ void func_801C3F9C(AxePrim* prim) {
|
||||
}
|
||||
|
||||
// Called by EntityAxeKnight
|
||||
extern s16* sprites_nz0_3[];
|
||||
s32 func_801C4198(Entity* axeKnight) {
|
||||
Primitive* prim;
|
||||
s32 primIndex;
|
||||
@ -63,7 +64,7 @@ s32 func_801C4198(Entity* axeKnight) {
|
||||
switch (axeKnight->step_s) {
|
||||
case 0:
|
||||
clutBase = D_80180C6A;
|
||||
dataPtr = D_801A79E4[axeKnight->animCurFrame];
|
||||
dataPtr = sprites_nz0_3[axeKnight->animCurFrame];
|
||||
primIndex = g_api.AllocPrimitives(PRIM_GT4, *dataPtr * 2);
|
||||
if (primIndex != -1) {
|
||||
axeKnight->flags |= FLAG_HAS_PRIMS;
|
||||
|
@ -1,35 +0,0 @@
|
||||
#include "nz0.h"
|
||||
|
||||
void Update(void);
|
||||
void HitDetection(void);
|
||||
void UpdateRoomPosition(void);
|
||||
void InitRoomEntities(s32 objLayoutId);
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern s16** g_SpriteBanks[];
|
||||
extern void* g_Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern void* g_EntityGfxs[];
|
||||
void UpdateStageEntities(void);
|
||||
|
||||
extern u8** D_801A6E8C;
|
||||
extern s32* D_801A6FCC;
|
||||
extern s32* D_801A7304;
|
||||
|
||||
Overlay g_StageOverlay = {
|
||||
/* 0x00 */ Update,
|
||||
/* 0x04 */ HitDetection,
|
||||
/* 0x08 */ UpdateRoomPosition,
|
||||
/* 0x0C */ InitRoomEntities,
|
||||
/* 0x10 */ g_Rooms,
|
||||
/* 0x14 */ 0x8018002C,
|
||||
/* 0x18 */ g_Cluts,
|
||||
/* 0x1C */ g_pStObjLayoutHorizontal,
|
||||
/* 0x20 */ g_TileLayers,
|
||||
/* 0x24 */ g_EntityGfxs,
|
||||
/* 0x28 */ UpdateStageEntities,
|
||||
/* 0x2C */ NULL,
|
||||
/* 0x30 */ &D_801A6E8C,
|
||||
/* 0x34 */ &D_801A6FCC,
|
||||
/* 0x38 */ &D_801A7304,
|
||||
/* 0x3C */ 0x801A79E4,
|
||||
};
|
64
src/st/nz0/header.c
Normal file
64
src/st/nz0/header.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include "nz0.h"
|
||||
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* g_Cluts[];
|
||||
extern void* g_EntityGfxs[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
|
||||
void Update();
|
||||
void HitDetection();
|
||||
void UpdateRoomPosition();
|
||||
void InitRoomEntities();
|
||||
void UpdateStageEntities();
|
||||
|
||||
AbbreviatedOverlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = g_Cluts,
|
||||
.objLayoutHorizontal = g_pStObjLayoutHorizontal,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_EntityGfxs,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
};
|
||||
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u_long* D_80195C3C;
|
||||
extern u_long* D_80195CDC;
|
||||
extern u_long* D_80195D3C;
|
||||
extern u_long* D_80195DBC;
|
||||
extern u_long* D_80195E1C;
|
||||
extern u_long* D_80195E3C;
|
||||
extern u_long* D_80195E9C;
|
||||
extern u_long* D_80195F1C;
|
||||
extern u_long* D_80195F9C;
|
||||
extern u_long* D_8019601C;
|
||||
extern u_long* D_801962DC;
|
||||
extern u_long* D_8019641C;
|
||||
extern u_long* D_8019647C;
|
||||
extern u_long* D_8019657C;
|
||||
extern u_long* D_8019663C;
|
||||
extern u_long* D_8019665C;
|
||||
extern u_long* D_8019685C;
|
||||
static u_long* D_8018008C[] = {
|
||||
0x00000005, 0x00002000, 0x00000040, &D_80195C3C, 0x00002040, 0x00000010,
|
||||
&D_80195CDC, 0x00002050, 0x00000040, &D_80195D3C, 0x00002090, 0x00000030,
|
||||
&D_80195DBC, 0x000020C0, 0x00000010, &D_80195E1C, 0x000020D0, 0x00000030,
|
||||
&D_80195E3C, 0x00002110, 0x00000040, &D_80195F1C, 0x00002150, 0x00000040,
|
||||
&D_80195F9C, 0x00002190, 0x00000040, &D_80195E9C, 0x000021D0, 0x000000A0,
|
||||
&D_801962DC, 0x00002270, 0x00000030, &D_8019641C, 0x000022A0, 0x00000080,
|
||||
&D_8019647C, 0x00002320, 0x00000050, &D_8019657C, 0x00002370, 0x00000010,
|
||||
&D_8019663C, 0x00002380, 0x00000080, &D_8019665C, 0x00002400, 0x00000080,
|
||||
&D_8019685C, 0x00002E00, 0x00000100, &D_8019601C, 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
static void* g_Cluts[] = {
|
||||
&D_8018008C,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
@ -1,5 +1,7 @@
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) NZ0_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0x7E
|
||||
|
||||
typedef enum {
|
||||
@ -402,7 +404,6 @@ extern u8 D_80181538[];
|
||||
|
||||
// for func_801C4198
|
||||
extern u16 D_80180C6A;
|
||||
extern s16* D_801A79E4[];
|
||||
|
||||
// for EntityMagicallySealedDoor
|
||||
extern u8 g_eBlueDoorUV[3][8];
|
||||
|
37
src/st/nz0/tile_data.c
Normal file
37
src/st/nz0/tile_data.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_16A5C.h"
|
||||
#include "tilemap_16C5C.h"
|
||||
#include "tilemap_16E5C.h"
|
||||
#include "tilemap_1725C.h"
|
||||
#include "tilemap_17A5C.h"
|
||||
#include "tilemap_17C5C.h"
|
||||
#include "tilemap_17E5C.h"
|
||||
#include "tilemap_1805C.h"
|
||||
#include "tilemap_1845C.h"
|
||||
#include "tilemap_1865C.h"
|
||||
#include "tilemap_1885C.h"
|
||||
#include "tilemap_18A5C.h"
|
||||
#include "tilemap_1905C.h"
|
||||
#include "tilemap_1925C.h"
|
||||
#include "tilemap_19C5C.h"
|
||||
#include "tilemap_1A05C.h"
|
||||
#include "tilemap_1A45C.h"
|
||||
#include "tilemap_1AA5C.h"
|
||||
#include "tilemap_1AE5C.h"
|
||||
#include "tilemap_1BE5C.h"
|
||||
#include "tilemap_1C65C.h"
|
||||
#include "tilemap_1C85C.h"
|
||||
#include "tilemap_1D45C.h"
|
||||
#include "tilemap_1D65C.h"
|
||||
#include "tilemap_1E25C.h"
|
||||
#include "tilemap_1EE5C.h"
|
||||
#include "tilemap_1F25C.h"
|
||||
#include "tilemap_2005C.h"
|
||||
#include "tilemap_2025C.h"
|
||||
#include "tilemap_2045C.h"
|
||||
#include "tilemap_2065C.h"
|
||||
#include "tilemap_2085C.h"
|
||||
#include "tiledef_20E5C.h"
|
||||
#include "tiledef_24E6C.h"
|
||||
#include "tiledef_26E7C.h"
|
@ -192,7 +192,26 @@ u8 func_80191CC8(s32 arg0) {
|
||||
|
||||
INCLUDE_ASM("st/rwrp/nonmatchings/113A0", func_80192348);
|
||||
|
||||
INCLUDE_ASM("st/rwrp/nonmatchings/113A0", func_80192414);
|
||||
extern PfnEntityUpdate PfnEntityUpdates[];
|
||||
void func_80192414(u16 entityId, Entity* src, Entity* dst) {
|
||||
DestroyEntity(dst);
|
||||
dst->entityId = entityId;
|
||||
dst->pfnUpdate = PfnEntityUpdates[entityId - 1];
|
||||
dst->posX.i.hi = src->posX.i.hi;
|
||||
dst->posY.i.hi = src->posY.i.hi;
|
||||
dst->unk5A = src->unk5A;
|
||||
dst->zPriority = src->zPriority;
|
||||
dst->animSet = src->animSet;
|
||||
dst->flags = FLAG_UNK_2000 | FLAG_UNK_01000000 | FLAG_UNK_04000000 |
|
||||
FLAG_UNK_08000000 | FLAG_DESTROY_IF_BARELY_OUT_OF_CAMERA |
|
||||
FLAG_DESTROY_IF_OUT_OF_CAMERA;
|
||||
|
||||
if (src->palette & PAL_OVL_FLAG) {
|
||||
dst->palette = src->hitEffect;
|
||||
} else {
|
||||
dst->palette = src->palette;
|
||||
}
|
||||
}
|
||||
|
||||
void func_801924DC(void) {
|
||||
Entity* entity;
|
||||
|
@ -1,40 +1,27 @@
|
||||
#include "rwrp.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* g_GfxBanks[];
|
||||
void UpdateStageEntities();
|
||||
|
||||
static Overlay StageOverlay = {
|
||||
static Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = NULL,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_GfxBanks,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
.unk2c = NULL,
|
||||
.unk30 = NULL,
|
||||
.unk34 = NULL,
|
||||
.unk38 = NULL,
|
||||
.StageEndCutScene = NULL,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_80186FE8[];
|
||||
extern SpriteParts* D_80187090[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_80186FE8, D_80187090, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_80181D08[0x100];
|
||||
|
||||
@ -47,3 +34,5 @@ static u16** Clut[] = {
|
||||
static void* Cluts[] = {
|
||||
Clut,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "common.h"
|
||||
#include "stage.h"
|
||||
|
||||
#define OVL_EXPORT(x) RWRP_##x
|
||||
|
||||
#define CASTLE_FLAG_BANK 0x00
|
||||
|
||||
// RWRP Sound IDs
|
||||
|
10
src/st/rwrp/tile_data.c
Normal file
10
src/st/rwrp/tile_data.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_01FC8.h"
|
||||
#include "tilemap_021C8.h"
|
||||
#include "tilemap_023C8.h"
|
||||
#include "tilemap_025C8.h"
|
||||
#include "tilemap_027C8.h"
|
||||
#include "tilemap_029C8.h"
|
||||
#include "tiledef_02FC8.h"
|
||||
#include "tiledef_06FD8.h"
|
@ -1,46 +1,29 @@
|
||||
#include "st0.h"
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern SpriteParts** SpriteBanks[];
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
extern signed short* spriteBanks[];
|
||||
extern void* Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
extern GfxBank* g_EntityGfxs[];
|
||||
void UpdateStageEntities();
|
||||
void PrologueScroll();
|
||||
|
||||
static Overlay StageOverlay = {
|
||||
static Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = g_Rooms,
|
||||
.spriteBanks = SpriteBanks,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = Cluts,
|
||||
.objLayoutHorizontal = g_pStObjLayoutHorizontal,
|
||||
.tileLayers = g_TileLayers,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = g_EntityGfxs,
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
.unk2c = NULL,
|
||||
.unk30 = NULL,
|
||||
.unk34 = NULL,
|
||||
.unk38 = NULL,
|
||||
.StageEndCutScene = PrologueScroll,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_801A26E0[];
|
||||
extern SpriteParts* D_801A4298[];
|
||||
extern SpriteParts* D_801A6A68[];
|
||||
extern SpriteParts* D_801A6F90[];
|
||||
extern SpriteParts* D_801A7430[];
|
||||
extern SpriteParts* D_801A7728[];
|
||||
extern SpriteParts* D_801A77CC[];
|
||||
|
||||
static SpriteParts** SpriteBanks[] = {
|
||||
NULL, D_801A26E0, D_801A4298, D_801A6A68, D_801A6F90, D_801A7430,
|
||||
D_801A7728, D_801A77CC, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16* D_8019A750[0x70];
|
||||
extern u16* D_8019AD30[0x20];
|
||||
@ -64,3 +47,5 @@ static u16** Clut[] = {
|
||||
static void* Cluts[] = {
|
||||
Clut,
|
||||
};
|
||||
|
||||
#include "layers.h"
|
||||
|
@ -3,6 +3,8 @@
|
||||
#undef STAGE
|
||||
#define STAGE STAGE_ST0
|
||||
|
||||
#define OVL_EXPORT(x) ST0_##x
|
||||
|
||||
typedef enum {
|
||||
E_NONE,
|
||||
E_BREAKABLE,
|
||||
|
8
src/st/st0/tile_data.c
Normal file
8
src/st/st0/tile_data.c
Normal file
@ -0,0 +1,8 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_1B2D0.h"
|
||||
#include "tilemap_1C6D0.h"
|
||||
#include "tilemap_1DAD0.h"
|
||||
#include "tilemap_1E0D0.h"
|
||||
#include "tilemap_1E2D0.h"
|
||||
#include "tiledef_226D0.h"
|
@ -1,269 +0,0 @@
|
||||
/*
|
||||
* File: 6FD0.c
|
||||
* Overlay: WRP
|
||||
* Description: All warp rooms.
|
||||
*/
|
||||
|
||||
#include "wrp.h"
|
||||
#include "../st_private.h"
|
||||
|
||||
// *** Overlay exports start ***
|
||||
void CreateEntityWhenInHorizontalRange(LayoutEntity*);
|
||||
void func_8018A520(s16);
|
||||
void func_8018CAB0(void);
|
||||
void func_801916C4(u16);
|
||||
void BottomCornerText(u8*, u8);
|
||||
|
||||
extern u32 D_80181420[];
|
||||
extern u32 D_80181764[];
|
||||
static void* D_801801B8[] = {
|
||||
/* 0x1B8 */ (void*)0x00000000,
|
||||
/* 0x1BC */ (void*)0x00000000,
|
||||
/* 0x1C0 */ (void*)0x00000000,
|
||||
/* 0x1C4 */ (void*)0x00000000,
|
||||
/* 0x1C8 */ (void*)0xFFFFFFFF,
|
||||
};
|
||||
static void* D_801801CC[] = {
|
||||
/* 0x1CC */ (void*)0x00000004,
|
||||
/* 0x1D0 */ (void*)0x00400100,
|
||||
/* 0x1D4 */ (void*)0x00800080,
|
||||
/* 0x1D8 */ (void*)D_80181420,
|
||||
/* 0x1DC */ (void*)0x00600100,
|
||||
/* 0x1E0 */ (void*)0x00800080,
|
||||
/* 0x1E4 */ (void*)D_80181764,
|
||||
/* 0x1E8 */ (void*)0xFFFFFFFF,
|
||||
};
|
||||
void* OVL_EXPORT(g_EntityGfxs)[] = {
|
||||
/* 0x1EC */ D_801801B8,
|
||||
/* 0x1F0 */ D_801801B8,
|
||||
/* 0x1F4 */ D_801801B8,
|
||||
/* 0x1F8 */ D_801801B8,
|
||||
/* 0x1FC */ D_801801B8,
|
||||
/* 0x200 */ D_801801CC,
|
||||
/* 0x204 */ D_801801B8,
|
||||
/* 0x208 */ D_801801B8,
|
||||
/* 0x20C */ D_801801B8,
|
||||
/* 0x210 */ D_801801B8,
|
||||
/* 0x214 */ D_801801B8,
|
||||
/* 0x218 */ D_801801B8,
|
||||
/* 0x21C */ D_801801B8,
|
||||
/* 0x220 */ D_801801B8,
|
||||
/* 0x224 */ D_801801B8,
|
||||
/* 0x228 */ D_801801B8,
|
||||
/* 0x22C */ D_801801B8,
|
||||
/* 0x230 */ D_801801B8,
|
||||
/* 0x234 */ D_801801B8,
|
||||
/* 0x238 */ D_801801B8,
|
||||
};
|
||||
// *** Layout definition end ***
|
||||
|
||||
// *** Layout entity definition start ***
|
||||
extern LayoutEntity D_80181228[];
|
||||
extern LayoutEntity D_80181250[];
|
||||
extern LayoutEntity D_801812A0[];
|
||||
extern LayoutEntity D_801812C8[];
|
||||
extern LayoutEntity D_80181278[];
|
||||
extern LayoutEntity D_801812F0[];
|
||||
extern LayoutEntity D_80181304[];
|
||||
LayoutEntity* g_pStObjLayoutHorizontal[] = {
|
||||
/* 0x23C */ D_801812F0,
|
||||
/* 0x240 */ D_80181228,
|
||||
/* 0x244 */ D_80181250,
|
||||
/* 0x248 */ D_801812A0,
|
||||
/* 0x24C */ D_801812C8,
|
||||
/* 0x250 */ D_80181278,
|
||||
/* 0x254 */ D_801812F0,
|
||||
/* 0x258 */ D_801812F0,
|
||||
/* 0x25C */ D_801812F0,
|
||||
/* 0x260 */ D_801812F0,
|
||||
/* 0x264 */ D_801812F0,
|
||||
/* 0x268 */ D_801812F0,
|
||||
/* 0x26C */ D_801812F0,
|
||||
/* 0x270 */ D_801812F0,
|
||||
/* 0x274 */ D_801812F0,
|
||||
/* 0x278 */ D_801812F0,
|
||||
/* 0x27C */ D_801812F0,
|
||||
/* 0x280 */ D_801812F0,
|
||||
/* 0x284 */ D_801812F0,
|
||||
/* 0x288 */ D_801812F0,
|
||||
/* 0x28C */ D_801812F0,
|
||||
/* 0x290 */ D_801812F0,
|
||||
/* 0x294 */ D_801812F0,
|
||||
/* 0x298 */ D_801812F0,
|
||||
/* 0x29C */ D_801812F0,
|
||||
/* 0x2A0 */ D_801812F0,
|
||||
/* 0x2A4 */ D_801812F0,
|
||||
/* 0x2A8 */ D_801812F0,
|
||||
/* 0x2AC */ D_801812F0,
|
||||
/* 0x2B0 */ D_801812F0,
|
||||
/* 0x2B4 */ D_801812F0,
|
||||
/* 0x2B8 */ D_801812F0,
|
||||
/* 0x2BC */ D_801812F0,
|
||||
/* 0x2C0 */ D_801812F0,
|
||||
/* 0x2C4 */ D_801812F0,
|
||||
/* 0x2C8 */ D_801812F0,
|
||||
/* 0x2CC */ D_801812F0,
|
||||
/* 0x2D0 */ D_801812F0,
|
||||
/* 0x2D4 */ D_801812F0,
|
||||
/* 0x2D8 */ D_801812F0,
|
||||
/* 0x2DC */ D_801812F0,
|
||||
/* 0x2E0 */ D_801812F0,
|
||||
/* 0x2E4 */ D_801812F0,
|
||||
/* 0x2E8 */ D_801812F0,
|
||||
/* 0x2EC */ D_801812F0,
|
||||
/* 0x2F0 */ D_801812F0,
|
||||
/* 0x2F4 */ D_801812F0,
|
||||
/* 0x2F8 */ D_801812F0,
|
||||
/* 0x2FC */ D_80181304,
|
||||
/* 0x300 */ D_80181304,
|
||||
/* 0x304 */ D_80181304,
|
||||
/* 0x308 */ D_80181304,
|
||||
/* 0x30C */ D_80181304,
|
||||
};
|
||||
|
||||
extern LayoutEntity D_80181324[];
|
||||
extern LayoutEntity D_8018134C[];
|
||||
extern LayoutEntity D_80181374[];
|
||||
extern LayoutEntity D_8018139C[];
|
||||
extern LayoutEntity D_801813C4[];
|
||||
extern LayoutEntity D_801813EC[];
|
||||
extern LayoutEntity D_80181400[];
|
||||
LayoutEntity* g_pStObjLayoutVertical[] = {
|
||||
/* 310 */ D_801813EC,
|
||||
/* 314 */ D_80181324,
|
||||
/* 318 */ D_8018134C,
|
||||
/* 31C */ D_8018139C,
|
||||
/* 320 */ D_801813C4,
|
||||
/* 324 */ D_80181374,
|
||||
/* 328 */ D_801813EC,
|
||||
/* 32C */ D_801813EC,
|
||||
/* 330 */ D_801813EC,
|
||||
/* 334 */ D_801813EC,
|
||||
/* 338 */ D_801813EC,
|
||||
/* 33C */ D_801813EC,
|
||||
/* 340 */ D_801813EC,
|
||||
/* 344 */ D_801813EC,
|
||||
/* 348 */ D_801813EC,
|
||||
/* 34C */ D_801813EC,
|
||||
/* 350 */ D_801813EC,
|
||||
/* 354 */ D_801813EC,
|
||||
/* 358 */ D_801813EC,
|
||||
/* 35C */ D_801813EC,
|
||||
/* 360 */ D_801813EC,
|
||||
/* 364 */ D_801813EC,
|
||||
/* 368 */ D_801813EC,
|
||||
/* 36C */ D_801813EC,
|
||||
/* 370 */ D_801813EC,
|
||||
/* 374 */ D_801813EC,
|
||||
/* 378 */ D_801813EC,
|
||||
/* 37C */ D_801813EC,
|
||||
/* 380 */ D_801813EC,
|
||||
/* 384 */ D_801813EC,
|
||||
/* 388 */ D_801813EC,
|
||||
/* 38C */ D_801813EC,
|
||||
/* 390 */ D_801813EC,
|
||||
/* 394 */ D_801813EC,
|
||||
/* 398 */ D_801813EC,
|
||||
/* 39C */ D_801813EC,
|
||||
/* 3A0 */ D_801813EC,
|
||||
/* 3A4 */ D_801813EC,
|
||||
/* 3A8 */ D_801813EC,
|
||||
/* 3AC */ D_801813EC,
|
||||
/* 3B0 */ D_801813EC,
|
||||
/* 3B4 */ D_801813EC,
|
||||
/* 3B8 */ D_801813EC,
|
||||
/* 3BC */ D_801813EC,
|
||||
/* 3C0 */ D_801813EC,
|
||||
/* 3C4 */ D_801813EC,
|
||||
/* 3C8 */ D_801813EC,
|
||||
/* 3CC */ D_801813EC,
|
||||
/* 3D0 */ D_80181400,
|
||||
/* 3D4 */ D_80181400,
|
||||
/* 3D8 */ D_80181400,
|
||||
/* 3DC */ D_80181400,
|
||||
/* 3E0 */ D_80181400,
|
||||
};
|
||||
// *** Layout entity definition end ***
|
||||
|
||||
// *** entity definition start ***
|
||||
void func_80186FD0(Entity*);
|
||||
void func_801870B0(Entity*);
|
||||
void EntityUnkId13(Entity*);
|
||||
void EntityUnkId14(Entity*);
|
||||
void EntityUnkId15(Entity*);
|
||||
void EntityWarpRoom(Entity*);
|
||||
void EntityWarpSmallRocks(Entity*);
|
||||
void EntityPrizeDrop(Entity*);
|
||||
PfnEntityUpdate PfnEntityUpdates[] = {
|
||||
/* 3E4 */ (PfnEntityUpdate)EntityBreakable,
|
||||
/* 3E8 */ (PfnEntityUpdate)EntityExplosion,
|
||||
/* 3EC */ (PfnEntityUpdate)EntityPrizeDrop,
|
||||
/* 3F0 */ (PfnEntityUpdate)EntityDamageDisplay,
|
||||
/* 3F4 */ (PfnEntityUpdate)EntityRedDoor,
|
||||
/* 3F8 */ (PfnEntityUpdate)EntityIntenseExplosion,
|
||||
/* 3FC */ (PfnEntityUpdate)EntitySoulStealOrb,
|
||||
/* 400 */ (PfnEntityUpdate)EntityRoomForeground,
|
||||
/* 404 */ (PfnEntityUpdate)EntityStageNamePopup,
|
||||
/* 408 */ (PfnEntityUpdate)EntityEquipItemDrop,
|
||||
/* 40C */ (PfnEntityUpdate)EntityRelicOrb,
|
||||
/* 410 */ (PfnEntityUpdate)EntityHeartDrop,
|
||||
/* 414 */ (PfnEntityUpdate)EntityEnemyBlood,
|
||||
/* 418 */ (PfnEntityUpdate)EntityMessageBox,
|
||||
/* 41C */ (PfnEntityUpdate)EntityDummy,
|
||||
/* 420 */ (PfnEntityUpdate)EntityDummy,
|
||||
/* 424 */ (PfnEntityUpdate)func_80186FD0, // unused
|
||||
/* 428 */ (PfnEntityUpdate)func_801870B0, // unused? looks debugging stuff
|
||||
/* 42C */ (PfnEntityUpdate)EntityUnkId13,
|
||||
/* 430 */ (PfnEntityUpdate)EntityUnkId14,
|
||||
/* 434 */ (PfnEntityUpdate)EntityUnkId15,
|
||||
/* 438 */ (PfnEntityUpdate)EntityWarpRoom,
|
||||
/* 43C */ (PfnEntityUpdate)EntityWarpSmallRocks,
|
||||
};
|
||||
|
||||
// *** Group here all the Entity Init ***
|
||||
u16 g_eBreakableInit[] = {
|
||||
0x8001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
};
|
||||
u16 g_InitializeData0[] = {
|
||||
0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
|
||||
};
|
||||
u16 g_InitializeEntityData0[] = {
|
||||
0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000,
|
||||
};
|
||||
u16 g_EInitGeneric[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000,
|
||||
};
|
||||
u16 g_InitDataEnt13[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000,
|
||||
};
|
||||
u16 D_80180488[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
|
||||
};
|
||||
u16 g_eInitGeneric2[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
};
|
||||
u16 g_eDamageDisplayInit[] = {
|
||||
/**/ 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
/**/ 0x8001, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
/**/ 0x800B, 0x0001, 0x0048, 0x021A, 0x0061, 0x0000,
|
||||
};
|
||||
u16 D_801804C4[] = {
|
||||
0x8001, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000,
|
||||
};
|
||||
// ******
|
||||
|
||||
static u32 D_801804D0[] = {0x00FF0140};
|
||||
static u32 D_801804D4[] = {0x26022502, 0x26022702, 0x00000000};
|
||||
ObjInit2 D_801804E0[] = {
|
||||
{0x0006, 0x01FA, 0x0000, 0x0000, 0x00, 0x10, 0x00000000, D_801804D0},
|
||||
{0x8001, 0x00C0, 0x0000, 0x0000, 0x03, 0x30, 0x00000000, D_801804D4},
|
||||
};
|
||||
|
||||
// Owned by EntityRedDoor to animate the tiles behind the door itself.
|
||||
// There is a loop in EntityRedDoor that forces to write those tiles
|
||||
// at every frame based on the door state to create the animation.
|
||||
u16 g_eRedDoorTiles[2][8] = {
|
||||
{0x1D, 0x25, 0x75, 0x7D, 0xC6, 0xC7, 0xC8, 0xC9},
|
||||
{0x1F, 0x27, 0x77, 0x7F, 0xCA, 0xCB, 0xCC, 0xCD},
|
||||
};
|
44
src/st/wrp/d_1b8.c
Normal file
44
src/st/wrp/d_1b8.c
Normal file
@ -0,0 +1,44 @@
|
||||
#include "wrp.h"
|
||||
#include "../st_private.h"
|
||||
|
||||
extern u32 D_80181420[];
|
||||
extern u32 D_80181764[];
|
||||
static void* D_801801B8[] = {
|
||||
/* 0x1B8 */ (void*)0x00000000,
|
||||
/* 0x1BC */ (void*)0x00000000,
|
||||
/* 0x1C0 */ (void*)0x00000000,
|
||||
/* 0x1C4 */ (void*)0x00000000,
|
||||
/* 0x1C8 */ (void*)0xFFFFFFFF,
|
||||
};
|
||||
static void* D_801801CC[] = {
|
||||
/* 0x1CC */ (void*)0x00000004,
|
||||
/* 0x1D0 */ (void*)0x00400100,
|
||||
/* 0x1D4 */ (void*)0x00800080,
|
||||
/* 0x1D8 */ (void*)D_80181420,
|
||||
/* 0x1DC */ (void*)0x00600100,
|
||||
/* 0x1E0 */ (void*)0x00800080,
|
||||
/* 0x1E4 */ (void*)D_80181764,
|
||||
/* 0x1E8 */ (void*)0xFFFFFFFF,
|
||||
};
|
||||
void* OVL_EXPORT(g_EntityGfxs)[] = {
|
||||
/* 0x1EC */ D_801801B8,
|
||||
/* 0x1F0 */ D_801801B8,
|
||||
/* 0x1F4 */ D_801801B8,
|
||||
/* 0x1F8 */ D_801801B8,
|
||||
/* 0x1FC */ D_801801B8,
|
||||
/* 0x200 */ D_801801CC,
|
||||
/* 0x204 */ D_801801B8,
|
||||
/* 0x208 */ D_801801B8,
|
||||
/* 0x20C */ D_801801B8,
|
||||
/* 0x210 */ D_801801B8,
|
||||
/* 0x214 */ D_801801B8,
|
||||
/* 0x218 */ D_801801B8,
|
||||
/* 0x21C */ D_801801B8,
|
||||
/* 0x220 */ D_801801B8,
|
||||
/* 0x224 */ D_801801B8,
|
||||
/* 0x228 */ D_801801B8,
|
||||
/* 0x22C */ D_801801B8,
|
||||
/* 0x230 */ D_801801B8,
|
||||
/* 0x234 */ D_801801B8,
|
||||
/* 0x238 */ D_801801B8,
|
||||
};
|
84
src/st/wrp/d_3e4.c
Normal file
84
src/st/wrp/d_3e4.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "wrp.h"
|
||||
#include "../st_private.h"
|
||||
|
||||
void func_80186FD0(Entity*);
|
||||
void func_801870B0(Entity*);
|
||||
void EntityUnkId13(Entity*);
|
||||
void EntityUnkId14(Entity*);
|
||||
void EntityUnkId15(Entity*);
|
||||
void EntityWarpRoom(Entity*);
|
||||
void EntityWarpSmallRocks(Entity*);
|
||||
void EntityPrizeDrop(Entity*);
|
||||
PfnEntityUpdate PfnEntityUpdates[] = {
|
||||
/* 3E4 */ (PfnEntityUpdate)EntityBreakable,
|
||||
/* 3E8 */ (PfnEntityUpdate)EntityExplosion,
|
||||
/* 3EC */ (PfnEntityUpdate)EntityPrizeDrop,
|
||||
/* 3F0 */ (PfnEntityUpdate)EntityDamageDisplay,
|
||||
/* 3F4 */ (PfnEntityUpdate)EntityRedDoor,
|
||||
/* 3F8 */ (PfnEntityUpdate)EntityIntenseExplosion,
|
||||
/* 3FC */ (PfnEntityUpdate)EntitySoulStealOrb,
|
||||
/* 400 */ (PfnEntityUpdate)EntityRoomForeground,
|
||||
/* 404 */ (PfnEntityUpdate)EntityStageNamePopup,
|
||||
/* 408 */ (PfnEntityUpdate)EntityEquipItemDrop,
|
||||
/* 40C */ (PfnEntityUpdate)EntityRelicOrb,
|
||||
/* 410 */ (PfnEntityUpdate)EntityHeartDrop,
|
||||
/* 414 */ (PfnEntityUpdate)EntityEnemyBlood,
|
||||
/* 418 */ (PfnEntityUpdate)EntityMessageBox,
|
||||
/* 41C */ (PfnEntityUpdate)EntityDummy,
|
||||
/* 420 */ (PfnEntityUpdate)EntityDummy,
|
||||
/* 424 */ (PfnEntityUpdate)func_80186FD0, // unused
|
||||
/* 428 */ (PfnEntityUpdate)func_801870B0, // unused? looks debugging stuff
|
||||
/* 42C */ (PfnEntityUpdate)EntityUnkId13,
|
||||
/* 430 */ (PfnEntityUpdate)EntityUnkId14,
|
||||
/* 434 */ (PfnEntityUpdate)EntityUnkId15,
|
||||
/* 438 */ (PfnEntityUpdate)EntityWarpRoom,
|
||||
/* 43C */ (PfnEntityUpdate)EntityWarpSmallRocks,
|
||||
};
|
||||
|
||||
// *** Group here all the Entity Init ***
|
||||
u16 g_eBreakableInit[] = {
|
||||
0x8001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
};
|
||||
u16 g_InitializeData0[] = {
|
||||
0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
|
||||
};
|
||||
u16 g_InitializeEntityData0[] = {
|
||||
0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000,
|
||||
};
|
||||
u16 g_EInitGeneric[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000,
|
||||
};
|
||||
u16 g_InitDataEnt13[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000,
|
||||
};
|
||||
u16 D_80180488[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000,
|
||||
};
|
||||
u16 g_eInitGeneric2[] = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
};
|
||||
u16 g_eDamageDisplayInit[] = {
|
||||
/**/ 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
/**/ 0x8001, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000,
|
||||
/**/ 0x800B, 0x0001, 0x0048, 0x021A, 0x0061, 0x0000,
|
||||
};
|
||||
u16 D_801804C4[] = {
|
||||
0x8001, 0x0000, 0x0000, 0x0000, 0x0005, 0x0000,
|
||||
};
|
||||
// ******
|
||||
|
||||
static u32 D_801804D0[] = {0x00FF0140};
|
||||
static u32 D_801804D4[] = {0x26022502, 0x26022702, 0x00000000};
|
||||
ObjInit2 D_801804E0[] = {
|
||||
{0x0006, 0x01FA, 0x0000, 0x0000, 0x00, 0x10, 0x00000000, D_801804D0},
|
||||
{0x8001, 0x00C0, 0x0000, 0x0000, 0x03, 0x30, 0x00000000, D_801804D4},
|
||||
};
|
||||
|
||||
// Owned by EntityRedDoor to animate the tiles behind the door itself.
|
||||
// There is a loop in EntityRedDoor that forces to write those tiles
|
||||
// at every frame based on the door state to create the animation.
|
||||
u16 g_eRedDoorTiles[2][8] = {
|
||||
{0x1D, 0x25, 0x75, 0x7D, 0xC6, 0xC7, 0xC8, 0xC9},
|
||||
{0x1F, 0x27, 0x77, 0x7F, 0xCA, 0xCB, 0xCC, 0xCD},
|
||||
};
|
@ -10,59 +10,30 @@ void InitRoomEntities(s32 objLayoutId);
|
||||
void func_801916C4(u16);
|
||||
void BottomCornerText(u8*, u8);
|
||||
|
||||
extern RoomHeader g_Rooms[];
|
||||
extern s16** g_SpriteBanks[];
|
||||
extern MyRoomDef rooms_layers[];
|
||||
|
||||
extern RoomHeader OVL_EXPORT(rooms)[];
|
||||
static signed short* spriteBanks[];
|
||||
extern void* g_Cluts[];
|
||||
extern RoomDef g_TileLayers[];
|
||||
extern void* OVL_EXPORT(g_EntityGfxs)[];
|
||||
void UpdateStageEntities(void);
|
||||
|
||||
Overlay g_StageOverlay = {
|
||||
/* 0x00 */ Update,
|
||||
/* 0x04 */ HitDetection,
|
||||
/* 0x08 */ UpdateRoomPosition,
|
||||
/* 0x0C */ InitRoomEntities,
|
||||
/* 0x10 */ g_Rooms,
|
||||
/* 0x14 */ g_SpriteBanks,
|
||||
/* 0x18 */ g_Cluts,
|
||||
/* 0x1C */ NULL,
|
||||
/* 0x20 */ g_TileLayers,
|
||||
/* 0x24 */ OVL_EXPORT(g_EntityGfxs),
|
||||
/* 0x28 */ UpdateStageEntities,
|
||||
/* 0x2C */ 0x00000000,
|
||||
/* 0x30 */ 0x00000000,
|
||||
/* 0x34 */ 0x00000000,
|
||||
/* 0x38 */ 0x00000000,
|
||||
/* 0x3C */ 0x00000000,
|
||||
static Overlay OVL_EXPORT(Overlay) = {
|
||||
.Update = Update,
|
||||
.HitDetection = HitDetection,
|
||||
.UpdateRoomPosition = UpdateRoomPosition,
|
||||
.InitRoomEntities = InitRoomEntities,
|
||||
.rooms = OVL_EXPORT(rooms),
|
||||
.spriteBanks = spriteBanks,
|
||||
.cluts = g_Cluts,
|
||||
.objLayoutHorizontal = NULL,
|
||||
.tileLayers = rooms_layers,
|
||||
.gfxBanks = OVL_EXPORT(g_EntityGfxs),
|
||||
.UpdateStageEntities = UpdateStageEntities,
|
||||
};
|
||||
|
||||
extern SpriteParts* D_80186D88[];
|
||||
s16** g_SpriteBanks[] = {
|
||||
/* 0x040 */ 0x00000000,
|
||||
/* 0x044 */ D_80186D88,
|
||||
/* 0x048 */ 0x00000000,
|
||||
/* 0x04C */ 0x00000000,
|
||||
/* 0x050 */ 0x00000000,
|
||||
/* 0x054 */ 0x00000000,
|
||||
/* 0x058 */ 0x00000000,
|
||||
/* 0x05C */ 0x00000000,
|
||||
/* 0x060 */ 0x00000000,
|
||||
/* 0x064 */ 0x00000000,
|
||||
/* 0x068 */ 0x00000000,
|
||||
/* 0x06C */ 0x00000000,
|
||||
/* 0x070 */ 0x00000000,
|
||||
/* 0x074 */ 0x00000000,
|
||||
/* 0x078 */ 0x00000000,
|
||||
/* 0x07C */ 0x00000000,
|
||||
/* 0x080 */ 0x00000000,
|
||||
/* 0x084 */ 0x00000000,
|
||||
/* 0x088 */ 0x00000000,
|
||||
/* 0x08C */ 0x00000000,
|
||||
/* 0x090 */ 0x00000000,
|
||||
/* 0x094 */ 0x00000000,
|
||||
/* 0x098 */ 0x00000000,
|
||||
/* 0x09C */ 0x00000000,
|
||||
};
|
||||
#include "sprite_banks.h"
|
||||
|
||||
extern u16 D_80181D08[16];
|
||||
void* D_801800A0[] = {
|
||||
@ -74,34 +45,4 @@ void* g_Cluts[] = {
|
||||
/* 0x0B4 */ D_801800A0,
|
||||
};
|
||||
|
||||
// TileDefinition D_80182D68[];
|
||||
// TileDefinition D_80186D78;
|
||||
// u16 D_80181D68[];
|
||||
// u16 D_80181F68[];
|
||||
// u16 D_80182168[];
|
||||
// u16 D_80182368[];
|
||||
// u16 D_80182568[];
|
||||
// u16 D_80182768[];
|
||||
// LayerDef D_801800B8 = {0, 0, 0, 0, 0, 0};
|
||||
// LayerDef D_801800C8 = {D_80181D68, &D_80186D78, 0x01328328, 0x60, 3, 0};
|
||||
// LayerDef D_801800D8 = {D_80181F68, &D_80186D78, 0x01565565, 0x60, 3, 0};
|
||||
// LayerDef D_801800E8 = {D_80182168, &D_80186D78, 0x0147B47B, 0x60, 3, 0};
|
||||
// LayerDef D_801800F8 = {D_80182368, &D_80186D78, 0x0198F98F, 0x60, 3, 0};
|
||||
// LayerDef D_80180108 = {D_80182568, &D_80186D78, 0x01B23B23, 0x60, 3, 0};
|
||||
// LayerDef D_80180118 = {D_80182768, &D_80182D68, 0x40B22B22, 0x20, 3, 2};
|
||||
// LayerDef D_80180128 = {D_80182768, &D_80182D68, 0x41990990, 0x1F, 3, 2};
|
||||
// LayerDef D_80180138 = {D_80182768, &D_80182D68, 0x40564564, 0x1E, 3, 2};
|
||||
// LayerDef D_80180148 = {D_80182768, &D_80182D68, 0x4147C47C, 0x1D, 3, 2};
|
||||
// LayerDef D_80180158 = {D_80182768, &D_80182D68, 0x40327327, 0x1C, 3, 2};
|
||||
// RoomDef g_TileLayers[] = {
|
||||
// /* 0x168 */ {&D_801800C8, &D_801800B8},
|
||||
// /* 0x170 */ {&D_801800D8, &D_801800B8},
|
||||
// /* 0x178 */ {&D_801800E8, &D_801800B8},
|
||||
// /* 0x180 */ {&D_801800F8, &D_801800B8},
|
||||
// /* 0x188 */ {&D_80180108, &D_801800B8},
|
||||
// /* 0x190 */ {&D_80180118, &D_801800B8},
|
||||
// /* 0x198 */ {&D_80180128, &D_801800B8},
|
||||
// /* 0x1A0 */ {&D_80180138, &D_801800B8},
|
||||
// /* 0x1A8 */ {&D_80180148, &D_801800B8},
|
||||
// /* 0x1B0 */ {&D_80180158, &D_801800B8},
|
||||
// };
|
||||
#include "layers.h"
|
||||
|
10
src/st/wrp/tile_data.c
Normal file
10
src/st/wrp/tile_data.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stage.h>
|
||||
|
||||
#include "tilemap_01D68.h"
|
||||
#include "tilemap_01F68.h"
|
||||
#include "tilemap_02168.h"
|
||||
#include "tilemap_02368.h"
|
||||
#include "tilemap_02568.h"
|
||||
#include "tilemap_02768.h"
|
||||
#include "tiledef_02D68.h"
|
||||
#include "tiledef_06D78.h"
|
533
tools/sotn-assets/build.go
Normal file
533
tools/sotn-assets/build.go
Normal file
@ -0,0 +1,533 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func makeSymbolFromFileName(fileName string) string {
|
||||
return strings.Split(path.Base(fileName), ".")[0]
|
||||
}
|
||||
|
||||
func writeStaticU8(w io.Writer, fileName string, symbol string) error {
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("// clang-format off\n")
|
||||
sb.WriteString(fmt.Sprintf("static unsigned char %s[] = {\n", symbol))
|
||||
for i := 0; i < len(data); i++ {
|
||||
sb.WriteString(fmt.Sprintf("0x%02X,", data[i]))
|
||||
if (i & 15) == 15 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
|
||||
_, err = w.Write([]byte(sb.String()))
|
||||
return err
|
||||
}
|
||||
|
||||
func buildGenericU16(fileName string, symbol string, outputDir string) error {
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("// clang-format off\n")
|
||||
sb.WriteString(fmt.Sprintf("unsigned short %s[] = {\n", symbol))
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
sb.WriteString(fmt.Sprintf("0x%02X%02X,", data[i+1], data[i]))
|
||||
if (i & 31) == 30 {
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
|
||||
return os.WriteFile(path.Join(outputDir, fmt.Sprintf("%s.h", symbol)), []byte(sb.String()), 0644)
|
||||
}
|
||||
|
||||
func buildRooms(fileName string, outputDir string) error {
|
||||
ovlName := path.Base(outputDir)
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rooms []room
|
||||
if err := json.Unmarshal(data, &rooms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content := strings.Builder{}
|
||||
content.WriteString("// clang-format off\n")
|
||||
content.WriteString(fmt.Sprintf("unsigned char %s_rooms[] = {\n", strings.ToUpper(ovlName)))
|
||||
for _, room := range rooms {
|
||||
s := fmt.Sprintf(" %d, %d, %d, %d, %d, %d, %d, %d,\n",
|
||||
room.Left, room.Top, room.Right, room.Bottom,
|
||||
room.LayerID, room.TileDefID, room.EntityGfxID, room.EntityLayoutID)
|
||||
content.WriteString(s)
|
||||
}
|
||||
content.WriteString(" 0x40\n};\n")
|
||||
return os.WriteFile(path.Join(outputDir, "rooms.c"), []byte(content.String()), 0644)
|
||||
}
|
||||
|
||||
func buildTiledefs(fileName string, symbol string, outputDir string) error {
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tiledef tileDefPaths
|
||||
if err := json.Unmarshal(data, &tiledef); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(path.Join(outputDir, fmt.Sprintf("%s.h", symbol)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
tilesSymbol := makeSymbolFromFileName(tiledef.Tiles)
|
||||
tilesFileName := path.Join(path.Dir(fileName), tiledef.Tiles)
|
||||
if err := writeStaticU8(f, tilesFileName, tilesSymbol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pagesSymbol := makeSymbolFromFileName(tiledef.Pages)
|
||||
pagesFileName := path.Join(path.Dir(fileName), tiledef.Pages)
|
||||
if err := writeStaticU8(f, pagesFileName, pagesSymbol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clutsSymbol := makeSymbolFromFileName(tiledef.Cluts)
|
||||
clutsFileName := path.Join(path.Dir(fileName), tiledef.Cluts)
|
||||
if err := writeStaticU8(f, clutsFileName, clutsSymbol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
colsSymbol := makeSymbolFromFileName(tiledef.Collisions)
|
||||
colsFileName := path.Join(path.Dir(fileName), tiledef.Collisions)
|
||||
if err := writeStaticU8(f, colsFileName, colsSymbol); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = f.WriteString("// clang-format off\n")
|
||||
_, _ = f.WriteString(fmt.Sprintf("TileDefinition %s[] = {\n", symbol))
|
||||
_, _ = f.WriteString(fmt.Sprintf(" %s,\n", tilesSymbol))
|
||||
_, _ = f.WriteString(fmt.Sprintf(" %s,\n", pagesSymbol))
|
||||
_, _ = f.WriteString(fmt.Sprintf(" %s,\n", clutsSymbol))
|
||||
_, _ = f.WriteString(fmt.Sprintf(" %s,\n", colsSymbol))
|
||||
_, _ = f.WriteString("};\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildLayers(inputDir string, fileName string, outputDir string) error {
|
||||
getHash := func(l layerUnpacked) string {
|
||||
return fmt.Sprintf("%s-%s-%d-%d-%d-%d", l.Data, l.Tiledef, l.Left, l.Top, l.Right, l.Bottom)
|
||||
}
|
||||
pack := func(l layerUnpacked) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"data": makeSymbolFromFileName(l.Data),
|
||||
"tiledef": makeSymbolFromFileName(l.Tiledef),
|
||||
"params": l.Left | (l.Top << 6) | (l.Right << 12) | (l.Bottom << 18) | (l.ScrollMode << 24) |
|
||||
(btoi(l.IsSaveRoom) << 29) | (btoi(l.IsLoadingRoom) << 30) | (btoi(l.UnusedFlag) << 31),
|
||||
"zPriority": l.ZPriority,
|
||||
"unkE": l.UnkE,
|
||||
"unkF": l.UnkF,
|
||||
}
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var roomsLayers []map[string]*layerUnpacked
|
||||
if err := json.Unmarshal(data, &roomsLayers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tilemaps := map[string]struct{}{}
|
||||
tiledefs := map[string]struct{}{}
|
||||
for _, room := range roomsLayers {
|
||||
// the split on '.' is to remove the extension
|
||||
if layer, found := room["fg"]; found {
|
||||
tilemaps[layer.Data] = struct{}{}
|
||||
tiledefs[layer.Tiledef] = struct{}{}
|
||||
}
|
||||
if layer, found := room["bg"]; found {
|
||||
tilemaps[layer.Data] = struct{}{}
|
||||
tiledefs[layer.Tiledef] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// use unused tiledefs
|
||||
files, err := os.ReadDir(inputDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasPrefix(file.Name(), "tiledef_") && strings.HasSuffix(file.Name(), ".json") {
|
||||
tiledefs[file.Name()] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
eg := errgroup.Group{}
|
||||
for name := range tilemaps {
|
||||
fullPath := path.Join(path.Dir(fileName), name)
|
||||
symbol := makeSymbolFromFileName(name)
|
||||
eg.Go(func() error {
|
||||
return buildGenericU16(fullPath, symbol, outputDir)
|
||||
})
|
||||
}
|
||||
for name := range tiledefs {
|
||||
fullPath := path.Join(path.Dir(fileName), name)
|
||||
symbol := makeSymbolFromFileName(name)
|
||||
eg.Go(func() error {
|
||||
return buildTiledefs(fullPath, symbol, outputDir)
|
||||
})
|
||||
}
|
||||
if err := eg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
layers := []map[string]interface{}{} // first layer is always empty
|
||||
layers = append(layers, map[string]interface{}{})
|
||||
roomLayersId := make([]int, len(roomsLayers)*2)
|
||||
pool := map[string]int{}
|
||||
pool[""] = 0
|
||||
for _, rl := range roomsLayers {
|
||||
if l, fgFound := rl["fg"]; fgFound {
|
||||
hash := getHash(*l)
|
||||
if index, found := pool[hash]; !found {
|
||||
pool[hash] = len(layers)
|
||||
roomLayersId = append(roomLayersId, len(layers))
|
||||
layers = append(layers, pack(*l))
|
||||
} else {
|
||||
roomLayersId = append(roomLayersId, index)
|
||||
}
|
||||
} else {
|
||||
roomLayersId = append(roomLayersId, 0)
|
||||
}
|
||||
if l, bgFound := rl["bg"]; bgFound {
|
||||
hash := getHash(*l)
|
||||
if index, found := pool[hash]; !found {
|
||||
pool[hash] = len(layers)
|
||||
roomLayersId = append(roomLayersId, len(layers))
|
||||
layers = append(layers, pack(*l))
|
||||
} else {
|
||||
roomLayersId = append(roomLayersId, index)
|
||||
}
|
||||
} else {
|
||||
roomLayersId = append(roomLayersId, 0)
|
||||
}
|
||||
}
|
||||
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("// clang-format off\n")
|
||||
for name := range tilemaps {
|
||||
symbol := makeSymbolFromFileName(name)
|
||||
sb.WriteString(fmt.Sprintf("extern unsigned short %s[];\n", symbol))
|
||||
}
|
||||
for name := range tiledefs {
|
||||
symbol := makeSymbolFromFileName(name)
|
||||
sb.WriteString(fmt.Sprintf("extern TileDefinition %s[];\n", symbol))
|
||||
}
|
||||
|
||||
sb.WriteString("static MyLayer layers[] = {\n")
|
||||
sb.WriteString(" {},\n")
|
||||
for _, l := range layers[1:] {
|
||||
sb.WriteString(fmt.Sprintf(" { %s, %s, 0x%08X, 0x%02X, %d, %d },\n",
|
||||
makeSymbolFromFileName(l["data"].(string)),
|
||||
makeSymbolFromFileName(l["tiledef"].(string)),
|
||||
l["params"],
|
||||
l["zPriority"],
|
||||
l["unkE"],
|
||||
l["unkF"],
|
||||
))
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
|
||||
sb.WriteString("static MyRoomDef rooms_layers[] = {\n")
|
||||
for _, rl := range roomsLayers {
|
||||
if l, found := rl["fg"]; found {
|
||||
sb.WriteString(fmt.Sprintf(" { &layers[%d], ", pool[getHash(*l)]))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf(" { &layers[0], "))
|
||||
}
|
||||
if l, found := rl["bg"]; found {
|
||||
sb.WriteString(fmt.Sprintf("&layers[%d] },\n", pool[getHash(*l)]))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("&layers[0] },\n"))
|
||||
}
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
return os.WriteFile(path.Join(outputDir, "layers.h"), []byte(sb.String()), 0644)
|
||||
}
|
||||
|
||||
func buildSpriteGroup(sb *strings.Builder, sprites []*[]sprite, mainSymbol string, r *rand.Rand) {
|
||||
symbols := []string{}
|
||||
for _, spriteGroup := range sprites {
|
||||
if spriteGroup != nil {
|
||||
symbol := fmt.Sprintf("spriteGroup_%08X", r.Int31())
|
||||
size := len(*spriteGroup)*11 + 1
|
||||
if (len(*spriteGroup) & 1) == 1 { // perform alignment at the end
|
||||
size += 2
|
||||
} else {
|
||||
size += 1
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("static signed short %s[%d];\n", symbol, size))
|
||||
symbols = append(symbols, symbol)
|
||||
} else {
|
||||
symbols = append(symbols, "")
|
||||
}
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("signed short* %s[] = {\n", mainSymbol))
|
||||
for _, symbol := range symbols {
|
||||
if len(symbol) > 0 {
|
||||
sb.WriteString(fmt.Sprintf(" %s,\n", symbol))
|
||||
} else {
|
||||
sb.WriteString(" 0,\n")
|
||||
}
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
for i, spriteGroup := range sprites {
|
||||
if spriteGroup == nil {
|
||||
continue
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("static signed short %s[] = {\n", symbols[i]))
|
||||
sb.WriteString(fmt.Sprintf(" %d,\n", len(*spriteGroup)))
|
||||
for _, sprite := range *spriteGroup {
|
||||
sb.WriteString(fmt.Sprintf(" %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d,\n",
|
||||
sprite.Flags, sprite.X, sprite.Y, sprite.Width, sprite.Height,
|
||||
sprite.Clut, sprite.Tileset, sprite.Left, sprite.Top, sprite.Right, sprite.Bottom))
|
||||
}
|
||||
if (len(*spriteGroup) & 1) == 1 { // perform alignment at the end
|
||||
sb.WriteString(" 0, 0\n")
|
||||
} else {
|
||||
sb.WriteString(" 0\n")
|
||||
}
|
||||
sb.WriteString("};\n")
|
||||
}
|
||||
}
|
||||
|
||||
func buildSprites(fileName string, outputDir string) error {
|
||||
ovlName := path.Base(outputDir)
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var spritesBanks spriteDefs
|
||||
if err := json.Unmarshal(data, &spritesBanks); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := rand.New(rand.NewSource(int64(len(data))))
|
||||
sbHeader := strings.Builder{}
|
||||
sbHeader.WriteString("// clang-format off\n")
|
||||
sbData := strings.Builder{}
|
||||
sbData.WriteString("// clang-format off\n")
|
||||
symbols := []string{}
|
||||
for i, sprites := range spritesBanks.Banks {
|
||||
if len(sprites) == 0 {
|
||||
symbols = append(symbols, "")
|
||||
continue
|
||||
}
|
||||
symbol := fmt.Sprintf("sprites_%s_%d", ovlName, i)
|
||||
sbHeader.WriteString(fmt.Sprintf("extern signed short* %s;\n", symbol))
|
||||
buildSpriteGroup(&sbData, sprites, symbol, r)
|
||||
symbols = append(symbols, symbol)
|
||||
}
|
||||
|
||||
sbHeader.WriteString("static signed short* spriteBanks[] = {\n")
|
||||
for _, index := range spritesBanks.Indices {
|
||||
if index >= 0 {
|
||||
sbHeader.WriteString(fmt.Sprintf(" &%s,\n", symbols[index]))
|
||||
} else {
|
||||
sbHeader.WriteString(fmt.Sprintf(" 0,\n"))
|
||||
}
|
||||
}
|
||||
sbHeader.WriteString("};\n")
|
||||
if err := os.WriteFile(path.Join(outputDir, "sprites.c"), []byte(sbData.String()), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path.Join(outputDir, "sprite_banks.h"), []byte(sbHeader.String()), 0644)
|
||||
}
|
||||
|
||||
func buildEntityLayouts(fileName string, outputDir string) error {
|
||||
writeLayoutEntries := func(sb *strings.Builder, banks [][]layoutEntry, align4 bool) error {
|
||||
nWritten := 0
|
||||
for i, entries := range banks {
|
||||
// do a sanity check on the entries as we do not want to build something that will cause the game to crash
|
||||
if entries[0].X != -2 || entries[0].Y != -2 {
|
||||
return fmt.Errorf("layout entity bank %d needs to have a X:-2 and Y:-2 entry at the beginning", i)
|
||||
}
|
||||
lastEntry := entries[len(entries)-1]
|
||||
if lastEntry.X != -1 || lastEntry.Y != -1 {
|
||||
return fmt.Errorf("layout entity bank %d needs to have a X:-1 and Y:-1 entry at the end", i)
|
||||
}
|
||||
|
||||
for _, e := range entries {
|
||||
sb.WriteString(fmt.Sprintf(" 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X,\n",
|
||||
uint16(e.X), uint16(e.Y), int(e.ID)|(int(e.Flags)<<8), int(e.Slot)|(int(e.SpawnID)<<8), e.Params))
|
||||
}
|
||||
nWritten += len(entries)
|
||||
}
|
||||
if align4 && nWritten%2 != 0 {
|
||||
sb.WriteString(" 0, // padding\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
makeSortedBanks := func(banks [][]layoutEntry, sortByX bool) [][]layoutEntry {
|
||||
var toSort []layoutEntry
|
||||
var less func(i, j int) bool
|
||||
if sortByX {
|
||||
less = func(i, j int) bool {
|
||||
return toSort[i].X < toSort[j].X
|
||||
}
|
||||
} else {
|
||||
less = func(i, j int) bool {
|
||||
if toSort[i].Y < toSort[j].Y {
|
||||
return true
|
||||
}
|
||||
if toSort[i].Y > toSort[j].Y {
|
||||
return false
|
||||
}
|
||||
if toSort[i].YOrder != nil && toSort[j].YOrder != nil {
|
||||
return *toSort[i].YOrder < *toSort[j].YOrder
|
||||
}
|
||||
return i < j
|
||||
}
|
||||
}
|
||||
sorting := make([][]layoutEntry, len(banks))
|
||||
for i, entries := range banks {
|
||||
sorting[i] = make([]layoutEntry, len(entries)-2)
|
||||
if len(sorting[i]) > 0 { // do not sort if the list is empty
|
||||
copy(sorting[i], entries[1:len(entries)-1]) // do not sort the -2 and -1 entries
|
||||
toSort = sorting[i]
|
||||
sort.SliceStable(toSort, less)
|
||||
}
|
||||
|
||||
// put back the -2 and -1
|
||||
sorting[i] = append([]layoutEntry{entries[0]}, sorting[i]...)
|
||||
sorting[i] = append(sorting[i], entries[len(entries)-1])
|
||||
}
|
||||
return sorting
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var el layouts
|
||||
if err := json.Unmarshal(data, &el); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := fnv.New32()
|
||||
h.Write([]byte(outputDir))
|
||||
symbolVariant := strconv.FormatUint(uint64(h.Sum32()), 16)
|
||||
symbolName := fmt.Sprintf("entity_layout_%s", symbolVariant)
|
||||
offsets := make([]int, len(el.Entities))
|
||||
offsetCur := 0
|
||||
for i := 0; i < len(el.Entities); i++ {
|
||||
offsets[i] = offsetCur
|
||||
offsetCur += len(el.Entities[i]) * 5
|
||||
}
|
||||
|
||||
sbHeader := strings.Builder{}
|
||||
sbHeader.WriteString("#include <stage.h>\n\n")
|
||||
sbHeader.WriteString("// clang-format off\n")
|
||||
sbHeader.WriteString(fmt.Sprintf("extern u16 %s_x[];\n", symbolName))
|
||||
sbHeader.WriteString("LayoutEntity* g_pStObjLayoutHorizontal[] = {\n")
|
||||
for _, i := range el.Indices {
|
||||
sbHeader.WriteString(fmt.Sprintf(" (LayoutEntity*)&%s_x[%d],\n", symbolName, offsets[i]))
|
||||
}
|
||||
sbHeader.WriteString(fmt.Sprintf("};\n"))
|
||||
sbHeader.WriteString(fmt.Sprintf("extern u16 %s_y[];\n", symbolName))
|
||||
sbHeader.WriteString("LayoutEntity* g_pStObjLayoutVertical[] = {\n")
|
||||
for _, i := range el.Indices {
|
||||
sbHeader.WriteString(fmt.Sprintf(" (LayoutEntity*)&%s_y[%d],\n", symbolName, offsets[i]))
|
||||
}
|
||||
sbHeader.WriteString(fmt.Sprintf("};\n"))
|
||||
|
||||
sbData := strings.Builder{}
|
||||
sbData.WriteString("// clang-format off\n")
|
||||
sbData.WriteString(fmt.Sprintf("unsigned short %s_x[] = {\n", symbolName))
|
||||
if err := writeLayoutEntries(&sbData, makeSortedBanks(el.Entities, true), false); err != nil {
|
||||
return fmt.Errorf("unable to build X entity layout: %w", err)
|
||||
}
|
||||
sbData.WriteString(fmt.Sprintf("};\n"))
|
||||
sbData.WriteString(fmt.Sprintf("unsigned short %s_y[] = {\n", symbolName))
|
||||
if err := writeLayoutEntries(&sbData, makeSortedBanks(el.Entities, false), true); err != nil {
|
||||
return fmt.Errorf("unable to build Y entity layout: %w", err)
|
||||
}
|
||||
sbData.WriteString(fmt.Sprintf("};\n"))
|
||||
|
||||
if err := os.WriteFile(path.Join(outputDir, "e_layout.c"), []byte(sbData.String()), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path.Join(outputDir, "e_laydef.c"), []byte(sbHeader.String()), 0644)
|
||||
}
|
||||
|
||||
func buildAll(inputDir string, outputDir string) error {
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eg := errgroup.Group{}
|
||||
eg.Go(func() error {
|
||||
if err := buildRooms(path.Join(inputDir, "rooms.json"), outputDir); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
if err := buildLayers(inputDir, path.Join(inputDir, "layers.json"), outputDir); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
if err := buildSprites(path.Join(inputDir, "sprites.json"), outputDir); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
if err := buildEntityLayouts(path.Join(inputDir, "entity_layouts.json"), outputDir); err != nil {
|
||||
if !errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return eg.Wait()
|
||||
}
|
79
tools/sotn-assets/data_range.go
Normal file
79
tools/sotn-assets/data_range.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type dataRange struct {
|
||||
begin PsxOffset
|
||||
end PsxOffset
|
||||
}
|
||||
|
||||
func (r dataRange) Format(f fmt.State, c rune) {
|
||||
f.Write([]byte(fmt.Sprintf("(%s, %s)", r.begin, r.end)))
|
||||
}
|
||||
|
||||
func (r dataRange) empty() bool {
|
||||
return r.begin == RamNull && r.end == RamNull
|
||||
}
|
||||
|
||||
func mergeDataRanges(ranges []dataRange) dataRange {
|
||||
if len(ranges) == 0 {
|
||||
err := fmt.Errorf("no datarange, bug?!")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sort.Slice(ranges, func(i, j int) bool {
|
||||
return ranges[i].begin < ranges[j].begin
|
||||
})
|
||||
|
||||
// performs a sanity check before merging everything
|
||||
for i := 0; i < len(ranges)-1; i++ {
|
||||
if ranges[i].end != ranges[i+1].begin {
|
||||
var err error
|
||||
if ranges[i].end < ranges[i+1].begin {
|
||||
err = fmt.Errorf("gap between data detected: %s != %s", ranges[i].end, ranges[i+1].begin)
|
||||
} else {
|
||||
err = fmt.Errorf("overlap between data detected: %s != %s", ranges[i].end, ranges[i+1].begin)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return dataRange{
|
||||
begin: ranges[0].begin,
|
||||
end: ranges[len(ranges)-1].end,
|
||||
}
|
||||
}
|
||||
|
||||
func consolidateDataRanges(ranges []dataRange) []dataRange {
|
||||
if len(ranges) == 0 {
|
||||
return []dataRange{}
|
||||
}
|
||||
|
||||
sort.Slice(ranges, func(i, j int) bool {
|
||||
return ranges[i].begin < ranges[j].begin
|
||||
})
|
||||
|
||||
for ranges[0].empty() {
|
||||
ranges = ranges[1:]
|
||||
}
|
||||
|
||||
consolidated := []dataRange{}
|
||||
first := 0
|
||||
for i := 0; i < len(ranges)-1; i++ {
|
||||
if ranges[i].end != ranges[i+1].begin {
|
||||
consolidated = append(consolidated, dataRange{
|
||||
begin: ranges[first].begin,
|
||||
end: ranges[i].end,
|
||||
})
|
||||
first = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
return append(consolidated, dataRange{
|
||||
begin: ranges[first].begin,
|
||||
end: ranges[len(ranges)-1].end,
|
||||
})
|
||||
}
|
5
tools/sotn-assets/go.mod
Normal file
5
tools/sotn-assets/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/xeeynamo/sotn-decomp/tools/sotn-assets
|
||||
|
||||
go 1.21
|
||||
|
||||
require golang.org/x/sync v0.7.0 // indirect
|
2
tools/sotn-assets/go.sum
Normal file
2
tools/sotn-assets/go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
112
tools/sotn-assets/graphics.go
Normal file
112
tools/sotn-assets/graphics.go
Normal file
@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os"
|
||||
)
|
||||
|
||||
type gfxKind uint16
|
||||
|
||||
const (
|
||||
gfxBankNone = gfxKind(iota)
|
||||
gfxBank4bpp
|
||||
gfxBank8bpp
|
||||
gfxBank16bpp
|
||||
gfxBankCompressed
|
||||
)
|
||||
|
||||
type gfxEntry struct {
|
||||
X uint16
|
||||
Y uint16
|
||||
Width uint16
|
||||
Height uint16
|
||||
Data PsxOffset
|
||||
}
|
||||
|
||||
type gfxBlock struct {
|
||||
kind gfxKind
|
||||
flags uint16
|
||||
entries []gfxEntry
|
||||
}
|
||||
|
||||
type gfx struct {
|
||||
blocks []gfxBlock
|
||||
indices []int
|
||||
}
|
||||
|
||||
func readGraphics(file *os.File, off PsxOffset) (gfx, dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
|
||||
// all the offsets are before the array, so it is easy to find where the offsets array ends
|
||||
blockOffsets := []PsxOffset{}
|
||||
for {
|
||||
var offBank PsxOffset
|
||||
if err := binary.Read(file, binary.LittleEndian, &offBank); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
if offBank >= off {
|
||||
break
|
||||
}
|
||||
blockOffsets = append(blockOffsets, offBank)
|
||||
}
|
||||
|
||||
// the order of each gfxBlock must be preserved
|
||||
pool := map[PsxOffset]int{}
|
||||
pool[RamNull] = -1
|
||||
blocks := []gfxBlock{}
|
||||
ranges := []dataRange{}
|
||||
for _, blockOffset := range sortUniqueOffsets(blockOffsets) {
|
||||
if blockOffset == RamNull { // exception for ST0
|
||||
continue
|
||||
}
|
||||
if err := blockOffset.moveFile(file); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
var block gfxBlock
|
||||
if err := binary.Read(file, binary.LittleEndian, &block.kind); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
if err := binary.Read(file, binary.LittleEndian, &block.flags); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
|
||||
if block.kind == gfxKind(0xFFFF) && block.flags == 0xFFFF { // exception for ST0
|
||||
pool[blockOffset] = len(blocks)
|
||||
blocks = append(blocks, block)
|
||||
ranges = append(ranges, dataRange{
|
||||
begin: blockOffset,
|
||||
end: blockOffset.sum(4),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
for {
|
||||
var entry gfxEntry
|
||||
if err := binary.Read(file, binary.LittleEndian, &entry); err != nil {
|
||||
return gfx{}, dataRange{}, err
|
||||
}
|
||||
if entry.X == 0xFFFF && entry.Y == 0xFFFF {
|
||||
break
|
||||
}
|
||||
block.entries = append(block.entries, entry)
|
||||
}
|
||||
pool[blockOffset] = len(blocks)
|
||||
blocks = append(blocks, block)
|
||||
ranges = append(ranges, dataRange{
|
||||
begin: blockOffset,
|
||||
end: blockOffset.sum(4 + len(block.entries)*12 + 4),
|
||||
})
|
||||
}
|
||||
|
||||
var g gfx
|
||||
for _, blockOffset := range blockOffsets {
|
||||
g.indices = append(g.indices, pool[blockOffset])
|
||||
}
|
||||
|
||||
return g, mergeDataRanges(append(ranges, dataRange{
|
||||
begin: off,
|
||||
end: off.sum(len(blockOffsets) * 4),
|
||||
})), nil
|
||||
}
|
134
tools/sotn-assets/layer.go
Normal file
134
tools/sotn-assets/layer.go
Normal file
@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type layer struct {
|
||||
Data PsxOffset
|
||||
Tiledef PsxOffset
|
||||
PackedInfo uint32
|
||||
ZPriority uint16
|
||||
UnkE uint8
|
||||
UnkF uint8
|
||||
}
|
||||
|
||||
type layerUnpacked struct {
|
||||
Data string `json:"data"`
|
||||
Tiledef string `json:"tiledef"`
|
||||
Left int `json:"left"`
|
||||
Top int `json:"top"`
|
||||
Right int `json:"right"`
|
||||
Bottom int `json:"bottom"`
|
||||
ScrollMode int `json:"scrollMode"`
|
||||
IsSaveRoom bool `json:"isSaveRoom"`
|
||||
IsLoadingRoom bool `json:"isLoadingRoom"`
|
||||
UnusedFlag bool `json:"unusedFlag"`
|
||||
ZPriority int `json:"zPriority"`
|
||||
UnkE int `json:"unkE"`
|
||||
UnkF int `json:"unkF"`
|
||||
}
|
||||
|
||||
type roomLayers struct {
|
||||
fg *layer
|
||||
bg *layer
|
||||
}
|
||||
|
||||
func (l *layer) tilemapFileSize() int {
|
||||
sx := int((l.PackedInfo >> 0) & 0x3F)
|
||||
sy := int((l.PackedInfo >> 6) & 0x3F)
|
||||
ex := int((l.PackedInfo >> 12) & 0x3F)
|
||||
ey := int((l.PackedInfo >> 18) & 0x3F)
|
||||
w := ex - sx + 1
|
||||
h := ey - sy + 1
|
||||
return w * h * 512
|
||||
}
|
||||
|
||||
func (l *layer) unpack() layerUnpacked {
|
||||
return layerUnpacked{
|
||||
Data: getTilemapFileName(l.Data),
|
||||
Tiledef: getTiledefFileName(l.Tiledef),
|
||||
Left: int((l.PackedInfo >> 0) & 0x3F),
|
||||
Top: int((l.PackedInfo >> 6) & 0x3F),
|
||||
Right: int((l.PackedInfo >> 12) & 0x3F),
|
||||
Bottom: int((l.PackedInfo >> 18) & 0x3F),
|
||||
ScrollMode: int((l.PackedInfo >> 24) & 0x1F),
|
||||
IsSaveRoom: int((l.PackedInfo>>24)&0x20) != 0,
|
||||
IsLoadingRoom: int((l.PackedInfo>>24)&0x40) != 0,
|
||||
UnusedFlag: int((l.PackedInfo>>24)&0x80) != 0,
|
||||
ZPriority: int(l.ZPriority),
|
||||
UnkE: int(l.UnkE),
|
||||
UnkF: int(l.UnkF),
|
||||
}
|
||||
}
|
||||
|
||||
func (r roomLayers) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{}
|
||||
if r.fg != nil {
|
||||
m["fg"] = r.fg.unpack()
|
||||
}
|
||||
if r.bg != nil {
|
||||
m["bg"] = r.bg.unpack()
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func readLayers(file *os.File, off PsxOffset) ([]roomLayers, dataRange, error) {
|
||||
if off == 0 {
|
||||
return nil, dataRange{}, nil
|
||||
}
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
|
||||
// when the data starts to no longer makes sense, we can assume we reached the end of the array
|
||||
layerOffsets := []PsxOffset{}
|
||||
layersOff := make([]PsxOffset, 2)
|
||||
for {
|
||||
if err := binary.Read(file, binary.LittleEndian, layersOff); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
if layersOff[0] <= RamStageBegin || layersOff[0] >= off ||
|
||||
layersOff[1] <= RamStageBegin || layersOff[1] >= off {
|
||||
break
|
||||
}
|
||||
layerOffsets = append(layerOffsets, layersOff...)
|
||||
}
|
||||
|
||||
// Creates a map of layers, so we can re-use them when a layer is used by multiple rooms
|
||||
pool := map[PsxOffset]*layer{}
|
||||
pool[PsxOffset(0)] = nil
|
||||
for _, layerOffset := range layerOffsets {
|
||||
if _, exists := pool[layerOffset]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := layerOffset.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
var l layer
|
||||
if err := binary.Read(file, binary.LittleEndian, &l); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
if l.Data != RamNull || l.Tiledef != RamNull || l.PackedInfo != 0 {
|
||||
pool[layerOffset] = &l
|
||||
} else {
|
||||
pool[layerOffset] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// creates the real array with all the layers mapped
|
||||
count := len(layerOffsets) >> 1
|
||||
roomsLayers := make([]roomLayers, count)
|
||||
for i := 0; i < count; i++ {
|
||||
roomsLayers[i].fg = pool[layerOffsets[i*2+0]]
|
||||
roomsLayers[i].bg = pool[layerOffsets[i*2+1]]
|
||||
}
|
||||
return roomsLayers, dataRange{
|
||||
begin: slices.Min(layerOffsets),
|
||||
end: off.sum(count * 8),
|
||||
}, nil
|
||||
}
|
160
tools/sotn-assets/layout.go
Normal file
160
tools/sotn-assets/layout.go
Normal file
@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type layoutEntry struct {
|
||||
X int16 `json:"x"`
|
||||
Y int16 `json:"y"`
|
||||
ID uint8 `json:"id"`
|
||||
Flags uint8 `json:"flags"` // TODO properly de-serialize this
|
||||
Slot uint8 `json:"slot"`
|
||||
SpawnID uint8 `json:"spawnId"`
|
||||
Params uint16 `json:"params"`
|
||||
YOrder *int `json:"yOrder,omitempty"`
|
||||
}
|
||||
|
||||
type layouts struct {
|
||||
Entities [][]layoutEntry `json:"entities"`
|
||||
Indices []int `json:"indices"`
|
||||
}
|
||||
|
||||
func readEntityLayoutEntry(file *os.File) (layoutEntry, error) {
|
||||
bs := make([]byte, 10)
|
||||
if _, err := io.ReadFull(file, bs); err != nil {
|
||||
return layoutEntry{}, err
|
||||
}
|
||||
return layoutEntry{
|
||||
X: int16(binary.LittleEndian.Uint16(bs[0:2])),
|
||||
Y: int16(binary.LittleEndian.Uint16(bs[2:4])),
|
||||
ID: bs[4],
|
||||
Flags: bs[5],
|
||||
Slot: bs[6],
|
||||
SpawnID: bs[7],
|
||||
Params: binary.LittleEndian.Uint16(bs[8:10]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// the Y-ordered entries list has a different order than the X-ordered one. The order cannot consistently get
|
||||
// restored by just sorting entries by Y as usually entries with the same Y results swapped.
|
||||
// This algorithm will fill the optional field YOrder, only useful to restore the original order.
|
||||
func hydrateYOrderFields(x layouts, y layouts) error {
|
||||
if len(x.Indices) != len(y.Indices) {
|
||||
return fmt.Errorf("number of X and Y layout indices do not match")
|
||||
}
|
||||
if len(x.Entities) != len(y.Entities) {
|
||||
return fmt.Errorf("number of X and Y layout entries do not match")
|
||||
}
|
||||
|
||||
populateYOrderField := func(xEntries []layoutEntry, yEntries []layoutEntry) {
|
||||
yIndexMap := make(map[layoutEntry]int, len(yEntries))
|
||||
for i, e := range yEntries {
|
||||
yIndexMap[e] = i
|
||||
}
|
||||
for i := 0; i < len(xEntries); i++ {
|
||||
if yOrder, found := yIndexMap[xEntries[i]]; found {
|
||||
xEntries[i].YOrder = &yOrder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(x.Entities); i++ {
|
||||
xList := x.Entities[i]
|
||||
yList := y.Entities[i]
|
||||
if len(xList) != len(yList) {
|
||||
return fmt.Errorf("number of X and Y entries do not match")
|
||||
}
|
||||
populateYOrderField(xList, yList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readEntityLayout(file *os.File, off PsxOffset, count int, isX bool) (layouts, []dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return layouts{}, nil, err
|
||||
}
|
||||
|
||||
// there are two copies of the layout, one ordered by X and the other one ordered by Y
|
||||
// we will only read the first one, which is ordered by Y
|
||||
blockOffsets := make([]PsxOffset, count)
|
||||
if err := binary.Read(file, binary.LittleEndian, blockOffsets); err != nil {
|
||||
return layouts{}, nil, err
|
||||
}
|
||||
|
||||
// the order of each layout entry must be preserved
|
||||
pool := map[PsxOffset]int{}
|
||||
blocks := [][]layoutEntry{}
|
||||
xRanges := []dataRange{}
|
||||
for _, blockOffset := range sortUniqueOffsets(blockOffsets) {
|
||||
if err := blockOffset.moveFile(file); err != nil {
|
||||
return layouts{}, nil, err
|
||||
}
|
||||
entries := []layoutEntry{}
|
||||
for {
|
||||
entry, err := readEntityLayoutEntry(file)
|
||||
if err != nil {
|
||||
return layouts{}, nil, err
|
||||
}
|
||||
if entry.X == -1 && entry.Y == -1 {
|
||||
entries = append(entries, entry)
|
||||
break
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
// sanity check on the first entry
|
||||
if entries[0].X != -2 || entries[0].Y != -2 {
|
||||
err := fmt.Errorf("first layout entry does not mark the beginning of the array: %v", entries[0])
|
||||
return layouts{}, nil, err
|
||||
}
|
||||
|
||||
pool[blockOffset] = len(blocks)
|
||||
blocks = append(blocks, entries)
|
||||
xRanges = append(xRanges, dataRange{
|
||||
begin: blockOffset,
|
||||
end: blockOffset.sum(len(entries) * 10),
|
||||
})
|
||||
}
|
||||
// the very last entry needs to be aligned by 4
|
||||
xRanges[len(xRanges)-1].end = xRanges[len(xRanges)-1].end.align4()
|
||||
|
||||
l := layouts{Entities: blocks}
|
||||
for _, blockOffset := range blockOffsets {
|
||||
l.Indices = append(l.Indices, pool[blockOffset])
|
||||
}
|
||||
|
||||
endOfArray := off.sum(count * 4)
|
||||
if isX { // we want to do the same thing with the vertically aligned layout
|
||||
yLayouts, yRanges, err := readEntityLayout(file, endOfArray, count, false)
|
||||
if err != nil {
|
||||
return layouts{}, nil, fmt.Errorf("readEntityLayout failed on Y: %w", err)
|
||||
}
|
||||
if err := hydrateYOrderFields(l, yLayouts); err != nil {
|
||||
return layouts{}, nil, fmt.Errorf("unable to populate YOrder field: %w", err)
|
||||
}
|
||||
xMerged := mergeDataRanges(xRanges)
|
||||
yMerged := yRanges[1]
|
||||
return l, []dataRange{
|
||||
mergeDataRanges([]dataRange{
|
||||
{
|
||||
begin: off,
|
||||
end: endOfArray,
|
||||
},
|
||||
yRanges[0],
|
||||
}),
|
||||
mergeDataRanges([]dataRange{xMerged, yMerged}),
|
||||
}, nil
|
||||
} else {
|
||||
return l, []dataRange{
|
||||
{
|
||||
begin: off,
|
||||
end: endOfArray,
|
||||
},
|
||||
mergeDataRanges(xRanges),
|
||||
}, nil
|
||||
}
|
||||
}
|
379
tools/sotn-assets/main.go
Normal file
379
tools/sotn-assets/main.go
Normal file
@ -0,0 +1,379 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
type dataContainer[T any] struct {
|
||||
dataRange dataRange
|
||||
content T
|
||||
}
|
||||
|
||||
type ovl struct {
|
||||
ranges []dataRange
|
||||
rooms dataContainer[[]room]
|
||||
layers dataContainer[[]roomLayers]
|
||||
sprites dataContainer[spriteDefs]
|
||||
graphics dataContainer[gfx]
|
||||
layouts dataContainer[layouts]
|
||||
layoutsExtraRange dataRange
|
||||
tileMaps dataContainer[map[PsxOffset][]byte]
|
||||
tileDefs dataContainer[map[PsxOffset]tileDef]
|
||||
}
|
||||
|
||||
func getOvlAssets(fileName string) (ovl, error) {
|
||||
type stageHeader struct {
|
||||
FnUpdate PsxOffset
|
||||
FnHitDetection PsxOffset
|
||||
FnUpdateRoomPos PsxOffset
|
||||
FnInitRoomEntities PsxOffset
|
||||
Rooms PsxOffset // ✅
|
||||
Sprites PsxOffset // ✅
|
||||
Cluts PsxOffset // 🫥
|
||||
Layouts PsxOffset // ✅
|
||||
Layers PsxOffset // ✅
|
||||
Graphics PsxOffset // 🫥 WIP
|
||||
FnUpdateStageEntities PsxOffset
|
||||
}
|
||||
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return ovl{}, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var header stageHeader
|
||||
if err := binary.Read(file, binary.LittleEndian, &header); err != nil {
|
||||
return ovl{}, err
|
||||
}
|
||||
|
||||
rooms, roomsRange, err := readRooms(file, header.Rooms)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to read rooms: %w", err)
|
||||
}
|
||||
|
||||
layers, layersRange, err := readLayers(file, header.Layers)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to read layers: %w", err)
|
||||
}
|
||||
|
||||
tileMaps, tileMapsRange, err := readAllTileMaps(file, layers)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to gather all the tile maps: %w", err)
|
||||
}
|
||||
|
||||
tileDefs, tileDefsRange, err := readAllTiledefs(file, layers)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to gather all the tile defs: %w", err)
|
||||
}
|
||||
|
||||
// check for unused tile defs (CEN has one)
|
||||
for tileMapsRange.end < tileDefsRange.begin {
|
||||
offset := tileDefsRange.begin.sum(-0x10)
|
||||
unusedTileDef, unusedTileDefRange, err := readTiledef(file, offset)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("there is a gap between tileMaps and tileDefs: %w", err)
|
||||
}
|
||||
tileDefs[offset] = unusedTileDef
|
||||
tileDefsRange = mergeDataRanges([]dataRange{tileDefsRange, unusedTileDefRange})
|
||||
}
|
||||
|
||||
sprites, spritesRange, err := readSpritesBanks(file, header.Sprites)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to gather all sprites: %w", err)
|
||||
}
|
||||
|
||||
graphics, graphicsRange, err := readGraphics(file, header.Graphics)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to gather all graphics: %w", err)
|
||||
}
|
||||
|
||||
layoutOff := header.Layouts
|
||||
if layoutOff == RamNull {
|
||||
// some overlays have this field nulled, we have to find the offset ourselves
|
||||
// it should be usually be right after header.Graphics
|
||||
layoutOff = graphicsRange.end // ⚠️ assumption
|
||||
}
|
||||
nLayouts := maxBy(rooms, func(r room) int { // ⚠️ assumption
|
||||
return int(r.EntityLayoutID)
|
||||
}) + 1
|
||||
nLayouts = 53 // it seems there are always 53 elements?!
|
||||
entityLayouts, layoutsRange, err := readEntityLayout(file, layoutOff, nLayouts, true)
|
||||
if err != nil {
|
||||
return ovl{}, fmt.Errorf("unable to gather all entity layouts: %w", err)
|
||||
}
|
||||
|
||||
return ovl{
|
||||
ranges: consolidateDataRanges([]dataRange{
|
||||
roomsRange,
|
||||
layersRange,
|
||||
spritesRange,
|
||||
graphicsRange,
|
||||
layoutsRange[0],
|
||||
layoutsRange[1],
|
||||
tileMapsRange,
|
||||
tileDefsRange,
|
||||
}),
|
||||
rooms: dataContainer[[]room]{dataRange: roomsRange, content: rooms},
|
||||
layers: dataContainer[[]roomLayers]{dataRange: layersRange, content: layers},
|
||||
sprites: dataContainer[spriteDefs]{dataRange: spritesRange, content: sprites},
|
||||
graphics: dataContainer[gfx]{dataRange: graphicsRange, content: graphics},
|
||||
layouts: dataContainer[layouts]{dataRange: layoutsRange[1], content: entityLayouts},
|
||||
layoutsExtraRange: layoutsRange[0],
|
||||
tileMaps: dataContainer[map[PsxOffset][]byte]{dataRange: tileMapsRange, content: tileMaps},
|
||||
tileDefs: dataContainer[map[PsxOffset]tileDef]{dataRange: tileDefsRange, content: tileDefs},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func extractOvlAssets(o ovl, outputDir string) error {
|
||||
if err := os.MkdirAll(outputDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := json.MarshalIndent(o.rooms.content, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, "rooms.json"), content, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create rooms file: %w", err)
|
||||
}
|
||||
|
||||
content, err = json.MarshalIndent(o.layers.content, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, "layers.json"), content, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create layers file: %w", err)
|
||||
}
|
||||
|
||||
content, err = json.MarshalIndent(o.layouts.content, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, "entity_layouts.json"), content, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create entity layouts file: %w", err)
|
||||
}
|
||||
|
||||
for offset, bytes := range o.tileMaps.content {
|
||||
fileName := path.Join(outputDir, getTilemapFileName(offset))
|
||||
if err := os.WriteFile(fileName, bytes, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", fileName, err)
|
||||
}
|
||||
}
|
||||
|
||||
for offset, tileDefsData := range o.tileDefs.content {
|
||||
defs := tileDefPaths{
|
||||
Tiles: getTiledefIndicesFileName(offset),
|
||||
Pages: getTiledefPagesFileName(offset),
|
||||
Cluts: getTiledefClutsFileName(offset),
|
||||
Collisions: getTiledefCollisionsFileName(offset),
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, defs.Tiles), tileDefsData.tiles, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", defs.Tiles, err)
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, defs.Pages), tileDefsData.pages, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", defs.Pages, err)
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, defs.Cluts), tileDefsData.cluts, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", defs.Cluts, err)
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, defs.Collisions), tileDefsData.cols, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", defs.Collisions, err)
|
||||
}
|
||||
|
||||
content, err = json.MarshalIndent(defs, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileName := path.Join(outputDir, getTiledefFileName(offset))
|
||||
if err := os.WriteFile(fileName, content, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create %q: %w", fileName, err)
|
||||
}
|
||||
}
|
||||
|
||||
content, err = json.MarshalIndent(o.sprites.content, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path.Join(outputDir, "sprites.json"), content, 0644); err != nil {
|
||||
return fmt.Errorf("unable to create sprites file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extract(fileName string, outputDir string) error {
|
||||
o, err := getOvlAssets(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve OVL assets: %w", err)
|
||||
}
|
||||
|
||||
err = extractOvlAssets(o, outputDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to extract OVL assets: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func info(fileName string) error {
|
||||
o, err := getOvlAssets(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve OVL assets: %w", err)
|
||||
}
|
||||
|
||||
entries := []struct {
|
||||
dataRange dataRange
|
||||
name string
|
||||
comment string
|
||||
}{
|
||||
{o.layers.dataRange, "header", "layers"},
|
||||
{o.layoutsExtraRange, "e_laydef", "layout entries header"},
|
||||
{o.rooms.dataRange, "rooms", ""},
|
||||
{o.layouts.dataRange, "e_layout", "layout entries data"},
|
||||
{o.tileMaps.dataRange, "tile_data", "tile data"},
|
||||
{o.tileDefs.dataRange, "tile_data", "tile definitions"},
|
||||
{o.sprites.dataRange, "sprites", ""},
|
||||
}
|
||||
|
||||
fmt.Printf("data coverage: %+v\n", o.ranges)
|
||||
fmt.Println("subsegment hints:")
|
||||
fmt.Println(" - [0x0, .data, header]")
|
||||
for i := 0; i < len(entries); i++ {
|
||||
e := entries[i]
|
||||
s := fmt.Sprintf(" - [0x%X, .data, %s]", e.dataRange.begin.real(), e.name)
|
||||
if e.comment != "" {
|
||||
s = fmt.Sprintf("%s # %s", s, e.comment)
|
||||
}
|
||||
fmt.Println(s)
|
||||
|
||||
// if there is a gap between the current entry and the next one, mark it as unrecognized data
|
||||
if i == len(entries)-1 || e.dataRange.end != entries[i+1].dataRange.begin {
|
||||
fmt.Printf(" - [0x%X, data]\n", e.dataRange.end.real())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testStuff() {
|
||||
_ = []string{
|
||||
"ARE", "CAT", "CEN", "CHI", "DAI", "DRE", "LIB", "MAD",
|
||||
"NO0", "NO1", "NO2", "NO3", "NO4", "NP3", "NZ0", "NZ1",
|
||||
"ST0", "TE1", "TE2", "TE3", "TE4", "TE5", "TOP", "WRP",
|
||||
"RARE", "RCAT", "RCEN", "RCHI", "RDAI", "RLIB", "RNO0", "RNO1",
|
||||
"RNO2", "RNO3", "RNO4", "RNZ0", "RNZ1", "RTOP", "RWRP"}
|
||||
//ovls := []string{
|
||||
// /*"ARE",*/ "CAT", "CEN", "CHI" /*"DAI",*/, "DRE", "LIB", /*"MAD",*/
|
||||
// /*"NO0",*/ "NO1", "NO2", "NO3" /*"NO4",*/, "NP3", "NZ0", "NZ1",
|
||||
// "ST0", "TE1", "TE2", "TE3", "TE4", "TE5" /*"TOP",*/, "WRP",
|
||||
// "RARE", "RCAT" /*"RCEN",*/, "RCHI" /*"RDAI",*/ /*"RLIB",*/ /*"RNO0",*/, "RNO1",
|
||||
// /*"RNO2",*/ "RNO3" /*"RNO4",*/ /*"RNZ0",*/ /*"RNZ1",*/ /*"RTOP",*/, "RWRP"}
|
||||
ovls := []string{"NZ0"}
|
||||
|
||||
for _, ovl := range ovls {
|
||||
fmt.Printf("processing %s...\n", ovl)
|
||||
fileName := fmt.Sprintf("../../disks/us/ST/%s/%s.BIN", ovl, ovl)
|
||||
if err := extract(fileName, "sample/"+ovl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := buildAll("sample/NZ0", "buildAll/nz0"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("expected 'info', 'extract', 'build' or 'build_all' subcommands")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "info":
|
||||
extractCmd := flag.NewFlagSet("info", flag.ExitOnError)
|
||||
var stageOvl string
|
||||
extractCmd.StringVar(&stageOvl, "stage_ovl", "", "The overlay file to process")
|
||||
extractCmd.Parse(os.Args[2:])
|
||||
if err := info(stageOvl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "extract":
|
||||
extractCmd := flag.NewFlagSet("extract", flag.ExitOnError)
|
||||
var stageOvl string
|
||||
var assetDir string
|
||||
extractCmd.StringVar(&stageOvl, "stage_ovl", "", "The overlay file to process")
|
||||
extractCmd.StringVar(&assetDir, "o", "", "Where to extract the asset files")
|
||||
|
||||
extractCmd.Parse(os.Args[2:])
|
||||
if stageOvl == "" || assetDir == "" {
|
||||
fmt.Println("stage_ovl and asset_dir are required for extract")
|
||||
extractCmd.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := extract(stageOvl, assetDir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "build":
|
||||
buildCmd := flag.NewFlagSet("build", flag.ExitOnError)
|
||||
var file string
|
||||
var kind string
|
||||
var outputDir string
|
||||
buildCmd.StringVar(&file, "file", "", "File to process")
|
||||
buildCmd.StringVar(&kind, "kind", "", "Kind of the file to process")
|
||||
buildCmd.StringVar(&outputDir, "o", "", "Where to store the processed source files")
|
||||
|
||||
buildCmd.Parse(os.Args[2:])
|
||||
|
||||
if file == "" || kind == "" || outputDir == "" {
|
||||
fmt.Println("file, kind, and output_dir are required for build")
|
||||
buildCmd.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var err error
|
||||
switch kind {
|
||||
case "rooms":
|
||||
err = buildRooms(file, outputDir)
|
||||
case "layers":
|
||||
err = buildLayers(path.Base(file), file, outputDir)
|
||||
case "sprites":
|
||||
err = buildSprites(file, outputDir)
|
||||
default:
|
||||
fmt.Println("unknown kind, valid values are 'room', 'layer', 'sprites'")
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
case "build_all":
|
||||
buildCmd := flag.NewFlagSet("build_all", flag.ExitOnError)
|
||||
var inputDir string
|
||||
var outputDir string
|
||||
buildCmd.StringVar(&inputDir, "i", "", "Folder where all the assets are located")
|
||||
buildCmd.StringVar(&outputDir, "o", "", "Where to store the processed source files")
|
||||
|
||||
buildCmd.Parse(os.Args[2:])
|
||||
|
||||
if inputDir == "" || outputDir == "" {
|
||||
fmt.Println("input_dir and output_dir are required for build")
|
||||
buildCmd.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := buildAll(inputDir, outputDir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Println("expected 'info', 'extract', 'build' or 'build_all' subcommands")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
27
tools/sotn-assets/paths.go
Normal file
27
tools/sotn-assets/paths.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func getTilemapFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tilemap_%05X.bin", off.real())
|
||||
}
|
||||
|
||||
func getTiledefFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tiledef_%05X.json", off.real())
|
||||
}
|
||||
|
||||
func getTiledefIndicesFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tiledef_%05X_tiles.bin", off.real())
|
||||
}
|
||||
|
||||
func getTiledefPagesFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tiledef_%05X_pages.bin", off.real())
|
||||
}
|
||||
|
||||
func getTiledefClutsFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tiledef_%05X_cluts.bin", off.real())
|
||||
}
|
||||
|
||||
func getTiledefCollisionsFileName(off PsxOffset) string {
|
||||
return fmt.Sprintf("tiledef_%05X_cols.bin", off.real())
|
||||
}
|
84
tools/sotn-assets/psx_offsets.go
Normal file
84
tools/sotn-assets/psx_offsets.go
Normal file
@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type PsxOffset uint32
|
||||
|
||||
const (
|
||||
RamNull = PsxOffset(0)
|
||||
RamStageBegin = PsxOffset(0x80180000)
|
||||
RamStageEnd = PsxOffset(0x801C0000)
|
||||
)
|
||||
|
||||
func (off PsxOffset) Format(f fmt.State, c rune) {
|
||||
f.Write([]byte(fmt.Sprintf("0x%08X", uint32(off))))
|
||||
}
|
||||
|
||||
func (off PsxOffset) real() int {
|
||||
return int(off - RamStageBegin)
|
||||
}
|
||||
|
||||
func (off PsxOffset) align4() PsxOffset {
|
||||
if (off & 3) != 0 {
|
||||
return (off | 3) + 1
|
||||
}
|
||||
return off
|
||||
}
|
||||
|
||||
func (off PsxOffset) sum(x int) PsxOffset {
|
||||
return PsxOffset(uint32(off) + uint32(x))
|
||||
}
|
||||
|
||||
func (off PsxOffset) distanceTo(x PsxOffset) int {
|
||||
return int(x - off)
|
||||
}
|
||||
|
||||
func (off PsxOffset) valid() bool {
|
||||
return off >= RamStageBegin && off < RamStageEnd
|
||||
}
|
||||
|
||||
func (off PsxOffset) moveFile(file *os.File) error {
|
||||
if !off.valid() {
|
||||
err := fmt.Errorf("offset %08X is outside the stage boundaries", off)
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
stats, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileOffset := int64(off - RamStageBegin)
|
||||
if fileOffset >= stats.Size() {
|
||||
return fmt.Errorf("offset %08X is outside the file boundaries", off)
|
||||
}
|
||||
|
||||
file.Seek(fileOffset, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortUniqueOffsets(slice []PsxOffset) []PsxOffset {
|
||||
unique := map[PsxOffset]struct{}{}
|
||||
for _, v := range slice {
|
||||
unique[v] = struct{}{}
|
||||
}
|
||||
newSlice := make([]PsxOffset, 0, len(unique))
|
||||
for offset := range unique {
|
||||
newSlice = append(newSlice, offset)
|
||||
}
|
||||
|
||||
slices.SortFunc(newSlice, func(a, b PsxOffset) int {
|
||||
if a < b {
|
||||
return -1
|
||||
} else if a > b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
return newSlice
|
||||
}
|
46
tools/sotn-assets/room.go
Normal file
46
tools/sotn-assets/room.go
Normal file
@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os"
|
||||
)
|
||||
|
||||
type room struct {
|
||||
Left int8 `json:"left"`
|
||||
Top int8 `json:"top"`
|
||||
Right int8 `json:"right"`
|
||||
Bottom int8 `json:"bottom"`
|
||||
LayerID int8 `json:"layerId"`
|
||||
TileDefID int8 `json:"tileDefId"`
|
||||
EntityGfxID int8 `json:"entityGfxId"`
|
||||
EntityLayoutID int8 `json:"entityLayoutId"`
|
||||
}
|
||||
|
||||
func (r room) isTerminator() bool {
|
||||
return r.Left == 0x40
|
||||
}
|
||||
|
||||
func readRooms(file *os.File, off PsxOffset) ([]room, dataRange, error) {
|
||||
if off == 0 {
|
||||
return nil, dataRange{}, nil
|
||||
}
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
|
||||
rooms := []room{}
|
||||
for {
|
||||
var room room
|
||||
if err := binary.Read(file, binary.LittleEndian, &room); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
if room.isTerminator() {
|
||||
break
|
||||
}
|
||||
rooms = append(rooms, room)
|
||||
}
|
||||
return rooms, dataRange{
|
||||
begin: off,
|
||||
end: off.sum(len(rooms)*8 + 4),
|
||||
}, nil
|
||||
}
|
159
tools/sotn-assets/sprites.go
Normal file
159
tools/sotn-assets/sprites.go
Normal file
@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type sprite struct {
|
||||
Flags uint16 `json:"flags"`
|
||||
X int16 `json:"x"`
|
||||
Y int16 `json:"y"`
|
||||
Width uint16 `json:"width"`
|
||||
Height uint16 `json:"height"`
|
||||
Clut uint16 `json:"clut"`
|
||||
Tileset uint16 `json:"tileset"`
|
||||
Left uint16 `json:"left"`
|
||||
Top uint16 `json:"top"`
|
||||
Right uint16 `json:"right"`
|
||||
Bottom uint16 `json:"bottom"`
|
||||
}
|
||||
|
||||
type spriteDefs struct {
|
||||
Banks [][]*[]sprite `json:"banks"`
|
||||
Indices []int `json:"indices"`
|
||||
}
|
||||
|
||||
func readSprites(file *os.File, off PsxOffset) ([]sprite, dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("invalid sprites: %w", err)
|
||||
}
|
||||
|
||||
var count uint16
|
||||
if err := binary.Read(file, binary.LittleEndian, &count); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
|
||||
sprites := make([]sprite, count)
|
||||
if err := binary.Read(file, binary.LittleEndian, sprites); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
|
||||
return sprites, dataRange{
|
||||
begin: off,
|
||||
end: off.sum(4 + 0x16*int(count)).align4(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readSpriteBank(file *os.File, off PsxOffset) ([]*[]sprite, dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("invalid sprite Indices: %w", err)
|
||||
}
|
||||
|
||||
// the end of the sprite array is the beginning of the earliest sprite offset
|
||||
earliestSpriteOff := RamStageEnd
|
||||
currentOff := off
|
||||
spriteOffsets := make([]PsxOffset, 0)
|
||||
for {
|
||||
if currentOff == earliestSpriteOff {
|
||||
break
|
||||
}
|
||||
currentOff += 4
|
||||
|
||||
var spriteOffset PsxOffset
|
||||
if err := binary.Read(file, binary.LittleEndian, &spriteOffset); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
spriteOffsets = append(spriteOffsets, spriteOffset)
|
||||
if spriteOffset != RamNull {
|
||||
if !spriteOffset.valid() {
|
||||
err := fmt.Errorf("sprite offset %s is not valid", spriteOffset)
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
earliestSpriteOff = min(earliestSpriteOff, spriteOffset)
|
||||
}
|
||||
}
|
||||
headerRange := dataRange{
|
||||
begin: off,
|
||||
end: earliestSpriteOff,
|
||||
}
|
||||
|
||||
spriteBank := make([]*[]sprite, len(spriteOffsets))
|
||||
spriteRanges := []dataRange{}
|
||||
for i, offset := range spriteOffsets {
|
||||
if offset == RamNull {
|
||||
spriteBank[i] = nil
|
||||
continue
|
||||
}
|
||||
sprites, ranges, err := readSprites(file, offset)
|
||||
if err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("unable to read sprites: %w", err)
|
||||
}
|
||||
spriteBank[i] = &sprites
|
||||
spriteRanges = append(spriteRanges, ranges)
|
||||
}
|
||||
|
||||
return spriteBank, mergeDataRanges(append(spriteRanges, headerRange)), nil
|
||||
}
|
||||
|
||||
func readSpritesBanks(file *os.File, off PsxOffset) (spriteDefs, dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return spriteDefs{}, dataRange{}, err
|
||||
}
|
||||
|
||||
offBanks := make([]PsxOffset, 24)
|
||||
if err := binary.Read(file, binary.LittleEndian, offBanks); err != nil {
|
||||
return spriteDefs{}, dataRange{}, err
|
||||
}
|
||||
|
||||
// the order sprites are stored must be preserved
|
||||
pool := map[PsxOffset][]*[]sprite{}
|
||||
spriteRanges := []dataRange{}
|
||||
for _, offset := range offBanks {
|
||||
if offset == RamNull {
|
||||
continue
|
||||
}
|
||||
if _, found := pool[offset]; found {
|
||||
continue
|
||||
}
|
||||
bank, bankRange, err := readSpriteBank(file, offset)
|
||||
if err != nil {
|
||||
return spriteDefs{}, dataRange{}, fmt.Errorf("unable to read sprite Indices: %w", err)
|
||||
}
|
||||
pool[offset] = bank
|
||||
spriteRanges = append(spriteRanges, bankRange)
|
||||
}
|
||||
|
||||
// the indices do not guarantee sprites to be stored in a linear order
|
||||
// we must sort the offsets to preserve the order sprites are stored
|
||||
sortedOffsets := make([]PsxOffset, 0, len(pool))
|
||||
for offset := range pool {
|
||||
sortedOffsets = append(sortedOffsets, offset)
|
||||
}
|
||||
sort.Slice(sortedOffsets, func(i, j int) bool { return sortedOffsets[i] < sortedOffsets[j] })
|
||||
|
||||
// create a list of indices to replace the original pointers
|
||||
indices := make([]int, len(offBanks))
|
||||
for i, offset := range offBanks {
|
||||
if offset == RamNull {
|
||||
indices[i] = -1
|
||||
}
|
||||
for j, sortedOffset := range sortedOffsets {
|
||||
if offset == sortedOffset {
|
||||
indices[i] = j
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
banks := make([][]*[]sprite, len(sortedOffsets))
|
||||
for i, offset := range sortedOffsets {
|
||||
banks[i] = pool[offset]
|
||||
}
|
||||
|
||||
return spriteDefs{
|
||||
Banks: banks,
|
||||
Indices: indices,
|
||||
}, mergeDataRanges(spriteRanges), nil
|
||||
}
|
100
tools/sotn-assets/tile_def.go
Normal file
100
tools/sotn-assets/tile_def.go
Normal file
@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type tileDef struct {
|
||||
tiles []byte
|
||||
pages []byte
|
||||
cluts []byte
|
||||
cols []byte
|
||||
}
|
||||
|
||||
type tileDefPaths struct {
|
||||
Tiles string `json:"tiles"`
|
||||
Pages string `json:"pages"`
|
||||
Cluts string `json:"cluts"`
|
||||
Collisions string `json:"collisions"`
|
||||
}
|
||||
|
||||
func readTiledef(file *os.File, off PsxOffset) (tileDef, dataRange, error) {
|
||||
if err := off.moveFile(file); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
offsets := make([]PsxOffset, 4)
|
||||
if err := binary.Read(file, binary.LittleEndian, offsets); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
td := tileDef{
|
||||
tiles: make([]byte, offsets[1]-offsets[0]),
|
||||
pages: make([]byte, offsets[2]-offsets[1]),
|
||||
cluts: make([]byte, offsets[3]-offsets[2]),
|
||||
cols: make([]byte, off-offsets[3]),
|
||||
}
|
||||
|
||||
if err := offsets[0].moveFile(file); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
if _, err := file.Read(td.tiles); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
if err := offsets[1].moveFile(file); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
if _, err := file.Read(td.pages); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
if err := offsets[2].moveFile(file); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
if _, err := file.Read(td.cluts); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
if err := offsets[3].moveFile(file); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
if _, err := file.Read(td.cols); err != nil {
|
||||
return tileDef{}, dataRange{}, err
|
||||
}
|
||||
|
||||
return td, dataRange{
|
||||
begin: offsets[0],
|
||||
end: off.sum(0x10),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readAllTiledefs(file *os.File, roomLayers []roomLayers) (map[PsxOffset]tileDef, dataRange, error) {
|
||||
ranges := []dataRange{}
|
||||
processed := map[PsxOffset]tileDef{}
|
||||
for _, rl := range roomLayers {
|
||||
if rl.fg != nil {
|
||||
if _, found := processed[rl.fg.Tiledef]; !found {
|
||||
td, r, err := readTiledef(file, rl.fg.Tiledef)
|
||||
if err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("unable to read fg tiledef: %w", err)
|
||||
}
|
||||
processed[rl.fg.Tiledef] = td
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
}
|
||||
if rl.bg != nil {
|
||||
if _, found := processed[rl.bg.Tiledef]; !found {
|
||||
td, r, err := readTiledef(file, rl.bg.Tiledef)
|
||||
if err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("unable to read fg tiledef: %w", err)
|
||||
}
|
||||
processed[rl.bg.Tiledef] = td
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed, mergeDataRanges(ranges), nil
|
||||
}
|
48
tools/sotn-assets/tile_map.go
Normal file
48
tools/sotn-assets/tile_map.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func readTilemap(file *os.File, layer *layer) ([]byte, dataRange, error) {
|
||||
if err := layer.Data.moveFile(file); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
data := make([]byte, layer.tilemapFileSize())
|
||||
if _, err := file.Read(data); err != nil {
|
||||
return nil, dataRange{}, err
|
||||
}
|
||||
return data, dataRange{
|
||||
begin: layer.Data,
|
||||
end: layer.Data.sum(len(data)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func readAllTileMaps(file *os.File, roomLayers []roomLayers) (map[PsxOffset][]byte, dataRange, error) {
|
||||
ranges := []dataRange{}
|
||||
processed := map[PsxOffset][]byte{}
|
||||
for _, rl := range roomLayers {
|
||||
if rl.fg != nil {
|
||||
if _, found := processed[rl.fg.Data]; !found {
|
||||
td, r, err := readTilemap(file, rl.fg)
|
||||
if err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("unable to read fg tilemap: %w", err)
|
||||
}
|
||||
processed[rl.fg.Data] = td
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
}
|
||||
if rl.bg != nil {
|
||||
if _, found := processed[rl.bg.Data]; !found {
|
||||
td, r, err := readTilemap(file, rl.bg)
|
||||
if err != nil {
|
||||
return nil, dataRange{}, fmt.Errorf("unable to read fg tilemap: %w", err)
|
||||
}
|
||||
processed[rl.bg.Data] = td
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed, mergeDataRanges(ranges), nil
|
||||
}
|
36
tools/sotn-assets/utils.go
Normal file
36
tools/sotn-assets/utils.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
func minBy[T any](slice []T, getter func(T) int) (max int) {
|
||||
if len(slice) == 0 {
|
||||
return max
|
||||
}
|
||||
max = getter(slice[0])
|
||||
for _, item := range slice[1:] {
|
||||
val := getter(item)
|
||||
if val < max {
|
||||
max = val
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func maxBy[T any](slice []T, getter func(T) int) (max int) {
|
||||
if len(slice) == 0 {
|
||||
return max
|
||||
}
|
||||
max = getter(slice[0])
|
||||
for _, item := range slice[1:] {
|
||||
val := getter(item)
|
||||
if val > max {
|
||||
max = val
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func btoi(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
||||
from splat.util import options, log
|
||||
from splat.segtypes.n64.segment import N64Segment
|
||||
from splat.util.symbols import spim_context
|
||||
import utils
|
||||
|
||||
|
||||
def raise_err(str):
|
||||
sys.stderr.write("\033[91merror: " + str + "\u001b[0m" + "\n")
|
||||
raise Exception(str)
|
||||
|
||||
|
||||
def generate_assembly_layers(writer: io.BufferedWriter, name: str, content: str):
|
||||
obj = json.loads(content)
|
||||
|
||||
layer_set = {}
|
||||
|
||||
def get_int(myobj, name, min, max):
|
||||
value = myobj.get(name)
|
||||
if value == None:
|
||||
raise_err(f"expect '{name}' but found nothing\ncurrent object: {myobj}")
|
||||
if value < min:
|
||||
raise_err(f"expect '{name}' to be at least 0\ncurrent object: {myobj}")
|
||||
if value > max:
|
||||
raise_err(f"expect '{name}' to be less than {max}\ncurrent object: {myobj}")
|
||||
return value
|
||||
|
||||
def get_bool(myobj, name):
|
||||
value = myobj.get(name)
|
||||
if value == None:
|
||||
raise_err(f"expect '{name}' but found nothing\ncurrent object: {myobj}")
|
||||
return value
|
||||
|
||||
def get_str(myobj, name):
|
||||
value = myobj.get(name)
|
||||
if value == None:
|
||||
raise_err(f"expect '{name}' but found nothing\ncurrent object: {myobj}")
|
||||
return value
|
||||
|
||||
def write_layer(layer):
|
||||
hash = json.dumps(layer) # 🤮
|
||||
if hash in layer_set:
|
||||
return layer_set[hash]
|
||||
id = f"DATA_LAYER_{len(layer_set)}"
|
||||
layer_set[hash] = id
|
||||
|
||||
writer.write(f".global {id}\n")
|
||||
writer.write(f"{id}:\n")
|
||||
if layer == None:
|
||||
writer.write(f"\t.word 0, 0, 0, 0\n")
|
||||
else:
|
||||
data = get_str(layer, "data")
|
||||
tiledef = get_str(layer, "tiledef")
|
||||
flags = (
|
||||
get_int(layer, "left", 0, 0x3F)
|
||||
| get_int(layer, "top", 0, 0x3F) << 6
|
||||
| get_int(layer, "right", 0, 0x3F) << 12
|
||||
| get_int(layer, "bottom", 0, 0x3F) << 18
|
||||
| get_int(layer, "scrollMode", 0, 0x1F) << 24
|
||||
| (1 if get_bool(layer, "isSaveRoom") else 0) << 29
|
||||
| (1 if get_bool(layer, "isLoadingRoom") else 0) << 30
|
||||
| (1 if get_bool(layer, "unusedFlag") else 0) << 31
|
||||
)
|
||||
zPriority = get_int(layer, "zPriority", 0, 32768) # s16 or u16?
|
||||
unkE = get_int(layer, "unkE", 0, 255)
|
||||
unkF = get_int(layer, "unkF", 0, 255)
|
||||
writer.write(f"\t.word {data}, {tiledef}, 0x{flags:08X}\n")
|
||||
writer.write(f"\t.short 0x{zPriority:04X}\n")
|
||||
writer.write(f"\t.byte 0x{unkE:02X}, 0x{unkF:02X}\n")
|
||||
return id
|
||||
|
||||
writer.write(".section .data\n")
|
||||
write_layer(None)
|
||||
buf = ""
|
||||
for room in obj:
|
||||
fgSym = write_layer(room.get("fg"))
|
||||
bgSym = write_layer(room.get("bg"))
|
||||
buf += f".word {fgSym}, {bgSym}\n"
|
||||
writer.write(f".global g_TileLayers\n") # TODO: symbol name hardcoded 🤮
|
||||
writer.write(f"g_TileLayers:\n{buf}")
|
||||
|
||||
|
||||
class PSXSegLayers(N64Segment):
|
||||
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml):
|
||||
super().__init__(rom_start, rom_end, type, name, vram_start, args, yaml),
|
||||
|
||||
def out_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / self.name
|
||||
|
||||
def src_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}.layers.json"
|
||||
|
||||
def split(self, rom_bytes):
|
||||
path = self.src_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = self.parse_layers(rom_bytes[self.rom_start : self.rom_end])
|
||||
with open(path, "w") as f:
|
||||
f.write(json.dumps(data, indent=4))
|
||||
|
||||
def get_symbol(self, offset):
|
||||
if offset == 0:
|
||||
return 0
|
||||
|
||||
symbol = super().get_symbol(offset)
|
||||
if symbol == None or symbol.given_name == None:
|
||||
return f"D_{offset:08X}"
|
||||
return symbol.given_name
|
||||
|
||||
def parse_layers(self, data: bytearray):
|
||||
err = Exception(f"unable to decode '{self.name}'")
|
||||
roomCount = 0
|
||||
ram_start = self.vram_start
|
||||
ram_end = self.vram_start + self.rom_end - self.rom_start
|
||||
roomOff = len(data) - 8
|
||||
if roomOff < 0:
|
||||
log.error(f"data for '{self.name}' is to small")
|
||||
|
||||
# Since we do not know beforehand how many rooms there are (we do, but
|
||||
# we want it to be automatic), this algo performs validation, room
|
||||
# count and defines exactly where is the starting LayerDef and
|
||||
# the starting RoomDef. This is possible because the list of LayerDef
|
||||
# always comes right before RoomDef and it has a different structure.
|
||||
layerDefOffsets = set({})
|
||||
roomDefStart = ram_start
|
||||
while True:
|
||||
fgPtr = utils.to_u32(data[roomOff:])
|
||||
bgPtr = utils.to_u32(data[roomOff + 4 :])
|
||||
if (
|
||||
bgPtr < 0x80180000
|
||||
or fgPtr >= 0x801E0000
|
||||
or bgPtr < 0x80180000
|
||||
or fgPtr >= 0x801E0000
|
||||
or bgPtr >= ram_end
|
||||
or fgPtr >= ram_end
|
||||
):
|
||||
log.error(
|
||||
f"data for '{self.name}' does not describe a set of room layers"
|
||||
)
|
||||
raise err
|
||||
if fgPtr < ram_start or bgPtr < ram_start:
|
||||
min_start = min(fgPtr, bgPtr)
|
||||
log.error(
|
||||
f"data for '{self.name}' needs to start at least from 0x{min_start:X}"
|
||||
)
|
||||
raise err
|
||||
layerDefOffsets.add(fgPtr)
|
||||
layerDefOffsets.add(bgPtr)
|
||||
roomCount += 1
|
||||
|
||||
roomDefStart = max(roomDefStart, max(fgPtr, bgPtr))
|
||||
if ram_start + roomOff - 16 == roomDefStart:
|
||||
break
|
||||
|
||||
roomOff -= 8
|
||||
if roomOff <= 0:
|
||||
log.error(
|
||||
f"data for '{self.name}' needs to start at least from 0x{min_start:X}"
|
||||
)
|
||||
raise err
|
||||
|
||||
# Creates a dictionary of layers, so we can re-use them when a layer is
|
||||
# used by multiple rooms.
|
||||
layerPool = {}
|
||||
for off in layerDefOffsets:
|
||||
layerDefData = data[off - ram_start :]
|
||||
dataOff = utils.to_u32(layerDefData[0:])
|
||||
tiledefOff = utils.to_u32(layerDefData[4:])
|
||||
flags = utils.to_u32(layerDefData[8:])
|
||||
if dataOff != 0 or tiledefOff != 0 or flags != 0:
|
||||
layerPool[off] = {
|
||||
"data": self.get_symbol(dataOff),
|
||||
"tiledef": self.get_symbol(tiledefOff),
|
||||
"left": flags & 0x3F,
|
||||
"top": (flags >> 6) & 0x3F,
|
||||
"right": (flags >> 12) & 0x3F,
|
||||
"bottom": (flags >> 18) & 0x3F,
|
||||
"scrollMode": (flags >> 24) & 0x1F,
|
||||
"isSaveRoom": ((flags >> 24) & 0x20) != 0,
|
||||
"isLoadingRoom": ((flags >> 24) & 0x40) != 0,
|
||||
"unusedFlag": ((flags >> 24) & 0x80) != 0,
|
||||
"zPriority": utils.to_u16(layerDefData[12:]),
|
||||
"unkE": utils.to_u8(layerDefData[14:]),
|
||||
"unkF": utils.to_u8(layerDefData[15:]),
|
||||
}
|
||||
else:
|
||||
layerPool[off] = None
|
||||
|
||||
# NOTE: I am not sure if LayerDef{0, 0, 0, 0, 0, 0} comes always first
|
||||
# or it is just a coincidence due to the fact the first room of the
|
||||
# two stage I analalysed always contains an empty bg??
|
||||
rooms = []
|
||||
for i in range(0, roomCount):
|
||||
room = {}
|
||||
fg = layerPool[utils.to_u32(data[roomOff:])]
|
||||
if fg != None:
|
||||
room["fg"] = fg
|
||||
bg = layerPool[utils.to_u32(data[roomOff + 4 :])]
|
||||
if bg != None:
|
||||
room["bg"] = bg
|
||||
rooms.append(room)
|
||||
roomOff += 8
|
||||
|
||||
return rooms
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
def get_file_name(full_path):
|
||||
file_name = os.path.basename(full_path)
|
||||
exts = os.path.splitext(file_name)
|
||||
if len(exts) > 1 and len(exts[1]) > 0:
|
||||
return get_file_name(exts[0])
|
||||
return exts[0]
|
||||
|
||||
input_file_name = sys.argv[1]
|
||||
output_file_name = sys.argv[2]
|
||||
|
||||
with open(input_file_name, "r") as f_in:
|
||||
name = get_file_name(input_file_name)
|
||||
with open(output_file_name, "w") as f_out:
|
||||
generate_assembly_layers(f_out, name, f_in.read())
|
@ -1,85 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
||||
from splat.util import options, log
|
||||
from splat.segtypes.n64.segment import N64Segment
|
||||
import utils
|
||||
|
||||
item_size = 0xA # sizeof(LayoutEntity)
|
||||
|
||||
|
||||
def parse_layoutobj(data: bytearray) -> list:
|
||||
count = int(len(data) / item_size)
|
||||
items = []
|
||||
for i in range(0, count):
|
||||
item = {
|
||||
"x": utils.to_s16(data[i * item_size + 0 :]),
|
||||
"y": utils.to_s16(data[i * item_size + 2 :]),
|
||||
"entityId": utils.to_s16(data[i * item_size + 4 :]),
|
||||
"entityRoomIndex": utils.to_s16(data[i * item_size + 6 :]),
|
||||
"subId": utils.to_s16(data[i * item_size + 8 :]),
|
||||
}
|
||||
if item["x"] == -1 and item["y"] == -1:
|
||||
break
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
|
||||
def serialize_layoutobj(content: str) -> bytearray:
|
||||
def align(value: int, align: int):
|
||||
return value + (value % align)
|
||||
|
||||
def from_16(dst: bytearray, idx, value: int):
|
||||
dst[idx + 0] = value & 0xFF
|
||||
dst[idx + 1] = (value >> 8) & 0xFF
|
||||
|
||||
obj = json.loads(content)
|
||||
item_count = len(obj)
|
||||
byte_size = align((item_count + 1) * item_size, 4)
|
||||
data = bytearray(byte_size)
|
||||
for i in range(0, item_count):
|
||||
item = obj[i]
|
||||
from_16(data, i * item_size + 0, item["x"])
|
||||
from_16(data, i * item_size + 2, item["y"])
|
||||
from_16(data, i * item_size + 4, item["entityId"])
|
||||
from_16(data, i * item_size + 6, item["entityRoomIndex"])
|
||||
from_16(data, i * item_size + 8, item["subId"])
|
||||
from_16(data, item_count * item_size + 0, -1)
|
||||
from_16(data, item_count * item_size + 2, -1)
|
||||
return data
|
||||
|
||||
|
||||
class PSXSegLayoutobj(N64Segment):
|
||||
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml):
|
||||
super().__init__(rom_start, rom_end, type, name, vram_start, args, yaml),
|
||||
|
||||
def out_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / self.name
|
||||
|
||||
def src_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}.layoutobj.json"
|
||||
|
||||
def split(self, rom_bytes):
|
||||
path = self.src_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = parse_layoutobj(rom_bytes[self.rom_start : self.rom_end])
|
||||
with open(path, "w") as f:
|
||||
f.write(json.dumps(data, indent=4))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file_name = sys.argv[1]
|
||||
output_file_name = sys.argv[2]
|
||||
|
||||
with open(input_file_name, "r") as f_in:
|
||||
data = serialize_layoutobj(f_in.read())
|
||||
with open(output_file_name, "wb") as f_out:
|
||||
f_out.write(data)
|
@ -1,97 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
||||
from splat.util import options, log
|
||||
from splat.segtypes.n64.segment import N64Segment
|
||||
|
||||
item_size = 0x8 # sizeof(RoomHeader)
|
||||
|
||||
|
||||
def serialize_roomdef(content: str) -> bytearray:
|
||||
obj = json.loads(content)
|
||||
item_count = len(obj)
|
||||
byte_size = item_count * item_size + 4
|
||||
data = bytearray(byte_size)
|
||||
for i in range(0, item_count):
|
||||
item = obj[i]
|
||||
data[i * item_size + 0] = item["left"]
|
||||
data[i * item_size + 1] = item["top"]
|
||||
data[i * item_size + 2] = item["right"]
|
||||
data[i * item_size + 3] = item["bottom"]
|
||||
data[i * item_size + 4] = item["tileLayoutId"]
|
||||
data[i * item_size + 5] = item["tilesetId"]
|
||||
data[i * item_size + 6] = item["objGfxId"]
|
||||
data[i * item_size + 7] = item["objLayoutId"]
|
||||
data[byte_size - 4] = 0x40 # marks the end of the room array
|
||||
return data
|
||||
|
||||
|
||||
class PSXSegRoomdef(N64Segment):
|
||||
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml):
|
||||
super().__init__(rom_start, rom_end, type, name, vram_start, args, yaml),
|
||||
|
||||
def out_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / self.name
|
||||
|
||||
def src_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}.roomdef.json"
|
||||
|
||||
def split(self, rom_bytes):
|
||||
path = self.src_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = self.parse_roomdef(rom_bytes[self.rom_start : self.rom_end])
|
||||
with open(path, "w") as f:
|
||||
f.write(json.dumps(data, indent=4))
|
||||
|
||||
def parse_roomdef(self, data: bytearray) -> list:
|
||||
count = int(len(data) / item_size)
|
||||
expected_data_size = count * item_size + 4
|
||||
if len(data) != expected_data_size:
|
||||
log.write(
|
||||
f"data for '{self.name}' is {expected_data_size - len(data)} too long. Data might look incorrect.",
|
||||
status="warn",
|
||||
)
|
||||
|
||||
items = []
|
||||
for i in range(0, count):
|
||||
if data[i * item_size + 0] == 64:
|
||||
log.write(
|
||||
f"data for '{self.name}' includes the array terminator. Try reducing the size of the subsegment.",
|
||||
status="warn",
|
||||
)
|
||||
item = {
|
||||
"left": data[i * item_size + 0],
|
||||
"top": data[i * item_size + 1],
|
||||
"right": data[i * item_size + 2],
|
||||
"bottom": data[i * item_size + 3],
|
||||
"tileLayoutId": data[i * item_size + 4],
|
||||
"tilesetId": data[i * item_size + 5],
|
||||
"objGfxId": data[i * item_size + 6],
|
||||
"objLayoutId": data[i * item_size + 7],
|
||||
}
|
||||
items.append(item)
|
||||
|
||||
if data[count * item_size] != 64:
|
||||
log.write(
|
||||
f"data for '{self.name}' does not end with a terminator.", status="warn"
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
input_file_name = sys.argv[1]
|
||||
output_file_name = sys.argv[2]
|
||||
|
||||
with open(input_file_name, "r") as f_in:
|
||||
data = serialize_roomdef(f_in.read())
|
||||
with open(output_file_name, "wb") as f_out:
|
||||
f_out.write(data)
|
@ -1,129 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import ctypes
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
||||
from splat.util import options
|
||||
from splat.segtypes.n64.segment import N64Segment
|
||||
from splat.util.symbols import spim_context
|
||||
import utils
|
||||
|
||||
|
||||
def generate_assembly_tiledef(
|
||||
writer: io.BufferedWriter, base_dir: str, name: str, content: str
|
||||
):
|
||||
def incbin(writer: io.BufferedWriter, path: str):
|
||||
with open(path, "rb") as f:
|
||||
for ch in f.read():
|
||||
writer.write(f".byte {int(ch)}\n")
|
||||
|
||||
obj = json.loads(content)
|
||||
symbol_name = obj["name"]
|
||||
gfxPage = obj["gfxPage"]
|
||||
gfxIndex = obj["gfxIndex"]
|
||||
clut = obj["clut"]
|
||||
collision = obj["collision"]
|
||||
|
||||
writer.write(".section .data\n")
|
||||
writer.write(f"gfxPage:\n")
|
||||
incbin(writer, f"{base_dir}/{gfxPage}")
|
||||
writer.write(f"gfxIndex:\n")
|
||||
incbin(writer, f"{base_dir}/{gfxIndex}")
|
||||
writer.write(f"clut:\n")
|
||||
incbin(writer, f"{base_dir}/{clut}")
|
||||
writer.write(f"collision:\n")
|
||||
incbin(writer, f"{base_dir}/{collision}")
|
||||
|
||||
writer.write(f".global {symbol_name}\n")
|
||||
writer.write(f"{symbol_name}:\n")
|
||||
writer.write(f".word gfxPage, gfxIndex, clut, collision\n")
|
||||
|
||||
|
||||
class PSXSegTiledef(N64Segment):
|
||||
def __init__(self, rom_start, rom_end, type, name, vram_start, args, yaml):
|
||||
super().__init__(rom_start, rom_end, type, name, vram_start, args, yaml),
|
||||
|
||||
def out_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}"
|
||||
|
||||
def src_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}.tiledef.json"
|
||||
|
||||
def split(self, rom_bytes):
|
||||
if len(self.args) != 1:
|
||||
utils.log_fatal(f"unable to determine the real symbol name for {self.name}")
|
||||
symbol_name = self.args[0]
|
||||
|
||||
out_file_name = self.src_path()
|
||||
out_file_name.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
header_data = rom_bytes[self.rom_end - 0x10 : self.rom_end]
|
||||
gfxPage = utils.to_u32(header_data[0:]) - self.vram_start
|
||||
gfxIndex = utils.to_u32(header_data[4:]) - self.vram_start
|
||||
clut = utils.to_u32(header_data[8:]) - self.vram_start
|
||||
collision = utils.to_u32(header_data[12:]) - self.vram_start
|
||||
header_start = self.rom_end - 0x10 - self.rom_start
|
||||
|
||||
expected_start = min(gfxPage, min(gfxIndex, min(clut, collision)))
|
||||
if expected_start != 0:
|
||||
expected_start += self.rom_start
|
||||
actual_start = self.rom_start
|
||||
utils.log_fatal(
|
||||
f"{self.name} should start from 0x{expected_start:X} but got 0x{actual_start:X}\n"
|
||||
f"- [0x{expected_start:X}, tiledef, {self.name}]"
|
||||
)
|
||||
|
||||
descriptor = {
|
||||
"name": symbol_name,
|
||||
"gfxPage": f"{self.name}.page.bin",
|
||||
"gfxIndex": f"{self.name}.tile.bin",
|
||||
"clut": f"{self.name}.clut.bin",
|
||||
"collision": f"{self.name}.col.bin",
|
||||
}
|
||||
|
||||
file_sizes = [
|
||||
gfxIndex - gfxPage,
|
||||
clut - gfxIndex,
|
||||
collision - clut,
|
||||
header_start - collision,
|
||||
]
|
||||
|
||||
raw_data = rom_bytes[self.rom_start : self.rom_end]
|
||||
out_dir = options.opts.asset_path / self.dir
|
||||
with open(out_dir / descriptor["gfxPage"], "wb") as f:
|
||||
f.write(raw_data[gfxPage:][: file_sizes[0]])
|
||||
with open(out_dir / descriptor["gfxIndex"], "wb") as f:
|
||||
f.write(raw_data[gfxIndex:][: file_sizes[1]])
|
||||
with open(out_dir / descriptor["clut"], "wb") as f:
|
||||
f.write(raw_data[clut:][: file_sizes[2]])
|
||||
with open(out_dir / descriptor["collision"], "wb") as f:
|
||||
f.write(raw_data[collision:][: file_sizes[3]])
|
||||
|
||||
with open(out_file_name, "w") as f:
|
||||
f.write(json.dumps(descriptor, indent=4))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
def get_file_name(full_path):
|
||||
file_name = os.path.basename(full_path)
|
||||
exts = os.path.splitext(file_name)
|
||||
if len(exts) > 1 and len(exts[1]) > 0:
|
||||
return get_file_name(exts[0])
|
||||
return exts[0]
|
||||
|
||||
input_file_name = sys.argv[1]
|
||||
output_file_name = sys.argv[2]
|
||||
|
||||
with open(input_file_name, "r") as f_in:
|
||||
base_path = os.path.dirname(input_file_name)
|
||||
name = get_file_name(input_file_name)
|
||||
with open(output_file_name, "w") as f_out:
|
||||
generate_assembly_tiledef(f_out, base_path, name, f_in.read())
|
@ -1,15 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
from splat.util import options
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/{options.opts.extensions_path}")
|
||||
from raw import PSXSegRaw
|
||||
|
||||
|
||||
class PSXSegTilelayout(PSXSegRaw):
|
||||
pass
|
||||
|
||||
def src_path(self) -> Optional[Path]:
|
||||
return options.opts.asset_path / self.dir / f"{self.name}.tilelayout.bin"
|
Loading…
Reference in New Issue
Block a user