Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package redumper for openSUSE:Factory checked in at 2026-07-06 15:00:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/redumper (Old) and /work/SRC/openSUSE:Factory/.redumper.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "redumper" Mon Jul 6 15:00:30 2026 rev:7 rq:1364017 version:729 Changes: -------- --- /work/SRC/openSUSE:Factory/redumper/redumper.changes 2026-06-08 14:25:55.502562315 +0200 +++ /work/SRC/openSUSE:Factory/.redumper.new.1982/redumper.changes 2026-07-06 15:00:33.098513330 +0200 @@ -1,0 +2,15 @@ +Mon Jul 6 09:01:05 UTC 2026 - Martin Hauke <[email protected]> + +- Update to version 729 + * Show built-in option defaults in --help regardless of + overrides. + * Stop generating DVD ecc and replace with null bytes. + * Redump dot info #397. + * Print PS3 firmware version. + * Log hfs volume identifier. + * Add new Drives #387. +- Update patches + * redumper-no-static-linking.patch + * redumper-use-system-googletest.patch + +------------------------------------------------------------------- Old: ---- redumper-b722.tar.gz New: ---- redumper-b729.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ redumper.spec ++++++ --- /var/tmp/diff_new_pack.MYpczN/_old 2026-07-06 15:00:33.882540280 +0200 +++ /var/tmp/diff_new_pack.MYpczN/_new 2026-07-06 15:00:33.882540280 +0200 @@ -17,7 +17,7 @@ Name: redumper -Version: 722 +Version: 729 Release: 0 Summary: Low level CD dumper utility License: GPL-3.0-only ++++++ redumper-b722.tar.gz -> redumper-b729.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/CMakeLists.txt new/redumper-b729/CMakeLists.txt --- old/redumper-b722/CMakeLists.txt 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/CMakeLists.txt 2026-07-05 18:46:51.000000000 +0200 @@ -128,6 +128,11 @@ "dvd/dvd_split.ixx" "dvd/nintendo.ixx" "dvd/xbox.ixx" + "filesystem/apm/apm.ixx" + "filesystem/apm/apm_browser.ixx" + "filesystem/apm/apm_defs.ixx" + "filesystem/hfs/hfs.ixx" + "filesystem/hfs/hfs_defs.ixx" "filesystem/high_sierra/high_sierra.ixx" "filesystem/high_sierra/high_sierra_browser.ixx" "filesystem/high_sierra/high_sierra_defs.ixx" @@ -143,9 +148,11 @@ "hash/sha1.ixx" "readers/disc_read_cdda_reader.ixx" "readers/disc_read_reader.ixx" + "readers/emulated_sector_reader.ixx" "readers/image_bin_reader.ixx" "readers/image_iso_reader.ixx" "readers/image_raw_reader.ixx" + "readers/image_disk_reader.ixx" "readers/image_scram_reader.ixx" "readers/image_simple_reader.ixx" "readers/data_reader.ixx" @@ -165,6 +172,7 @@ "systems/ps5.ixx" "systems/sat.ixx" "systems/s_xbox.ixx" + "systems/s_hfs.ixx" "systems/gc.ixx" "systems/wii.ixx" "systems/systems.ixx" @@ -222,7 +230,8 @@ target_link_libraries(redumper ${libs}) if(REDUMPER_TARGET_LINUX) - target_link_options(redumper PRIVATE "-static") + set(REDUMPER_LINKER_FLAGS "-static" CACHE STRING "Linker flags") + target_link_options(redumper PRIVATE "${REDUMPER_LINKER_FLAGS}") endif() install(TARGETS redumper DESTINATION "bin") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/cd/cd_dump.ixx new/redumper-b729/cd/cd_dump.ixx --- old/redumper-b722/cd/cd_dump.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/cd/cd_dump.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -139,7 +139,7 @@ subcode_shift = shift; if(options.verbose) - LOG_R("[LBA: {:6}] subcode desync (shift: {:+})", lba, subcode_shift); + LOGC_R("[LBA: {:6}] subcode desync (shift: {:+})", lba, subcode_shift); } } } @@ -435,7 +435,7 @@ errors.scsi += CD_DATA_SIZE_SAMPLES; if(options.verbose) - LOG_R("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); + LOGC_R("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); } } else @@ -491,7 +491,7 @@ sector_c2_backup.assign(sector_c2.begin(), sector_c2.end()); - LOG_R("[LBA: {:6}] C2 error (bits: {:4}{})", lba, c2_bits_count(sector_c2), difference_message); + LOGC_R("[LBA: {:6}] C2 error (bits: {:4}{})", lba, c2_bits_count(sector_c2), difference_message); } } @@ -525,7 +525,7 @@ if(!dump && lba < lba_end && (data_updated || subcode_updated)) { if(options.verbose) - LOG_R("[LBA: {:6}] correction success", lba); + LOGC_R("[LBA: {:6}] correction success", lba); } break; @@ -558,7 +558,7 @@ failure = true; if(failure) - LOG_R("[LBA: {:6}] correction failure{}", lba, data_message); + LOGC_R("[LBA: {:6}] correction failure{}", lba, data_message); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/cd/cd_dump_extra.ixx new/redumper-b729/cd/cd_dump_extra.ixx --- old/redumper-b722/cd/cd_dump_extra.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/cd/cd_dump_extra.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -235,7 +235,7 @@ bool all_types = false; SPTD::Status status = read_sector(*ctx.sptd, sector_buffer.data(), all_types, ctx.drive_config, lba, false); if(status.status_code && options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); status = mediatek_cache_read(*ctx.sptd, cache, mediatek_get_config(ctx.drive_config.type).size_mb * CHUNK_1MB); if(status.status_code) @@ -299,7 +299,7 @@ sector_c2_leadout_backup.assign(sector_c2_leadout.begin(), sector_c2_leadout.end()); - LOG_R("[LBA: {:6}] C2 error (bits: {:4}{})", lba + i, c2_bits, difference_message); + LOGC_R("[LBA: {:6}] C2 error (bits: {:4}{})", lba + i, c2_bits, difference_message); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/drive/test.ixx new/redumper-b729/drive/test.ixx --- old/redumper-b722/drive/test.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/drive/test.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -107,7 +107,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); } else { @@ -118,7 +118,7 @@ if(std::all_of(sector_c2_sub.begin(), sector_c2_sub.end(), [](uint8_t value) { return value == 0; })) { if(options.verbose) - LOG("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); + LOGC("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); continue; } } @@ -161,7 +161,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); } else { @@ -172,7 +172,7 @@ if(std::all_of(sector_c2_sub.begin(), sector_c2_sub.end(), [](uint8_t value) { return value == 0; })) { if(options.verbose) - LOG("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); + LOGC("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); continue; } } @@ -215,7 +215,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); } else { @@ -226,7 +226,7 @@ if(std::all_of(sector_c2_sub.begin(), sector_c2_sub.end(), [](uint8_t value) { return value == 0; })) { if(options.verbose) - LOG("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); + LOGC("warning: subcode is zeroed, skipping (sector type: {}, error field: {}, sub channel: {})", std::get<0>(t), ef.second, sc.second); continue; } } @@ -273,7 +273,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", std::get<1>(t), SPTD::StatusMessage(status)); } else { @@ -310,7 +310,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); } else { @@ -332,7 +332,7 @@ if(status.status_code) { if(options.verbose) - LOG("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); + LOGC("[LBA: {:6}] SCSI error ({})", lba, SPTD::StatusMessage(status)); break; } else @@ -353,7 +353,7 @@ if(status.status_code) { if(options.verbose) - LOG("read cache failed, SCSI ({})", SPTD::StatusMessage(status)); + LOGC("read cache failed, SCSI ({})", SPTD::StatusMessage(status)); } else { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/drive.ixx new/redumper-b729/drive.ixx --- old/redumper-b722/drive.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/drive.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -179,6 +179,8 @@ { "HL-DT-ST", "BD-RE BP55EB40" , "1.00", "", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::MTK2B }, // AngelDevIndie // PATCHED { "TSSTcorp", "DVD-ROM TS-H353C" , "ZZ00", "" , "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // MoriGM + { "HL-DT-ST", "DVD-ROM DH41N" , "ZZ00", "" , "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // MoriGM + { "PLDS" , "DVD-ROM DH-16D5S" , "ZZ00", "2025-06-18 15:03 Fen", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // MoriGM { "hp" , "DVD RW AD-7290H5" , "SE02", "" , "", +48, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // seri { "Optiarc" , "DVD RW AD-7173A" , "1-04", "seri-v1 BT-LIGGY" , "", +48, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // seri { "NEC" , "DVD_RW ND-3520AW" , "3.07", "seri-01 BT-LIGGY" , "", +48, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // seri @@ -190,6 +192,8 @@ { "Optiarc" , "DVD RW AD-7203A" , "1.09", "seri-01 BT-LIGGY" , "", +48, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // seri // PATCHED KREON { "TSSTcorp", "DVD-ROM SH-D163B" , "ZZ01", "KREON V1.00.........", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // MoriGM + { "TSSTcorp", "DVD-ROM SH-D163A" , "DC02", "KREON V1.00.........", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_SUB_C2, Type::GENERIC }, // MoriGM + { "TSSTcorp", "DVD-ROM SH-D162D" , "ZZ01", "KREON V1.00.........", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC }, // MoriGM { "TSSTcorp", "DVD-ROM SH-D162C" , "DC02", "KREON V1.00.........", "", +6, 0, -135, ReadMethod::BE, SectorOrder::DATA_SUB_C2, Type::GENERIC }, // MoriGM // RECOMMENDED (GOOD) DRIVES ABOVE THIS LINE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/dvd/dvd.ixx new/redumper-b729/dvd/dvd.ixx --- old/redumper-b722/dvd/dvd.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/dvd/dvd.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -121,37 +121,6 @@ }; -void compute_parity_inner(std::span<uint8_t> parity_inner, std::span<const uint8_t> main_data) -{ - // PI: RS(n,k,d) where n=data_size+parity_size, k=data_size, d=parity_size - // generator polynomial roots: alpha^0, alpha^1, ..., alpha^(parity_size-1) - uint8_t parity[ECC_FRAMES] = {}; - for(uint32_t col = 0; col < main_data.size(); ++col) - { - uint8_t feedback = gf.add(main_data[col], parity[0]); - for(uint32_t j = 0; j < parity_inner.size() - 1; ++j) - parity[j] = gf.add(parity[j + 1], gf.mul(feedback, gf.exp[parity_inner.size() - 1 - j])); - parity[parity_inner.size() - 1] = gf.mul(feedback, gf.exp[0]); - } - for(uint32_t j = 0; j < parity_inner.size(); ++j) - parity_inner[j] = parity[parity_inner.size() - 1 - j]; -} - - -void compute_parity_outer(std::span<uint8_t> parity_outer, std::span<const uint8_t> main_data, uint32_t stride) -{ - // PO: simplified single-frame encoding - // treat each column as data_size bytes and compute 1 parity byte - for(uint32_t col = 0; col < parity_outer.size(); ++col) - { - uint8_t parity = 0; - for(uint32_t row = 0; row < main_data.size() / stride; ++row) - parity = gf.add(parity, main_data[row * stride + col]); - parity_outer[col] = parity; - } -} - - export RecordingFrame DataFrame_to_RecordingFrame(const DataFrame &data_frame) { RecordingFrame recording_frame = {}; @@ -161,11 +130,6 @@ for(uint32_t i = 0; i < std::size(recording_frame.row); ++i) std::copy_n(src + i * std::size(recording_frame.row[i].main_data), std::size(recording_frame.row[i].main_data), recording_frame.row[i].main_data); - for(uint32_t i = 0; i < std::size(recording_frame.row); ++i) - compute_parity_inner(recording_frame.row[i].parity_inner, recording_frame.row[i].main_data); - - compute_parity_outer(recording_frame.parity_outer, std::span((uint8_t *)recording_frame.row, sizeof(recording_frame.row)), sizeof(recording_frame.row[0])); - return recording_frame; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/dvd/dvd_dump.ixx new/redumper-b729/dvd/dvd_dump.ixx --- old/redumper-b722/dvd/dvd_dump.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/dvd/dvd_dump.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -695,7 +695,7 @@ updated = true; if(!dump && options.verbose) - LOG_R("[LBA: {}] correction success", lba + i); + LOGC_R("[LBA: {}] correction success", lba + i); } } else @@ -706,7 +706,7 @@ updated = true; if(!dump && options.verbose) - LOG_R("[LBA: {}] correction success", lba + i); + LOGC_R("[LBA: {}] correction success", lba + i); } } @@ -1213,7 +1213,7 @@ if(status.status_code) throw_line("kreon: failed lock drive, SCSI ({})", SPTD::StatusMessage(status)); if(options.verbose) - LOG_R("[LBA: {}] kreon: drive locked", lba); + LOGC_R("[LBA: {}] kreon: drive locked", lba); kreon_locked = true; } @@ -1268,7 +1268,7 @@ } if(options.verbose) - LOG_R("[LBA: {}] SCSI error ({}){}", lba + i, SPTD::StatusMessage(status), status_retries); + LOGC_R("[LBA: {}] SCSI error ({}){}", lba + i, SPTD::StatusMessage(status), status_retries); } } else @@ -1284,7 +1284,7 @@ } if(options.verbose) - LOG_R("[LBA: {}] EDC error{}", lba + i, status_retries); + LOGC_R("[LBA: {}] EDC error{}", lba + i, status_retries); } } } @@ -1317,7 +1317,7 @@ else { if(options.verbose) - LOG_R("[LBA: {} .. {}] correction failure", lba, lba + (int32_t)sectors_to_read - 1); + LOGC_R("[LBA: {} .. {}] correction failure", lba, lba + (int32_t)sectors_to_read - 1); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/filesystem/apm/apm.ixx new/redumper-b729/filesystem/apm/apm.ixx --- old/redumper-b722/filesystem/apm/apm.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/filesystem/apm/apm.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,4 @@ +export module filesystem.apm; + +export import :browser; +export import :defs; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/filesystem/apm/apm_browser.ixx new/redumper-b729/filesystem/apm/apm_browser.ixx --- old/redumper-b722/filesystem/apm/apm_browser.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/filesystem/apm/apm_browser.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,47 @@ +module; +#include <cstdint> +#include <ostream> +#include <vector> + +export module filesystem.apm:browser; + +import :defs; +import readers.data_reader; +import utils.strings; +import utils.endian; + + +export namespace gpsxre::apm +{ + +class Browser +{ +public: + static std::vector<PartitionMapEntry> getPartitions(DataReader *data_reader) + { + std::vector<PartitionMapEntry> partitions; + + DriveDescriptor drive_descriptor; + if(data_reader->read((uint8_t *)&drive_descriptor, data_reader->sectorsBase(), 1) == 1) + { + if(to_string_view(drive_descriptor.signature) == DRIVE_DESCRIPTOR_SIGNATURE) + { + PartitionMapEntry partition_map_entry; + uint32_t entry_index = 0; + constexpr uint32_t MAX_PARTITION_ENTRIES = 512; + do + { + if(data_reader->read((uint8_t *)&partition_map_entry, data_reader->sectorsBase() + PARTITION_MAP_OFFSET + entry_index, 1) != 1) + break; + + if(to_string_view(partition_map_entry.signature) == PARTITION_MAP_ENTRY_SIGNATURE) + partitions.push_back(partition_map_entry); + } while(++entry_index < std::min(endian_swap(partition_map_entry.entry_count), MAX_PARTITION_ENTRIES)); + } + } + + return partitions; + } +}; + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/filesystem/apm/apm_defs.ixx new/redumper-b729/filesystem/apm/apm_defs.ixx --- old/redumper-b722/filesystem/apm/apm_defs.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/filesystem/apm/apm_defs.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,117 @@ +module; +#include <cstdint> +#include <string_view> + +export module filesystem.apm:defs; + + + +export namespace gpsxre::apm +{ + +// Based on https://keramics.github.io/apm.html + +constexpr std::string_view DRIVE_DESCRIPTOR_SIGNATURE = "ER"; +constexpr std::string_view PARTITION_MAP_ENTRY_SIGNATURE = "PM"; +constexpr uint32_t PARTITION_MAP_OFFSET = 1; + +constexpr std::string_view PARTITION_TYPE_APPLE_BOOT = "Apple_Boot"; +constexpr std::string_view PARTITION_TYPE_APPLE_BOOT_RAID = "Apple_Boot_RAID"; +constexpr std::string_view PARTITION_TYPE_APPLE_BOOTSTRAP = "Apple_Bootstrap"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER = "Apple_Driver"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER43 = "Apple_Driver43"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER43_CD = "Apple_Driver43_CD"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER_ATA = "Apple_Driver_ATA"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER_ATAPI = "Apple_Driver_ATAPI"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER_IOKIT = "Apple_Driver_IOKit"; +constexpr std::string_view PARTITION_TYPE_APPLE_DRIVER_OPENFIRMWARE = "Apple_Driver_OpenFirmware"; +constexpr std::string_view PARTITION_TYPE_APPLE_EXTRA = "Apple_Extra"; +constexpr std::string_view PARTITION_TYPE_APPLE_FREE = "Apple_Free"; +constexpr std::string_view PARTITION_TYPE_APPLE_FWDRIVER = "Apple_FWDriver"; +constexpr std::string_view PARTITION_TYPE_APPLE_HFS = "Apple_HFS"; +constexpr std::string_view PARTITION_TYPE_APPLE_HFSX = "Apple_HFSX"; +constexpr std::string_view PARTITION_TYPE_APPLE_LOADER = "Apple_Loader"; +constexpr std::string_view PARTITION_TYPE_APPLE_MDFW = "Apple_MDFW"; +constexpr std::string_view PARTITION_TYPE_APPLE_MFS = "Apple_MFS"; +constexpr std::string_view PARTITION_TYPE_APPLE_PARTITION_MAP = "Apple_partition_map"; +constexpr std::string_view PARTITION_TYPE_APPLE_PATCHES = "Apple_Patches"; +constexpr std::string_view PARTITION_TYPE_APPLE_PRODOS = "Apple_PRODOS"; +constexpr std::string_view PARTITION_TYPE_APPLE_RAID = "Apple_RAID"; +constexpr std::string_view PARTITION_TYPE_APPLE_RHAPSODY_UFS = "Apple_Rhapsody_UFS"; +constexpr std::string_view PARTITION_TYPE_APPLE_SCRATCH = "Apple_Scratch"; +constexpr std::string_view PARTITION_TYPE_APPLE_SECOND = "Apple_Second"; +constexpr std::string_view PARTITION_TYPE_APPLE_UFS = "Apple_UFS"; +constexpr std::string_view PARTITION_TYPE_APPLE_UNIX_SVR2 = "Apple_UNIX_SVR2"; +constexpr std::string_view PARTITION_TYPE_APPLE_VOID = "Apple_Void"; +constexpr std::string_view PARTITION_TYPE_BE_BFS = "Be_BFS"; +constexpr std::string_view PARTITION_TYPE_MFS = "MFS"; + + +enum StatusFlags : uint32_t +{ + VALID = 1u << 0, + ALLOCATED = 1u << 1, + IN_USE = 1u << 2, + HAS_BOOT_INFO = 1u << 3, + READABLE = 1u << 4, + WRITABLE = 1u << 5, + PIC_BOOT_CODE = 1u << 6, + + HAS_COMPATIBLE_DRIVER = 1u << 8, + HAS_REAL_DRIVER = 1u << 9, + HAS_CHAIN_DRIVER = 1u << 10, + + AUTOMATIC_MOUNT = 1u << 30, + STARTUP_PARTITION = 1u << 31, +}; + + +struct DeviceDriverDescriptor +{ + uint32_t start_block; + uint16_t blocks; + uint16_t os; +}; + + +struct DriveDescriptor +{ + uint8_t signature[2]; + uint16_t block_size; + uint32_t block_count; + uint16_t device_type; + uint16_t device_identifier; + uint32_t device_data; + uint16_t driver_descriptor_count; + uint8_t first_driver_descriptor[8]; + uint8_t additional_driver_descriptors[484]; +}; + +static_assert(sizeof(DriveDescriptor) == 512, "DriveDescriptor must match one 512-byte sector"); + +struct PartitionMapEntry +{ + uint8_t signature[2]; + uint8_t reserved[2]; + uint32_t entry_count; + uint32_t partition_start_sector; + uint32_t partition_sector_count; + char partition_name[32]; + char partition_type[32]; + uint32_t data_area_start_sector; + uint32_t data_area_sector_count; + StatusFlags status_flags; + uint32_t boot_code_start_sector; + uint32_t boot_code_sector_count; + uint32_t boot_code_address; + uint32_t unknown_1; + uint32_t boot_code_entry_point; + uint32_t unknown_2; + uint32_t boot_code_checksum; + uint8_t processor_type[16]; + uint8_t unknown_3[2][188]; +}; + +static_assert(sizeof(PartitionMapEntry) == 512, "PartitionMapEntry must match one 512-byte sector"); + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/filesystem/hfs/hfs.ixx new/redumper-b729/filesystem/hfs/hfs.ixx --- old/redumper-b722/filesystem/hfs/hfs.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/filesystem/hfs/hfs.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,3 @@ +export module filesystem.hfs; + +export import :defs; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/filesystem/hfs/hfs_defs.ixx new/redumper-b729/filesystem/hfs/hfs_defs.ixx --- old/redumper-b722/filesystem/hfs/hfs_defs.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/filesystem/hfs/hfs_defs.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,72 @@ +module; +#include <array> +#include <cstdint> + +export module filesystem.hfs:defs; + +import utils.strings; + + + +export namespace gpsxre::hfs +{ + +// Based on https://developer.apple.com/library/archive/documentation/mac/pdf/Files/File_Manager.pdf + +constexpr uint16_t VOLUME_SIGNATURE_HFS = 0x4244; +constexpr uint16_t VOLUME_SIGNATURE_MFS = 0xD2D7; +constexpr uint32_t MDB_OFFSET = 2; + +#pragma pack(push, 1) +struct ExtentDescriptor +{ + uint16_t extent_start_allocation_block; + uint16_t extent_num_allocation_blocks; +}; + + +struct ExtentDataRecord +{ + std::array<ExtentDescriptor, 3> extents; +}; + + +struct MasterDirectoryBlock +{ + uint16_t volume_signature; + uint32_t volume_creation_date; + uint32_t volume_last_modification_date; + uint16_t volume_attributes; + uint16_t root_directory_file_count; + uint16_t volume_bitmap_start_block; + uint16_t allocation_search_start_block; + uint16_t total_allocation_blocks; + uint32_t allocation_block_size; + uint32_t default_clump_size; + uint16_t first_allocation_block; + uint32_t next_catalog_node_id; + uint16_t free_allocation_blocks; + pascal_string<27> volume_name; + uint32_t last_backup_date; + uint16_t volume_backup_sequence_number; + uint32_t volume_write_count; + uint32_t extents_overflow_clump_size; + uint32_t catalog_file_clump_size; + uint16_t root_directory_directory_count; + uint32_t total_file_count; + uint32_t total_directory_count; + std::array<uint32_t, 8> finder_information; + uint16_t volume_cache_size; + uint16_t volume_bitmap_cache_size; + uint16_t common_volume_cache_size; + uint32_t extents_overflow_file_size; + ExtentDataRecord extents_overflow_extent_record; + uint32_t catalog_file_size; + ExtentDataRecord catalog_file_extent_record; + uint8_t reserved[350]; +}; +#pragma pack(pop) + +static_assert(sizeof(MasterDirectoryBlock) == 512, "MasterDirectoryBlock must match one 512-byte sector"); + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/info.ixx new/redumper-b729/info.ixx --- old/redumper-b722/info.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/info.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -19,6 +19,7 @@ import readers.image_bin_reader; import readers.image_iso_reader; import readers.image_raw_reader; +import readers.image_disk_reader; import readers.data_reader; import systems.systems; import utils.hex_bin; @@ -55,6 +56,7 @@ { std::shared_ptr<DataReader> raw_reader; std::shared_ptr<DataReader> form1_reader; + std::shared_ptr<DataReader> emulated_disk_reader; if(track_type_is_data_iso(t.second)) form1_reader = std::make_shared<Image_ISO_Reader>(t.first); @@ -64,16 +66,26 @@ form1_reader = std::make_shared<Image_BIN_Reader>(t.first); } + if(form1_reader) + emulated_disk_reader = std::make_shared<Image_Disk_Reader>(form1_reader); + for(auto const &s : Systems::get()) { auto system = s(); - auto reader = system->getType() == System::Type::ISO ? form1_reader : raw_reader; + std::shared_ptr<DataReader> reader; + if(system->getType() == System::Type::ISO) + reader = form1_reader; + else if(system->getType() == System::Type::DISK) + reader = emulated_disk_reader; + else + reader = raw_reader; + if(!reader) continue; std::stringstream ss; - system->printInfo(ss, reader.get(), t.first, options.verbose); + system->printInfo(ss, reader.get(), t.first); if(ss.rdbuf()->in_avail()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/options.ixx new/redumper-b729/options.ixx --- old/redumper-b722/options.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/options.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -84,6 +84,15 @@ int cdr_error_threshold; int scsi_timeout; + // built-in defaults for numeric options, shared by the constructor and printUsage() so help shows the compile-time default rather than an argv override + static constexpr int retries_default = 0; + static constexpr int skip_fill_default = 0x55; + static constexpr int plextor_leadin_retries_default = 4; + static constexpr int mediatek_leadout_retries_default = 32; + static constexpr int audio_silence_threshold_default = 32; + static constexpr int cdr_error_threshold_default = 16; + static constexpr int scsi_timeout_default = 50000; + Options(int argc, const char *argv[]) : help(false) @@ -97,26 +106,26 @@ , overwrite(false) , force_split(false) , leave_unchanged(false) - , retries(0) + , retries(retries_default) , refine_subchannel(false) , refine_sector_mode(false) , lba_end_by_subcode(false) , force_qtoc(false) , legacy_subs(false) - , skip_fill(0x55) + , skip_fill(skip_fill_default) , filesystem_trim(false) , plextor_skip_leadin(false) - , plextor_leadin_retries(4) + , plextor_leadin_retries(plextor_leadin_retries_default) , plextor_leadin_force_store(false) , mediatek_skip_leadout(false) - , mediatek_leadout_retries(32) + , mediatek_leadout_retries(mediatek_leadout_retries_default) , kreon_partial_ss(false) , dvd_raw(false) , bd_raw(false) , disable_cdtext(false) , correct_offset_shift(false) , offset_shift_relocate(false) - , audio_silence_threshold(32) + , audio_silence_threshold(audio_silence_threshold_default) , overread_leadout(false) , force_unscrambled(false) , force_refine(false) @@ -125,8 +134,8 @@ , drive_test_skip_cache_read(false) , skip_subcode_desync(false) , rings(false) - , cdr_error_threshold(16) - , scsi_timeout(50000) + , cdr_error_threshold(cdr_error_threshold_default) + , scsi_timeout(scsi_timeout_default) { for(int i = 1; i < argc; ++i) arguments += str_quoted_if_space(argv[i]) + " "; @@ -400,7 +409,7 @@ LOG("\t--skeleton \tgenerate skeleton after dump"); LOG("\t--drive=VALUE \tdrive to use, first available drive with disc, if not provided"); LOG("\t--speed=VALUE \tdrive read speed, optimal drive speed will be used if not provided"); - LOG("\t--retries=VALUE \tnumber of sector retries in case of SCSI/C2 error (default: {})", retries); + LOG("\t--retries=VALUE \tnumber of sector retries in case of SCSI/C2 error (default: {})", retries_default); LOG("\t--image-path=VALUE \tdump files base directory"); LOG("\t--image-name=VALUE \tdump files prefix, autogenerated in dump mode if not provided"); LOG("\t--overwrite \toverwrites previously generated dump files"); @@ -416,19 +425,19 @@ LOG(""); LOG("\t(drive specific)"); LOG("\t--plextor-skip-leadin \tskip dumping lead-in using negative range"); - LOG("\t--plextor-leadin-retries=VALUE \tmaximum number of lead-in retries per session (default: {})", plextor_leadin_retries); + LOG("\t--plextor-leadin-retries=VALUE \tmaximum number of lead-in retries per session (default: {})", plextor_leadin_retries_default); LOG("\t--plextor-leadin-force-store \tstore unverified lead-in"); LOG("\t--kreon-partial-ss \tget minimal security sector (fixes bad firmware)"); LOG("\t--dvd-raw \tdump raw DVD sectors (OmniDrive)"); LOG("\t--bd-raw \tdump raw BD sectors (OmniDrive)"); LOG("\t--mediatek-skip-leadout \tskip extracting lead-out from drive cache"); - LOG("\t--mediatek-leadout-retries \tnumber of preceding lead-out sector reads to fill up the cache (default: {})", mediatek_leadout_retries); + LOG("\t--mediatek-leadout-retries \tnumber of preceding lead-out sector reads to fill up the cache (default: {})", mediatek_leadout_retries_default); LOG("\t--disable-cdtext \tdisable CD-TEXT reading"); LOG(""); LOG("\t(offset)"); LOG("\t--force-offset=VALUE \toverride offset autodetection and use supplied value"); - LOG("\t--audio-silence-threshold=VALUE \tmaximum absolute sample value to treat it as silence (default: {})", audio_silence_threshold); + LOG("\t--audio-silence-threshold=VALUE \tmaximum absolute sample value to treat it as silence (default: {})", audio_silence_threshold_default); LOG("\t--correct-offset-shift \tcorrect disc write offset shift"); LOG("\t--offset-shift-relocate \tdon't merge offset groups with non-matching LBA"); LOG(""); @@ -437,7 +446,7 @@ LOG("\t--leave-unchanged \tdon't replace erroneous sectors with generated ones"); LOG("\t--force-qtoc \tforce QTOC based track split"); LOG("\t--legacy-subs \treplicate DIC style subchannel based track split"); - LOG("\t--skip-fill=VALUE \tfill byte value for skipped sectors (default: 0x{:02X})", skip_fill); + LOG("\t--skip-fill=VALUE \tfill byte value for skipped sectors (default: 0x{:02X})", skip_fill_default); LOG("\t--filesystem-trim \ttrim data track to filesystem size (ISO9660: all media, UDF: DVD and later)"); LOG(""); LOG("\t(drive test)"); @@ -461,8 +470,8 @@ LOG("\t--force-flash \tskip drive vendor/model verification when flashing firmware (WARNING: can brick your drive)"); LOG("\t--skip-subcode-desync \tskip storing sectors with mismatching subcode Q absolute MSF"); LOG("\t--rings \tenable filesystem based rings detection"); - LOG("\t--cdr-error-threshold=VALUE \tmaximum number of trailing C2 errors allowed on a CD-R (default: {})", cdr_error_threshold); - LOG("\t--scsi-timeout=VALUE \tSCSI command timeout, milliseconds (default: {})", scsi_timeout); + LOG("\t--cdr-error-threshold=VALUE \tmaximum number of trailing C2 errors allowed on a CD-R (default: {})", cdr_error_threshold_default); + LOG("\t--scsi-timeout=VALUE \tSCSI command timeout, milliseconds (default: {})", scsi_timeout_default); } }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/readers/emulated_sector_reader.ixx new/redumper-b729/readers/emulated_sector_reader.ixx --- old/redumper-b722/readers/emulated_sector_reader.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/readers/emulated_sector_reader.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,86 @@ +module; +#include <cstdint> +#include <filesystem> +#include <fstream> +#include <string.h> +#include <utility> +#include <vector> +#include "throw_line.hh" + +export module readers.emulated_sector_reader; + +import readers.data_reader; + + + +namespace gpsxre +{ + +export template<uint32_t S> +class Emulated_Sector_Reader : public DataReader +{ +public: + Emulated_Sector_Reader(std::shared_ptr<DataReader> source) + : _source(std::move(source)) + { + _source_sector_size = _source->sectorSize(); + if(S > _source_sector_size) + throw_line("emulated sector size must be less than or equal to the source sector size"); + + if(_source_sector_size % S != 0) + throw_line("source sector size must be divisible by the emulated sector size"); + + _emulated_sectors_per_source = _source_sector_size / S; + } + + uint32_t read(uint8_t *sectors, int32_t lba, uint32_t count, bool form2 = false, bool *form_hint = nullptr) override + { + if(form_hint != nullptr) + *form_hint = false; + + if(form2) + return 0; + + const int32_t source_lba = lba / _emulated_sectors_per_source; + const uint32_t source_sectors_to_read = count / _emulated_sectors_per_source + (count % _emulated_sectors_per_source != 0) + (lba % _emulated_sectors_per_source != 0); + + std::vector<uint8_t> source_sectors(source_sectors_to_read * _source_sector_size); + const uint32_t source_sectors_read = _source->read(source_sectors.data(), source_lba, source_sectors_to_read, false, form_hint); + if(source_sectors_read == 0) + return 0; + + const uint32_t lba_offset = lba % _emulated_sectors_per_source; + const uint32_t emulated_sectors_available = source_sectors_read * _emulated_sectors_per_source - lba_offset; + const uint32_t byte_offset = lba_offset * S; + const uint32_t emulated_sectors_to_copy = std::min(count, emulated_sectors_available); + memcpy(sectors, &source_sectors[byte_offset], emulated_sectors_to_copy * S); + + return emulated_sectors_to_copy; + } + + uint32_t sectorSize(bool form2 = false) override + { + return S; + } + + int32_t sectorsBase() override + { + return _source->sectorsBase() * _emulated_sectors_per_source; + } + + uint32_t sectorsCount() const override + { + return _source->sectorsCount() * _emulated_sectors_per_source; + } + + int32_t sampleOffset(int32_t lba) override + { + return lba * S; + } + +private: + std::shared_ptr<DataReader> _source; + uint32_t _source_sector_size; + uint32_t _emulated_sectors_per_source; +}; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/readers/image_disk_reader.ixx new/redumper-b729/readers/image_disk_reader.ixx --- old/redumper-b722/readers/image_disk_reader.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/readers/image_disk_reader.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,14 @@ +module; + +export module readers.image_disk_reader; + +import readers.emulated_sector_reader; + + + +namespace gpsxre +{ + +export using Image_Disk_Reader = Emulated_Sector_Reader<512>; + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/dc.ixx new/redumper-b729/systems/dc.ixx --- old/redumper-b722/systems/dc.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/dc.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -40,7 +40,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { auto system_area = iso9660::Browser::readSystemArea(data_reader); if(system_area.size() < _ROM_HEADER_OFFSET + sizeof(ROMHeader) || memcmp(system_area.data(), dreamcast::SYSTEM_AREA_MAGIC.data(), dreamcast::SYSTEM_AREA_MAGIC.size())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/gc.ixx new/redumper-b729/systems/gc.ixx --- old/redumper-b722/systems/gc.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/gc.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -31,7 +31,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path) const override { std::vector<uint8_t> header_data(FORM1_DATA_SIZE); data_reader->read((uint8_t *)header_data.data(), 0, 1); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/mcd.ixx new/redumper-b729/systems/mcd.ixx --- old/redumper-b722/systems/mcd.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/mcd.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -39,7 +39,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { auto system_area = iso9660::Browser::readSystemArea(data_reader); if(system_area.size() < _ROM_HEADER_OFFSET + sizeof(ROMHeader) || memcmp(system_area.data(), _SYSTEM_MAGIC.data(), _SYSTEM_MAGIC.size())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/ps2.ixx new/redumper-b729/systems/ps2.ixx --- old/redumper-b722/systems/ps2.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/ps2.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -37,7 +37,7 @@ } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { iso9660::PrimaryVolumeDescriptor pvd; if(!iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/ps3.ixx new/redumper-b729/systems/ps3.ixx --- old/redumper-b722/systems/ps3.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/ps3.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -1,10 +1,12 @@ module; +#include <cstring> #include <filesystem> #include <format> #include <map> #include <ostream> #include <span> #include <string_view> +#include <vector> #include "system.hh" #include "throw_line.hh" @@ -35,7 +37,7 @@ } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { iso9660::PrimaryVolumeDescriptor pvd; if(!iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) @@ -77,9 +79,35 @@ if(!serial.empty()) os << std::format(" serial: {}", serial) << std::endl; + + auto firmware_version = pupVersion(data_reader, root_directory->subEntry("PS3_UPDATE/PS3UPDAT.PUP")); + if(!firmware_version.empty()) + os << std::format(" firmware version: {}", firmware_version) << std::endl; } private: + static constexpr std::string_view _PUP_MAGIC = "SCEUF\0\0\0"; + static constexpr uint64_t _VERSION_ID = 0x100; + static constexpr uint32_t _VERSION_LENGTH = 4; + + struct PUPHeader + { + uint8_t magic[8]; + uint64_t version; + uint64_t build; + uint64_t records_count; + uint64_t header_size; + uint64_t data_size; + }; + + struct PUPFile + { + uint64_t type; + uint64_t offset; + uint64_t size; + uint64_t padding; + }; + struct SFBHeader { uint8_t magic[4]; @@ -152,6 +180,41 @@ return sfb; } + std::string pupVersion(DataReader *data_reader, std::shared_ptr<iso9660::Entry> pup_file) const + { + if(!pup_file) + return ""; + + std::vector<uint8_t> sector_buffer(data_reader->sectorSize()); + if(data_reader->read(sector_buffer.data(), pup_file->sectorsLBA(), 1) != 1) + return ""; + + auto header = (PUPHeader *)sector_buffer.data(); + + if(memcmp(header->magic, _PUP_MAGIC.data(), _PUP_MAGIC.size())) + return ""; + + uint32_t cur = sizeof(PUPHeader); + uint64_t records_count = endian_swap(header->records_count); + for(uint64_t i = 0; i < records_count; ++i) + { + if(cur + sizeof(PUPFile) > sector_buffer.size()) + return ""; + + auto file = (PUPFile *)(sector_buffer.data() + cur); + cur += sizeof(PUPFile); + if(endian_swap(file->type) == 0x100) + { + uint64_t version_offset = endian_swap(file->offset); + if(version_offset > sector_buffer.size() - _VERSION_LENGTH) + return ""; + return std::string((char *)(sector_buffer.data() + version_offset), _VERSION_LENGTH); + } + } + + return ""; + } + protected: std::map<std::string, std::string> parseSFO(std::span<uint8_t> sfo_raw) const { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/ps4.ixx new/redumper-b729/systems/ps4.ixx --- old/redumper-b722/systems/ps4.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/ps4.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -27,7 +27,7 @@ } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { iso9660::PrimaryVolumeDescriptor pvd; if(!iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/ps5.ixx new/redumper-b729/systems/ps5.ixx --- old/redumper-b722/systems/ps5.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/ps5.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -34,7 +34,7 @@ } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { iso9660::PrimaryVolumeDescriptor pvd; if(!iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/psx.ixx new/redumper-b729/systems/psx.ixx --- old/redumper-b722/systems/psx.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/psx.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -39,7 +39,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path) const override { iso9660::PrimaryVolumeDescriptor pvd; if(!iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/s_cdrom.ixx new/redumper-b729/systems/s_cdrom.ixx --- old/redumper-b722/systems/s_cdrom.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/s_cdrom.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -40,7 +40,7 @@ return Type::RAW_DATA; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool verbose) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { std::vector<std::pair<int32_t, int32_t>> invalid_sync; uint32_t mode2_form1 = 0; @@ -203,23 +203,23 @@ os << std::format(" mode2 (form 2) EDC: {}", mode2_form2_edc ? "yes" : "no") << std::endl; } if(auto count = ranges_count(invalid_sync); count) - os << std::format(" invalid sync sectors: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(invalid_sync)) : "") << std::endl; + os << std::format(" invalid sync sectors: {} (LBA: {})", count, ranges_to_string(invalid_sync)) << std::endl; if(auto count = ranges_count(invalid_modes); count) - os << std::format(" invalid mode sectors: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(invalid_modes)) : "") << std::endl; + os << std::format(" invalid mode sectors: {} (LBA: {})", count, ranges_to_string(invalid_modes)) << std::endl; if(auto count = ranges_count(generated); count) - os << std::format(" generated sectors (0x55): {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(generated)) : "") << std::endl; + os << std::format(" generated sectors (0x55): {} (LBA: {})", count, ranges_to_string(generated)) << std::endl; if(auto count = ranges_count(msf_errors); count) - os << std::format(" MSF errors: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(msf_errors)) : "") << std::endl; + os << std::format(" MSF errors: {} (LBA: {})", count, ranges_to_string(msf_errors)) << std::endl; if(auto count = ranges_count(ecc_nc_errors); count) - os << std::format(" ECC errors (non-compliant): {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(ecc_nc_errors)) : "") << std::endl; + os << std::format(" ECC errors (non-compliant): {} (LBA: {})", count, ranges_to_string(ecc_nc_errors)) << std::endl; if(auto count = ranges_count(ecc_errors); count) - os << std::format(" ECC errors: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(ecc_errors)) : "") << std::endl; + os << std::format(" ECC errors: {} (LBA: {})", count, ranges_to_string(ecc_errors)) << std::endl; if(auto count = ranges_count(edc_errors); count) - os << std::format(" EDC errors: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(edc_errors)) : "") << std::endl; + os << std::format(" EDC errors: {} (LBA: {})", count, ranges_to_string(edc_errors)) << std::endl; if(auto count = ranges_count(subheader_mismatches); count) - os << std::format(" CD-XA subheader mismatches: {}{}", count, verbose ? std::format(" (LBA: {})", ranges_to_string(subheader_mismatches)) : "") << std::endl; + os << std::format(" CD-XA subheader mismatches: {} (LBA: {})", count, ranges_to_string(subheader_mismatches)) << std::endl; os << std::endl; - os << std::format(" REDUMP.ORG errors: {}", redump_errors) << std::endl; + os << std::format(" REDUMP.INFO errors: {}", redump_errors) << std::endl; } private: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/s_hfs.ixx new/redumper-b729/systems/s_hfs.ixx --- old/redumper-b722/systems/s_hfs.ixx 1970-01-01 01:00:00.000000000 +0100 +++ new/redumper-b729/systems/s_hfs.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -0,0 +1,54 @@ +module; +#include <cstdint> +#include <filesystem> +#include <format> +#include <ostream> +#include <ranges> +#include "system.hh" + +export module systems.hfs; + +import filesystem.apm; +import filesystem.hfs; +import readers.data_reader; +import utils.strings; +import utils.endian; + + + +namespace gpsxre +{ + +export class SystemHFS : public System +{ +public: + std::string getName() override + { + return "HFS"; + } + + Type getType() override + { + return Type::DISK; + } + + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override + { + auto hfs_partitions = apm::Browser::getPartitions(data_reader) | std::views::filter([](const apm::PartitionMapEntry &v) { return v.partition_type == apm::PARTITION_TYPE_APPLE_HFS; }); + for(const auto &partition_map_entry : hfs_partitions) + { + const uint32_t mdb_location = endian_swap(partition_map_entry.partition_start_sector) + hfs::MDB_OFFSET; + hfs::MasterDirectoryBlock mdb; + if(data_reader->read((uint8_t *)&mdb, data_reader->sectorsBase() + mdb_location, 1) != 1) + continue; + + if(endian_swap(mdb.volume_signature) == hfs::VOLUME_SIGNATURE_HFS) + { + std::string volume_identifier = mac_roman_to_utf8(from_pascal_string(mdb.volume_name)); + os << std::format(" volume identifier: {}", volume_identifier) << std::endl; + } + } + } +}; + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/s_high_sierra.ixx new/redumper-b729/systems/s_high_sierra.ixx --- old/redumper-b722/systems/s_high_sierra.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/s_high_sierra.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -32,7 +32,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { auto vd = high_sierra::Browser::findDescriptor(data_reader, high_sierra::VolumeDescriptorType::STANDARD_FS); if(vd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/s_iso.ixx new/redumper-b729/systems/s_iso.ixx --- old/redumper-b722/systems/s_iso.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/s_iso.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -33,7 +33,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { iso9660::PrimaryVolumeDescriptor pvd; if(iso9660::Browser::findDescriptor((iso9660::VolumeDescriptor &)pvd, data_reader, iso9660::VolumeDescriptorType::PRIMARY)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/s_xbox.ixx new/redumper-b729/systems/s_xbox.ixx --- old/redumper-b722/systems/s_xbox.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/s_xbox.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -39,7 +39,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path) const override { std::filesystem::path security_path = track_extract_basename(track_path.string()) + ".security"; if(!std::filesystem::exists(security_path)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/sat.ixx new/redumper-b729/systems/sat.ixx --- old/redumper-b722/systems/sat.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/sat.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -41,7 +41,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &) const override { auto system_area = iso9660::Browser::readSystemArea(data_reader); if(system_area.size() < _SYSTEM_MAGIC.size() || memcmp(system_area.data(), _SYSTEM_MAGIC.data(), _SYSTEM_MAGIC.size())) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/securom.ixx new/redumper-b729/systems/securom.ixx --- old/redumper-b722/systems/securom.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/securom.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -41,7 +41,7 @@ } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path) const override { // SecuROM data track has one sector with flipped mode static constexpr uint32_t flip_offset = 4; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/system.hh new/redumper-b729/systems/system.hh --- old/redumper-b722/systems/system.hh 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/system.hh 2026-07-05 18:46:51.000000000 +0200 @@ -19,14 +19,15 @@ { RAW_DATA, RAW_AUDIO, - ISO + ISO, + DISK }; virtual ~System() = default; virtual std::string getName() = 0; virtual Type getType() = 0; - virtual void printInfo(std::ostream &os, DataReader *sector_reader, const std::filesystem::path &track_path, bool verbose) const = 0; + virtual void printInfo(std::ostream &os, DataReader *sector_reader, const std::filesystem::path &track_path) const = 0; }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/systems.ixx new/redumper-b729/systems/systems.ixx --- old/redumper-b722/systems/systems.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/systems.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -9,6 +9,7 @@ import systems.cdrom; import systems.high_sierra; import systems.iso; +import systems.hfs; import systems.dc; import systems.mcd; import systems.psx; @@ -42,6 +43,7 @@ systems.push_back([]() { return std::make_unique<SystemSecuROM>(); }); systems.push_back([]() { return std::make_unique<SystemHighSierra>(); }); systems.push_back([]() { return std::make_unique<SystemISO>(); }); + systems.push_back([]() { return std::make_unique<SystemHFS>(); }); systems.push_back([]() { return std::make_unique<SystemDC>(); }); systems.push_back([]() { return std::make_unique<SystemMCD>(); }); systems.push_back([]() { return std::make_unique<SystemPSX>(); }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/systems/wii.ixx new/redumper-b729/systems/wii.ixx --- old/redumper-b722/systems/wii.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/systems/wii.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -32,7 +32,7 @@ return Type::ISO; } - void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override + void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path) const override { std::vector<uint8_t> header_data(FORM1_DATA_SIZE); data_reader->read((uint8_t *)header_data.data(), 0, 1); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/tests/CMakeLists.txt new/redumper-b729/tests/CMakeLists.txt --- old/redumper-b722/tests/CMakeLists.txt 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/tests/CMakeLists.txt 2026-07-05 18:46:51.000000000 +0200 @@ -4,6 +4,7 @@ googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.15.2 + FIND_PACKAGE_ARGS NAMES GTest ) if(MSVC) set(gtest_force_shared_crt OFF CACHE BOOL "" FORCE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/utils/logger.ixx new/redumper-b729/utils/logger.ixx --- old/redumper-b722/utils/logger.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/utils/logger.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -164,6 +164,14 @@ } +// erase current line and log message followed by a new line (console) +export template<typename... Args> +constexpr void LOGC_R(std::format_string<Args...> fmt, Args &&...args) +{ + Logger::get().carriageReturn().log(false, fmt, std::forward<Args>(args)...).lineFeed(false); +} + + // erase current line, log message and flush, no new line (console) // used for progress update export template<typename... Args> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redumper-b722/utils/strings.ixx new/redumper-b729/utils/strings.ixx --- old/redumper-b722/utils/strings.ixx 2026-06-05 14:33:12.000000000 +0200 +++ new/redumper-b729/utils/strings.ixx 2026-07-05 18:46:51.000000000 +0200 @@ -1,8 +1,10 @@ module; #include <algorithm> +#include <array> #include <cctype> #include <cmath> +#include <codecvt> #include <cstdint> #include <iterator> #include <optional> @@ -341,4 +343,57 @@ return std::string_view(reinterpret_cast<const char *>(arr), N * sizeof(T)); } + +export template<size_t M> +#pragma pack(push, 1) +struct pascal_string +{ + uint8_t length; + char data[M]; +}; +#pragma pack(pop) + + +export template<size_t M> +std::string from_pascal_string(const pascal_string<M> &string) +{ + return std::string(string.data, std::min((size_t)string.length, M)); +} + + +export std::string mac_roman_to_utf8(const std::string &string) +{ + std::string result; + + std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter; + + for(auto &c : string) + { + const uint8_t roman_code_point = c; + + constexpr uint8_t EXTENDED_CHARACTERS_START = 0x80; + if(roman_code_point < EXTENDED_CHARACTERS_START) + { + // 7-bit ASCII + result += (char)roman_code_point; + } + else + { + // Based on https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT + constexpr std::array<uint16_t, 128> extended_characters_map = { 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, + 0x00E8, 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, + 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, + 0x03A9, 0x00E6, 0x00F8, 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201C, 0x201D, + 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, + 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 }; + uint16_t unicode_code_point = extended_characters_map[roman_code_point - EXTENDED_CHARACTERS_START]; + + result += converter.to_bytes(unicode_code_point); + } + } + + return result; +} + + } ++++++ redumper-no-static-linking.patch ++++++ --- /var/tmp/diff_new_pack.MYpczN/_old 2026-07-06 15:00:34.162549904 +0200 +++ /var/tmp/diff_new_pack.MYpczN/_new 2026-07-06 15:00:34.170550180 +0200 @@ -1,13 +1,14 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index 5a29795..c8830b2 100644 +index 00dff0d..52ea70e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -220,10 +220,6 @@ endif() +@@ -229,11 +229,6 @@ endif() target_link_libraries(redumper ${libs}) -if(REDUMPER_TARGET_LINUX) -- target_link_options(redumper PRIVATE "-static") +- set(REDUMPER_LINKER_FLAGS "-static" CACHE STRING "Linker flags") +- target_link_options(redumper PRIVATE "${REDUMPER_LINKER_FLAGS}") -endif() - install(TARGETS redumper DESTINATION "bin") ++++++ redumper-use-system-googletest.patch ++++++ --- /var/tmp/diff_new_pack.MYpczN/_old 2026-07-06 15:00:34.186550729 +0200 +++ /var/tmp/diff_new_pack.MYpczN/_new 2026-07-06 15:00:34.198551142 +0200 @@ -1,14 +1,15 @@ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt -index 7505456..9f20d07 100644 +index 9952883..9f20d07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt -@@ -1,13 +1,2 @@ +@@ -1,14 +1,2 @@ -include(FetchContent) -set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.15.2 +- FIND_PACKAGE_ARGS NAMES GTest -) -if(MSVC) - set(gtest_force_shared_crt OFF CACHE BOOL "" FORCE)
