Files
Aaru-aaru-dps/Aaru.Devices/Device/ScsiCommands/OmniDrive.cs
Rebecca Wallander 7b7d3b1151 untangle if-statement
2026-04-03 16:34:53 +02:00

230 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /***************************************************************************
// Aaru Data Preservation Suite
// ----------------------------------------------------------------------------
//
// Filename : OmniDrive.cs
// Author(s) : Rebecca Wallander <sakcheen@gmail.com>
//
// Component : OmniDrive firmware vendor commands.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains vendor commands for OmniDrive firmware. OmniDrive is custom
// firmware that adds SCSI command 0xC0 to read raw DVD sectors (2064 bytes)
// directly by LBA.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Rebecca Wallander
// ****************************************************************************/
using System;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Logging;
using Aaru.Decoders.DVD;
using NintendoSector = Aaru.Decoders.Nintendo.Sector;
namespace Aaru.Devices;
public partial class Device
{
readonly NintendoSector _nintendoSectorDecoder = new NintendoSector();
readonly Sector _dvdSectorDecoder = new Sector();
enum OmniDriveDiscType
{
CD = 0,
DVD = 1,
BD = 2
}
/// <summary>
/// Encodes byte 1 of the OmniDrive READ CDB to match redumper's <c>CDB12_ReadOmniDrive</c>
/// (<c>scsi/mmc.ixx</c>): <c>disc_type</c> :2 (LSB), <c>raw_addressing</c> :1, <c>fua</c> :1, <c>descramble</c> :1, reserved :3.
/// </summary>
/// <param name="discType">0 = CD, 1 = DVD, 2 = BD (redumper <c>OmniDrive_DiscType</c>).</param>
static byte EncodeOmniDriveReadCdb1(OmniDriveDiscType discType, bool rawAddressing, bool fua, bool descramble)
{
int d = (byte)discType & 3;
int r = rawAddressing ? 1 : 0;
int f = fua ? 1 : 0;
int s = descramble ? 1 : 0;
return (byte)(d | (r << 2) | (f << 3) | (s << 4));
}
static void FillOmniDriveReadDvdCdb(Span<byte> cdb, uint lba, uint transferLength, byte cdbByte1)
{
cdb.Clear();
cdb[0] = (byte)ScsiCommands.ReadOmniDrive;
cdb[1] = cdbByte1;
cdb[2] = (byte)((lba >> 24) & 0xFF);
cdb[3] = (byte)((lba >> 16) & 0xFF);
cdb[4] = (byte)((lba >> 8) & 0xFF);
cdb[5] = (byte)(lba & 0xFF);
cdb[6] = (byte)((transferLength >> 24) & 0xFF);
cdb[7] = (byte)((transferLength >> 16) & 0xFF);
cdb[8] = (byte)((transferLength >> 8) & 0xFF);
cdb[9] = (byte)(transferLength & 0xFF);
cdb[10] = 0; // subchannels=NONE, c2=0
cdb[11] = 0; // control
}
/// <summary>
/// Checks if the drive has OmniDrive firmware by inspecting INQUIRY Reserved5 (bytes 74+) for "OmniDrive",
/// matching redumper's is_omnidrive_firmware behaviour.
/// </summary>
/// <returns><c>true</c> if Reserved5 starts with "OmniDrive" and has at least 11 bytes (8 + 3 for version).</returns>
public bool IsOmniDriveFirmware()
{
bool sense = ScsiInquiry(out byte[] buffer, out _, Timeout, out _);
if (sense || buffer == null) return false;
Inquiry? inquiry = Inquiry.Decode(buffer);
if (!inquiry.HasValue || inquiry.Value.Reserved5 == null || inquiry.Value.Reserved5.Length < 11)
return false;
byte[] reserved5 = inquiry.Value.Reserved5;
byte[] omnidrive = [0x4F, 0x6D, 0x6E, 0x69, 0x44, 0x72, 0x69, 0x76, 0x65]; // "OmniDrive"
if (reserved5.Length < omnidrive.Length) return false;
for (int i = 0; i < omnidrive.Length; i++)
if (reserved5[i] != omnidrive[i])
return false;
return true;
}
/// <summary>Reads raw DVD sectors (2064 bytes) directly by LBA on OmniDrive firmware.</summary>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
/// <param name="buffer">Buffer where the raw DataFrame response will be stored</param>
/// <param name="senseBuffer">Sense buffer.</param>
/// <param name="lba">Start block address (LBA).</param>
/// <param name="transferLength">Number of 2064-byte sectors to read.</param>
/// <param name="timeout">Timeout in seconds.</param>
/// <param name="duration">Duration in milliseconds it took for the device to execute the command.</param>
/// <param name="fua">Set to <c>true</c> if the command should use FUA.</param>
/// <param name="descramble">Set to <c>true</c> if the data should be descrambled by the device.</param>
public bool OmniDriveReadRawDvd(out byte[] buffer, out ReadOnlySpan<byte> senseBuffer, uint lba, uint transferLength,
uint timeout, out double duration, bool fua = false, bool descramble = true)
{
senseBuffer = SenseBuffer;
Span<byte> cdb = CdbBuffer[..12];
buffer = new byte[2064 * transferLength];
FillOmniDriveReadDvdCdb(cdb,
lba,
transferLength,
EncodeOmniDriveReadCdb1(OmniDriveDiscType.DVD, false, fua, descramble));
LastError = SendScsiCommand(cdb, ref buffer, timeout, ScsiDirection.In, out duration, out bool sense);
if (!Sector.CheckIed(buffer, transferLength)) return true;
if(descramble && !Sector.CheckEdc(buffer, transferLength)) return true;
Error = LastError != 0;
AaruLogging.Debug(SCSI_MODULE_NAME, "OmniDrive READ RAW DVD took {0} ms", duration);
return sense;
}
/// <summary>
/// Reads Nintendo GameCube/Wii DVD sectors (2064 bytes) on OmniDrive. The drive returns DVD-layer data with
/// drive-side DVD descramble off; this method applies per-sector Nintendo XOR descramble and returns the result.
/// LBAs 015 use key 0; LBAs ≥ 16 use <paramref name="derivedDiscKey" /> (from LBA 0 CPR_MAI), or 0 if not yet
/// derived.
/// </summary>
/// <param name="derivedDiscKey">Disc key from LBA 0 (015); <c>null</c> until the host has read and derived it.</param>
/// <param name="negativeAddressing">
/// True when caller is reading wrapped negative LBAs (lead-in); these sectors use standard DVD descramble.
/// </param>
/// <param name="regularDataEndExclusive">
/// Exclusive upper bound of regular user-data LBAs. Sectors at or above this are leadout and use standard DVD
/// descramble.
/// </param>
/// <returns><c>true</c> if the command failed and <paramref name="senseBuffer" /> contains the sense buffer.</returns>
public bool OmniDriveReadNintendoDvd(out byte[] buffer, out ReadOnlySpan<byte> senseBuffer, uint lba, uint transferLength,
uint timeout, out double duration, bool fua = false, bool descramble = true,
byte? derivedDiscKey = null, bool negativeAddressing = false,
ulong regularDataEndExclusive = 0)
{
senseBuffer = SenseBuffer;
Span<byte> cdb = CdbBuffer[..12];
buffer = new byte[2064 * transferLength];
FillOmniDriveReadDvdCdb(cdb,
lba,
transferLength,
EncodeOmniDriveReadCdb1(OmniDriveDiscType.DVD, false, fua, false));
LastError = SendScsiCommand(cdb, ref buffer, timeout, ScsiDirection.In, out duration, out bool sense);
if(!Sector.CheckIed(buffer, transferLength)) return true;
if(descramble)
{
const int sectorBytes = 2064;
byte[] outBuf = new byte[sectorBytes * transferLength];
const uint maxRegularLba = 0x00FFFFFF;
for(uint i = 0; i < transferLength; i++)
{
byte[] slice = new byte[sectorBytes];
Array.Copy(buffer, i * sectorBytes, slice, 0, sectorBytes);
uint absLba = lba + i;
bool wrappedNegative = negativeAddressing || absLba > maxRegularLba;
bool leadout = !wrappedNegative && regularDataEndExclusive > 0 && absLba >= regularDataEndExclusive;
ErrorNumber errno;
byte[] descrambled;
if(wrappedNegative || leadout)
errno = _dvdSectorDecoder.Scramble(slice, out descrambled);
else
{
byte key = absLba < 16 ? (byte)0 : (derivedDiscKey ?? (byte)0);
errno = _nintendoSectorDecoder.Scramble(slice, key, out descrambled);
}
if(errno != ErrorNumber.NoError || descrambled == null)
{
LastError = (int)errno;
Error = true;
return true;
}
Array.Copy(descrambled, 0, outBuf, i * sectorBytes, sectorBytes);
}
buffer = outBuf;
}
Error = LastError != 0;
AaruLogging.Debug(SCSI_MODULE_NAME, "OmniDrive READ NINTENDO DVD took {0} ms", duration);
return sense;
}
}