2023-03-18 14:16:56 +00:00
|
|
|
import ctypes
|
2023-08-22 02:24:44 +00:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
sys.path.append(f"{os.getcwd()}/tools/splat_ext")
|
2024-06-12 17:50:32 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-08-22 02:24:44 +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
|
|
|
|
|
|
|
|
2023-03-31 07:41:52 +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
|
|
|
|
|
|
|
|
2023-03-31 07:41:52 +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
|
|
|
|
|
|
|
|
2023-03-31 07:41:52 +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
|
|
|
|
|
|
|
|
|
2023-03-31 07:41:52 +00:00
|
|
|
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
|
|
|
|
DRA func_800F4D38 match + document Accessory fields (#276)
This started with simply decompiling func_800F4D38. But then I found it
referencing an unk08 within Accessory, which I now know to be attBonus.
I ended up going through accessory.json (which only just recently
started existing, with one of my other pull requests) and classified all
the equippables that change the player's stats. This includes, for
example, the Gauntlet which gives ATT+5, or Lapis Lazuli which gives LCK
+20.
Therefore this PR does not really address only a single task, since it
now extracts all the accessory fields. However, because they were not
extracted previously, there is no existing code which references these
fields of Accessory, and therefore there should be no issues with this.
Note that the decompilation references Equipment.damageScale; my other
current pending PR renames that field to itemCategory, and therefore
these two PRs will have a small conflict. Hopefully this is not too much
of an issue.
Finally, func_800F4D38 has been renamed as CalcAttack. This is because
it calculates how much Attack power the player has, taking into account
the equipped weapon, any stat-boosting armor, etc. All references to
this function have been changed to match.
2023-06-26 19:32:42 +00:00
|
|
|
def from_s8(num):
|
|
|
|
if num < 0:
|
|
|
|
num += 256
|
|
|
|
return bytes([num])
|
|
|
|
|
2023-07-25 17:38:30 +00:00
|
|
|
|
DRA func_800F4D38 match + document Accessory fields (#276)
This started with simply decompiling func_800F4D38. But then I found it
referencing an unk08 within Accessory, which I now know to be attBonus.
I ended up going through accessory.json (which only just recently
started existing, with one of my other pull requests) and classified all
the equippables that change the player's stats. This includes, for
example, the Gauntlet which gives ATT+5, or Lapis Lazuli which gives LCK
+20.
Therefore this PR does not really address only a single task, since it
now extracts all the accessory fields. However, because they were not
extracted previously, there is no existing code which references these
fields of Accessory, and therefore there should be no issues with this.
Note that the decompilation references Equipment.damageScale; my other
current pending PR renames that field to itemCategory, and therefore
these two PRs will have a small conflict. Hopefully this is not too much
of an issue.
Finally, func_800F4D38 has been renamed as CalcAttack. This is because
it calculates how much Attack power the player has, taking into account
the equipped weapon, any stat-boosting armor, etc. All references to
this function have been changed to match.
2023-06-26 19:32:42 +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])
|
|
|
|
|
|
|
|
|
2023-03-31 07:41:52 +00:00
|
|
|
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):
|
2023-03-31 07:41:52 +00:00
|
|
|
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")
|