123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- from lxml import etree
- import file_ops, xml
- exec(open("bin_tool.py").read())
- def rtn_def_param_str(string, params_list):
-
-
- return file_ops.get_path_str(string.replace("{$__gameid}", params_list[0]).replace("{$__region}", params_list[1]).replace("{$__maker}", params_list[2]))
- def check_game_image(game_image_path):
- result = exec_subprocess([wit_path, "verify", "--test", game_image_path])
- if (result.returncode != 0):
- return False
- return True
-
- def check_game_xml_id(game_image_path, riiv_xml_id):
-
-
- game_id = exec_subprocess([wit_path, "ID6", game_image_path]).stdout.replace("\n", "")
-
-
-
-
-
-
- is_id_sec_valid = [False, False, False]
-
-
- if (riiv_xml_id[0] == game_id[:-3] or riiv_xml_id[0] == ""):
- is_id_sec_valid[0] = True
-
- if (len(riiv_xml_id[1]) == 0):
- is_id_sec_valid[1] = True
- else:
- for reg in riiv_xml_id[1]:
- if (reg == game_id[3:-2]):
- is_id_sec_valid[1] = True
-
- if (riiv_xml_id[2] == game_id[4:] or riiv_xml_id[2] == ""):
- is_id_sec_valid[2] = True
-
-
- if (False in is_id_sec_valid):
- return False
- return game_id
- def check_riiv_xml(xml_path):
-
-
- return xml.check_with_sch(xml_path, "MyRiivolution.xsd")
- def get_patch_elem_root_path(root_elem, patch_elem):
-
- root_path = ""
- if ("root" in root_elem.attrib):
- root_path = root_path + root_elem.attrib["root"]
-
- if ("root" in patch_elem.attrib):
- root_path = root_path + patch_elem.attrib["root"]
- return file_ops.get_path_str(root_path)
- def check_riiv_patches(game_path, xml_path, mod_files_folder):
-
-
-
- if (check_game_image(game_path) == False):
- print("Invalid ISO/WBFS")
- return False
- if (check_riiv_xml(xml_path) == False):
- print("Invalid Riivolution XML")
- return False
-
-
- riiv_game_id = ["", [], ""]
- xml = etree.parse(xml_path, etree.XMLParser(ns_clean = True, remove_comments = True))
- root = xml.getroot()
-
-
-
- id_elem = root.find("id")
- if (id_elem != None):
-
- if ("game" in id_elem.attrib):
-
- riiv_game_id[0] = id_elem.attrib["game"]
-
- if (len(riiv_game_id[0]) == 3):
- if (len(id_elem) != 0):
- for reg in id_elem:
- riiv_game_id[1].append(reg.attrib["type"])
- else:
- riiv_game_id[1].append(riiv_game_id[0][3:])
- riiv_game_id[0] = riiv_game_id[0][:-1]
-
- if ("developer" in id_elem.attrib):
- riiv_game_id[2] = id_elem.attrib["developer"]
-
-
- game_id = ["", "", ""]
- game_id[0] = check_game_xml_id(game_path, riiv_game_id)
- if (game_id == False):
- print("Riivolution XML is not compatible with the Wii game provided")
- return False
- game_id[1] = game_id[0][3:-2]
- game_id[2] = game_id[0][4:]
- game_id[0] = game_id[0][:-3]
-
-
-
-
- opt_patches_id = []
- for child in root:
- if (child.tag == "options"):
- for sec in child:
- for opt in sec:
- for choice in opt:
- for patch in choice:
- opt_patches_id.append(patch.attrib["id"])
- elems_patch_id = []
- for patch in root.findall("patch"):
- elems_patch_id.append(patch.attrib["id"])
-
- patch_elems = []
- for patch_id in opt_patches_id:
- if (patch_id not in elems_patch_id):
- print("Patch to apply from options does not exist in the XML")
- return False
-
- for patch in root.findall("patch"):
- if (patch.attrib["id"] == patch_id):
- patch_elems.append(patch)
-
-
-
-
- game_files = exec_subprocess([wit_path, "files", game_path]).stdout.split("\n")
-
- cont_loop = True
- while (cont_loop == True):
- for i in range(0, len(game_files)):
- if (("DATA/files" not in game_files[i]) and ("./files" not in game_files[i])):
- game_files.pop(i)
- cont_loop = True
- break
- cont_loop = False
-
- for i in range(0, len(game_files)):
- if ("DATA/files" in game_files[i]):
- game_files[i] = file_ops.get_path_str(game_files[i].replace("DATA/files", ""))
- else:
- game_files[i] = file_ops.get_path_str(game_files[i].replace("./files", ""))
-
-
-
-
-
-
- for patch in patch_elems:
-
- patch_root_path = rtn_def_param_str(file_ops.get_base_path(mod_files_folder, True) + "/"
- + get_patch_elem_root_path(root, patch), game_id)
-
-
-
- for patch_type in patch:
-
- if (patch_type.tag == "file"):
- fpath = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["external"], game_id)
- if (file_ops.f_exists(fpath) == False):
- print("Line %d: File patch has invalid external path" % (patch_type.sourceline))
- print(fpath)
- return False
- else:
- if (patch_type.attrib["disc"] not in game_files):
- if ("create" not in patch_type.attrib):
- print("Line %d: File patch creates non-existant disc file without create attribute" % (patch_type.sourceline))
- return False
- else:
- if (patch_type.attrib["create"] == "false"):
- print("Line %d: File patch creates non-existant disc file with create = False" % (patch_type.sourceline))
- return False
-
-
- if (patch_type.tag == "folder"):
- fpath = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["external"], game_id)
- if (file_ops.f_exists(fpath) == False):
- print("Line %d: Folder patch has invalid external path" % (patch_type.sourceline))
- print(fpath)
- return False
- if ("disc" in patch_type.attrib):
- if (rtn_def_param_str(patch_type.attrib["disc"], game_id) not in game_files):
-
- if ("create" in patch_type.attrib):
- if (patch_type.attrib["create"] == "False"):
- print("Line %d: Folder patch creates non-existant disc folder with create = False" % (patch_type.sourceline))
- return False
- else:
- print("Line %d: Folder patch creates non-existant disc folder without create attribute" % (patch_type.sourceline))
- return False
-
- if (patch_type.tag == "memory"):
- if ("valuefile" in patch_type.attrib):
- fpath = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["valuefile"], game_id)
- if (file_ops.f_exists(fpath) == False):
- print("Line %d: Memory patch has invalid paths")
- print(fpath)
- return False
-
- if ("0x8" not in patch_type.attrib["offset"]):
- print("Line %d: Memory patch tries patching to a range outside the 0x80000000 memory area" % (patch_type.sourceline))
- return False
-
- if ("original" in patch_type.attrib):
- if (file_ops.f_exists("ram_dumps/" + game_id[0] + game_id[1] + game_id[2] + ".bin") == False
- or file_ops.get_file_size("ram_dumps/" + game_id[0] + game_id[1] + game_id[2] + ".bin") != 0x1800000):
- print("Line %d: Memory element has original condition." % (patch_type.sourceline))
- print("No/Invalid ram dump of \"%s\" was found in ram_dumps/" % (game_id[0] + game_id[1] + game_id[2]))
- return False
-
-
-
-
-
-
-
-
- return True
- def check_riiv_patches_wrapper(game_path, xml_path, mod_files_folder):
-
-
- result = check_riiv_patches(game_path, xml_path, mod_files_folder)
- file_ops.rm_folder("tmp")
- return result
- def get_riiv_patches_inf(game_path, xml_path, mod_files_folder):
-
-
-
-
- if (check_riiv_patches_wrapper(game_path, xml_path, mod_files_folder) == False):
- return None
-
-
-
- class section:
- def __init__(self, name):
- self.name = name
- self.options = []
- class option:
- def __init__(self, name):
- self.name = name
- self.choices = []
- class choice:
- def __init__(self, name):
- self.name = name
- self.patches = []
- class patch:
- def __init__(self, id):
- self.id = id
-
-
-
- sections = []
- for sec in etree.parse(xml_path, etree.XMLParser(remove_comments = True)).find("options"):
- sections.append(section(sec.attrib["name"]))
- for opt in sec:
- sections[-1].options.append(option(opt.attrib["name"]))
- for cho in opt:
- sections[-1].options[-1].choices.append(choice(cho.attrib["name"]))
- for pat in cho:
- sections[-1].options[-1].choices[-1].patches.append(patch(pat.attrib["id"]))
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- return sections
- def apply_riiv_patches(game_path, xml_path, mod_files_folder, choice_str_list):
-
-
- if (check_riiv_patches_wrapper(game_path, xml_path, mod_files_folder) == False):
- return False
-
-
-
- xml = etree.parse(xml_path, etree.XMLParser(remove_comments = True))
- root = xml.getroot()
- choices_names = []
- for sec in root.find("options"):
- for opt in sec:
- for cho in opt:
- choices_names.append(cho.attrib["name"])
- for choice_str in choice_str_list:
- if (choice_str not in choices_names):
- print("Invalid choice selected: %s" % (choice_str))
- return False
-
-
-
-
- patches_ids = []
- for sec in root.find("options"):
- for opt in sec:
- for cho in opt:
- if (cho.attrib["name"] in choice_str_list):
- for patch in cho:
- patches_ids.append(patch.attrib["id"])
-
- patch_elems = []
- for patch in root.findall("patch"):
- if (patch.attrib["id"] in patches_ids):
- patch_elems.append(patch)
-
-
- game_id = ["", "", ""]
- game_id[0] = exec_subprocess([wit_path, "ID6", game_path]).stdout.replace("\n", "")
- game_id[1] = game_id[0][3:-2]
- game_id[2] = game_id[0][4:]
- game_id[0] = game_id[0][:-3]
-
-
-
-
- result = exec_subprocess([wit_path, "extract", game_path, "tmp/"]).returncode
- if (result != 0):
- print("wit could not extract correctly the game files")
- return False
-
-
- ext_game_path = "tmp/files/"
- if (file_ops.f_exists("tmp/DATA/")):
- ext_game_path = "tmp/DATA/files/"
-
-
- memory_original_exists = False
- for patch in patch_elems:
-
- patch_root_path = rtn_def_param_str(file_ops.get_base_path(mod_files_folder, True) + "/"
- + get_patch_elem_root_path(root, patch), game_id)
-
- for patch_type in patch:
-
- if (patch_type.tag == "file"):
- fpath1 = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["external"], game_id)
- fpath2 = rtn_def_param_str(ext_game_path + patch_type.attrib["disc"], game_id)
- file_ops.cp_file(fpath1, fpath2)
-
- if (patch_type.tag == "folder"):
-
- if ("disc" in patch_type.attrib):
- fpath1 = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["external"], game_id)
- fpath2 = rtn_def_param_str(ext_game_path + patch_type.attrib["disc"], game_id)
- file_ops.cp_folder(fpath1, fpath2, False)
-
- if (patch_type.tag == "memory"):
- if ("original" in patch_type.attrib):
- memory_original_exists = True
-
-
-
-
-
-
-
-
-
- ram_dump = None
- if (memory_original_exists):
- ram_dump = open("ram_dumps/" + game_id[0] + game_id[1] + game_id[2] + ".bin", "rb")
- tmp_gl = open("tmp_gl.txt", "w")
- tmp_wit = open("tmp_wit.xml", "w")
-
-
- for patch in patch_elems:
- patch_root_path = rtn_def_param_str(file_ops.get_base_path(mod_files_folder, True) + "/"
- + get_patch_elem_root_path(root, patch), game_id)
- for patch_type in patch:
-
- if (patch_type.tag != "memory"):
- continue
- offset_hex_str = patch_type.attrib["offset"][2:]
- offset_hex_int = int(offset_hex_str, 16)
-
-
- if ("original" in patch_type.attrib):
- og_hex = patch_type.attrib["original"]
- ram_dump.seek(offset_hex_int - 0x80000000)
- dmp_hex = ram_dump.read(int(len(og_hex) / 2)).hex()
-
- if (og_hex.upper() != dmp_hex.upper()):
- continue
-
- patch_type.attrib["original"] = patch_type.attrib["original"].replace("0x", "")
-
-
- if ("valuefile" in patch_type.attrib):
- val_file_path = patch_root_path + "/" + rtn_def_param_str(patch_type.attrib["valuefile"], game_id)
- tmp = open(val_file_path, "rb")
- del patch_type.attrib["valuefile"]
- patch_type.set("value", tmp.read(file_ops.get_file_size(val_file_path)).hex().upper())
- tmp.close()
- else:
-
- patch_type.attrib["value"] = patch_type.attrib["value"].replace("0x", "")
-
-
- if (offset_hex_int > 0x8000FFFF):
- tmp = etree.tostring(patch_type).decode("utf-8")
- while (tmp[0] in " \t\r\n"):
- tmp = tmp[1:]
- while (tmp[-1] in " \t\r\n"):
- tmp = tmp[:-1]
- tmp_wit.write(tmp + "\n")
-
- else:
- tmp = patch_type.attrib["value"]
- last_offset = offset_hex_int
- if (len(tmp) > 8):
- for i in range(0, int(len(tmp) / 8)):
- last_offset = offset_hex_int + (4 * i)
- if (len(tmp) > 8):
- tmp_gl.write("%08X %s\n" % (last_offset - 0x80000000 + 0x04000000, tmp[:-len(tmp) + 8]))
- else:
- tmp_gl.write("%08X %s\n" % (last_offset - 0x80000000 + 0x04000000, tmp))
- tmp = tmp[8:]
- last_offset += 4
-
- if (len(tmp) == 6):
- tmp_gl.write("%08X 0000%s\n" % (last_offset - 0x80000000 + 0x02000000, tmp[:-2]))
- tmp_gl.write("%08X 000000%s\n" % (last_offset + 2 - 0x80000000, tmp[4:]))
- elif (len(tmp) == 4):
- tmp_gl.write("%08X 0000%s\n" % (last_offset - 0x80000000 + 0x02000000, tmp))
- elif (len(tmp) == 2):
- tmp_gl.write("%08X 000000%s\n" % (last_offset - 0x80000000, tmp))
-
-
- tmp_wit.close()
- tmp_gl.close()
- if (memory_original_exists):
- ram_dump.close()
-
-
- dol_path = ext_game_path.replace("files/", "sys/main.dol")
- dol_tmp_path = dol_path.replace("main.dol", "main_tmp.dol")
-
-
-
- if (file_ops.get_file_size("tmp_gl.txt") != 0):
-
- result = exec_subprocess(geckoloader_path + [dol_path, "tmp_gl.txt", "--dest",
- dol_tmp_path, "--txtcodes", "ALL", "--optimize"]).returncode
- if (result != 0):
- print("geckoloader could not handle tmp_gl.txt properly")
- return False
- file_ops.cp_file(dol_tmp_path, dol_path)
- file_ops.rm_file(dol_tmp_path)
-
- if (file_ops.get_file_size("tmp_wit.xml") != 0):
-
- result = exec_subprocess([wit_path, "dolpatch", dol_path,
- "XML=tmp_wit.xml", "--dest", dol_tmp_path]).returncode
- if (result != 0):
- print("wit could not handle tmp_wit.xml properly")
- return False
- file_ops.cp_file(dol_tmp_path, dol_path)
- file_ops.rm_file(dol_tmp_path)
-
-
- exec_subprocess([wit_path, "copy", "tmp/", "result.wbfs"])
-
-
- file_ops.rm_folder("tmp/")
- file_ops.rm_file("tmp_gl.txt")
- file_ops.rm_file("tmp_wit.xml")
-
-
- return True
- def apply_riiv_patches_wrapper(game_path, xml_path, mod_files_folder, choice_str_list):
-
-
- result = apply_riiv_patches(game_path, xml_path, mod_files_folder, choice_str_list)
- file_ops.rm_folder("tmp/")
- file_ops.rm_file("tmp_gl.txt")
- file_ops.rm_file("tmp_wit.xml")
- return result
|