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 *)¶meter_list_length)[1]; cdb.parameter_list_length[2] = ((uint8_t *)¶meter_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;
