Skip to content

Initial OmniDrive support (#336)#340

Merged
superg merged 12 commits intomainfrom
omnidrive
Feb 4, 2026
Merged

Initial OmniDrive support (#336)#340
superg merged 12 commits intomainfrom
omnidrive

Conversation

@superg
Copy link
Owner

@superg superg commented Jan 30, 2026

  • Initial OmniDrive support

  • Count SCSI errors in dumpable range during refine

  • Initial Xbox support

  • Raw BD support

  • revert bd

  • revert one more

  • Code review

Summary by CodeRabbit

  • New Features

    • Per‑sector DVD raw dumping, DVD scrambler, DVD EDC, GF(256) utilities, OmniDrive read support and new option to enable raw DVD reads.
  • Improvements

    • Enhanced drive-info and profile handling, deferred drive configuration, smarter speed logic, and improved OmniDrive firmware detection with version reporting.
  • Bug Fixes

    • More robust leadout/overread handling and detection of .sdram images for correct CD/DVD split behavior.
  • Tests

    • DVD EDC computation and verification added to test suite.

* Initial OmniDrive support

* Count SCSI errors in dumpable range during refine

* Initial Xbox support

* Raw BD support

* revert bd

* revert one more

* Code review
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

Adds DVD subsystem and OmniDrive/raw read support: new dvd modules (framing, EDC, scrambler), GF(256) utilities, OmniDrive SCSI opcode/command, drive reserved5 and OmniDrive detection, per-sector DVD dump refactor with read_dvd_sectors, signed LBA/range API changes, OVERREAD_COUNT and CHUNK_1MB, and build/test updates.

Changes

Cohort / File(s) Summary
Build / Tests
CMakeLists.txt, tests/CMakeLists.txt, tests/tests.cc
Updated target_sources to rename CD module files and add DVD/galois modules; added DVD EDC test verification.
Drive & Options
drive.ixx, drive/test.ixx, options.ixx
Added reserved5 field, made is_omnidrive_firmware return std::optional<std::string>, updated test leadout constants to OVERREAD_COUNT/CHUNK_1MB, and added Options::dvd_raw.
CD leadout & protection
cd/cd_dump_extra.ixx, cd/cd_dump.ixx, cd/protection.ixx, cd/cd_common.ixx
Replaced LEADOUT_OVERREAD_COUNT with OVERREAD_COUNT, switched 1MB literals to CHUNK_1MB, tightened Mediatek leadout guard, and broadened protection existence check to include .sdram.
DVD core modules
dvd/dvd.ixx, dvd/dvd_edc.ixx, dvd/dvd_scrambler.ixx, dvd/xbox.ixx
Added DVD framing types, parity/GF256 mapping, validate_id, DVD_EDC typedef, DVD_Scrambler class; changed xbox APIs to use signed int32_t ranges and adjusted initialize signature.
DVD dump & systems
dvd/dvd_dump.ixx, systems/s_xbox.ixx
Introduced DumpConfig, dump_get_config, read_dvd_sectors (with raw/OmniDrive support), dynamic sector_size/image extensions, lba_zero/start/end, and switched local Xbox protection vectors to Range<int32_t>.
SCSI layer
scsi/mmc.ixx, scsi/cmd.ixx
Added READ_OMNIDRIVE opcode, OmniDrive_DiscType/OmniDrive_Subchannels enums, CDB12_ReadOmniDrive struct, and exported cmd_read_omnidrive() wrapper.
Utilities & common
utils/galois.ixx, utils/misc.ixx, common.ixx
Added GF256 (exp/log LUTs, add/mul), constrained digits_count template to unsigned types, and exported OVERREAD_COUNT and CHUNK_1MB.
Main flow & split
redumper.ixx, cd/cd_split.ixx, rings.ixx, debug.ixx, hash.ixx
Deferred/enhanced drive/profile handling and read-speed logic, richer drive-feature logging, DVD split trigger now checks .iso or .sdram, and replaced magic 1MB literals with CHUNK_1MB.

Sequence Diagram

sequenceDiagram
    participant App as Redumper
    participant Drive as Drive (inquiry / reserved5)
    participant SCSI as SPTD / SCSI
    participant Dump as DumpConfig
    participant DVD as dvd module
    participant EDC as DVD_EDC
    participant FS as File I/O

    App->>Drive: query drive (inquiry -> DriveConfig)
    Drive-->>App: DriveConfig + optional OmniDrive version

    App->>Dump: dump_get_config(disc_type, raw)
    Dump-->>App: sector_size, image_extension, lba_start/end

    alt OmniDrive + raw
        App->>SCSI: cmd_read_omnidrive(buffer, block_size, lba, count, disc_type, ...)
        SCSI-->>App: raw sector data
    else standard read
        App->>SCSI: standard read command
        SCSI-->>App: sector data
    end

    App->>DVD: read_dvd_sectors(buffer, sector_size, lba, count, raw)
    DVD->>DVD: descramble / parity / validate_id
    DVD->>EDC: compute/verify EDC
    EDC-->>DVD: validation result
    DVD-->>App: processed sector

    App->>FS: write to image file (.iso/.sdram)
    FS-->>App: write complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • #336 — Overlapping OmniDrive/raw support, DVD modules, scsi/mmc/cmd additions, and dump logic changes.
  • #308 — Related refactor of dvd/dvd_dump to sector/LBA-based APIs and progress/signature changes.
  • #319 — Overlapping Mediatek leadout/cache handling edits affecting CD leadout code paths.

Poem

🐰 I hopped through parity and galois light,

I nibbled scramblers in the silent night,
OmniDrive hummed while PSNs turned to LBA,
EDC winked true and frames found their way,
A rabbit cheers — new DVDs saved the day!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Initial OmniDrive support' accurately describes the primary change across the changeset, which introduces OmniDrive-related structures, functions, drive detection, and firmware handling throughout multiple modules.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch omnidrive

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@cd/protection.ixx`:
- Around line 287-290: The existence check uses the wrong extension: change the
check that currently tests for image_prefix + ".sdram" to instead test for
image_prefix + ".scram" so it matches the file opened later; update the
condition that checks std::filesystem::exists(image_prefix + ".sdram") to
std::filesystem::exists(image_prefix + ".scram") (working with image_prefix
built from options.image_path and options.image_name).

In `@dvd/dvd_scrambler.ixx`:
- Around line 45-48: The current offset calculation mixes floating point by
using +7.5; change it to pure integer math: compute the nibble value (e.g.,
uint32_t nibble = (*key) ^ ((psn >> 4) & 0xF)) and then compute offset using the
integer equivalent of +7.5 as (15/2), i.e. offset = ((2 * nibble + 15) *
FORM1_DATA_SIZE) / 2, ensuring offset and intermediate values are uint32_t to
avoid any implicit floating-point conversions.

In `@systems/gc.ixx`:
- Around line 2-37: The code in SystemGC::printInfo currently casts
header_data.data() to Header* which breaks strict aliasing and can misalign
fields; add `#include` <cstring> and replace the cast with a local Header variable
(e.g., Header header{}), then std::memcpy(&header, header_data.data(),
sizeof(Header)); and update all uses of header->... to header.... Ensure
comparisons using _GC_MAGIC and subsequent field accesses reference the copied
header instance.

In `@systems/wii.ixx`:
- Around line 2-37: The code in SystemWII::printInfo currently casts
header_data.data() to Header*, which is undefined on architectures with stricter
alignment; instead allocate a local Header variable, call data_reader->read into
header_data as before, then use std::memcpy to copy sizeof(Header) bytes from
header_data.data() into the local Header, and use that local Header for checks
(e.g., comparing header.wii_magic to _WII_MAGIC) and subsequent accesses to
avoid misaligned reads.

In `@utils/misc.ixx`:
- Around line 274-278: The function digits_count(T value) can invoke undefined
behavior when negating the smallest signed value; instead compute the absolute
magnitude by converting value to an unsigned type: use std::make_unsigned_t<T>
(or a safe fixed unsigned type) to hold the magnitude, assign magnitude =
static_cast<std::make_unsigned_t<T>>(value < 0 ?
-static_cast<std::make_unsigned_t<T>>(value) : value), then compute and return
(magnitude ? log10(magnitude) : 0) + 1; update digits_count to use that unsigned
magnitude and add the needed <type_traits> include.
🧹 Nitpick comments (6)
dvd/xbox.ixx (2)

346-354: TODO comment indicates incomplete implementation for OmniDrive error handling.

The TODO at Line 352 notes that retry/recovery logic is needed when status code is non-zero, ID is invalid, or EDC fails. This may cause silent data corruption if the initial read fails but doesn't raise an error.

Consider implementing the retry mechanism before release, or at minimum logging a warning when the raw sector data may be unreliable.

Would you like me to help draft the retry logic for reading from XGD_SS_LEADOUT_SECTOR_PSN + 0x40?


382-387: OmniDrive security sector marked as "invalid" without user guidance.

The TODO indicates SS rebuild requires cryptography. Users will see "security sector: invalid" in logs without understanding why or what limitations this creates. Consider adding a LOG statement explaining that OmniDrive SS reconstruction is pending implementation.

dvd/dvd_raw.ixx (2)

3-5: Remove unused includes.

<cstring>, <vector>, and "throw_line.hh" appear to be unused in this file. Removing them would reduce compilation dependencies.

🧹 Proposed cleanup
 module;
 `#include` <cstdint>
-#include <cstring>
-#include <vector>
-#include "throw_line.hh"

 export module dvd.raw;

27-27: Consider simplifying the address adjustment.

address += -DVD_LBA_START can be simplified to address -= DVD_LBA_START for better readability.

✨ Proposed simplification
     if(address < 0)
     {
         raw_addressing = true;
-        address += -DVD_LBA_START;
+        address -= DVD_LBA_START;
     }
dvd/dvd_split.ixx (2)

135-136: Consider adding error handling for file open failures.

The file streams are opened without checking if the operations succeeded. If sdram_path exists but cannot be read (permissions, etc.), or if iso_path cannot be created, the subsequent operations will silently fail.

💡 Suggested improvement
     std::ifstream sdram_fs(sdram_path, std::ofstream::binary);
+    if(!sdram_fs)
+        throw_line("failed to open sdram file for reading ({})", sdram_path.filename().string());
     std::ofstream iso_fs(iso_path, std::ofstream::binary);
+    if(!iso_fs)
+        throw_line("failed to create iso file for writing ({})", iso_path.filename().string());

176-191: Infinite loop relies on EOF detection via partial read.

The while(true) loop exits when bytesRead != sector.size(), which handles normal EOF. However, if a read error occurs (not EOF), the loop will also exit but success state from the last descramble call may be misleading. Consider distinguishing between EOF and actual read errors for better diagnostics.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dvd/dvd_dump.ixx`:
- Around line 491-496: In progress_output, avoid division-by-zero when computing
percent_dumped: compute int64_t denom = (int64_t)lba_end - (int64_t)lba_start
and if denom == 0 set percent_dumped to a safe value (e.g., 100 when lba ==
lba_end else 0) otherwise compute percent_dumped = (int64_t)(lba - lba_start) *
100 / denom; update the logic in progress_output to use these checks and the
denom variable to prevent divide-by-zero while keeping the existing
spinner_animation and LOGC_RF call.

superg and others added 3 commits January 31, 2026 13:03
CR feedback

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@dvd/dvd_dump.ixx`:
- Around line 882-889: The vector allocation under std::vector<State>
state_buffer(lba_end - lba_start) can underflow when lba_end <= lba_start; guard
the allocation and subsequent read_entry/count logic by checking that lba_end >
lba_start (e.g. only construct state_buffer and call read_entry when the range
length is positive), otherwise skip allocation and ensure errors.scsi remains
zero; update the block around dump_mode / DumpMode::DUMP to perform this range
check before touching state_buffer, read_entry, or computing errors.scsi.
- Around line 828-858: The dvd_raw option must only affect DVD media: change the
computation and use of the local raw flag so it is true only when
options.dvd_raw is set, omnidrive_firmware is present, and the current disc type
(ctx.disc_type) is actually DVD; use that guarded raw everywhere (including the
dump_get_config(ctx.disc_type, raw) call and the branch that sets rom_update =
false / fs_ctx.search = false) so Blu‑ray or other media won’t have metadata/ROM
update disabled by the dvd_raw option.
🧹 Nitpick comments (1)
dvd/dvd_dump.ixx (1)

511-534: Use sector_size for buffer indexing to keep the raw path future-proof.
The loop currently indexes sectors by sizeof(RecordingFrame) which can drift from sector_size if config evolves.

♻️ Proposed tweak
-        for(uint32_t i = 0; i < sectors_count; ++i)
-        {
-            auto &recording_frame = (RecordingFrame &)sectors[i * sizeof(RecordingFrame)];
-            recording_frame = DataFrame_to_RecordingFrame(data_frames[i]);
-        }
+        for(uint32_t i = 0; i < sectors_count; ++i)
+        {
+            auto &recording_frame = (RecordingFrame &)sectors[i * sector_size];
+            recording_frame = DataFrame_to_RecordingFrame(data_frames[i]);
+        }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dvd/dvd_scrambler.ixx`:
- Around line 47-76: In descramble, avoid reading frame->edc or computing the
EDC when the provided buffer is smaller than a full DataFrame: add a guard
(check size >= sizeof(DataFrame)) before the code that accesses frame->edc and
calls DVD_EDC().update(...).final(), and only compare the EDC (and set
unscrambled / call the fallback process to re-scramble) when that size check
passes; keep all other validation and the process(...) calls for partial buffers
unchanged. Ensure references to DataFrame, descramble, DVD_EDC::update, and
offsetof(DataFrame, edc) are used to locate and gate the EDC access.
🧹 Nitpick comments (1)
dvd/dvd_dump.ixx (1)

528-532: Consider using reinterpret_cast for clarity.

The C-style cast to RecordingFrame & works correctly but is less explicit. Using reinterpret_cast would better communicate the intentional memory reinterpretation.

♻️ Suggested refactor
         for(uint32_t i = 0; i < sectors_count; ++i)
         {
-            auto &recording_frame = (RecordingFrame &)sectors[i * sizeof(RecordingFrame)];
-            recording_frame = DataFrame_to_RecordingFrame(data_frames[i]);
+            auto *recording_frame = reinterpret_cast<RecordingFrame *>(sectors + i * sizeof(RecordingFrame));
+            *recording_frame = DataFrame_to_RecordingFrame(data_frames[i]);
         }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@dvd/dvd_dump.ixx`:
- Around line 874-884: The code currently throws when lba_start >= lba_end;
instead preserve the previous no-op behavior by removing the throw_line and
guarding allocations and read_entry so they only run when lba_end > lba_start
and dump_mode != DumpMode::DUMP; specifically, in the block using Errors,
std::vector<State> state_buffer, and read_entry, wrap the allocation/read in an
if (lba_end > lba_start) check (or skip allocation when the computed size would
be zero/negative), ensure errors.scsi is set appropriately (e.g. zero) when
skipped, and leave the FIXME about chunking for separate follow-up work
(references: lba_start, lba_end, DumpMode::DUMP, state_buffer, read_entry,
cfg.lba_zero, State::ERROR_SKIP).
- Around line 512-525: The code allocates std::vector<DataFrame> data_frames but
passes sector_size bytes per sector to cmd_read_omnidrive, which can overflow
when sector_size > sizeof(DataFrame); replace the DataFrame vector with a byte
buffer sized sectors_count * sector_size (e.g., std::vector<uint8_t>
raw_buf(sectors_count * sector_size)), call cmd_read_omnidrive(...,
raw_buf.data(), sector_size, ...), then in the loop for each i reinterpret a
DataFrame from raw_buf.data() + i*sector_size (or memcpy into a DataFrame
instance) and convert it with DataFrame_to_RecordingFrame into the output buffer
at sectors + i*sector_size (avoid the current incorrect cast using sectors[i *
sizeof(RecordingFrame)]). Ensure all pointer arithmetic uses sector_size and no
buffer overrun occurs.
🧹 Nitpick comments (1)
dvd/dvd.ixx (1)

28-105: Add size/layout guards for raw frame structs.
These types are serialized via byte copies; a static_assert makes layout drift obvious.

♻️ Suggested guard rails
 export struct RecordingFrame
 {
     struct
     {
         uint8_t main_data[172];
         uint8_t parity_inner[10];
     } row[12];
     uint8_t parity_outer[182];
 };
+
+static_assert(sizeof(DataFrame) == FORM1_DATA_SIZE + 16, "DataFrame size mismatch");
+static_assert(sizeof(RecordingFrame) == 12 * (172 + 10) + 182, "RecordingFrame size mismatch");

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dvd/dvd_dump.ixx`:
- Around line 512-529: The loop in read_dvd_sectors casts the byte buffer to
RecordingFrame& when writing converted frames; replace that with a memcpy
pattern: for each i, create a RecordingFrame tmp =
DataFrame_to_RecordingFrame(data_frames[i]) (or construct the RecordingFrame),
then memcpy the bytes into the destination buffer at sectors + i *
sizeof(RecordingFrame) using sizeof(RecordingFrame). This keeps behavior
identical to DataFrame_to_RecordingFrame while following the codebase's
established memcpy pattern and avoiding direct reference casting.

@superg superg merged commit 1c246b5 into main Feb 4, 2026
11 checks passed
Deterous added a commit to Deterous/redumper that referenced this pull request Feb 5, 2026
* Skip all sectors after a subcode desync instead of just the first (superg#341)

* Initial OmniDrive support (superg#336) (superg#340)

* Initial OmniDrive support (superg#336)

* Initial OmniDrive support

* Count SCSI errors in dumpable range during refine

* Initial Xbox support

* Raw BD support

* revert bd

* revert one more

* Code review

* changed dvd scrambler table init to construction (temporary?)

* revert code that will come in other PRs

* refactoring

* rework drive info output

* recording/data frame conversion, dvd dump loop refactored

* Apply suggestions from code review

CR feedback

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* added parity generation

* commonize overread count, dvd overread configuration, PSN refactoring

* chunk refactoring, addressed CR review

* removed unnecessary code

* addressed CR issue

---------

Co-authored-by: Deterous <138427222+Deterous@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* reverted to --rings option, removed desync stats (superg#342)

---------

Co-authored-by: Sazpaimon <sazpaimon@x-cult.org>
Co-authored-by: Hennadiy Brych <gennadiy.brich@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@superg superg deleted the omnidrive branch February 28, 2026 15:26
@coderabbitai coderabbitai bot mentioned this pull request Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants