sotn-decomp/tools/splat_ext/utils.py

130 lines
2.6 KiB
Python
Raw Permalink Normal View History

2023-03-18 14:16:56 +00:00
import ctypes
import os
import sys
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
from splat.util import log
2023-03-18 14:16:56 +00:00
subchar81_dict = {
0x44: 0x2E, # '.'
0x43: 0x2C, # ','
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!
2023-06-19 18:12:20 +00:00
0x48: 0x3F, # '?'
2023-03-18 14:16:56 +00:00
0x49: 0x21, # '!'
0x66: 0x27, # '''
0x68: 0x22, # '"'
0x69: 0x28, # '('
0x6A: 0x29, # ')'
0x6D: 0x5B, # '['
0x6E: 0x5D, # ']'
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!
2023-06-19 18:12:20 +00:00
0x7B: 0x2B, # '+'
2023-03-18 14:16:56 +00:00
0x7C: 0x2D, # '-'
2023-07-25 17:38:30 +00:00
0x93: 0x25, # '%'
2023-03-18 14:16:56 +00:00
}
def log_fatal(msg):
log.write(msg, status="error")
raise Exception(msg)
def log_warn(msg):
log.write(msg, status="warn")
2023-03-18 14:16:56 +00:00
def from_s32(num):
2023-07-25 17:38:30 +00:00
return num.to_bytes(4, byteorder="little", signed=True)
2023-03-18 14:16:56 +00:00
def to_s32(data: bytearray):
2023-07-25 17:38:30 +00:00
return int.from_bytes(data[:4], byteorder="little", signed=True)
2023-03-18 14:16:56 +00:00
def from_u32(num):
2023-07-25 17:38:30 +00:00
return bytes([(num >> i) & 0xFF for i in range(0, 32, 8)])
2023-03-18 14:16:56 +00:00
def to_u32(data: bytearray):
2023-03-18 14:16:56 +00:00
return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)
def from_16(num):
2023-07-25 17:38:30 +00:00
return bytes([num & 0xFF, (num >> 8) & 0xFF])
2023-03-18 14:16:56 +00:00
def to_s16(data: bytearray):
2023-03-18 14:16:56 +00:00
return ctypes.c_int16(data[0] | (data[1] << 8)).value
def to_u16(data: bytearray):
2023-03-18 14:16:56 +00:00
return data[0] | (data[1] << 8)
2023-07-25 17:38:30 +00:00
def from_s8(num):
if num < 0:
num += 256
return bytes([num])
2023-07-25 17:38:30 +00:00
def to_s8(data: bytearray):
raw = data[0]
if raw >= 128:
return raw - 256
return raw
2023-03-18 14:16:56 +00:00
2023-07-25 17:38:30 +00:00
2023-03-18 14:16:56 +00:00
def from_u8(num):
return bytes([num])
def to_u8(data: bytearray):
2023-03-18 14:16:56 +00:00
return data[0]
def from_bool(val):
return bytes([int(val)])
def to_bool(data):
return bool(data[0])
def from_ptr_str(ptr_str):
return from_u32(int(ptr_str[2:], 16))
2023-03-18 14:16:56 +00:00
def to_ptr_str(data):
return f"0x{to_u32(data):08X}"
def sotn_menu_name_to_str(data: bytearray) -> str:
end_of_str = data.find(0xFF)
2023-07-25 17:38:30 +00:00
assert end_of_str >= 0
2023-03-18 14:16:56 +00:00
if end_of_str == 0:
return ""
utf8 = bytearray(data[:end_of_str])
for i in range(len(utf8)):
utf8[i] += 0x20
2023-07-25 17:38:30 +00:00
return utf8.decode("utf-8")
2023-03-18 14:16:56 +00:00
def sotn_menu_desc_to_str(data: bytearray) -> str:
utf8 = []
while data[0] not in {0xFF, 0x00}:
if data[0] == 0x81:
ch = subchar81_dict.get(data[1])
if ch is None:
2023-07-25 17:38:30 +00:00
raise Exception(f"subchar {data[0]:02X} {data[1]:02X} not recognised")
2023-03-18 14:16:56 +00:00
data = data[2:]
elif data[0] == 0x82:
if 0x4F <= data[1] <= 0x58:
ch = data[1] - 0x4F + 0x30 # from '0' to '9'
else:
2023-07-25 17:38:30 +00:00
raise Exception(f"subchar {data[0]:02X} {data[1]:02X} not recognised")
2023-03-18 14:16:56 +00:00
data = data[2:]
else:
ch = data[0]
data = data[1:]
utf8.append(ch)
2023-07-25 17:38:30 +00:00
return bytes(utf8).decode("utf-8")