from lib2to3.pgen2.parse import ParseError from xml.etree import ElementTree as ET import os import argparse import copy # Duplicate the Item with the given filename value replacing filename value dataFiles = ".//dataFiles/Item/filename/[.='{file}']" # /filename # Duplicate the Item with the given Item value replacing Item value filesToInvalidate = ".//contentChangeSets/Item/filesToInvalidate/Item/[.='{file}']" # Duplicate the Item with the given Item value replacing Item value filesToEnable = ".//contentChangeSets/Item/filesToEnable/Item/[.='{file}']" # Duplicate the Item with the given Item value replacing Item value filesToDisable = ".//contentChangeSets/Item/filesToDisable/Item/[.='{file}']" # Duplicate the Item with the given Item value replacing Item value mapfilesToInvalidate = ".//contentChangeSets/Item/mapChangeSetData/Item/filesToInvalidate/Item/[.='{file}']" # Duplicate the Item with the given Item value replacing Item value mapfilesToEnable = ".//contentChangeSets/Item/mapChangeSetData/Item/filesToEnable/Item/[.='{file}']" # Duplicate the Item with the given Item value replacing Item value mapfilesToDisable = ".//contentChangeSets/Item/mapChangeSetData/Item/filesToDisable/Item/[.='{file}']" changeset_paths = [filesToInvalidate, filesToEnable, filesToDisable, mapfilesToInvalidate, mapfilesToEnable, mapfilesToDisable] # class doop: # pass # args = doop() # args.in_file = "content.xml" # args.copy_insert = "dlc_lev_des.rpf,dlc_lev_des_bvh.rpf,dlc_lev_des_bvh.rpf,dlc_lev_des_doop.rpf" # args.insert = "dlc_lev_des_bvh.rpf" # args.out_file = "dupe.xml" def main(): parser = argparse.ArgumentParser() parser.add_argument("-i", '--in_file', metavar='IN', type=str, required=True, help='The input file "IN", used to parse for file entries and insertions if out_file is supplied. e.g. path/to/content.xml') parser.add_argument("-c", '--copy_insert', metavar='CP,INS,...', type=str, required=True, help='The file to be copied "CP" followed by the file to insert "INS", within the content.xml document. e.g. copy.rpf,insert.rpf') parser.add_argument("-o", '--out_file', metavar='OUT', type=str, required=True, help='The output file "OUT", if not set input file is used. e.g. path/to/content.xml') args = parser.parse_args() if not os.path.exists(args.in_file): print("Error: in_file '"+args.in_file+"' doesn't exist") exit(1) print("Begin parsing "+args.in_file) try: with open(args.in_file, encoding="utf-8") as config: xml = ET.fromstring(config.read()) except ParseError as e: print(e) exit(1) # Bit of a hack here for getting parents. element.parent doesn't exist in python xml. parents = {c:p for p in xml.iter() for c in p} args.copy_insert = args.copy_insert.split(",") if len(args.copy_insert) % 2 == 1: print(f"Error: '{args.copy_insert}' is not even!") exit(1) # layout pairs e.g. [[copy,insert],[copy,insert]] args.copy_insert = [args.copy_insert[f:f+2] for f in range(0, len(args.copy_insert), 2)] # print(args.copy_insert) for duplicate, insert in args.copy_insert: print("========================") print(f"Finding: {duplicate} Inserting: {insert}") skip_insert = False found = False for elem in xml.iter(): if not found and elem != None and elem.text != None and duplicate.lower() in elem.text.lower(): swap = duplicate.lower() duplicate = elem.text insert = elem.text.lower().replace(swap, insert) found = True elif elem != None and elem.text != None and insert.lower() in elem.text.lower(): print(f"Error: '{insert}' already exists!") skip_insert = True break if not found: print(f"Error: Could not find '{duplicate}'") print(f"It's not referenced in {args.in_file}") skip_insert = True if skip_insert: print(f"Skipping '{insert}'") continue all_instances = xml.findall(f".//*[.='{duplicate}']") print("Copying '{file}': "+duplicate) print(f"Number: {len(all_instances)}") print("Inserting: "+insert) print("========================") # Handle dataFiles (special case) found = dataFiles.format(file=duplicate) found = xml.find(found) if found != None: item = parents[found] parent = parents[item] dupe = copy.deepcopy(item) dupe.find("filename").text = insert index = list(parent).index(item) parent.insert(index+1, dupe) all_instances.remove(found) # update parents, merge parent dict of dupe with parents and add parents[dupe] = parent parents = {**parents, **{c:p for p in dupe.iter() for c in p}} parents[dupe] = parent elem = ET.tostring(dupe).decode().strip() print(f"Copied {dataFiles}: \n{elem}") print("===") # Handle changesets for cs_path in changeset_paths: found = cs_path.format(file=duplicate) found = xml.findall(found) for f in found: parent = parents[f] dupe = copy.deepcopy(f) dupe.text = insert index = list(parent).index(f) parent.insert(index+1, dupe) all_instances.remove(f) # update parents, merge parent dict of dupe with parents and add parents[dupe] = parent parents = {**parents, **{c:p for p in dupe.iter() for c in p}} parents[dupe] = parent cs = parents[parent] while cs.find("changeSetName") == None: cs = parents[cs] changeset = cs.find("changeSetName").text elem = ET.tostring(dupe).decode().strip() print(f"Copied {cs_path} '{changeset}':\n"+elem) print("===") if len(all_instances) != 0: print(f"Error: not all references to '{duplicate}' handled.") print("The script needs to be updated with the missing xpaths/instances.") print("Element/s:") for e in all_instances: parent = parents[e] elem = ET.tostring(parent).decode().strip() print(elem) exit(1) print("========================\n") if args.out_file: ET.ElementTree(xml).write(args.out_file, encoding="utf-8", xml_declaration=True) else: ET.ElementTree(xml).write(args.in_file, encoding="utf-8", xml_declaration=True) pass if __name__ == "__main__": main() pass