diff --git a/tools/audit_dump.py b/tools/audit_dump.py index c734c54650..f9ef69e0db 100644 --- a/tools/audit_dump.py +++ b/tools/audit_dump.py @@ -11,7 +11,7 @@ import import_audit import unwrap_self DESCRIPTION = """Audit the imports of an extracted title before it is launched. Unwraps the dump's eboot.bin and the modules -it ships (sce_module/, sce_modules/ and prx/, Unity's Media/Modules/ and Media/Plugins/, and .prx files beside +it ships (sce_module/, sce_modules/ and prx/, Unity's Media/Modules/ and Media/Plugins/, and module files beside eboot.bin) into a work directory, converts them with relinker --registry, and runs tools/import_audit.py on the registries with the unwrapped modules as --modules. The dump is only read; the source/ and output/ directories of a reused --work directory are emptied first. Exit code: that of import_audit.py, or 2 @@ -19,7 +19,7 @@ when unwrapping or conversion fails.""" STANDARD_DIRECTORIES = ("sce_module", "sce_modules", "prx") EXTRA_DIRECTORIES = ("Media/Modules", "Media/Plugins") -MODULE_SUFFIXES = (".prx", ".sprx") +MODULE_SUFFIXES = import_audit.MODULE_FILE_SUFFIXES ELF_MAGIC = b"\x7fELF" diff --git a/tools/tests/test_audit_dump.py b/tools/tests/test_audit_dump.py index eb98b5acab..f70d8c5c83 100644 --- a/tools/tests/test_audit_dump.py +++ b/tools/tests/test_audit_dump.py @@ -69,6 +69,36 @@ class AuditDumpTests(unittest.TestCase): with self.assertRaises(unwrap_self.Refused): audit_dump.prepare(self.dump, self.root / "source", log=lambda text: None) + def test_extended_module_suffixes_are_staged_at_every_module_location(self): + locations = (*audit_dump.STANDARD_DIRECTORIES, *audit_dump.EXTRA_DIRECTORIES, ".") + names = ("plain.prx", "shared.sprx", "user.suprx", "debug.debug_prx", "upper.SUPRX", "upper.DEBUG_PRX") + staged = [] + for location in locations: + for index, name in enumerate(names): + relative = Path(location) / name + self.module(relative, ELF if index % 2 else SELF) + staged.append(relative) + for name in ("backup.suprx.bak", "debug.debug_prx.txt"): + self.module(Path(location) / name, b"not a module") + original = {path: path.read_bytes() for path in self.dump.rglob("*") if path.is_file()} + source = self.root / "source" + directories, extra = audit_dump.prepare(self.dump, source, log=lambda text: None) + for relative in staged: + self.assertTrue((source / relative).read_bytes().startswith(ELF), str(relative)) + copied = {path.relative_to(source) for path in source.rglob("*") if path.is_file()} + self.assertEqual(copied, {Path("eboot.elf"), *staged}) + self.assertEqual(directories, [source / location for location in locations]) + self.assertEqual(extra, [*audit_dump.EXTRA_DIRECTORIES, "."]) + self.assertEqual({path: path.read_bytes() for path in original}, original) + + def test_invalid_extended_modules_are_not_silently_skipped(self): + for name in ("bad.suprx", "bad.debug_prx"): + with self.subTest(name=name): + self.module("sce_module/" + name, b"MZ" + b"\0" * 62) + with self.assertRaises(unwrap_self.Refused): + audit_dump.prepare(self.dump, self.root / "source", log=lambda text: None) + (self.dump / "sce_module" / name).unlink() + def test_the_relinker_gets_the_registry_windows_and_module_dir_options(self): with mock.patch.object(subprocess, "run", return_value=subprocess.CompletedProcess([], 0, "", "")) as run: audit_dump.convert(self.relinker, self.root / "source", self.root / "out" / "app.exe", ["Media/Modules", "."], True)