diff --git a/gcmtool/gcmtest.py b/gcmtool/gcmtest.py index 636ebf7..96eddfb 100644 --- a/gcmtool/gcmtest.py +++ b/gcmtool/gcmtest.py @@ -3,9 +3,11 @@ from pathlib import Path import os import shutil from time import time +from gcmtool import Gcm +from gcmtool import InvalidDVDMagicError, InvalidUnpackFolderError, InvalidPackIsoError, InvalidFSTSizeError, DolSizeOverflowError, InvalidRootFileFolderCountError, InvalidFSTFileSizeError, FSTDirNotFoundError, FSTFileNotFoundError, BadAlignError -__version__ = "0.0.7" +__version__ = "0.0.8" __author__ = "rigodron, algoflash, GGLinnk" __license__ = "MIT" __status__ = "developpement" @@ -48,8 +50,8 @@ def print_paths_differences(folder1_paths:list, folder2_paths:list): print(path) -# compare two files def compare_files(file1_path:Path, file2_path:Path): + "Compare two files." CLUSTER_LEN = 131072 with file1_path.open("rb") as file1, file2_path.open("rb") as file2: # Init @@ -63,9 +65,11 @@ def compare_files(file1_path:Path, file2_path:Path): return True -# compare two GCM -# -> raise an exception if there is a difference def compare_GCM(folder1: Path, folder2: Path): + """ + Compare two GCM + -> raise an exception if there is a difference + """ folder1_root_paths = list((folder1 / "root").glob("**/*")) folder1_file_count = len(folder1_root_paths) print(f"compare \"{folder1}\" - \"{folder2}\" ({folder1_file_count} files)") @@ -110,7 +114,7 @@ def gcmtool_stats(path:Path): raise Exception("Error while getting stats.") -TEST_COUNT = 4 +TEST_COUNT = 5 start = time() print("###############################################################################") @@ -121,7 +125,6 @@ if unpack_path.is_dir() or unpack2_path.is_dir() or repack_path.is_dir(): raise Exception(f"Error - Please remove:\n-{unpack_path}\n-{unpack2_path}\n-{repack_path}") test_storage() - print("###############################################################################") print(f"# TEST 1/{TEST_COUNT}") print("# Comparing roms_path->unpack->[unpack_path] ROMs with [dolphin_unpack_path]") @@ -140,7 +143,6 @@ print("######################################################################### print(f"# TEST 2/{TEST_COUNT}") print("# Testing stats on folders & isos") print("###############################################################################") - for iso_path in roms_path.glob("*"): if iso_path.is_file(): gcmtool_stats(iso_path) @@ -192,20 +194,164 @@ unpack2_path.mkdir() for iso_path in repack_path.glob("*"): gcmtool_unpack(iso_path, unpack2_path / iso_path.name) -# remove repack_path -shutil.rmtree(repack_path) - # compare unpack_path unpack2_path for folder_path in unpack_path.glob("*"): compare_GCM(folder_path, unpack2_path / folder_path.name) +# remove unpack2_path +shutil.rmtree(unpack_path) +shutil.rmtree(unpack2_path) + +print("###############################################################################") +print(f"# TEST 5/{TEST_COUNT}") +print("# Testing exceptions.") +print("###############################################################################") +gcm = Gcm() +first_repacked = list(repack_path.glob("*"))[0] +# change DVD magic +with first_repacked.open("rb+") as first_repacked_file: + first_repacked_file.seek(0x1c) + first_repacked_file.write(b"\xC2\x33\x9F\x3E") + +try: + gcm.unpack(first_repacked, unpack2_path / first_repacked.name) + raise Exception("Error - InvalidDVDMagicError should have been triggered.") +except InvalidDVDMagicError: + print("Correct InvalidDVDMagicError triggered.") + +# restore DVD magic +with first_repacked.open("rb+") as first_repacked_file: + first_repacked_file.seek(0x1c) + first_repacked_file.write(b"\xC2\x33\x9F\x3D") + +(unpack2_path / first_repacked.stem).mkdir(parents=True) +try: + gcm.unpack(first_repacked, unpack2_path / first_repacked.stem) + raise Exception("Error - InvalidUnpackFolderError should have been triggered.") +except InvalidUnpackFolderError: + print("Correct InvalidUnpackFolderError triggered.") + +shutil.rmtree(unpack2_path) + +unpack_path.mkdir() +first_unpacked = unpack_path / "Final Fantasy - Crystal Chronicles (Europe) (En,Fr,De,Es,It).iso" +gcmtool_unpack(roms_path / first_unpacked.name, first_unpacked) +try: + gcm.pack(first_unpacked, first_repacked) + raise Exception("Error - InvalidPackIsoError should have been triggered.") +except InvalidPackIsoError: + print("Correct InvalidPackIsoError triggered.") + +fst_data = (first_unpacked / "sys/fst.bin").read_bytes() +(first_unpacked / "sys/fst.bin").write_bytes(fst_data + b"\x00") +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - InvalidFSTSizeError should have been triggered.") +except InvalidFSTSizeError: + print("Correct InvalidFSTSizeError triggered.") +(first_unpacked / "sys/fst.bin").write_bytes(fst_data) + +(first_unpacked / "root/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").mkdir() +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - InvalidRootFileFolderCountError should have been triggered.") +except InvalidRootFileFolderCountError: + print("Correct InvalidRootFileFolderCountError triggered.") +(first_unpacked / "root/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").rmdir() + +first_unpacked_file = None +first_unpacked_dir = None +for path in (first_unpacked / "root").glob("*"): + if path.is_file() and first_unpacked_file is None: + first_unpacked_file = path + elif path.is_dir() and first_unpacked_dir is None: + first_unpacked_dir = path + if first_unpacked_file is not None and first_unpacked_dir is not None: + break + +first_unpacked_file_data = first_unpacked_file.read_bytes() +first_unpacked_file.write_bytes(first_unpacked_file_data + b"\x00") +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - InvalidFSTFileSizeError should have been triggered.") +except InvalidFSTFileSizeError: + print("Correct InvalidFSTFileSizeError triggered.") +first_unpacked_file.write_bytes(first_unpacked_file_data) + +new_dir = first_unpacked_dir.rename(first_unpacked_dir.parent / (first_unpacked_dir.name +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - FSTDirNotFoundError should have been triggered.") +except FSTDirNotFoundError: + print("Correct FSTDirNotFoundError triggered.") +new_dir.rename(first_unpacked_dir) + +new_file = first_unpacked_file.rename(first_unpacked_file.parent / (first_unpacked_file.name +"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - FSTFileNotFoundError should have been triggered.") +except FSTFileNotFoundError: + print("Correct FSTFileNotFoundError triggered.") +new_file.rename(first_unpacked_file) + +try: + gcm.stats(first_unpacked, 0x800) + raise Exception("Error - BadAlignError should have been triggered.") +except BadAlignError: + print("Correct BadAlignError triggered.") + +#| 0001ec00 | 0023f9a0 | 00220da0 | boot.dol +#| 0023fa00 | 00276058 | 00036658 | fst.bin +# dol size for overflowing on FST + 1: 0023fa00 - 0001ec00 = 220e00 +backup_dol_data = (first_unpacked / "sys/boot.dol").read_bytes() +with (first_unpacked / "sys/boot.dol").open("rb+") as bootdol_file: + bootdol_file.seek(0x220e00) + bootdol_file.write(b"\x00") +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - DolSizeOverflowError should have been triggered.") +except DolSizeOverflowError: + print("Correct DolSizeOverflowError triggered.") +(first_unpacked / "sys/boot.dol").write_bytes(backup_dol_data) + +with (first_unpacked / "sys/boot.dol").open("rb+") as bootdol_file: + bootdol_file.seek(0x220dff) + bootdol_file.write(b"\x00") +gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "ok1.iso")) +print("Correct pack with max dol size before FST.") + +# patch boot.bin to put fst before dol and make dol overflow on first file +# fst_len = 00036658 and new offset 0001ec00 +with (first_unpacked / "sys/boot.bin").open("rb+") as bootbin: + bootbin.seek(0x420) # dol offset + bootbin.write(b"\x00\x05\x53\x00") # after the FST + # now seeked on FST offset + bootbin.write(b"\x00\x01\xEC\x00") # replace the dol +#| 0001ec00 | 00055258 | 00036658 | fst.bin +#| 00055300 | ? | ? | boot.dol +#| 00278000 | 005111de | 002991de | game.MAP +# max dol size = 00278000 - 00055300 = 222D00 +with (first_unpacked / "sys/boot.dol").open("rb+") as bootdol_file: + bootdol_file.seek(0x222cff) + bootdol_file.write(b"\x00") +gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "ok2.iso")) +print("Correct pack with max dol size before first file.") + +with (first_unpacked / "sys/boot.dol").open("rb+") as first_unpacked_file: + first_unpacked_file.seek(0x222d00) + first_unpacked_file.write(b"\x00") +try: + gcm.pack(first_unpacked, repack_path / (first_unpacked.stem + "except.iso")) + raise Exception("Error - DolSizeOverflowError should have been triggered.") +except DolSizeOverflowError: + print("Correct DolSizeOverflowError triggered.") # Remove tests folders print("###############################################################################") print(f"# Cleaning test folders.") print("###############################################################################") +shutil.rmtree(repack_path) shutil.rmtree(unpack_path) -shutil.rmtree(unpack2_path) end = time() print("###############################################################################")