from doltool import Dol, MemoryObject, Section, ActionReplayCode from doltool import SectionType, IntervalDiv from doltool import InvalidImgOffsetError, InvalidVirtualAddressError, InvalidIniFileEntryError, OutOfMemoryError, InvalidSectionAlignError, SectionsOverflowError from doltool import parse_action_replay_ini, get_overlapping_arcodes, get_unmapped_intervals import copy import os from pathlib import Path import shutil from time import time __version__ = "0.0.4" __author__ = "rigodron, algoflash, GGLinnk" __license__ = "MIT" __status__ = "developpement" ################################################## # Installation ################################################## # Original Gotcha Force "eu.dol" has to be placed in the same folder than this script. ################################################## # Path of test folder ################################################## # Dols samples path dols_path = Path("dol_samples") # Inis path ini_path = Path("ini_tests") # Used to create dols. dol_tests_path = Path("dol_tests") # Dols parsed and rebuild path dols_save_path = Path("dol_save") ################################################## # doltool.py commands wrappers ################################################## def doltool_par(dol_path:Path, output_path:Path, ini_path:Path): if os.system(f"python doltool.py -par \"{dol_path}\" -o \"{output_path}\" -ini \"{ini_path}\"") != 0: raise Exception("Error while patching dol using ARCodes ini file.") def doltool_par_sr(dol_path:Path, output_path:Path, ini_path:Path): if os.system(f"python doltool.py -par \"{dol_path}\" -o \"{output_path}\" -ini \"{ini_path}\" -sr\"") != 0: raise Exception("Error while patching dol using ARCodes ini file and section remapping.") ################################################## # Helpers ################################################## class DolDescriptor: def __init__(self, index:int, offset:int, address:int, length:int, byte:bytes): self.index = index self.offset = offset + 0x100 self.address = address self.length = length self.byte = byte def boffset(self): return self.offset.to_bytes(4,"big") def baddress(self): return (self.address + 0x80003100).to_bytes(4,"big") def blength(self): return self.length.to_bytes(4,"big") def map_offsets(datas:bytes, offsets_map:list, intervals:list): 'create virtual space temporary to patch then replace as initial mapped with patched datas.' max_address = 0 for beg,length,dest in offsets_map: max_address = max(max_address, dest+length) remapped_datas = bytearray(b"\x00"*max_address) for [beg, length, dest] in offsets_map: remapped_datas[dest:dest+length] = datas[beg:beg+length] for beg,end,byte in intervals: remapped_datas[beg:end] = byte * (end - beg) for [beg, length, dest] in offsets_map: datas[beg:beg+length] = remapped_datas[dest:dest+length] return datas def create_dol(dol_name:str, descriptors_list:list, bss_addr:int = 20, bss_length:int = 100, entry_point:int = 0): """ input: [DolDescriptor, ...] create a dol with specified values in dol_tests_path """ descriptors_list.sort(key=lambda x:x.index) offsets = b"" address = b"" lengths = b"" tmp_list = copy.deepcopy(descriptors_list) for index in range(18): if tmp_list: if tmp_list[0].index == index: offsets += tmp_list[0].boffset() address += tmp_list[0].baddress() lengths += tmp_list[0].blength() tmp_list.pop(0) continue offsets += b"\x00\x00\x00\x00" address += b"\x00\x00\x00\x00" lengths += b"\x00\x00\x00\x00" datas = (offsets + address + lengths + (bss_addr + 0x80003100).to_bytes(4,"big") + bss_length.to_bytes(4,"big") + (entry_point + 0x80003100).to_bytes(4,"big")).ljust(0x100, b"\x00") descriptors_list.sort(key=lambda x:x.offset) for descriptor in descriptors_list: if len(datas) != descriptor.offset: raise Exception("doltest.py - Invalid dol creation offset.") datas += descriptor.byte * descriptor.length Path(dol_tests_path / dol_name).write_bytes(datas) def to_action_replay_list(memory_objects:list): arc_list = [] for memory_object in memory_objects: arc_list.append(ActionReplayCode("04003100 12345678", 0)) arc_list[-1].set_address(memory_object.address()) arc_list[-1].set_end_address(memory_object.end_address()) arc_list[-1].set_datas(b"a" * memory_object.length()) return arc_list def memory_objects_to_ini_txt(memory_objects:list): str_buffer = "" for memory_object in memory_objects: addr = memory_object.address() & 0x01FFFFFF str_buffer += f"" if memory_object.length() == 4: str_buffer += f"{addr | 0x04000000:08x} " + f"{memory_object.datas()[0]:02x}"*4 + "\n" elif memory_object.length() % 2 == 0: str_buffer += f"{addr | 0x02000000:08x} {((memory_object.length() // 2) - 1):04x}" + f"{memory_object.datas()[0]:02x}"*2 + "\n" else: str_buffer += f"{addr:08x} {(memory_object.length() - 1):06x}" + f"{memory_object.datas()[0]:02x}" + "\n" return str_buffer def create_memory_objects_from_intervals(*intervals:list): 'Create memory objects list from intervals.' if intervals is None: return None res = [] for interval in intervals: memory_object = MemoryObject(0x80003100 + interval[0], end_address = 0x80003100 + interval[1]) res.append( memory_object ) if len(interval) == 3: memory_object.set_datas(interval[2] * memory_object.length()) return res TEST_COUNT = 8 start = time() print("###############################################################################") print("# Checking tests folder") print("###############################################################################") # Check if tests folders exist if ini_path.is_dir() or dol_tests_path.is_dir() or dols_save_path.is_dir(): raise Exception(f"Error - Please remove:\n-{ini_path}\n-{dol_tests_path}\n-{dols_save_path}") print("###############################################################################") print(f"# TEST 1/{TEST_COUNT}") print("# Testing valid dol.resolve_img2virtual conversion.") print("###############################################################################") dol = Dol(Path("eu.dol")) print("Testing first offset of each segments with correct output:") for (offset, virtual_address) in [(0x100, 0x80003100), (0x25e0, 0x800055e0), (0x2aede0, 0x802b1de0), (0x2aee00, 0x802b1e00), (0x2aee20, 0x802b1e20), (0x2bde80, 0x802c0e80), (0x3b3bc0, 0x8043cbe0), (0x3b66e0, 0x80440080)]: if dol.resolve_img2virtual(offset) == virtual_address: print("Correct translation") else: raise Exception(f"Error - resolve_img2virtual invalid translation for offset {offset:08x}: {virtual_address:08x}.") print("Testing last offset of each segments with correct output:") for (offset, virtual_address) in [(0x100 + 0x24e0 - 1, 0x800055e0 - 1), (0x25e0 + 0x2ac800 - 1, 0x802b1de0 - 1), (0x2aede0 + 0x20 - 1, 0x802b1e00 - 1), (0x2aee00 + 0x20 - 1, 0x802b1e20 - 1), (0x2aee20 + 0xf060 - 1, 0x802c0e80 - 1), (0x2bde80 + 0xf5d40 - 1, 0x803b6bc0 - 1), (0x3b3bc0 + 0x2b20 - 1, 0x8043f700 - 1), (0x3b66e0 + 0x6d20 - 1, 0x80446da0 - 1)]: if dol.resolve_img2virtual(offset) == virtual_address: print("Correct translation") else: raise Exception(f"Error - resolve_img2virtual invalid translation for offset {offset:08x}: {virtual_address:08x}.") print("Testing first and last offset out of file datas to raise Exception:") for invalid_offset in [0x9f, 0x3bd400]: try: dol.resolve_img2virtual(invalid_offset) raise Exception("Error - InvalidImgOffsetError Exception should have been triggered.") except InvalidImgOffsetError: print("Correct InvalidImgOffsetError triggered.") print("###############################################################################") print(f"# TEST 2/{TEST_COUNT}") print("# Testing valid dol.resolve_virtual2img conversion.") print("###############################################################################") print("Testing first virtual address of each segments with correct output:") for (offset, virtual_address) in [(0x100, 0x80003100), (0x25e0, 0x800055e0), (0x2aede0, 0x802b1de0), (0x2aee00, 0x802b1e00), (0x2aee20, 0x802b1e20), (0x2bde80, 0x802c0e80), (0x3b3bc0, 0x8043cbe0), (0x3b66e0, 0x80440080)]: if dol.resolve_virtual2img(virtual_address) == offset: print("Correct translation") else: print(f"{dol.resolve_virtual2img(virtual_address):08x}") raise Exception(f"Error - resolve_virtual2img invalid translation for offset {virtual_address:08x}:{offset:08x}.") print("Testing last virtual address of each segments with correct output:") for (offset, virtual_address) in [(0x100 + 0x24e0 - 1, 0x800055e0 - 1), (0x25e0 + 0x2ac800 - 1, 0x802b1de0 - 1), (0x2aede0 + 0x20 - 1, 0x802b1e00 - 1), (0x2aee00 + 0x20 - 1, 0x802b1e20 - 1), (0x2aee20 + 0xf060 - 1, 0x802c0e80 - 1), (0x2bde80 + 0xf5d40 - 1, 0x803b6bc0 - 1), (0x3b3bc0 + 0x2b20 - 1, 0x8043f700 - 1), (0x3b66e0 + 0x6d20 - 1, 0x80446da0 - 1)]: if dol.resolve_virtual2img(virtual_address) == offset: print("Correct translation") else: raise Exception(f"Error - resolve_virtual2img invalid translation for offset {virtual_address:08x}:{offset:08x}.") print("Testing bounding virtual addresses of non existing offset to raise Exception:") for invalid_offset in [0x800030ff, 0x803b6bc0, 0x803b6bc0, 0x8043cbe0 - 1, 0x8043f700, 0x80440080 - 1, 0x80446da0, 0x80446dc8, 0x81800000]: try: dol.resolve_virtual2img(invalid_offset) raise Exception("Error - InvalidVirtualAddressError Exception should have been triggered.") except InvalidVirtualAddressError: print("Correct InvalidVirtualAddressError triggered.") print("###############################################################################") print(f"# TEST 3/{TEST_COUNT}") print("# Testing MemoryObject.") print("###############################################################################") print("Testing __init__ & __str__") # address:int, section_type:SectionType = SectionType.UNMAPPED, name:str = None, length:int = None, end_address:int = None memory_object1 = MemoryObject(0x80003100 + 100, SectionType.BSS, "abcd", length=10) if str(memory_object1) != f"| {'abcd'.ljust(11)} | {0x80003100+100:08x} | {0x80003100+110:08x} | {10:08x} |" or memory_object1.type() != SectionType.BSS: raise Exception("Invalid MemoryObject constructor or __str__.") memory_object2 = MemoryObject(0x80003100 + 200, name="efgh", end_address=0x80003100 + 310) if str(memory_object2) != f"| {'efgh'.ljust(11)} | {0x80003100+200:08x} | {0x80003100+310:08x} | {110:08x} |" or memory_object2.type() != SectionType.UNMAPPED: raise Exception("Invalid MemoryObject constructor or __str__.") print("Testing set_address and set_name") memory_object1.set_end_address(0x80003100 + 200) memory_object1.set_name("blah") if str(memory_object1) != f"| {'blah'.ljust(11)} | {0x80003100+100:08x} | {0x80003100+200:08x} | {100:08x} |": raise Exception("Invalid MemoryObject set_end_address.") print("Testing set_end_address") memory_object1.set_address(0x80003100 + 150) if str(memory_object1) != f"| {'blah'.ljust(11)} | {0x80003100+150:08x} | {0x80003100+200:08x} | {50:08x} |": raise Exception("Invalid MemoryObject set_address.") print("Testing OutOfMemoryError") MemoryObject(0x811fffff, length = 1) MemoryObject(0x80003100, length = 1) try: MemoryObject(0x800030ff, length = 2) raise Exception("Error - OutOfMemoryError should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") try: MemoryObject(0x80003000, end_address = 0x80003800) raise Exception("Error - OutOfMemoryError should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") try: MemoryObject(0x811fff00, end_address = 0x811fff00 + 0x200) raise Exception("Error - OutOfMemoryError should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") try: MemoryObject(0x811fffff, length = 2) raise Exception("Error - OutOfMemoryError should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") try: MemoryObject(0x81200000, length = 1) raise Exception("Error - OutOfMemoryError should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") print("Testing __sub__:") interval = MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20) for [intervals_to_remove, expected_res] in [ [[[0,10]], [[10,20]]], # Before with match [[[20,30]], [[10,20]]], # After with match [[[0,10],[20,30]], [[10,20]]], # Before and after [[[0,11],[20,30]], [[11,20]]], # left truncate [[[0,11],[19,30]], [[11,19]]], # left and right truncate [[[0,10],[19,30]], [[10,19]]], # right truncate [[[0,11],[12,13],[14,15],[19,30]], [[11,12],[13,14],[15,19]]], # left middle and right truncate [[[0,11],[11,13],[13,15],[19,30]], [[15,19]]], # following truncates left truncate rigth truncate [[[0,11],[11,13],[13,15],[15,20]], None], # following truncates overlap with end match [[[10,13],[13,15],[15,25]], None], # following truncates overlap with begin match [[[10,13],[13,15],[15,20]], None], # following truncates in with begin and end match [[[11,13],[13,15],[15,19]], [[10,11],[19,20]]], # following truncates in [[[10,13],[13,15],[15,19]], [[19,20]]], # following truncates in with begin match [[[11,13],[13,15],[15,20]], [[10,11]]], # following truncates in with end match [[[0,30]], None], # total overlap overflowing left right [[[10,30]], None], # total overlap overflowing left [[[0,20]], None], # total overlap overflowing right [[[10,20]], None]]: # total match res_interval = interval - create_memory_objects_from_intervals( *intervals_to_remove ) expected_res = create_memory_objects_from_intervals(*expected_res) if expected_res is not None else None if expected_res is None and res_interval is None: print("Correct result.") continue if len(res_interval) != len(expected_res): raise Exception("Error - Invalid __sub__ result.") for index, res_interval in enumerate(res_interval): if res_interval.address() != expected_res[index].address() or res_interval.end_address() != expected_res[index].end_address(): raise Exception("Error - Invalid __sub__ result.") print("Correct result.") interval = MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20) print("Testing __lt__:") for interval_lt, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), True], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), True], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), False], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), False], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), False], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), False], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), False], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), False], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), False], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), False], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), False], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), False], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), False], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False]]: # total overlap matching right if (interval_lt < interval) != expected_res: raise Exception("Error - Invalid __lt__ result.") else: print("Correct result.") print("Testing __le__:") for interval_le, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), False], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), False], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), True], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), True], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), True], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), False], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), False], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), False], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), False], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), False], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), False], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), False], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), False], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), False], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), True]]: # total overlap matching right if (interval_le <= interval) != expected_res: raise Exception("Error - Invalid __le__ result.") else: print("Correct result.") print("Testing __ge__:") for interval_ge, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), False], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), False], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), False], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), False], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), False], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), False], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), False], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), False], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), True], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), True], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), True], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), False], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), False], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), False], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), True], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False]]: # total overlap matching right if (interval_ge >= interval) != expected_res: raise Exception("Error - Invalid __ge__ result.") else: print("Correct result.") print("Testing __gt__:") for interval_gt, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), False], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), False], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), False], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), False], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), False], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), False], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), False], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), False], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), False], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), False], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), True], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), True], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), False], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False]]: # total overlap matching right if (interval_gt > interval) != expected_res: raise Exception("Error - Invalid __gt__ result.") else: print("Correct result.") print("Testing __contains__:") for interval_contains, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), False], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), False], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), False], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), False], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), True], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), True], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), True], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), True], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), False], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), False], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), False], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), False], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), False], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), False], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), False]]: # total overlap matching right if (interval_contains in interval) != expected_res: raise Exception("Error - Invalid __contains__ result.") else: print("Correct result.") print("Testing __and__:") for interval_and, expected_res in [ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9), False], # __lt__ [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), False], # __lt__ matching [MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11), True], # __le__ + 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11), True], # __le__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), True], # __le__ - 1 byte with matching [MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19), True], # __contains__ [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15), True], # __contains__ matching left [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), True], # __contains__ matching left and right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), True], # __contains__ matching right [MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21), True], # __ge__ + 1 byte [MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21), True], # __ge__ + 1 byte - 1 byte [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), True], # __ge__ + 1 byte with matching [MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25), False], # __gt__ matching [MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25), False], # __gt__ [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21), True], # total overlap [MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21), True], # total overlap matching left [MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20), True]]: # total overlap matching right if (interval_and & interval) != expected_res: raise Exception("Error - Invalid __and__ result.") else: print("Correct result.") print("Testing __truediv__:") intervals_truediv = [] intervals_truediv.append( MemoryObject(0x80003100 + 0, end_address=0x80003100 + 9) ) intervals_truediv[0].set_datas(b"l"*9) intervals_truediv.append( MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10) ) intervals_truediv[1].set_datas(b"l"*10) intervals_truediv.append( MemoryObject(0x80003100 + 0, end_address=0x80003100 + 11) ) intervals_truediv[2].set_datas(b"l"*10 + b"i") intervals_truediv.append( MemoryObject(0x80003100 + 9, end_address=0x80003100 + 11) ) intervals_truediv[3].set_datas(b"l" + b"i") intervals_truediv.append( MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20) ) intervals_truediv[4].set_datas(b"l" + 10*b"i") intervals_truediv.append( MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19) ) intervals_truediv[5].set_datas(b"i"*8) intervals_truediv.append( MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15) ) intervals_truediv[6].set_datas(b"i"*5) intervals_truediv.append( MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20) ) intervals_truediv[7].set_datas(b"i"*10) intervals_truediv.append( MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20) ) intervals_truediv[8].set_datas(b"i"*5) intervals_truediv.append( MemoryObject(0x80003100 + 15, end_address=0x80003100 + 21) ) intervals_truediv[9].set_datas(b"i"*5 + b"r") intervals_truediv.append( MemoryObject(0x80003100 + 19, end_address=0x80003100 + 21) ) intervals_truediv[10].set_datas(b"i" + b"r") intervals_truediv.append( MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21) ) intervals_truediv[11].set_datas(b"i"*10 + b"r") intervals_truediv.append( MemoryObject(0x80003100 + 20, end_address=0x80003100 + 25) ) intervals_truediv[12].set_datas(b"r"*5) intervals_truediv.append( MemoryObject(0x80003100 + 21, end_address=0x80003100 + 25) ) intervals_truediv[13].set_datas(b"r"*4) intervals_truediv.append( MemoryObject(0x80003100 + 9, end_address=0x80003100 + 21) ) intervals_truediv[14].set_datas(b"l" + b"i"*10 + b"r") intervals_truediv.append( MemoryObject(0x80003100 + 10, end_address=0x80003100 + 21) ) intervals_truediv[15].set_datas(b"i"*10 + b"r") intervals_truediv.append( MemoryObject(0x80003100 + 9, end_address=0x80003100 + 20) ) intervals_truediv[16].set_datas(b"l" + b"i"*10) expected_truediv_res = [] expected_truediv_res.append(None) # __lt__ expected_truediv_res.append(None) # __lt__ matching # __le__ + 1 byte expected_truediv_res.append({IntervalDiv.LEFT: MemoryObject(0x80003100 + 0, end_address=0x80003100 + 10), IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 11)}) expected_truediv_res[2][IntervalDiv.LEFT].set_datas(b"l"*10) expected_truediv_res[2][IntervalDiv.IN].set_datas(b"i") # __le__ + 1 byte - 1 byte expected_truediv_res.append({IntervalDiv.LEFT: MemoryObject(0x80003100 + 9, end_address=0x80003100 + 10), IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 11)}) expected_truediv_res[3][IntervalDiv.LEFT].set_datas(b"l") expected_truediv_res[3][IntervalDiv.IN].set_datas(b"i") # __le__ - 1 byte with matching expected_truediv_res.append({IntervalDiv.LEFT: MemoryObject(0x80003100 + 9, end_address=0x80003100 + 10), IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20)}) expected_truediv_res[4][IntervalDiv.LEFT].set_datas(b"l") expected_truediv_res[4][IntervalDiv.IN].set_datas(b"i"*10) # __contains__ expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 11, end_address=0x80003100 + 19)}) expected_truediv_res[5][IntervalDiv.IN].set_datas(b"i"*8) # __contains__ matching left expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 15)}) expected_truediv_res[6][IntervalDiv.IN].set_datas(b"i"*5) # __contains__ matching left and right expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20)}) expected_truediv_res[7][IntervalDiv.IN].set_datas(b"i"*10) # __contains__ matching right expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20)}) expected_truediv_res[8][IntervalDiv.IN].set_datas(b"i"*5) # __ge__ + 1 byte expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 15, end_address=0x80003100 + 20), IntervalDiv.RIGHT: MemoryObject(0x80003100 + 20, end_address=0x80003100 + 21)}) expected_truediv_res[9][IntervalDiv.IN].set_datas(b"i"*5) expected_truediv_res[9][IntervalDiv.RIGHT].set_datas(b"r") # __ge__ + 1 byte - 1 byte expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 19, end_address=0x80003100 + 20), IntervalDiv.RIGHT: MemoryObject(0x80003100 + 20, end_address=0x80003100 + 21)}) expected_truediv_res[10][IntervalDiv.IN].set_datas(b"i") expected_truediv_res[10][IntervalDiv.RIGHT].set_datas(b"r") # __ge__ + 1 byte with matching expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), IntervalDiv.RIGHT: MemoryObject(0x80003100 + 20, end_address=0x80003100 + 21)}) expected_truediv_res[11][IntervalDiv.IN].set_datas(b"i"*10) expected_truediv_res[11][IntervalDiv.RIGHT].set_datas(b"r") expected_truediv_res.append(None) # __gt__ matching expected_truediv_res.append(None) # __gt__ # total overlap expected_truediv_res.append({IntervalDiv.LEFT: MemoryObject(0x80003100 + 9, end_address=0x80003100 + 10), IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), IntervalDiv.RIGHT: MemoryObject(0x80003100 + 20, end_address=0x80003100 + 21)}) expected_truediv_res[14][IntervalDiv.LEFT].set_datas(b"l") expected_truediv_res[14][IntervalDiv.IN].set_datas(b"i"*10) expected_truediv_res[14][IntervalDiv.RIGHT].set_datas(b"r") # total overlap matching left expected_truediv_res.append({IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20), IntervalDiv.RIGHT: MemoryObject(0x80003100 + 20, end_address=0x80003100 + 21)}) expected_truediv_res[15][IntervalDiv.IN].set_datas(b"i"*10) expected_truediv_res[15][IntervalDiv.RIGHT].set_datas(b"r") # total overlap matching right expected_truediv_res.append({IntervalDiv.LEFT: MemoryObject(0x80003100 + 9, end_address=0x80003100 + 10), IntervalDiv.IN: MemoryObject(0x80003100 + 10, end_address=0x80003100 + 20)}) expected_truediv_res[16][IntervalDiv.LEFT].set_datas(b"l") expected_truediv_res[16][IntervalDiv.IN].set_datas(b"i"*10) for index in range(len(expected_truediv_res)): interval_truediv = intervals_truediv[index] expected_res = expected_truediv_res[index] new_intervals = interval_truediv / interval if expected_res is None and new_intervals is None: print("Correct result") continue for key in expected_res.keys(): if key not in new_intervals: raise Exception("Error - Invalid __truediv__ result.") if new_intervals[key].address() != expected_res[key].address() or new_intervals[key].end_address() != expected_res[key].end_address() or new_intervals[key].datas() != expected_res[key].datas(): raise Exception("Error - Invalid __truediv__ result.") print("Correct result.") print("Testing to_memory_object:") memory_object3 = MemoryObject(0x80003100 + 20, end_address=0x80003100 + 42) print("Testing set_datas:") memory_object3.set_datas(b"11333333333333333333BB") if memory_object3.datas() != b"11333333333333333333BB": raise Exception("Error - Invalid set_datas result.") print("Correct result.") memory_object3.set_datas(b"00112233445566778899AA") if memory_object3.datas() != b"00112233445566778899AA": raise Exception("Error - Invalid set_datas result.") print("Correct result.") print("Testing update_datas:") update = MemoryObject(0x80003100 + 21, end_address=0x80003100 + 41) update.set_datas(b"cccccccccccccccccccc") memory_object3.update_datas(update) if memory_object3.datas() != b"0ccccccccccccccccccccA": raise Exception("Error - Invalid update_datas result.") print("Correct result.") update.set_datas(b"9999999999999999999999") update.set_address(0x80003100 + 20) update.set_end_address(0x80003100 + 42) memory_object3.update_datas(update) if memory_object3.datas() != b"9999999999999999999999": raise Exception("Error - Invalid update_datas result.") print("Correct result.") print("Testing align:") memory_object3.set_address(0x80003100 + 3) memory_object3.set_end_address(0x80003100 + 9) memory_object3.align() if memory_object3.address() != 0x80003100 + 0 or memory_object3.end_address() != 0x80003100 + 32: raise Exception("Error - Invalid align result.") print("Correct result.") print("###############################################################################") print(f"# TEST 4/{TEST_COUNT}") print("# Testing valid action_replay_code parsing.") print("###############################################################################") ini_path.mkdir() dol_tests_path.mkdir() valid_action_replay_ini = "[ActionReplay_Enabled]\n$Costs\n$HP\n$B Ammo and Refill Codes\n$B Mode and Reload Codes\n"+\ "$X Ammo and Refill Codes\n$X Mode and Reload Codes\n$Warehouse Full\n\n[ActionReplay]\n$Costs\n022E2CC0 00050096\n"+\ "022E2CCC 00050136\n022E2CD8 0005012C\n022E2CE4 000500D2\n042E4E2A 0000005A\n042E4F92 000001E0\n042E50FA 0000005A\n"+\ "042E5262 0001003C\n042E53CA 00000078\n042E5532 0000003C\n042E569A 0000003C\n042E5802 00000078\n042E596A 0000000A\n"+\ "003CE5C2 00000003\n0040E5C2 00000344\n" (ini_path / "test1.ini").write_text(valid_action_replay_ini) action_replay_list = parse_action_replay_ini(ini_path / "test1.ini") expected_res6 = [ (int("802E2CC0", 16), b"\x00\x96\x00\x96\x00\x96\x00\x96\x00\x96\x00\x96"), (int("802E2CCC", 16), b"\x01\x36\x01\x36\x01\x36\x01\x36\x01\x36\x01\x36"), (int("802E2CD8", 16), b"\x01\x2C\x01\x2C\x01\x2C\x01\x2C\x01\x2C\x01\x2C"), (int("802E2CE4", 16), b"\x00\xD2\x00\xD2\x00\xD2\x00\xD2\x00\xD2\x00\xD2"), (int("802E4E2A", 16), b"\x00\x00\x00\x5A"), (int("802E4F92", 16), b"\x00\x00\x01\xE0"), (int("802E50FA", 16), b"\x00\x00\x00\x5A"), (int("802E5262", 16), b"\x00\x01\x00\x3C"), (int("802E53CA", 16), b"\x00\x00\x00\x78"), (int("802E5532", 16), b"\x00\x00\x00\x3C"), (int("802E569A", 16), b"\x00\x00\x00\x3C"), (int("802E5802", 16), b"\x00\x00\x00\x78"), (int("802E596A", 16), b"\x00\x00\x00\x0A"), (int("803CE5C2", 16), b"\x03"), (int("8040E5C2", 16), b"\x44\x44\x44\x44")] if len(expected_res6) != len(action_replay_list): raise Exception("Error - Invalid ini parsing.") for index, exp_res in enumerate(expected_res6): if action_replay_list[index].address() != expected_res6[index][0] or action_replay_list[index].datas() != expected_res6[index][1]: raise Exception("Error - Invalid ini parsing.") print("Valid parsing as Expected.") for invalid_action_replay_ini in ["a\n082E2CC0 00050096\n","0A02E2CC0 00050096","082E2CC0 00050096", "\n122E2CC0 00050096\n"]: try: (ini_path / "test2.ini").write_text(invalid_action_replay_ini) parse_action_replay_ini(ini_path / "test2.ini") raise Exception("Error - InvalidIniFileEntryError Exception should have been triggered.") except InvalidIniFileEntryError: print("Correct InvalidIniFileEntryError triggered.") # 41200000 for invalid_action_replay_ini in ["\n020030ff 00050096\n","05200000 00050096","05800000 00050096", "\n02000000 04050096\n"]: try: (ini_path / "test3.ini").write_text(invalid_action_replay_ini) parse_action_replay_ini(ini_path / "test3.ini") raise Exception("Error - OutOfMemoryError Exception should have been triggered.") except OutOfMemoryError: print("Correct OutOfMemoryError triggered.") print("###############################################################################") print(f"# TEST 5/{TEST_COUNT}") print("# Testing intervals functions.") print("###############################################################################") print("Testing _Dol__get_merged_mapped_memory.") # There is always sections # * Sections never overlap # * unsorted intervals create_dol("dol0.dol", [ DolDescriptor(index=0, offset=107, address=160, length=40, byte=b"a"), # [160, 200] # * > 1 spacing DolDescriptor(index=1, offset=95, address=128, length=12, byte=b"b"), # [128, 140] # * 0 spacing x2 DolDescriptor(index=3, offset=63, address=96, length=32, byte=b"b"), # [96, 128] # * 0 spacing DolDescriptor(index=5, offset=0, address=32, length=31, byte=b"c"), # [32, 63] DolDescriptor(index=7, offset=31, address=64, length=32, byte=b"d")]) # [64, 96] # * 1 spacing between two intervals expected_res7 = create_memory_objects_from_intervals([32,63],[64,140],[160,200]) dol0 = Dol(dol_tests_path / "dol0.dol") merged_list = dol0._Dol__get_merged_mapped_memory() if len(expected_res7) != len(merged_list): raise Exception("Error - Invalid merged_list.") for index, merged in enumerate(merged_list): if merged.address() != expected_res7[index].address() or merged.end_address() != expected_res7[index].end_address(): raise Exception("Error - Invalid intervals merge.") print("Correct result.") print("Testing get_overlapping_arcodes.") arcode0 = ActionReplayCode("04003100 AAAAAAAA", 1) arcode1 = ActionReplayCode("04003104 BBBBBBBB", 2) # matching arcode2 = ActionReplayCode("04003107 BBBBBBBB", 3) # overlapping from 1 byte arcode3 = ActionReplayCode("0400310C BBBBBBBB", 4) # interval 1 byte arcode4 = ActionReplayCode("0400310C BBBBBBBB", 5) # total overlap arcode5 = ActionReplayCode("0400310E BBBBBBBB", 6) # overlapping from 2 bytes arcode6 = ActionReplayCode("02003114 000FBBBB", 7) # overlapping totaly next arcode7 = ActionReplayCode("04003118 ABCFBBBB", 8) # totaly overlapped expected_res8 = [[arcode1, arcode2], [arcode3, arcode4], [arcode4, arcode5], [arcode6, arcode7]] overlaps0 = get_overlapping_arcodes([arcode0,arcode1,arcode2,arcode3,arcode4,arcode5,arcode6,arcode7]) if len(overlaps0) != len(expected_res8): raise Exception("Error - Invalid get_overlapping_arcodes result.") for index, (overlap0, overlap1) in enumerate(overlaps0): if overlap0 != expected_res8[index][0] or overlap1 != expected_res8[index][1]: raise Exception("Error - Invalid get_overlapping_arcodes result.") print("Testing get_unmapped_intervals.") merged_memo_res = [[ # Testing all limits create_memory_objects_from_intervals([50,75],[100,200],[250,260],[270,280],[300,400]), create_memory_objects_from_intervals( [0,5], # before all intervals group [10,12],[15,20], [25,50], # map before # in group [50,75], # in with begin and end map # new group [80,90], # between group [91,92], # between group [94,95], # between group [95,101], # overlap begining with 1 byte # in group [101,195], # in match previous interval to test end and next interval to test begin [195,200], # in with end map # empty group [250,258], # in with begin map # new group [259,264], # overlap ending with 1 byte # new group [280,285], # map after # new group [420,430], # after group [450,470]), create_memory_objects_from_intervals([0,50],[80,100],[260,264],[280,285],[420,470]), ],[ # All before with overlap create_memory_objects_from_intervals([75,200],[250,260],[270,280]), create_memory_objects_from_intervals([0,5],[10,12],[15,20],[25,50],[50,76]), create_memory_objects_from_intervals([0,75]) ],[ # All after with overlap create_memory_objects_from_intervals([50,75],[100,120],[140,196]), create_memory_objects_from_intervals([195,200],[250,258],[259,264],[450,470]), create_memory_objects_from_intervals([196,470]) ],[ # All between with overlap create_memory_objects_from_intervals([10,20],[50,196],[469,520],[600,700]), create_memory_objects_from_intervals( [195,200], # before overlap [250,258], # between [259,264], # between [450,470]), # after overlap create_memory_objects_from_intervals([196, 469]) ],[ # All between with begin and end match create_memory_objects_from_intervals([10,20],[50,196],[469,520],[600,700]), create_memory_objects_from_intervals([196,200], [250,258], [259,264], [450,469]), create_memory_objects_from_intervals([196, 469]) ],[ # All in create_memory_objects_from_intervals([0,500]), create_memory_objects_from_intervals([195,200],[250,258],[259,264],[450,470]), None ]] for merged_intervals, memory_objects_list, expected_res in merged_memo_res: unmapped_intervals = get_unmapped_intervals(merged_intervals, to_action_replay_list(memory_objects_list)) if expected_res is None and unmapped_intervals is None: print("Corrects get_unmapped_intervals test.") continue if len(unmapped_intervals) != len(expected_res): raise Exception(f"Error - get_unmapped_intervals invalid result.") for index, unmapped_interval in enumerate(unmapped_intervals): if expected_res[index].address() != unmapped_interval.address() or expected_res[index].end_address() != unmapped_interval.end_address(): raise Exception(f"Error - get_unmapped_intervals invalid result.") print("Corrects get_unmapped_intervals test.") print("###############################################################################") print(f"# TEST 6/{TEST_COUNT}") print("# Testing Section align.") print("###############################################################################") # index:int, offset:int, address:int, length:int) try: Section(0, 0x1000, 0x80003101, 10) except InvalidSectionAlignError: print("Correct invalid Section align.") try: Section(0, 0x1000, 0x8000311F, 10) except InvalidSectionAlignError: print("Correct invalid Section align.") print("###############################################################################") print(f"# TEST 7/{TEST_COUNT}") print("# Testing dol._Dol__save.") print("###############################################################################") dols_save_path.mkdir() for input_path in dols_path.glob("*"): dol = Dol(input_path) dol._Dol__save(dols_save_path / input_path.name) if input_path.read_bytes() != (dols_save_path / input_path.name).read_bytes(): raise Exception(f"Error - Invalid dol parsing and save for dol {input_path}.") else: print(f"Correct parsing and saving.") print("###############################################################################") print(f"# TEST 8/{TEST_COUNT}") print("# Testing --patch-action-replay commands.") print("###############################################################################") # Testing correct patch between two section + 1 # Sections: [0, 32], [32, 64], [64, 96], [96, 128], [128, 160], [160, 192], [192, 224], [224, 256], [256, 288], # [288, 320], [320, 352], [352, 384], [384, 416], [416, 448] print("Following sections sorted with offset == address; offset != address and so on.") # Following sections sorted by offset create_dol("dol1.dol", [ DolDescriptor(index=0, offset=0, address=0, length=32, byte=b"\x10"), DolDescriptor(index=2, offset=32, address=32, length=32, byte=b"\x20"), DolDescriptor(index=3, offset=64, address=64, length=32, byte=b"\x30"), DolDescriptor(index=5, offset=96, address=96, length=32, byte=b"\x40"), DolDescriptor(index=8, offset=128, address=128, length=32, byte=b"\x50"), DolDescriptor(index=9, offset=160, address=160, length=32, byte=b"\x60"), DolDescriptor(index=10, offset=192, address=192, length=32, byte=b"\x70"), DolDescriptor(index=11, offset=224, address=224, length=32, byte=b"\x80"), DolDescriptor(index=12, offset=256, address=256, length=32, byte=b"\x90"), DolDescriptor(index=13, offset=288, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=14, offset=320, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=15, offset=352, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=16, offset=384, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=17, offset=416, address=416, length=32, byte=b"\xE0")]) # Following sections reverse sorted by offset # Here there can't be out overlappings because of address sorting. create_dol("dol2.dol", [ DolDescriptor(index=0, offset=416, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=2, offset=384, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=3, offset=352, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=5, offset=320, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=8, offset=288, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=9, offset=256, address=256, length=32, byte=b"\x90"), DolDescriptor(index=10, offset=224, address=224, length=32, byte=b"\x80"), DolDescriptor(index=11, offset=192, address=192, length=32, byte=b"\x70"), DolDescriptor(index=12, offset=160, address=160, length=32, byte=b"\x60"), DolDescriptor(index=13, offset=128, address=128, length=32, byte=b"\x50"), DolDescriptor(index=14, offset=96, address=96, length=32, byte=b"\x40"), DolDescriptor(index=15, offset=64, address=64, length=32, byte=b"\x30"), DolDescriptor(index=16, offset=32, address=32, length=32, byte=b"\x20"), DolDescriptor(index=17, offset=0, address=0, length=32, byte=b"\x10")]) # Following sections shuffled # Here there can't be out overlappings because of address sorting. # Following sections unsorted by offset create_dol("dol3.dol", [ DolDescriptor(index=0, offset=192, address=192, length=32, byte=b"\x70"), DolDescriptor(index=2, offset=64, address=64, length=32, byte=b"\x30"), DolDescriptor(index=3, offset=160, address=160, length=32, byte=b"\x60"), DolDescriptor(index=5, offset=288, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=8, offset=128, address=128, length=32, byte=b"\x50"), DolDescriptor(index=9, offset=32, address=32, length=32, byte=b"\x20"), DolDescriptor(index=10, offset=96, address=96, length=32, byte=b"\x40"), DolDescriptor(index=11, offset=224, address=224, length=32, byte=b"\x80"), DolDescriptor(index=12, offset=416, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=13, offset=352, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=14, offset=320, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=15, offset=384, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=16, offset=0, address=0, length=32, byte=b"\x10"), DolDescriptor(index=17, offset=256, address=256, length=32, byte=b"\x90")]) mappeurs_dict = {} # Sames as previously but with offset reverse sorted / shuffled / from addresses create_dol("dol4.dol", [ DolDescriptor(index=0, offset=416, address=0, length=32, byte=b"\x10"), DolDescriptor(index=2, offset=384, address=32, length=32, byte=b"\x20"), DolDescriptor(index=3, offset=352, address=64, length=32, byte=b"\x30"), DolDescriptor(index=5, offset=320, address=96, length=32, byte=b"\x40"), DolDescriptor(index=8, offset=288, address=128, length=32, byte=b"\x50"), DolDescriptor(index=9, offset=256, address=160, length=32, byte=b"\x60"), DolDescriptor(index=10, offset=224, address=192, length=32, byte=b"\x70"), DolDescriptor(index=11, offset=192, address=224, length=32, byte=b"\x80"), DolDescriptor(index=12, offset=160, address=256, length=32, byte=b"\x90"), DolDescriptor(index=13, offset=128, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=14, offset=96, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=15, offset=64, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=16, offset=32, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=17, offset=0, address=416, length=32, byte=b"\xE0")]) mappeurs_dict[4] = [ [416, 32, 0], [384, 32, 32], [352, 32, 64], [320, 32, 96], [288, 32, 128], [256, 32, 160], [224, 32, 192], [192, 32, 224], [160, 32, 256], [128, 32, 288], [96, 32, 320], [64, 32, 352], [32, 32, 384], [0, 32, 416]] create_dol("dol5.dol", [ DolDescriptor(index=0, offset=0, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=2, offset=32, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=3, offset=64, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=5, offset=96, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=8, offset=128, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=9, offset=160, address=256, length=32, byte=b"\x90"), DolDescriptor(index=10, offset=192, address=224, length=32, byte=b"\x80"), DolDescriptor(index=11, offset=224, address=192, length=32, byte=b"\x70"), DolDescriptor(index=12, offset=256, address=160, length=32, byte=b"\x60"), DolDescriptor(index=13, offset=288, address=128, length=32, byte=b"\x50"), DolDescriptor(index=14, offset=320, address=96, length=32, byte=b"\x40"), DolDescriptor(index=15, offset=352, address=64, length=32, byte=b"\x30"), DolDescriptor(index=16, offset=384, address=32, length=32, byte=b"\x20"), DolDescriptor(index=17, offset=416, address=0, length=32, byte=b"\x10")]) mappeurs_dict[5] = [ [0, 32, 416], [32, 32, 384], [64, 32, 352], [96, 32, 320], [128, 32, 288], [160, 32, 256], [192, 32, 224], [224, 32, 192], [256, 32, 160], [288, 32, 128], [320, 32, 96], [352, 32, 64], [384, 32, 32], [416, 32, 0]] create_dol("dol6.dol", [ DolDescriptor(index=0, offset=416, address=192, length=32, byte=b"\x70"), DolDescriptor(index=2, offset=0, address=64, length=32, byte=b"\x30"), DolDescriptor(index=3, offset=192, address=160, length=32, byte=b"\x60"), DolDescriptor(index=5, offset=160, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=8, offset=128, address=128, length=32, byte=b"\x50"), DolDescriptor(index=9, offset=64, address=32, length=32, byte=b"\x20"), DolDescriptor(index=10, offset=320, address=96, length=32, byte=b"\x40"), DolDescriptor(index=11, offset=384, address=224, length=32, byte=b"\x80"), DolDescriptor(index=12, offset=224, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=13, offset=32, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=14, offset=96, address=320, length=32, byte=b"\xB0"), DolDescriptor(index=15, offset=288, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=16, offset=352, address=0, length=32, byte=b"\x10"), DolDescriptor(index=17, offset=256, address=256, length=32, byte=b"\x90")]) mappeurs_dict[6] = [ [416, 32, 192], [0, 32, 64], [192, 32, 160], [160, 32, 288], [128, 32, 128], [64, 32, 32], [320, 32, 96], [384, 32, 224], [224, 32, 416], [32, 32, 352], [96, 32, 320], [288, 32, 384], [352, 32, 0], [256, 32, 256]] # Here there can't be out overlappings. # Beginning of dol file # [0, 24] 24 \x11 # Middle -1 + 1 # [33, 63] 30 \x22 # Middle # [80, 84] 4 \x33 # Overlapping + 1 # [93, 97] 4 \x44 # Overlapping - 1 # [127, 131] 4 \x55 # Endin map # [156, 160] 4 \x66 # Begin map # [160, 164] 4 \x77 # Begin and ending map # [192, 224] 32 \x88 # Total overlapping +1 -1 # [255, 289] 34 \x99 # Total overlapping 3 sec # [319, 417] 98 \xAA # Ending of dol file # [444, 448] 4 \xBB # [[begin, end, byte], ...] intervals_list = [] intervals_list.append([[0, 24, b"\x11"], [33, 63, b"\x22"], [80, 84, b"\x33"], [93, 97, b"\x44"], [127, 131, b"\x55"], [156, 160, b"\x66"], [160, 164, b"\x77"], [192, 224, b"\x88"], [255, 289, b"\x99"], [319, 417, b"\xAA"], [444, 448, b"\xBB"]]) # same reverse sorted intervals_list.append(sorted(intervals_list[0], key=lambda x: x, reverse=True)) # same but shuffled intervals_list.append([[156, 160, b"\x66"], [255, 289, b"\x99"], [160, 164, b"\x77"], [93, 97, b"\x44"],\ [444, 448, b"\xBB"], [80, 84, b"\x33"], [319, 417, b"\xAA"], [33, 63, b"\x22"],\ [0, 24, b"\x11"], [127, 131, b"\x55"], [192, 224, b"\x88"]]) # just to remember, sames sections: # [0, 32], [32, 64], [64, 96], [96, 128], [128, 160], [160, 192], [192, 224], [224, 256], [256, 288], # [288, 320], [320, 352], [352, 384], [384, 416], [416, 448] intervals_list.append([ [24, 64, b"\x11"], # overlap left match right [96, 130, b"\x22"], # overlap right match left [120, 170, b"\x33"], # overlap left and right [191, 225, b"\x44"], # overlap left and right +1-1 [255, 353, b"\x55"]]) # overlap left and right +- 3 sections (reversed) intervals_list.append([ # same but reverse sorted [255, 353, b"\x11"], # overlap left and right +- 3 sections (reversed) [191, 225, b"\x22"], # overlap left and right +1-1 [120, 170, b"\x33"], # overlap left and right [96, 130, b"\x44"], # overlap right match left [24, 64, b"\x55"]]) # overlap left match right intervals_list.append([ # same but shuffled [120, 170, b"\x11"], # overlap left and right [255, 353, b"\x22"], # overlap left and right +- 3 sections (reversed) [96, 130, b"\x33"], # overlap right match left [24, 64, b"\x44"], # overlap left match right [191, 225, b"\x55"]]) # overlap left and right +1-1 # total file patch # overlap right (match left) 3 sections dol1 <- sorted # overlap left match right 3 sections dol3 <- reverse sorted intervals_list.append([[0, 448, b"\x11"]]) # overlap left and right +3 sections # overlap left and right -3 sections intervals_list.append([[254, 448, b"\x11"]]) # overlap left and right +-3 sections <- dol2 1st section intervals_list.append([[100, 340, b"\x11"]]) # one byte patch test intervals_list.append([[100, 101, b"\x11"], [105, 110, b"\x22"]]) """ dol123_datas = b"".join(list(map(lambda x: x.to_bytes(4, "big"), [ 0x100+0, 0, 0x100+32, 0x100+64, 0, 0x100+96, 0, 0, 0x100+128, 0x100+160, 0x100+192, 0x100+224, 0x100+256, 0x100+288, 0x100+320, 0x100+352, 0x100+384, 0x100+416, 0x80003100+0, 0, 0x80003100+32, 0x80003100+64, 0, 0x80003100+96, 0, 0, 0x80003100+128, 0x80003100+160, 0x80003100+192, 0x80003100+224, 0x80003100+256, 0x80003100+288, 0x80003100+320, 0x80003100+352, 0x80003100+384, 0x80003100+416, 32, 0, 32, 32, 0, 32, 0, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0x80003100+20,100, 0x80003100]))).ljust(0x100, b"\x00") + b"\x10"*32 + b"\x20"*32 + \ b"\x30"*32 + b"\x40"*32 + b"\x50"*32 + b"\x60"*32 + b"\x70"*32 + b"\x80"*32 + b"\x90"*32 + b"\xA0"*32 + b"\xB0"*32 + \ b"\xC0"*32 + b"\xD0"*32 + b"\xE0"*32 """ def test_dols(range_dols, intervals_list:list): for interval_index, intervals in enumerate(intervals_list): for dol_index in range_dols: dol_path = f"dol{dol_index}.dol" dol_patched_path = f"dol{dol_index}_{interval_index}_patched.dol" dol_ini_path = f"dol{dol_index}_{interval_index}.ini" # write ini file with same sorting than intervals (ini_path / dol_ini_path).write_text( memory_objects_to_ini_txt(create_memory_objects_from_intervals(*intervals)) ) dol_datas = bytearray((dol_tests_path / dol_path).read_bytes()) dol_header = dol_datas[:0x100] dol_datas = dol_datas[0x100:] if dol_index in mappeurs_dict: dol_datas = map_offsets(dol_datas, mappeurs_dict[dol_index], intervals) else: for beg,end,byte in intervals: dol_datas[beg:end] = byte * (end - beg) Path(dol_tests_path / f"dol{dol_index}_{interval_index}_patched.dol.expected").write_bytes(dol_header + dol_datas) doltool_par(dol_tests_path / dol_path, dol_tests_path / dol_patched_path, ini_path / dol_ini_path) if dol_header + dol_datas != (dol_tests_path / dol_patched_path).read_bytes(): print(interval_index, intervals) raise Exception("Error - Invalid -par result.") test_dols(range(1,7), intervals_list) # Testing Overflowing at the end of section + 1 intervals_list = [] intervals_list.append([[445, 449, b"\x11"], [33, 63, b"\x22"]]) intervals_list.append([[0, 24, b"\x11"], [445, 449, b"\x22"]]) intervals_list.append([[0, 24, b"\x11"], [447, 451, b"\x22"]]) intervals_list.append([[447, 451, b"\x11"], [33, 63, b"\x22"]]) intervals_list.append([[447, 451, b"\x11"]]) intervals_list.append([[445, 449, b"\x11"]]) intervals_list.append([[446, 449, b"\x11"]]) intervals_list.append([[447, 450, b"\x11"]]) intervals_list.append([[447, 456, b"\x11"]]) for interval_index, intervals in enumerate(intervals_list): for dol_index in range(1,7): try: dol_ini_path = f"dol{dol_index}_{interval_index}_exception.ini" (ini_path / dol_ini_path).write_text( memory_objects_to_ini_txt(create_memory_objects_from_intervals(*intervals)) ) dol = Dol(dol_tests_path / f"dol{dol_index}.dol") action_replay_list = parse_action_replay_ini(ini_path / dol_ini_path) dol.patch_memory_objects(dol_tests_path / f"dol{dol_index}_patched.dol", action_replay_list) raise Exception("Error - SectionsOverflowError Exception should have been triggered.") except SectionsOverflowError: print("Correct SectionsOverflowError triggered.") mappeurs_dict = {} # Sections: [0, 32], [96, 128], [128, 160], [224, 256], [256, 288], [288, 320], [352, 384], [384, 416], [416, 448] print("Following sections sorted with offset == address - section patch using offset is safe.") # Following sections sorted by offset create_dol("dol7.dol", [ DolDescriptor(index=1, offset=0, address=0, length=32, byte=b"\x10"), DolDescriptor(index=5, offset=32, address=96, length=32, byte=b"\x40"), DolDescriptor(index=8, offset=64, address=128, length=32, byte=b"\x50"), DolDescriptor(index=10, offset=96, address=224, length=32, byte=b"\x80"), DolDescriptor(index=11, offset=128, address=256, length=32, byte=b"\x90"), DolDescriptor(index=12, offset=160, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=13, offset=192, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=14, offset=224, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=15, offset=256, address=416, length=32, byte=b"\xE0")]) mappeurs_dict[7] = [ [0, 32, 0], [32, 32, 96], [64, 32, 128], [96, 32, 224], [128, 32, 256], [160, 32, 288], [192, 32, 352], [224, 32, 384], [256, 32, 416]] # Following sections reverse sorted by offset create_dol("dol8.dol", [ DolDescriptor(index=1, offset=256, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=5, offset=224, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=8, offset=192, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=10, offset=160, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=11, offset=128, address=256, length=32, byte=b"\x90"), DolDescriptor(index=12, offset=96, address=224, length=32, byte=b"\x80"), DolDescriptor(index=13, offset=64, address=128, length=32, byte=b"\x50"), DolDescriptor(index=14, offset=32, address=96, length=32, byte=b"\x40"), DolDescriptor(index=15, offset=0, address=0, length=32, byte=b"\x10")]) mappeurs_dict[8] = [ [256, 32, 416], [224, 32, 384], [192, 32, 352], [160, 32, 288], [128, 32, 256], [96, 32, 224], [64, 32, 128], [32, 32, 96], [0, 32, 0]] # shuffled # Following sections unsorted by offset create_dol("dol9.dol", [ DolDescriptor(index=1, offset=64, address=128, length=32, byte=b"\x50"), DolDescriptor(index=5, offset=0, address=0, length=32, byte=b"\x10"), DolDescriptor(index=8, offset=32, address=96, length=32, byte=b"\x40"), DolDescriptor(index=10, offset=224, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=11, offset=192, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=12, offset=96, address=224, length=32, byte=b"\x80"), DolDescriptor(index=13, offset=256, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=14, offset=160, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=15, offset=128, address=256, length=32, byte=b"\x90")]) mappeurs_dict[9] = [ [64, 32, 128], [0, 32, 0], [32, 32, 96], [224, 32, 384], [192, 32, 352], [96, 32, 224], [256, 32, 416], [160, 32, 288], [128, 32, 256]] # Sames as previously but with offset reverse sorted / shuffled / from addresses # offset reverse sorted from address create_dol("dol10.dol", [ DolDescriptor(index=1, offset=256, address=0, length=32, byte=b"\x10"), DolDescriptor(index=5, offset=224, address=96, length=32, byte=b"\x40"), DolDescriptor(index=8, offset=192, address=128, length=32, byte=b"\x50"), DolDescriptor(index=10, offset=160, address=224, length=32, byte=b"\x80"), DolDescriptor(index=11, offset=128, address=256, length=32, byte=b"\x90"), DolDescriptor(index=12, offset=96, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=13, offset=64, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=14, offset=32, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=15, offset=0, address=416, length=32, byte=b"\xE0")]) mappeurs_dict[10] = [ [256, 32, 0], [224, 32, 96], [192, 32, 128], [160, 32, 224], [128, 32, 256], [96, 32, 288], [64, 32, 352], [32, 32, 384], [0, 32, 416]] # offset reverse sorted from address / reversed create_dol("dol11.dol", [ DolDescriptor(index=1, offset=0, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=5, offset=32, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=8, offset=64, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=10, offset=96, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=11, offset=128, address=256, length=32, byte=b"\x90"), DolDescriptor(index=12, offset=160, address=224, length=32, byte=b"\x80"), DolDescriptor(index=13, offset=192, address=128, length=32, byte=b"\x50"), DolDescriptor(index=14, offset=224, address=96, length=32, byte=b"\x40"), DolDescriptor(index=15, offset=256, address=0, length=32, byte=b"\x10")]) mappeurs_dict[11] = [ [0, 32, 416], [32, 32, 384], [64, 32, 352], [96, 32, 288], [128, 32, 256], [160, 32, 224], [192, 32, 128], [224, 32, 96], [256, 32, 0]] # offset shuffled from address create_dol("dol12.dol", [ DolDescriptor(index=1, offset=192, address=128, length=32, byte=b"\x50"), DolDescriptor(index=5, offset=224, address=0, length=32, byte=b"\x10"), DolDescriptor(index=8, offset=160, address=96, length=32, byte=b"\x40"), DolDescriptor(index=10, offset=64, address=384, length=32, byte=b"\xD0"), DolDescriptor(index=11, offset=0, address=352, length=32, byte=b"\xC0"), DolDescriptor(index=12, offset=32, address=224, length=32, byte=b"\x80"), DolDescriptor(index=13, offset=128, address=416, length=32, byte=b"\xE0"), DolDescriptor(index=14, offset=256, address=288, length=32, byte=b"\xA0"), DolDescriptor(index=15, offset=96, address=256, length=32, byte=b"\x90")]) mappeurs_dict[12] = [ [192, 32, 128], [224, 32, 0], [160, 32, 96], [64, 32, 384], [0, 32, 352], [32, 32, 224], [128, 32, 416], [256, 32, 288], [96, 32, 256]] # Overlapping +3 sections matching right # [126, 320] # Middle -1 + 1 # [353, 383] \x55 intervals_list = [] # Sections: [0, 32], [96, 128], [128, 160], [224, 256], [256, 288], [288, 320], [352, 384], [384, 416], [416, 448] # Beginning of dol file # [0, 24] \x11 # Matching right before empty # [28, 32] \x22 # Matching left after empty # [96, 100] \x33 # Overlapping +3 sections matching right # [225, 383] \x55 # Middle # [390, 394] \x66 # Endin map with overlap # [414, 448] \x77 # [[begin, end, byte], ...] intervals_list.append([[0, 24, b"\x11"], [28, 32, b"\x22"], [96, 100, b"\x33"], [224, 320, b"\x44"], [225, 319, b"\x55"], [390, 394, b"\x66"], [414, 448, b"\x77"]]) intervals_list.append(sorted(intervals_list[0], key=lambda x: x, reverse=True)) intervals_list.append([[414, 448, b"\x77"], [353, 383, b"\x55"], [96, 100, b"\x33"], [390, 394, b"\x66"], [28, 32, b"\x22"], [224, 320, b"\x44"], [0, 24, b"\x11"]]) # Total file patch intervals_list.append([[0, 32, b"\x11"], [96, 160, b"\x22"], [224, 320, b"\x33"], [352, 448, b"\x44"]]) intervals_list.append(sorted(intervals_list[1], key=lambda x: x, reverse=True)) intervals_list.append([[96, 160, b"\x22"], [0, 32, b"\x11"], [352, 448, b"\x44"], [224, 320, b"\x33"]]) # same but shuffled test_dols(range(7,12), intervals_list) print("###############################################################################") print(f"# Cleaning test folders.") print("###############################################################################") shutil.rmtree(ini_path) shutil.rmtree(dol_tests_path) shutil.rmtree(dols_save_path) end = time() print("###############################################################################") print(f"# All tests are OK - elapsed time: {end - start}") print("###############################################################################")