mirror of
https://github.com/GreemDev/Ryujinx
synced 2024-12-22 03:46:39 +01:00
Compare commits
8 commits
9f274d0b93
...
fa44db8464
Author | SHA1 | Date | |
---|---|---|---|
|
fa44db8464 | ||
|
e0acefeeef | ||
|
b5604311cb | ||
|
2c53242b31 | ||
|
2874c40ee9 | ||
|
91518acf30 | ||
|
2af9a33979 | ||
|
0adaa4cb96 |
39 changed files with 2129 additions and 941 deletions
|
@ -3,6 +3,7 @@ using ARMeilleure.CodeGen.Linking;
|
||||||
using ARMeilleure.CodeGen.Unwinding;
|
using ARMeilleure.CodeGen.Unwinding;
|
||||||
using ARMeilleure.Common;
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
@ -30,7 +31,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 6997; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7007; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -183,6 +184,36 @@ namespace ARMeilleure.Translation.PTC
|
||||||
InitializeCarriers();
|
InitializeCarriers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ContainsBlacklistedFunctions()
|
||||||
|
{
|
||||||
|
List<ulong> blacklist = Profiler.GetBlacklistedFunctions();
|
||||||
|
bool containsBlacklistedFunctions = false;
|
||||||
|
_infosStream.Seek(0L, SeekOrigin.Begin);
|
||||||
|
bool foundBadFunction = false;
|
||||||
|
|
||||||
|
for (int index = 0; index < GetEntriesCount(); index++)
|
||||||
|
{
|
||||||
|
InfoEntry infoEntry = DeserializeStructure<InfoEntry>(_infosStream);
|
||||||
|
foreach (ulong address in blacklist)
|
||||||
|
{
|
||||||
|
if (infoEntry.Address == address)
|
||||||
|
{
|
||||||
|
containsBlacklistedFunctions = true;
|
||||||
|
Logger.Warning?.Print(LogClass.Ptc, "PPTC cache invalidated: Found blacklisted functions in PPTC cache");
|
||||||
|
foundBadFunction = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundBadFunction)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return containsBlacklistedFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
private void PreLoad()
|
private void PreLoad()
|
||||||
{
|
{
|
||||||
string fileNameActual = $"{CachePathActual}.cache";
|
string fileNameActual = $"{CachePathActual}.cache";
|
||||||
|
@ -531,7 +562,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
public void LoadTranslations(Translator translator)
|
public void LoadTranslations(Translator translator)
|
||||||
{
|
{
|
||||||
if (AreCarriersEmpty())
|
if (AreCarriersEmpty() || ContainsBlacklistedFunctions())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -834,10 +865,18 @@ namespace ARMeilleure.Translation.PTC
|
||||||
while (profiledFuncsToTranslate.TryDequeue(out var item))
|
while (profiledFuncsToTranslate.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
ulong address = item.address;
|
ulong address = item.address;
|
||||||
|
ExecutionMode executionMode = item.funcProfile.Mode;
|
||||||
|
bool highCq = item.funcProfile.HighCq;
|
||||||
|
|
||||||
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
||||||
|
|
||||||
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
TranslatedFunction func = translator.Translate(address, executionMode, highCq);
|
||||||
|
|
||||||
|
if (func == null)
|
||||||
|
{
|
||||||
|
Profiler.UpdateEntry(address, executionMode, true, true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func);
|
bool isAddressUnique = translator.Functions.TryAdd(address, func.GuestSize, func);
|
||||||
|
|
||||||
|
@ -884,7 +923,14 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount);
|
PtcStateChanged?.Invoke(PtcLoadingState.Loaded, _translateCount, _translateTotalCount);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism} in {sw.Elapsed.TotalSeconds} s");
|
if (_translateCount == _translateTotalCount)
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | Thread count: {degreeOfParallelism} in {sw.Elapsed.TotalSeconds} s");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {_translateTotalCount} functions translated | {_translateTotalCount - _translateCount} function{(_translateTotalCount - _translateCount != 1 ? "s" : "")} blacklisted | Thread count: {degreeOfParallelism} in {sw.Elapsed.TotalSeconds} s");
|
||||||
|
}
|
||||||
|
|
||||||
Thread preSaveThread = new(PreSave)
|
Thread preSaveThread = new(PreSave)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,10 +23,11 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 7007; //! Not to be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private static readonly uint[] _migrateInternalVersions = {
|
private static readonly uint[] _migrateInternalVersions = {
|
||||||
1866,
|
1866,
|
||||||
|
5518,
|
||||||
};
|
};
|
||||||
|
|
||||||
private const int SaveInterval = 30; // Seconds.
|
private const int SaveInterval = 30; // Seconds.
|
||||||
|
@ -72,20 +73,30 @@ namespace ARMeilleure.Translation.PTC
|
||||||
Enabled = false;
|
Enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
public void AddEntry(ulong address, ExecutionMode mode, bool highCq, bool blacklist = false)
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
Debug.Assert(!highCq);
|
Debug.Assert(!highCq);
|
||||||
|
|
||||||
lock (_lock)
|
if (blacklist)
|
||||||
{
|
{
|
||||||
ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false));
|
lock (_lock)
|
||||||
|
{
|
||||||
|
ProfiledFuncs[address] = new FuncProfile(mode, highCq: false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
ProfiledFuncs.TryAdd(address, new FuncProfile(mode, highCq: false, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq, bool? blacklist = null)
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
|
@ -95,7 +106,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
Debug.Assert(ProfiledFuncs.ContainsKey(address));
|
Debug.Assert(ProfiledFuncs.ContainsKey(address));
|
||||||
|
|
||||||
ProfiledFuncs[address] = new FuncProfile(mode, highCq: true);
|
ProfiledFuncs[address] = new FuncProfile(mode, highCq: true, blacklist ?? ProfiledFuncs[address].Blacklist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +122,7 @@ namespace ARMeilleure.Translation.PTC
|
||||||
|
|
||||||
foreach (var profiledFunc in ProfiledFuncs)
|
foreach (var profiledFunc in ProfiledFuncs)
|
||||||
{
|
{
|
||||||
if (!funcs.ContainsKey(profiledFunc.Key))
|
if (!funcs.ContainsKey(profiledFunc.Key) && !profiledFunc.Value.Blacklist)
|
||||||
{
|
{
|
||||||
profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
|
profiledFuncsToTranslate.Enqueue((profiledFunc.Key, profiledFunc.Value));
|
||||||
}
|
}
|
||||||
|
@ -126,6 +137,24 @@ namespace ARMeilleure.Translation.PTC
|
||||||
ProfiledFuncs.TrimExcess();
|
ProfiledFuncs.TrimExcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ulong> GetBlacklistedFunctions()
|
||||||
|
{
|
||||||
|
List<ulong> funcs = new List<ulong>();
|
||||||
|
|
||||||
|
foreach (var profiledFunc in ProfiledFuncs)
|
||||||
|
{
|
||||||
|
if (profiledFunc.Value.Blacklist)
|
||||||
|
{
|
||||||
|
if (!funcs.Contains(profiledFunc.Key))
|
||||||
|
{
|
||||||
|
funcs.Add(profiledFunc.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return funcs;
|
||||||
|
}
|
||||||
|
|
||||||
public void PreLoad()
|
public void PreLoad()
|
||||||
{
|
{
|
||||||
_lastHash = default;
|
_lastHash = default;
|
||||||
|
@ -216,13 +245,18 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null;
|
||||||
|
|
||||||
switch (outerHeader.InfoFileVersion)
|
switch (outerHeader.InfoFileVersion)
|
||||||
{
|
{
|
||||||
case InternalVersion:
|
case InternalVersion:
|
||||||
ProfiledFuncs = Deserialize(stream);
|
ProfiledFuncs = Deserialize(stream);
|
||||||
break;
|
break;
|
||||||
case 1866:
|
case 1866:
|
||||||
ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile));
|
migrateEntryFunc = (address, profile) => (address + 0x500000UL, profile);
|
||||||
|
goto case 5518;
|
||||||
|
case 5518:
|
||||||
|
ProfiledFuncs = DeserializeAddBlacklist(stream, migrateEntryFunc);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache.");
|
Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache.");
|
||||||
|
@ -252,6 +286,16 @@ namespace ARMeilleure.Translation.PTC
|
||||||
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
|
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Dictionary<ulong, FuncProfile> DeserializeAddBlacklist(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null)
|
||||||
|
{
|
||||||
|
if (migrateEntryFunc != null)
|
||||||
|
{
|
||||||
|
return DeserializeAndUpdateDictionary(stream, (Stream stream) => { return new FuncProfile(DeserializeStructure<FuncProfilePreBlacklist>(stream)); }, migrateEntryFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeserializeDictionary<ulong, FuncProfile>(stream, (Stream stream) => { return new FuncProfile(DeserializeStructure<FuncProfilePreBlacklist>(stream)); });
|
||||||
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||||
{
|
{
|
||||||
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
||||||
|
@ -383,13 +427,35 @@ namespace ARMeilleure.Translation.PTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 6*/)]
|
||||||
public struct FuncProfile
|
public struct FuncProfile
|
||||||
{
|
{
|
||||||
public ExecutionMode Mode;
|
public ExecutionMode Mode;
|
||||||
public bool HighCq;
|
public bool HighCq;
|
||||||
|
public bool Blacklist;
|
||||||
|
|
||||||
public FuncProfile(ExecutionMode mode, bool highCq)
|
public FuncProfile(ExecutionMode mode, bool highCq, bool blacklist)
|
||||||
|
{
|
||||||
|
Mode = mode;
|
||||||
|
HighCq = highCq;
|
||||||
|
Blacklist = blacklist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FuncProfile(FuncProfilePreBlacklist fp)
|
||||||
|
{
|
||||||
|
Mode = fp.Mode;
|
||||||
|
HighCq = fp.HighCq;
|
||||||
|
Blacklist = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
||||||
|
public struct FuncProfilePreBlacklist
|
||||||
|
{
|
||||||
|
public ExecutionMode Mode;
|
||||||
|
public bool HighCq;
|
||||||
|
|
||||||
|
public FuncProfilePreBlacklist(ExecutionMode mode, bool highCq)
|
||||||
{
|
{
|
||||||
Mode = mode;
|
Mode = mode;
|
||||||
HighCq = highCq;
|
HighCq = highCq;
|
||||||
|
|
|
@ -249,6 +249,11 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter<uint> counter);
|
||||||
|
|
||||||
|
if (cfg == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
ulong funcSize = funcRange.End - funcRange.Start;
|
ulong funcSize = funcRange.End - funcRange.Start;
|
||||||
|
|
||||||
Logger.EndPass(PassName.Translation, cfg);
|
Logger.EndPass(PassName.Translation, cfg);
|
||||||
|
@ -407,6 +412,11 @@ namespace ARMeilleure.Translation
|
||||||
if (opCode.Instruction.Emitter != null)
|
if (opCode.Instruction.Emitter != null)
|
||||||
{
|
{
|
||||||
opCode.Instruction.Emitter(context);
|
opCode.Instruction.Emitter(context);
|
||||||
|
if (opCode.Instruction.Name == InstName.Und && blkIndex == 0)
|
||||||
|
{
|
||||||
|
range = new Range(rangeStart, rangeEnd);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
private readonly string _titleIdText;
|
private readonly string _titleIdText;
|
||||||
private readonly string _displayVersion;
|
private readonly string _displayVersion;
|
||||||
private readonly bool _diskCacheEnabled;
|
private readonly bool _diskCacheEnabled;
|
||||||
|
private readonly string _diskCacheSelector;
|
||||||
private readonly ulong _codeAddress;
|
private readonly ulong _codeAddress;
|
||||||
private readonly ulong _codeSize;
|
private readonly ulong _codeSize;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
string titleIdText,
|
string titleIdText,
|
||||||
string displayVersion,
|
string displayVersion,
|
||||||
bool diskCacheEnabled,
|
bool diskCacheEnabled,
|
||||||
|
string diskCacheSelector,
|
||||||
ulong codeAddress,
|
ulong codeAddress,
|
||||||
ulong codeSize)
|
ulong codeSize)
|
||||||
{
|
{
|
||||||
|
@ -39,6 +41,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
_titleIdText = titleIdText;
|
_titleIdText = titleIdText;
|
||||||
_displayVersion = displayVersion;
|
_displayVersion = displayVersion;
|
||||||
_diskCacheEnabled = diskCacheEnabled;
|
_diskCacheEnabled = diskCacheEnabled;
|
||||||
|
_diskCacheSelector = diskCacheSelector;
|
||||||
_codeAddress = codeAddress;
|
_codeAddress = codeAddress;
|
||||||
_codeSize = codeSize;
|
_codeSize = codeSize;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +117,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, "default"); //Ready for exefs profiles
|
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, _diskCacheSelector ?? "default");
|
||||||
|
|
||||||
return processContext;
|
return processContext;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemA
|
||||||
using Ryujinx.HLE.HOS.Services.Apm;
|
using Ryujinx.HLE.HOS.Services.Apm;
|
||||||
using Ryujinx.HLE.HOS.Services.Caps;
|
using Ryujinx.HLE.HOS.Services.Caps;
|
||||||
using Ryujinx.HLE.HOS.Services.Mii;
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||||
|
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.Services.Nv;
|
using Ryujinx.HLE.HOS.Services.Nv;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
||||||
|
@ -337,6 +339,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
||||||
{
|
{
|
||||||
|
if (VirtualAmiibo.ApplicationBytes.Length > 0)
|
||||||
|
{
|
||||||
|
VirtualAmiibo.ApplicationBytes = new byte[0];
|
||||||
|
VirtualAmiibo.InputBin = string.Empty;
|
||||||
|
}
|
||||||
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
|
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
|
||||||
{
|
{
|
||||||
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
|
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
|
||||||
|
@ -344,6 +351,22 @@ namespace Ryujinx.HLE.HOS
|
||||||
NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid;
|
NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void ScanAmiiboFromBin(string path)
|
||||||
|
{
|
||||||
|
VirtualAmiibo.InputBin = path;
|
||||||
|
if (VirtualAmiibo.ApplicationBytes.Length > 0)
|
||||||
|
{
|
||||||
|
VirtualAmiibo.ApplicationBytes = new byte[0];
|
||||||
|
}
|
||||||
|
byte[] encryptedData = File.ReadAllBytes(path);
|
||||||
|
VirtualAmiiboFile newFile = AmiiboBinReader.ReadBinFile(encryptedData);
|
||||||
|
if (SearchingForAmiibo(out int nfpDeviceId))
|
||||||
|
{
|
||||||
|
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
|
||||||
|
NfpDevices[nfpDeviceId].AmiiboId = newFile.AmiiboId;
|
||||||
|
NfpDevices[nfpDeviceId].UseRandomUuid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool SearchingForAmiibo(out int nfpDeviceId)
|
public bool SearchingForAmiibo(out int nfpDeviceId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@ using LibHac.FsSystem;
|
||||||
using LibHac.Loader;
|
using LibHac.Loader;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.RomFs;
|
using LibHac.Tools.FsSystem.RomFs;
|
||||||
|
using LibHac.Util;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
@ -18,6 +19,7 @@ using System.Collections.Specialized;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using LazyFile = Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy.LazyFile;
|
using LazyFile = Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy.LazyFile;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
|
@ -583,6 +585,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
public BitVector32 Stubs;
|
public BitVector32 Stubs;
|
||||||
public BitVector32 Replaces;
|
public BitVector32 Replaces;
|
||||||
public MetaLoader Npdm;
|
public MetaLoader Npdm;
|
||||||
|
public string Hash;
|
||||||
|
|
||||||
public bool Modified => (Stubs.Data | Replaces.Data) != 0;
|
public bool Modified => (Stubs.Data | Replaces.Data) != 0;
|
||||||
}
|
}
|
||||||
|
@ -593,8 +596,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
Stubs = new BitVector32(),
|
Stubs = new BitVector32(),
|
||||||
Replaces = new BitVector32(),
|
Replaces = new BitVector32(),
|
||||||
|
Hash = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
string tempHash = string.Empty;
|
||||||
|
|
||||||
if (!_appMods.TryGetValue(applicationId, out ModCache mods) || mods.ExefsDirs.Count == 0)
|
if (!_appMods.TryGetValue(applicationId, out ModCache mods) || mods.ExefsDirs.Count == 0)
|
||||||
{
|
{
|
||||||
return modLoadResult;
|
return modLoadResult;
|
||||||
|
@ -630,8 +636,16 @@ namespace Ryujinx.HLE.HOS
|
||||||
|
|
||||||
modLoadResult.Replaces[1 << i] = true;
|
modLoadResult.Replaces[1 << i] = true;
|
||||||
|
|
||||||
nsos[i] = new NsoExecutable(nsoFile.OpenRead().AsStorage(), nsoName);
|
using (FileStream stream = nsoFile.OpenRead())
|
||||||
Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced");
|
{
|
||||||
|
nsos[i] = new NsoExecutable(stream.AsStorage(), nsoName);
|
||||||
|
Logger.Info?.Print(LogClass.ModLoader, $"NSO '{nsoName}' replaced");
|
||||||
|
using (MD5 md5 = MD5.Create())
|
||||||
|
{
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
tempHash += BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension));
|
modLoadResult.Stubs[1 << i] |= File.Exists(Path.Combine(mod.Path.FullName, nsoName + StubExtension));
|
||||||
|
@ -663,6 +677,14 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(tempHash))
|
||||||
|
{
|
||||||
|
using (MD5 md5 = MD5.Create())
|
||||||
|
{
|
||||||
|
modLoadResult.Hash += BitConverter.ToString(md5.ComputeHash(tempHash.ToBytes())).Replace("-", "").ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return modLoadResult;
|
return modLoadResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,340 @@
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
|
||||||
|
{
|
||||||
|
public class AmiiboBinReader
|
||||||
|
{
|
||||||
|
private static byte CalculateBCC0(byte[] uid)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VirtualAmiiboFile ReadBinFile(byte[] fileBytes)
|
||||||
|
{
|
||||||
|
string keyRetailBinPath = GetKeyRetailBinPath();
|
||||||
|
if (string.IsNullOrEmpty(keyRetailBinPath))
|
||||||
|
{
|
||||||
|
return new VirtualAmiiboFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] initialCounter = new byte[16];
|
||||||
|
|
||||||
|
const int totalPages = 135;
|
||||||
|
const int pageSize = 4;
|
||||||
|
const int totalBytes = totalPages * pageSize;
|
||||||
|
|
||||||
|
if (fileBytes.Length < totalBytes)
|
||||||
|
{
|
||||||
|
return new VirtualAmiiboFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
|
||||||
|
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(fileBytes);
|
||||||
|
|
||||||
|
byte[] titleId = new byte[8];
|
||||||
|
byte[] usedCharacter = new byte[2];
|
||||||
|
byte[] variation = new byte[2];
|
||||||
|
byte[] amiiboID = new byte[2];
|
||||||
|
byte[] setID = new byte[1];
|
||||||
|
byte[] initDate = new byte[2];
|
||||||
|
byte[] writeDate = new byte[2];
|
||||||
|
byte[] writeCounter = new byte[2];
|
||||||
|
byte[] appId = new byte[8];
|
||||||
|
byte[] settingsBytes = new byte[2];
|
||||||
|
byte formData = 0;
|
||||||
|
byte[] applicationAreas = new byte[216];
|
||||||
|
byte[] dataFull = amiiboDump.GetData();
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Data Full Length: {dataFull.Length}");
|
||||||
|
byte[] uid = new byte[7];
|
||||||
|
Array.Copy(dataFull, 0, uid, 0, 7);
|
||||||
|
|
||||||
|
byte bcc0 = CalculateBCC0(uid);
|
||||||
|
byte bcc1 = CalculateBCC1(uid);
|
||||||
|
LogDebugData(uid, bcc0, bcc1);
|
||||||
|
for (int page = 0; page < 128; page++) // NTAG215 has 128 pages
|
||||||
|
{
|
||||||
|
int pageStartIdx = page * 4; // Each page is 4 bytes
|
||||||
|
byte[] pageData = new byte[4];
|
||||||
|
byte[] sourceBytes = dataFull;
|
||||||
|
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
|
||||||
|
// Special handling for specific pages
|
||||||
|
switch (page)
|
||||||
|
{
|
||||||
|
case 0: // Page 0 (UID + BCC0)
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, "Page 0: UID and BCC0.");
|
||||||
|
break;
|
||||||
|
case 2: // Page 2 (BCC1 + Internal Value)
|
||||||
|
byte internalValue = pageData[1];
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Page 2: BCC1 + Internal Value 0x{internalValue:X2} (Expected 0x48).");
|
||||||
|
break;
|
||||||
|
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, 2, writeDate, 0, 2);
|
||||||
|
break;
|
||||||
|
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, 2, variation, 0, 2);
|
||||||
|
break;
|
||||||
|
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);
|
||||||
|
setID[0] = pageData[2];
|
||||||
|
formData = pageData[3];
|
||||||
|
break;
|
||||||
|
case 64:
|
||||||
|
case 65:
|
||||||
|
// Extract title ID
|
||||||
|
int titleIdOffset = (page - 64) * 4;
|
||||||
|
Array.Copy(pageData, 0, titleId, titleIdOffset, 4);
|
||||||
|
break;
|
||||||
|
case 66:
|
||||||
|
// Bytes 0 and 1 are write counter
|
||||||
|
Array.Copy(pageData, 0, writeCounter, 0, 2);
|
||||||
|
break;
|
||||||
|
// Pages 76 to 127 are application areas
|
||||||
|
case >= 76 and <= 127:
|
||||||
|
int appAreaOffset = (page - 76) * 4;
|
||||||
|
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string usedCharacterStr = BitConverter.ToString(usedCharacter).Replace("-", "");
|
||||||
|
string variationStr = BitConverter.ToString(variation).Replace("-", "");
|
||||||
|
string amiiboIDStr = BitConverter.ToString(amiiboID).Replace("-", "");
|
||||||
|
string setIDStr = BitConverter.ToString(setID).Replace("-", "");
|
||||||
|
string head = usedCharacterStr + variationStr;
|
||||||
|
string tail = amiiboIDStr + setIDStr + "02";
|
||||||
|
string finalID = head + tail;
|
||||||
|
|
||||||
|
ushort settingsValue = BitConverter.ToUInt16(settingsBytes, 0);
|
||||||
|
ushort initDateValue = BitConverter.ToUInt16(initDate, 0);
|
||||||
|
ushort writeDateValue = BitConverter.ToUInt16(writeDate, 0);
|
||||||
|
DateTime initDateTime = DateTimeFromTag(initDateValue);
|
||||||
|
DateTime writeDateTime = DateTimeFromTag(writeDateValue);
|
||||||
|
ushort writeCounterValue = BitConverter.ToUInt16(writeCounter, 0);
|
||||||
|
string nickName = amiiboDump.AmiiboNickname;
|
||||||
|
LogFinalData(titleId, appId, head, tail, finalID, nickName, initDateTime, writeDateTime, settingsValue, writeCounterValue, applicationAreas);
|
||||||
|
|
||||||
|
VirtualAmiiboFile virtualAmiiboFile = new VirtualAmiiboFile
|
||||||
|
{
|
||||||
|
FileVersion = 1,
|
||||||
|
TagUuid = uid,
|
||||||
|
AmiiboId = finalID,
|
||||||
|
NickName = nickName,
|
||||||
|
FirstWriteDate = initDateTime,
|
||||||
|
LastWriteDate = writeDateTime,
|
||||||
|
WriteCounter = writeCounterValue,
|
||||||
|
};
|
||||||
|
if (writeCounterValue > 0)
|
||||||
|
{
|
||||||
|
VirtualAmiibo.ApplicationBytes = applicationAreas;
|
||||||
|
}
|
||||||
|
VirtualAmiibo.NickName = nickName;
|
||||||
|
return virtualAmiiboFile;
|
||||||
|
}
|
||||||
|
public static bool SaveBinFile(string inputFile, byte[] appData)
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceNfp, "Saving bin file.");
|
||||||
|
byte[] readBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
readBytes = File.ReadAllBytes(inputFile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, $"Error reading file: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
string keyRetailBinPath = GetKeyRetailBinPath();
|
||||||
|
if (string.IsNullOrEmpty(keyRetailBinPath))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Key retail path is empty.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appData.Length != 216) // Ensure application area size is valid
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid application data length. Expected 216 bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
|
||||||
|
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
|
||||||
|
|
||||||
|
byte[] oldData = amiiboDump.GetData();
|
||||||
|
if (oldData.Length != 540) // Verify the expected length for NTAG215 tags
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid tag data length. Expected 540 bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] newData = new byte[oldData.Length];
|
||||||
|
Array.Copy(oldData, newData, oldData.Length);
|
||||||
|
|
||||||
|
// Replace application area with appData
|
||||||
|
int appAreaOffset = 76 * 4; // Starting page (76) times 4 bytes per page
|
||||||
|
Array.Copy(appData, 0, newData, appAreaOffset, appData.Length);
|
||||||
|
|
||||||
|
AmiiboDump encryptedDump = amiiboDecryptor.EncryptAmiiboDump(newData);
|
||||||
|
byte[] encryptedData = encryptedDump.GetData();
|
||||||
|
|
||||||
|
if (encryptedData == null || encryptedData.Length != readBytes.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Failed to encrypt data correctly.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inputFile = inputFile.Replace("_modified", string.Empty);
|
||||||
|
// Save the encrypted data to file or return it for saving externally
|
||||||
|
string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_modified.bin");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(outputFilePath, encryptedData);
|
||||||
|
Logger.Info?.Print(LogClass.ServiceNfp, $"Modified Amiibo data saved to {outputFilePath}.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, $"Error saving file: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static bool SaveBinFile(string inputFile, string newNickName)
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceNfp, "Saving bin file.");
|
||||||
|
byte[] readBytes;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
readBytes = File.ReadAllBytes(inputFile);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, $"Error reading file: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
string keyRetailBinPath = GetKeyRetailBinPath();
|
||||||
|
if (string.IsNullOrEmpty(keyRetailBinPath))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Key retail path is empty.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
|
||||||
|
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
|
||||||
|
amiiboDump.AmiiboNickname = newNickName;
|
||||||
|
byte[] oldData = amiiboDump.GetData();
|
||||||
|
if (oldData.Length != 540) // Verify the expected length for NTAG215 tags
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid tag data length. Expected 540 bytes.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] encryptedData = amiiboDecryptor.EncryptAmiiboDump(oldData).GetData();
|
||||||
|
|
||||||
|
if (encryptedData == null || encryptedData.Length != readBytes.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, "Failed to encrypt data correctly.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inputFile = inputFile.Replace("_modified", string.Empty);
|
||||||
|
// Save the encrypted data to file or return it for saving externally
|
||||||
|
string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_modified.bin");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(outputFilePath, encryptedData);
|
||||||
|
Logger.Info?.Print(LogClass.ServiceNfp, $"Modified Amiibo data saved to {outputFilePath}.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceNfp, $"Error saving file: {ex.Message}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void LogDebugData(byte[] uid, byte bcc0, byte bcc1)
|
||||||
|
{
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"UID: {BitConverter.ToString(uid)}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"BCC0: 0x{bcc0:X2}, BCC1: 0x{bcc1:X2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Title ID: 0x{BitConverter.ToString(titleId).Replace("-", "")}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Application Program ID: 0x{BitConverter.ToString(appId).Replace("-", "")}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Head: {head}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Tail: {tail}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Final ID: {finalID}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Nickname: {nickName}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Init Date: {initDateTime}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Write Date: {writeDateTime}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Settings: 0x{settingsValue:X4}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, $"Write Counter: {writeCounterValue}");
|
||||||
|
Logger.Debug?.Print(LogClass.ServiceNfp, "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 crc = i;
|
||||||
|
for (int j = 0; j < 8; ++j)
|
||||||
|
{
|
||||||
|
if ((crc & 1) != 0)
|
||||||
|
crc = (crc >> 1) ^ polynomial;
|
||||||
|
else
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
table[i] = crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint result = 0xFFFFFFFF;
|
||||||
|
foreach (byte b in input)
|
||||||
|
{
|
||||||
|
byte index = (byte)((result & 0xFF) ^ b);
|
||||||
|
result = (result >> 8) ^ table[index];
|
||||||
|
}
|
||||||
|
return ~result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetKeyRetailBinPath()
|
||||||
|
{
|
||||||
|
return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasKeyRetailBinPath()
|
||||||
|
{
|
||||||
|
return File.Exists(GetKeyRetailBinPath());
|
||||||
|
}
|
||||||
|
public static DateTime DateTimeFromTag(ushort value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int day = value & 0x1F;
|
||||||
|
int month = (value >> 5) & 0x0F;
|
||||||
|
int year = (value >> 9) & 0x7F;
|
||||||
|
|
||||||
|
if (day == 0 || month == 0 || month > 12 || day > DateTime.DaysInMonth(2000 + year, month))
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
return new DateTime(2000 + year, month, day);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
|
||||||
|
{
|
||||||
|
public class AmiiboDecrypter
|
||||||
|
{
|
||||||
|
public AmiiboMasterKey DataKey { get; private set; }
|
||||||
|
public AmiiboMasterKey TagKey { get; private set; }
|
||||||
|
|
||||||
|
public AmiiboDecrypter(string keyRetailBinPath)
|
||||||
|
{
|
||||||
|
var combinedKeys = File.ReadAllBytes(keyRetailBinPath);
|
||||||
|
var keys = AmiiboMasterKey.FromCombinedBin(combinedKeys);
|
||||||
|
DataKey = keys.DataKey;
|
||||||
|
TagKey = keys.TagKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmiiboDump DecryptAmiiboDump(byte[] encryptedDumpData)
|
||||||
|
{
|
||||||
|
// Initialize AmiiboDump with encrypted data
|
||||||
|
AmiiboDump amiiboDump = new AmiiboDump(encryptedDumpData, DataKey, TagKey, isLocked: true);
|
||||||
|
|
||||||
|
// Unlock (decrypt) the dump
|
||||||
|
amiiboDump.Unlock();
|
||||||
|
|
||||||
|
// Optional: Verify HMACs
|
||||||
|
amiiboDump.VerifyHMACs();
|
||||||
|
|
||||||
|
return amiiboDump;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmiiboDump EncryptAmiiboDump(byte[] decryptedDumpData)
|
||||||
|
{
|
||||||
|
// Initialize AmiiboDump with decrypted data
|
||||||
|
AmiiboDump amiiboDump = new AmiiboDump(decryptedDumpData, DataKey, TagKey, isLocked: false);
|
||||||
|
|
||||||
|
// Lock (encrypt) the dump
|
||||||
|
amiiboDump.Lock();
|
||||||
|
|
||||||
|
return amiiboDump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
387
src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs
Normal file
387
src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
|
||||||
|
{
|
||||||
|
public class AmiiboDump
|
||||||
|
{
|
||||||
|
private AmiiboMasterKey dataMasterKey;
|
||||||
|
private AmiiboMasterKey tagMasterKey;
|
||||||
|
|
||||||
|
private bool isLocked;
|
||||||
|
private byte[] data;
|
||||||
|
private byte[] hmacTagKey;
|
||||||
|
private byte[] hmacDataKey;
|
||||||
|
private byte[] aesKey;
|
||||||
|
private byte[] aesIv;
|
||||||
|
|
||||||
|
public AmiiboDump(byte[] dumpData, AmiiboMasterKey dataKey, AmiiboMasterKey tagKey, bool isLocked = true)
|
||||||
|
{
|
||||||
|
if (dumpData.Length < 540)
|
||||||
|
throw new ArgumentException("Incomplete dump. Amiibo data is at least 540 bytes.");
|
||||||
|
|
||||||
|
this.data = new byte[540];
|
||||||
|
Array.Copy(dumpData, this.data, dumpData.Length);
|
||||||
|
this.dataMasterKey = dataKey;
|
||||||
|
this.tagMasterKey = tagKey;
|
||||||
|
this.isLocked = isLocked;
|
||||||
|
|
||||||
|
if (!isLocked)
|
||||||
|
{
|
||||||
|
DeriveKeysAndCipher();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] DeriveKey(AmiiboMasterKey key, bool deriveAes, out byte[] derivedAesKey, out byte[] derivedAesIv)
|
||||||
|
{
|
||||||
|
List<byte> seed = new List<byte>();
|
||||||
|
|
||||||
|
// Start with the type string (14 bytes)
|
||||||
|
seed.AddRange(key.TypeString);
|
||||||
|
|
||||||
|
// Append data based on magic size
|
||||||
|
int append = 16 - key.MagicSize;
|
||||||
|
byte[] extract = new byte[16];
|
||||||
|
Array.Copy(this.data, 0x011, extract, 0, 2); // Extract two bytes from user data section
|
||||||
|
for (int i = 2; i < 16; i++)
|
||||||
|
{
|
||||||
|
extract[i] = 0x00;
|
||||||
|
}
|
||||||
|
seed.AddRange(extract.Take(append));
|
||||||
|
|
||||||
|
// Add the magic bytes
|
||||||
|
seed.AddRange(key.MagicBytes.Take(key.MagicSize));
|
||||||
|
|
||||||
|
// Extract the UID (UID is 8 bytes)
|
||||||
|
byte[] uid = new byte[8];
|
||||||
|
Array.Copy(this.data, 0x000, uid, 0, 8);
|
||||||
|
seed.AddRange(uid);
|
||||||
|
seed.AddRange(uid);
|
||||||
|
|
||||||
|
// Extract some tag data (pages 0x20 - 0x28)
|
||||||
|
byte[] user = new byte[32];
|
||||||
|
Array.Copy(this.data, 0x060, user, 0, 32);
|
||||||
|
|
||||||
|
// XOR it with the key padding (XorPad)
|
||||||
|
byte[] paddedUser = new byte[32];
|
||||||
|
for (int i = 0; i < user.Length; i++)
|
||||||
|
{
|
||||||
|
paddedUser[i] = (byte)(user[i] ^ key.XorPad[i]);
|
||||||
|
}
|
||||||
|
seed.AddRange(paddedUser);
|
||||||
|
|
||||||
|
byte[] seedBytes = seed.ToArray();
|
||||||
|
if (seedBytes.Length != 78)
|
||||||
|
{
|
||||||
|
throw new Exception("Size check for key derived seed failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] hmacKey;
|
||||||
|
derivedAesKey = null;
|
||||||
|
derivedAesIv = null;
|
||||||
|
|
||||||
|
if (deriveAes)
|
||||||
|
{
|
||||||
|
// Derive AES Key and IV
|
||||||
|
var dataForAes = new byte[2 + seedBytes.Length];
|
||||||
|
dataForAes[0] = 0x00;
|
||||||
|
dataForAes[1] = 0x00; // Counter (0)
|
||||||
|
Array.Copy(seedBytes, 0, dataForAes, 2, seedBytes.Length);
|
||||||
|
|
||||||
|
byte[] derivedBytes;
|
||||||
|
using (var hmac = new HMACSHA256(key.HmacKey))
|
||||||
|
{
|
||||||
|
derivedBytes = hmac.ComputeHash(dataForAes);
|
||||||
|
}
|
||||||
|
|
||||||
|
derivedAesKey = derivedBytes.Take(16).ToArray();
|
||||||
|
derivedAesIv = derivedBytes.Skip(16).Take(16).ToArray();
|
||||||
|
|
||||||
|
// Derive HMAC Key
|
||||||
|
var dataForHmacKey = new byte[2 + seedBytes.Length];
|
||||||
|
dataForHmacKey[0] = 0x00;
|
||||||
|
dataForHmacKey[1] = 0x01; // Counter (1)
|
||||||
|
Array.Copy(seedBytes, 0, dataForHmacKey, 2, seedBytes.Length);
|
||||||
|
|
||||||
|
using (var hmac = new HMACSHA256(key.HmacKey))
|
||||||
|
{
|
||||||
|
derivedBytes = hmac.ComputeHash(dataForHmacKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
hmacKey = derivedBytes.Take(16).ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Derive HMAC Key only
|
||||||
|
var dataForHmacKey = new byte[2 + seedBytes.Length];
|
||||||
|
dataForHmacKey[0] = 0x00;
|
||||||
|
dataForHmacKey[1] = 0x01; // Counter (1)
|
||||||
|
Array.Copy(seedBytes, 0, dataForHmacKey, 2, seedBytes.Length);
|
||||||
|
|
||||||
|
byte[] derivedBytes;
|
||||||
|
using (var hmac = new HMACSHA256(key.HmacKey))
|
||||||
|
{
|
||||||
|
derivedBytes = hmac.ComputeHash(dataForHmacKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
hmacKey = derivedBytes.Take(16).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hmacKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeriveKeysAndCipher()
|
||||||
|
{
|
||||||
|
byte[] discard;
|
||||||
|
// Derive HMAC Tag Key
|
||||||
|
this.hmacTagKey = DeriveKey(this.tagMasterKey, false, out discard, out discard);
|
||||||
|
|
||||||
|
// Derive HMAC Data Key and AES Key/IV
|
||||||
|
this.hmacDataKey = DeriveKey(this.dataMasterKey, true, out aesKey, out aesIv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DecryptData()
|
||||||
|
{
|
||||||
|
byte[] encryptedBlock = new byte[0x020 + 0x168];
|
||||||
|
Array.Copy(data, 0x014, encryptedBlock, 0, 0x020); // data[0x014:0x034]
|
||||||
|
Array.Copy(data, 0x0A0, encryptedBlock, 0x020, 0x168); // data[0x0A0:0x208]
|
||||||
|
|
||||||
|
byte[] decryptedBlock = AES_CTR_Transform(encryptedBlock, aesKey, aesIv);
|
||||||
|
|
||||||
|
// Copy decrypted data back
|
||||||
|
Array.Copy(decryptedBlock, 0, data, 0x014, 0x020);
|
||||||
|
Array.Copy(decryptedBlock, 0x020, data, 0x0A0, 0x168);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EncryptData()
|
||||||
|
{
|
||||||
|
byte[] plainBlock = new byte[0x020 + 0x168];
|
||||||
|
Array.Copy(data, 0x014, plainBlock, 0, 0x020); // data[0x014:0x034]
|
||||||
|
Array.Copy(data, 0x0A0, plainBlock, 0x020, 0x168); // data[0x0A0:0x208]
|
||||||
|
|
||||||
|
byte[] encryptedBlock = AES_CTR_Transform(plainBlock, aesKey, aesIv);
|
||||||
|
|
||||||
|
// Copy encrypted data back
|
||||||
|
Array.Copy(encryptedBlock, 0, data, 0x014, 0x020);
|
||||||
|
Array.Copy(encryptedBlock, 0x020, data, 0x0A0, 0x168);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] AES_CTR_Transform(byte[] data, byte[] key, byte[] iv)
|
||||||
|
{
|
||||||
|
byte[] output = new byte[data.Length];
|
||||||
|
|
||||||
|
using (Aes aes = Aes.Create())
|
||||||
|
{
|
||||||
|
aes.Key = key;
|
||||||
|
aes.Mode = CipherMode.ECB;
|
||||||
|
aes.Padding = PaddingMode.None;
|
||||||
|
|
||||||
|
int blockSize = aes.BlockSize / 8; // in bytes, should be 16
|
||||||
|
byte[] counter = new byte[blockSize];
|
||||||
|
Array.Copy(iv, counter, blockSize);
|
||||||
|
|
||||||
|
using (ICryptoTransform encryptor = aes.CreateEncryptor())
|
||||||
|
{
|
||||||
|
byte[] encryptedCounter = new byte[blockSize];
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i += blockSize)
|
||||||
|
{
|
||||||
|
// Encrypt the counter
|
||||||
|
encryptor.TransformBlock(counter, 0, blockSize, encryptedCounter, 0);
|
||||||
|
|
||||||
|
// Determine the number of bytes to process in this block
|
||||||
|
int blockLength = Math.Min(blockSize, data.Length - i);
|
||||||
|
|
||||||
|
// XOR the encrypted counter with the plaintext/ciphertext block
|
||||||
|
for (int j = 0; j < blockLength; j++)
|
||||||
|
{
|
||||||
|
output[i + j] = (byte)(data[i + j] ^ encryptedCounter[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the counter
|
||||||
|
IncrementCounter(counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IncrementCounter(byte[] counter)
|
||||||
|
{
|
||||||
|
for (int i = counter.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (++counter[i] != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeriveHMACs()
|
||||||
|
{
|
||||||
|
if (isLocked)
|
||||||
|
throw new InvalidOperationException("Cannot derive HMACs when data is locked.");
|
||||||
|
|
||||||
|
// Calculate tag HMAC
|
||||||
|
byte[] tagHmacData = new byte[8 + 44];
|
||||||
|
Array.Copy(data, 0x000, tagHmacData, 0, 8);
|
||||||
|
Array.Copy(data, 0x054, tagHmacData, 8, 44);
|
||||||
|
|
||||||
|
byte[] tagHmac;
|
||||||
|
using (var hmac = new HMACSHA256(hmacTagKey))
|
||||||
|
{
|
||||||
|
tagHmac = hmac.ComputeHash(tagHmacData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite the stored tag HMAC
|
||||||
|
Array.Copy(tagHmac, 0, data, 0x034, 32);
|
||||||
|
|
||||||
|
// Prepare data for data HMAC
|
||||||
|
int len1 = 0x023; // 0x011 to 0x034 (0x034 - 0x011)
|
||||||
|
int len2 = 0x168; // 0x0A0 to 0x208 (0x208 - 0x0A0)
|
||||||
|
int len3 = tagHmac.Length; // 32 bytes
|
||||||
|
int len4 = 0x008; // 0x000 to 0x008 (0x008 - 0x000)
|
||||||
|
int len5 = 0x02C; // 0x054 to 0x080 (0x080 - 0x054)
|
||||||
|
int totalLength = len1 + len2 + len3 + len4 + len5;
|
||||||
|
byte[] dataHmacData = new byte[totalLength];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
Array.Copy(data, 0x011, dataHmacData, offset, len1);
|
||||||
|
offset += len1;
|
||||||
|
Array.Copy(data, 0x0A0, dataHmacData, offset, len2);
|
||||||
|
offset += len2;
|
||||||
|
Array.Copy(tagHmac, 0, dataHmacData, offset, len3);
|
||||||
|
offset += len3;
|
||||||
|
Array.Copy(data, 0x000, dataHmacData, offset, len4);
|
||||||
|
offset += len4;
|
||||||
|
Array.Copy(data, 0x054, dataHmacData, offset, len5);
|
||||||
|
|
||||||
|
byte[] dataHmac;
|
||||||
|
using (var hmac = new HMACSHA256(hmacDataKey))
|
||||||
|
{
|
||||||
|
dataHmac = hmac.ComputeHash(dataHmacData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite the stored data HMAC
|
||||||
|
Array.Copy(dataHmac, 0, data, 0x080, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VerifyHMACs()
|
||||||
|
{
|
||||||
|
if (isLocked)
|
||||||
|
throw new InvalidOperationException("Cannot verify HMACs when data is locked.");
|
||||||
|
|
||||||
|
// Calculate tag HMAC
|
||||||
|
byte[] tagHmacData = new byte[8 + 44];
|
||||||
|
Array.Copy(data, 0x000, tagHmacData, 0, 8);
|
||||||
|
Array.Copy(data, 0x054, tagHmacData, 8, 44);
|
||||||
|
|
||||||
|
byte[] calculatedTagHmac;
|
||||||
|
using (var hmac = new HMACSHA256(hmacTagKey))
|
||||||
|
{
|
||||||
|
calculatedTagHmac = hmac.ComputeHash(tagHmacData);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] storedTagHmac = new byte[32];
|
||||||
|
Array.Copy(data, 0x034, storedTagHmac, 0, 32);
|
||||||
|
|
||||||
|
if (!calculatedTagHmac.SequenceEqual(storedTagHmac))
|
||||||
|
{
|
||||||
|
throw new Exception("Tag HMAC verification failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare data for data HMAC
|
||||||
|
int len1 = 0x023; // 0x011 to 0x034
|
||||||
|
int len2 = 0x168; // 0x0A0 to 0x208
|
||||||
|
int len3 = calculatedTagHmac.Length; // 32 bytes
|
||||||
|
int len4 = 0x008; // 0x000 to 0x008
|
||||||
|
int len5 = 0x02C; // 0x054 to 0x080
|
||||||
|
int totalLength = len1 + len2 + len3 + len4 + len5;
|
||||||
|
byte[] dataHmacData = new byte[totalLength];
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
Array.Copy(data, 0x011, dataHmacData, offset, len1);
|
||||||
|
offset += len1;
|
||||||
|
Array.Copy(data, 0x0A0, dataHmacData, offset, len2);
|
||||||
|
offset += len2;
|
||||||
|
Array.Copy(calculatedTagHmac, 0, dataHmacData, offset, len3);
|
||||||
|
offset += len3;
|
||||||
|
Array.Copy(data, 0x000, dataHmacData, offset, len4);
|
||||||
|
offset += len4;
|
||||||
|
Array.Copy(data, 0x054, dataHmacData, offset, len5);
|
||||||
|
|
||||||
|
byte[] calculatedDataHmac;
|
||||||
|
using (var hmac = new HMACSHA256(hmacDataKey))
|
||||||
|
{
|
||||||
|
calculatedDataHmac = hmac.ComputeHash(dataHmacData);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] storedDataHmac = new byte[32];
|
||||||
|
Array.Copy(data, 0x080, storedDataHmac, 0, 32);
|
||||||
|
|
||||||
|
if (!calculatedDataHmac.SequenceEqual(storedDataHmac))
|
||||||
|
{
|
||||||
|
throw new Exception("Data HMAC verification failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unlock()
|
||||||
|
{
|
||||||
|
if (!isLocked)
|
||||||
|
throw new InvalidOperationException("Data is already unlocked.");
|
||||||
|
|
||||||
|
// Derive keys and cipher
|
||||||
|
DeriveKeysAndCipher();
|
||||||
|
|
||||||
|
// Decrypt the encrypted data
|
||||||
|
DecryptData();
|
||||||
|
|
||||||
|
isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Lock()
|
||||||
|
{
|
||||||
|
if (isLocked)
|
||||||
|
throw new InvalidOperationException("Data is already locked.");
|
||||||
|
|
||||||
|
// Recalculate HMACs
|
||||||
|
DeriveHMACs();
|
||||||
|
|
||||||
|
// Encrypt the data
|
||||||
|
EncryptData();
|
||||||
|
|
||||||
|
isLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] GetData()
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Property to get or set Amiibo nickname
|
||||||
|
public string AmiiboNickname
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// data[0x020:0x034], big endian UTF-16
|
||||||
|
byte[] nicknameBytes = new byte[0x014];
|
||||||
|
Array.Copy(data, 0x020, nicknameBytes, 0, 0x014);
|
||||||
|
string nickname = System.Text.Encoding.BigEndianUnicode.GetString(nicknameBytes).TrimEnd('\0');
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
byte[] nicknameBytes = System.Text.Encoding.BigEndianUnicode.GetBytes(value.PadRight(10, '\0'));
|
||||||
|
if (nicknameBytes.Length > 20)
|
||||||
|
throw new ArgumentException("Nickname too long.");
|
||||||
|
Array.Copy(nicknameBytes, 0, data, 0x020, nicknameBytes.Length);
|
||||||
|
// Pad remaining bytes with zeros
|
||||||
|
for (int i = 0x020 + nicknameBytes.Length; i < 0x034; i++)
|
||||||
|
{
|
||||||
|
data[i] = 0x00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
|
||||||
|
{
|
||||||
|
public class AmiiboMasterKey
|
||||||
|
{
|
||||||
|
public byte[] HmacKey { get; private set; } // 16 bytes
|
||||||
|
public byte[] TypeString { get; private set; } // 14 bytes
|
||||||
|
public byte Rfu { get; private set; } // 1 byte
|
||||||
|
public byte MagicSize { get; private set; } // 1 byte
|
||||||
|
public byte[] MagicBytes { get; private set; } // 16 bytes
|
||||||
|
public byte[] XorPad { get; private set; } // 32 bytes
|
||||||
|
|
||||||
|
public AmiiboMasterKey(byte[] data)
|
||||||
|
{
|
||||||
|
if (data.Length != 80)
|
||||||
|
throw new ArgumentException("Master key data must be 80 bytes.");
|
||||||
|
|
||||||
|
HmacKey = data.Take(16).ToArray();
|
||||||
|
TypeString = data.Skip(16).Take(14).ToArray();
|
||||||
|
Rfu = data[30];
|
||||||
|
MagicSize = data[31];
|
||||||
|
MagicBytes = data.Skip(32).Take(16).ToArray();
|
||||||
|
XorPad = data.Skip(48).Take(32).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (AmiiboMasterKey DataKey, AmiiboMasterKey TagKey) FromCombinedBin(byte[] combinedBin)
|
||||||
|
{
|
||||||
|
if (combinedBin.Length != 160)
|
||||||
|
throw new ArgumentException($"Data is {combinedBin.Length} bytes (should be 160).");
|
||||||
|
|
||||||
|
byte[] dataBin = combinedBin.Take(80).ToArray();
|
||||||
|
byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray();
|
||||||
|
|
||||||
|
AmiiboMasterKey dataKey = new AmiiboMasterKey(dataBin);
|
||||||
|
AmiiboMasterKey tagKey = new AmiiboMasterKey(tagBin);
|
||||||
|
|
||||||
|
return (dataKey, tagKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,7 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
if (_state == State.Initialized)
|
if (_state == State.Initialized)
|
||||||
{
|
{
|
||||||
_cancelTokenSource?.Cancel();
|
_cancelTokenSource?.Cancel();
|
||||||
|
|
||||||
// NOTE: All events are destroyed here.
|
// NOTE: All events are destroyed here.
|
||||||
context.Device.System.NfpDevices.Clear();
|
context.Device.System.NfpDevices.Clear();
|
||||||
|
|
||||||
|
@ -146,9 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_cancelTokenSource = new CancellationTokenSource();
|
_cancelTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -199,7 +196,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Found how the MountTarget is handled.
|
// TODO: Found how the MountTarget is handled.
|
||||||
|
|
||||||
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
|
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
|
||||||
{
|
{
|
||||||
if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
|
if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
|
||||||
|
@ -488,14 +483,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
||||||
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||||
#pragma warning restore IDE0059
|
#pragma warning restore IDE0059
|
||||||
|
|
||||||
if (context.Device.System.NfpDevices.Count == 0)
|
if (context.Device.System.NfpDevices.Count == 0)
|
||||||
{
|
{
|
||||||
return ResultCode.DeviceNotFound;
|
return ResultCode.DeviceNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case.
|
// NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case.
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,7 +877,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.DeviceNotFound;
|
return ResultCode.DeviceNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
|
||||||
{
|
{
|
||||||
struct VirtualAmiiboFile
|
public struct VirtualAmiiboFile
|
||||||
{
|
{
|
||||||
public uint FileVersion { get; set; }
|
public uint FileVersion { get; set; }
|
||||||
public byte[] TagUuid { get; set; }
|
public byte[] TagUuid { get; set; }
|
||||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
|
||||||
public List<VirtualAmiiboApplicationArea> ApplicationAreas { get; set; }
|
public List<VirtualAmiiboApplicationArea> ApplicationAreas { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VirtualAmiiboApplicationArea
|
public struct VirtualAmiiboApplicationArea
|
||||||
{
|
{
|
||||||
public uint ApplicationAreaId { get; set; }
|
public uint ApplicationAreaId { get; set; }
|
||||||
public byte[] ApplicationArea { get; set; }
|
public byte[] ApplicationArea { get; set; }
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Mii;
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -14,10 +15,11 @@ 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];
|
||||||
|
public static string InputBin = string.Empty;
|
||||||
|
public static string NickName = string.Empty;
|
||||||
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)
|
||||||
|
@ -69,6 +71,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
{
|
{
|
||||||
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
|
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
|
||||||
string nickname = amiiboFile.NickName ?? "Ryujinx";
|
string nickname = amiiboFile.NickName ?? "Ryujinx";
|
||||||
|
if (NickName != string.Empty)
|
||||||
|
{
|
||||||
|
nickname = NickName;
|
||||||
|
NickName = string.Empty;
|
||||||
|
}
|
||||||
UtilityImpl utilityImpl = new(tickSource);
|
UtilityImpl utilityImpl = new(tickSource);
|
||||||
CharInfo charInfo = new();
|
CharInfo charInfo = new();
|
||||||
|
|
||||||
|
@ -98,16 +105,26 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
{
|
{
|
||||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||||
virtualAmiiboFile.NickName = newNickName;
|
virtualAmiiboFile.NickName = newNickName;
|
||||||
|
if (InputBin != string.Empty)
|
||||||
|
{
|
||||||
|
AmiiboBinReader.SaveBinFile(InputBin, virtualAmiiboFile.NickName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
SaveAmiiboFile(virtualAmiiboFile);
|
SaveAmiiboFile(virtualAmiiboFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.Any(item => item.ApplicationAreaId == applicationAreaId))
|
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId))
|
||||||
{
|
{
|
||||||
_openedApplicationAreaId = applicationAreaId;
|
OpenedApplicationAreaId = applicationAreaId;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -117,11 +134,17 @@ 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)
|
||||||
{
|
{
|
||||||
if (applicationArea.ApplicationAreaId == _openedApplicationAreaId)
|
if (applicationArea.ApplicationAreaId == OpenedApplicationAreaId)
|
||||||
{
|
{
|
||||||
return applicationArea.ApplicationArea;
|
return applicationArea.ApplicationArea;
|
||||||
}
|
}
|
||||||
|
@ -152,17 +175,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
|
|
||||||
public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData)
|
public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData)
|
||||||
{
|
{
|
||||||
|
if (InputBin != string.Empty)
|
||||||
|
{
|
||||||
|
AmiiboBinReader.SaveBinFile(InputBin, applicationAreaData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||||
|
|
||||||
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == _openedApplicationAreaId))
|
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == OpenedApplicationAreaId))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++)
|
for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++)
|
||||||
{
|
{
|
||||||
if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == _openedApplicationAreaId)
|
if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == OpenedApplicationAreaId)
|
||||||
{
|
{
|
||||||
virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea()
|
virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea()
|
||||||
{
|
{
|
||||||
ApplicationAreaId = _openedApplicationAreaId,
|
ApplicationAreaId = OpenedApplicationAreaId,
|
||||||
ApplicationArea = applicationAreaData,
|
ApplicationArea = applicationAreaData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -205,10 +233,21 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||||
return virtualAmiiboFile;
|
return virtualAmiiboFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile)
|
public static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
{
|
||||||
|
if (InputBin != string.Empty)
|
||||||
|
{
|
||||||
|
SaveAmiiboFile(virtualAmiiboFile);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
return File.Exists(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,13 +82,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
// Apply Nsos patches.
|
// Apply Nsos patches.
|
||||||
device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables);
|
device.Configuration.VirtualFileSystem.ModLoader.ApplyNsoPatches(programId, nsoExecutables);
|
||||||
|
|
||||||
// Don't use PTC if ExeFS files have been replaced.
|
|
||||||
bool enablePtc = device.System.EnablePtc && !modLoadResult.Modified;
|
|
||||||
if (!enablePtc)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
string programName = string.Empty;
|
string programName = string.Empty;
|
||||||
|
|
||||||
if (!isHomebrew && programId > 0x010000000000FFFF)
|
if (!isHomebrew && programId > 0x010000000000FFFF)
|
||||||
|
@ -115,7 +108,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
device.System.KernelContext,
|
device.System.KernelContext,
|
||||||
metaLoader,
|
metaLoader,
|
||||||
nacpData,
|
nacpData,
|
||||||
enablePtc,
|
device.System.EnablePtc,
|
||||||
|
modLoadResult.Hash,
|
||||||
true,
|
true,
|
||||||
programName,
|
programName,
|
||||||
metaLoader.GetProgramId(),
|
metaLoader.GetProgramId(),
|
||||||
|
|
|
@ -212,6 +212,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
dummyExeFs.GetNpdm(),
|
dummyExeFs.GetNpdm(),
|
||||||
nacpData,
|
nacpData,
|
||||||
diskCacheEnabled: false,
|
diskCacheEnabled: false,
|
||||||
|
diskCacheSelector: null,
|
||||||
allowCodeMemoryForJit: true,
|
allowCodeMemoryForJit: true,
|
||||||
programName,
|
programName,
|
||||||
programId,
|
programId,
|
||||||
|
|
|
@ -186,6 +186,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
string.Empty,
|
string.Empty,
|
||||||
string.Empty,
|
string.Empty,
|
||||||
false,
|
false,
|
||||||
|
null,
|
||||||
codeAddress,
|
codeAddress,
|
||||||
codeSize);
|
codeSize);
|
||||||
|
|
||||||
|
@ -226,6 +227,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
MetaLoader metaLoader,
|
MetaLoader metaLoader,
|
||||||
BlitStruct<ApplicationControlProperty> applicationControlProperties,
|
BlitStruct<ApplicationControlProperty> applicationControlProperties,
|
||||||
bool diskCacheEnabled,
|
bool diskCacheEnabled,
|
||||||
|
string diskCacheSelector,
|
||||||
bool allowCodeMemoryForJit,
|
bool allowCodeMemoryForJit,
|
||||||
string name,
|
string name,
|
||||||
ulong programId,
|
ulong programId,
|
||||||
|
@ -379,6 +381,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
$"{programId:x16}",
|
$"{programId:x16}",
|
||||||
displayVersion,
|
displayVersion,
|
||||||
diskCacheEnabled,
|
diskCacheEnabled,
|
||||||
|
diskCacheSelector,
|
||||||
codeStart,
|
codeStart,
|
||||||
codeSize);
|
codeSize);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_الإجراءات",
|
"MenuBarActions": "_الإجراءات",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ",
|
"MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ",
|
||||||
"MenuBarActionsScanAmiibo": "فحص Amiibo",
|
"MenuBarActionsScanAmiibo": "فحص Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_الأدوات",
|
"MenuBarTools": "_الأدوات",
|
||||||
"MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت",
|
"MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "إدارة ذاكرة التخزين المؤقت",
|
"GameListContextMenuCacheManagement": "إدارة ذاكرة التخزين المؤقت",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "قائمة انتظار إعادة بناء الـPPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "قائمة انتظار إعادة بناء الـPPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "تنشيط PPTC لإعادة البناء في وقت الإقلاع عند بدء تشغيل اللعبة التالي",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "تنشيط PPTC لإعادة البناء في وقت الإقلاع عند بدء تشغيل اللعبة التالي",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "يحذف ذاكرة مرشحات الفيديو المؤقتة الخاصة بالتطبيق",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "يحذف ذاكرة مرشحات الفيديو المؤقتة الخاصة بالتطبيق",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "فتح مجلد PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "فتح مجلد PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "تحذير",
|
"DialogWarning": "تحذير",
|
||||||
"DialogPPTCDeletionMessage": "أنت على وشك الإنتظار لإعادة بناء ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) عند الإقلاع التالي لـ:\n\n{0}\n\nأمتأكد من رغبتك في المتابعة؟",
|
"DialogPPTCDeletionMessage": "أنت على وشك الإنتظار لإعادة بناء ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) عند الإقلاع التالي لـ:\n\n{0}\n\nأمتأكد من رغبتك في المتابعة؟",
|
||||||
"DialogPPTCDeletionErrorMessage": "خطأ خلال تنظيف ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) في {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "خطأ خلال تنظيف ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) في {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "أنت على وشك حذف ذاكرة المظللات المؤقتة ل:\n\n{0}\n\nهل انت متأكد انك تريد المتابعة؟",
|
"DialogShaderDeletionMessage": "أنت على وشك حذف ذاكرة المظللات المؤقتة ل:\n\n{0}\n\nهل انت متأكد انك تريد المتابعة؟",
|
||||||
"DialogShaderDeletionErrorMessage": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "واجه ريوجينكس خطأ",
|
"DialogRyujinxErrorMessage": "واجه ريوجينكس خطأ",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Aktionen",
|
"MenuBarActions": "_Aktionen",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Aufwachnachricht simulieren",
|
"MenuBarOptionsSimulateWakeUpMessage": "Aufwachnachricht simulieren",
|
||||||
"MenuBarActionsScanAmiibo": "Amiibo scannen",
|
"MenuBarActionsScanAmiibo": "Amiibo scannen",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Tools",
|
"MenuBarTools": "_Tools",
|
||||||
"MenuBarToolsInstallFirmware": "Firmware installieren",
|
"MenuBarToolsInstallFirmware": "Firmware installieren",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Firmware von einer XCI- oder einer ZIP-Datei installieren",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Firmware von einer XCI- oder einer ZIP-Datei installieren",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Cache-Verwaltung",
|
"GameListContextMenuCacheManagement": "Cache-Verwaltung",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "PPTC als ungültig markieren",
|
"GameListContextMenuCacheManagementPurgePptc": "PPTC als ungültig markieren",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Markiert den PPTC als ungültig, sodass dieser beim nächsten Spielstart neu erstellt wird",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Markiert den PPTC als ungültig, sodass dieser beim nächsten Spielstart neu erstellt wird",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Shader Cache löschen",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Shader Cache löschen",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Löscht den Shader-Cache der Anwendung",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Löscht den Shader-Cache der Anwendung",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC-Verzeichnis öffnen",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC-Verzeichnis öffnen",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Warnung",
|
"DialogWarning": "Warnung",
|
||||||
"DialogPPTCDeletionMessage": "Du bist dabei den PPTC für das folgende Spiel als ungültig zu markieren:\n\n{0}\n\nWirklich fortfahren?",
|
"DialogPPTCDeletionMessage": "Du bist dabei den PPTC für das folgende Spiel als ungültig zu markieren:\n\n{0}\n\nWirklich fortfahren?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Fehler bei der Löschung des PPTC Caches bei {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Fehler bei der Löschung des PPTC Caches bei {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Du bist dabei, den Shader Cache zu löschen für :\n\n{0}\n\nWirklich fortfahren?",
|
"DialogShaderDeletionMessage": "Du bist dabei, den Shader Cache zu löschen für :\n\n{0}\n\nWirklich fortfahren?",
|
||||||
"DialogShaderDeletionErrorMessage": "Es ist ein Fehler bei der Löschung des Shader Caches bei {0}: {1} aufgetreten",
|
"DialogShaderDeletionErrorMessage": "Es ist ein Fehler bei der Löschung des Shader Caches bei {0}: {1} aufgetreten",
|
||||||
"DialogRyujinxErrorMessage": "Ein Fehler ist aufgetreten",
|
"DialogRyujinxErrorMessage": "Ein Fehler ist aufgetreten",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Δράσεις",
|
"MenuBarActions": "_Δράσεις",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Προσομοίωση Μηνύματος Αφύπνισης",
|
"MenuBarOptionsSimulateWakeUpMessage": "Προσομοίωση Μηνύματος Αφύπνισης",
|
||||||
"MenuBarActionsScanAmiibo": "Σάρωση Amiibo",
|
"MenuBarActionsScanAmiibo": "Σάρωση Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Εργαλεία",
|
"MenuBarTools": "_Εργαλεία",
|
||||||
"MenuBarToolsInstallFirmware": "Εγκατάσταση Firmware",
|
"MenuBarToolsInstallFirmware": "Εγκατάσταση Firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Εγκατάσταση Firmware από XCI ή ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Εγκατάσταση Firmware από XCI ή ZIP",
|
||||||
|
@ -76,6 +77,8 @@
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Εκκαθάριση Προσωρινής Μνήμης PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Εκκαθάριση Προσωρινής Μνήμης PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Διαγράφει την προσωρινή μνήμη PPTC της εφαρμογής",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Διαγράφει την προσωρινή μνήμη PPTC της εφαρμογής",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Εκκαθάριση Προσωρινής Μνήμης Shader",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Εκκαθάριση Προσωρινής Μνήμης Shader",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Διαγράφει την προσωρινή μνήμη Shader της εφαρμογής",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Διαγράφει την προσωρινή μνήμη Shader της εφαρμογής",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Άνοιγμα Τοποθεσίας PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Άνοιγμα Τοποθεσίας PPTC",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει τη προσωρινή μνήμη PPTC της εφαρμογής",
|
"GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει τη προσωρινή μνήμη PPTC της εφαρμογής",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Προειδοποίηση",
|
"DialogWarning": "Προειδοποίηση",
|
||||||
"DialogPPTCDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη PPTC για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
"DialogPPTCDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη PPTC για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
||||||
"DialogPPTCDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης PPTC στο {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης PPTC στο {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη Shader για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
"DialogShaderDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη Shader για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
||||||
"DialogShaderDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης Shader στο {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης Shader στο {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Το Ryujinx αντιμετώπισε σφάλμα",
|
"DialogRyujinxErrorMessage": "Το Ryujinx αντιμετώπισε σφάλμα",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Actions",
|
"MenuBarActions": "_Actions",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Simulate Wake-up message",
|
"MenuBarOptionsSimulateWakeUpMessage": "Simulate Wake-up message",
|
||||||
"MenuBarActionsScanAmiibo": "Scan An Amiibo",
|
"MenuBarActionsScanAmiibo": "Scan An Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Tools",
|
"MenuBarTools": "_Tools",
|
||||||
"MenuBarToolsInstallFirmware": "Install Firmware",
|
"MenuBarToolsInstallFirmware": "Install Firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Cache Management",
|
"GameListContextMenuCacheManagement": "Cache Management",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild",
|
"GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Purge Shader Cache",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Purge Shader Cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deletes Application's shader cache",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deletes Application's shader cache",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Open PPTC Directory",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Open PPTC Directory",
|
||||||
|
@ -516,6 +519,7 @@
|
||||||
"DialogWarning": "Warning",
|
"DialogWarning": "Warning",
|
||||||
"DialogPPTCDeletionMessage": "You are about to queue a PPTC rebuild on the next boot of:\n\n{0}\n\nAre you sure you want to proceed?",
|
"DialogPPTCDeletionMessage": "You are about to queue a PPTC rebuild on the next boot of:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Error purging PPTC cache at {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Error purging PPTC cache at {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "You are about to delete the Shader cache for :\n\n{0}\n\nAre you sure you want to proceed?",
|
"DialogShaderDeletionMessage": "You are about to delete the Shader cache for :\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx has encountered an error",
|
"DialogRyujinxErrorMessage": "Ryujinx has encountered an error",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Acciones",
|
"MenuBarActions": "_Acciones",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Simular mensaje de reactivación",
|
"MenuBarOptionsSimulateWakeUpMessage": "Simular mensaje de reactivación",
|
||||||
"MenuBarActionsScanAmiibo": "Escanear Amiibo",
|
"MenuBarActionsScanAmiibo": "Escanear Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Herramientas",
|
"MenuBarTools": "_Herramientas",
|
||||||
"MenuBarToolsInstallFirmware": "Instalar firmware",
|
"MenuBarToolsInstallFirmware": "Instalar firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware desde un archivo XCI o ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware desde un archivo XCI o ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Gestión de caché ",
|
"GameListContextMenuCacheManagement": "Gestión de caché ",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Reconstruir PPTC en cola",
|
"GameListContextMenuCacheManagementPurgePptc": "Reconstruir PPTC en cola",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Elimina la caché de PPTC de esta aplicación",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Elimina la caché de PPTC de esta aplicación",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Limpiar caché de sombreadores",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Limpiar caché de sombreadores",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Eliminar la caché de sombreadores de esta aplicación",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Eliminar la caché de sombreadores de esta aplicación",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir carpeta de PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir carpeta de PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Advertencia",
|
"DialogWarning": "Advertencia",
|
||||||
"DialogPPTCDeletionMessage": "Vas a borrar la caché de PPTC para:\n\n{0}\n\n¿Estás seguro de querer continuar?",
|
"DialogPPTCDeletionMessage": "Vas a borrar la caché de PPTC para:\n\n{0}\n\n¿Estás seguro de querer continuar?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Error purgando la caché de PPTC en {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Error purgando la caché de PPTC en {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Vas a borrar la caché de sombreadores para:\n\n{0}\n\n¿Estás seguro de querer continuar?",
|
"DialogShaderDeletionMessage": "Vas a borrar la caché de sombreadores para:\n\n{0}\n\n¿Estás seguro de querer continuar?",
|
||||||
"DialogShaderDeletionErrorMessage": "Error purgando la caché de sombreadores en {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Error purgando la caché de sombreadores en {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx ha encontrado un error",
|
"DialogRyujinxErrorMessage": "Ryujinx ha encontrado un error",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Actions",
|
"MenuBarActions": "_Actions",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Simuler un message de réveil",
|
"MenuBarOptionsSimulateWakeUpMessage": "Simuler un message de réveil",
|
||||||
"MenuBarActionsScanAmiibo": "Scanner un Amiibo",
|
"MenuBarActionsScanAmiibo": "Scanner un Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Outils",
|
"MenuBarTools": "_Outils",
|
||||||
"MenuBarToolsInstallFirmware": "Installer un firmware",
|
"MenuBarToolsInstallFirmware": "Installer un firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Installer un firmware depuis un fichier XCI ou ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Installer un firmware depuis un fichier XCI ou ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Gestion des caches",
|
"GameListContextMenuCacheManagement": "Gestion des caches",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Reconstruction du PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Reconstruction du PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Effectuer une reconstruction du PPTC au prochain démarrage du jeu",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Effectuer une reconstruction du PPTC au prochain démarrage du jeu",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Purger les shaders",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Purger les shaders",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Supprime les shaders du jeu",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Supprime les shaders du jeu",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Ouvrir le dossier du PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Ouvrir le dossier du PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Avertissement",
|
"DialogWarning": "Avertissement",
|
||||||
"DialogPPTCDeletionMessage": "Vous êtes sur le point de mettre en file d'attente une reconstruction PPTC au prochain démarrage de :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?",
|
"DialogPPTCDeletionMessage": "Vous êtes sur le point de mettre en file d'attente une reconstruction PPTC au prochain démarrage de :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Erreur lors de la purge du cache PPTC à {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Erreur lors de la purge du cache PPTC à {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Vous êtes sur le point de supprimer le cache du Shader pour :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?",
|
"DialogShaderDeletionMessage": "Vous êtes sur le point de supprimer le cache du Shader pour :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?",
|
||||||
"DialogShaderDeletionErrorMessage": "Erreur lors de la purge du cache du Shader à {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Erreur lors de la purge du cache du Shader à {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx a rencontré une erreur",
|
"DialogRyujinxErrorMessage": "Ryujinx a rencontré une erreur",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_פעולות",
|
"MenuBarActions": "_פעולות",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "דמה הודעת השכמה",
|
"MenuBarOptionsSimulateWakeUpMessage": "דמה הודעת השכמה",
|
||||||
"MenuBarActionsScanAmiibo": "סרוק אמיבו",
|
"MenuBarActionsScanAmiibo": "סרוק אמיבו",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_כלים",
|
"MenuBarTools": "_כלים",
|
||||||
"MenuBarToolsInstallFirmware": "התקן קושחה",
|
"MenuBarToolsInstallFirmware": "התקן קושחה",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "התקן קושחה מקובץ- ZIP/XCI",
|
"MenuBarFileToolsInstallFirmwareFromFile": "התקן קושחה מקובץ- ZIP/XCI",
|
||||||
|
@ -74,6 +75,8 @@
|
||||||
"GameListContextMenuManageDlcToolTip": "פותח את חלון מנהל הרחבות המשחקים",
|
"GameListContextMenuManageDlcToolTip": "פותח את חלון מנהל הרחבות המשחקים",
|
||||||
"GameListContextMenuCacheManagement": "ניהול מטמון",
|
"GameListContextMenuCacheManagement": "ניהול מטמון",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "הוסף PPTC לתור בנייה מחדש",
|
"GameListContextMenuCacheManagementPurgePptc": "הוסף PPTC לתור בנייה מחדש",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "גרום ל-PPTC להבנות מחדש בפתיחה הבאה של המשחק",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "גרום ל-PPTC להבנות מחדש בפתיחה הבאה של המשחק",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "ניקוי מטמון הצללות",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "ניקוי מטמון הצללות",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "מוחק את מטמון ההצללות של היישום",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "מוחק את מטמון ההצללות של היישום",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "אזהרה",
|
"DialogWarning": "אזהרה",
|
||||||
"DialogPPTCDeletionMessage": "אם תמשיכו אתם עומדים לגרום לבנייה מחדש של מטמון ה-PPTC עבור:\n\n{0}",
|
"DialogPPTCDeletionMessage": "אם תמשיכו אתם עומדים לגרום לבנייה מחדש של מטמון ה-PPTC עבור:\n\n{0}",
|
||||||
"DialogPPTCDeletionErrorMessage": "שגיאה בטיהור מטמון PPTC ב-{0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "שגיאה בטיהור מטמון PPTC ב-{0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "אם תמשיכו אתם עומדים למחוק את מטמון ההצללות עבור:\n\n{0}",
|
"DialogShaderDeletionMessage": "אם תמשיכו אתם עומדים למחוק את מטמון ההצללות עבור:\n\n{0}",
|
||||||
"DialogShaderDeletionErrorMessage": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}",
|
"DialogShaderDeletionErrorMessage": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "ריוג'ינקס נתקלה בשגיאה",
|
"DialogRyujinxErrorMessage": "ריוג'ינקס נתקלה בשגיאה",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"MenuBarActions": "_Azioni",
|
"MenuBarActions": "_Azioni",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Simula messaggio Wake-up",
|
"MenuBarOptionsSimulateWakeUpMessage": "Simula messaggio Wake-up",
|
||||||
"MenuBarActionsScanAmiibo": "Scansiona un Amiibo",
|
"MenuBarActionsScanAmiibo": "Scansiona un Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Strumenti",
|
"MenuBarTools": "_Strumenti",
|
||||||
"MenuBarToolsInstallFirmware": "Installa firmware",
|
"MenuBarToolsInstallFirmware": "Installa firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Installa un firmware da file XCI o ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Installa un firmware da file XCI o ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Gestione della cache",
|
"GameListContextMenuCacheManagement": "Gestione della cache",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Accoda rigenerazione della cache PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Accoda rigenerazione della cache PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Esegue la rigenerazione della cache PPTC al prossimo avvio del gioco",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Esegue la rigenerazione della cache PPTC al prossimo avvio del gioco",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Elimina la cache degli shader",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Elimina la cache degli shader",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Elimina la cache degli shader dell'applicazione",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Elimina la cache degli shader dell'applicazione",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Apri la cartella della cache PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Apri la cartella della cache PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Avviso",
|
"DialogWarning": "Avviso",
|
||||||
"DialogPPTCDeletionMessage": "Stai per accodare la rigenerazione della cache PPTC al prossimo avvio per:\n\n{0}\n\nSei sicuro di voler proseguire?",
|
"DialogPPTCDeletionMessage": "Stai per accodare la rigenerazione della cache PPTC al prossimo avvio per:\n\n{0}\n\nSei sicuro di voler proseguire?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Errore nell'eliminazione della cache PPTC a {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Errore nell'eliminazione della cache PPTC a {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Stai per eliminare la cache degli shader per:\n\n{0}\n\nSei sicuro di voler proseguire?",
|
"DialogShaderDeletionMessage": "Stai per eliminare la cache degli shader per:\n\n{0}\n\nSei sicuro di voler proseguire?",
|
||||||
"DialogShaderDeletionErrorMessage": "Errore nell'eliminazione della cache degli shader a {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Errore nell'eliminazione della cache degli shader a {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx ha incontrato un errore",
|
"DialogRyujinxErrorMessage": "Ryujinx ha incontrato un errore",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "アクション(_A)",
|
"MenuBarActions": "アクション(_A)",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "スリープ復帰メッセージをシミュレート",
|
"MenuBarOptionsSimulateWakeUpMessage": "スリープ復帰メッセージをシミュレート",
|
||||||
"MenuBarActionsScanAmiibo": "Amiibo をスキャン",
|
"MenuBarActionsScanAmiibo": "Amiibo をスキャン",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "ツール(_T)",
|
"MenuBarTools": "ツール(_T)",
|
||||||
"MenuBarToolsInstallFirmware": "ファームウェアをインストール",
|
"MenuBarToolsInstallFirmware": "ファームウェアをインストール",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "XCI または ZIP からファームウェアをインストール",
|
"MenuBarFileToolsInstallFirmwareFromFile": "XCI または ZIP からファームウェアをインストール",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "キャッシュ管理",
|
"GameListContextMenuCacheManagement": "キャッシュ管理",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "PPTC を再構築",
|
"GameListContextMenuCacheManagementPurgePptc": "PPTC を再構築",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "次回のゲーム起動時に PPTC を再構築します",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "次回のゲーム起動時に PPTC を再構築します",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "シェーダーキャッシュを破棄",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "シェーダーキャッシュを破棄",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "アプリケーションのシェーダーキャッシュを破棄します",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "アプリケーションのシェーダーキャッシュを破棄します",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC ディレクトリを開く",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC ディレクトリを開く",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "警告",
|
"DialogWarning": "警告",
|
||||||
"DialogPPTCDeletionMessage": "次回起動時に PPTC を再構築します:\n\n{0}\n\n実行してよろしいですか?",
|
"DialogPPTCDeletionMessage": "次回起動時に PPTC を再構築します:\n\n{0}\n\n実行してよろしいですか?",
|
||||||
"DialogPPTCDeletionErrorMessage": "PPTC キャッシュ破棄エラー {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "PPTC キャッシュ破棄エラー {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "シェーダーキャッシュを破棄しようとしています:\n\n{0}\n\n実行してよろしいですか?",
|
"DialogShaderDeletionMessage": "シェーダーキャッシュを破棄しようとしています:\n\n{0}\n\n実行してよろしいですか?",
|
||||||
"DialogShaderDeletionErrorMessage": "シェーダーキャッシュ破棄エラー {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "シェーダーキャッシュ破棄エラー {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "エラーが発生しました",
|
"DialogRyujinxErrorMessage": "エラーが発生しました",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "동작(_A)",
|
"MenuBarActions": "동작(_A)",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "웨이크업 메시지 시뮬레이션",
|
"MenuBarOptionsSimulateWakeUpMessage": "웨이크업 메시지 시뮬레이션",
|
||||||
"MenuBarActionsScanAmiibo": "Amiibo 스캔",
|
"MenuBarActionsScanAmiibo": "Amiibo 스캔",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "도구(_T)",
|
"MenuBarTools": "도구(_T)",
|
||||||
"MenuBarToolsInstallFirmware": "펌웨어 설치",
|
"MenuBarToolsInstallFirmware": "펌웨어 설치",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "XCI 또는 ZIP으로 펌웨어 설치",
|
"MenuBarFileToolsInstallFirmwareFromFile": "XCI 또는 ZIP으로 펌웨어 설치",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "캐시 관리",
|
"GameListContextMenuCacheManagement": "캐시 관리",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "대기열 PPTC 재구성",
|
"GameListContextMenuCacheManagementPurgePptc": "대기열 PPTC 재구성",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "다음 게임 실행 부팅 시, PPTC를 트리거하여 다시 구성",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "다음 게임 실행 부팅 시, PPTC를 트리거하여 다시 구성",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "퍼지 셰이더 캐시",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "퍼지 셰이더 캐시",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "앱의 셰이더 캐시 삭제",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "앱의 셰이더 캐시 삭제",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC 디렉터리 열기",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC 디렉터리 열기",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "경고",
|
"DialogWarning": "경고",
|
||||||
"DialogPPTCDeletionMessage": "다음에 부팅할 때, PPTC 재구축을 대기열에 추가하려고 합니다.\n\n{0}\n\n계속하시겠습니까?",
|
"DialogPPTCDeletionMessage": "다음에 부팅할 때, PPTC 재구축을 대기열에 추가하려고 합니다.\n\n{0}\n\n계속하시겠습니까?",
|
||||||
"DialogPPTCDeletionErrorMessage": "{0}에서 PPTC 캐시를 지우는 중 오류 발생 : {1}",
|
"DialogPPTCDeletionErrorMessage": "{0}에서 PPTC 캐시를 지우는 중 오류 발생 : {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "다음 셰이더 캐시를 삭제 :\n\n{0}\n\n계속하시겠습니까?",
|
"DialogShaderDeletionMessage": "다음 셰이더 캐시를 삭제 :\n\n{0}\n\n계속하시겠습니까?",
|
||||||
"DialogShaderDeletionErrorMessage": "{0}에서 셰이더 캐시를 삭제하는 중 오류 발생 : {1}",
|
"DialogShaderDeletionErrorMessage": "{0}에서 셰이더 캐시를 삭제하는 중 오류 발생 : {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx에서 오류 발생",
|
"DialogRyujinxErrorMessage": "Ryujinx에서 오류 발생",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Akcje",
|
"MenuBarActions": "_Akcje",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Symuluj wiadomość wybudzania",
|
"MenuBarOptionsSimulateWakeUpMessage": "Symuluj wiadomość wybudzania",
|
||||||
"MenuBarActionsScanAmiibo": "Skanuj Amiibo",
|
"MenuBarActionsScanAmiibo": "Skanuj Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Narzędzia",
|
"MenuBarTools": "_Narzędzia",
|
||||||
"MenuBarToolsInstallFirmware": "Zainstaluj oprogramowanie",
|
"MenuBarToolsInstallFirmware": "Zainstaluj oprogramowanie",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Zainstaluj oprogramowanie z XCI lub ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Zainstaluj oprogramowanie z XCI lub ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Zarządzanie Cache",
|
"GameListContextMenuCacheManagement": "Zarządzanie Cache",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Zakolejkuj rekompilację PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Zakolejkuj rekompilację PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Zainicjuj Rekompilację PPTC przy następnym uruchomieniu gry",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Zainicjuj Rekompilację PPTC przy następnym uruchomieniu gry",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Wyczyść pamięć podręczną cieni",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Wyczyść pamięć podręczną cieni",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Usuwa pamięć podręczną cieni danej aplikacji",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Usuwa pamięć podręczną cieni danej aplikacji",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Otwórz katalog PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Otwórz katalog PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Uwaga",
|
"DialogWarning": "Uwaga",
|
||||||
"DialogPPTCDeletionMessage": "Masz zamiar umieścić w kolejce rekompilację PPTC przy następnym uruchomieniu:\n\n{0}\n\nCzy na pewno chcesz kontynuować?",
|
"DialogPPTCDeletionMessage": "Masz zamiar umieścić w kolejce rekompilację PPTC przy następnym uruchomieniu:\n\n{0}\n\nCzy na pewno chcesz kontynuować?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Błąd czyszczenia cache PPTC w {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Błąd czyszczenia cache PPTC w {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Zamierzasz usunąć cache Shaderów dla :\n\n{0}\n\nNa pewno chcesz kontynuować?",
|
"DialogShaderDeletionMessage": "Zamierzasz usunąć cache Shaderów dla :\n\n{0}\n\nNa pewno chcesz kontynuować?",
|
||||||
"DialogShaderDeletionErrorMessage": "Błąd czyszczenia cache Shaderów w {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Błąd czyszczenia cache Shaderów w {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx napotkał błąd",
|
"DialogRyujinxErrorMessage": "Ryujinx napotkał błąd",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Ações",
|
"MenuBarActions": "_Ações",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "_Simular mensagem de acordar console",
|
"MenuBarOptionsSimulateWakeUpMessage": "_Simular mensagem de acordar console",
|
||||||
"MenuBarActionsScanAmiibo": "Escanear um Amiibo",
|
"MenuBarActionsScanAmiibo": "Escanear um Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Ferramentas",
|
"MenuBarTools": "_Ferramentas",
|
||||||
"MenuBarToolsInstallFirmware": "_Instalar firmware",
|
"MenuBarToolsInstallFirmware": "_Instalar firmware",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware a partir de um arquivo ZIP/XCI",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware a partir de um arquivo ZIP/XCI",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Gerenciamento de cache",
|
"GameListContextMenuCacheManagement": "Gerenciamento de cache",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Limpar cache PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Limpar cache PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Deleta o cache PPTC armazenado em disco do jogo",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Deleta o cache PPTC armazenado em disco do jogo",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Limpar cache de Shader",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Limpar cache de Shader",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deleta o cache de Shader armazenado em disco do jogo",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deleta o cache de Shader armazenado em disco do jogo",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir diretório do cache PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir diretório do cache PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Alerta",
|
"DialogWarning": "Alerta",
|
||||||
"DialogPPTCDeletionMessage": "Você está prestes a apagar o cache PPTC para :\n\n{0}\n\nTem certeza que deseja continuar?",
|
"DialogPPTCDeletionMessage": "Você está prestes a apagar o cache PPTC para :\n\n{0}\n\nTem certeza que deseja continuar?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Erro apagando cache PPTC em {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Erro apagando cache PPTC em {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Você está prestes a apagar o cache de Shader para :\n\n{0}\n\nTem certeza que deseja continuar?",
|
"DialogShaderDeletionMessage": "Você está prestes a apagar o cache de Shader para :\n\n{0}\n\nTem certeza que deseja continuar?",
|
||||||
"DialogShaderDeletionErrorMessage": "Erro apagando o cache de Shader em {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Erro apagando o cache de Shader em {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx encontrou um erro",
|
"DialogRyujinxErrorMessage": "Ryujinx encontrou um erro",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Действия",
|
"MenuBarActions": "_Действия",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Имитировать сообщение пробуждения",
|
"MenuBarOptionsSimulateWakeUpMessage": "Имитировать сообщение пробуждения",
|
||||||
"MenuBarActionsScanAmiibo": "Сканировать Amiibo",
|
"MenuBarActionsScanAmiibo": "Сканировать Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Инструменты",
|
"MenuBarTools": "_Инструменты",
|
||||||
"MenuBarToolsInstallFirmware": "Установка прошивки",
|
"MenuBarToolsInstallFirmware": "Установка прошивки",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "Установить прошивку из XCI или ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "Установить прошивку из XCI или ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Управление кэшем",
|
"GameListContextMenuCacheManagement": "Управление кэшем",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "Перестроить очередь PPTC",
|
"GameListContextMenuCacheManagementPurgePptc": "Перестроить очередь PPTC",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Запускает перестройку PPTC во время следующего запуска игры.",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Запускает перестройку PPTC во время следующего запуска игры.",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Очистить кэш шейдеров",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Очистить кэш шейдеров",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Удаляет кеш шейдеров приложения",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Удаляет кеш шейдеров приложения",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "Открыть папку PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "Открыть папку PPTC",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Внимание",
|
"DialogWarning": "Внимание",
|
||||||
"DialogPPTCDeletionMessage": "Вы собираетесь перестроить кэш PPTC при следующем запуске для:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
"DialogPPTCDeletionMessage": "Вы собираетесь перестроить кэш PPTC при следующем запуске для:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Ошибка очистки кэша PPTC в {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Ошибка очистки кэша PPTC в {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Вы собираетесь удалить кэш шейдеров для:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
"DialogShaderDeletionMessage": "Вы собираетесь удалить кэш шейдеров для:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
||||||
"DialogShaderDeletionErrorMessage": "Ошибка очистки кэша шейдеров в {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Ошибка очистки кэша шейдеров в {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx обнаружил ошибку",
|
"DialogRyujinxErrorMessage": "Ryujinx обнаружил ошибку",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "การดำเนินการ",
|
"MenuBarActions": "การดำเนินการ",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "จำลองข้อความปลุก",
|
"MenuBarOptionsSimulateWakeUpMessage": "จำลองข้อความปลุก",
|
||||||
"MenuBarActionsScanAmiibo": "สแกนหา Amiibo",
|
"MenuBarActionsScanAmiibo": "สแกนหา Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_เครื่องมือ",
|
"MenuBarTools": "_เครื่องมือ",
|
||||||
"MenuBarToolsInstallFirmware": "ติดตั้งเฟิร์มแวร์",
|
"MenuBarToolsInstallFirmware": "ติดตั้งเฟิร์มแวร์",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "ติดตั้งเฟิร์มแวร์จาก ไฟล์ XCI หรือ ไฟล์ ZIP",
|
"MenuBarFileToolsInstallFirmwareFromFile": "ติดตั้งเฟิร์มแวร์จาก ไฟล์ XCI หรือ ไฟล์ ZIP",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "จัดการแคช",
|
"GameListContextMenuCacheManagement": "จัดการแคช",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "เพิ่มคิวการสร้าง PPTC ใหม่",
|
"GameListContextMenuCacheManagementPurgePptc": "เพิ่มคิวการสร้าง PPTC ใหม่",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "ให้ PPTC สร้างใหม่ในเวลาบูตเมื่อเปิดเกมครั้งถัดไป",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "ให้ PPTC สร้างใหม่ในเวลาบูตเมื่อเปิดเกมครั้งถัดไป",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "ล้างแคช แสงเงา",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "ล้างแคช แสงเงา",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "ลบแคช แสงเงา ของแอปพลิเคชัน",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "ลบแคช แสงเงา ของแอปพลิเคชัน",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "เปิดไดเรกทอรี่ PPTC",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "เปิดไดเรกทอรี่ PPTC",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "_Eylemler",
|
"MenuBarActions": "_Eylemler",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "Uyandırma Mesajı Simüle Et",
|
"MenuBarOptionsSimulateWakeUpMessage": "Uyandırma Mesajı Simüle Et",
|
||||||
"MenuBarActionsScanAmiibo": "Bir Amiibo Tara",
|
"MenuBarActionsScanAmiibo": "Bir Amiibo Tara",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "_Araçlar",
|
"MenuBarTools": "_Araçlar",
|
||||||
"MenuBarToolsInstallFirmware": "Yazılım Yükle",
|
"MenuBarToolsInstallFirmware": "Yazılım Yükle",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "XCI veya ZIP'ten Yazılım Yükle",
|
"MenuBarFileToolsInstallFirmwareFromFile": "XCI veya ZIP'ten Yazılım Yükle",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "Önbellek Yönetimi",
|
"GameListContextMenuCacheManagement": "Önbellek Yönetimi",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "PPTC Yeniden Yapılandırmasını Başlat",
|
"GameListContextMenuCacheManagementPurgePptc": "PPTC Yeniden Yapılandırmasını Başlat",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "Oyunun bir sonraki açılışında PPTC'yi yeniden yapılandır",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "Oyunun bir sonraki açılışında PPTC'yi yeniden yapılandır",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "Shader Önbelleğini Temizle",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "Shader Önbelleğini Temizle",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Uygulamanın shader önbelleğini temizler",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Uygulamanın shader önbelleğini temizler",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC Dizinini Aç",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC Dizinini Aç",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "Uyarı",
|
"DialogWarning": "Uyarı",
|
||||||
"DialogPPTCDeletionMessage": "Belirtilen PPTC cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?",
|
"DialogPPTCDeletionMessage": "Belirtilen PPTC cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?",
|
||||||
"DialogPPTCDeletionErrorMessage": "Belirtilen PPTC cache temizlenirken hata {0}: {1}",
|
"DialogPPTCDeletionErrorMessage": "Belirtilen PPTC cache temizlenirken hata {0}: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "Belirtilen Shader cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?",
|
"DialogShaderDeletionMessage": "Belirtilen Shader cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?",
|
||||||
"DialogShaderDeletionErrorMessage": "Belirtilen Shader cache temizlenirken hata {0}: {1}",
|
"DialogShaderDeletionErrorMessage": "Belirtilen Shader cache temizlenirken hata {0}: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx bir hata ile karşılaştı",
|
"DialogRyujinxErrorMessage": "Ryujinx bir hata ile karşılaştı",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "操作(_A)",
|
"MenuBarActions": "操作(_A)",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "模拟唤醒消息",
|
"MenuBarOptionsSimulateWakeUpMessage": "模拟唤醒消息",
|
||||||
"MenuBarActionsScanAmiibo": "扫描 Amiibo",
|
"MenuBarActionsScanAmiibo": "扫描 Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "工具(_T)",
|
"MenuBarTools": "工具(_T)",
|
||||||
"MenuBarToolsInstallFirmware": "安装系统固件",
|
"MenuBarToolsInstallFirmware": "安装系统固件",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "从 XCI 或 ZIP 文件中安装系统固件",
|
"MenuBarFileToolsInstallFirmwareFromFile": "从 XCI 或 ZIP 文件中安装系统固件",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "缓存管理",
|
"GameListContextMenuCacheManagement": "缓存管理",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "清除 PPTC 缓存文件",
|
"GameListContextMenuCacheManagementPurgePptc": "清除 PPTC 缓存文件",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "删除游戏的 PPTC 缓存文件,下次启动游戏时重新编译生成 PPTC 缓存文件",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "删除游戏的 PPTC 缓存文件,下次启动游戏时重新编译生成 PPTC 缓存文件",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "清除着色器缓存文件",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "清除着色器缓存文件",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "删除游戏的着色器缓存文件,下次启动游戏时重新生成着色器缓存文件",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "删除游戏的着色器缓存文件,下次启动游戏时重新生成着色器缓存文件",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "打开 PPTC 缓存目录",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "打开 PPTC 缓存目录",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "警告",
|
"DialogWarning": "警告",
|
||||||
"DialogPPTCDeletionMessage": "您即将删除:\n\n{0} 的 PPTC 缓存文件\n\n确定吗?",
|
"DialogPPTCDeletionMessage": "您即将删除:\n\n{0} 的 PPTC 缓存文件\n\n确定吗?",
|
||||||
"DialogPPTCDeletionErrorMessage": "清除 {0} 的 PPTC 缓存文件时出错:{1}",
|
"DialogPPTCDeletionErrorMessage": "清除 {0} 的 PPTC 缓存文件时出错:{1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "您即将删除:\n\n{0} 的着色器缓存文件\n\n确定吗?",
|
"DialogShaderDeletionMessage": "您即将删除:\n\n{0} 的着色器缓存文件\n\n确定吗?",
|
||||||
"DialogShaderDeletionErrorMessage": "清除 {0} 的着色器缓存文件时出错:{1}",
|
"DialogShaderDeletionErrorMessage": "清除 {0} 的着色器缓存文件时出错:{1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx 模拟器发生错误",
|
"DialogRyujinxErrorMessage": "Ryujinx 模拟器发生错误",
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
"MenuBarActions": "動作(_A)",
|
"MenuBarActions": "動作(_A)",
|
||||||
"MenuBarOptionsSimulateWakeUpMessage": "模擬喚醒訊息",
|
"MenuBarOptionsSimulateWakeUpMessage": "模擬喚醒訊息",
|
||||||
"MenuBarActionsScanAmiibo": "掃描 Amiibo",
|
"MenuBarActionsScanAmiibo": "掃描 Amiibo",
|
||||||
|
"MenuBarActionsScanAmiiboBin": "Scan An Amiibo (From Bin)",
|
||||||
"MenuBarTools": "工具(_T)",
|
"MenuBarTools": "工具(_T)",
|
||||||
"MenuBarToolsInstallFirmware": "安裝韌體",
|
"MenuBarToolsInstallFirmware": "安裝韌體",
|
||||||
"MenuBarFileToolsInstallFirmwareFromFile": "從 XCI 或 ZIP 安裝韌體",
|
"MenuBarFileToolsInstallFirmwareFromFile": "從 XCI 或 ZIP 安裝韌體",
|
||||||
|
@ -75,6 +76,8 @@
|
||||||
"GameListContextMenuCacheManagement": "快取管理",
|
"GameListContextMenuCacheManagement": "快取管理",
|
||||||
"GameListContextMenuCacheManagementPurgePptc": "佇列 PPTC 重建",
|
"GameListContextMenuCacheManagementPurgePptc": "佇列 PPTC 重建",
|
||||||
"GameListContextMenuCacheManagementPurgePptcToolTip": "下一次啟動遊戲時,觸發 PPTC 進行重建",
|
"GameListContextMenuCacheManagementPurgePptcToolTip": "下一次啟動遊戲時,觸發 PPTC 進行重建",
|
||||||
|
"GameListContextMenuCacheManagementNukePptc": "Purge PPTC cache",
|
||||||
|
"GameListContextMenuCacheManagementNukePptcToolTip": "Deletes Application's PPTC cache",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCache": "清除著色器快取",
|
"GameListContextMenuCacheManagementPurgeShaderCache": "清除著色器快取",
|
||||||
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "刪除應用程式的著色器快取",
|
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "刪除應用程式的著色器快取",
|
||||||
"GameListContextMenuCacheManagementOpenPptcDirectory": "開啟 PPTC 資料夾",
|
"GameListContextMenuCacheManagementOpenPptcDirectory": "開啟 PPTC 資料夾",
|
||||||
|
@ -504,6 +507,7 @@
|
||||||
"DialogWarning": "警告",
|
"DialogWarning": "警告",
|
||||||
"DialogPPTCDeletionMessage": "您將在下一次啟動時佇列重建以下遊戲的 PPTC:\n\n{0}\n\n您確定要繼續嗎?",
|
"DialogPPTCDeletionMessage": "您將在下一次啟動時佇列重建以下遊戲的 PPTC:\n\n{0}\n\n您確定要繼續嗎?",
|
||||||
"DialogPPTCDeletionErrorMessage": "在 {0} 清除 PPTC 快取時出錯: {1}",
|
"DialogPPTCDeletionErrorMessage": "在 {0} 清除 PPTC 快取時出錯: {1}",
|
||||||
|
"DialogPPTCNukeMessage": "You are about to purge all PPTC data from:\n\n{0}\n\nAre you sure you want to proceed?",
|
||||||
"DialogShaderDeletionMessage": "您將刪除以下遊戲的著色器快取:\n\n{0}\n\n您確定要繼續嗎?",
|
"DialogShaderDeletionMessage": "您將刪除以下遊戲的著色器快取:\n\n{0}\n\n您確定要繼續嗎?",
|
||||||
"DialogShaderDeletionErrorMessage": "在 {0} 清除著色器快取時出錯: {1}",
|
"DialogShaderDeletionErrorMessage": "在 {0} 清除著色器快取時出錯: {1}",
|
||||||
"DialogRyujinxErrorMessage": "Ryujinx 遇到錯誤",
|
"DialogRyujinxErrorMessage": "Ryujinx 遇到錯誤",
|
||||||
|
|
|
@ -82,6 +82,11 @@
|
||||||
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
|
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
|
||||||
Icon="{ext:Icon mdi-refresh}"
|
Icon="{ext:Icon mdi-refresh}"
|
||||||
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
||||||
|
<MenuItem
|
||||||
|
Click="NukePtcCache_Click"
|
||||||
|
Header="{ext:Locale GameListContextMenuCacheManagementNukePptc}"
|
||||||
|
Icon="{ext:Icon mdi-delete-alert}"
|
||||||
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementNukePptcToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="PurgeShaderCache_Click"
|
Click="PurgeShaderCache_Click"
|
||||||
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
||||||
|
|
|
@ -170,6 +170,52 @@ namespace Ryujinx.Ava.UI.Controls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void NukePtcCache_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
return;
|
||||||
|
|
||||||
|
UserResult result = await ContentDialogHelper.CreateLocalizedConfirmationDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogWarning],
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCNukeMessage, viewModel.SelectedApplication.Name)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == UserResult.Yes)
|
||||||
|
{
|
||||||
|
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "0"));
|
||||||
|
DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.IdString, "cache", "cpu", "1"));
|
||||||
|
|
||||||
|
List<FileInfo> cacheFiles = new();
|
||||||
|
|
||||||
|
if (mainDir.Exists)
|
||||||
|
{
|
||||||
|
cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache"));
|
||||||
|
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupDir.Exists)
|
||||||
|
{
|
||||||
|
cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache"));
|
||||||
|
cacheFiles.AddRange(mainDir.EnumerateFiles("*.info"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheFiles.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (FileInfo file in cacheFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args)
|
public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
|
|
|
@ -29,12 +29,14 @@ using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.UI;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common;
|
using Ryujinx.UI.Common;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.UI.Common.Configuration;
|
||||||
using Ryujinx.UI.Common.Helper;
|
using Ryujinx.UI.Common.Helper;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -71,6 +73,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
private string _gpuStatusText;
|
private string _gpuStatusText;
|
||||||
private string _shaderCountText;
|
private string _shaderCountText;
|
||||||
private bool _isAmiiboRequested;
|
private bool _isAmiiboRequested;
|
||||||
|
private bool _isAmiiboBinRequested;
|
||||||
private bool _showShaderCompilationHint;
|
private bool _showShaderCompilationHint;
|
||||||
private bool _isGameRunning;
|
private bool _isGameRunning;
|
||||||
private bool _isFullScreen;
|
private bool _isFullScreen;
|
||||||
|
@ -317,7 +320,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool IsAmiiboBinRequested
|
||||||
|
{
|
||||||
|
get => _isAmiiboBinRequested && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isAmiiboBinRequested = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
public bool ShowLoadProgress
|
public bool ShowLoadProgress
|
||||||
{
|
{
|
||||||
get => _showLoadProgress;
|
get => _showLoadProgress;
|
||||||
|
@ -2060,6 +2072,32 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task OpenBinFile()
|
||||||
|
{
|
||||||
|
if (!IsAmiiboRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (AppHost.Device.System.SearchingForAmiibo(out int deviceId))
|
||||||
|
{
|
||||||
|
var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
|
||||||
|
AllowMultiple = false,
|
||||||
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
|
{
|
||||||
|
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||||
|
{
|
||||||
|
Patterns = new[] { "*.bin" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (result.Count > 0)
|
||||||
|
{
|
||||||
|
AppHost.Device.System.ScanAmiiboFromBin(result[0].Path.LocalPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ToggleFullscreen()
|
public void ToggleFullscreen()
|
||||||
{
|
{
|
||||||
|
|
|
@ -241,6 +241,13 @@
|
||||||
Icon="{ext:Icon mdi-cube-scan}"
|
Icon="{ext:Icon mdi-cube-scan}"
|
||||||
InputGesture="Ctrl + A"
|
InputGesture="Ctrl + A"
|
||||||
IsEnabled="{Binding IsAmiiboRequested}" />
|
IsEnabled="{Binding IsAmiiboRequested}" />
|
||||||
|
<MenuItem
|
||||||
|
Name="ScanAmiiboMenuItemFromBin"
|
||||||
|
AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree"
|
||||||
|
Click="OpenBinFile"
|
||||||
|
Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
|
||||||
|
Icon="{ext:Icon mdi-cube-scan}"
|
||||||
|
IsEnabled="{Binding IsAmiiboBinRequested}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding TakeScreenshot}"
|
Command="{Binding TakeScreenshot}"
|
||||||
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
||||||
|
|
|
@ -13,6 +13,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common;
|
using Ryujinx.UI.Common;
|
||||||
|
@ -151,6 +152,9 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||||
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
|
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
|
||||||
=> await ViewModel.OpenAmiiboWindow();
|
=> await ViewModel.OpenAmiiboWindow();
|
||||||
|
|
||||||
|
public async void OpenBinFile(object sender, RoutedEventArgs e)
|
||||||
|
=> await ViewModel.OpenBinFile();
|
||||||
|
|
||||||
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
|
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (!ViewModel.IsGameRunning)
|
if (!ViewModel.IsGameRunning)
|
||||||
|
@ -173,6 +177,12 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||||
ViewModel.IsAmiiboRequested = ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
|
ViewModel.IsAmiiboRequested = ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem)
|
||||||
|
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasKeyRetailBinPath();
|
||||||
|
}
|
||||||
|
|
||||||
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
|
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
||||||
|
|
Loading…
Reference in a new issue