diff --git a/isotool.py b/isotool.py index 2258e63..6418b8d 100644 --- a/isotool.py +++ b/isotool.py @@ -1,10 +1,13 @@ +import io import struct import argparse from pathlib import Path from dataclasses import dataclass +import typing -SCRIPT_VERSION = "1.0" - +SCRIPT_VERSION = "1.1" +SECTOR_SIZE = 0x800 +SYSTEM_AREA_SIZE = 0x10 * SECTOR_SIZE @dataclass class FileListData: @@ -102,8 +105,16 @@ def get_arguments(argv=None): return args -def dump_iso(iso_path: Path, filelist: Path, iso_files: Path) -> None: +def check_pvd(fp: typing.BinaryIO, pvd_loc: int) -> bool: + fp.seek(pvd_loc) + vd_type, vd_id = struct.unpack(" None: if iso_path.exists() is False: print(f"Could not to find '{iso_path.name}'!") return @@ -111,25 +122,70 @@ def dump_iso(iso_path: Path, filelist: Path, iso_files: Path) -> None: iso_files.mkdir(parents=True, exist_ok=True) with open(iso_path, "rb") as iso: - - iso.seek(0x809E) + # Sanity check + assert check_pvd(iso, 0x8000), "No valid PVD found in Layer0!" + + # Test dual-layer-dness + iso.seek(0x8050) + layer0_sector_count = struct.unpack("") + else: + print("WARNING: Iso data suggest this is a double layer image") + print(" but no valid PVD was found for Layer1, iso might be corrupt") + + path_parts = [] record_ends = [] record_pos = [] file_info = FileListInfo([], 0) - # get the root directory record off the PVD + # get the root directory record off the PVD in Layer1 + if has_second_layer: + iso.seek(layer1_pvd_pos + 0x9E) + dr_data_pos, dr_data_len = struct.unpack("= record_ends[-1]: + if len(record_ends) == 2 and has_second_layer: + print("") + print("< Dumping Second Layer >") + print("") + path_parts.append("") + read_offset = layer1_pvd_pos - (SECTOR_SIZE * 0x10) + in_layer1 = True + if len(record_ends) == 1: # If it's the last one, we finished break @@ -137,11 +193,12 @@ def dump_iso(iso_path: Path, filelist: Path, iso_files: Path) -> None: # Otherwise keep reading the previous one record_ends.pop() path_parts.pop() - iso.seek(record_pos.pop()) + iso.seek(record_pos.pop() + read_offset) continue # Parse the record inode = iso.tell() + print(inode) dr_len = struct.unpack(" None: # Go to its directory record record_pos.append(iso.tell()) record_ends.append(dr_data_pos + dr_data_len) + + # if has_second_layer: + # if in_layer1: + # fp = iso_files / "Layer1" + # else: + # fp = iso_files / "Layer0" + # else: + # fp = iso_files + fp = iso_files / "/".join(path_parts) - fp.mkdir(exist_ok=True) - iso.seek(dr_data_pos) + fp.mkdir(exist_ok=True, parents=True) + iso.seek(dr_data_pos + read_offset) continue else: # Otherwise dump the file @@ -196,11 +262,11 @@ def dump_iso(iso_path: Path, filelist: Path, iso_files: Path) -> None: save_pos = iso.tell() with open(iso_files / fp, "wb+") as f: - iso.seek(dr_data_pos) + iso.seek(dr_data_pos + read_offset) f.write(iso.read(dr_data_len)) iso.seek(save_pos) - file_info.files.append(FileListData(Path(fp), inode, dr_data_pos)) + file_info.files.append(FileListData(Path(fp), inode, dr_data_pos + read_offset)) path_parts.pop() # The filelist file has the files ordered based on their disc position