Add OmniDrive READ CD command.

This commit is contained in:
2026-07-04 20:14:28 +01:00
parent d352f34004
commit 5c2006d778

View File

@@ -4,6 +4,7 @@
//
// Filename : OmniDrive.cs
// Author(s) : Rebecca Wallander <sakcheen@gmail.com>
// Natalia Portillo <clania@claunia.com>
//
// Component : OmniDrive firmware vendor commands.
//
@@ -30,31 +31,27 @@
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2026 Rebecca Wallander
// Copyright © 2011-2026 Natalia Portillo
// ****************************************************************************/
using System;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Structs.Devices.SCSI;
using Aaru.Logging;
using Aaru.Decoders.DVD;
using Aaru.Logging;
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
}
readonly Sector _dvdSectorDecoder = new();
readonly NintendoSector _nintendoSectorDecoder = new();
/// <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.
/// (<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)
@@ -64,22 +61,22 @@ public partial class Device
int f = fua ? 1 : 0;
int s = descramble ? 1 : 0;
return (byte)(d | (r << 2) | (f << 3) | (s << 4));
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[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
}
@@ -97,21 +94,21 @@ public partial class Device
bool sense = ScsiInquiry(out byte[] buffer, out _, Timeout, out _);
if (sense || buffer == null) return false;
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;
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;
if(reserved5.Length < omnidrive.Length) return false;
for (int i = 0; i < omnidrive.Length; i++)
if (reserved5[i] != omnidrive[i])
return false;
for(var i = 0; i < omnidrive.Length; i++)
{
if(reserved5[i] != omnidrive[i]) return false;
}
major = reserved5[9];
minor = reserved5[10];
@@ -130,8 +127,9 @@ public partial class Device
/// <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)
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];
@@ -144,7 +142,7 @@ public partial class Device
LastError = SendScsiCommand(cdb, ref buffer, timeout, ScsiDirection.In, out duration, out bool sense);
if (!Sector.CheckIed(buffer, transferLength)) return true;
if(!Sector.CheckIed(buffer, transferLength)) return true;
if(descramble && !Sector.CheckEdc(buffer, transferLength)) return true;
@@ -170,10 +168,10 @@ public partial class Device
/// 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)
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];
@@ -190,13 +188,13 @@ public partial class Device
if(descramble)
{
const int sectorBytes = 2064;
byte[] outBuf = new byte[sectorBytes * transferLength];
const int sectorBytes = 2064;
var outBuf = new byte[sectorBytes * transferLength];
const uint maxRegularLba = 0x00FFFFFF;
for(uint i = 0; i < transferLength; i++)
{
byte[] slice = new byte[sectorBytes];
var slice = new byte[sectorBytes];
Array.Copy(buffer, i * sectorBytes, slice, 0, sectorBytes);
uint absLba = lba + i;
@@ -210,7 +208,7 @@ public partial class Device
errno = _dvdSectorDecoder.Scramble(slice, out descrambled);
else
{
byte key = absLba < 16 ? (byte)0 : (derivedDiscKey ?? (byte)0);
byte key = absLba < 16 ? (byte)0 : derivedDiscKey ?? 0;
errno = _nintendoSectorDecoder.Scramble(slice, key, out descrambled);
}
@@ -234,4 +232,41 @@ public partial class Device
return sense;
}
}
public bool OmniDriveReadCd(out byte[] buffer, out ReadOnlySpan<byte> senseBuffer, uint lba, uint transferLength,
uint timeout, out double duration, bool fua = false, bool rawAddressing = false)
{
senseBuffer = SenseBuffer;
Span<byte> cdb = CdbBuffer[..12];
buffer = new byte[2448 * transferLength];
cdb.Clear();
cdb[0] = (byte)ScsiCommands.ReadOmniDrive;
cdb[1] = EncodeOmniDriveReadCdb1(OmniDriveDiscType.CD, rawAddressing, fua, false);
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] = 1; // subchannels=RW
cdb[11] = 0; // control
LastError = SendScsiCommand(cdb, ref buffer, timeout, ScsiDirection.In, out duration, out bool sense);
Error = LastError != 0;
AaruLogging.Debug(SCSI_MODULE_NAME, "OmniDrive READ CD took {0} ms", duration);
return sense;
}
enum OmniDriveDiscType
{
CD = 0,
DVD = 1,
BD = 2
}
}