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)

Reply via email to