mirror of
https://github.com/Xeeynamo/sotn-decomp.git
synced 2024-10-07 02:24:01 +00:00
Accessories extracted and building (#264)
Like Equipment, this will take the accessories, extract to a json in the assets directory, and then when building the game, read items from that json. Full list of changes required for this capability, and their reasons/purposes: Addition of entry in the makefile for the accessory json. This is literally just the equipment one copied and the name changed. Adjustment of splat extraction. Accessory data runs all the way up until a bunch of strings which are now in 8258.data.s. Previously, the file 8000.data.s was created, and was just a whole bunch of uninterpreted .word values. The accessory table runs all the way up to 0x8258. Happily, this means there is no longer any gap between understood and non-understood data in this region of the ROM. Accessory.py is extremely similar to the existing Equipment.py. As stated in the large comment at the top of this script, it would be neat if we could combine the two scripts to one script that would process all equippables in the game. For now, this is functional. Tidying this up could be a nice future task, especially for someone new to the project who is more comfortable with Python than C. Finally, the dictionary of punctuation in the utils.py has been expanded. The question mark shows up in the description of the Alucart Mail "Resists fire, lightning, ice?", while the plus signs show up in many items which provide stat bonuses that have descriptions like "DEF+15". These symbols were previously unknown. Feels very nice to now have equipment and accessories both extracting to human-editable forms!
This commit is contained in:
parent
69efed1321
commit
60293618e5
3
Makefile
3
Makefile
@ -528,6 +528,9 @@ $(BUILD_DIR)/$(ASSETS_DIR)/%.spriteparts.json.o: $(ASSETS_DIR)/%.spriteparts.jso
|
||||
$(BUILD_DIR)/$(ASSETS_DIR)/%.equipment.json.o: $(ASSETS_DIR)/%.equipment.json
|
||||
./tools/splat_ext/equipment.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)/%.accessory.json.o: $(ASSETS_DIR)/%.accessory.json
|
||||
./tools/splat_ext/accessory.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)/%.spritepartslist.json.o: $(ASSETS_DIR)/%.spritepartslist.json
|
||||
./tools/splat_ext/spritepartslist.py $< $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
$(AS) $(AS_FLAGS) -o $(BUILD_DIR)/$(ASSETS_DIR)/$*.o $(BUILD_DIR)/$(ASSETS_DIR)/$*.s
|
||||
|
@ -35,8 +35,8 @@ segments:
|
||||
- [0x3C40, data]
|
||||
- [0x4A00, data]
|
||||
- [0x4B04, equipment, equipments]
|
||||
- [0x7718, raw, accessory_data]
|
||||
- [0x8000, data]
|
||||
- [0x7718, accessory, accessory_data]
|
||||
- [0x8258, data]
|
||||
- [0x8900, data]
|
||||
- [0xCEB0, data]
|
||||
- [0xD670, rodata] # not rodata but data
|
||||
|
107
tools/splat_ext/accessory.py
Executable file
107
tools/splat_ext/accessory.py
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
#Note: This file was created by bismurphy. It is effectively the same as
|
||||
#equipment.py, just with the current best-known state of the accessory
|
||||
#struct subbed into the two locations where it comes up.
|
||||
#Longer term, would be cool if equipment and accessories could be parsed
|
||||
#by the same Python script, rather than tons of duplicate code.
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(f"{os.getcwd()}/tools/n64splat")
|
||||
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
||||
from util import options, log
|
||||
from segtypes.n64.segment import N64Segment
|
||||
import utils
|
||||
|
||||
item_size = 0x20 # sizeof(Accessory)
|
||||
|
||||
|
||||
def serialize_accessory(content: str) -> bytearray:
|
||||
obj = json.loads(content)
|
||||
item_count = len(obj)
|
||||
serialized_data = bytearray()
|
||||
for i in range(0, item_count):
|
||||
item = obj[i]
|
||||
serialized_data += utils.from_ptr_str(item["name_addr"])
|
||||
serialized_data += utils.from_ptr_str(item["desc_addr"])
|
||||
serialized_data += utils.from_u32(item["unk08"])
|
||||
serialized_data += utils.from_u32(item["unk0C"])
|
||||
serialized_data += utils.from_u32(item["unk10"])
|
||||
serialized_data += utils.from_u32(item["unk14"])
|
||||
serialized_data += utils.from_16(item["menuIcon"])
|
||||
serialized_data += utils.from_16(item["menuPalette"])
|
||||
serialized_data += utils.from_u32(item["unk1C"])
|
||||
|
||||
expected_data_size = item_count * item_size
|
||||
assert (len(serialized_data) == expected_data_size)
|
||||
|
||||
return serialized_data
|
||||
|
||||
|
||||
class PSXSegAccessory(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}.accessory.json"
|
||||
|
||||
def split(self, rom_bytes):
|
||||
path = self.src_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = self.parse_accessory(
|
||||
rom_bytes[self.rom_start:self.rom_end], rom_bytes)
|
||||
with open(path, "w") as f:
|
||||
f.write(json.dumps(data, indent=4))
|
||||
|
||||
def parse_accessory(self, data: bytearray, rom: bytearray) -> list:
|
||||
def get_ptr_data(src_ptr_data):
|
||||
return rom[utils.to_u32(src_ptr_data) - (self.vram_start - self.rom_start):]
|
||||
|
||||
count = int(len(data) / item_size)
|
||||
expected_data_size = count * item_size
|
||||
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):
|
||||
item_data = data[i * item_size:][:item_size]
|
||||
item = {
|
||||
# debugging stuff
|
||||
"id": i,
|
||||
"id_hex": hex(i)[2:].upper(),
|
||||
"ram_addr": hex(self.vram_start + i * item_size)[2:].upper(),
|
||||
"name_resolved": utils.sotn_menu_name_to_str(get_ptr_data(item_data[0x00:])),
|
||||
"desc_resolved": utils.sotn_menu_desc_to_str(get_ptr_data(item_data[0x04:])),
|
||||
# debugging stuff ends
|
||||
|
||||
"name_addr": utils.to_ptr_str(item_data[0x00:]),
|
||||
"desc_addr": utils.to_ptr_str(item_data[0x04:]),
|
||||
"unk08": utils.to_u32(item_data[0x08:]),
|
||||
"unk0C": utils.to_u32(item_data[0x0C:]),
|
||||
"unk10": utils.to_u32(item_data[0x10:]),
|
||||
"unk14": utils.to_u32(item_data[0x14:]),
|
||||
"menuIcon": utils.to_u16(item_data[0x18:]),
|
||||
"menuPalette": utils.to_u16(item_data[0x1A:]),
|
||||
"unk1C": utils.to_u32(item_data[0x1C:]),
|
||||
}
|
||||
items.append(item)
|
||||
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_accessory(f_in.read())
|
||||
with open(output_file_name, "wb") as f_out:
|
||||
f_out.write(data)
|
@ -3,6 +3,7 @@ import ctypes
|
||||
subchar81_dict = {
|
||||
0x44: 0x2E, # '.'
|
||||
0x43: 0x2C, # ','
|
||||
0x48: 0x3F, # '?'
|
||||
0x49: 0x21, # '!'
|
||||
0x66: 0x27, # '''
|
||||
0x68: 0x22, # '"'
|
||||
@ -10,6 +11,7 @@ subchar81_dict = {
|
||||
0x6A: 0x29, # ')'
|
||||
0x6D: 0x5B, # '['
|
||||
0x6E: 0x5D, # ']'
|
||||
0x7B: 0x2B, # '+'
|
||||
0x7C: 0x2D, # '-'
|
||||
0x93: 0x25 # '%'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user