Skip to content

NR/RVT-R dumping support (WIP) - #388

Draft
RibShark wants to merge 1 commit into
superg:mainfrom
RibShark:main
Draft

NR/RVT-R dumping support (WIP)#388
RibShark wants to merge 1 commit into
superg:mainfrom
RibShark:main

Conversation

@RibShark

@RibShark RibShark commented Jun 9, 2026

Copy link
Copy Markdown

This is a WIP PR for dumping NR/RVT-R discs via an upcoming version of OmniDrive.
NR/RVT-R discs differ from normal Nintendo discs in the following ways:

  • Scrambling is applied to all sectors, including DMI/PFI, so these must be obtained and descrambled manually. Note that the sector layout for DMI/PFI is normal (CPR_MAI not shifted to end of sector).
  • Nintendo Key for scrambling is fixed at 0x9 for the entire disc, with no additional offset, including lead-in and LBA 0-15.
  • Book type in PFI is standard for DVD-R, so the logic for detecting Nintendo discs in split was modified to instead search for Nintendo in the DMI, and Emu in the same string to detect a dev disc.
  • In OmniDrive, NR/RVT-R discs return a capacity of 1, but the full capacity when the PFI is queried (the drive fakes the PFI). This is used to detect Nintendo dev discs in dump, but I'm not sure if XGD3s with no video partition also appear like this? If so, logic will need to be changed.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7642119-0e9d-489e-a09a-7c3aff54b829

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@superg

superg commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Will try to review this week, Nintendo likes it complicated!

@superg superg left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This looks good, just couple small things.

If it's tested, let's clean it up and merge.

Comment thread dvd/dvd_split.ixx
auto manufacturer = read_vector(manufacturer_path);
if(manufacturer.size() > sizeof(CMD_ParameterListHeader))
{
auto idString = (const char*)&manufacturer[sizeof(CMD_ParameterListHeader) + 0x10];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I like this detection better than what I had. Is there any structure definition for nintendo manufacturer or it's not reverse engineered?

Comment thread dvd/dvd_scrambler.ixx
if(nintendo_key)
offset = (*nintendo_key ^ (psn >> 4 & 0xF)) * FORM1_DATA_SIZE + 7 * FORM1_DATA_SIZE + FORM1_DATA_SIZE / 2;
{
offset = *nintendo_key ^ (psn >> 4 & 0xF);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

It's nit, but I'd do it like this:

offset = (*nintendo_key ^ (psn >> 4 & 0xF)) * FORM1_DATA_SIZE;
if(!nintendo_dev_disc)
    offset += 7 * FORM1_DATA_SIZE + FORM1_DATA_SIZE / 2;

Comment thread dvd/dvd_dump.ixx
Comment on lines +952 to +977
// nintendo dev discs need manual descrambling
dvd::DataFrame df;
bool dmi_found = false;
for(uint8_t dmi_retries = 0; dmi_retries < 192; ++dmi_retries)
{
uint32_t dmi_address = 0x2E401 + dmi_retries * 0x10;
auto status = cmd_read_omnidrive(*ctx.sptd, (uint8_t *)&df, sizeof(dvd::DataFrame), 0x2E401, 1, OmniDrive_DiscType::DVD, true, false, false, OmniDrive_Subchannels::NONE, false);
if(status.status_code)
LOG("[PSN: {:X}] omnidrive: SCSI error ({})", 0x2E401, SPTD::StatusMessage(status));
else
{
if (df.valid(9, true))
{
df.descramble(9, true);
dmi_found = true;
}
else
LOG("[PSN: {:X}] omnidrive: invalid DMI, discarded", dmi_address);
}
}
if (dmi_found)
{
structure.resize(sizeof(CMD_ParameterListHeader) + FORM1_DATA_SIZE);
*(CMD_ParameterListHeader*)structure.data() = { .data_length = endian_swap((uint16_t)(FORM1_DATA_SIZE + 2)) };
std::copy(df.main_data, df.main_data + FORM1_DATA_SIZE, structure.data() + sizeof(CMD_ParameterListHeader));
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Can you extract this as a function and use it for both DMI and PFI? I see only sector address is different, everything else looks the same.

Use/pass nintendo_key instead of 9

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