add default GC patches
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import collections
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
version_tokens = ("JP12", "JP13", "JP14", "JP15", "US10", "US11", "US12", "EU")
|
||||
version_to_specific_version = {
|
||||
"JP12": "3OJ2",
|
||||
"JP13": "3OJ3",
|
||||
"JP14": "3OJ4",
|
||||
"JP15": "3OJ5",
|
||||
"US10": "3OE0",
|
||||
"US11": "3OE1",
|
||||
"US12": "3OE2",
|
||||
"EU": "3OP0",
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class WriteRegion:
|
||||
address: int
|
||||
data: list[int]
|
||||
|
||||
|
||||
def disassemble_opcode(opcode: int, start_address: int) -> str:
|
||||
try:
|
||||
result = subprocess.check_output(
|
||||
[
|
||||
"m68kdasm",
|
||||
f"--start-address={hex(start_address)}",
|
||||
"--ppc32",
|
||||
"--parse-data",
|
||||
],
|
||||
input=f"{opcode:08X}".encode("ascii"),
|
||||
)
|
||||
return result.decode("ascii").strip().split(None, 2)[2]
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def write_patches_for_code(
|
||||
out_dir: str,
|
||||
name: str,
|
||||
version_to_lines: dict[str, dict[int, int]],
|
||||
long_name: str | None,
|
||||
desc: str | None,
|
||||
) -> None:
|
||||
for v, lines in version_to_lines.items():
|
||||
write_regions: list[WriteRegion] = []
|
||||
for addr, value in sorted(lines.items()):
|
||||
if write_regions and (
|
||||
write_regions[-1].address + len(write_regions[-1].data) * 4 == addr
|
||||
):
|
||||
write_regions[-1].data.append(value)
|
||||
else:
|
||||
write_regions.append(WriteRegion(address=addr, data=[value]))
|
||||
|
||||
if write_regions:
|
||||
filename = os.path.join(
|
||||
out_dir,
|
||||
f'{name.replace(" ", "")}.{version_to_specific_version[v]}.patch.s',
|
||||
)
|
||||
with open(filename, "wt") as f:
|
||||
if long_name is not None:
|
||||
f.write(f'.meta name="{long_name}"\n')
|
||||
if desc is not None:
|
||||
f.write(f'.meta description="{desc}"\n')
|
||||
f.write("\n")
|
||||
f.write("entry_ptr:\n")
|
||||
f.write("reloc0:\n")
|
||||
f.write(" .offsetof start\n")
|
||||
f.write("start:\n")
|
||||
f.write(" .include WriteCodeBlocks\n")
|
||||
for region in write_regions:
|
||||
f.write(
|
||||
f" # region @ {region.address:08X} ({len(region.data) * 4} bytes)\n"
|
||||
)
|
||||
f.write(f" .data 0x{region.address:08X} # address\n")
|
||||
f.write(f" .data 0x{(len(region.data) * 4):08X} # size\n")
|
||||
for z, value in enumerate(region.data):
|
||||
addr = region.address + (z * 4)
|
||||
disassembly = disassemble_opcode(value, addr)
|
||||
f.write(
|
||||
f" .data 0x{value:08X} # {addr:08X} => {disassembly}\n"
|
||||
)
|
||||
f.write(" # end sentinel\n")
|
||||
f.write(" .data 0x00000000 # address\n")
|
||||
f.write(" .data 0x00000000 # size\n")
|
||||
print(f"... {filename}")
|
||||
else:
|
||||
print(f"*** {filename} (no data to write)")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
raise RuntimeError(
|
||||
"Usage: python3 generate-patches.py <source-filename> <out-dir>"
|
||||
)
|
||||
src_file = sys.argv[1]
|
||||
out_dir = sys.argv[2]
|
||||
|
||||
with open(src_file, "rt") as f:
|
||||
lines = f.read().splitlines()
|
||||
|
||||
reading_code = False
|
||||
reading_patch = False
|
||||
code_name = ""
|
||||
version_name = ""
|
||||
name_to_version_to_lines = collections.defaultdict(
|
||||
lambda: collections.defaultdict(dict)
|
||||
) # {name:{version: {addr: value}}}
|
||||
name_to_long_name = {}
|
||||
name_to_description = {}
|
||||
for line in lines:
|
||||
if not line:
|
||||
reading_code = False
|
||||
reading_patch = False
|
||||
elif reading_code:
|
||||
for z, v in enumerate(version_tokens):
|
||||
addr_str = line[18 * z : 18 * z + 8]
|
||||
value_str = line[18 * z + 9 : 18 * z + 17]
|
||||
if addr_str != " " and value_str != " ":
|
||||
addr = int(addr_str, 16)
|
||||
if addr in name_to_version_to_lines[code_name][v]:
|
||||
raise ValueError(f"duplicate write to address {addr:08X}")
|
||||
name_to_version_to_lines[code_name][v][addr] = int(value_str, 16)
|
||||
elif line.startswith("*** name="):
|
||||
name_to_long_name[code_name] = line[9:]
|
||||
elif line.startswith("*** desc="):
|
||||
name_to_description[code_name] = line[9:]
|
||||
elif line.startswith("======== PsoV3-"):
|
||||
reading_patch = True
|
||||
version_name = line[15:].split(".")[0]
|
||||
elif reading_patch:
|
||||
addr_str, data_str = line.split()
|
||||
addr = int(addr_str, 16)
|
||||
data = bytes.fromhex(data_str)
|
||||
for z in range(0, len(data), 4):
|
||||
name_to_version_to_lines[code_name][version_name][addr + z] = (
|
||||
(data[z] << 24)
|
||||
| (data[z + 1] << 16)
|
||||
| (data[z + 2] << 8)
|
||||
| (data[z + 3] << 0)
|
||||
)
|
||||
elif line.startswith("JP12------------"):
|
||||
reading_code = True
|
||||
else:
|
||||
code_name = line
|
||||
|
||||
for name, version_to_lines in name_to_version_to_lines.items():
|
||||
write_patches_for_code(
|
||||
out_dir,
|
||||
name,
|
||||
version_to_lines,
|
||||
name_to_long_name.get(name),
|
||||
name_to_description.get(name),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user