mirror of
https://github.com/GreemDev/Ryujinx
synced 2025-02-05 03:58:56 +01:00
Some small fixes
The data now loads but SMBU says it can't be loaded The date time is for some reason in the future idk why still working on it
This commit is contained in:
parent
6b155f5fbe
commit
46f8b8fa85
3 changed files with 90 additions and 72 deletions
|
@ -3,9 +3,11 @@ 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.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.Intrinsics.Arm;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
|
@ -64,17 +66,24 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
byte[] writeDate = new byte[2];
|
byte[] writeDate = new byte[2];
|
||||||
byte[] writeCounter = new byte[2];
|
byte[] writeCounter = new byte[2];
|
||||||
byte formData = 0;
|
byte formData = 0;
|
||||||
byte[] applicationAreas = new byte[212];
|
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
|
// Reading specific pages and parsing bytes
|
||||||
for (int page = 0; page < 128; page++) // NTAG215 has 128 pages
|
for (int page = 0; page < 134; page++) // NTAG215 has 128 pages
|
||||||
{
|
{
|
||||||
int pageStartIdx = page * 4; // Each page is 4 bytes
|
int pageStartIdx = page * 4; // Each page is 4 bytes
|
||||||
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;
|
||||||
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
|
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
|
||||||
|
|
||||||
// Special handling for specific pages
|
// Special handling for specific pages
|
||||||
switch (page)
|
switch (page)
|
||||||
{
|
{
|
||||||
|
@ -86,6 +95,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
byte internalValue = pageData[1];
|
byte internalValue = pageData[1];
|
||||||
Console.WriteLine($"Page 2: BCC1 + Internal Value 0x{internalValue:X2} (Expected 0x48).");
|
Console.WriteLine($"Page 2: BCC1 + Internal Value 0x{internalValue:X2} (Expected 0x48).");
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
// byte 0 amd 1 are settings
|
||||||
|
Array.Copy(pageData, 0, SettingsBytes, 0, 2);
|
||||||
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
// Bytes 0 and 1 are init date, bytes 2 and 3 are write date
|
// Bytes 0 and 1 are init date, bytes 2 and 3 are write date
|
||||||
|
@ -126,22 +139,28 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
case 66:
|
case 66:
|
||||||
// Bytes 0 and 1 are write counter
|
// 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
|
// Pages 76 to 127 are application areas
|
||||||
case >= 76 and <= 127:
|
case >= 76 and <= 129:
|
||||||
int appAreaOffset = (page - 76) * 4;
|
int appAreaOffset = (page - 76) * 4;
|
||||||
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
|
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
string titleIdStr = BitConverter.ToString(titleId).Replace("-", "");
|
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 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 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";
|
||||||
|
@ -161,6 +180,8 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
Console.WriteLine($"Nickname: {nickName}");
|
Console.WriteLine($"Nickname: {nickName}");
|
||||||
Console.WriteLine($"Init Date: {initDateStr}");
|
Console.WriteLine($"Init Date: {initDateStr}");
|
||||||
Console.WriteLine($"Write Date: {writeDateStr}");
|
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
|
||||||
{
|
{
|
||||||
|
@ -168,9 +189,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
TagUuid = uid,
|
TagUuid = uid,
|
||||||
AmiiboId = finalID
|
AmiiboId = finalID
|
||||||
};
|
};
|
||||||
|
ushort initDateValue = BitConverter.ToUInt16(initDate, 0);
|
||||||
DateTime initDateTime = DateTimeFromBytes(initDate);
|
ushort writeDateValue = BitConverter.ToUInt16(writeDate, 0);
|
||||||
DateTime writeDateTime = DateTimeFromBytes(writeDate);
|
DateTime initDateTime = DateTimeFromTag(initDateValue);
|
||||||
|
DateTime writeDateTime = DateTimeFromTag(writeDateValue);
|
||||||
|
|
||||||
Console.WriteLine($"Parsed Init Date: {initDateTime}");
|
Console.WriteLine($"Parsed Init Date: {initDateTime}");
|
||||||
Console.WriteLine($"Parsed Write Date: {writeDateTime}");
|
Console.WriteLine($"Parsed Write Date: {writeDateTime}");
|
||||||
|
@ -178,18 +200,44 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
virtualAmiiboFile.FirstWriteDate = initDateTime;
|
virtualAmiiboFile.FirstWriteDate = initDateTime;
|
||||||
virtualAmiiboFile.LastWriteDate = writeDateTime;
|
virtualAmiiboFile.LastWriteDate = writeDateTime;
|
||||||
virtualAmiiboFile.WriteCounter = BitConverter.ToUInt16(writeCounter, 0);
|
virtualAmiiboFile.WriteCounter = BitConverter.ToUInt16(writeCounter, 0);
|
||||||
|
|
||||||
// Parse application areas
|
|
||||||
//List<VirtualAmiiboApplicationArea> applicationAreasList = ParseAmiiboData(applicationAreas);
|
|
||||||
List<VirtualAmiiboApplicationArea> applicationAreasList = new List<VirtualAmiiboApplicationArea>();
|
|
||||||
virtualAmiiboFile.ApplicationAreas = applicationAreasList;
|
|
||||||
|
|
||||||
// Save the virtual Amiibo file
|
|
||||||
VirtualAmiibo.SaveAmiiboFile(virtualAmiiboFile);
|
VirtualAmiibo.SaveAmiiboFile(virtualAmiiboFile);
|
||||||
|
VirtualAmiibo.applicationBytes = applicationAreas;
|
||||||
return virtualAmiiboFile;
|
return virtualAmiiboFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static uint CalculateCRC32(byte[] input)
|
||||||
|
{
|
||||||
|
// Setup CRC 32 table
|
||||||
|
uint p0 = 0xEDB88320u | 0x80000000u;
|
||||||
|
uint[] u0 = new uint[0x100];
|
||||||
|
|
||||||
|
for (uint i = 1; i < 0x100; i++)
|
||||||
|
{
|
||||||
|
uint t0 = i;
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if ((t0 & 1) != 0)
|
||||||
|
{
|
||||||
|
t0 = (t0 >> 1) ^ p0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t0 >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u0[i] = t0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate CRC32 from table
|
||||||
|
uint t = 0x0;
|
||||||
|
foreach (byte k in input)
|
||||||
|
{
|
||||||
|
t = (t >> 8) ^ u0[(k ^ t) & 0xFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
return t ^ 0xFFFFFFFFu;
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetKeyRetailBinPath()
|
private static string GetKeyRetailBinPath()
|
||||||
{
|
{
|
||||||
return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin");
|
return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin");
|
||||||
|
@ -197,37 +245,25 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
|
|
||||||
public static bool IsPageEncrypted(int page)
|
public static bool IsPageEncrypted(int page)
|
||||||
{
|
{
|
||||||
return (page >= 6 && page <= 9) || (page >= 43 && page <= 84);
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DateTime DateTimeFromBytes(byte[] date)
|
public static DateTime DateTimeFromTag(ushort value)
|
||||||
{
|
{
|
||||||
if (date == null || date.Length != 2)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Invalid date bytes.");
|
|
||||||
return DateTime.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort value = BitConverter.ToUInt16(date, 0);
|
|
||||||
|
|
||||||
int day = value & 0x1F;
|
|
||||||
int month = (value >> 5) & 0x0F;
|
|
||||||
int year = (value >> 9) & 0x7F;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new DateTime(2000 + year, month, day);
|
var day = value & 0x1F;
|
||||||
|
var month = (value >> 5) & 0x0F;
|
||||||
|
var year = (value >> 9) & 0x7F;
|
||||||
|
return new DateTime(1970 + year, month, day);
|
||||||
}
|
}
|
||||||
catch (ArgumentOutOfRangeException)
|
catch
|
||||||
{
|
{
|
||||||
Console.WriteLine("Invalid date values extracted.");
|
return DateTime.Now;
|
||||||
return DateTime.MinValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<VirtualAmiiboApplicationArea> ParseAmiiboData(byte[] decryptedData)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize<List<VirtualAmiiboApplicationArea>>(decryptedData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,35 +79,5 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Bin
|
||||||
break; // Stop if no overflow
|
break; // Stop if no overflow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime ParseDate(byte[] data, int offset)
|
|
||||||
{
|
|
||||||
ushort year = BitConverter.ToUInt16(data, offset);
|
|
||||||
byte month = data[offset + 2];
|
|
||||||
byte day = data[offset + 3];
|
|
||||||
byte hour = data[offset + 4];
|
|
||||||
byte minute = data[offset + 5];
|
|
||||||
byte second = data[offset + 6];
|
|
||||||
|
|
||||||
return new DateTime(year, month, day, hour, minute, second);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<object> ParseApplicationAreas(byte[] data, int startOffset, int areaSize)
|
|
||||||
{
|
|
||||||
var areas = new List<object>();
|
|
||||||
for (int i = 0; i < 8; i++) // Assuming 8 areas
|
|
||||||
{
|
|
||||||
int offset = startOffset + (i * areaSize);
|
|
||||||
string applicationId = BitConverter.ToString(data[offset..(offset + 4)]).Replace("-", "");
|
|
||||||
byte[] areaData = data[(offset + 4)..(offset + areaSize)];
|
|
||||||
areas.Add(new VirtualAmiiboApplicationArea
|
|
||||||
{
|
|
||||||
ApplicationAreaId = uint.Parse(applicationId),
|
|
||||||
ApplicationArea = areaData
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return areas;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
{
|
{
|
||||||
static class VirtualAmiibo
|
static class VirtualAmiibo
|
||||||
{
|
{
|
||||||
private static uint _openedApplicationAreaId;
|
public static uint _openedApplicationAreaId;
|
||||||
|
public static byte[] applicationBytes = new byte[0];
|
||||||
private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default;
|
private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default;
|
||||||
|
|
||||||
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
|
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
|
||||||
{
|
{
|
||||||
if (useRandomUuid)
|
if (useRandomUuid)
|
||||||
|
@ -103,6 +102,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId)
|
public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId)
|
||||||
{
|
{
|
||||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||||
|
if (applicationBytes.Length > 0)
|
||||||
|
{
|
||||||
|
_openedApplicationAreaId = applicationAreaId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId))
|
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId))
|
||||||
{
|
{
|
||||||
|
@ -116,6 +120,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
|
|
||||||
public static byte[] GetApplicationArea(string amiiboId)
|
public static byte[] GetApplicationArea(string amiiboId)
|
||||||
{
|
{
|
||||||
|
if (applicationBytes.Length > 0)
|
||||||
|
{
|
||||||
|
byte[] bytes = applicationBytes;
|
||||||
|
applicationBytes = new byte[0];
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||||
|
|
||||||
foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas)
|
foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas)
|
||||||
|
@ -209,5 +219,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json");
|
string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json");
|
||||||
JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile);
|
JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool SaveFileExists(VirtualAmiiboFile virtualAmiiboFile) => File.Exists(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue