NeoGF/doltool/doltest.py
2022-04-23 22:30:22 +02:00

1236 lines
66 KiB
Python

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.3"
__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):
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 len(tmp_list) > 0:
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:
print(f"{memory_object.address():08x} {memory_object.length():08x}")
raise("doltest: Invalid ARCode length - should be aligned to 2 or 4")
return str_buffer
def create_memory_objects_from_intervals(*intervals:list):
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 [
[create_memory_objects_from_intervals([0,10]), create_memory_objects_from_intervals([10,20])], # Before
[create_memory_objects_from_intervals([20,30]), create_memory_objects_from_intervals([10,20])], # After
[create_memory_objects_from_intervals([0,10],[20,30]), create_memory_objects_from_intervals([10,20])], # Before and after
[create_memory_objects_from_intervals([0,11],[20,30]), create_memory_objects_from_intervals([11,20])], # left truncate
[create_memory_objects_from_intervals([0,11],[19,30]), create_memory_objects_from_intervals([11,19])], # left and right truncate
[create_memory_objects_from_intervals([0,10],[19,30]), create_memory_objects_from_intervals([10,19])], # right truncate
[create_memory_objects_from_intervals([0,11],[12,13],[14,15],[19,30]), create_memory_objects_from_intervals([11,12],[13,14],[15,19])], # left middle and right truncate
[create_memory_objects_from_intervals([0,11],[11,13],[13,15],[19,30]), create_memory_objects_from_intervals([15,19])], # following truncates left truncate rigth truncate
[create_memory_objects_from_intervals([0,11],[11,13],[13,15],[15,20]), None], # following truncates overlap with end match
[create_memory_objects_from_intervals([10,13],[13,15],[15,25]), None], # following truncates overlap with begin match
[create_memory_objects_from_intervals([10,13],[13,15],[15,20]), None], # following truncates in with begin and end match
[create_memory_objects_from_intervals([11,13],[13,15],[15,19]), create_memory_objects_from_intervals([10,11],[19,20])], # following truncates in
[create_memory_objects_from_intervals([10,13],[13,15],[15,19]), create_memory_objects_from_intervals([19,20])], # following truncates in with begin match
[create_memory_objects_from_intervals([11,13],[13,15],[15,20]), create_memory_objects_from_intervals([10,11])], # following truncates in with end match
[create_memory_objects_from_intervals([0,30]), None], # total overlap overflowing left right
[create_memory_objects_from_intervals([10,30]), None], # total overlap overflowing left
[create_memory_objects_from_intervals([0,20]), None], # total overlap overflowing right
[create_memory_objects_from_intervals([10,20]), None]]: # total match
res_interval = interval - intervals_to_remove
if expected_res is None and res_interval is None:
print("Correct result.")
continue
for a in res_interval:
print(a)
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\n022E2CCC 00050136\n022E2CD8 0005012C\n022E2CE4 000500D2\n042E4E2A 0000005A\n042E4F92 000001E0\n042E50FA 0000005A\n042E5262 0001003C\n042E53CA 00000078\n042E5532 0000003C\n042E569A 0000003C\n042E5802 00000078\n042E596A 0000000A\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")]
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","0002E2CC0 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"\x11"], # overlap right match left
[120, 170, b"\x11"], # overlap left and right
[191, 225, b"\x11"], # overlap left and right +1-1
[255, 353, b"\x11"]]) # 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"\x11"], # overlap left and right +1-1
[120, 170, b"\x11"], # overlap left and right
[96, 130, b"\x11"], # overlap right match left
[24, 64, b"\x11"]]) # overlap left match right
intervals_list.append([ # same but shuffled
[120, 170, b"\x11"], # overlap left and right
[255, 353, b"\x11"], # overlap left and right +- 3 sections (reversed)
[96, 130, b"\x11"], # overlap right match left
[24, 64, b"\x11"], # overlap left match right
[191, 225, b"\x11"]]) # 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"]])
"""
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():
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"]])
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("###############################################################################")