Conversation
📝 WalkthroughWalkthroughThis PR introduces comprehensive DVD support infrastructure, including OmniDrive firmware detection for raw DVD operations, Galois Field arithmetic utilities for descrambling, and system-specific implementations for GameCube and Wii. New SCSI command structures enable raw DVD reads, while updated dumping and splitting logic handle raw sector modes, descrambling workflows, and signed address ranges for DVD media processing. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/CLI
participant Dumper as DVD Dumper
participant Drive as OmniDrive
participant Splitter as DVD Splitter
participant Descrambler as Descrambler
User->>Dumper: redumper_dump_dvd(context, options)
Dumper->>Drive: Check is_omnidrive_firmware()
alt OmniDrive Detected
Dumper->>Drive: cmd_read_omnidrive(raw mode)
Drive-->>Dumper: Raw sector data (DataFrame)
Dumper->>Dumper: Store as .sdram (raw)
else Standard Drive
Dumper->>Drive: cmd_read (standard mode)
Drive-->>Dumper: Standard sector data
Dumper->>Dumper: Store as .iso
end
User->>Splitter: redumper_split_dvd(context, options)
alt Raw DVD Detected (.sdram exists)
Splitter->>Splitter: Read raw .sdram
Splitter->>Descrambler: descramble(sector, psn, key)
Descrambler->>Descrambler: validate_id(header)
Descrambler->>Descrambler: XOR descrambling via table
Descrambler->>Descrambler: Verify EDC checksum
Descrambler-->>Splitter: Plaintext user data
Splitter->>Splitter: Write plaintext to .iso
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@drive.ixx`:
- Around line 427-438: The omnidrive_version function can sign-extend bytes when
casting chars directly to uint32_t; update omnidrive_version to read bytes 9–11
from drive_config.reserved5 by first casting each byte to uint8_t and then to
uint32_t (e.g., convert (*drive_config.reserved5)[9/10/11] ->
(uint32_t)(uint8_t)(...)) to preserve the raw byte values while keeping the
existing is_omnidrive_firmware check and string formatting logic intact.
In `@dvd/dvd_dump.ixx`:
- Around line 840-848: The state_buffer is allocated with size sectors_count but
read_entry in raw mode requests (lba_end - lba_start) elements which can exceed
sectors_count; fix by sizing or bounding the read: compute a count_to_read = raw
? (lba_end - lba_start) : sectors_count and either allocate state_buffer with
count_to_read when raw, or clamp count_to_read to state_buffer.size() before
calling read_entry; update the call to read_entry to use that bounded count and
ensure errors.scsi counts only the actually-read entries (using
State::ERROR_SKIP on the same range).
In `@systems/wii.ixx`:
- Around line 34-49: The code casts header_data.data() to Header* which can
cause misalignment and strict-aliasing issues; instead allocate a properly
aligned Header instance on the stack, copy the bytes read by data_reader->read
into that instance using std::memcpy, then use that Header object for checks
against _WII_MAGIC and to read fields like disc_version, disc_id, title and
disc_number; replace uses of the pointer-cast (header) with the stack Header
variable and preserve the same logic for logging.
In `@utils/misc.ixx`:
- Around line 273-278: digits_count is vulnerable to signed-min overflow when
negating a negative T; update the template to compute digit count on an unsigned
magnitude instead of directly negating a possibly-min signed value. For example,
inside template<typename T> T digits_count(T value) use a type alias U =
std::make_unsigned_t<T> (with a static_assert that T is integral), then compute
U u = value < 0 ? static_cast<U>(-(value + 1)) + 1 : static_cast<U>(value); and
use u for the log10/zero check and return type conversion; alternatively, if you
prefer to forbid signed types, add a static_assert(!std::is_signed_v<T>,
"digits_count requires unsigned type") and keep the current logic. Ensure you
reference the digits_count template and include headers (<type_traits>, <cmath>)
as needed.
🧹 Nitpick comments (9)
dvd/xbox.ixx (1)
330-385: Consider clamping negative L1 padding length before inserting ranges.If
ss_psn_first <= layer0_last,l1_padding_lengthbecomes negative, which can create an inverted range ininsert_range. A small clamp keeps this defensive.🧰 Suggested tweak
int32_t psn_first = sign_extend<24>(endian_swap(layer0_ld.data_start_sector)); int32_t layer0_last = sign_extend<24>(endian_swap(layer0_ld.layer0_end_sector)); int32_t ss_psn_first = sign_extend<24>(endian_swap(sld.ld.data_start_sector)); int32_t l1_padding_length = ss_psn_first - layer0_last - 1; + if(l1_padding_length < 0) + l1_padding_length = 0; if(xgd_version(ss_layer0_last) == 3) l1_padding_length += 4096;options.ixx (1)
412-412: Help text placement could be improved.The
--dvd-rawoption is grouped under "drive specific" section, which is appropriate, but there's a missing newline before the next option (--mediatek-skip-leadout), which breaks the visual flow of the help output. Consider verifying the help output formatting.dvd/dvd_raw.ixx (3)
21-25:MediatekCacheFramestruct is defined but unused.This struct appears to be prepared for the TODO on line 40 (
cmd_mediatek_read_cache). Consider adding a comment indicating its intended future use, or removing it until the cache read functionality is implemented.
28-41: TODO comments indicate incomplete implementation gates.The function works but has two outstanding TODOs:
- Line 37: OmniDrive check should gate this code path
- Line 40: Mediatek cache read alternative is not implemented
These are appropriate markers for future work. Ensure these are tracked for follow-up.
Would you like me to open issues to track these TODO items?
34-34: Consider simplifying the address arithmetic.
address += -DVD_LBA_STARTis equivalent toaddress -= DVD_LBA_START. The current form works but the subtraction is more idiomatic.♻️ Suggested simplification
- address += -DVD_LBA_START; + address -= DVD_LBA_START;systems/gc.ixx (1)
32-38: Consider adding error handling for failed reads.If
data_reader->readfails or returns less data than expected, the code continues to accessheader->magicwhich could contain uninitialized data. Consider checking the return value or initializingheader_datato zero.♻️ Suggested improvement
void printInfo(std::ostream &os, DataReader *data_reader, const std::filesystem::path &track_path, bool) const override { - std::vector<uint8_t> header_data(FORM1_DATA_SIZE); + std::vector<uint8_t> header_data(FORM1_DATA_SIZE, 0); data_reader->read((uint8_t *)header_data.data(), 0, 1); auto header = (Header *)header_data.data(); if(header->magic != _GC_MAGIC) return;dvd/dvd_split.ixx (2)
134-135: Usestd::ifstream::binaryfor the input file stream.While
std::ofstream::binaryandstd::ifstream::binaryhave the same underlying value (std::ios::binary), usingstd::ofstream::binaryfor anifstreamis misleading to readers.♻️ Suggested fix
- std::ifstream raw_fs(raw_path, std::ofstream::binary); - std::ofstream iso_fs(iso_path, std::ofstream::binary); + std::ifstream raw_fs(raw_path, std::ifstream::binary); + std::ofstream iso_fs(iso_path, std::ofstream::binary);
175-190: Consider adding progress feedback and interrupt handling.The infinite loop lacks progress reporting and interrupt handling, unlike the dump loop in
dvd_dump.ixx. For large DVD images, users have no visibility into the descrambling progress.dvd/dvd_scrambler.ixx (1)
45-48: Implicit floating-point to integer conversion for Nintendo offset.The expression
((*key ^ (psn >> 4 & 0xF)) + 7.5) * FORM1_DATA_SIZEuses floating-point arithmetic that gets implicitly truncated touint32_t. Consider making the truncation explicit if this is intentional.♻️ Suggested explicit cast
// custom XOR table offset for nintendo if(key) - offset = ((*key ^ (psn >> 4 & 0xF)) + 7.5) * FORM1_DATA_SIZE; + offset = static_cast<uint32_t>(((*key ^ (psn >> 4 & 0xF)) + 7.5) * FORM1_DATA_SIZE);
| export template<typename T> | ||
| T digits_count(T value) | ||
| { | ||
| if(value < 0) | ||
| value = -value; | ||
| return (value ? log10(value) : 0) + 1; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's locate and read the file
find . -name "misc.ixx" -type fRepository: superg/redumper
Length of output: 75
🏁 Script executed:
# Read the specific lines mentioned in the review
head -n 280 utils/misc.ixx | tail -n 20Repository: superg/redumper
Length of output: 397
🏁 Script executed:
# Search for all usages of digits_count to understand the context
rg "digits_count" -A 2 -B 2Repository: superg/redumper
Length of output: 936
🏁 Script executed:
# Find the type of lba_end - search around the usage in dvd_dump.ixx
rg "lba_end" dvd/dvd_dump.ixx -B 5 -A 2 | head -n 50Repository: superg/redumper
Length of output: 1899
🏁 Script executed:
# Search for the function containing lba_end to understand its type
rg "lba_end.*=" dvd/dvd_dump.ixx -B 10 | head -n 50Repository: superg/redumper
Length of output: 1283
🏁 Script executed:
# Search for LBA type definition
rg "using LBA|typedef.*LBA|struct LBA|class LBA" -A 2Repository: superg/redumper
Length of output: 41
🏁 Script executed:
# Find DVD_LBA_START value to confirm it's positive
rg "DVD_LBA_START" -A 1 -B 1 | head -n 20Repository: superg/redumper
Length of output: 1154
🏁 Script executed:
# Check if there are any other signed integer usages of digits_count
rg "digits_count" --type-list | head -20Repository: superg/redumper
Length of output: 448
🏁 Script executed:
# Broader search for digits_count calls to see all instantiations
rg "digits_count\s*\(" -B 3Repository: superg/redumper
Length of output: 800
Guard against signed-min overflow when negating.
value = -value is undefined behavior for std::numeric_limits<T>::min() on signed integers. While digits_count is currently called only with positive values in the codebase (lba_end is validated to be ≥ DVD_LBA_START, and *decimal is uint64_t), the template's general-purpose nature makes it vulnerable if instantiated with signed types elsewhere.
Rewrite to safely handle negative values by casting to unsigned before negation, or add a precondition. Note: the suggested fix using value + 1 is incorrect as it also overflows for signed min; instead, consider:
- Casting negative values to unsigned directly (if the type can represent the magnitude)
- Or using
std::abswith appropriate handling for signed min - Or adding a static assertion to reject signed types
🤖 Prompt for AI Agents
In `@utils/misc.ixx` around lines 273 - 278, digits_count is vulnerable to
signed-min overflow when negating a negative T; update the template to compute
digit count on an unsigned magnitude instead of directly negating a possibly-min
signed value. For example, inside template<typename T> T digits_count(T value)
use a type alias U = std::make_unsigned_t<T> (with a static_assert that T is
integral), then compute U u = value < 0 ? static_cast<U>(-(value + 1)) + 1 :
static_cast<U>(value); and use u for the log10/zero check and return type
conversion; alternatively, if you prefer to forbid signed types, add a
static_assert(!std::is_signed_v<T>, "digits_count requires unsigned type") and
keep the current logic. Ensure you reference the digits_count template and
include headers (<type_traits>, <cmath>) as needed.
dvd/xbox.ixx
Outdated
|
|
||
| export std::shared_ptr<Context> initialize(std::vector<Range<uint32_t>> &protection, SPTD &sptd, const READ_DVD_STRUCTURE_LayerDescriptor &layer0_ld, uint32_t sectors_count_capacity, bool partial_ss, | ||
| bool kreon_custom_firmware) | ||
| export std::shared_ptr<Context> initialize(std::vector<Range<int32_t>> &protection, SPTD &sptd, const READ_DVD_STRUCTURE_LayerDescriptor &layer0_ld, int32_t sectors_count_capacity, bool partial_ss, |
There was a problem hiding this comment.
Can sectors_count_capacity be negative?
There was a problem hiding this comment.
Fixed: moved the cast to int32_t within this function, so the argument is still unsigned.
dvd/dvd_dump.ixx
Outdated
| else | ||
| { | ||
| LOG("kreon: malformed XGD detected, continuing in normal dump mode"); | ||
| if(kreon_firmware) |
There was a problem hiding this comment.
if(kreon_firmware || omnidrive_firmware)
LOG("{}: malformed XGD detected, continuing in normal dump mode", kreon_firmware ? "kreon" : "omnidrive");
(not to duplicate almost identical strings)
There was a problem hiding this comment.
If more firmwares are added in the future, ternary statement becomes too complex, but I'll change it for now.
dvd/dvd_dump.ixx
Outdated
| if(!raw && *options.lba_start < 0) | ||
| throw_line("lba_start must be non-negative for non-raw DVD dumps"); | ||
| else if(*options.lba_start < DVD_LBA_START) | ||
| throw_line("lba_start must be at least {}", DVD_LBA_START); |
There was a problem hiding this comment.
Please rephrase this, "at least" is not clear if it should be lower or higher than this value.
Something like "lba_start is lower than {}".
| else if(options.dvd_raw) | ||
| LOG("warning: drive not compatible with raw DVD dumping"); | ||
|
|
||
| uint32_t sector_size = raw ? sizeof(DataFrame) : FORM1_DATA_SIZE; |
There was a problem hiding this comment.
I will be changing this to sizeof(RecordingFrame) after I implement conversion functions, correct?
There was a problem hiding this comment.
Yes if the sdram function is storing recording frames.
I'm interested to see how complex adding support for recording frames will be, including any performance issues.
dvd/dvd_dump.ixx
Outdated
| if(!raw && *options.lba_end < 0) | ||
| throw_line("lba_end must be non-negative for non-raw DVD dumps"); | ||
| else if(*options.lba_end < DVD_LBA_START) | ||
| throw_line("lba_end must be at least {}", DVD_LBA_START); |
There was a problem hiding this comment.
Same here,
Please rephrase this, "at least" is not clear if it should be lower or higher than this value.
Something like "lba_end is higher than {}".
| } | ||
|
|
||
| if(xbox) | ||
| if(kreon_firmware && xbox) |
There was a problem hiding this comment.
Why additional check for kreon_firmware?
There was a problem hiding this comment.
That section is not needed for OmniDrive, so it's considered Kreon specific.
Omnidrive does not do any "locking", the entire disc is readable like a normal DVD.
dvd/dvd_raw.ixx
Outdated
| // TODO: gate behind OmniDrive check | ||
| return cmd_read_omnidrive(*ctx.sptd, buffer, block_size, address, transfer_length, OmniDrive_DiscType::DVD, raw_addressing, force_unit_access, false, OmniDrive_Subchannels::NONE, false); | ||
|
|
||
| // TODO: read raw DVD from cache (cmd_mediatek_read_cache) |
There was a problem hiding this comment.
That's not achievable, how would you implement that, whole cache read on every sector?
There was a problem hiding this comment.
I've removed all mentions of the future support of cache reading
| bool raw_addressing = false; | ||
| if(address < 0) | ||
| { | ||
| raw_addressing = true; |
There was a problem hiding this comment.
I thought raw addressing is PSN addressing and not negative only addressing?
There was a problem hiding this comment.
I thought the new C0h command doesn't work with negative LBA addresses, so I had to use PSN addressing for dumping DVD lead-in. If C0h accepts negative LBA, then this isn't needed, I'll have to check later.
dvd/xbox.ixx
Outdated
| int32_t ss_psn_first = sign_extend<24>(endian_swap(sld.ld.data_start_sector)); | ||
|
|
||
| uint32_t l1_padding_length = ss_psn_first - layer0_last - 1; | ||
| int32_t l1_padding_length = ss_psn_first - layer0_last - 1; |
There was a problem hiding this comment.
Okay, I will cast to int32_t when inserting into the protection ranges instead.
| export template<typename T> | ||
| T digits_count(T value) | ||
| { | ||
| if(value < 0) |
There was a problem hiding this comment.
When dumping DVD leadin, the LBA is negative, and digits_count has a negative input (which breaks with the log())
* Initial OmniDrive support (#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>
* 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>
Previous review comments: #332