mirror of
https://github.com/GreemDev/Ryujinx
synced 2025-01-10 21:19:58 +01:00
Simplify The Logic
This commit is contained in:
parent
46f8b8fa85
commit
334e71528e
1 changed files with 103 additions and 120 deletions
|
@ -3,60 +3,59 @@ using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
|
||||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||||
using Ryujinx.HLE.HOS.Tamper;
|
using Ryujinx.HLE.HOS.Tamper;
|
||||||
using System;
|
using System;
|
||||||
using System.CodeDom;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.Intrinsics.Arm;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
{
|
{
|
||||||
public class AmiiboBinReader
|
public class AmiiboBinReader
|
||||||
{
|
{
|
||||||
// Method to calculate BCC (XOR checksum) from UID bytes
|
private static byte CalculateBCC0(byte[] uid)
|
||||||
private static byte CalculateBCC(byte[] uid, int startIdx)
|
|
||||||
{
|
{
|
||||||
return (byte)(uid[startIdx] ^ uid[startIdx + 1] ^ uid[startIdx + 2] ^ 0x88);
|
return (byte)(uid[0] ^ uid[1] ^ uid[2] ^ 0x88);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte CalculateBCC1(byte[] uid)
|
||||||
|
{
|
||||||
|
return (byte)(uid[3] ^ uid[4] ^ uid[5] ^ uid[6]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to read and process a .bin file
|
|
||||||
public static VirtualAmiiboFile ReadBinFile(byte[] fileBytes)
|
public static VirtualAmiiboFile ReadBinFile(byte[] fileBytes)
|
||||||
{
|
{
|
||||||
string keyRetailBinPath = GetKeyRetailBinPath();
|
string keyRetailBinPath = GetKeyRetailBinPath();
|
||||||
if (string.IsNullOrEmpty(keyRetailBinPath))
|
if (string.IsNullOrEmpty(keyRetailBinPath))
|
||||||
{
|
{
|
||||||
Console.WriteLine("Key retail bin path not found.");
|
|
||||||
return new VirtualAmiiboFile();
|
return new VirtualAmiiboFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] initialCounter = new byte[16];
|
byte[] initialCounter = new byte[16];
|
||||||
|
|
||||||
// Ensure the file is long enough
|
const int totalPages = 135;
|
||||||
if (fileBytes.Length < 128 * 4) // Each page is 4 bytes, total 512 bytes
|
const int pageSize = 4;
|
||||||
|
const int totalBytes = totalPages * pageSize;
|
||||||
|
|
||||||
|
if (fileBytes.Length < totalBytes)
|
||||||
{
|
{
|
||||||
Console.WriteLine("File is too short to process.");
|
|
||||||
return new VirtualAmiiboFile();
|
return new VirtualAmiiboFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the Amiibo data
|
|
||||||
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
|
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
|
||||||
byte[] decryptedFileBytes = amiiboDecryptor.DecryptAmiiboData(fileBytes, initialCounter);
|
byte[] decryptedFileBytes = amiiboDecryptor.DecryptAmiiboData(fileBytes, initialCounter);
|
||||||
|
|
||||||
// Assuming the UID is stored in the first 7 bytes (NTAG215 UID length)
|
if (decryptedFileBytes.Length != totalBytes)
|
||||||
|
{
|
||||||
|
Array.Resize(ref decryptedFileBytes, totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] uid = new byte[7];
|
byte[] uid = new byte[7];
|
||||||
Array.Copy(fileBytes, 0, uid, 0, 7);
|
Array.Copy(fileBytes, 0, uid, 0, 7);
|
||||||
|
|
||||||
// Calculate BCC values
|
byte bcc0 = CalculateBCC0(uid);
|
||||||
byte bcc0 = CalculateBCC(uid, 0); // BCC0 = UID0 ^ UID1 ^ UID2 ^ 0x88
|
byte bcc1 = CalculateBCC1(uid);
|
||||||
byte bcc1 = CalculateBCC(uid, 3); // BCC1 = UID3 ^ UID4 ^ UID5 ^ 0x00
|
|
||||||
|
|
||||||
Console.WriteLine($"UID: {BitConverter.ToString(uid)}");
|
LogDebugData(uid, bcc0, bcc1);
|
||||||
Console.WriteLine($"BCC0: 0x{bcc0:X2}, BCC1: 0x{bcc1:X2}");
|
|
||||||
|
|
||||||
// Initialize byte arrays for data extraction
|
byte[] nickNameBytes = new byte[20];
|
||||||
byte[] nickNameBytes = new byte[20]; // Amiibo nickname is 20 bytes
|
|
||||||
byte[] titleId = new byte[8];
|
byte[] titleId = new byte[8];
|
||||||
byte[] usedCharacter = new byte[2];
|
byte[] usedCharacter = new byte[2];
|
||||||
byte[] variation = new byte[2];
|
byte[] variation = new byte[2];
|
||||||
|
@ -65,177 +64,160 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
byte[] initDate = new byte[2];
|
byte[] initDate = new byte[2];
|
||||||
byte[] writeDate = new byte[2];
|
byte[] writeDate = new byte[2];
|
||||||
byte[] writeCounter = new byte[2];
|
byte[] writeCounter = new byte[2];
|
||||||
|
byte[] appId = new byte[8];
|
||||||
|
byte[] settingsBytes = new byte[2];
|
||||||
byte formData = 0;
|
byte formData = 0;
|
||||||
byte[] applicationAreas = new byte[216];
|
byte[] applicationAreas = new byte[216];
|
||||||
byte[] appid = new byte[2];
|
|
||||||
byte[] SettingsBytes = new byte[2];
|
|
||||||
//// apply to decrypt bytes self.data[304:308] = self._calculate_crc32(self.data[308:520]).to_bytes(4, "little")
|
|
||||||
//byte[] crc32Bytes = new byte[212];
|
|
||||||
//Array.Copy(decryptedFileBytes, 308, crc32Bytes, 0, 212);
|
|
||||||
//byte[] toApply = BitConverter.GetBytes(CalculateCRC32(crc32Bytes));
|
|
||||||
//Array.Reverse(crc32Bytes);
|
|
||||||
//Array.Copy(toApply, 0, decryptedFileBytes, 304, 4);
|
|
||||||
|
|
||||||
// Reading specific pages and parsing bytes
|
for (int page = 0; page < totalPages; page++)
|
||||||
for (int page = 0; page < 134; page++) // NTAG215 has 128 pages
|
|
||||||
{
|
{
|
||||||
int pageStartIdx = page * 4; // Each page is 4 bytes
|
int pageStartIdx = page * pageSize;
|
||||||
byte[] pageData = new byte[4];
|
byte[] pageData = new byte[4];
|
||||||
bool isEncrypted = IsPageEncrypted(page);
|
bool isEncrypted = IsPageEncrypted(page);
|
||||||
byte[] sourceBytes = isEncrypted ? decryptedFileBytes : fileBytes;
|
byte[] sourceBytes = isEncrypted ? decryptedFileBytes : fileBytes;
|
||||||
|
if (pageStartIdx + pageSize > sourceBytes.Length)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
|
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
|
||||||
// Special handling for specific pages
|
|
||||||
switch (page)
|
switch (page)
|
||||||
{
|
{
|
||||||
case 0: // Page 0 (UID + BCC0)
|
case 0:
|
||||||
Console.WriteLine("Page 0: UID and BCC0.");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // Page 2 (BCC1 + Internal Value)
|
case 2:
|
||||||
byte internalValue = pageData[1];
|
byte internalValue = pageData[1];
|
||||||
Console.WriteLine($"Page 2: BCC1 + Internal Value 0x{internalValue:X2} (Expected 0x48).");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
// byte 0 amd 1 are settings
|
Array.Copy(pageData, 0, settingsBytes, 0, 2);
|
||||||
Array.Copy(pageData, 0, SettingsBytes, 0, 2);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
// Bytes 0 and 1 are init date, bytes 2 and 3 are write date
|
|
||||||
Array.Copy(pageData, 0, initDate, 0, 2);
|
Array.Copy(pageData, 0, initDate, 0, 2);
|
||||||
Array.Copy(pageData, 2, writeDate, 0, 2);
|
Array.Copy(pageData, 2, writeDate, 0, 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8:
|
case >= 8 and <= 12:
|
||||||
case 9:
|
|
||||||
case 10:
|
|
||||||
case 11:
|
|
||||||
case 12:
|
|
||||||
// Extract nickname bytes
|
|
||||||
int nickNameOffset = (page - 8) * 4;
|
int nickNameOffset = (page - 8) * 4;
|
||||||
Array.Copy(pageData, 0, nickNameBytes, nickNameOffset, 4);
|
Array.Copy(pageData, 0, nickNameBytes, nickNameOffset, 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 21:
|
case 21:
|
||||||
// Bytes 0 and 1 are used character, bytes 2 and 3 are variation
|
|
||||||
Array.Copy(pageData, 0, usedCharacter, 0, 2);
|
Array.Copy(pageData, 0, usedCharacter, 0, 2);
|
||||||
Array.Copy(pageData, 2, variation, 0, 2);
|
Array.Copy(pageData, 2, variation, 0, 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 22:
|
case 22:
|
||||||
// Bytes 0 and 1 are amiibo ID, byte 2 is set ID, byte 3 is form data
|
|
||||||
Array.Copy(pageData, 0, amiiboID, 0, 2);
|
Array.Copy(pageData, 0, amiiboID, 0, 2);
|
||||||
setID[0] = pageData[2];
|
setID[0] = pageData[2];
|
||||||
formData = pageData[3];
|
formData = pageData[3];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 40:
|
||||||
|
case 41:
|
||||||
|
int appIdOffset = (page - 40) * 4;
|
||||||
|
Array.Copy(decryptedFileBytes, pageStartIdx, appId, appIdOffset, 4);
|
||||||
|
break;
|
||||||
|
|
||||||
case 64:
|
case 64:
|
||||||
case 65:
|
case 65:
|
||||||
// Extract title ID
|
|
||||||
int titleIdOffset = (page - 64) * 4;
|
int titleIdOffset = (page - 64) * 4;
|
||||||
Array.Copy(pageData, 0, titleId, titleIdOffset, 4);
|
Array.Copy(sourceBytes, pageStartIdx, titleId, titleIdOffset, 4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 66:
|
case 66:
|
||||||
// Bytes 0 and 1 are write counter
|
|
||||||
Array.Copy(pageData, 0, writeCounter, 0, 2);
|
Array.Copy(pageData, 0, writeCounter, 0, 2);
|
||||||
// bytes 2 and 3 are appid
|
|
||||||
Array.Copy(pageData, 2, appid, 0, 2);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Pages 76 to 127 are application areas
|
|
||||||
case >= 76 and <= 129:
|
case >= 76 and <= 129:
|
||||||
int appAreaOffset = (page - 76) * 4;
|
int appAreaOffset = (page - 76) * 4;
|
||||||
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
|
if (appAreaOffset + 4 <= applicationAreas.Length)
|
||||||
|
{
|
||||||
|
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugging
|
|
||||||
uint titleIdStr = BitConverter.ToUInt32(titleId, 0);
|
|
||||||
string usedCharacterStr = BitConverter.ToString(usedCharacter).Replace("-", "");
|
string usedCharacterStr = BitConverter.ToString(usedCharacter).Replace("-", "");
|
||||||
string variationStr = BitConverter.ToString(variation).Replace("-", "");
|
string variationStr = BitConverter.ToString(variation).Replace("-", "");
|
||||||
string amiiboIDStr = BitConverter.ToString(amiiboID).Replace("-", "");
|
string amiiboIDStr = BitConverter.ToString(amiiboID).Replace("-", "");
|
||||||
string formDataStr = formData.ToString("X2");
|
|
||||||
string setIDStr = BitConverter.ToString(setID).Replace("-", "");
|
string setIDStr = BitConverter.ToString(setID).Replace("-", "");
|
||||||
uint settingsStr = BitConverter.ToUInt16(SettingsBytes, 0);
|
|
||||||
|
|
||||||
string nickName = Encoding.BigEndianUnicode.GetString(nickNameBytes).TrimEnd('\0');
|
|
||||||
string head = usedCharacterStr + variationStr;
|
string head = usedCharacterStr + variationStr;
|
||||||
string tail = amiiboIDStr + setIDStr + "02";
|
string tail = amiiboIDStr + setIDStr + "02";
|
||||||
string finalID = head + tail;
|
string finalID = head + tail;
|
||||||
string initDateStr = BitConverter.ToString(initDate).Replace("-", "");
|
|
||||||
string writeDateStr = BitConverter.ToString(writeDate).Replace("-", "");
|
|
||||||
|
|
||||||
Console.WriteLine($"Title ID: {titleIdStr}");
|
ushort settingsValue = BitConverter.ToUInt16(settingsBytes, 0);
|
||||||
Console.WriteLine($"Head: {head}");
|
string nickName = Encoding.BigEndianUnicode.GetString(nickNameBytes).TrimEnd('\0');
|
||||||
Console.WriteLine($"Tail: {tail}");
|
ushort initDateValue = BitConverter.ToUInt16(initDate, 0);
|
||||||
Console.WriteLine($"Used Character: {usedCharacterStr}");
|
ushort writeDateValue = BitConverter.ToUInt16(writeDate, 0);
|
||||||
Console.WriteLine($"Form Data: {formDataStr}");
|
DateTime initDateTime = DateTimeFromTag(initDateValue);
|
||||||
Console.WriteLine($"Variation: {variationStr}");
|
DateTime writeDateTime = DateTimeFromTag(writeDateValue);
|
||||||
Console.WriteLine($"Amiibo ID: {amiiboIDStr}");
|
ushort writeCounterValue = BitConverter.ToUInt16(writeCounter, 0);
|
||||||
Console.WriteLine($"Set ID: {setIDStr}");
|
|
||||||
Console.WriteLine($"Final ID: {finalID}");
|
LogFinalData(titleId, appId, head, tail, finalID, nickName, initDateTime, writeDateTime, settingsValue, writeCounterValue, applicationAreas);
|
||||||
Console.WriteLine($"Nickname: {nickName}");
|
|
||||||
Console.WriteLine($"Init Date: {initDateStr}");
|
|
||||||
Console.WriteLine($"Write Date: {writeDateStr}");
|
|
||||||
Console.WriteLine($"Settings: {settingsStr}");
|
|
||||||
Console.WriteLine("Length of Application Areas: " + applicationAreas.Length);
|
|
||||||
|
|
||||||
VirtualAmiiboFile virtualAmiiboFile = new VirtualAmiiboFile
|
VirtualAmiiboFile virtualAmiiboFile = new VirtualAmiiboFile
|
||||||
{
|
{
|
||||||
FileVersion = 1,
|
FileVersion = 1,
|
||||||
TagUuid = uid,
|
TagUuid = uid,
|
||||||
AmiiboId = finalID
|
AmiiboId = finalID,
|
||||||
|
FirstWriteDate = initDateTime,
|
||||||
|
LastWriteDate = writeDateTime,
|
||||||
|
WriteCounter = writeCounterValue,
|
||||||
};
|
};
|
||||||
ushort initDateValue = BitConverter.ToUInt16(initDate, 0);
|
|
||||||
ushort writeDateValue = BitConverter.ToUInt16(writeDate, 0);
|
|
||||||
DateTime initDateTime = DateTimeFromTag(initDateValue);
|
|
||||||
DateTime writeDateTime = DateTimeFromTag(writeDateValue);
|
|
||||||
|
|
||||||
Console.WriteLine($"Parsed Init Date: {initDateTime}");
|
|
||||||
Console.WriteLine($"Parsed Write Date: {writeDateTime}");
|
|
||||||
|
|
||||||
virtualAmiiboFile.FirstWriteDate = initDateTime;
|
|
||||||
virtualAmiiboFile.LastWriteDate = writeDateTime;
|
|
||||||
virtualAmiiboFile.WriteCounter = BitConverter.ToUInt16(writeCounter, 0);
|
|
||||||
VirtualAmiibo.SaveAmiiboFile(virtualAmiiboFile);
|
|
||||||
VirtualAmiibo.applicationBytes = applicationAreas;
|
VirtualAmiibo.applicationBytes = applicationAreas;
|
||||||
|
|
||||||
return virtualAmiiboFile;
|
return virtualAmiiboFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static uint CalculateCRC32(byte[] input)
|
private static void LogDebugData(byte[] uid, byte bcc0, byte bcc1)
|
||||||
{
|
{
|
||||||
// Setup CRC 32 table
|
Console.WriteLine($"UID: {BitConverter.ToString(uid)}");
|
||||||
uint p0 = 0xEDB88320u | 0x80000000u;
|
Console.WriteLine($"BCC0: 0x{bcc0:X2}, BCC1: 0x{bcc1:X2}");
|
||||||
uint[] u0 = new uint[0x100];
|
}
|
||||||
|
|
||||||
for (uint i = 1; i < 0x100; i++)
|
private static void LogFinalData(byte[] titleId, byte[] appId, string head, string tail, string finalID, string nickName, DateTime initDateTime, DateTime writeDateTime, ushort settingsValue, ushort writeCounterValue, byte[] applicationAreas)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Title ID: 0x{BitConverter.ToString(titleId).Replace("-", "")}");
|
||||||
|
Console.WriteLine($"Application Program ID: 0x{BitConverter.ToString(appId).Replace("-", "")}");
|
||||||
|
Console.WriteLine($"Head: {head}");
|
||||||
|
Console.WriteLine($"Tail: {tail}");
|
||||||
|
Console.WriteLine($"Final ID: {finalID}");
|
||||||
|
Console.WriteLine($"Nickname: {nickName}");
|
||||||
|
Console.WriteLine($"Init Date: {initDateTime}");
|
||||||
|
Console.WriteLine($"Write Date: {writeDateTime}");
|
||||||
|
Console.WriteLine($"Settings: 0x{settingsValue:X4}");
|
||||||
|
Console.WriteLine($"Write Counter: {writeCounterValue}");
|
||||||
|
Console.WriteLine("Length of Application Areas: " + applicationAreas.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint CalculateCRC32(byte[] input)
|
||||||
|
{
|
||||||
|
uint[] table = new uint[256];
|
||||||
|
uint polynomial = 0xEDB88320;
|
||||||
|
for (uint i = 0; i < table.Length; ++i)
|
||||||
{
|
{
|
||||||
uint t0 = i;
|
uint crc = i;
|
||||||
for (int j = 0; j < 8; j++)
|
for (int j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
if ((t0 & 1) != 0)
|
if ((crc & 1) != 0)
|
||||||
{
|
crc = (crc >> 1) ^ polynomial;
|
||||||
t0 = (t0 >> 1) ^ p0;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
crc >>= 1;
|
||||||
t0 >>= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
u0[i] = t0;
|
table[i] = crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate CRC32 from table
|
uint result = 0xFFFFFFFF;
|
||||||
uint t = 0x0;
|
foreach (byte b in input)
|
||||||
foreach (byte k in input)
|
|
||||||
{
|
{
|
||||||
t = (t >> 8) ^ u0[(k ^ t) & 0xFF];
|
byte index = (byte)((result & 0xFF) ^ b);
|
||||||
|
result = (result >> 8) ^ table[index];
|
||||||
}
|
}
|
||||||
|
return ~result;
|
||||||
return t ^ 0xFFFFFFFFu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetKeyRetailBinPath()
|
private static string GetKeyRetailBinPath()
|
||||||
|
@ -245,9 +227,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
|
|
||||||
public static bool IsPageEncrypted(int page)
|
public static bool IsPageEncrypted(int page)
|
||||||
{
|
{
|
||||||
// 0-4 are not encrypted, 5-12 is encrypted, 13-39 is not encrypted,
|
|
||||||
// 40-129 is encrypted, and 130-134 is not encrypted.
|
|
||||||
|
|
||||||
return (page >= 5 && page <= 12) || (page >= 40 && page <= 129);
|
return (page >= 5 && page <= 12) || (page >= 40 && page <= 129);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,10 +234,14 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var day = value & 0x1F;
|
int day = value & 0x1F;
|
||||||
var month = (value >> 5) & 0x0F;
|
int month = (value >> 5) & 0x0F;
|
||||||
var year = (value >> 9) & 0x7F;
|
int year = (value >> 9) & 0x7F;
|
||||||
return new DateTime(1970 + year, month, day);
|
|
||||||
|
if (day == 0 || month == 0 || month > 12 || day > DateTime.DaysInMonth(2000 + year, month))
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
return new DateTime(2000 + year, month, day);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue