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-09-28 10:44:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/redumper (Old)
 and      /work/SRC/openSUSE:Factory/.redumper.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "redumper"

Mon Sep 28 10:44:46 2026 rev:10 rq:1380756 version:752

Changes:
--------
--- /work/SRC/openSUSE:Factory/redumper/redumper.changes        2026-09-04 
12:39:00.696637379 +0200
+++ /work/SRC/openSUSE:Factory/.redumper.new.383539/redumper.changes    
2026-09-28 10:45:23.765296642 +0200
@@ -1,0 +2,14 @@
+Fri Sep 25 20:18:11 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 752
+  * Support BU50N in flash_mt1959 (#436).
+  * Find root directory on UDF 2.50 (#430).
+  * Fix bad Sector order for ASUS SDRW-08D2S-U LG GP75N-based hardware.
+  * Force omnidrive flag (#443).
+  * Fix UDF trimming for BD-R dumps (#437).
+  * Add PlayStation disc profiles (#444).
+  * SCSI transfer fix (#447).
+  * BDXL channel lengths (#446).
+  * Mont detection fix.
+
+-------------------------------------------------------------------

Old:
----
  redumper-b746.tar.gz

New:
----
  redumper-b752.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ redumper.spec ++++++
--- /var/tmp/diff_new_pack.l42mk8/_old  2026-09-28 10:45:24.366321823 +0200
+++ /var/tmp/diff_new_pack.l42mk8/_new  2026-09-28 10:45:24.367321865 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           redumper
-Version:        746
+Version:        752
 Release:        0
 Summary:        Low level CD dumper utility
 License:        GPL-3.0-only

++++++ redumper-b746.tar.gz -> redumper-b752.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/.github/workflows/cmake.yml 
new/redumper-b752/.github/workflows/cmake.yml
--- old/redumper-b746/.github/workflows/cmake.yml       2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/.github/workflows/cmake.yml       2026-09-18 
02:05:14.000000000 +0200
@@ -110,24 +110,37 @@
     needs: check
     strategy:
       matrix:
-        arch: [x64, arm64]
-    runs-on: macos-14
+        include:
+          - arch: x64
+            os: macos-15-intel
+            host_arch: x86_64
+          - arch: arm64
+            os: macos-15
+            host_arch: arm64
+    runs-on: ${{matrix.os}}
     steps:
-    - name: Customize Software
+    - name: Verify Runner Toolchain
+      shell: bash
       run: |
-        brew install llvm@18
-    - name: Customize Software
-      if: ${{ matrix.arch == 'x64' }}
-      run: |
-        softwareupdate --install-rosetta --agree-to-license
-        arch -x86_64 /bin/bash -c "$(curl -fsSL 
https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-        arch -x86_64 /usr/local/bin/brew install llvm@18
+        actual_arch="$(uname -m)"
+        echo "Host architecture: ${actual_arch}"
+        test "${actual_arch}" = "${{matrix.host_arch}}"
+
+        llvm_root="$(brew --prefix llvm@18)"
+        echo "LLVM root: ${llvm_root}"
+        test -x "${llvm_root}/bin/clang++"
     - name: Checkout
       uses: actions/checkout@v6
     - name: CMake Configure
       run: cmake -B ${{github.workspace}}/BUILD -G "Ninja" 
-DCMAKE_BUILD_TYPE=Release 
-DREDUMPER_VERSION_BUILD=${{needs.check.outputs.build_number}} 
-DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/macos-${{matrix.arch}}.cmake"
     - name: CMake Build
       run: cmake --build ${{github.workspace}}/BUILD --config Release
+    - name: Verify Architecture
+      shell: bash
+      run: |
+        binary_arch="$(lipo -archs "${{github.workspace}}/BUILD/redumper")"
+        echo "Binary architecture: ${binary_arch}"
+        test "${binary_arch}" = "${{matrix.host_arch}}"
     - name: CTest
       working-directory: ${{github.workspace}}/BUILD
       run: ctest -C Release --output-on-failure --verbose
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/CMakeLists.txt 
new/redumper-b752/CMakeLists.txt
--- old/redumper-b746/CMakeLists.txt    2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/CMakeLists.txt    2026-09-18 02:05:14.000000000 +0200
@@ -153,6 +153,7 @@
     "filesystem/udf/udf.ixx"
     "filesystem/udf/udf_browser.ixx"
     "filesystem/udf/udf_defs.ixx"
+    "filesystem/udf/udf_size.ixx"
     "hash/block_hasher.ixx"
     "hash/md5.ixx"
     "hash/sha1.ixx"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/cd/cd_common.ixx 
new/redumper-b752/cd/cd_common.ixx
--- old/redumper-b746/cd/cd_common.ixx  2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/cd/cd_common.ixx  2026-09-18 02:05:14.000000000 +0200
@@ -468,7 +468,7 @@
         if(drive_config.read_method == ReadMethod::D8)
         {
             auto sub_code = drive_config.sector_order == SectorOrder::DATA_SUB 
? READ_CDDA_SubCode::DATA_SUB : READ_CDDA_SubCode::DATA_C2_SUB;
-            status = cmd_read_cdda(sptd, sector_buffer.data(), 
CD_RAW_DATA_SIZE, lba, sectors_count, sub_code);
+            status = cmd_read_cdda(sptd, sector_buffer.data(), layout.size, 
lba, sectors_count, sub_code);
         }
         else
         {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/cmake/toolchains/macos-x64.cmake 
new/redumper-b752/cmake/toolchains/macos-x64.cmake
--- old/redumper-b746/cmake/toolchains/macos-x64.cmake  2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/cmake/toolchains/macos-x64.cmake  2026-09-18 
02:05:14.000000000 +0200
@@ -1,4 +1,4 @@
- # cross-compiling for x64 using native system compiler
+# native x64 compilation
 execute_process(COMMAND brew --prefix llvm@18 OUTPUT_VARIABLE LLVM_ROOT 
OUTPUT_STRIP_TRAILING_WHITESPACE)
 
 set(CMAKE_SYSTEM_NAME Darwin)
@@ -9,9 +9,8 @@
 set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0")
 execute_process(COMMAND xcrun --sdk macosx --show-sdk-path OUTPUT_VARIABLE 
CMAKE_OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
 
- # use x64 LLVM libc++ instead of system, arch -x86_64 ensures x64 libs
-execute_process(COMMAND arch -x86_64 /usr/local/bin/brew --prefix llvm@18 
OUTPUT_VARIABLE LLVM_X64_ROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
-set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_X64_ROOT}/lib/c++")
+# use x64 LLVM libc++ instead of system
+set(CMAKE_EXE_LINKER_FLAGS_INIT "-L${LLVM_ROOT}/lib/c++")
 
 # used in CMakeLists.txt to bundle libc++ with the application
-set(LLVM_LIB_PATH "${LLVM_X64_ROOT}/lib" CACHE PATH "Path to LLVM libraries")
+set(LLVM_LIB_PATH "${LLVM_ROOT}/lib" CACHE PATH "Path to LLVM libraries")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/debug.ixx new/redumper-b752/debug.ixx
--- old/redumper-b746/debug.ixx 2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/debug.ixx 2026-09-18 02:05:14.000000000 +0200
@@ -124,6 +124,96 @@
     std::filesystem::path physical_path(image_prefix + ".physical");
     std::filesystem::path sub_path(image_prefix + ".subcode");
 
+    if(1)
+    {
+        SPTD sptd(options.drive, options.scsi_timeout);
+
+        auto drive_query = cmd_drive_query(sptd);
+        bool w5224 = drive_query.vendor_id == "PLEXTOR" && 
drive_query.product_id.find("PX-W5224A") != std::string::npos;
+        bool monitor_mode = w5224 && drive_query.product_revision_level == 
"0.00";
+        bool firmware_mode = w5224 && !monitor_mode;
+
+        LOG("Plextor boot-state probe");
+        LOG("  inquiry: {} {} {}", drive_query.vendor_id, 
drive_query.product_id, drive_query.product_revision_level);
+        LOG("  active mode: {}", monitor_mode ? "recovery monitor" : 
firmware_mode ? "normal firmware" : "unknown");
+
+        uint8_t port_mask = 0;
+        for(uint8_t mode = 0; mode <= 4; ++mode)
+        {
+            uint8_t cdb[12] = {};
+            cdb[0] = 0xde;
+            cdb[1] = mode;
+
+            uint8_t response[2] = {};
+            if(auto status = sptd.sendCommand(cdb, sizeof(cdb), response, 
sizeof(response)).first; status.status_code)
+                throw_line("DE mode {} probe failed, SCSI ({})", mode, 
SPTD::StatusMessage(status));
+
+            LOG("  DE mode {}: {:02x} {:02x} ({})", mode, response[0], 
response[1], (uint8_t)(response[0] ^ response[1]) == 0xff ? "complement valid" 
: "complement invalid");
+            if(mode == 0)
+                port_mask = response[0];
+        }
+
+        LOG("  P0.1: {}", port_mask & 0x01 ? "low" : "high");
+        LOG("  P0.2: {}", port_mask & 0x02 ? "low" : "high");
+        LOG("  P0.6: {}", port_mask & 0x04 ? "low" : "high");
+        LOG("  P0.0: {}", port_mask & 0x08 ? "low" : "high");
+        LOG("  P0.5: {}", port_mask & 0x10 ? "low" : "high");
+
+        uint8_t cdb[12] = {};
+        cdb[0] = 0xf5;
+        cdb[5] = 0xff;
+        cdb[6] = 0xc0;
+        cdb[7] = 0x1e;
+        cdb[9] = 2;
+
+        uint8_t configuration[2] = {};
+        if(auto status = sptd.sendCommand(cdb, sizeof(cdb), configuration, 
sizeof(configuration)).first; status.status_code)
+            throw_line("F5 configuration read failed, SCSI ({})", 
SPTD::StatusMessage(status));
+
+        uint8_t configuration_value = configuration[1];
+        uint32_t low_count = std::popcount((uint32_t)(port_mask & 0x07));
+        bool visible_recovery_gates = low_count < 2 && ((port_mask & 0x04) == 
0 || configuration_value != 0) && configuration_value != 0x11 && 
configuration_value != 0x12;
+
+        LOG("  configuration entry 0x0f at 0x00ffc01e: {:02x} {:02x}", 
configuration[0], configuration[1]);
+        LOG("  forced-recovery gates excluding P1.6: {}", 
visible_recovery_gates ? "pass" : "fail");
+        LOG("  P1.6: not observable through DE/F5");
+        LOG("");
+    }
+
+    if(0)
+    {
+        SPTD sptd(options.drive, options.scsi_timeout);
+
+        auto read_memory = [&sptd](uint32_t address, uint16_t length)
+        {
+            uint8_t cdb[12] = {};
+            cdb[0] = 0xf5;
+            cdb[4] = (uint8_t)(address >> 24);
+            cdb[5] = (uint8_t)(address >> 16);
+            cdb[6] = (uint8_t)(address >> 8);
+            cdb[7] = (uint8_t)address;
+            cdb[8] = (uint8_t)(length >> 8);
+            cdb[9] = (uint8_t)length;
+
+            std::vector<uint8_t> data(length);
+            if(auto status = sptd.sendCommand(cdb, sizeof(cdb), data.data(), 
data.size()).first; status.status_code)
+                throw_line("F5 memory read failed at 0x{:08x}, SCSI ({})", 
address, SPTD::StatusMessage(status));
+
+            return data;
+        };
+
+        std::vector<uint8_t> high_memory;
+        high_memory.reserve(0x10000);
+        for(uint32_t address = 0x00ff0000; address < 0x01000000; address += 
0x4000)
+        {
+            LOG("reading memory at 0x{:08x}", address);
+            auto chunk = read_memory(address, 0x4000);
+            high_memory.insert(high_memory.end(), chunk.begin(), chunk.end());
+        }
+        write_vector("read_memory_ff0000.debug", high_memory);
+        LOG("");
+    }
+
     if(0)
     {
         // auto ss = read_vector("security_sector3.debug");
@@ -305,7 +395,7 @@
     }
 
     // MEDIATEK cache dump extract
-    if(1)
+    if(0)
     {
         std::vector<uint8_t> cache = read_vector(cache_path);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/drive.ixx new/redumper-b752/drive.ixx
--- old/redumper-b746/drive.ixx 2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/drive.ixx 2026-09-18 02:05:14.000000000 +0200
@@ -214,7 +214,7 @@
     { "PLEXTOR" , "CD-R PX-W8220T"   , ""    , "", "", +355, 294,  -75, 
ReadMethod::D8, SectorOrder::DATA_SUB   , Type::PLEXTOR  },
     { "PLEXTOR" , "CD-R PX-W8432T"   , ""    , "", "", +355, 294,  -75, 
ReadMethod::D8, SectorOrder::DATA_SUB   , Type::PLEXTOR  },
     // OTHER
-    { "ASUS"    , "SDRW-08D2S-U"     , "A812", "", "",    +6,  0,    0, 
ReadMethod::BE, SectorOrder::DATA_C2_SUB, Type::GENERIC  }, // internal model: 
LG GP75N
+    { "ASUS"    , "SDRW-08D2S-U"     , "A812", "", "",    +6,  0,    0, 
ReadMethod::BE, SectorOrder::DATA_SUB_C2, Type::GENERIC  }, // internal model: 
LG GP75N
     { "ASUS"    , "SDRW-08D2S-U"     , "B901", "", "",    +6,  0, -135, 
ReadMethod::BE, SectorOrder::DATA_SUB_C2, Type::GENERIC  }, // internal model: 
DU-8A6NH11B
     { "ASUS"    , "SDRW-08U9M-U"     , "A112", "", "",    +6,  0, -135, 
ReadMethod::BE, SectorOrder::DATA_SUB_C2, Type::GENERIC  },
     { "Lite-On" , "LTN483S 48x Max"  , "PD03", "", "", -1164,  0,    0, 
ReadMethod::BE, SectorOrder::DATA_C2    , Type::GENERIC  },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/dvd/dvd_dump.ixx 
new/redumper-b752/dvd/dvd_dump.ixx
--- old/redumper-b746/dvd/dvd_dump.ixx  2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/dvd/dvd_dump.ixx  2026-09-18 02:05:14.000000000 +0200
@@ -26,6 +26,7 @@
 import dvd.xbox;
 import filesystem.iso9660;
 import filesystem.udf;
+import filesystem.udf_size;
 import interval_set;
 import options;
 import range;
@@ -155,6 +156,8 @@
     "74.5nm",
     "69.0nm",
     "reserved1",
+    "58.26nm",
+    "55.87nm",
     "reserved2",
     "reserved3",
     "reserved4",
@@ -164,9 +167,7 @@
     "reserved8",
     "reserved9",
     "reserved10",
-    "reserved11",
-    "reserved12",
-    "reserved13"
+    "reserved11"
 };
 // clang-format on
 
@@ -415,6 +416,7 @@
     bool search = true;
     bool udf = false;
     std::vector<std::pair<uint32_t, uint32_t>> udf_vds;
+    std::optional<udf::ExtentDescriptor> udf_reserve_vds;
 };
 
 
@@ -449,19 +451,23 @@
         }
     }
 
-    if(ctx.udf)
+    // A valid primary AVDP is sufficient to detect UDF even when the volume
+    // recognition sequence is absent or not recognized.
+    if(lba == udf::AVDP_PRIMARY_LBA)
     {
-        if(lba == udf::AVDP_PRIMARY_LBA)
+        if(auto const &avdp = (udf::AnchorVolumeDescriptorPointer &)data[0]; 
avdp.descriptor_tag.tag_identifier == udf::TagIdentifier::ANCHOR_POINTER && 
avdp.descriptor_tag.tag_location == lba)
         {
-            if(auto const &avdp = (udf::AnchorVolumeDescriptorPointer 
&)data[0]; avdp.descriptor_tag.tag_identifier == 
udf::TagIdentifier::ANCHOR_POINTER)
-            {
-                // ordering is intentional
-                ctx.udf_vds.emplace_back(avdp.reserve_vds.location, 
scale_up(avdp.reserve_vds.length, FORM1_DATA_SIZE));
-                ctx.udf_vds.emplace_back(avdp.main_vds.location, 
scale_up(avdp.main_vds.length, FORM1_DATA_SIZE));
-            }
+            ctx.udf = true;
+            // ordering is intentional
+            ctx.udf_vds.emplace_back(avdp.reserve_vds.location, 
scale_up(avdp.reserve_vds.length, FORM1_DATA_SIZE));
+            ctx.udf_vds.emplace_back(avdp.main_vds.location, 
scale_up(avdp.main_vds.length, FORM1_DATA_SIZE));
+            ctx.udf_reserve_vds = avdp.reserve_vds;
         }
+    }
 
-        if(!ctx.udf_vds.empty() && ctx.udf_vds.back().first + 
ctx.udf_vds.back().second <= lba)
+    if(ctx.udf)
+    {
+        if(!ctx.udf_vds.empty() && (uint64_t)ctx.udf_vds.back().first + 
ctx.udf_vds.back().second <= lba)
         {
             std::vector<uint8_t> sector_data_file(ctx.udf_vds.back().second * 
FORM1_DATA_SIZE);
             std::vector<State> sector_state_file(ctx.udf_vds.back().second);
@@ -482,7 +488,9 @@
                     {
                         auto const &partition = (udf::PartitionDescriptor 
&)sector_data_file[i * FORM1_DATA_SIZE];
 
-                        sectors_count = std::max(sectors_count, 
partition.partition_starting_location + partition.partition_length);
+                        uint64_t partition_end = 
(uint64_t)partition.partition_starting_location + partition.partition_length;
+                        if(partition_end <= 
std::numeric_limits<uint32_t>::max())
+                            sectors_count = std::max(sectors_count, 
(uint32_t)partition_end);
                     }
                     else if(tag.tag_identifier == 
udf::TagIdentifier::TERMINATING)
                         break;
@@ -490,9 +498,12 @@
 
                 ctx.udf_vds.clear();
 
-                if(sectors_count)
-                    // account for trailing AVDP
-                    ss = std::make_pair(sectors_count + 1, true);
+                auto reserve_vds = 
ctx.udf_reserve_vds.value_or(udf::ExtentDescriptor{});
+                if(auto volume_sectors_count = 
udf::get_volume_sectors_count(sectors_count, reserve_vds.location, 
reserve_vds.length, FORM1_DATA_SIZE); volume_sectors_count)
+                {
+                    ss = std::make_pair(*volume_sectors_count, true);
+                    ctx.udf = false;
+                }
             }
             else
                 ctx.udf_vds.pop_back();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/dvd/dvd_split.ixx 
new/redumper-b752/dvd/dvd_split.ixx
--- old/redumper-b746/dvd/dvd_split.ixx 2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/dvd/dvd_split.ixx 2026-09-18 02:05:14.000000000 +0200
@@ -5,6 +5,7 @@
 #include <fstream>
 #include <span>
 #include <string>
+#include <string_view>
 #include <vector>
 #include "throw_line.hh"
 
@@ -136,7 +137,7 @@
 
     std::filesystem::path state_path(image_prefix + ".state");
     std::filesystem::path iso_path(image_prefix + ".iso");
-    std::filesystem::path physical_path(image_prefix + ".physical");
+    std::filesystem::path manufacturer_path(image_prefix + ".manufacturer");
     if(std::filesystem::exists(iso_path) && !options.overwrite)
     {
         LOG("warning: file already exists ({})", iso_path.filename().string());
@@ -166,11 +167,17 @@
     if(sdram_fs.fail())
         throw_line("seek failed");
 
-    if(std::filesystem::exists(physical_path))
+    if(std::filesystem::exists(manufacturer_path))
     {
-        auto physical = read_vector(physical_path);
-        if(physical.size() > sizeof(CMD_ParameterListHeader) && 
physical[sizeof(CMD_ParameterListHeader)] == 0xFF)
-            nintendo_key = 0;
+        auto manufacturer = read_vector(manufacturer_path);
+        constexpr size_t id_offset = sizeof(CMD_ParameterListHeader) + 0x10;
+        constexpr std::string_view nintendo_id_start = "Nintendo";
+        if(manufacturer.size() >= id_offset + nintendo_id_start.size())
+        {
+            std::string_view id_string(reinterpret_cast<const char 
*>(manufacturer.data() + id_offset), nintendo_id_start.size());
+            if(id_string == nintendo_id_start)
+                nintendo_key = 0;
+        }
     }
 
     uint32_t main_data_offset = nintendo_key ? offsetof(dvd::DataFrame, 
cpr_mai) : offsetof(dvd::DataFrame, main_data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/filesystem/udf/udf_browser.ixx 
new/redumper-b752/filesystem/udf/udf_browser.ixx
--- old/redumper-b746/filesystem/udf/udf_browser.ixx    2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/filesystem/udf/udf_browser.ixx    2026-09-18 
02:05:14.000000000 +0200
@@ -3,6 +3,8 @@
 #include <cstring>
 #include <memory>
 #include <optional>
+#include <string_view>
+#include <variant>
 #include <vector>
 
 export module filesystem.udf:browser;
@@ -57,7 +59,7 @@
         return {};
     }
 
-    static std::shared_ptr<udf::FileEntry> rootDirectory(DataReader 
*data_reader)
+    static std::variant<std::shared_ptr<udf::FileEntry>, 
std::shared_ptr<udf::ExtendedFileEntry>> rootDirectory(DataReader *data_reader)
     {
         // Try and find the AVDP so we can look through the volume descriptor 
sequence.
         auto const avdp = findAnchorVolumeDescriptorPointer(data_reader);
@@ -74,6 +76,10 @@
             data_reader->read(partition_data.data(), partition_offset.value(), 
1);
             struct udf::PartitionDescriptor partition_descriptor = 
(udf::PartitionDescriptor &)partition_data[0];
 
+            // For the moment, we're just assuming that partitions are 
inserted into this in order.
+            std::vector<uint32_t> partition_starting_locations;
+            
partition_starting_locations.push_back(partition_descriptor.partition_starting_location);
+
             // Search for the logical volume descriptor. This contains the 
extent of the file set descriptor.
             auto lvd_offset = findDescriptorOffset(data_reader, 
avdp.value().main_vds, udf::TagIdentifier::LOGICAL);
             if(!lvd_offset.has_value())
@@ -84,26 +90,67 @@
             data_reader->read(lvd_data.data(), lvd_offset.value(), 1);
             struct udf::LogicalVolumeDescriptor logical_volume_descriptor = 
(udf::LogicalVolumeDescriptor &)lvd_data[0];
 
+            // Parse the partition maps in the logical volume descriptor to 
find the metadata partition map, if it's present.
+            auto partition_map_offset = sizeof(udf::LogicalVolumeDescriptor);
+            for(int i = 0; i < 
logical_volume_descriptor.number_of_partition_maps; i++)
+            {
+                struct udf::partition_map_header partition_map_header = 
(udf::partition_map_header &)lvd_data[partition_map_offset];
+
+                if(2 == partition_map_header.partition_map_type)
+                {
+                    struct udf::partition_map_type_2_header 
partition_map_type_2_header = (udf::partition_map_type_2_header 
&)lvd_data[partition_map_offset];
+
+                    // While we know the identifier is only 23 bytes long, if 
we don't wrap it in a bounded string view the comparison will continue reading 
after it until it finds a null terminator.
+                    
if(std::string_view(partition_map_type_2_header.partition_type_identifier.identifier,
 23) == PARTITION_TYPE_ID_METADATA)
+                    {
+                        // If we've found the metadata partition, parse it as 
an extended file entry and use the position of the first allocation descriptor 
to determine the start of partition 1.
+                        struct udf::metadata_partition_map 
metadata_partition_map = (udf::metadata_partition_map 
&)lvd_data[partition_map_offset];
+
+                        std::vector<uint8_t> 
metadata_file_data(FORM1_DATA_SIZE);
+                        data_reader->read(metadata_file_data.data(), 
partition_starting_locations[metadata_partition_map.partition_number] + 
metadata_partition_map.metadata_file_location, 1);
+                        struct udf::ExtendedFileEntry metadata_file = 
(udf::ExtendedFileEntry &)metadata_file_data[0];
+
+                        // This isn't particularly well explained in the spec, 
so this behavior comes from libudfread.
+                        struct udf::short_ad metadata_file_ad = (udf::short_ad 
&)metadata_file_data[sizeof(udf::ExtendedFileEntry) + 
metadata_file.length_of_extended_attributes];
+                        
partition_starting_locations.push_back(partition_starting_locations[0] + 
metadata_file_ad.extent_position);
+                    }
+                }
+                partition_map_offset += 
partition_map_header.partition_map_length;
+            }
+
             auto const &file_set_descriptor_extent = (udf::long_ad 
&)logical_volume_descriptor.logical_volume_contents_use;
 
             // If we have the partition the file set descriptor is found in, 
parse it to find the root directory ICB extent.
-            if(partition_descriptor.partition_number == 
file_set_descriptor_extent.extent_location.partition_reference_number)
+            if(partition_starting_locations.size() - 1 >= 
file_set_descriptor_extent.extent_location.partition_reference_number)
             {
                 std::vector<uint8_t> fsd_data(FORM1_DATA_SIZE);
-                data_reader->read(fsd_data.data(), 
partition_descriptor.partition_starting_location + 
file_set_descriptor_extent.extent_location.logical_block_number, 1);
+                data_reader->read(fsd_data.data(),
+                    
partition_starting_locations[file_set_descriptor_extent.extent_location.partition_reference_number]
 + file_set_descriptor_extent.extent_location.logical_block_number, 1);
                 auto const &file_set_descriptor = (udf::FileSetDescriptor 
&)fsd_data[0];
 
                 auto const &root_directory_icb_extent = 
file_set_descriptor.root_directory_icb;
 
                 // If we have the partition the root directory is in, read and 
return it.
-                if(partition_descriptor.partition_number == 
root_directory_icb_extent.extent_location.partition_reference_number)
+                if(partition_starting_locations.size() - 1 >= 
root_directory_icb_extent.extent_location.partition_reference_number)
                 {
                     std::vector<uint8_t> root_directory_data(FORM1_DATA_SIZE);
-                    data_reader->read(root_directory_data.data(), 
partition_descriptor.partition_starting_location + 
root_directory_icb_extent.extent_location.logical_block_number, 1);
+                    data_reader->read(root_directory_data.data(),
+                        
partition_starting_locations[root_directory_icb_extent.extent_location.partition_reference_number]
 + root_directory_icb_extent.extent_location.logical_block_number, 1);
+
+                    auto const &tag = (udf::DescriptorTag 
&)root_directory_data[0];
+                    if(tag.tag_identifier == udf::TagIdentifier::FILE_ENTRY)
+                    {
+                        auto root_directory_file_entry = 
std::shared_ptr<udf::FileEntry>(new udf::FileEntry());
+                        std::memcpy(root_directory_file_entry.get(), 
root_directory_data.data(), sizeof(udf::FileEntry));
+                        return root_directory_file_entry;
+                    }
+                    else if(tag.tag_identifier == 
udf::TagIdentifier::EXTENDED_FILE_ENTRY)
+                    {
+                        auto root_directory_file_entry = 
std::shared_ptr<udf::ExtendedFileEntry>(new udf::ExtendedFileEntry());
+                        std::memcpy(root_directory_file_entry.get(), 
root_directory_data.data(), sizeof(udf::ExtendedFileEntry));
 
-                    auto root_directory_file_entry = 
std::shared_ptr<udf::FileEntry>(new udf::FileEntry());
-                    std::memcpy(root_directory_file_entry.get(), 
root_directory_data.data(), sizeof(udf::FileEntry));
-                    return root_directory_file_entry;
+                        return root_directory_file_entry;
+                    }
                 }
             }
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/filesystem/udf/udf_defs.ixx 
new/redumper-b752/filesystem/udf/udf_defs.ixx
--- old/redumper-b746/filesystem/udf/udf_defs.ixx       2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/filesystem/udf/udf_defs.ixx       2026-09-18 
02:05:14.000000000 +0200
@@ -18,6 +18,9 @@
 constexpr std::string_view DESCRIPTOR_ID_NSR3 = "NSR03";
 constexpr std::string_view DESCRIPTOR_ID_TEA = "TEA01";
 const std::set<std::string_view> DESCRIPTORS = { DESCRIPTOR_ID_BEA, 
DESCRIPTOR_ID_BOOT2, DESCRIPTOR_ID_CDW, DESCRIPTOR_ID_NSR2, DESCRIPTOR_ID_NSR3, 
DESCRIPTOR_ID_TEA };
+constexpr std::string_view PARTITION_TYPE_ID_VIRTUAL = "*UDF Virtual 
Partition";
+constexpr std::string_view PARTITION_TYPE_ID_SPARABLE = "*UDF Sparable 
Partition";
+constexpr std::string_view PARTITION_TYPE_ID_METADATA = "*UDF Metadata 
Partition";
 
 
 enum class TagIdentifier : uint16_t
@@ -31,9 +34,14 @@
     LOGICAL,
     UNALLOCATED_SPACE,
     TERMINATING,
-    LOGICAL_INTEGRITY
-};
+    LOGICAL_INTEGRITY,
 
+    FILE_SET = 256,
+    FILE_IDENTIFIER = 257,
+    ALLOCATION_EXTENT = 258,
+    FILE_ENTRY = 261,
+    EXTENDED_FILE_ENTRY = 266
+};
 
 #pragma pack(push, 1)
 struct DescriptorTag
@@ -84,6 +92,12 @@
     uint8_t character_set_info[63];
 };
 
+struct short_ad
+{
+    uint32_t extent_length;
+    uint32_t extent_position;
+};
+
 struct lb_addr
 {
     uint32_t logical_block_number;
@@ -100,8 +114,39 @@
 struct EntityID
 {
     uint8_t flags;
-    uint8_t identifier[23];
-    uint8_t identifier_suffix[8];
+    char identifier[23];
+    char identifier_suffix[8];
+};
+
+struct partition_map_header
+{
+    uint8_t partition_map_type;
+    uint8_t partition_map_length;
+};
+
+struct partition_map_type_1 : partition_map_header
+{
+    uint16_t volume_sequence_number;
+    uint16_t partition_number;
+};
+
+struct partition_map_type_2_header : partition_map_header
+{
+    uint8_t reserved_1[2];
+    EntityID partition_type_identifier;
+};
+
+struct metadata_partition_map : partition_map_type_2_header
+{
+    uint16_t volume_sequence_number;
+    uint16_t partition_number;
+    uint32_t metadata_file_location;
+    uint32_t metadata_mirror_file_location;
+    uint32_t metadata_bitmap_file_location;
+    uint32_t allocation_unit_size;
+    uint16_t alignment_unit_size;
+    uint8_t flags;
+    uint8_t reserved_2[5];
 };
 
 struct LogicalVolumeDescriptor
@@ -177,6 +222,35 @@
     EntityID implementation_identifier;
     uint64_t unique_id;
     uint32_t length_of_extended_attributes;
+    uint32_t length_of_allocation_descriptors;
+    uint8_t extended_attributes_and_allocation_descriptors[];
+};
+
+struct ExtendedFileEntry
+{
+    DescriptorTag descriptor_tag;
+    icbtag icb_tag;
+    uint32_t uid;
+    uint32_t gid;
+    uint32_t permissions;
+    uint16_t file_link_count;
+    uint8_t record_format;
+    uint8_t record_display_attributes;
+    uint32_t record_length;
+    uint64_t information_length;
+    uint64_t object_size;
+    uint64_t logical_blocks_recorded;
+    timestamp access_time;
+    timestamp modification_time;
+    timestamp creation_time;
+    timestamp attribute_time;
+    uint32_t checkpoint;
+    uint32_t reserved;
+    long_ad extended_attribute_icb;
+    long_ad stream_directory_icb;
+    EntityID implementation_identifier;
+    uint64_t unique_id;
+    uint32_t length_of_extended_attributes;
     uint32_t length_of_allocation_descriptors;
     uint8_t extended_attributes_and_allocation_descriptors[];
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/filesystem/udf/udf_size.ixx 
new/redumper-b752/filesystem/udf/udf_size.ixx
--- old/redumper-b746/filesystem/udf/udf_size.ixx       1970-01-01 
01:00:00.000000000 +0100
+++ new/redumper-b752/filesystem/udf/udf_size.ixx       2026-09-18 
02:05:14.000000000 +0200
@@ -0,0 +1,30 @@
+module;
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <optional>
+
+export module filesystem.udf_size;
+
+
+
+export namespace gpsxre::udf
+{
+
+constexpr std::optional<uint32_t> get_volume_sectors_count(uint32_t 
partition_end, uint32_t reserve_vds_location, uint32_t reserve_vds_length, 
uint32_t sector_size)
+{
+    if(!partition_end || !sector_size)
+        return std::nullopt;
+
+    uint64_t reserve_length = ((uint64_t)reserve_vds_length + sector_size - 1) 
/ sector_size;
+    uint64_t reserve_end = reserve_length ? (uint64_t)reserve_vds_location + 
reserve_length : 0;
+    uint64_t metadata_end = std::max<uint64_t>(partition_end, reserve_end);
+
+    // Account for the trailing AVDP immediately following the filesystem 
metadata.
+    if(metadata_end >= std::numeric_limits<uint32_t>::max())
+        return std::nullopt;
+
+    return metadata_end + 1;
+}
+
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/options.ixx 
new/redumper-b752/options.ixx
--- old/redumper-b746/options.ixx       2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/options.ixx       2026-09-18 02:05:14.000000000 +0200
@@ -67,6 +67,7 @@
     bool kreon_partial_ss;
     bool dvd_raw;
     bool bd_raw;
+    bool force_omnidrive;
     bool disable_cdtext;
     bool correct_offset_shift;
     bool offset_shift_relocate;
@@ -126,6 +127,7 @@
         , kreon_partial_ss(false)
         , dvd_raw(false)
         , bd_raw(false)
+        , force_omnidrive(false)
         , disable_cdtext(false)
         , correct_offset_shift(false)
         , offset_shift_relocate(false)
@@ -292,6 +294,8 @@
                         dvd_raw = true;
                     else if(key == "--bd-raw")
                         bd_raw = true;
+                    else if(key == "--force-omnidrive")
+                        force_omnidrive = true;
                     else if(key == "--disable-cdtext")
                         disable_cdtext = true;
                     else if(key == "--correct-offset-shift")
@@ -445,6 +449,7 @@
         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--force-omnidrive               \tforce OmniDrive mode (if 
drive enclosure overwrites drive inquiry)");
         LOG("\t--disable-cdtext                \tdisable CD-TEXT reading");
         LOG("");
         LOG("\t(offset)");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/redumper.ixx 
new/redumper-b752/redumper.ixx
--- old/redumper-b746/redumper.ixx      2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/redumper.ixx      2026-09-18 02:05:14.000000000 +0200
@@ -43,6 +43,7 @@
 import scsi.mmc;
 import scsi.sptd;
 import skeleton;
+import utils.endian;
 import utils.file_io;
 import utils.logger;
 import utils.misc;
@@ -202,6 +203,13 @@
     { GET_CONFIGURATION_FeatureCode_ProfileList::HDDVD_R_DL,         "HD DVD-R 
DL"        },
     { GET_CONFIGURATION_FeatureCode_ProfileList::HDDVD_RW_DL,        "HD 
DVD-RW DL"       },
 
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS1_CD_ROM,         "PS1 
CD-ROM"         },
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS2_CD_ROM,         "PS2 
CD-ROM"         },
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS2_DVD_ROM,        "PS2 
DVD-ROM"        },
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS3_DVD_ROM,        "PS3 
DVD-ROM"        },
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS3_BD_ROM,         "PS3 
BD-ROM"         },
+    { GET_CONFIGURATION_FeatureCode_ProfileList::PS4_BD_ROM,         "PS4 
BD-ROM"         },
+
     { GET_CONFIGURATION_FeatureCode_ProfileList::NON_STANDARD,       "NON 
STANDARD"       }
 };
 
@@ -216,6 +224,8 @@
     case GET_CONFIGURATION_FeatureCode_ProfileList::CD_ROM:
     case GET_CONFIGURATION_FeatureCode_ProfileList::CD_R:
     case GET_CONFIGURATION_FeatureCode_ProfileList::CD_RW:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS1_CD_ROM:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS2_CD_ROM:
         disc_type = DiscType::CD;
         break;
 
@@ -230,10 +240,14 @@
     case GET_CONFIGURATION_FeatureCode_ProfileList::DVD_PLUS_R:
     case GET_CONFIGURATION_FeatureCode_ProfileList::DVD_PLUS_RW_DL:
     case GET_CONFIGURATION_FeatureCode_ProfileList::DVD_PLUS_R_DL:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS2_DVD_ROM:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS3_DVD_ROM:
         disc_type = DiscType::DVD;
         break;
 
     case GET_CONFIGURATION_FeatureCode_ProfileList::BD_ROM:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS3_BD_ROM:
+    case GET_CONFIGURATION_FeatureCode_ProfileList::PS4_BD_ROM:
         disc_type = DiscType::BLURAY;
         break;
 
@@ -433,6 +447,13 @@
         drive_override_config(ctx.drive_config, options.drive_type.get(), 
options.drive_read_offset.get(), options.drive_c2_shift.get(), 
options.drive_pregap_start.get(),
             options.drive_read_method.get(), options.drive_sector_order.get());
 
+        if(options.force_omnidrive && !is_omnidrive_firmware(ctx.drive_config))
+        {
+            char vvv[3];
+            endian_swap_to_array(vvv, omnidrive_minimum_version());
+            ctx.drive_config.reserved5 = std::string("OmniDrive") + 
std::string(vvv, sizeof(vvv));
+        }
+
         std::optional<GET_CONFIGURATION_FeatureCode_ProfileList> 
current_profile;
         if(aggregate.drive_ready)
         {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/scsi/cmd.ixx 
new/redumper-b752/scsi/cmd.ixx
--- old/redumper-b746/scsi/cmd.ixx      2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/scsi/cmd.ixx      2026-09-18 02:05:14.000000000 +0200
@@ -49,7 +49,7 @@
 
     response.resize(initial_size);
     *(uint16_t *)cdb.allocation_length = 
endian_swap<uint16_t>(response.size());
-    status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), 
(uint32_t)response.size());
+    status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), 
(uint32_t)response.size()).first;
     if(status.status_code)
     {
         response.clear();
@@ -63,7 +63,7 @@
 
             *(uint16_t *)cdb.allocation_length = 
endian_swap<uint16_t>(response_size);
 
-            status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), 
(uint32_t)response.size());
+            status = sptd.sendCommand(&cdb, sizeof(cdb), response.data(), 
(uint32_t)response.size()).first;
             if(status.status_code)
                 response_size = 0;
             // always use the size from the latest read attempt, this fixes 
some identified KREON issues
@@ -83,7 +83,7 @@
     CDB6_Generic cdb = {};
     cdb.operation_code = (uint8_t)CDB_OperationCode::TEST_UNIT_READY;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -97,7 +97,7 @@
 
     *(uint16_t *)cdb.allocation_length = endian_swap<uint16_t>(data_size);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size);
+    return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size).first;
 }
 
 
@@ -111,7 +111,7 @@
 
     READ_CAPACITY_Response response;
 
-    auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, 
sizeof(response));
+    auto status = sptd.sendCommand(&cdb, sizeof(cdb), &response, 
sizeof(response)).first;
     if(!status.status_code)
     {
         lba = endian_swap(response.address);
@@ -176,7 +176,7 @@
         memcpy(parameter_list.data() + sizeof(CMD_ParameterListHeader), data, 
data_size);
     }
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? 
nullptr : parameter_list.data(), parameter_list.size(), true);
+    return sptd.sendCommand(&cdb, sizeof(cdb), parameter_list.empty() ? 
nullptr : parameter_list.data(), parameter_list.size(), true).first;
 }
 
 
@@ -189,7 +189,7 @@
     cdb.agid = agid;
     cdb.key_format = (uint8_t)key_format;
 
-    return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? 
sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0) : cdb_send_receive(sptd, 
response, cdb);
+    return key_format == REPORT_KEY_KeyFormat::INVALIDATE_AGID ? 
sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first : cdb_send_receive(sptd, 
response, cdb);
 }
 
 
@@ -199,7 +199,7 @@
     cdb.operation_code = (uint8_t)CDB_OperationCode::SEEK;
     *(int32_t *)cdb.lba = endian_swap(lba);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -211,7 +211,7 @@
     *(int32_t *)cdb.starting_lba = endian_swap(start_lba);
     *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * 
transfer_length);
+    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * 
transfer_length).first;
 }
 
 
@@ -237,7 +237,7 @@
 
     uint32_t transfer_length = MSF_to_LBA(end_msf) - MSF_to_LBA(start_msf);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * 
transfer_length);
+    return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * 
transfer_length).first;
 }
 
 
@@ -273,7 +273,7 @@
     cdb.include_sync_data = expected_sector_type == 
READ_CD_ExpectedSectorType::CD_DA ? 0 : 1;
     cdb.sub_channel_selection = (uint8_t)sub_channel;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * 
transfer_length);
+    return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * 
transfer_length).first;
 }
 
 
@@ -287,7 +287,11 @@
     *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length);
     cdb.sub_code = (uint8_t)sub_code;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), sectors, block_size * 
transfer_length);
+    auto [status, transferred_length] = sptd.sendCommand(&cdb, sizeof(cdb), 
sectors, block_size * transfer_length);
+    if(!status.status_code && transferred_length != 
READ_CDDA_SIZES[(uint8_t)sub_code] * transfer_length)
+        status.status_code = SPTD::HOST_SHORT_TRANSFER;
+
+    return status;
 }
 
 
@@ -296,7 +300,7 @@
     CDB6_Generic cdb = {};
     cdb.operation_code = (uint8_t)CDB_OperationCode::PLEXTOR_RESET;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -305,7 +309,7 @@
     CDB6_Generic cdb = {};
     cdb.operation_code = (uint8_t)CDB_OperationCode::SYNCHRONIZE_CACHE;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -315,7 +319,7 @@
     cdb.operation_code = (uint8_t)CDB_OperationCode::SET_CD_SPEED;
     *(uint16_t *)cdb.read_speed = endian_swap(speed);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -327,7 +331,7 @@
     cdb.offset = endian_swap(offset);
     cdb.size = endian_swap(size);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size);
+    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, size).first;
 }
 
 
@@ -341,7 +345,7 @@
     GET_CONFIGURATION_FeatureHeader feature_header = {};
     uint16_t size = sizeof(feature_header);
     *(uint16_t *)cdb.allocation_length = endian_swap(size);
-    auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, size);
+    auto status = sptd.sendCommand(&cdb, sizeof(cdb), &feature_header, 
size).first;
 
     current_profile = 
(GET_CONFIGURATION_FeatureCode_ProfileList)endian_swap(feature_header.current_profile);
 
@@ -360,7 +364,7 @@
     *(uint16_t *)cdb.allocation_length = endian_swap(size);
     std::vector<uint8_t> buffer(size);
 
-    auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), 
buffer.size());
+    auto status = sptd.sendCommand(&cdb, sizeof(cdb), buffer.data(), 
buffer.size()).first;
 
     auto feature_header = (GET_CONFIGURATION_FeatureHeader *)buffer.data();
     uint32_t fds_size = endian_swap(feature_header->data_length) - 
(sizeof(GET_CONFIGURATION_FeatureHeader) - sizeof(feature_header->data_length));
@@ -391,7 +395,7 @@
     cdb.reserved2 = ss_val;
     cdb.control = 0xC0;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), response_data.data(), 
response_data.size());
+    return sptd.sendCommand(&cdb, sizeof(cdb), response_data.data(), 
response_data.size()).first;
 }
 
 
@@ -413,7 +417,7 @@
         cdb.extended = (uint8_t)lock_state;
     }
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -424,7 +428,7 @@
     cdb.load_eject = load_eject;
     cdb.start = start;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0);
+    return sptd.sendCommand(&cdb, sizeof(cdb), nullptr, 0).first;
 }
 
 
@@ -435,7 +439,7 @@
     cdb.unknown1 = unknown1;
     cdb.mode = (uint8_t)mode;
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), (void *)data, data_size, true);
+    return sptd.sendCommand(&cdb, sizeof(cdb), (void *)data, data_size, 
true).first;
 }
 
 
@@ -451,7 +455,7 @@
     cdb.parameter_list_length[1] = ((uint8_t *)&parameter_list_length)[1];
     cdb.parameter_list_length[2] = ((uint8_t *)&parameter_list_length)[0];
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), (void *)data, data_size, true);
+    return sptd.sendCommand(&cdb, sizeof(cdb), (void *)data, data_size, 
true).first;
 }
 
 
@@ -467,7 +471,7 @@
     cdb.allocation_length[1] = ((uint8_t *)&allocation_length)[1];
     cdb.allocation_length[2] = ((uint8_t *)&allocation_length)[0];
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size);
+    return sptd.sendCommand(&cdb, sizeof(cdb), data, data_size).first;
 }
 
 
@@ -485,7 +489,7 @@
     *(int32_t *)cdb.address = endian_swap(address);
     *(uint32_t *)cdb.transfer_blocks = endian_swap(transfer_length);
 
-    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * 
transfer_length);
+    return sptd.sendCommand(&cdb, sizeof(cdb), buffer, block_size * 
transfer_length).first;
 }
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/scsi/mmc.ixx 
new/redumper-b752/scsi/mmc.ixx
--- old/redumper-b746/scsi/mmc.ixx      2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/scsi/mmc.ixx      2026-09-18 02:05:14.000000000 +0200
@@ -128,6 +128,13 @@
     HDDVD_R_DL = 0x58,
     HDDVD_RW_DL = 0x5A,
 
+    PS1_CD_ROM = 0xFF50,
+    PS2_CD_ROM = 0xFF60,
+    PS2_DVD_ROM = 0xFF61,
+    PS3_DVD_ROM = 0xFF70,
+    PS3_BD_ROM = 0xFF71,
+    PS4_BD_ROM = 0xFF80,
+
     NON_STANDARD = 0xFFFF
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/scsi/sptd.ixx 
new/redumper-b752/scsi/sptd.ixx
--- old/redumper-b746/scsi/sptd.ixx     2026-08-25 03:51:25.000000000 +0200
+++ new/redumper-b752/scsi/sptd.ixx     2026-09-18 02:05:14.000000000 +0200
@@ -9,6 +9,7 @@
 #include <optional>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 #include "throw_line.hh"
 
@@ -45,6 +46,7 @@
 {
 public:
     static constexpr uint32_t DEFAULT_TIMEOUT = 50000;
+    static constexpr uint8_t HOST_SHORT_TRANSFER = 0xFF;
 
     struct Status
     {
@@ -65,7 +67,11 @@
 #endif
     {
 #if defined(_WIN32)
-        _handle = CreateFile(std::format("//./{}:", drive_path[0]).c_str(), 
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 
nullptr);
+        std::string device_name = drive_path;
+        if(device_name.size() == 1)
+            device_name += ':';
+
+        _handle = CreateFile(std::format("//./{}", device_name).c_str(), 
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 
nullptr);
         if(_handle == INVALID_HANDLE_VALUE)
             throw_line("unable to open drive ({}, SYSTEM: {})", drive_path, 
getLastError());
 #elif defined(__APPLE__)
@@ -150,9 +156,10 @@
     }
 
 
-    Status sendCommand(const void *cdb, uint8_t cdb_length, void *buffer, 
uint32_t buffer_length, bool out = false)
+    std::pair<Status, uint32_t> sendCommand(const void *cdb, uint8_t 
cdb_length, void *buffer, uint32_t buffer_length, bool out = false)
     {
         Status status = {};
+        uint32_t transferred_length = 0;
 
 #if defined(_WIN32)
         // FIXME: simplify and reuse common SenseData
@@ -172,6 +179,8 @@
         if(success != TRUE)
             throw_line("SYSTEM ({})", getLastError());
 
+        transferred_length = sptd_sd.sptd.DataTransferLength;
+
         if(sptd_sd.sptd.ScsiStatus != SCSISTAT_GOOD)
         {
             status.status_code = sptd_sd.sptd.ScsiStatus;
@@ -209,6 +218,8 @@
         if(auto kret = (*task.get())->ExecuteTaskSync(task.get(), &sense_data, 
&task_status, &transfer_count); kret != KERN_SUCCESS)
             throw_line("failed to execute task (MACH: {})", 
mach_error_string(kret));
 
+        transferred_length = (uint32_t)transfer_count;
+
         if(task_status != kSCSITaskStatus_GOOD)
         {
             status.status_code = task_status;
@@ -234,6 +245,8 @@
         if(result < 0)
             throw_line("SYSTEM ({})", getLastError());
 
+        transferred_length = hdr.dxfer_len - hdr.resid;
+
         if(hdr.status)
         {
             status.status_code = hdr.status;
@@ -243,7 +256,7 @@
         }
 #endif
 
-        return status;
+        return { status, transferred_length };
     }
 
 
@@ -561,7 +574,8 @@
     { 0x22, "COMMAND TERMINATED"           },
     { 0x28, "TASK SET FULL"                },
     { 0x30, "ACA ACTIVE"                   },
-    { 0x40, "TASK ABORTED"                 }
+    { 0x40, "TASK ABORTED"                 },
+    { 0xFF, "HOST: SHORT TRANSFER"         }  // host error
 };
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/tests/gtest/CMakeLists.txt 
new/redumper-b752/tests/gtest/CMakeLists.txt
--- old/redumper-b746/tests/gtest/CMakeLists.txt        2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/tests/gtest/CMakeLists.txt        2026-09-18 
02:05:14.000000000 +0200
@@ -59,7 +59,10 @@
         "${CMAKE_SOURCE_DIR}/utils/file_io.ixx"
         "${CMAKE_SOURCE_DIR}/utils/misc.ixx"
         "${CMAKE_SOURCE_DIR}/utils/strings.ixx"
-    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.."
+    WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+)
+target_compile_definitions(gtest_unscramble PRIVATE
+    REDUMPER_TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../unscramble"
 )
 
 add_gtest(gtest_crc
@@ -87,3 +90,8 @@
     SOURCE  "test_bit_copy.cc"
     MODULES "${CMAKE_SOURCE_DIR}/utils/misc.ixx"
 )
+
+add_gtest(gtest_udf
+    SOURCE  "test_udf.cc"
+    MODULES "${CMAKE_SOURCE_DIR}/filesystem/udf/udf_size.ixx"
+)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/tests/gtest/test_udf.cc 
new/redumper-b752/tests/gtest/test_udf.cc
--- old/redumper-b746/tests/gtest/test_udf.cc   1970-01-01 01:00:00.000000000 
+0100
+++ new/redumper-b752/tests/gtest/test_udf.cc   2026-09-18 02:05:14.000000000 
+0200
@@ -0,0 +1,38 @@
+#include <cstdint>
+#include <gtest/gtest.h>
+#include <limits>
+#include <optional>
+
+import filesystem.udf_size;
+
+using namespace gpsxre;
+
+
+TEST(UDF, VolumeSectorsCountIncludesReserveVDSAndTrailingAVDP)
+{
+    constexpr uint32_t sector_size = 2048;
+    constexpr uint32_t partition_start = 277;
+    constexpr uint32_t partition_length = 23728682;
+    EXPECT_EQ(udf::get_volume_sectors_count(partition_start + 
partition_length, 23728959, 32768, sector_size), 23728976);
+}
+
+
+TEST(UDF, VolumeSectorsCountUsesPartitionEndWithoutReserveVDS)
+{
+    EXPECT_EQ(udf::get_volume_sectors_count(1000, 0, 0, 2048), 1001);
+}
+
+
+TEST(UDF, VolumeSectorsCountUsesLargestMetadataEnd)
+{
+    EXPECT_EQ(udf::get_volume_sectors_count(1000, 500, 2048, 2048), 1001);
+    EXPECT_EQ(udf::get_volume_sectors_count(900, 1000, 2049, 2048), 1003);
+}
+
+
+TEST(UDF, VolumeSectorsCountRejectsInvalidOrOverflowingValues)
+{
+    EXPECT_EQ(udf::get_volume_sectors_count(0, 0, 0, 2048), std::nullopt);
+    EXPECT_EQ(udf::get_volume_sectors_count(1000, 0, 0, 0), std::nullopt);
+    EXPECT_EQ(udf::get_volume_sectors_count(1000, 
std::numeric_limits<uint32_t>::max(), 2048, 2048), std::nullopt);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/redumper-b746/tests/gtest/test_unscramble.cc 
new/redumper-b752/tests/gtest/test_unscramble.cc
--- old/redumper-b746/tests/gtest/test_unscramble.cc    2026-08-25 
03:51:25.000000000 +0200
+++ new/redumper-b752/tests/gtest/test_unscramble.cc    2026-09-18 
02:05:14.000000000 +0200
@@ -16,9 +16,8 @@
 
 TEST(Unscramble, AllSamples)
 {
-    const std::filesystem::path data_dir = "unscramble";
-    ASSERT_TRUE(std::filesystem::exists(data_dir) && 
std::filesystem::is_directory(data_dir))
-        << "unscramble/ data directory not found relative to working directory 
" << std::filesystem::current_path();
+    const std::filesystem::path data_dir = REDUMPER_TEST_DATA_DIR;
+    ASSERT_TRUE(std::filesystem::exists(data_dir) && 
std::filesystem::is_directory(data_dir)) << "unscramble data directory not 
found at " << data_dir;
 
     Scrambler scrambler;
 

Reply via email to