Add support for loading in backup TAD files
Kind of silly usecase, but it's not too bad to implement anyways
This commit is contained in:
parent
c2b1e0110b
commit
4424a7103c
|
@ -1,10 +1,12 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -81,6 +83,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
return new(x, y);
|
return new(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly BigInteger GENERATOR_CONSTANT = BigInteger.Parse("0FFFEFB4E295902582A680F5F1A4F3E79", NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||||
|
private static readonly BigInteger U128_MAX = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
[CoreConstructor(VSystemID.Raw.NDS)]
|
[CoreConstructor(VSystemID.Raw.NDS)]
|
||||||
public NDS(CoreLoadParameters<NDSSettings, NDSSyncSettings> lp)
|
public NDS(CoreLoadParameters<NDSSettings, NDSSyncSettings> lp)
|
||||||
: base(lp.Comm, new()
|
: base(lp.Comm, new()
|
||||||
|
@ -106,7 +111,190 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
var roms = lp.Roms.Select(r => r.FileData).ToList();
|
var roms = lp.Roms.Select(r => r.FileData).ToList();
|
||||||
|
|
||||||
DSiTitleId = GetDSiTitleId(roms[0]);
|
// make sure we have a valid header before doing any parsing
|
||||||
|
if (roms[0].Length < 0x1000)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ROM is too small to be a valid NDS ROM!");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> romHeader = roms[0].AsSpan(0, 0x1000);
|
||||||
|
ReadOnlySpan<byte> dsiWare = [ ], bios7i = [ ];
|
||||||
|
if (!IsRomValid(roms[0]))
|
||||||
|
{
|
||||||
|
// if the ROM isn't valid, this could potentially be a backup TAD the user is attempting to load
|
||||||
|
// backup TADs are DSiWare exported to the SD Card
|
||||||
|
// https://problemkaputt.de/gbatek.htm#dsisdmmcdsiwarefilesonexternalsdcardbinakatadfiles
|
||||||
|
bios7i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i"));
|
||||||
|
var fixTadKeyX = bios7i.Slice(0xB588, 0x10);
|
||||||
|
var fixTadKeyY = bios7i.Slice(0x8328, 0x10);
|
||||||
|
var varTadKeyY = bios7i.Slice(0x8318, 0x10);
|
||||||
|
|
||||||
|
// CCM is used to encrypt backup TAD files
|
||||||
|
// For purposes of decryption, this is just CTR really, as we can more or less ignore authentication
|
||||||
|
// CTR isn't implemented by C# AES of course... so have to reimplement it
|
||||||
|
// Note: Not a copy paste of N3DSHasher! DSi is weird and transforms each block in little endian rather than big endian
|
||||||
|
static void AesCtrTransform(Aes aes, byte[] iv, Span<byte> inputOutput)
|
||||||
|
{
|
||||||
|
// ECB encryptor is used for both CTR encryption and decryption
|
||||||
|
using var encryptor = aes.CreateEncryptor();
|
||||||
|
var blockSize = aes.BlockSize / 8;
|
||||||
|
var outputBlockBuffer = new byte[blockSize];
|
||||||
|
|
||||||
|
// mostly copied from tiny-AES-c (public domain)
|
||||||
|
for (int i = 0, bi = blockSize; i < inputOutput.Length; ++i, ++bi)
|
||||||
|
{
|
||||||
|
if (bi == blockSize)
|
||||||
|
{
|
||||||
|
encryptor.TransformBlock(iv, 0, iv.Length, outputBlockBuffer, 0);
|
||||||
|
for (bi = blockSize - 1; bi >= 0; --bi)
|
||||||
|
{
|
||||||
|
if (iv[bi] == 0xFF)
|
||||||
|
{
|
||||||
|
iv[bi] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
++iv[bi];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputOutput[i] ^= outputBlockBuffer[blockSize - 1 - bi];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] DeriveNormalKey(ReadOnlySpan<byte> keyX, ReadOnlySpan<byte> keyY)
|
||||||
|
{
|
||||||
|
static BigInteger LeftRot128(BigInteger v, int rot)
|
||||||
|
{
|
||||||
|
var l = (v << rot) & U128_MAX;
|
||||||
|
var r = v >> (128 - rot);
|
||||||
|
return l | r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BigInteger Add128(BigInteger v1, BigInteger v2)
|
||||||
|
=> (v1 + v2) & U128_MAX;
|
||||||
|
|
||||||
|
var keyBytes = new byte[17];
|
||||||
|
keyX.CopyTo(keyBytes);
|
||||||
|
var keyXBigInteger = new BigInteger(keyBytes);
|
||||||
|
|
||||||
|
keyY.CopyTo(keyBytes);
|
||||||
|
var keyYBigInteger = new BigInteger(keyBytes);
|
||||||
|
|
||||||
|
var normalKey = LeftRot128(Add128(keyXBigInteger ^ keyYBigInteger, GENERATOR_CONSTANT), 42);
|
||||||
|
var normalKeyBytes = normalKey.ToByteArray();
|
||||||
|
if (normalKeyBytes.Length > 17)
|
||||||
|
{
|
||||||
|
// this shoudn't ever happen
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Resize(ref normalKeyBytes, 16);
|
||||||
|
Array.Reverse(normalKeyBytes);
|
||||||
|
return normalKeyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitIv(Span<byte> iv, ReadOnlySpan<byte> nonce)
|
||||||
|
{
|
||||||
|
iv[0] = 0x02;
|
||||||
|
// ES block nonce
|
||||||
|
for (var i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
iv[1 + i] = nonce[11 - i];
|
||||||
|
}
|
||||||
|
|
||||||
|
iv[13] = 0x00;
|
||||||
|
iv[14] = 0x00;
|
||||||
|
iv[15] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var aes = Aes.Create();
|
||||||
|
aes.Mode = CipherMode.ECB;
|
||||||
|
aes.Padding = PaddingMode.None;
|
||||||
|
aes.BlockSize = 128;
|
||||||
|
aes.KeySize = 128;
|
||||||
|
var iv = new byte[16];
|
||||||
|
|
||||||
|
// first decrypt the header (mainly to verify this is in fact a backup TAD)
|
||||||
|
aes.Key = DeriveNormalKey(fixTadKeyX, fixTadKeyY);
|
||||||
|
InitIv(iv, roms[0].AsSpan(0x4020 + 0xB4 + 0x11, 12));
|
||||||
|
AesCtrTransform(aes, iv, roms[0].AsSpan(0x4020, 0x30));
|
||||||
|
|
||||||
|
if (!roms[0].AsSpan(0x4020, 4).SequenceEqual("4ANT"u8))
|
||||||
|
{
|
||||||
|
// not a backup TAD, this is a garbage NDS anyways
|
||||||
|
throw new InvalidOperationException("Invalid ROM");
|
||||||
|
}
|
||||||
|
|
||||||
|
// these include the ES block footer
|
||||||
|
var tmdSize = BinaryPrimitives.ReadUInt32LittleEndian(roms[0].AsSpan(0x4020 + 0x28, 4));
|
||||||
|
var appSize = BinaryPrimitives.ReadUInt32LittleEndian(roms[0].AsSpan(0x4020 + 0x2C, 4));
|
||||||
|
|
||||||
|
if (appSize % 0x20020 < 0x20)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid ROM");
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrypt cert area now (to fetch the console unique id, which Nintendo mistakenly includes in here)
|
||||||
|
InitIv(iv, roms[0].AsSpan(0x40F4 + 0x440 + 0x11, 12));
|
||||||
|
AesCtrTransform(aes, iv, roms[0].AsSpan(0x40F4, 0x440));
|
||||||
|
|
||||||
|
var consoleIdStr = Encoding.ASCII.GetString(roms[0].AsSpan(0x40F4 + 0x38F, 16));
|
||||||
|
if (!ulong.TryParse(consoleIdStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var consoleId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to parse console ID in TAD TWL cert!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> varTadKeyX = stackalloc byte[16];
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(varTadKeyX, 0x4E00004A);
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(varTadKeyX[4..], 0x4A00004E);
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(varTadKeyX[8..], (uint)(consoleId >> 32) ^ 0xC80C4B72);
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(varTadKeyX[12..], (uint)(consoleId & 0xFFFFFFFF));
|
||||||
|
|
||||||
|
// decrypt app area now
|
||||||
|
aes.Key = DeriveNormalKey(varTadKeyX, varTadKeyY);
|
||||||
|
var appStart = (int)(0x4554 + tmdSize);
|
||||||
|
var appEnd = appStart + (int)appSize;
|
||||||
|
var appOffset = 0;
|
||||||
|
var appDataOffset = 0;
|
||||||
|
while (appStart + appOffset < appEnd)
|
||||||
|
{
|
||||||
|
var esBlockFooterOffset = (int)Math.Min(0x20020, appSize - appOffset) - 0x20;
|
||||||
|
InitIv(iv, roms[0].AsSpan(appStart + appOffset + esBlockFooterOffset + 0x11, 12));
|
||||||
|
AesCtrTransform(aes, iv, roms[0].AsSpan(appStart + appOffset, esBlockFooterOffset));
|
||||||
|
appOffset += esBlockFooterOffset + 0x20;
|
||||||
|
appDataOffset += esBlockFooterOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var appData = new byte[appDataOffset];
|
||||||
|
appOffset = 0;
|
||||||
|
appDataOffset = 0;
|
||||||
|
while (appStart + appOffset < appEnd)
|
||||||
|
{
|
||||||
|
var esBlockFooterOffset = (int)Math.Min(0x20020, appSize - appOffset) - 0x20;
|
||||||
|
roms[0].AsSpan(appStart + appOffset, esBlockFooterOffset).CopyTo(appData.AsSpan(appDataOffset));
|
||||||
|
appOffset += esBlockFooterOffset + 0x20;
|
||||||
|
appDataOffset += esBlockFooterOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
dsiWare = appData;
|
||||||
|
if (dsiWare.Length < 0x1000 || !IsRomValid(dsiWare))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid ROM in TAD");
|
||||||
|
}
|
||||||
|
|
||||||
|
romHeader = dsiWare[..0x1000];
|
||||||
|
DSiTitleId = GetDSiTitleId(romHeader);
|
||||||
|
if (!IsDSiWare)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Backup TAD did not have DSiWare");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DSiTitleId = GetDSiTitleId(romHeader);
|
||||||
IsDSi |= IsDSiWare;
|
IsDSi |= IsDSiWare;
|
||||||
|
|
||||||
if (roms.Count > (IsDSi ? 1 : 2))
|
if (roms.Count > (IsDSi ? 1 : 2))
|
||||||
|
@ -203,17 +391,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
if (!_activeSyncSettings.UseRealBIOS)
|
if (!_activeSyncSettings.UseRealBIOS)
|
||||||
{
|
{
|
||||||
var arm9RomOffset = BinaryPrimitives.ReadInt32LittleEndian(roms[0].AsSpan(0x20, 4));
|
var arm9RomOffset = BinaryPrimitives.ReadInt32LittleEndian(romHeader.Slice(0x20, 4));
|
||||||
if (arm9RomOffset is >= 0x4000 and < 0x8000)
|
if (arm9RomOffset is >= 0x4000 and < 0x8000)
|
||||||
{
|
{
|
||||||
// check if the user is using an encrypted rom
|
// check if the user is using an encrypted rom
|
||||||
// if they are, they need to be using real bios files
|
// if they are, they need to be using real bios files
|
||||||
var secureAreaId = BinaryPrimitives.ReadUInt64LittleEndian(roms[0].AsSpan(arm9RomOffset, 8));
|
var secureAreaId = BinaryPrimitives.ReadUInt64LittleEndian(romHeader.Slice(arm9RomOffset, 8));
|
||||||
_activeSyncSettings.UseRealBIOS = secureAreaId != 0xE7FFDEFF_E7FFDEFF;
|
_activeSyncSettings.UseRealBIOS = secureAreaId != 0xE7FFDEFF_E7FFDEFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bios9 = null, bios7 = null, firmware = null;
|
ReadOnlySpan<byte> bios9 = [ ], bios7 = [ ], firmware = [ ];
|
||||||
if (_activeSyncSettings.UseRealBIOS)
|
if (_activeSyncSettings.UseRealBIOS)
|
||||||
{
|
{
|
||||||
bios9 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"));
|
bios9 = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9"));
|
||||||
|
@ -228,19 +416,19 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
NDSFirmware.MaybeWarnIfBadFw(firmware, CoreComm.ShowMessage);
|
NDSFirmware.MaybeWarnIfBadFw(firmware, CoreComm.ShowMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bios9i = null, bios7i = null, nand = null;
|
ReadOnlySpan<byte> bios9i = [ ], nand = [ ];
|
||||||
if (IsDSi)
|
if (IsDSi)
|
||||||
{
|
{
|
||||||
bios9i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i"));
|
bios9i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios9i"));
|
||||||
bios7i = CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i"));
|
bios7i = bios7i.IsEmpty ? CoreComm.CoreFileProvider.GetFirmwareOrThrow(new("NDS", "bios7i")) : bios7i;
|
||||||
nand = DecideNAND(CoreComm.CoreFileProvider, (DSiTitleId.Upper & ~0xFF) == 0x00030000, roms[0][0x1B0]);
|
nand = DecideNAND(CoreComm.CoreFileProvider, (DSiTitleId.Upper & ~0xFF) == 0x00030000, romHeader[0x1B0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] ndsRom = null, gbaRom = null, dsiWare = null, tmd = null;
|
ReadOnlySpan<byte> ndsRom = [ ], gbaRom = [ ], tmd = [ ];
|
||||||
if (IsDSiWare)
|
if (IsDSiWare)
|
||||||
{
|
{
|
||||||
tmd = GetTMDData(DSiTitleId.Full);
|
tmd = GetTMDData(DSiTitleId.Full);
|
||||||
dsiWare = roms[0];
|
dsiWare = dsiWare.IsEmpty ? roms[0] : dsiWare;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -264,16 +452,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
|
|
||||||
LibMelonDS.ConsoleCreationArgs consoleCreationArgs;
|
LibMelonDS.ConsoleCreationArgs consoleCreationArgs;
|
||||||
|
|
||||||
consoleCreationArgs.NdsRomLength = ndsRom?.Length ?? 0;
|
consoleCreationArgs.NdsRomLength = ndsRom.Length;
|
||||||
consoleCreationArgs.GbaRomLength = gbaRom?.Length ?? 0;
|
consoleCreationArgs.GbaRomLength = gbaRom.Length;
|
||||||
consoleCreationArgs.Arm9BiosLength = bios9?.Length ?? 0;
|
consoleCreationArgs.Arm9BiosLength = bios9.Length;
|
||||||
consoleCreationArgs.Arm7BiosLength = bios7?.Length ?? 0;
|
consoleCreationArgs.Arm7BiosLength = bios7.Length;
|
||||||
consoleCreationArgs.FirmwareLength = firmware?.Length ?? 0;
|
consoleCreationArgs.FirmwareLength = firmware.Length;
|
||||||
consoleCreationArgs.Arm9iBiosLength = bios9i?.Length ?? 0;
|
consoleCreationArgs.Arm9iBiosLength = bios9i.Length;
|
||||||
consoleCreationArgs.Arm7iBiosLength = bios7i?.Length ?? 0;
|
consoleCreationArgs.Arm7iBiosLength = bios7i.Length;
|
||||||
consoleCreationArgs.NandLength = nand?.Length ?? 0;
|
consoleCreationArgs.NandLength = nand.Length;
|
||||||
consoleCreationArgs.DsiWareLength = dsiWare?.Length ?? 0;
|
consoleCreationArgs.DsiWareLength = dsiWare.Length;
|
||||||
consoleCreationArgs.TmdLength = tmd?.Length ?? 0;
|
consoleCreationArgs.TmdLength = tmd.Length;
|
||||||
|
|
||||||
consoleCreationArgs.DSi = IsDSi;
|
consoleCreationArgs.DSi = IsDSi;
|
||||||
consoleCreationArgs.ClearNAND = _activeSyncSettings.ClearNAND || lp.DeterministicEmulationRequested;
|
consoleCreationArgs.ClearNAND = _activeSyncSettings.ClearNAND || lp.DeterministicEmulationRequested;
|
||||||
|
@ -377,14 +565,47 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (ulong Full, uint Upper, uint Lower) GetDSiTitleId(ReadOnlySpan<byte> file)
|
private static bool IsRomValid(ReadOnlySpan<byte> rom)
|
||||||
{
|
{
|
||||||
ulong titleId = 0;
|
// check ARM7/ARM9 (and maybe ARM7i/ARM9i) binary offsets/sizes to see if they're sane
|
||||||
for (var i = 0; i < 8; i++)
|
// if these are wildly wrong, the ROM may crash the core
|
||||||
|
var unitCode = rom[0x12];
|
||||||
|
var isDsiExclusive = (unitCode & 0x03) == 3;
|
||||||
|
var arm9RomOffset = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x20, 4));
|
||||||
|
var arm9Size = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x2C, 4));
|
||||||
|
var arm7RomOffset = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x30, 4));
|
||||||
|
var arm7Size = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x3C, 4));
|
||||||
|
if (arm9RomOffset > rom.Length ||
|
||||||
|
(arm9Size > 0x3BFE00 && !isDsiExclusive) ||
|
||||||
|
arm9RomOffset + arm9Size > rom.Length ||
|
||||||
|
arm7RomOffset > rom.Length ||
|
||||||
|
(arm7Size > 0x3BFE00 && !isDsiExclusive) ||
|
||||||
|
arm7RomOffset + arm7Size > rom.Length)
|
||||||
{
|
{
|
||||||
titleId <<= 8;
|
return false;
|
||||||
titleId |= file[0x237 - i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((unitCode & 0x02) != 0)
|
||||||
|
{
|
||||||
|
var arm9iRomOffset = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x1C0, 4));
|
||||||
|
var arm9iSize = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x1CC, 4));
|
||||||
|
var arm7iRomOffset = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x1D0, 4));
|
||||||
|
var arm7iSize = BinaryPrimitives.ReadUInt32LittleEndian(rom.Slice(0x1DC, 4));
|
||||||
|
if (arm9iRomOffset > rom.Length ||
|
||||||
|
arm9iRomOffset + arm9iSize > rom.Length ||
|
||||||
|
arm7iRomOffset > rom.Length ||
|
||||||
|
arm7iRomOffset + arm7iSize > rom.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (ulong Full, uint Upper, uint Lower) GetDSiTitleId(ReadOnlySpan<byte> romHeader)
|
||||||
|
{
|
||||||
|
var titleId = BinaryPrimitives.ReadUInt64LittleEndian(romHeader.Slice(0x230, 8));
|
||||||
return (titleId, (uint)(titleId >> 32), (uint)(titleId & 0xFFFFFFFFU));
|
return (titleId, (uint)(titleId >> 32), (uint)(titleId & 0xFFFFFFFFU));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
// TODO: a lot of this has been removed as it's not really needed anymore (our c++ code forces correctness everywhere)
|
// TODO: a lot of this has been removed as it's not really needed anymore (our c++ code forces correctness everywhere)
|
||||||
internal static class NDSFirmware
|
internal static class NDSFirmware
|
||||||
{
|
{
|
||||||
public static void MaybeWarnIfBadFw(byte[] fw, Action<string> warningCallback)
|
public static void MaybeWarnIfBadFw(ReadOnlySpan<byte> fw, Action<string> warningCallback)
|
||||||
{
|
{
|
||||||
if (fw[0x17C] != 0xFF)
|
if (fw[0x17C] != 0xFF)
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern bool GetDecryptedFirmware(byte[] fw, int fwlen, out IntPtr decryptedFw, out int decryptedlen);
|
[return: MarshalAs(UnmanagedType.U1)]
|
||||||
|
private static extern bool GetDecryptedFirmware(IntPtr fw, int fwLen, out IntPtr decryptedFw, out int decryptedLen);
|
||||||
|
|
||||||
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libfwunpack", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void FreeDecryptedFirmware(IntPtr decryptedFw);
|
private static extern void FreeDecryptedFirmware(IntPtr decryptedFw);
|
||||||
|
@ -46,21 +47,30 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NDS
|
||||||
"BFBC33D996AA73A050F1951529327D5844461A00", // MACi DS Lite (Korean v5, 2006-11-09)
|
"BFBC33D996AA73A050F1951529327D5844461A00", // MACi DS Lite (Korean v5, 2006-11-09)
|
||||||
};
|
};
|
||||||
|
|
||||||
private static void CheckDecryptedCodeChecksum(byte[] fw, Action<string> warningCallback)
|
private static unsafe void CheckDecryptedCodeChecksum(ReadOnlySpan<byte> fw, Action<string> warningCallback)
|
||||||
{
|
{
|
||||||
if (!GetDecryptedFirmware(fw, fw.Length, out var decryptedfw, out var decrypedfwlen))
|
IntPtr decryptedFw;
|
||||||
|
int decrypedFwLen;
|
||||||
|
fixed (byte* fwPtr = fw)
|
||||||
{
|
{
|
||||||
warningCallback("Firmware could not be decryped for verification! This firmware might be not work!");
|
if (!GetDecryptedFirmware((IntPtr)fwPtr, fw.Length, out decryptedFw, out decrypedFwLen))
|
||||||
return;
|
{
|
||||||
|
warningCallback("Firmware could not be decryped for verification! This firmware might be not work!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var decryptedFirmware = new byte[decrypedfwlen];
|
try
|
||||||
Marshal.Copy(decryptedfw, decryptedFirmware, 0, decrypedfwlen);
|
|
||||||
FreeDecryptedFirmware(decryptedfw);
|
|
||||||
var hash = SHA1Checksum.ComputeDigestHex(decryptedFirmware);
|
|
||||||
if (!_goodHashes.Contains(hash))
|
|
||||||
{
|
{
|
||||||
warningCallback("Potentially bad firmware dump! Decrypted hash " + hash + " does not match known good dumps.");
|
var hash = SHA1Checksum.ComputeDigestHex(Util.UnsafeSpanFromPointer(decryptedFw, decrypedFwLen));
|
||||||
|
if (!_goodHashes.Contains(hash))
|
||||||
|
{
|
||||||
|
warningCallback($"Potentially bad firmware dump! Decrypted hash {hash} does not match known good dumps.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
FreeDecryptedFirmware(decryptedFw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue