You've already forked PythonLib
mirror of
https://github.com/lifebottle/PythonLib.git
synced 2026-02-13 15:25:50 -08:00
sfm2 ductape
This commit is contained in:
15
Tales_Exe.py
15
Tales_Exe.py
@@ -41,10 +41,10 @@ def get_arguments(argv=None):
|
||||
sp_extract.add_argument(
|
||||
"-ft",
|
||||
"--file_type",
|
||||
choices=["Iso", "Main", "Menu", "Story", "Skits"],
|
||||
choices=["Iso", "Main", "Menu", "Story", "Minigame", "Skits"],
|
||||
required=True,
|
||||
metavar="file_type",
|
||||
help="(Required) - Options: Iso, Init, Main, Menu, Story, Skits",
|
||||
help="(Required) - Options: Iso, Init, Main, Menu, Story, Minigame, Skits",
|
||||
)
|
||||
|
||||
sp_extract.add_argument(
|
||||
@@ -80,10 +80,10 @@ def get_arguments(argv=None):
|
||||
sp_insert.add_argument(
|
||||
"-ft",
|
||||
"--file_type",
|
||||
choices=["Iso", "Main", "Menu", "Story", "Skits", "All", "Asm"],
|
||||
choices=["Iso", "Main", "Menu", "Story", "Skits", "Minigame", "All", "Asm"],
|
||||
required=True,
|
||||
metavar="file_type",
|
||||
help="(Required) - Options: Iso, Init, Main, Elf, Story, Skits, All, Asm",
|
||||
help="(Required) - Options: Iso, Init, Main, Elf, Story, Skits, Minigame, All, Asm",
|
||||
)
|
||||
|
||||
sp_insert.add_argument(
|
||||
@@ -169,6 +169,9 @@ if __name__ == "__main__":
|
||||
|
||||
elif args.file_type == "Story":
|
||||
tales_instance.pack_all_story()
|
||||
|
||||
elif args.file_type == "Minigame":
|
||||
tales_instance.pack_all_minigame()
|
||||
|
||||
elif args.file_type == "Iso":
|
||||
tales_instance.make_iso()
|
||||
@@ -186,6 +189,7 @@ if __name__ == "__main__":
|
||||
tales_instance.pack_all_story()
|
||||
tales_instance.pack_all_skits()
|
||||
tales_instance.pack_all_menu()
|
||||
tales_instance.pack_all_minigame()
|
||||
tales_instance.patch_binaries()
|
||||
tales_instance.make_iso()
|
||||
|
||||
@@ -204,5 +208,8 @@ if __name__ == "__main__":
|
||||
if args.file_type == "Story":
|
||||
tales_instance.extract_all_story()
|
||||
|
||||
if args.file_type == "Minigame":
|
||||
tales_instance.extract_all_minigame()
|
||||
|
||||
if args.file_type == "Skits":
|
||||
tales_instance.extract_all_skits(args.replace)
|
||||
|
||||
@@ -15,6 +15,7 @@ import pycdlib
|
||||
import pyjson5 as json
|
||||
from dulwich import line_ending, porcelain
|
||||
from tqdm import tqdm
|
||||
import io
|
||||
|
||||
import pythonlib.formats.pak2 as pak2lib
|
||||
import pythonlib.utils.comptolib as comptolib
|
||||
@@ -362,6 +363,7 @@ class ToolsTOR(ToolsTales):
|
||||
while True:
|
||||
b = src.read(1)
|
||||
if b == b"\x00": break
|
||||
if b == b"": break
|
||||
|
||||
b = ord(b)
|
||||
# Custom Encoded Text
|
||||
@@ -1102,7 +1104,163 @@ class ToolsTOR(ToolsTales):
|
||||
i += 1
|
||||
yield data + (b"\x00" * remainder), 1
|
||||
|
||||
def get_xml_from_list(self, lines: list[tuple[int, str]], section: str) -> bytes:
|
||||
|
||||
root = etree.Element("SceneText")
|
||||
strings_node = etree.SubElement(root, 'Strings')
|
||||
etree.SubElement(strings_node, 'Section').text = section
|
||||
|
||||
for line in lines:
|
||||
entry_node = etree.SubElement(strings_node, "Entry")
|
||||
etree.SubElement(entry_node,"PointerOffset").text = str(line[0])
|
||||
|
||||
text_split = list(filter(None, re.split(self.COMMON_TAG, line[1])))
|
||||
|
||||
if len(text_split) > 1 and text_split[0].startswith("<voice:"):
|
||||
etree.SubElement(entry_node,"VoiceId").text = text_split[0][1:-1].split(":")[1]
|
||||
etree.SubElement(entry_node, "JapaneseText").text = ''.join(text_split[1:])
|
||||
else:
|
||||
etree.SubElement(entry_node, "JapaneseText").text = line[1]
|
||||
|
||||
etree.SubElement(entry_node,"EnglishText")
|
||||
etree.SubElement(entry_node,"Notes")
|
||||
etree.SubElement(entry_node,"Id").text = str(self.id)
|
||||
|
||||
self.id = self.id + 1
|
||||
|
||||
if line[1] == '':
|
||||
statusText = 'Done'
|
||||
else:
|
||||
statusText = 'To Do'
|
||||
etree.SubElement(entry_node,"Status").text = statusText
|
||||
|
||||
# Return XML string
|
||||
return etree.tostring(root, encoding="UTF-8", pretty_print=True)
|
||||
|
||||
def extract_all_minigame(self, replace=False) -> None:
|
||||
print("Extracting Minigame files...")
|
||||
|
||||
valid_sfm2 = [
|
||||
1, 17, 30, 31, 34, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 45, 48, 53, 61
|
||||
]
|
||||
|
||||
folder_path = self.paths["translated_files"] / "minigame"
|
||||
folder_path.mkdir(exist_ok=True)
|
||||
pak3_path = self.paths["extracted_files"] / "DAT" / "PAK3" / "00023.pak3"
|
||||
|
||||
minigame_pak = Pak.from_path(pak3_path, 3)
|
||||
for index in tqdm(valid_sfm2):
|
||||
self.id = 0
|
||||
sfm = minigame_pak.files[index].data
|
||||
|
||||
lines = []
|
||||
|
||||
with FileIO(sfm, "rb") as f:
|
||||
# special case
|
||||
if index == 45:
|
||||
tbl_off = f.read_int32_at(0x20)
|
||||
f.seek(tbl_off)
|
||||
offsets = struct.unpack("<14I", f.read(0x38))[1::2]
|
||||
|
||||
for off in offsets:
|
||||
lines.append((off, self.bytes_to_text(f, tbl_off + off))) # fake offset
|
||||
|
||||
code_off = f.read_int32_at(0x18)
|
||||
code_end = code_off + f.read_int32_at(0xC)
|
||||
text_off = f.read_int32_at(0x1C)
|
||||
|
||||
f.seek(code_off)
|
||||
while f.tell() < code_end:
|
||||
b = f.read_int8()
|
||||
len = (b >> 4) & 0xF
|
||||
opcode = b & 0xF
|
||||
|
||||
# is it the load string opcode?
|
||||
if opcode == 7:
|
||||
if f.read_int8() == 2:
|
||||
off = f.tell()
|
||||
if len == 3:
|
||||
str_off = f.read_uint8()
|
||||
elif len == 4:
|
||||
str_off = f.read_uint16()
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
save = f.tell()
|
||||
lines.append((off, self.bytes_to_text(f, str_off + text_off)))
|
||||
f.seek(save)
|
||||
else:
|
||||
f.read(len - 2)
|
||||
else:
|
||||
f.read(len - 1)
|
||||
|
||||
|
||||
xml_text = self.get_xml_from_list(lines, "Minigame")
|
||||
|
||||
xml_name = f"{index:04d}.xml"
|
||||
with open(folder_path / xml_name, "wb") as xml:
|
||||
xml.write(xml_text.replace(b"\n", b"\r\n"))
|
||||
|
||||
|
||||
def pack_all_minigame(self):
|
||||
print("Recreating Minigame files...")
|
||||
|
||||
valid_sfm2 = [
|
||||
1, 17, 30, 31, 34, 36, 37, 38,
|
||||
39, 40, 41, 42, 43, 45, 48, 53, 61
|
||||
]
|
||||
|
||||
pak3_path = self.paths["extracted_files"] / "DAT" / "PAK3" / "00023.pak3"
|
||||
out_path = self.paths["temp_files"] / "DAT" / "SCPK" / "00023"
|
||||
out_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
minigame_pak = Pak.from_path(pak3_path, 3)
|
||||
for index in tqdm(valid_sfm2):
|
||||
|
||||
sfm = minigame_pak.files[index].data
|
||||
|
||||
with FileIO(sfm, "rb") as f:
|
||||
new_text_offsets = dict()
|
||||
root = self.read_xml(folder_path / f"{index:04d}.xml")
|
||||
text_off = f.read_int32_at(0x1C)
|
||||
tbl_off = f.read_int32_at(0x20) + 4
|
||||
f.seek(text_off)
|
||||
f.truncate()
|
||||
|
||||
nodes = [ele for ele in root.iter('Entry')]
|
||||
nodes = [ele for ele in nodes if ele.find('PointerOffset').text != "-1"]
|
||||
|
||||
for entry_node in nodes:
|
||||
new_text_offsets[entry_node.find("PointerOffset").text] = f.tell()
|
||||
bytes_entry = self.get_node_bytes(entry_node)
|
||||
f.write(bytes_entry + b'\x00')
|
||||
|
||||
for i, (pointer, text_offset) in enumerate(new_text_offsets.items()):
|
||||
new_value = text_offset - text_off
|
||||
|
||||
if index == 45 and i < 7:
|
||||
f.write_uint32_at(tbl_off + (i * 8), new_value + 0x54)
|
||||
else:
|
||||
f.seek(int(pointer) - 2)
|
||||
len = (f.read_uint8() >> 4) & 0xF
|
||||
if len == 3:
|
||||
f.write(struct.pack("<B", new_value))
|
||||
elif len == 4:
|
||||
f.write(struct.pack("<H", new_value))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
f.seek(0, io.SEEK_END)
|
||||
file_size = f.tell()
|
||||
f.write_uint32_at(0x8, file_size)
|
||||
f.write_uint32_at(0x10, file_size - text_off)
|
||||
|
||||
f.seek(0)
|
||||
with open(out_path / f"{index:04d}.bin", "wb") as o:
|
||||
o.write(f.read())
|
||||
|
||||
|
||||
def pack_all_story(self):
|
||||
print("Recreating Story files...")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user