Implement mii:u and mii:e entirely (#955)
* Implement mii:u and mii:e entirely Co-authored-by: AcK77 <Acoustik666@gmail.com> This commit implement the mii service accurately. This is based on Ac_k work but was polished and updated to 7.x. Please note that the following calls are partially implemented: - Convert: Used to convert from old console format (Wii/Wii U/3ds) - Import and Export: this is shouldn't be accesible in production mode. * Remove some debug leftovers * Make it possible to load an arbitrary mii database from a Switch * Address gdk's comments * Reduce visibility of all the Mii code * Address Ac_K's comments * Remove the StructLayout of DatabaseSessionMetadata * Add a missing line return in DatabaseSessionMetadata * Misc fixes and style changes * Fix some issues from last commit * Fix database server metadata UpdateCounter in MarkDirty (Thanks Moose for the catch) * MountCounter should only be incremented when no error is reported * Fix FixDatabase Co-authored-by: Alex Barney <thealexbarney@gmail.com>
This commit is contained in:
parent
7d1a294eae
commit
3b531de670
49 changed files with 6728 additions and 15 deletions
|
@ -14,6 +14,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
||||||
using Ryujinx.HLE.HOS.Services.Settings;
|
using Ryujinx.HLE.HOS.Services.Settings;
|
||||||
using Ryujinx.HLE.HOS.Services.Sm;
|
using Ryujinx.HLE.HOS.Services.Sm;
|
||||||
|
@ -232,6 +233,8 @@ namespace Ryujinx.HLE.HOS
|
||||||
// FIXME: TimeZone shoud be init here but it's actually done in ContentManager
|
// FIXME: TimeZone shoud be init here but it's actually done in ContentManager
|
||||||
|
|
||||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
||||||
|
|
||||||
|
DatabaseImpl.Instance.InitializeDatabase(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using LibHac.Account;
|
using LibHac.Account;
|
||||||
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -73,9 +74,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||||
return HashCode.Combine(Low, High);
|
return HashCode.Combine(Low, High);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uid ToLibHacUid()
|
public readonly Uid ToLibHacUid()
|
||||||
{
|
{
|
||||||
return new Uid((ulong)High, (ulong)Low);
|
return new Uid((ulong)High, (ulong)Low);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly UInt128 ToUInt128()
|
||||||
|
{
|
||||||
|
return new UInt128(Low, High);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
326
Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs
Normal file
326
Ryujinx.HLE/HOS/Services/Mii/DatabaseImpl.cs
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
class DatabaseImpl
|
||||||
|
{
|
||||||
|
private static DatabaseImpl _instance;
|
||||||
|
|
||||||
|
public static DatabaseImpl Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
_instance = new DatabaseImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UtilityImpl _utilityImpl;
|
||||||
|
private MiiDatabaseManager _miiDatabase;
|
||||||
|
private bool _isBroken;
|
||||||
|
|
||||||
|
public DatabaseImpl()
|
||||||
|
{
|
||||||
|
_utilityImpl = new UtilityImpl();
|
||||||
|
_miiDatabase = new MiiDatabaseManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUpdated(DatabaseSessionMetadata metadata, SourceFlag flag)
|
||||||
|
{
|
||||||
|
if (flag.HasFlag(SourceFlag.Database))
|
||||||
|
{
|
||||||
|
return _miiDatabase.IsUpdated(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBrokenDatabaseWithClearFlag()
|
||||||
|
{
|
||||||
|
bool result = _isBroken;
|
||||||
|
|
||||||
|
if (_isBroken)
|
||||||
|
{
|
||||||
|
_isBroken = false;
|
||||||
|
|
||||||
|
Format(new DatabaseSessionMetadata(0, new SpecialMiiKeyCode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFullDatabase()
|
||||||
|
{
|
||||||
|
return _miiDatabase.IsFullDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResultCode GetDefault<T>(SourceFlag flag, ref int count, Span<T> elements) where T : struct, IElement
|
||||||
|
{
|
||||||
|
if (!flag.HasFlag(SourceFlag.Default))
|
||||||
|
{
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint i = 0; i < DefaultMii.TableLength; i++)
|
||||||
|
{
|
||||||
|
if (count >= elements.Length)
|
||||||
|
{
|
||||||
|
return ResultCode.BufferTooSmall;
|
||||||
|
}
|
||||||
|
|
||||||
|
elements[count] = default;
|
||||||
|
elements[count].SetFromStoreData(StoreData.BuildDefault(_utilityImpl, i));
|
||||||
|
elements[count].SetSource(Source.Default);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, IStoredData<T> oldMiiData, SourceFlag flag, IStoredData<T> newMiiData) where T : unmanaged
|
||||||
|
{
|
||||||
|
if (!flag.HasFlag(SourceFlag.Database))
|
||||||
|
{
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.IsInterfaceVersionSupported(1) && !oldMiiData.IsValid())
|
||||||
|
{
|
||||||
|
return oldMiiData.InvalidData;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode result = _miiDatabase.FindIndex(metadata, out int index, oldMiiData.CreateId);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
_miiDatabase.Get(metadata, index, out StoreData storeData);
|
||||||
|
|
||||||
|
if (storeData.Type != oldMiiData.Type)
|
||||||
|
{
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
newMiiData.SetFromStoreData(storeData);
|
||||||
|
|
||||||
|
if (oldMiiData == newMiiData)
|
||||||
|
{
|
||||||
|
return ResultCode.NotUpdated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Get<T>(DatabaseSessionMetadata metadata, SourceFlag flag, out int count, Span<T> elements) where T : struct, IElement
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
if (!flag.HasFlag(SourceFlag.Database))
|
||||||
|
{
|
||||||
|
return GetDefault(flag, ref count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
int databaseCount = _miiDatabase.GetCount(metadata);
|
||||||
|
|
||||||
|
for (int i = 0; i < databaseCount; i++)
|
||||||
|
{
|
||||||
|
if (count >= elements.Length)
|
||||||
|
{
|
||||||
|
return ResultCode.BufferTooSmall;
|
||||||
|
}
|
||||||
|
|
||||||
|
_miiDatabase.Get(metadata, i, out StoreData storeData);
|
||||||
|
|
||||||
|
elements[count] = default;
|
||||||
|
elements[count].SetFromStoreData(storeData);
|
||||||
|
elements[count].SetSource(Source.Database);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetDefault(flag, ref count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode InitializeDatabase(Switch device)
|
||||||
|
{
|
||||||
|
_miiDatabase.InitializeDatabase(device);
|
||||||
|
_miiDatabase.LoadFromFile(out _isBroken);
|
||||||
|
|
||||||
|
// Nintendo ignore any error code from before
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSessionMetadata CreateSessionMetadata(SpecialMiiKeyCode miiKeyCode)
|
||||||
|
{
|
||||||
|
return _miiDatabase.CreateSessionMetadata(miiKeyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInterfaceVersion(DatabaseSessionMetadata metadata, uint interfaceVersion)
|
||||||
|
{
|
||||||
|
_miiDatabase.SetInterfaceVersion(metadata, interfaceVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Format(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
_miiDatabase.FormatDatabase(metadata);
|
||||||
|
_miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode DestroyFile(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
_isBroken = true;
|
||||||
|
|
||||||
|
return _miiDatabase.DestroyFile(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildDefault(uint index, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
StoreData storeData = StoreData.BuildDefault(_utilityImpl, index);
|
||||||
|
|
||||||
|
charInfo = default;
|
||||||
|
|
||||||
|
charInfo.SetFromStoreData(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
StoreData storeData = StoreData.BuildRandom(_utilityImpl, age, gender, race);
|
||||||
|
|
||||||
|
charInfo = default;
|
||||||
|
|
||||||
|
charInfo.SetFromStoreData(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode DeleteFile()
|
||||||
|
{
|
||||||
|
return _miiDatabase.DeleteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
charInfo = new CharInfo();
|
||||||
|
|
||||||
|
if (!coreData.IsValid())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidCoreData;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreData storeData = StoreData.BuildFromCoreData(_utilityImpl, coreData);
|
||||||
|
|
||||||
|
if (!storeData.CoreData.Nickname.IsValidForFontRegion(storeData.CoreData.FontRegion))
|
||||||
|
{
|
||||||
|
storeData.CoreData.Nickname = Nickname.Question;
|
||||||
|
storeData.UpdateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
charInfo.SetFromStoreData(storeData);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FindIndex(CreateId createId, bool isSpecial)
|
||||||
|
{
|
||||||
|
if (_miiDatabase.FindIndex(out int index, createId, isSpecial) == ResultCode.Success)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetCount(DatabaseSessionMetadata metadata, SourceFlag flag)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (flag.HasFlag(SourceFlag.Default))
|
||||||
|
{
|
||||||
|
count += DefaultMii.TableLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.HasFlag(SourceFlag.Database))
|
||||||
|
{
|
||||||
|
count += _miiDatabase.GetCount(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Move(DatabaseSessionMetadata metadata, int index, CreateId createId)
|
||||||
|
{
|
||||||
|
ResultCode result = _miiDatabase.Move(metadata, index, createId);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
||||||
|
{
|
||||||
|
ResultCode result = _miiDatabase.Delete(metadata, createId);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode AddOrReplace(DatabaseSessionMetadata metadata, StoreData storeData)
|
||||||
|
{
|
||||||
|
ResultCode result = _miiDatabase.AddOrReplace(metadata, storeData);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
||||||
|
{
|
||||||
|
coreData = new CoreData();
|
||||||
|
|
||||||
|
if (charInfo.IsValid())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidCharInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
coreData.SetFromCharInfo(charInfo);
|
||||||
|
|
||||||
|
if (!coreData.Nickname.IsValidForFontRegion(coreData.FontRegion))
|
||||||
|
{
|
||||||
|
coreData.Nickname = Nickname.Question;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode GetIndex(DatabaseSessionMetadata metadata, CharInfo charInfo, out int index)
|
||||||
|
{
|
||||||
|
if (!charInfo.IsValid())
|
||||||
|
{
|
||||||
|
index = -1;
|
||||||
|
|
||||||
|
return ResultCode.InvalidCharInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_miiDatabase.FindIndex(out index, charInfo.CreateId, metadata.MiiKeyCode.IsEnabledSpecialMii()) != ResultCode.Success)
|
||||||
|
{
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs
Normal file
24
Ryujinx.HLE/HOS/Services/Mii/DatabaseSessionMetadata.cs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
class DatabaseSessionMetadata
|
||||||
|
{
|
||||||
|
public uint InterfaceVersion;
|
||||||
|
public ulong UpdateCounter;
|
||||||
|
|
||||||
|
public SpecialMiiKeyCode MiiKeyCode { get; private set; }
|
||||||
|
|
||||||
|
public DatabaseSessionMetadata(ulong updateCounter, SpecialMiiKeyCode miiKeyCode)
|
||||||
|
{
|
||||||
|
InterfaceVersion = 0;
|
||||||
|
UpdateCounter = updateCounter;
|
||||||
|
MiiKeyCode = miiKeyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsInterfaceVersionSupported(uint interfaceVersion)
|
||||||
|
{
|
||||||
|
return InterfaceVersion >= interfaceVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
Ryujinx.HLE/HOS/Services/Mii/Helper.cs
Normal file
44
Ryujinx.HLE/HOS/Services/Mii/Helper.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
static class Helper
|
||||||
|
{
|
||||||
|
public static ushort CalculateCrc16BE(ReadOnlySpan<byte> data, int crc = 0)
|
||||||
|
{
|
||||||
|
const ushort poly = 0x1021;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
crc ^= data[i] << 8;
|
||||||
|
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
crc <<= 1;
|
||||||
|
|
||||||
|
if ((crc & 0x10000) != 0)
|
||||||
|
{
|
||||||
|
crc = (crc ^ poly) & 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BinaryPrimitives.ReverseEndianness((ushort)crc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 GetDeviceId()
|
||||||
|
{
|
||||||
|
// FIXME: call set:sys GetMiiAuthorId
|
||||||
|
return SystemStateMgr.DefaultUserId.ToUInt128();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReadOnlySpan<byte> Ver3FacelineColorTable => new byte[] { 0, 1, 2, 3, 4, 5 };
|
||||||
|
public static ReadOnlySpan<byte> Ver3HairColorTable => new byte[] { 8, 1, 2, 3, 4, 5, 6, 7 };
|
||||||
|
public static ReadOnlySpan<byte> Ver3EyeColorTable => new byte[] { 8, 9, 10, 11, 12, 13 };
|
||||||
|
public static ReadOnlySpan<byte> Ver3MouthColorTable => new byte[] { 19, 20, 21, 22, 23 };
|
||||||
|
public static ReadOnlySpan<byte> Ver3GlassColorTable => new byte[] { 8, 14, 15, 16, 17, 18, 0 };
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Sdb.Mii
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
{
|
{
|
||||||
[Service("miiimg")] // 5.0.0+
|
[Service("miiimg")] // 5.0.0+
|
||||||
class IImageDatabaseService : IpcService
|
class IImageDatabaseService : IpcService
|
32
Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs
Normal file
32
Ryujinx.HLE/HOS/Services/Mii/IStaticService.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.StaticService;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
[Service("mii:e", true)]
|
||||||
|
[Service("mii:u", false)]
|
||||||
|
class IStaticService : IpcService
|
||||||
|
{
|
||||||
|
private DatabaseImpl _databaseImpl;
|
||||||
|
|
||||||
|
private bool _isSystem;
|
||||||
|
|
||||||
|
public IStaticService(ServiceCtx context, bool isSystem)
|
||||||
|
{
|
||||||
|
_isSystem = isSystem;
|
||||||
|
_databaseImpl = DatabaseImpl.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(0)]
|
||||||
|
// GetDatabaseService(u32 mii_key_code) -> object<nn::mii::detail::IDatabaseService>
|
||||||
|
public ResultCode GetDatabaseService(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SpecialMiiKeyCode miiKeyCode = context.RequestData.ReadStruct<SpecialMiiKeyCode>();
|
||||||
|
|
||||||
|
MakeObject(context, new DatabaseServiceImpl(_databaseImpl, _isSystem, miiKeyCode));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
510
Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs
Normal file
510
Ryujinx.HLE/HOS/Services/Mii/MiiDatabaseManager.cs
Normal file
|
@ -0,0 +1,510 @@
|
||||||
|
using LibHac;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
class MiiDatabaseManager
|
||||||
|
{
|
||||||
|
private static bool IsTestModeEnabled = false;
|
||||||
|
private static uint MountCounter = 0;
|
||||||
|
|
||||||
|
private const ulong DatabaseTestSaveDataId = 0x8000000000000031;
|
||||||
|
private const ulong DatabaseSaveDataId = 0x8000000000000030;
|
||||||
|
private const ulong NsTitleId = 0x010000000000001F;
|
||||||
|
private const ulong SdbTitleId = 0x0100000000000039;
|
||||||
|
private const string DatabasePath = "mii:/MiiDatabase.dat";
|
||||||
|
private const string MountName = "mii";
|
||||||
|
|
||||||
|
private NintendoFigurineDatabase _database;
|
||||||
|
private bool _isDirty;
|
||||||
|
|
||||||
|
private FileSystemClient _filesystemClient;
|
||||||
|
|
||||||
|
protected ulong UpdateCounter { get; private set; }
|
||||||
|
|
||||||
|
public MiiDatabaseManager()
|
||||||
|
{
|
||||||
|
_database = new NintendoFigurineDatabase();
|
||||||
|
_isDirty = false;
|
||||||
|
UpdateCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetDatabase()
|
||||||
|
{
|
||||||
|
_database = new NintendoFigurineDatabase();
|
||||||
|
_database.Format();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MarkDirty(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
_isDirty = true;
|
||||||
|
|
||||||
|
UpdateCounter++;
|
||||||
|
|
||||||
|
metadata.UpdateCounter = UpdateCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetAtVirtualIndex(int index, out int realIndex, out StoreData storeData)
|
||||||
|
{
|
||||||
|
realIndex = -1;
|
||||||
|
storeData = new StoreData();
|
||||||
|
|
||||||
|
int virtualIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < _database.Length; i++)
|
||||||
|
{
|
||||||
|
StoreData tmp = _database.Get(i);
|
||||||
|
|
||||||
|
if (!tmp.IsSpecial())
|
||||||
|
{
|
||||||
|
if (index == virtualIndex)
|
||||||
|
{
|
||||||
|
realIndex = i;
|
||||||
|
storeData = tmp;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtualIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ConvertRealIndexToVirtualIndex(int realIndex)
|
||||||
|
{
|
||||||
|
int virtualIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < realIndex; i++)
|
||||||
|
{
|
||||||
|
StoreData tmp = _database.Get(i);
|
||||||
|
|
||||||
|
if (!tmp.IsSpecial())
|
||||||
|
{
|
||||||
|
virtualIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return virtualIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeDatabase(Switch device)
|
||||||
|
{
|
||||||
|
_filesystemClient = device.FileSystem.FsClient;
|
||||||
|
|
||||||
|
// Ensure we have valid data in the database
|
||||||
|
_database.Format();
|
||||||
|
|
||||||
|
MountSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result MountSave()
|
||||||
|
{
|
||||||
|
Result result = Result.Success;
|
||||||
|
|
||||||
|
if (MountCounter == 0)
|
||||||
|
{
|
||||||
|
ulong targetSaveDataId;
|
||||||
|
ulong targetTitleId;
|
||||||
|
|
||||||
|
if (IsTestModeEnabled)
|
||||||
|
{
|
||||||
|
targetSaveDataId = DatabaseTestSaveDataId;
|
||||||
|
targetTitleId = SdbTitleId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetSaveDataId = DatabaseSaveDataId;
|
||||||
|
|
||||||
|
// Nintendo use NS TitleID when creating the production save even on sdb, let's follow that behaviour.
|
||||||
|
targetTitleId = NsTitleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
U8Span mountName = new U8Span(MountName);
|
||||||
|
|
||||||
|
result = _filesystemClient.MountSystemSaveData(mountName, SaveDataSpaceId.System, targetSaveDataId);
|
||||||
|
|
||||||
|
if (result.IsFailure())
|
||||||
|
{
|
||||||
|
if (ResultFs.TargetNotFound == result)
|
||||||
|
{
|
||||||
|
// TODO: We're currently always specifying the owner ID because FS doesn't have a way of
|
||||||
|
// knowing which process called it
|
||||||
|
result = _filesystemClient.CreateSystemSaveData(targetSaveDataId, new TitleId(targetTitleId), 0x10000, 0x10000, SaveDataFlags.KeepAfterResettingSystemSaveDataWithoutUserSaveData);
|
||||||
|
if (result.IsFailure()) return result;
|
||||||
|
|
||||||
|
result = _filesystemClient.MountSystemSaveData(mountName, SaveDataSpaceId.System, targetSaveDataId);
|
||||||
|
if (result.IsFailure()) return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == Result.Success)
|
||||||
|
{
|
||||||
|
MountCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode DeleteFile()
|
||||||
|
{
|
||||||
|
ResultCode result = (ResultCode)_filesystemClient.DeleteFile(DatabasePath).Value;
|
||||||
|
|
||||||
|
_filesystemClient.Commit(MountName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode LoadFromFile(out bool isBroken)
|
||||||
|
{
|
||||||
|
isBroken = false;
|
||||||
|
|
||||||
|
if (MountCounter == 0)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCounter++;
|
||||||
|
|
||||||
|
ResetDatabase();
|
||||||
|
|
||||||
|
Result result = _filesystemClient.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Read);
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
result = _filesystemClient.GetFileSize(out long fileSize, handle);
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
if (fileSize == Unsafe.SizeOf<NintendoFigurineDatabase>())
|
||||||
|
{
|
||||||
|
result = _filesystemClient.ReadFile(handle, 0, _database.AsSpan());
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
if (_database.Verify() != ResultCode.Success)
|
||||||
|
{
|
||||||
|
ResetDatabase();
|
||||||
|
|
||||||
|
isBroken = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isBroken = _database.FixDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isBroken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_filesystemClient.CloseFile(handle);
|
||||||
|
|
||||||
|
return (ResultCode)result.Value;
|
||||||
|
}
|
||||||
|
else if (result == ResultFs.PathNotFound)
|
||||||
|
{
|
||||||
|
return (ResultCode)ForceSaveDatabase().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result ForceSaveDatabase()
|
||||||
|
{
|
||||||
|
Result result = _filesystemClient.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
|
||||||
|
|
||||||
|
if (result.IsSuccess() || result == ResultFs.PathAlreadyExists)
|
||||||
|
{
|
||||||
|
result = _filesystemClient.OpenFile(out FileHandle handle, DatabasePath, OpenMode.Write);
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
result = _filesystemClient.GetFileSize(out long fileSize, handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
// If the size doesn't match, recreate the file
|
||||||
|
if (fileSize != Unsafe.SizeOf<NintendoFigurineDatabase>())
|
||||||
|
{
|
||||||
|
_filesystemClient.CloseFile(handle);
|
||||||
|
|
||||||
|
result = _filesystemClient.DeleteFile(DatabasePath);
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
result = _filesystemClient.CreateFile(DatabasePath, Unsafe.SizeOf<NintendoFigurineDatabase>());
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
result = _filesystemClient.OpenFile(out handle, DatabasePath, OpenMode.Write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsFailure())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _filesystemClient.WriteFile(handle, 0, _database.AsReadOnlySpan(), WriteOption.Flush);
|
||||||
|
}
|
||||||
|
|
||||||
|
_filesystemClient.CloseFile(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsSuccess())
|
||||||
|
{
|
||||||
|
_isDirty = false;
|
||||||
|
|
||||||
|
result = _filesystemClient.Commit(MountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSessionMetadata CreateSessionMetadata(SpecialMiiKeyCode miiKeyCode)
|
||||||
|
{
|
||||||
|
return new DatabaseSessionMetadata(UpdateCounter, miiKeyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInterfaceVersion(DatabaseSessionMetadata metadata, uint interfaceVersion)
|
||||||
|
{
|
||||||
|
metadata.InterfaceVersion = interfaceVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsUpdated(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
bool result = metadata.UpdateCounter != UpdateCounter;
|
||||||
|
|
||||||
|
metadata.UpdateCounter = UpdateCounter;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCount(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < _database.Length; i++)
|
||||||
|
{
|
||||||
|
StoreData tmp = _database.Get(i);
|
||||||
|
|
||||||
|
if (!tmp.IsSpecial())
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _database.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Get(DatabaseSessionMetadata metadata, int index, out StoreData storeData)
|
||||||
|
{
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
|
||||||
|
{
|
||||||
|
if (GetAtVirtualIndex(index, out int realIndex, out _))
|
||||||
|
{
|
||||||
|
index = realIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeData = _database.Get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode FindIndex(DatabaseSessionMetadata metadata, out int index, CreateId createId)
|
||||||
|
{
|
||||||
|
return FindIndex(out index, createId, metadata.MiiKeyCode.IsEnabledSpecialMii());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode FindIndex(out int index, CreateId createId, bool isSpecial)
|
||||||
|
{
|
||||||
|
if (_database.GetIndexByCreatorId(out int realIndex, createId))
|
||||||
|
{
|
||||||
|
if (isSpecial)
|
||||||
|
{
|
||||||
|
index = realIndex;
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreData storeData = _database.Get(realIndex);
|
||||||
|
|
||||||
|
if (!storeData.IsSpecial())
|
||||||
|
{
|
||||||
|
if (realIndex < 1)
|
||||||
|
{
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = ConvertRealIndexToVirtualIndex(realIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = -1;
|
||||||
|
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Move(DatabaseSessionMetadata metadata, int newIndex, CreateId createId)
|
||||||
|
{
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
|
||||||
|
{
|
||||||
|
if (GetAtVirtualIndex(newIndex, out int realIndex, out _))
|
||||||
|
{
|
||||||
|
newIndex = realIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_database.GetIndexByCreatorId(out int oldIndex, createId))
|
||||||
|
{
|
||||||
|
StoreData realStoreData = _database.Get(oldIndex);
|
||||||
|
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii() && realStoreData.IsSpecial())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode result = _database.Move(newIndex, oldIndex);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
MarkDirty(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode AddOrReplace(DatabaseSessionMetadata metadata, StoreData storeData)
|
||||||
|
{
|
||||||
|
if (!storeData.IsValid())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidStoreData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii() && !storeData.IsSpecial())
|
||||||
|
{
|
||||||
|
if (_database.GetIndexByCreatorId(out int index, storeData.CreateId))
|
||||||
|
{
|
||||||
|
StoreData oldStoreData = _database.Get(index);
|
||||||
|
|
||||||
|
if (oldStoreData.IsSpecial())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.Replace(index, storeData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_database.IsFull())
|
||||||
|
{
|
||||||
|
return ResultCode.DatabaseFull;
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.Add(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkDirty(metadata);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
||||||
|
{
|
||||||
|
if (!_database.GetIndexByCreatorId(out int index, createId))
|
||||||
|
{
|
||||||
|
return ResultCode.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metadata.MiiKeyCode.IsEnabledSpecialMii())
|
||||||
|
{
|
||||||
|
StoreData storeData = _database.Get(index);
|
||||||
|
|
||||||
|
if (storeData.IsSpecial())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.Delete(index);
|
||||||
|
|
||||||
|
MarkDirty(metadata);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode DestroyFile(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
_database.CorruptDatabase();
|
||||||
|
|
||||||
|
MarkDirty(metadata);
|
||||||
|
|
||||||
|
ResultCode result = SaveDatabase();
|
||||||
|
|
||||||
|
ResetDatabase();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode SaveDatabase()
|
||||||
|
{
|
||||||
|
if (_isDirty)
|
||||||
|
{
|
||||||
|
return (ResultCode)ForceSaveDatabase().Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ResultCode.NotUpdated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FormatDatabase(DatabaseSessionMetadata metadata)
|
||||||
|
{
|
||||||
|
_database.Format();
|
||||||
|
|
||||||
|
MarkDirty(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFullDatabase()
|
||||||
|
{
|
||||||
|
return _database.IsFull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs
Normal file
29
Ryujinx.HLE/HOS/Services/Mii/ResultCode.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
public enum ResultCode
|
||||||
|
{
|
||||||
|
ModuleId = 126,
|
||||||
|
ErrorCodeShift = 9,
|
||||||
|
|
||||||
|
Success = 0,
|
||||||
|
|
||||||
|
InvalidArgument = (1 << ErrorCodeShift) | ModuleId,
|
||||||
|
BufferTooSmall = (2 << ErrorCodeShift) | ModuleId,
|
||||||
|
NotUpdated = (3 << ErrorCodeShift) | ModuleId,
|
||||||
|
NotFound = (4 << ErrorCodeShift) | ModuleId,
|
||||||
|
DatabaseFull = (5 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidCharInfo = (100 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidCrc = (101 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidDeviceCrc = (102 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidDatabaseMagic = (103 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidDatabaseVersion = (104 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidDatabaseSize = (105 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidCreateId = (106 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidCoreData = (108 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidStoreData = (109 << ErrorCodeShift) | ModuleId,
|
||||||
|
|
||||||
|
InvalidOperationOnSpecialMii = (202 << ErrorCodeShift) | ModuleId,
|
||||||
|
PermissionDenied = (203 << ErrorCodeShift) | ModuleId,
|
||||||
|
TestModeNotEnabled = (204 << ErrorCodeShift) | ModuleId,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,266 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Settings;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
||||||
|
{
|
||||||
|
class DatabaseServiceImpl : IDatabaseService
|
||||||
|
{
|
||||||
|
private DatabaseImpl _database;
|
||||||
|
private DatabaseSessionMetadata _metadata;
|
||||||
|
private bool _isSystem;
|
||||||
|
|
||||||
|
public DatabaseServiceImpl(DatabaseImpl database, bool isSystem, SpecialMiiKeyCode miiKeyCode)
|
||||||
|
{
|
||||||
|
_database = database;
|
||||||
|
_metadata = _database.CreateSessionMetadata(miiKeyCode);
|
||||||
|
_isSystem = isSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDatabaseTestModeEnabled()
|
||||||
|
{
|
||||||
|
if (NxSettings.Settings.TryGetValue("mii!is_db_test_mode_enabled", out object isDatabaseTestModeEnabled))
|
||||||
|
{
|
||||||
|
return (bool)isDatabaseTestModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsUpdated(SourceFlag flag)
|
||||||
|
{
|
||||||
|
return _database.IsUpdated(_metadata, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsFullDatabase()
|
||||||
|
{
|
||||||
|
return _database.IsFullDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override uint GetCount(SourceFlag flag)
|
||||||
|
{
|
||||||
|
return _database.GetCount(_metadata, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Get(SourceFlag flag, out int count, Span<CharInfoElement> elements)
|
||||||
|
{
|
||||||
|
return _database.Get(_metadata, flag, out count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Get1(SourceFlag flag, out int count, Span<CharInfo> elements)
|
||||||
|
{
|
||||||
|
return _database.Get(_metadata, flag, out count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
||||||
|
{
|
||||||
|
newCharInfo = default;
|
||||||
|
|
||||||
|
return _database.UpdateLatest(_metadata, oldCharInfo, flag, newCharInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
if (age > Age.All || gender > Gender.All || race > Race.All)
|
||||||
|
{
|
||||||
|
charInfo = default;
|
||||||
|
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.BuildRandom(age, gender, race, out charInfo);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode BuildDefault(uint index, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
if (index >= DefaultMii.TableLength)
|
||||||
|
{
|
||||||
|
charInfo = default;
|
||||||
|
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.BuildDefault(index, out charInfo);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Get2(SourceFlag flag, out int count, Span<StoreDataElement> elements)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
count = -1;
|
||||||
|
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.Get(_metadata, flag, out count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Get3(SourceFlag flag, out int count, Span<StoreData> elements)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
count = -1;
|
||||||
|
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.Get(_metadata, flag, out count, elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
|
||||||
|
{
|
||||||
|
newStoreData = default;
|
||||||
|
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.UpdateLatest(_metadata, oldStoreData, flag, newStoreData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
index = -1;
|
||||||
|
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = _database.FindIndex(createId, isSpecial);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Move(CreateId createId, int newIndex)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex > 0 && _database.GetCount(_metadata, SourceFlag.Database) > newIndex)
|
||||||
|
{
|
||||||
|
return _database.Move(_metadata, newIndex, createId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode AddOrReplace(StoreData storeData)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.AddOrReplace(_metadata, storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Delete(CreateId createId)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.Delete(_metadata, createId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode DestroyFile()
|
||||||
|
{
|
||||||
|
if (!IsDatabaseTestModeEnabled())
|
||||||
|
{
|
||||||
|
return ResultCode.TestModeNotEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.DestroyFile(_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode DeleteFile()
|
||||||
|
{
|
||||||
|
if (!IsDatabaseTestModeEnabled())
|
||||||
|
{
|
||||||
|
return ResultCode.TestModeNotEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _database.DeleteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Format()
|
||||||
|
{
|
||||||
|
if (!IsDatabaseTestModeEnabled())
|
||||||
|
{
|
||||||
|
return ResultCode.TestModeNotEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
_database.Format(_metadata);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Import(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (!IsDatabaseTestModeEnabled())
|
||||||
|
{
|
||||||
|
return ResultCode.TestModeNotEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Export(Span<byte> data)
|
||||||
|
{
|
||||||
|
if (!IsDatabaseTestModeEnabled())
|
||||||
|
{
|
||||||
|
return ResultCode.TestModeNotEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode IsBrokenDatabaseWithClearFlag(out bool isBrokenDatabase)
|
||||||
|
{
|
||||||
|
if (!_isSystem)
|
||||||
|
{
|
||||||
|
isBrokenDatabase = false;
|
||||||
|
|
||||||
|
return ResultCode.PermissionDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBrokenDatabase = _database.IsBrokenDatabaseWithClearFlag();
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode GetIndex(CharInfo charInfo, out int index)
|
||||||
|
{
|
||||||
|
return _database.GetIndex(_metadata, charInfo, out index);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void SetInterfaceVersion(uint interfaceVersion)
|
||||||
|
{
|
||||||
|
_database.SetInterfaceVersion(_metadata, interfaceVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Convert(Ver3StoreData ver3StoreData, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo)
|
||||||
|
{
|
||||||
|
return _database.ConvertCoreDataToCharInfo(coreData, out charInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
||||||
|
{
|
||||||
|
return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
423
Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
Normal file
423
Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
||||||
|
{
|
||||||
|
abstract class IDatabaseService : IpcService
|
||||||
|
{
|
||||||
|
[Command(0)]
|
||||||
|
// IsUpdated(SourceFlag flag) -> bool
|
||||||
|
public ResultCode IsUpdated(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
context.ResponseData.Write(IsUpdated(flag));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(1)]
|
||||||
|
// IsFullDatabase() -> bool
|
||||||
|
public ResultCode IsFullDatabase(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write(IsFullDatabase());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(2)]
|
||||||
|
// GetCount(SourceFlag flag) -> u32
|
||||||
|
public ResultCode GetCount(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
context.ResponseData.Write(GetCount(flag));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(3)]
|
||||||
|
// Get(SourceFlag flag) -> (s32 count, buffer<nn::mii::CharInfoRawElement, 6>)
|
||||||
|
public ResultCode Get(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
IpcBuffDesc outputBuffer = context.Request.ReceiveBuff[0];
|
||||||
|
|
||||||
|
Span<CharInfoElement> elementsSpan = CreateSpanFromBuffer<CharInfoElement>(context, outputBuffer, true);
|
||||||
|
|
||||||
|
ResultCode result = Get(flag, out int count, elementsSpan);
|
||||||
|
|
||||||
|
elementsSpan = elementsSpan.Slice(0, count);
|
||||||
|
|
||||||
|
context.ResponseData.Write(count);
|
||||||
|
|
||||||
|
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(4)]
|
||||||
|
// Get1(SourceFlag flag) -> (s32 count, buffer<nn::mii::CharInfo, 6>)
|
||||||
|
public ResultCode Get1(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
IpcBuffDesc outputBuffer = context.Request.ReceiveBuff[0];
|
||||||
|
|
||||||
|
Span<CharInfo> elementsSpan = CreateSpanFromBuffer<CharInfo>(context, outputBuffer, true);
|
||||||
|
|
||||||
|
ResultCode result = Get1(flag, out int count, elementsSpan);
|
||||||
|
|
||||||
|
elementsSpan = elementsSpan.Slice(0, count);
|
||||||
|
|
||||||
|
context.ResponseData.Write(count);
|
||||||
|
|
||||||
|
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(5)]
|
||||||
|
// UpdateLatest(nn::mii::CharInfo old_char_info, SourceFlag flag) -> nn::mii::CharInfo
|
||||||
|
public ResultCode UpdateLatest(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CharInfo oldCharInfo = context.RequestData.ReadStruct<CharInfo>();
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
ResultCode result = UpdateLatest(oldCharInfo, flag, out CharInfo newCharInfo);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(newCharInfo);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(6)]
|
||||||
|
// BuildRandom(Age age, Gender gender, Race race) -> nn::mii::CharInfo
|
||||||
|
public ResultCode BuildRandom(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Age age = (Age)context.RequestData.ReadInt32();
|
||||||
|
Gender gender = (Gender)context.RequestData.ReadInt32();
|
||||||
|
Race race = (Race)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
ResultCode result = BuildRandom(age, gender, race, out CharInfo charInfo);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(charInfo);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(7)]
|
||||||
|
// BuildDefault(u32 index) -> nn::mii::CharInfoRaw
|
||||||
|
public ResultCode BuildDefault(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint index = context.RequestData.ReadUInt32();
|
||||||
|
|
||||||
|
ResultCode result = BuildDefault(index, out CharInfo charInfo);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(charInfo);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(8)]
|
||||||
|
// Get2(SourceFlag flag) -> (u32 count, buffer<nn::mii::StoreDataElement, 6>)
|
||||||
|
public ResultCode Get2(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
IpcBuffDesc outputBuffer = context.Request.ReceiveBuff[0];
|
||||||
|
|
||||||
|
Span<StoreDataElement> elementsSpan = CreateSpanFromBuffer<StoreDataElement>(context, outputBuffer, true);
|
||||||
|
|
||||||
|
ResultCode result = Get2(flag, out int count, elementsSpan);
|
||||||
|
|
||||||
|
elementsSpan = elementsSpan.Slice(0, count);
|
||||||
|
|
||||||
|
context.ResponseData.Write(count);
|
||||||
|
|
||||||
|
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(9)]
|
||||||
|
// Get3(SourceFlag flag) -> (u32 count, buffer<nn::mii::StoreData, 6>)
|
||||||
|
public ResultCode Get3(ServiceCtx context)
|
||||||
|
{
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
IpcBuffDesc outputBuffer = context.Request.ReceiveBuff[0];
|
||||||
|
|
||||||
|
Span<StoreData> elementsSpan = CreateSpanFromBuffer<StoreData>(context, outputBuffer, true);
|
||||||
|
|
||||||
|
ResultCode result = Get3(flag, out int count, elementsSpan);
|
||||||
|
|
||||||
|
elementsSpan = elementsSpan.Slice(0, count);
|
||||||
|
|
||||||
|
context.ResponseData.Write(count);
|
||||||
|
|
||||||
|
WriteSpanToBuffer(context, outputBuffer, elementsSpan);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(10)]
|
||||||
|
// UpdateLatest1(nn::mii::StoreData old_store_data, SourceFlag flag) -> nn::mii::StoreData
|
||||||
|
public ResultCode UpdateLatest1(ServiceCtx context)
|
||||||
|
{
|
||||||
|
StoreData oldStoreData = context.RequestData.ReadStruct<StoreData>();
|
||||||
|
SourceFlag flag = (SourceFlag)context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
ResultCode result = UpdateLatest1(oldStoreData, flag, out StoreData newStoreData);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(newStoreData);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(11)]
|
||||||
|
// FindIndex(nn::mii::CreateId create_id, bool is_special) -> s32
|
||||||
|
public ResultCode FindIndex(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CreateId createId = context.RequestData.ReadStruct<CreateId>();
|
||||||
|
bool isSpecial = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
|
ResultCode result = FindIndex(createId, isSpecial, out int index);
|
||||||
|
|
||||||
|
context.ResponseData.Write(index);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(12)]
|
||||||
|
// Move(nn::mii::CreateId create_id, s32 new_index)
|
||||||
|
public ResultCode Move(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CreateId createId = context.RequestData.ReadStruct<CreateId>();
|
||||||
|
int newIndex = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
return Move(createId, newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(13)]
|
||||||
|
// AddOrReplace(nn::mii::StoreData store_data)
|
||||||
|
public ResultCode AddOrReplace(ServiceCtx context)
|
||||||
|
{
|
||||||
|
StoreData storeData = context.RequestData.ReadStruct<StoreData>();
|
||||||
|
|
||||||
|
return AddOrReplace(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(14)]
|
||||||
|
// Delete(nn::mii::CreateId create_id)
|
||||||
|
public ResultCode Delete(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CreateId createId = context.RequestData.ReadStruct<CreateId>();
|
||||||
|
|
||||||
|
return Delete(createId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(15)]
|
||||||
|
// DestroyFile()
|
||||||
|
public ResultCode DestroyFile(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return DestroyFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(16)]
|
||||||
|
// DeleteFile()
|
||||||
|
public ResultCode DeleteFile(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return DeleteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(17)]
|
||||||
|
// Format()
|
||||||
|
public ResultCode Format(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return Format();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(18)]
|
||||||
|
// Import(buffer<bytes, 5>)
|
||||||
|
public ResultCode Import(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> data = CreateByteSpanFromBuffer(context, context.Request.SendBuff[0], false);
|
||||||
|
|
||||||
|
return Import(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(19)]
|
||||||
|
// Export() -> buffer<bytes, 6>
|
||||||
|
public ResultCode Export(ServiceCtx context)
|
||||||
|
{
|
||||||
|
IpcBuffDesc outputBuffer = context.Request.ReceiveBuff[0];
|
||||||
|
|
||||||
|
Span<byte> data = CreateByteSpanFromBuffer(context, outputBuffer, true);
|
||||||
|
|
||||||
|
ResultCode result = Export(data);
|
||||||
|
|
||||||
|
context.Memory.WriteBytes(outputBuffer.Position, data.ToArray());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(20)]
|
||||||
|
// IsBrokenDatabaseWithClearFlag() -> bool
|
||||||
|
public ResultCode IsBrokenDatabaseWithClearFlag(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ResultCode result = IsBrokenDatabaseWithClearFlag(out bool isBrokenDatabase);
|
||||||
|
|
||||||
|
context.ResponseData.Write(isBrokenDatabase);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(21)]
|
||||||
|
// GetIndex(nn::mii::CharInfo char_info) -> s32
|
||||||
|
public ResultCode GetIndex(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
|
||||||
|
|
||||||
|
ResultCode result = GetIndex(charInfo, out int index);
|
||||||
|
|
||||||
|
context.ResponseData.Write(index);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(22)] // 5.0.0+
|
||||||
|
// SetInterfaceVersion(u32 version)
|
||||||
|
public ResultCode SetInterfaceVersion(ServiceCtx context)
|
||||||
|
{
|
||||||
|
uint interfaceVersion = context.RequestData.ReadUInt32();
|
||||||
|
|
||||||
|
SetInterfaceVersion(interfaceVersion);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(23)] // 5.0.0+
|
||||||
|
// Convert(nn::mii::Ver3StoreData ver3_store_data) -> nn::mii::CharInfo
|
||||||
|
public ResultCode Convert(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Ver3StoreData ver3StoreData = context.RequestData.ReadStruct<Ver3StoreData>();
|
||||||
|
|
||||||
|
ResultCode result = Convert(ver3StoreData, out CharInfo charInfo);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(charInfo);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(24)] // 7.0.0+
|
||||||
|
// ConvertCoreDataToCharInfo(nn::mii::CoreData core_data) -> nn::mii::CharInfo
|
||||||
|
public ResultCode ConvertCoreDataToCharInfo(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CoreData coreData = context.RequestData.ReadStruct<CoreData>();
|
||||||
|
|
||||||
|
ResultCode result = ConvertCoreDataToCharInfo(coreData, out CharInfo charInfo);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(charInfo);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(25)] // 7.0.0+
|
||||||
|
// ConvertCharInfoToCoreData(nn::mii::CharInfo char_info) -> nn::mii::CoreData
|
||||||
|
public ResultCode ConvertCharInfoToCoreData(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
|
||||||
|
|
||||||
|
ResultCode result = ConvertCharInfoToCoreData(charInfo, out CoreData coreData);
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(coreData);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
||||||
|
{
|
||||||
|
byte[] rawData;
|
||||||
|
|
||||||
|
if (isOutput)
|
||||||
|
{
|
||||||
|
rawData = new byte[ipcBuff.Size];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rawData = context.Memory.ReadBytes(ipcBuff.Position, ipcBuff.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Span<byte>(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Span<T> CreateSpanFromBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) where T: unmanaged
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<byte, T>(CreateByteSpanFromBuffer(context, ipcBuff, isOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteSpanToBuffer<T>(ServiceCtx context, IpcBuffDesc ipcBuff, Span<T> span) where T: unmanaged
|
||||||
|
{
|
||||||
|
Span<byte> rawData = MemoryMarshal.Cast<T, byte>(span);
|
||||||
|
|
||||||
|
context.Memory.WriteBytes(ipcBuff.Position, rawData.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool IsUpdated(SourceFlag flag);
|
||||||
|
|
||||||
|
protected abstract bool IsFullDatabase();
|
||||||
|
|
||||||
|
protected abstract uint GetCount(SourceFlag flag);
|
||||||
|
|
||||||
|
protected abstract ResultCode Get(SourceFlag flag, out int count, Span<CharInfoElement> elements);
|
||||||
|
|
||||||
|
protected abstract ResultCode Get1(SourceFlag flag, out int count, Span<CharInfo> elements);
|
||||||
|
|
||||||
|
protected abstract ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo);
|
||||||
|
|
||||||
|
protected abstract ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo);
|
||||||
|
|
||||||
|
protected abstract ResultCode BuildDefault(uint index, out CharInfo charInfo);
|
||||||
|
|
||||||
|
protected abstract ResultCode Get2(SourceFlag flag, out int count, Span<StoreDataElement> elements);
|
||||||
|
|
||||||
|
protected abstract ResultCode Get3(SourceFlag flag, out int count, Span<StoreData> elements);
|
||||||
|
|
||||||
|
protected abstract ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData);
|
||||||
|
|
||||||
|
protected abstract ResultCode FindIndex(CreateId createId, bool isSpecial, out int index);
|
||||||
|
|
||||||
|
protected abstract ResultCode Move(CreateId createId, int newIndex);
|
||||||
|
|
||||||
|
protected abstract ResultCode AddOrReplace(StoreData storeData);
|
||||||
|
|
||||||
|
protected abstract ResultCode Delete(CreateId createId);
|
||||||
|
|
||||||
|
protected abstract ResultCode DestroyFile();
|
||||||
|
|
||||||
|
protected abstract ResultCode DeleteFile();
|
||||||
|
|
||||||
|
protected abstract ResultCode Format();
|
||||||
|
|
||||||
|
protected abstract ResultCode Import(ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
|
protected abstract ResultCode Export(Span<byte> data);
|
||||||
|
|
||||||
|
protected abstract ResultCode IsBrokenDatabaseWithClearFlag(out bool isBrokenDatabase);
|
||||||
|
|
||||||
|
protected abstract ResultCode GetIndex(CharInfo charInfo, out int index);
|
||||||
|
|
||||||
|
protected abstract void SetInterfaceVersion(uint interfaceVersion);
|
||||||
|
|
||||||
|
protected abstract ResultCode Convert(Ver3StoreData ver3StoreData, out CharInfo charInfo);
|
||||||
|
|
||||||
|
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
|
||||||
|
|
||||||
|
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs
Normal file
10
Ryujinx.HLE/HOS/Services/Mii/Types/Age.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum Age : uint
|
||||||
|
{
|
||||||
|
Young,
|
||||||
|
Normal,
|
||||||
|
Old,
|
||||||
|
All
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs
Normal file
15
Ryujinx.HLE/HOS/Services/Mii/Types/BeardType.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum BeardType : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Goatee,
|
||||||
|
GoateeLong,
|
||||||
|
LionsManeLong,
|
||||||
|
LionsMane,
|
||||||
|
Full,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 5
|
||||||
|
}
|
||||||
|
}
|
329
Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs
Normal file
329
Ryujinx.HLE/HOS/Services/Mii/Types/CharInfo.cs
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x58)]
|
||||||
|
struct CharInfo : IStoredData<CharInfo>
|
||||||
|
{
|
||||||
|
public CreateId CreateId;
|
||||||
|
public Nickname Nickname;
|
||||||
|
public FontRegion FontRegion;
|
||||||
|
public byte FavoriteColor;
|
||||||
|
public Gender Gender;
|
||||||
|
public byte Height;
|
||||||
|
public byte Build;
|
||||||
|
public byte Type;
|
||||||
|
public byte RegionMove;
|
||||||
|
public FacelineType FacelineType;
|
||||||
|
public FacelineColor FacelineColor;
|
||||||
|
public FacelineWrinkle FacelineWrinkle;
|
||||||
|
public FacelineMake FacelineMake;
|
||||||
|
public HairType HairType;
|
||||||
|
public CommonColor HairColor;
|
||||||
|
public HairFlip HairFlip;
|
||||||
|
public EyeType EyeType;
|
||||||
|
public CommonColor EyeColor;
|
||||||
|
public byte EyeScale;
|
||||||
|
public byte EyeAspect;
|
||||||
|
public byte EyeRotate;
|
||||||
|
public byte EyeX;
|
||||||
|
public byte EyeY;
|
||||||
|
public EyebrowType EyebrowType;
|
||||||
|
public CommonColor EyebrowColor;
|
||||||
|
public byte EyebrowScale;
|
||||||
|
public byte EyebrowAspect;
|
||||||
|
public byte EyebrowRotate;
|
||||||
|
public byte EyebrowX;
|
||||||
|
public byte EyebrowY;
|
||||||
|
public NoseType NoseType;
|
||||||
|
public byte NoseScale;
|
||||||
|
public byte NoseY;
|
||||||
|
public MouthType MouthType;
|
||||||
|
public CommonColor MouthColor;
|
||||||
|
public byte MouthScale;
|
||||||
|
public byte MouthAspect;
|
||||||
|
public byte MouthY;
|
||||||
|
public CommonColor BeardColor;
|
||||||
|
public BeardType BeardType;
|
||||||
|
public MustacheType MustacheType;
|
||||||
|
public byte MustacheScale;
|
||||||
|
public byte MustacheY;
|
||||||
|
public GlassType GlassType;
|
||||||
|
public CommonColor GlassColor;
|
||||||
|
public byte GlassScale;
|
||||||
|
public byte GlassY;
|
||||||
|
public MoleType MoleType;
|
||||||
|
public byte MoleScale;
|
||||||
|
public byte MoleX;
|
||||||
|
public byte MoleY;
|
||||||
|
public byte Reserved;
|
||||||
|
|
||||||
|
byte IStoredData<CharInfo>.Type => Type;
|
||||||
|
|
||||||
|
CreateId IStoredData<CharInfo>.CreateId => CreateId;
|
||||||
|
|
||||||
|
public ResultCode InvalidData => ResultCode.InvalidCharInfo;
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
return Verify() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Verify()
|
||||||
|
{
|
||||||
|
if (!CreateId.IsValid) return 50;
|
||||||
|
if (!Nickname.IsValid()) return 51;
|
||||||
|
if ((byte)FontRegion > 3) return 23;
|
||||||
|
if (FavoriteColor > 11) return 22;
|
||||||
|
if (Gender > Gender.Max) return 24;
|
||||||
|
if ((sbyte)Height < 0) return 32;
|
||||||
|
if ((sbyte)Build < 0) return 3;
|
||||||
|
if (Type > 1) return 53;
|
||||||
|
if (RegionMove > 3) return 49;
|
||||||
|
if (FacelineType > FacelineType.Max) return 21;
|
||||||
|
if (FacelineColor > FacelineColor.Max) return 18;
|
||||||
|
if (FacelineWrinkle > FacelineWrinkle.Max) return 20;
|
||||||
|
if (FacelineMake > FacelineMake.Max) return 19;
|
||||||
|
if (HairType > HairType.Max) return 31;
|
||||||
|
if (HairColor > CommonColor.Max) return 29;
|
||||||
|
if (HairFlip > HairFlip.Max) return 30;
|
||||||
|
if (EyeType > EyeType.Max) return 8;
|
||||||
|
if (EyeColor > CommonColor.Max) return 5;
|
||||||
|
if (EyeScale > 7) return 7;
|
||||||
|
if (EyeAspect > 6) return 4;
|
||||||
|
if (EyeRotate > 7) return 6;
|
||||||
|
if (EyeX > 12) return 9;
|
||||||
|
if (EyeY > 18) return 10;
|
||||||
|
if (EyebrowType > EyebrowType.Max) return 15;
|
||||||
|
if (EyebrowColor > CommonColor.Max) return 12;
|
||||||
|
if (EyebrowScale > 8) return 14;
|
||||||
|
if (EyebrowAspect > 6) return 11;
|
||||||
|
if (EyebrowRotate > 11) return 13;
|
||||||
|
if (EyebrowX > 12) return 16;
|
||||||
|
if (EyebrowY - 3 > 15) return 17;
|
||||||
|
if (NoseType > NoseType.Max) return 47;
|
||||||
|
if (NoseScale > 8) return 46;
|
||||||
|
if (NoseY> 18) return 48;
|
||||||
|
if (MouthType > MouthType.Max) return 40;
|
||||||
|
if (MouthColor > CommonColor.Max) return 38;
|
||||||
|
if (MouthScale > 8) return 39;
|
||||||
|
if (MouthAspect > 6) return 37;
|
||||||
|
if (MouthY > 18) return 41;
|
||||||
|
if (BeardColor > CommonColor.Max) return 1;
|
||||||
|
if (BeardType > BeardType.Max) return 2;
|
||||||
|
if (MustacheType > MustacheType.Max) return 43;
|
||||||
|
if (MustacheScale > 8) return 42;
|
||||||
|
if (MustacheY > 16) return 44;
|
||||||
|
if (GlassType > GlassType.Max) return 27;
|
||||||
|
if (GlassColor > CommonColor.Max) return 25;
|
||||||
|
if (GlassScale > 7) return 26;
|
||||||
|
if (GlassY > 20) return 28;
|
||||||
|
if (MoleType > MoleType.Max) return 34;
|
||||||
|
if (MoleScale > 8) return 33;
|
||||||
|
if (MoleX > 16) return 35;
|
||||||
|
if (MoleY >= 31) return 36;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFromStoreData(StoreData storeData)
|
||||||
|
{
|
||||||
|
Nickname = storeData.CoreData.Nickname;
|
||||||
|
CreateId = storeData.CreateId;
|
||||||
|
FontRegion = storeData.CoreData.FontRegion;
|
||||||
|
FavoriteColor = storeData.CoreData.FavoriteColor;
|
||||||
|
Gender = storeData.CoreData.Gender;
|
||||||
|
Height = storeData.CoreData.Height;
|
||||||
|
Build = storeData.CoreData.Build;
|
||||||
|
Type = storeData.CoreData.Type;
|
||||||
|
RegionMove = storeData.CoreData.RegionMove;
|
||||||
|
FacelineType = storeData.CoreData.FacelineType;
|
||||||
|
FacelineColor = storeData.CoreData.FacelineColor;
|
||||||
|
FacelineWrinkle = storeData.CoreData.FacelineWrinkle;
|
||||||
|
FacelineMake = storeData.CoreData.FacelineMake;
|
||||||
|
HairType = storeData.CoreData.HairType;
|
||||||
|
HairColor = storeData.CoreData.HairColor;
|
||||||
|
HairFlip = storeData.CoreData.HairFlip;
|
||||||
|
EyeType = storeData.CoreData.EyeType;
|
||||||
|
EyeColor = storeData.CoreData.EyeColor;
|
||||||
|
EyeScale = storeData.CoreData.EyeScale;
|
||||||
|
EyeAspect = storeData.CoreData.EyeAspect;
|
||||||
|
EyeRotate = storeData.CoreData.EyeRotate;
|
||||||
|
EyeX = storeData.CoreData.EyeX;
|
||||||
|
EyeY = storeData.CoreData.EyeY;
|
||||||
|
EyebrowType = storeData.CoreData.EyebrowType;
|
||||||
|
EyebrowColor = storeData.CoreData.EyebrowColor;
|
||||||
|
EyebrowScale = storeData.CoreData.EyebrowScale;
|
||||||
|
EyebrowAspect = storeData.CoreData.EyebrowAspect;
|
||||||
|
EyebrowRotate = storeData.CoreData.EyebrowRotate;
|
||||||
|
EyebrowX = storeData.CoreData.EyebrowX;
|
||||||
|
EyebrowY = storeData.CoreData.EyebrowY;
|
||||||
|
NoseType = storeData.CoreData.NoseType;
|
||||||
|
NoseScale = storeData.CoreData.NoseScale;
|
||||||
|
NoseY = storeData.CoreData.NoseY;
|
||||||
|
MouthType = storeData.CoreData.MouthType;
|
||||||
|
MouthColor = storeData.CoreData.MouthColor;
|
||||||
|
MouthScale = storeData.CoreData.MouthScale;
|
||||||
|
MouthAspect = storeData.CoreData.MouthAspect;
|
||||||
|
MouthY = storeData.CoreData.MouthY;
|
||||||
|
BeardColor = storeData.CoreData.BeardColor;
|
||||||
|
BeardType = storeData.CoreData.BeardType;
|
||||||
|
MustacheType = storeData.CoreData.MustacheType;
|
||||||
|
MustacheScale = storeData.CoreData.MustacheScale;
|
||||||
|
MustacheY = storeData.CoreData.MustacheY;
|
||||||
|
GlassType = storeData.CoreData.GlassType;
|
||||||
|
GlassColor = storeData.CoreData.GlassColor;
|
||||||
|
GlassScale = storeData.CoreData.GlassScale;
|
||||||
|
GlassY = storeData.CoreData.GlassY;
|
||||||
|
MoleType = storeData.CoreData.MoleType;
|
||||||
|
MoleScale = storeData.CoreData.MoleScale;
|
||||||
|
MoleX = storeData.CoreData.MoleX;
|
||||||
|
MoleY = storeData.CoreData.MoleY;
|
||||||
|
Reserved = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSource(Source source)
|
||||||
|
{
|
||||||
|
// Only implemented for Element variants.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(CharInfo x, CharInfo y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(CharInfo x, CharInfo y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is CharInfo charInfo && Equals(charInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(CharInfo cmpObj)
|
||||||
|
{
|
||||||
|
if (!cmpObj.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
result &= Nickname == cmpObj.Nickname;
|
||||||
|
result &= CreateId == cmpObj.CreateId;
|
||||||
|
result &= FontRegion == cmpObj.FontRegion;
|
||||||
|
result &= FavoriteColor == cmpObj.FavoriteColor;
|
||||||
|
result &= Gender == cmpObj.Gender;
|
||||||
|
result &= Height == cmpObj.Height;
|
||||||
|
result &= Build == cmpObj.Build;
|
||||||
|
result &= Type == cmpObj.Type;
|
||||||
|
result &= RegionMove == cmpObj.RegionMove;
|
||||||
|
result &= FacelineType == cmpObj.FacelineType;
|
||||||
|
result &= FacelineColor == cmpObj.FacelineColor;
|
||||||
|
result &= FacelineWrinkle == cmpObj.FacelineWrinkle;
|
||||||
|
result &= FacelineMake == cmpObj.FacelineMake;
|
||||||
|
result &= HairType == cmpObj.HairType;
|
||||||
|
result &= HairColor == cmpObj.HairColor;
|
||||||
|
result &= HairFlip == cmpObj.HairFlip;
|
||||||
|
result &= EyeType == cmpObj.EyeType;
|
||||||
|
result &= EyeColor == cmpObj.EyeColor;
|
||||||
|
result &= EyeScale == cmpObj.EyeScale;
|
||||||
|
result &= EyeAspect == cmpObj.EyeAspect;
|
||||||
|
result &= EyeRotate == cmpObj.EyeRotate;
|
||||||
|
result &= EyeX == cmpObj.EyeX;
|
||||||
|
result &= EyeY == cmpObj.EyeY;
|
||||||
|
result &= EyebrowType == cmpObj.EyebrowType;
|
||||||
|
result &= EyebrowColor == cmpObj.EyebrowColor;
|
||||||
|
result &= EyebrowScale == cmpObj.EyebrowScale;
|
||||||
|
result &= EyebrowAspect == cmpObj.EyebrowAspect;
|
||||||
|
result &= EyebrowRotate == cmpObj.EyebrowRotate;
|
||||||
|
result &= EyebrowX == cmpObj.EyebrowX;
|
||||||
|
result &= EyebrowY == cmpObj.EyebrowY;
|
||||||
|
result &= NoseType == cmpObj.NoseType;
|
||||||
|
result &= NoseScale == cmpObj.NoseScale;
|
||||||
|
result &= NoseY == cmpObj.NoseY;
|
||||||
|
result &= MouthType == cmpObj.MouthType;
|
||||||
|
result &= MouthColor == cmpObj.MouthColor;
|
||||||
|
result &= MouthScale == cmpObj.MouthScale;
|
||||||
|
result &= MouthAspect == cmpObj.MouthAspect;
|
||||||
|
result &= MouthY == cmpObj.MouthY;
|
||||||
|
result &= BeardColor == cmpObj.BeardColor;
|
||||||
|
result &= BeardType == cmpObj.BeardType;
|
||||||
|
result &= MustacheType == cmpObj.MustacheType;
|
||||||
|
result &= MustacheScale == cmpObj.MustacheScale;
|
||||||
|
result &= MustacheY == cmpObj.MustacheY;
|
||||||
|
result &= GlassType == cmpObj.GlassType;
|
||||||
|
result &= GlassColor == cmpObj.GlassColor;
|
||||||
|
result &= GlassScale == cmpObj.GlassScale;
|
||||||
|
result &= GlassY == cmpObj.GlassY;
|
||||||
|
result &= MoleType == cmpObj.MoleType;
|
||||||
|
result &= MoleScale == cmpObj.MoleScale;
|
||||||
|
result &= MoleX == cmpObj.MoleX;
|
||||||
|
result &= MoleY == cmpObj.MoleY;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hashCode = new HashCode();
|
||||||
|
|
||||||
|
hashCode.Add(Nickname);
|
||||||
|
hashCode.Add(CreateId);
|
||||||
|
hashCode.Add(FontRegion);
|
||||||
|
hashCode.Add(FavoriteColor);
|
||||||
|
hashCode.Add(Gender);
|
||||||
|
hashCode.Add(Height);
|
||||||
|
hashCode.Add(Build);
|
||||||
|
hashCode.Add(Type);
|
||||||
|
hashCode.Add(RegionMove);
|
||||||
|
hashCode.Add(FacelineType);
|
||||||
|
hashCode.Add(FacelineColor);
|
||||||
|
hashCode.Add(FacelineWrinkle);
|
||||||
|
hashCode.Add(FacelineMake);
|
||||||
|
hashCode.Add(HairType);
|
||||||
|
hashCode.Add(HairColor);
|
||||||
|
hashCode.Add(HairFlip);
|
||||||
|
hashCode.Add(EyeType);
|
||||||
|
hashCode.Add(EyeColor);
|
||||||
|
hashCode.Add(EyeScale);
|
||||||
|
hashCode.Add(EyeAspect);
|
||||||
|
hashCode.Add(EyeRotate);
|
||||||
|
hashCode.Add(EyeX);
|
||||||
|
hashCode.Add(EyeY);
|
||||||
|
hashCode.Add(EyebrowType);
|
||||||
|
hashCode.Add(EyebrowColor);
|
||||||
|
hashCode.Add(EyebrowScale);
|
||||||
|
hashCode.Add(EyebrowAspect);
|
||||||
|
hashCode.Add(EyebrowRotate);
|
||||||
|
hashCode.Add(EyebrowX);
|
||||||
|
hashCode.Add(EyebrowY);
|
||||||
|
hashCode.Add(NoseType);
|
||||||
|
hashCode.Add(NoseScale);
|
||||||
|
hashCode.Add(NoseY);
|
||||||
|
hashCode.Add(MouthType);
|
||||||
|
hashCode.Add(MouthColor);
|
||||||
|
hashCode.Add(MouthScale);
|
||||||
|
hashCode.Add(MouthAspect);
|
||||||
|
hashCode.Add(MouthY);
|
||||||
|
hashCode.Add(BeardColor);
|
||||||
|
hashCode.Add(BeardType);
|
||||||
|
hashCode.Add(MustacheType);
|
||||||
|
hashCode.Add(MustacheScale);
|
||||||
|
hashCode.Add(MustacheY);
|
||||||
|
hashCode.Add(GlassType);
|
||||||
|
hashCode.Add(GlassColor);
|
||||||
|
hashCode.Add(GlassScale);
|
||||||
|
hashCode.Add(GlassY);
|
||||||
|
hashCode.Add(MoleType);
|
||||||
|
hashCode.Add(MoleScale);
|
||||||
|
hashCode.Add(MoleX);
|
||||||
|
hashCode.Add(MoleY);
|
||||||
|
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs
Normal file
21
Ryujinx.HLE/HOS/Services/Mii/Types/CharInfoElement.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x5C)]
|
||||||
|
struct CharInfoElement : IElement
|
||||||
|
{
|
||||||
|
public CharInfo CharInfo;
|
||||||
|
public Source Source;
|
||||||
|
|
||||||
|
public void SetFromStoreData(StoreData storeData)
|
||||||
|
{
|
||||||
|
CharInfo.SetFromStoreData(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSource(Source source)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs
Normal file
9
Ryujinx.HLE/HOS/Services/Mii/Types/CommonColor.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum CommonColor : byte
|
||||||
|
{
|
||||||
|
Min = 0,
|
||||||
|
Max = 99
|
||||||
|
}
|
||||||
|
}
|
911
Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs
Normal file
911
Ryujinx.HLE/HOS/Services/Mii/Types/CoreData.cs
Normal file
|
@ -0,0 +1,911 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Ryujinx.HLE.HOS.Services.Mii.Types.RandomMiiConstants;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = Size)]
|
||||||
|
struct CoreData : IEquatable<CoreData>
|
||||||
|
{
|
||||||
|
public const int Size = 0x30;
|
||||||
|
|
||||||
|
private byte _storage;
|
||||||
|
|
||||||
|
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x18)]
|
||||||
|
public struct ElementInfo
|
||||||
|
{
|
||||||
|
public int ByteOffset;
|
||||||
|
public int BitOffset;
|
||||||
|
public int BitWidth;
|
||||||
|
public int MinValue;
|
||||||
|
public int MaxValue;
|
||||||
|
public int Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private int GetValue(ElementInfoIndex index)
|
||||||
|
{
|
||||||
|
ElementInfo info = ElementInfos[(int)index];
|
||||||
|
|
||||||
|
return ((Storage[info.ByteOffset] >> info.BitOffset) & ~(-1 << info.BitWidth)) + info.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void SetValue(ElementInfoIndex index, int value)
|
||||||
|
{
|
||||||
|
ElementInfo info = ElementInfos[(int)index];
|
||||||
|
|
||||||
|
int newValue = Storage[info.ByteOffset] & ~(~(-1 << info.BitWidth) << info.BitOffset) | (((value - info.MinValue) & ~(-1 << info.BitWidth)) << info.BitOffset);
|
||||||
|
|
||||||
|
Storage[info.ByteOffset] = (byte)newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private bool IsElementValid(ElementInfoIndex index)
|
||||||
|
{
|
||||||
|
ElementInfo info = ElementInfos[(int)index];
|
||||||
|
|
||||||
|
int value = GetValue(index);
|
||||||
|
|
||||||
|
return value >= info.MinValue && value <= info.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid(bool acceptEmptyNickname = false)
|
||||||
|
{
|
||||||
|
if (!Nickname.IsValid() || (!acceptEmptyNickname && Nickname.IsEmpty()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ElementInfos.Length; i++)
|
||||||
|
{
|
||||||
|
if (!IsElementValid((ElementInfoIndex)i))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDefault()
|
||||||
|
{
|
||||||
|
Storage.Fill(0);
|
||||||
|
|
||||||
|
Nickname = Nickname.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HairType HairType
|
||||||
|
{
|
||||||
|
get => (HairType)GetValue(ElementInfoIndex.HairType);
|
||||||
|
set => SetValue(ElementInfoIndex.HairType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte Height
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.Height);
|
||||||
|
set => SetValue(ElementInfoIndex.Height, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MoleType MoleType
|
||||||
|
{
|
||||||
|
get => (MoleType)GetValue(ElementInfoIndex.MoleType);
|
||||||
|
set => SetValue(ElementInfoIndex.MoleType, (byte)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte Build
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.Build);
|
||||||
|
set => SetValue(ElementInfoIndex.Build, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HairFlip HairFlip
|
||||||
|
{
|
||||||
|
get => (HairFlip)GetValue(ElementInfoIndex.HairFlip);
|
||||||
|
set => SetValue(ElementInfoIndex.HairFlip, (byte)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor HairColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.HairColor);
|
||||||
|
set => SetValue(ElementInfoIndex.HairColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte Type
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.Type);
|
||||||
|
set => SetValue(ElementInfoIndex.Type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor EyeColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.EyeColor);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Gender Gender
|
||||||
|
{
|
||||||
|
get => (Gender)GetValue(ElementInfoIndex.Gender);
|
||||||
|
set => SetValue(ElementInfoIndex.Gender, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor EyebrowColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.EyebrowColor);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor MouthColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.MouthColor);
|
||||||
|
set => SetValue(ElementInfoIndex.MouthColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor BeardColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.BeardColor);
|
||||||
|
set => SetValue(ElementInfoIndex.BeardColor, (byte)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommonColor GlassColor
|
||||||
|
{
|
||||||
|
get => (CommonColor)GetValue(ElementInfoIndex.GlassColor);
|
||||||
|
set => SetValue(ElementInfoIndex.GlassColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EyeType EyeType
|
||||||
|
{
|
||||||
|
get => (EyeType)GetValue(ElementInfoIndex.EyeType);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte RegionMove
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.RegionMove);
|
||||||
|
set => SetValue(ElementInfoIndex.RegionMove, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MouthType MouthType
|
||||||
|
{
|
||||||
|
get => (MouthType)GetValue(ElementInfoIndex.MouthType);
|
||||||
|
set => SetValue(ElementInfoIndex.MouthType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontRegion FontRegion
|
||||||
|
{
|
||||||
|
get => (FontRegion)GetValue(ElementInfoIndex.FontRegion);
|
||||||
|
set => SetValue(ElementInfoIndex.FontRegion, (byte)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyeY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyeY);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte GlassScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.GlassScale);
|
||||||
|
set => SetValue(ElementInfoIndex.GlassScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EyebrowType EyebrowType
|
||||||
|
{
|
||||||
|
get => (EyebrowType)GetValue(ElementInfoIndex.EyebrowType);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MustacheType MustacheType
|
||||||
|
{
|
||||||
|
get => (MustacheType)GetValue(ElementInfoIndex.MustacheType);
|
||||||
|
set => SetValue(ElementInfoIndex.MustacheType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoseType NoseType
|
||||||
|
{
|
||||||
|
get => (NoseType)GetValue(ElementInfoIndex.NoseType);
|
||||||
|
set => SetValue(ElementInfoIndex.NoseType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeardType BeardType
|
||||||
|
{
|
||||||
|
get => (BeardType)GetValue(ElementInfoIndex.BeardType);
|
||||||
|
set => SetValue(ElementInfoIndex.BeardType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte NoseY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.NoseY);
|
||||||
|
set => SetValue(ElementInfoIndex.NoseY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MouthAspect
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MouthAspect);
|
||||||
|
set => SetValue(ElementInfoIndex.MouthAspect, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MouthY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MouthY);
|
||||||
|
set => SetValue(ElementInfoIndex.MouthY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyebrowAspect
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyebrowAspect);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowAspect, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MustacheY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MustacheY);
|
||||||
|
set => SetValue(ElementInfoIndex.MustacheY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyeRotate
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyeRotate);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeRotate, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte GlassY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.GlassY);
|
||||||
|
set => SetValue(ElementInfoIndex.GlassY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyeAspect
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyeAspect);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeAspect, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MoleX
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MoleX);
|
||||||
|
set => SetValue(ElementInfoIndex.MoleX, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyeScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyeScale);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MoleY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MoleY);
|
||||||
|
set => SetValue(ElementInfoIndex.MoleY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GlassType GlassType
|
||||||
|
{
|
||||||
|
get => (GlassType)GetValue(ElementInfoIndex.GlassType);
|
||||||
|
set => SetValue(ElementInfoIndex.GlassType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte FavoriteColor
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.FavoriteColor);
|
||||||
|
set => SetValue(ElementInfoIndex.FavoriteColor, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacelineType FacelineType
|
||||||
|
{
|
||||||
|
get => (FacelineType)GetValue(ElementInfoIndex.FacelineType);
|
||||||
|
set => SetValue(ElementInfoIndex.FacelineType, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacelineColor FacelineColor
|
||||||
|
{
|
||||||
|
get => (FacelineColor)GetValue(ElementInfoIndex.FacelineColor);
|
||||||
|
set => SetValue(ElementInfoIndex.FacelineColor, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacelineWrinkle FacelineWrinkle
|
||||||
|
{
|
||||||
|
get => (FacelineWrinkle)GetValue(ElementInfoIndex.FacelineWrinkle);
|
||||||
|
set => SetValue(ElementInfoIndex.FacelineWrinkle, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacelineMake FacelineMake
|
||||||
|
{
|
||||||
|
get => (FacelineMake)GetValue(ElementInfoIndex.FacelineMake);
|
||||||
|
set => SetValue(ElementInfoIndex.FacelineMake, (int)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyeX
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyeX);
|
||||||
|
set => SetValue(ElementInfoIndex.EyeX, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyebrowScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyebrowScale);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyebrowRotate
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyebrowRotate);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowRotate, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyebrowX
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyebrowX);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowX, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte EyebrowY
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.EyebrowY);
|
||||||
|
set => SetValue(ElementInfoIndex.EyebrowY, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte NoseScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.NoseScale);
|
||||||
|
set => SetValue(ElementInfoIndex.NoseScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MouthScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MouthScale);
|
||||||
|
set => SetValue(ElementInfoIndex.MouthScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MustacheScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MustacheScale);
|
||||||
|
set => SetValue(ElementInfoIndex.MustacheScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte MoleScale
|
||||||
|
{
|
||||||
|
get => (byte)GetValue(ElementInfoIndex.MoleScale);
|
||||||
|
set => SetValue(ElementInfoIndex.MoleScale, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> GetNicknameStorage()
|
||||||
|
{
|
||||||
|
return Storage.Slice(0x1c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Nickname Nickname
|
||||||
|
{
|
||||||
|
get => Nickname.FromBytes(GetNicknameStorage());
|
||||||
|
set => value.Raw.Slice(0, 20).CopyTo(GetNicknameStorage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CoreData BuildRandom(UtilityImpl utilImpl, Age age, Gender gender, Race race)
|
||||||
|
{
|
||||||
|
CoreData coreData = new CoreData();
|
||||||
|
|
||||||
|
coreData.SetDefault();
|
||||||
|
|
||||||
|
if (gender == Types.Gender.All)
|
||||||
|
{
|
||||||
|
gender = (Gender)utilImpl.GetRandom((int)gender);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (age == Age.All)
|
||||||
|
{
|
||||||
|
int ageDecade = utilImpl.GetRandom(10);
|
||||||
|
|
||||||
|
if (ageDecade >= 8)
|
||||||
|
{
|
||||||
|
age = Age.Old;
|
||||||
|
}
|
||||||
|
else if (ageDecade >= 4)
|
||||||
|
{
|
||||||
|
age = Age.Normal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
age = Age.Young;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (race == Race.All)
|
||||||
|
{
|
||||||
|
int raceTempValue = utilImpl.GetRandom(10);
|
||||||
|
|
||||||
|
if (raceTempValue >= 8)
|
||||||
|
{
|
||||||
|
race = Race.Black;
|
||||||
|
}
|
||||||
|
else if (raceTempValue >= 4)
|
||||||
|
{
|
||||||
|
race = Race.White;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
race = Race.Asian;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int axisY = 0;
|
||||||
|
|
||||||
|
if (gender == Types.Gender.Female && age == Age.Young)
|
||||||
|
{
|
||||||
|
axisY = utilImpl.GetRandom(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexFor4 = 3 * (int)age + 9 * (int)gender + (int)race;
|
||||||
|
|
||||||
|
var facelineTypeInfo = RandomMiiFacelineArray[indexFor4];
|
||||||
|
var facelineColorInfo = RandomMiiFacelineColorArray[3 * (int)gender + (int)race];
|
||||||
|
var facelineWrinkleInfo = RandomMiiFacelineWrinkleArray[indexFor4];
|
||||||
|
var facelineMakeInfo = RandomMiiFacelineMakeArray[indexFor4];
|
||||||
|
var hairTypeInfo = RandomMiiHairTypeArray[indexFor4];
|
||||||
|
var hairColorInfo = RandomMiiHairColorArray[3 * (int)race + (int)age];
|
||||||
|
var eyeTypeInfo = RandomMiiEyeTypeArray[indexFor4];
|
||||||
|
var eyeColorInfo = RandomMiiEyeColorArray[(int)race];
|
||||||
|
var eyebrowTypeInfo = RandomMiiEyebrowTypeArray[indexFor4];
|
||||||
|
var noseTypeInfo = RandomMiiNoseTypeArray[indexFor4];
|
||||||
|
var mouthTypeInfo = RandomMiiMouthTypeArray[indexFor4];
|
||||||
|
var glassTypeInfo = RandomMiiGlassTypeArray[(int)age];
|
||||||
|
|
||||||
|
// Faceline
|
||||||
|
coreData.FacelineType = (FacelineType)facelineTypeInfo.Values[utilImpl.GetRandom(facelineTypeInfo.ValuesCount)];
|
||||||
|
coreData.FacelineColor = (FacelineColor)Helper.Ver3FacelineColorTable[facelineColorInfo.Values[utilImpl.GetRandom(facelineColorInfo.ValuesCount)]];
|
||||||
|
coreData.FacelineWrinkle = (FacelineWrinkle)facelineWrinkleInfo.Values[utilImpl.GetRandom(facelineWrinkleInfo.ValuesCount)];
|
||||||
|
coreData.FacelineMake = (FacelineMake)facelineMakeInfo.Values[utilImpl.GetRandom(facelineMakeInfo.ValuesCount)];
|
||||||
|
|
||||||
|
// Hair
|
||||||
|
coreData.HairType = (HairType)hairTypeInfo.Values[utilImpl.GetRandom(hairTypeInfo.ValuesCount)];
|
||||||
|
coreData.HairColor = (CommonColor)Helper.Ver3HairColorTable[hairColorInfo.Values[utilImpl.GetRandom(hairColorInfo.ValuesCount)]];
|
||||||
|
coreData.HairFlip = (HairFlip)utilImpl.GetRandom((int)HairFlip.Max + 1);
|
||||||
|
|
||||||
|
// Eye
|
||||||
|
coreData.EyeType = (EyeType)eyeTypeInfo.Values[utilImpl.GetRandom(eyeTypeInfo.ValuesCount)];
|
||||||
|
|
||||||
|
int eyeRotateKey1 = gender != Types.Gender.Male ? 4 : 2;
|
||||||
|
int eyeRotateKey2 = gender != Types.Gender.Male ? 3 : 4;
|
||||||
|
|
||||||
|
byte eyeRotateOffset = (byte)(32 - EyeRotateTable[eyeRotateKey1] + eyeRotateKey2);
|
||||||
|
byte eyeRotate = (byte)(32 - EyeRotateTable[(int)coreData.EyeType]);
|
||||||
|
|
||||||
|
coreData.EyeColor = (CommonColor)Helper.Ver3EyeColorTable[eyeColorInfo.Values[utilImpl.GetRandom(eyeColorInfo.ValuesCount)]];
|
||||||
|
coreData.EyeScale = 4;
|
||||||
|
coreData.EyeAspect = 3;
|
||||||
|
coreData.EyeRotate = (byte)(eyeRotateOffset - eyeRotate);
|
||||||
|
coreData.EyeX = 2;
|
||||||
|
coreData.EyeY = (byte)(axisY + 12);
|
||||||
|
|
||||||
|
// Eyebrow
|
||||||
|
coreData.EyebrowType = (EyebrowType)eyebrowTypeInfo.Values[utilImpl.GetRandom(eyebrowTypeInfo.ValuesCount)];
|
||||||
|
|
||||||
|
int eyebrowRotateKey = race == Race.Asian ? 6 : 0;
|
||||||
|
int eyebrowY = race == Race.Asian ? 9 : 10;
|
||||||
|
|
||||||
|
byte eyebrowRotateOffset = (byte)(32 - EyebrowRotateTable[eyebrowRotateKey] + 6);
|
||||||
|
byte eyebrowRotate = (byte)(32 - EyebrowRotateTable[(int)coreData.EyebrowType]);
|
||||||
|
|
||||||
|
coreData.EyebrowColor = coreData.HairColor;
|
||||||
|
coreData.EyebrowScale = 4;
|
||||||
|
coreData.EyebrowAspect = 3;
|
||||||
|
coreData.EyebrowRotate = (byte)(eyebrowRotateOffset - eyebrowRotate);
|
||||||
|
coreData.EyebrowX = 2;
|
||||||
|
coreData.EyebrowY = (byte)(axisY + eyebrowY);
|
||||||
|
|
||||||
|
// Nose
|
||||||
|
int noseScale = gender == Types.Gender.Female ? 3 : 4;
|
||||||
|
|
||||||
|
coreData.NoseType = (NoseType)noseTypeInfo.Values[utilImpl.GetRandom(noseTypeInfo.ValuesCount)];
|
||||||
|
coreData.NoseScale = (byte)noseScale;
|
||||||
|
coreData.NoseY = (byte)(axisY + 9);
|
||||||
|
|
||||||
|
// Mouth
|
||||||
|
int mouthColor = gender == Types.Gender.Female ? utilImpl.GetRandom(0, 4) : 0;
|
||||||
|
|
||||||
|
coreData.MouthType = (MouthType)mouthTypeInfo.Values[utilImpl.GetRandom(mouthTypeInfo.ValuesCount)];
|
||||||
|
coreData.MouthColor = (CommonColor)Helper.Ver3MouthColorTable[mouthColor];
|
||||||
|
coreData.MouthScale = 4;
|
||||||
|
coreData.MouthAspect = 3;
|
||||||
|
coreData.MouthY = (byte)(axisY + 13);
|
||||||
|
|
||||||
|
// Beard & Mustache
|
||||||
|
coreData.BeardColor = coreData.HairColor;
|
||||||
|
coreData.MustacheScale = 4;
|
||||||
|
|
||||||
|
if (gender == Types.Gender.Male && age != Age.Young && utilImpl.GetRandom(10) < 2)
|
||||||
|
{
|
||||||
|
BeardAndMustacheFlag mustacheAndBeardFlag = (BeardAndMustacheFlag)utilImpl.GetRandom(3);
|
||||||
|
|
||||||
|
BeardType beardType = BeardType.None;
|
||||||
|
MustacheType mustacheType = MustacheType.None;
|
||||||
|
|
||||||
|
if ((mustacheAndBeardFlag & BeardAndMustacheFlag.Beard) == BeardAndMustacheFlag.Beard)
|
||||||
|
{
|
||||||
|
beardType = (BeardType)utilImpl.GetRandom((int)BeardType.Goatee, (int)BeardType.Full);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mustacheAndBeardFlag & BeardAndMustacheFlag.Mustache) == BeardAndMustacheFlag.Mustache)
|
||||||
|
{
|
||||||
|
mustacheType = (MustacheType)utilImpl.GetRandom((int)MustacheType.Walrus, (int)MustacheType.Toothbrush);
|
||||||
|
}
|
||||||
|
|
||||||
|
coreData.MustacheType = mustacheType;
|
||||||
|
coreData.BeardType = beardType;
|
||||||
|
coreData.MustacheY = 10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
coreData.MustacheType = MustacheType.None;
|
||||||
|
coreData.BeardType = BeardType.None;
|
||||||
|
coreData.MustacheY = (byte)(axisY + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glass
|
||||||
|
int glassTypeStart = utilImpl.GetRandom(100);
|
||||||
|
GlassType glassType = GlassType.None;
|
||||||
|
|
||||||
|
while (glassTypeStart < glassTypeInfo.Values[(int)glassType])
|
||||||
|
{
|
||||||
|
glassType++;
|
||||||
|
|
||||||
|
if ((int)glassType >= glassTypeInfo.ValuesCount)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("glassTypeStart shouldn't exceed glassTypeInfo.ValuesCount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coreData.GlassType = glassType;
|
||||||
|
coreData.GlassColor = (CommonColor)Helper.Ver3GlassColorTable[0];
|
||||||
|
coreData.GlassScale = 4;
|
||||||
|
coreData.GlassY = (byte)(axisY + 10);
|
||||||
|
|
||||||
|
// Mole
|
||||||
|
coreData.MoleType = 0;
|
||||||
|
coreData.MoleScale = 4;
|
||||||
|
coreData.MoleX = 2;
|
||||||
|
coreData.MoleY = 20;
|
||||||
|
|
||||||
|
// Body sizing
|
||||||
|
coreData.Height = 64;
|
||||||
|
coreData.Build = 64;
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
coreData.Nickname = Nickname.Default;
|
||||||
|
coreData.Gender = gender;
|
||||||
|
coreData.FavoriteColor = (byte)utilImpl.GetRandom(0, 11);
|
||||||
|
coreData.RegionMove = 0;
|
||||||
|
coreData.FontRegion = 0;
|
||||||
|
coreData.Type = 0;
|
||||||
|
|
||||||
|
return coreData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFromCharInfo(CharInfo charInfo)
|
||||||
|
{
|
||||||
|
Nickname = charInfo.Nickname;
|
||||||
|
FontRegion = charInfo.FontRegion;
|
||||||
|
FavoriteColor = charInfo.FavoriteColor;
|
||||||
|
Gender = (Gender)charInfo.Gender;
|
||||||
|
Height = charInfo.Height;
|
||||||
|
Build = charInfo.Build;
|
||||||
|
Type = charInfo.Type;
|
||||||
|
RegionMove = charInfo.RegionMove;
|
||||||
|
FacelineType = charInfo.FacelineType;
|
||||||
|
FacelineColor = charInfo.FacelineColor;
|
||||||
|
FacelineWrinkle = charInfo.FacelineWrinkle;
|
||||||
|
FacelineMake = charInfo.FacelineMake;
|
||||||
|
HairType = charInfo.HairType;
|
||||||
|
HairColor = charInfo.HairColor;
|
||||||
|
HairFlip = charInfo.HairFlip;
|
||||||
|
EyeType = charInfo.EyeType;
|
||||||
|
EyeColor = charInfo.EyeColor;
|
||||||
|
EyeScale = charInfo.EyeScale;
|
||||||
|
EyeAspect = charInfo.EyeAspect;
|
||||||
|
EyeRotate = charInfo.EyeRotate;
|
||||||
|
EyeX = charInfo.EyeX;
|
||||||
|
EyeY = charInfo.EyeY;
|
||||||
|
EyebrowType = charInfo.EyebrowType;
|
||||||
|
EyebrowColor = charInfo.EyebrowColor;
|
||||||
|
EyebrowScale = charInfo.EyebrowScale;
|
||||||
|
EyebrowAspect = charInfo.EyebrowAspect;
|
||||||
|
EyebrowRotate = charInfo.EyebrowRotate;
|
||||||
|
EyebrowX = charInfo.EyebrowX;
|
||||||
|
EyebrowY = charInfo.EyebrowY;
|
||||||
|
NoseType = charInfo.NoseType;
|
||||||
|
NoseScale = charInfo.NoseScale;
|
||||||
|
NoseY = charInfo.NoseY;
|
||||||
|
MouthType = charInfo.MouthType;
|
||||||
|
MouthColor = charInfo.MouthColor;
|
||||||
|
MouthScale = charInfo.MouthScale;
|
||||||
|
MouthAspect = charInfo.MouthAspect;
|
||||||
|
MouthY = charInfo.MouthY;
|
||||||
|
BeardColor = charInfo.BeardColor;
|
||||||
|
BeardType = charInfo.BeardType;
|
||||||
|
MustacheType = charInfo.MustacheType;
|
||||||
|
MustacheScale = charInfo.MustacheScale;
|
||||||
|
MustacheY = charInfo.MustacheY;
|
||||||
|
GlassType = charInfo.GlassType;
|
||||||
|
GlassColor = charInfo.GlassColor;
|
||||||
|
GlassScale = charInfo.GlassScale;
|
||||||
|
GlassY = charInfo.GlassY;
|
||||||
|
MoleType = charInfo.MoleType;
|
||||||
|
MoleScale = charInfo.MoleScale;
|
||||||
|
MoleX = charInfo.MoleX;
|
||||||
|
MoleY = charInfo.MoleY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(CoreData x, CoreData y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(CoreData x, CoreData y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is CoreData coreData && Equals(coreData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(CoreData cmpObj)
|
||||||
|
{
|
||||||
|
if (!cmpObj.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
result &= Nickname == cmpObj.Nickname;
|
||||||
|
result &= FontRegion == cmpObj.FontRegion;
|
||||||
|
result &= FavoriteColor == cmpObj.FavoriteColor;
|
||||||
|
result &= Gender == cmpObj.Gender;
|
||||||
|
result &= Height == cmpObj.Height;
|
||||||
|
result &= Build == cmpObj.Build;
|
||||||
|
result &= Type == cmpObj.Type;
|
||||||
|
result &= RegionMove == cmpObj.RegionMove;
|
||||||
|
result &= FacelineType == cmpObj.FacelineType;
|
||||||
|
result &= FacelineColor == cmpObj.FacelineColor;
|
||||||
|
result &= FacelineWrinkle == cmpObj.FacelineWrinkle;
|
||||||
|
result &= FacelineMake == cmpObj.FacelineMake;
|
||||||
|
result &= HairType == cmpObj.HairType;
|
||||||
|
result &= HairColor == cmpObj.HairColor;
|
||||||
|
result &= HairFlip == cmpObj.HairFlip;
|
||||||
|
result &= EyeType == cmpObj.EyeType;
|
||||||
|
result &= EyeColor == cmpObj.EyeColor;
|
||||||
|
result &= EyeScale == cmpObj.EyeScale;
|
||||||
|
result &= EyeAspect == cmpObj.EyeAspect;
|
||||||
|
result &= EyeRotate == cmpObj.EyeRotate;
|
||||||
|
result &= EyeX == cmpObj.EyeX;
|
||||||
|
result &= EyeY == cmpObj.EyeY;
|
||||||
|
result &= EyebrowType == cmpObj.EyebrowType;
|
||||||
|
result &= EyebrowColor == cmpObj.EyebrowColor;
|
||||||
|
result &= EyebrowScale == cmpObj.EyebrowScale;
|
||||||
|
result &= EyebrowAspect == cmpObj.EyebrowAspect;
|
||||||
|
result &= EyebrowRotate == cmpObj.EyebrowRotate;
|
||||||
|
result &= EyebrowX == cmpObj.EyebrowX;
|
||||||
|
result &= EyebrowY == cmpObj.EyebrowY;
|
||||||
|
result &= NoseType == cmpObj.NoseType;
|
||||||
|
result &= NoseScale == cmpObj.NoseScale;
|
||||||
|
result &= NoseY == cmpObj.NoseY;
|
||||||
|
result &= MouthType == cmpObj.MouthType;
|
||||||
|
result &= MouthColor == cmpObj.MouthColor;
|
||||||
|
result &= MouthScale == cmpObj.MouthScale;
|
||||||
|
result &= MouthAspect == cmpObj.MouthAspect;
|
||||||
|
result &= MouthY == cmpObj.MouthY;
|
||||||
|
result &= BeardColor == cmpObj.BeardColor;
|
||||||
|
result &= BeardType == cmpObj.BeardType;
|
||||||
|
result &= MustacheType == cmpObj.MustacheType;
|
||||||
|
result &= MustacheScale == cmpObj.MustacheScale;
|
||||||
|
result &= MustacheY == cmpObj.MustacheY;
|
||||||
|
result &= GlassType == cmpObj.GlassType;
|
||||||
|
result &= GlassColor == cmpObj.GlassColor;
|
||||||
|
result &= GlassScale == cmpObj.GlassScale;
|
||||||
|
result &= GlassY == cmpObj.GlassY;
|
||||||
|
result &= MoleType == cmpObj.MoleType;
|
||||||
|
result &= MoleScale == cmpObj.MoleScale;
|
||||||
|
result &= MoleX == cmpObj.MoleX;
|
||||||
|
result &= MoleY == cmpObj.MoleY;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hashCode = new HashCode();
|
||||||
|
|
||||||
|
hashCode.Add(Nickname);
|
||||||
|
hashCode.Add(FontRegion);
|
||||||
|
hashCode.Add(FavoriteColor);
|
||||||
|
hashCode.Add(Gender);
|
||||||
|
hashCode.Add(Height);
|
||||||
|
hashCode.Add(Build);
|
||||||
|
hashCode.Add(Type);
|
||||||
|
hashCode.Add(RegionMove);
|
||||||
|
hashCode.Add(FacelineType);
|
||||||
|
hashCode.Add(FacelineColor);
|
||||||
|
hashCode.Add(FacelineWrinkle);
|
||||||
|
hashCode.Add(FacelineMake);
|
||||||
|
hashCode.Add(HairType);
|
||||||
|
hashCode.Add(HairColor);
|
||||||
|
hashCode.Add(HairFlip);
|
||||||
|
hashCode.Add(EyeType);
|
||||||
|
hashCode.Add(EyeColor);
|
||||||
|
hashCode.Add(EyeScale);
|
||||||
|
hashCode.Add(EyeAspect);
|
||||||
|
hashCode.Add(EyeRotate);
|
||||||
|
hashCode.Add(EyeX);
|
||||||
|
hashCode.Add(EyeY);
|
||||||
|
hashCode.Add(EyebrowType);
|
||||||
|
hashCode.Add(EyebrowColor);
|
||||||
|
hashCode.Add(EyebrowScale);
|
||||||
|
hashCode.Add(EyebrowAspect);
|
||||||
|
hashCode.Add(EyebrowRotate);
|
||||||
|
hashCode.Add(EyebrowX);
|
||||||
|
hashCode.Add(EyebrowY);
|
||||||
|
hashCode.Add(NoseType);
|
||||||
|
hashCode.Add(NoseScale);
|
||||||
|
hashCode.Add(NoseY);
|
||||||
|
hashCode.Add(MouthType);
|
||||||
|
hashCode.Add(MouthColor);
|
||||||
|
hashCode.Add(MouthScale);
|
||||||
|
hashCode.Add(MouthAspect);
|
||||||
|
hashCode.Add(MouthY);
|
||||||
|
hashCode.Add(BeardColor);
|
||||||
|
hashCode.Add(BeardType);
|
||||||
|
hashCode.Add(MustacheType);
|
||||||
|
hashCode.Add(MustacheScale);
|
||||||
|
hashCode.Add(MustacheY);
|
||||||
|
hashCode.Add(GlassType);
|
||||||
|
hashCode.Add(GlassColor);
|
||||||
|
hashCode.Add(GlassScale);
|
||||||
|
hashCode.Add(GlassY);
|
||||||
|
hashCode.Add(MoleType);
|
||||||
|
hashCode.Add(MoleScale);
|
||||||
|
hashCode.Add(MoleX);
|
||||||
|
hashCode.Add(MoleY);
|
||||||
|
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlySpan<ElementInfo> ElementInfos => MemoryMarshal.Cast<byte, ElementInfo>(ElementInfoArray);
|
||||||
|
|
||||||
|
private enum ElementInfoIndex : int
|
||||||
|
{
|
||||||
|
HairType,
|
||||||
|
Height,
|
||||||
|
MoleType,
|
||||||
|
Build,
|
||||||
|
HairFlip,
|
||||||
|
HairColor,
|
||||||
|
Type,
|
||||||
|
EyeColor,
|
||||||
|
Gender,
|
||||||
|
EyebrowColor,
|
||||||
|
MouthColor,
|
||||||
|
BeardColor,
|
||||||
|
GlassColor,
|
||||||
|
EyeType,
|
||||||
|
RegionMove,
|
||||||
|
MouthType,
|
||||||
|
FontRegion,
|
||||||
|
EyeY,
|
||||||
|
GlassScale,
|
||||||
|
EyebrowType,
|
||||||
|
MustacheType,
|
||||||
|
NoseType,
|
||||||
|
BeardType,
|
||||||
|
NoseY,
|
||||||
|
MouthAspect,
|
||||||
|
MouthY,
|
||||||
|
EyebrowAspect,
|
||||||
|
MustacheY,
|
||||||
|
EyeRotate,
|
||||||
|
GlassY,
|
||||||
|
EyeAspect,
|
||||||
|
MoleX,
|
||||||
|
EyeScale,
|
||||||
|
MoleY,
|
||||||
|
GlassType,
|
||||||
|
FavoriteColor,
|
||||||
|
FacelineType,
|
||||||
|
FacelineColor,
|
||||||
|
FacelineWrinkle,
|
||||||
|
FacelineMake,
|
||||||
|
EyeX,
|
||||||
|
EyebrowScale,
|
||||||
|
EyebrowRotate,
|
||||||
|
EyebrowX,
|
||||||
|
EyebrowY,
|
||||||
|
NoseScale,
|
||||||
|
MouthScale,
|
||||||
|
MustacheScale,
|
||||||
|
MoleScale
|
||||||
|
}
|
||||||
|
|
||||||
|
#region "Element Info Array"
|
||||||
|
private static ReadOnlySpan<byte> ElementInfoArray => new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x83, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x63, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x0f, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x11, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x15, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x16, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x19, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x1a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x1b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
46
Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs
Normal file
46
Ryujinx.HLE/HOS/Services/Mii/Types/CreateId.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
struct CreateId : IEquatable<CreateId>
|
||||||
|
{
|
||||||
|
public UInt128 Raw;
|
||||||
|
|
||||||
|
public bool IsNull => Raw.IsNull;
|
||||||
|
public bool IsValid => !IsNull && (Raw.High & 0xC0) == 0x80;
|
||||||
|
|
||||||
|
public CreateId(byte[] data)
|
||||||
|
{
|
||||||
|
Raw = new UInt128(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(CreateId x, CreateId y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(CreateId x, CreateId y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is CreateId createId && Equals(createId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(CreateId cmpObj)
|
||||||
|
{
|
||||||
|
// Nintendo additionally check that the CreatorId is valid before doing the actual comparison.
|
||||||
|
return IsValid && Raw == cmpObj.Raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Raw.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
197
Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs
Normal file
197
Ryujinx.HLE/HOS/Services/Mii/Types/DefaultMii.cs
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = Size)]
|
||||||
|
struct DefaultMii
|
||||||
|
{
|
||||||
|
public const int Size = 0xD8;
|
||||||
|
|
||||||
|
public int FacelineType;
|
||||||
|
public int FacelineColorVer3;
|
||||||
|
public int FacelineWrinkle;
|
||||||
|
public int FacelineMake;
|
||||||
|
public int HairType;
|
||||||
|
public int HairColorVer3;
|
||||||
|
public int HairFlip;
|
||||||
|
public int EyeType;
|
||||||
|
public int EyeColorVer3;
|
||||||
|
public int EyeScale;
|
||||||
|
public int EyeAspect;
|
||||||
|
public int EyeRotate;
|
||||||
|
public int EyeX;
|
||||||
|
public int EyeY;
|
||||||
|
public int EyebrowType;
|
||||||
|
public int EyebrowColorVer3;
|
||||||
|
public int EyebrowScale;
|
||||||
|
public int EyebrowAspect;
|
||||||
|
public int EyebrowRotate;
|
||||||
|
public int EyebrowX;
|
||||||
|
public int EyebrowY;
|
||||||
|
public int NoseType;
|
||||||
|
public int NoseScale;
|
||||||
|
public int NoseY;
|
||||||
|
public int MouthType;
|
||||||
|
public int MouthColorVer3;
|
||||||
|
public int MouthScale;
|
||||||
|
public int MouthAspect;
|
||||||
|
public int MouthY;
|
||||||
|
public int MustacheType;
|
||||||
|
public int BeardType;
|
||||||
|
public int BeardColorVer3;
|
||||||
|
public int MustacheScale;
|
||||||
|
public int MustacheY;
|
||||||
|
public int GlassType;
|
||||||
|
public int GlassColorVer3;
|
||||||
|
public int GlassScale;
|
||||||
|
public int GlassY;
|
||||||
|
public int MoleType;
|
||||||
|
public int MoleScale;
|
||||||
|
public int MoleX;
|
||||||
|
public int MoleY;
|
||||||
|
public int Height;
|
||||||
|
public int Build;
|
||||||
|
public int Gender;
|
||||||
|
public int FavoriteColor;
|
||||||
|
public int RegionMove;
|
||||||
|
public int FontRegion;
|
||||||
|
public int Type;
|
||||||
|
|
||||||
|
private byte _nicknameFirstByte;
|
||||||
|
|
||||||
|
public Span<byte> NicknameStorage => MemoryMarshal.CreateSpan(ref _nicknameFirstByte, 20);
|
||||||
|
|
||||||
|
public Nickname Nickname
|
||||||
|
{
|
||||||
|
get => Nickname.FromBytes(NicknameStorage);
|
||||||
|
set => value.Raw.Slice(0, 20).CopyTo(NicknameStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReadOnlySpan<DefaultMii> Table => MemoryMarshal.Cast<byte, DefaultMii>(TableRawArray);
|
||||||
|
|
||||||
|
// The first 2 Mii in the default table are used as base for Male/Female in editor but not exposed via IPC.
|
||||||
|
public static int TableLength => _fromIndex.Length;
|
||||||
|
|
||||||
|
private static readonly int[] _fromIndex = new int[] { 2, 3, 4, 5, 6, 7 };
|
||||||
|
|
||||||
|
public static DefaultMii GetDefaultMii(uint index)
|
||||||
|
{
|
||||||
|
return Table[_fromIndex[index]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#region "Raw Table Array"
|
||||||
|
private static ReadOnlySpan<byte> TableRawArray => new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
|
||||||
|
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
|
||||||
|
0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
|
||||||
|
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
|
||||||
|
0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
|
0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
|
||||||
|
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00,
|
||||||
|
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x6f, 0x00,
|
||||||
|
0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
69
Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs
Normal file
69
Ryujinx.HLE/HOS/Services/Mii/Types/EyeType.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum EyeType : byte
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
NormalLash,
|
||||||
|
WhiteLash,
|
||||||
|
WhiteNoBottom,
|
||||||
|
OvalAngledWhite,
|
||||||
|
AngryWhite,
|
||||||
|
DotLashType1,
|
||||||
|
Line,
|
||||||
|
DotLine,
|
||||||
|
OvalWhite,
|
||||||
|
RoundedWhite,
|
||||||
|
NormalShadow,
|
||||||
|
CircleWhite,
|
||||||
|
Circle,
|
||||||
|
CircleWhiteStroke,
|
||||||
|
NormalOvalNoBottom,
|
||||||
|
NormalOvalLarge,
|
||||||
|
NormalRoundedNoBottom,
|
||||||
|
SmallLash,
|
||||||
|
Small,
|
||||||
|
TwoSmall,
|
||||||
|
NormalLongLash,
|
||||||
|
WhiteTwoLashes,
|
||||||
|
WhiteThreeLashes,
|
||||||
|
DotAngry,
|
||||||
|
DotAngled,
|
||||||
|
Oval,
|
||||||
|
SmallWhite,
|
||||||
|
WhiteAngledNoBottom,
|
||||||
|
WhiteAngledNoLeft,
|
||||||
|
SmallWhiteTwoLashes,
|
||||||
|
LeafWhiteLash,
|
||||||
|
WhiteLargeNoBottom,
|
||||||
|
Dot,
|
||||||
|
DotLashType2,
|
||||||
|
DotThreeLashes,
|
||||||
|
WhiteOvalTop,
|
||||||
|
WhiteOvalBottom,
|
||||||
|
WhiteOvalBottomFlat,
|
||||||
|
WhiteOvalTwoLashes,
|
||||||
|
WhiteOvalThreeLashes,
|
||||||
|
WhiteOvalNoBottomTwoLashes,
|
||||||
|
DotWhite,
|
||||||
|
WhiteOvalTopFlat,
|
||||||
|
WhiteThinLeaf,
|
||||||
|
StarThreeLashes,
|
||||||
|
LineTwoLashes,
|
||||||
|
CrowsFeet,
|
||||||
|
WhiteNoBottomFlat,
|
||||||
|
WhiteNoBottomRounded,
|
||||||
|
WhiteSmallBottomLine,
|
||||||
|
WhiteNoBottomLash,
|
||||||
|
WhiteNoPartialBottomLash,
|
||||||
|
WhiteOvalBottomLine,
|
||||||
|
WhiteNoBottomLashTopLine,
|
||||||
|
WhiteNoPartialBottomTwoLashes,
|
||||||
|
NormalTopLine,
|
||||||
|
WhiteOvalLash,
|
||||||
|
RoundTired,
|
||||||
|
WhiteLarge,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 59
|
||||||
|
}
|
||||||
|
}
|
33
Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs
Normal file
33
Ryujinx.HLE/HOS/Services/Mii/Types/EyebrowType.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum EyebrowType : byte
|
||||||
|
{
|
||||||
|
FlatAngledLarge,
|
||||||
|
LowArchRoundedThin,
|
||||||
|
SoftAngledLarge,
|
||||||
|
MediumArchRoundedThin,
|
||||||
|
RoundedMedium,
|
||||||
|
LowArchMedium,
|
||||||
|
RoundedThin,
|
||||||
|
UpThin,
|
||||||
|
MediumArchRoundedMedium,
|
||||||
|
RoundedLarge,
|
||||||
|
UpLarge,
|
||||||
|
FlatAngledLargeInverted,
|
||||||
|
MediumArchFlat,
|
||||||
|
AngledThin,
|
||||||
|
HorizontalLarge,
|
||||||
|
HighArchFlat,
|
||||||
|
Flat,
|
||||||
|
MediumArchLarge,
|
||||||
|
LowArchThin,
|
||||||
|
RoundedThinInverted,
|
||||||
|
HighArchLarge,
|
||||||
|
Hairy,
|
||||||
|
Dotted,
|
||||||
|
None,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 23
|
||||||
|
}
|
||||||
|
}
|
19
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs
Normal file
19
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineColor.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum FacelineColor : byte
|
||||||
|
{
|
||||||
|
Beige,
|
||||||
|
WarmBeige,
|
||||||
|
Natural,
|
||||||
|
Honey,
|
||||||
|
Chestnut,
|
||||||
|
Porcelain,
|
||||||
|
Ivory,
|
||||||
|
WarmIvory,
|
||||||
|
Almond,
|
||||||
|
Espresso,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 9
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs
Normal file
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineMake.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum FacelineMake : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
CheekPorcelain,
|
||||||
|
CheekNatural,
|
||||||
|
EyeShadowBlue,
|
||||||
|
CheekBlushPorcelain,
|
||||||
|
CheekBlushNatural,
|
||||||
|
CheekPorcelainEyeShadowBlue,
|
||||||
|
CheekPorcelainEyeShadowNatural,
|
||||||
|
CheekBlushPorcelainEyeShadowEspresso,
|
||||||
|
Freckles,
|
||||||
|
LionsManeBeard,
|
||||||
|
StubbleBeard,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 11
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs
Normal file
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineType.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum FacelineType : byte
|
||||||
|
{
|
||||||
|
Sharp,
|
||||||
|
Rounded,
|
||||||
|
SharpRounded,
|
||||||
|
SharpRoundedSmall,
|
||||||
|
Large,
|
||||||
|
LargeRounded,
|
||||||
|
SharpSmall,
|
||||||
|
Flat,
|
||||||
|
Bump,
|
||||||
|
Angular,
|
||||||
|
FlatRounded,
|
||||||
|
AngularSmall,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 11
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs
Normal file
21
Ryujinx.HLE/HOS/Services/Mii/Types/FacelineWrinkle.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum FacelineWrinkle : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
TearTroughs,
|
||||||
|
FacialPain,
|
||||||
|
Cheeks,
|
||||||
|
Folds,
|
||||||
|
UnderTheEyes,
|
||||||
|
SplitChin,
|
||||||
|
Chin,
|
||||||
|
BrowDroop,
|
||||||
|
MouthFrown,
|
||||||
|
CrowsFeet,
|
||||||
|
FoldsCrowsFrown,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 11
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs
Normal file
10
Ryujinx.HLE/HOS/Services/Mii/Types/FontRegion.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum FontRegion : byte
|
||||||
|
{
|
||||||
|
Standard,
|
||||||
|
China,
|
||||||
|
Korea,
|
||||||
|
Taiwan
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs
Normal file
12
Ryujinx.HLE/HOS/Services/Mii/Types/Gender.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum Gender : byte
|
||||||
|
{
|
||||||
|
Male,
|
||||||
|
Female,
|
||||||
|
All,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 1
|
||||||
|
}
|
||||||
|
}
|
29
Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs
Normal file
29
Ryujinx.HLE/HOS/Services/Mii/Types/GlassType.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum GlassType : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Oval,
|
||||||
|
Wayfarer,
|
||||||
|
Rectangle,
|
||||||
|
TopRimless,
|
||||||
|
Rounded,
|
||||||
|
Oversized,
|
||||||
|
CatEye,
|
||||||
|
Square,
|
||||||
|
BottomRimless,
|
||||||
|
SemiOpaqueRounded,
|
||||||
|
SemiOpaqueCatEye,
|
||||||
|
SemiOpaqueOval,
|
||||||
|
SemiOpaqueRectangle,
|
||||||
|
SemiOpaqueAviator,
|
||||||
|
OpaqueRounded,
|
||||||
|
OpaqueCatEye,
|
||||||
|
OpaqueOval,
|
||||||
|
OpaqueRectangle,
|
||||||
|
OpaqueAviator,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 19
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs
Normal file
11
Ryujinx.HLE/HOS/Services/Mii/Types/HairFlip.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum HairFlip : byte
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 1
|
||||||
|
}
|
||||||
|
}
|
141
Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs
Normal file
141
Ryujinx.HLE/HOS/Services/Mii/Types/HairType.cs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum HairType : byte
|
||||||
|
{
|
||||||
|
NormalLong,
|
||||||
|
NormalShort,
|
||||||
|
NormalMedium,
|
||||||
|
NormalExtraLong,
|
||||||
|
NormalLongBottom,
|
||||||
|
NormalTwoPeaks,
|
||||||
|
PartingLong,
|
||||||
|
FrontLock,
|
||||||
|
PartingShort,
|
||||||
|
PartingExtraLongCurved,
|
||||||
|
PartingExtraLong,
|
||||||
|
PartingMiddleLong,
|
||||||
|
PartingSquared,
|
||||||
|
PartingLongBottom,
|
||||||
|
PeaksTop,
|
||||||
|
PeaksSquared,
|
||||||
|
PartingPeaks,
|
||||||
|
PeaksLongBottom,
|
||||||
|
Peaks,
|
||||||
|
PeaksRounded,
|
||||||
|
PeaksSide,
|
||||||
|
PeaksMedium,
|
||||||
|
PeaksLong,
|
||||||
|
PeaksRoundedLong,
|
||||||
|
PartingFrontPeaks,
|
||||||
|
PartingLongFront,
|
||||||
|
PartingLongRounded,
|
||||||
|
PartingFrontPeaksLong,
|
||||||
|
PartingExtraLongRounded,
|
||||||
|
LongRounded,
|
||||||
|
NormalUnknown1,
|
||||||
|
NormalUnknown2,
|
||||||
|
NormalUnknown3,
|
||||||
|
NormalUnknown4,
|
||||||
|
NormalUnknown5,
|
||||||
|
NormalUnknown6,
|
||||||
|
DreadLocks,
|
||||||
|
PlatedMats,
|
||||||
|
Caps,
|
||||||
|
Afro,
|
||||||
|
PlatedMatsLong,
|
||||||
|
Beanie,
|
||||||
|
Short,
|
||||||
|
ShortTopLongSide,
|
||||||
|
ShortUnknown1,
|
||||||
|
ShortUnknown2,
|
||||||
|
MilitaryParting,
|
||||||
|
Military,
|
||||||
|
ShortUnknown3,
|
||||||
|
ShortUnknown4,
|
||||||
|
ShortUnknown5,
|
||||||
|
ShortUnknown6,
|
||||||
|
NoneTop,
|
||||||
|
None,
|
||||||
|
LongUnknown1,
|
||||||
|
LongUnknown2,
|
||||||
|
LongUnknown3,
|
||||||
|
LongUnknown4,
|
||||||
|
LongUnknown5,
|
||||||
|
LongUnknown6,
|
||||||
|
LongUnknown7,
|
||||||
|
LongUnknown8,
|
||||||
|
LongUnknown9,
|
||||||
|
LongUnknown10,
|
||||||
|
LongUnknown11,
|
||||||
|
LongUnknown12,
|
||||||
|
LongUnknown13,
|
||||||
|
LongUnknown14,
|
||||||
|
LongUnknown15,
|
||||||
|
LongUnknown16,
|
||||||
|
LongUnknown17,
|
||||||
|
LongUnknown18,
|
||||||
|
LongUnknown19,
|
||||||
|
LongUnknown20,
|
||||||
|
LongUnknown21,
|
||||||
|
LongUnknown22,
|
||||||
|
LongUnknown23,
|
||||||
|
LongUnknown24,
|
||||||
|
LongUnknown25,
|
||||||
|
LongUnknown26,
|
||||||
|
LongUnknown27,
|
||||||
|
LongUnknown28,
|
||||||
|
LongUnknown29,
|
||||||
|
LongUnknown30,
|
||||||
|
LongUnknown31,
|
||||||
|
LongUnknown32,
|
||||||
|
LongUnknown33,
|
||||||
|
LongUnknown34,
|
||||||
|
LongUnknown35,
|
||||||
|
LongUnknown36,
|
||||||
|
LongUnknown37,
|
||||||
|
LongUnknown38,
|
||||||
|
LongUnknown39,
|
||||||
|
LongUnknown40,
|
||||||
|
LongUnknown41,
|
||||||
|
LongUnknown42,
|
||||||
|
LongUnknown43,
|
||||||
|
LongUnknown44,
|
||||||
|
LongUnknown45,
|
||||||
|
LongUnknown46,
|
||||||
|
LongUnknown47,
|
||||||
|
LongUnknown48,
|
||||||
|
LongUnknown49,
|
||||||
|
LongUnknown50,
|
||||||
|
LongUnknown51,
|
||||||
|
LongUnknown52,
|
||||||
|
LongUnknown53,
|
||||||
|
LongUnknown54,
|
||||||
|
LongUnknown55,
|
||||||
|
LongUnknown56,
|
||||||
|
LongUnknown57,
|
||||||
|
LongUnknown58,
|
||||||
|
LongUnknown59,
|
||||||
|
LongUnknown60,
|
||||||
|
LongUnknown61,
|
||||||
|
LongUnknown62,
|
||||||
|
LongUnknown63,
|
||||||
|
LongUnknown64,
|
||||||
|
LongUnknown65,
|
||||||
|
LongUnknown66,
|
||||||
|
TwoMediumFrontStrandsOneLongBackPonyTail,
|
||||||
|
TwoFrontStrandsLongBackPonyTail,
|
||||||
|
PartingFrontTwoLongBackPonyTails,
|
||||||
|
TwoFrontStrandsOneLongBackPonyTail,
|
||||||
|
LongBackPonyTail,
|
||||||
|
LongFrontTwoLongBackPonyTails,
|
||||||
|
StrandsTwoShortSidedPonyTails,
|
||||||
|
TwoMediumSidedPonyTails,
|
||||||
|
ShortFrontTwoBackPonyTails,
|
||||||
|
TwoShortSidedPonyTails,
|
||||||
|
TwoLongSidedPonyTails,
|
||||||
|
LongFrontTwoBackPonyTails,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 131
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs
Normal file
9
Ryujinx.HLE/HOS/Services/Mii/Types/IElement.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
interface IElement
|
||||||
|
{
|
||||||
|
void SetFromStoreData(StoreData storeData);
|
||||||
|
|
||||||
|
void SetSource(Source source);
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs
Normal file
15
Ryujinx.HLE/HOS/Services/Mii/Types/IStoredData.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
interface IStoredData<T> : IElement, IEquatable<T> where T : notnull
|
||||||
|
{
|
||||||
|
byte Type { get; }
|
||||||
|
|
||||||
|
CreateId CreateId { get; }
|
||||||
|
|
||||||
|
ResultCode InvalidData { get; }
|
||||||
|
|
||||||
|
bool IsValid();
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs
Normal file
11
Ryujinx.HLE/HOS/Services/Mii/Types/MoleType.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum MoleType : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
OneDot,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 1
|
||||||
|
}
|
||||||
|
}
|
45
Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs
Normal file
45
Ryujinx.HLE/HOS/Services/Mii/Types/MouthType.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum MouthType : byte
|
||||||
|
{
|
||||||
|
Neutral,
|
||||||
|
NeutralLips,
|
||||||
|
Smile,
|
||||||
|
SmileStroke,
|
||||||
|
SmileTeeth,
|
||||||
|
LipsSmall,
|
||||||
|
LipsLarge,
|
||||||
|
Wave,
|
||||||
|
WaveAngrySmall,
|
||||||
|
NeutralStrokeLarge,
|
||||||
|
TeethSurprised,
|
||||||
|
LipsExtraLarge,
|
||||||
|
LipsUp,
|
||||||
|
NeutralDown,
|
||||||
|
Surprised,
|
||||||
|
TeethMiddle,
|
||||||
|
NeutralStroke,
|
||||||
|
LipsExtraSmall,
|
||||||
|
Malicious,
|
||||||
|
LipsDual,
|
||||||
|
NeutralComma,
|
||||||
|
NeutralUp,
|
||||||
|
TeethLarge,
|
||||||
|
WaveAngry,
|
||||||
|
LipsSexy,
|
||||||
|
SmileInverted,
|
||||||
|
LipsSexyOutline,
|
||||||
|
SmileRounded,
|
||||||
|
LipsTeeth,
|
||||||
|
NeutralOpen,
|
||||||
|
TeethRounded,
|
||||||
|
WaveAngrySmallInverted,
|
||||||
|
NeutralCommaInverted,
|
||||||
|
TeethFull,
|
||||||
|
SmileDownLine,
|
||||||
|
Kiss,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 35
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs
Normal file
15
Ryujinx.HLE/HOS/Services/Mii/Types/MustacheType.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum MustacheType : byte
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Walrus,
|
||||||
|
Pencil,
|
||||||
|
Horseshoe,
|
||||||
|
Normal,
|
||||||
|
Toothbrush,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 5
|
||||||
|
}
|
||||||
|
}
|
120
Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs
Normal file
120
Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 2, Size = SizeConst)]
|
||||||
|
struct Nickname : IEquatable<Nickname>
|
||||||
|
{
|
||||||
|
public const int CharCount = 10;
|
||||||
|
private const int SizeConst = (CharCount + 1) * 2;
|
||||||
|
|
||||||
|
private byte _storage;
|
||||||
|
|
||||||
|
public static Nickname Default => FromString("no name");
|
||||||
|
public static Nickname Question => FromString("???");
|
||||||
|
|
||||||
|
public Span<byte> Raw => MemoryMarshal.CreateSpan(ref _storage, SizeConst);
|
||||||
|
|
||||||
|
private ReadOnlySpan<ushort> Characters => MemoryMarshal.Cast<byte, ushort>(Raw);
|
||||||
|
|
||||||
|
private int GetEndCharacterIndex()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Characters.Length; i++)
|
||||||
|
{
|
||||||
|
if (Characters[i] == 0)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEmpty()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Characters.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (Characters[i] != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
// Create a new unicode encoding instance with error checking enabled
|
||||||
|
UnicodeEncoding unicodeEncoding = new UnicodeEncoding(false, false, true);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
unicodeEncoding.GetString(Raw);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidForFontRegion(FontRegion fontRegion)
|
||||||
|
{
|
||||||
|
// TODO: We need to extract the character tables used here, for now just assume that if it's valid Unicode, it will be valid for any font.
|
||||||
|
return IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Encoding.Unicode.GetString(Raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Nickname FromBytes(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Length > SizeConst)
|
||||||
|
{
|
||||||
|
data = data.Slice(0, SizeConst);
|
||||||
|
}
|
||||||
|
|
||||||
|
Nickname result = new Nickname();
|
||||||
|
|
||||||
|
data.CopyTo(result.Raw);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Nickname FromString(string nickname)
|
||||||
|
{
|
||||||
|
return FromBytes(Encoding.Unicode.GetBytes(nickname));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(Nickname x, Nickname y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(Nickname x, Nickname y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is Nickname nickname && Equals(nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(Nickname cmpObj)
|
||||||
|
{
|
||||||
|
return Raw.SequenceEqual(cmpObj.Raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Raw.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
254
Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs
Normal file
254
Ryujinx.HLE/HOS/Services/Mii/Types/NintendoFigurineDatabase.cs
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8, Size = 0x1A98)]
|
||||||
|
struct NintendoFigurineDatabase
|
||||||
|
{
|
||||||
|
private const int DatabaseMagic = ('N' << 0) | ('F' << 8) | ('D' << 16) | ('B' << 24);
|
||||||
|
private const byte MaxMii = 100;
|
||||||
|
private const byte CurrentVersion = 1;
|
||||||
|
|
||||||
|
private const int FigurineArraySize = MaxMii * StoreData.Size;
|
||||||
|
|
||||||
|
private uint _magic;
|
||||||
|
|
||||||
|
private FigurineStorageStruct _figurineStorage;
|
||||||
|
|
||||||
|
private byte _version;
|
||||||
|
private byte _figurineCount;
|
||||||
|
private ushort _crc;
|
||||||
|
|
||||||
|
// Set to true to allow fixing database with invalid storedata device crc instead of deleting them.
|
||||||
|
private const bool AcceptInvalidDeviceCrc = true;
|
||||||
|
|
||||||
|
public int Length => _figurineCount;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = FigurineArraySize)]
|
||||||
|
private struct FigurineStorageStruct { }
|
||||||
|
|
||||||
|
private Span<StoreData> Figurines => SpanHelpers.AsSpan<FigurineStorageStruct, StoreData>(ref _figurineStorage);
|
||||||
|
|
||||||
|
public StoreData Get(int index)
|
||||||
|
{
|
||||||
|
return Figurines[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFull()
|
||||||
|
{
|
||||||
|
return Length >= MaxMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetIndexByCreatorId(out int index, CreateId createId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Length; i++)
|
||||||
|
{
|
||||||
|
if (Figurines[i].CreateId == createId)
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = -1;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Move(int newIndex, int oldIndex)
|
||||||
|
{
|
||||||
|
if (newIndex == oldIndex)
|
||||||
|
{
|
||||||
|
return ResultCode.NotUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreData tmp = Figurines[oldIndex];
|
||||||
|
|
||||||
|
int targetLength;
|
||||||
|
int sourceIndex;
|
||||||
|
int destinationIndex;
|
||||||
|
|
||||||
|
if (newIndex < oldIndex)
|
||||||
|
{
|
||||||
|
targetLength = oldIndex - newIndex;
|
||||||
|
sourceIndex = newIndex;
|
||||||
|
destinationIndex = newIndex + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetLength = newIndex - oldIndex;
|
||||||
|
sourceIndex = oldIndex + 1;
|
||||||
|
destinationIndex = oldIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Figurines.Slice(sourceIndex, targetLength).CopyTo(Figurines.Slice(destinationIndex, targetLength));
|
||||||
|
|
||||||
|
Figurines[newIndex] = tmp;
|
||||||
|
|
||||||
|
UpdateCrc();
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Replace(int index, StoreData storeData)
|
||||||
|
{
|
||||||
|
Figurines[index] = storeData;
|
||||||
|
|
||||||
|
UpdateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(StoreData storeData)
|
||||||
|
{
|
||||||
|
Replace(_figurineCount++, storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(int index)
|
||||||
|
{
|
||||||
|
int newCount = _figurineCount - 1;
|
||||||
|
|
||||||
|
// If this isn't the only element in the list, move the data in it.
|
||||||
|
if (index < newCount)
|
||||||
|
{
|
||||||
|
int targetLength = newCount - index;
|
||||||
|
int sourceIndex = index + 1;
|
||||||
|
int destinationIndex = index;
|
||||||
|
|
||||||
|
Figurines.Slice(sourceIndex, targetLength).CopyTo(Figurines.Slice(destinationIndex, targetLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
_figurineCount = (byte)newCount;
|
||||||
|
|
||||||
|
UpdateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FixDatabase()
|
||||||
|
{
|
||||||
|
bool isBroken = false;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
while (i < Length)
|
||||||
|
{
|
||||||
|
ref StoreData figurine = ref Figurines[i];
|
||||||
|
|
||||||
|
if (!figurine.IsValid())
|
||||||
|
{
|
||||||
|
if (AcceptInvalidDeviceCrc && figurine.CoreData.IsValid() && figurine.IsValidDataCrc())
|
||||||
|
{
|
||||||
|
figurine.UpdateCrc();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Delete(i);
|
||||||
|
isBroken = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool hasDuplicate = false;
|
||||||
|
CreateId createId = figurine.CreateId;
|
||||||
|
|
||||||
|
for (int j = 0; j < i; j++)
|
||||||
|
{
|
||||||
|
if (Figurines[j].CreateId == createId)
|
||||||
|
{
|
||||||
|
hasDuplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDuplicate)
|
||||||
|
{
|
||||||
|
Delete(i);
|
||||||
|
isBroken = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCrc();
|
||||||
|
|
||||||
|
return isBroken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode Verify()
|
||||||
|
{
|
||||||
|
if (_magic != DatabaseMagic)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidDatabaseMagic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_version != CurrentVersion)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidDatabaseVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidCrc())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidCrc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_figurineCount > 100)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidDatabaseSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Format()
|
||||||
|
{
|
||||||
|
_magic = DatabaseMagic;
|
||||||
|
_version = CurrentVersion;
|
||||||
|
_figurineCount = 0;
|
||||||
|
|
||||||
|
// Fill with empty data
|
||||||
|
Figurines.Fill(new StoreData());
|
||||||
|
|
||||||
|
UpdateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CorruptDatabase()
|
||||||
|
{
|
||||||
|
UpdateCrc();
|
||||||
|
|
||||||
|
_crc = (ushort)~_crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCrc()
|
||||||
|
{
|
||||||
|
_crc = CalculateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidCrc()
|
||||||
|
{
|
||||||
|
return _crc == CalculateCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort CalculateCrc()
|
||||||
|
{
|
||||||
|
return Helper.CalculateCrc16BE(AsSpanWithoutCrc());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span<byte> AsSpan()
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsByteSpan(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> AsReadOnlySpan()
|
||||||
|
{
|
||||||
|
return SpanHelpers.AsReadOnlyByteSpan(ref this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AsSpanWithoutCrc()
|
||||||
|
{
|
||||||
|
return AsReadOnlySpan().Slice(0, Unsafe.SizeOf<NintendoFigurineDatabase>() - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs
Normal file
27
Ryujinx.HLE/HOS/Services/Mii/Types/NoseType.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum NoseType : byte
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Rounded,
|
||||||
|
Dot,
|
||||||
|
Arrow,
|
||||||
|
Roman,
|
||||||
|
Triangle,
|
||||||
|
Button,
|
||||||
|
RoundedInverted,
|
||||||
|
Potato,
|
||||||
|
Grecian,
|
||||||
|
Snub,
|
||||||
|
Aquiline,
|
||||||
|
ArrowLeft,
|
||||||
|
RoundedLarge,
|
||||||
|
Hooked,
|
||||||
|
Fat,
|
||||||
|
Droopy,
|
||||||
|
ArrowLarge,
|
||||||
|
|
||||||
|
Min = 0,
|
||||||
|
Max = 17
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs
Normal file
10
Ryujinx.HLE/HOS/Services/Mii/Types/Race.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum Race : uint
|
||||||
|
{
|
||||||
|
Black,
|
||||||
|
White,
|
||||||
|
Asian,
|
||||||
|
All
|
||||||
|
}
|
||||||
|
}
|
2254
Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs
Normal file
2254
Ryujinx.HLE/HOS/Services/Mii/Types/RandomMiiConstants.cs
Normal file
File diff suppressed because it is too large
Load diff
8
Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs
Normal file
8
Ryujinx.HLE/HOS/Services/Mii/Types/Source.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
enum Source : int
|
||||||
|
{
|
||||||
|
Database,
|
||||||
|
Default
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs
Normal file
12
Ryujinx.HLE/HOS/Services/Mii/Types/SourceFlag.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum SourceFlag : int
|
||||||
|
{
|
||||||
|
Database = 1 << Source.Database,
|
||||||
|
Default = 1 << Source.Default,
|
||||||
|
All = Database | Default
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs
Normal file
17
Ryujinx.HLE/HOS/Services/Mii/Types/SpecialMiiKeyCode.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)]
|
||||||
|
struct SpecialMiiKeyCode
|
||||||
|
{
|
||||||
|
private const uint SpecialMiiMagic = 0xA523B78F;
|
||||||
|
|
||||||
|
public uint RawValue;
|
||||||
|
|
||||||
|
public bool IsEnabledSpecialMii()
|
||||||
|
{
|
||||||
|
return RawValue == SpecialMiiMagic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
232
Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs
Normal file
232
Ryujinx.HLE/HOS/Services/Mii/Types/StoreData.cs
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
using LibHac.Common;
|
||||||
|
using Ryujinx.HLE.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = Size)]
|
||||||
|
struct StoreData : IStoredData<StoreData>
|
||||||
|
{
|
||||||
|
public const int Size = 0x44;
|
||||||
|
|
||||||
|
public CoreData CoreData;
|
||||||
|
private CreateId _createId;
|
||||||
|
public ushort DataCrc;
|
||||||
|
public ushort DeviceCrc;
|
||||||
|
|
||||||
|
public byte Type => CoreData.Type;
|
||||||
|
|
||||||
|
public CreateId CreateId => _createId;
|
||||||
|
|
||||||
|
public ResultCode InvalidData => ResultCode.InvalidStoreData;
|
||||||
|
|
||||||
|
private void UpdateDataCrc()
|
||||||
|
{
|
||||||
|
DataCrc = CalculateDataCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDeviceCrc()
|
||||||
|
{
|
||||||
|
DeviceCrc = CalculateDeviceCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateCrc()
|
||||||
|
{
|
||||||
|
UpdateDataCrc();
|
||||||
|
UpdateDeviceCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSpecial()
|
||||||
|
{
|
||||||
|
return CoreData.Type == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
return CoreData.IsValid() && IsValidDataCrc() && IsValidDeviceCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidDataCrc()
|
||||||
|
{
|
||||||
|
return DataCrc == CalculateDataCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidDeviceCrc()
|
||||||
|
{
|
||||||
|
return DeviceCrc == CalculateDeviceCrc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort CalculateDataCrc()
|
||||||
|
{
|
||||||
|
return Helper.CalculateCrc16BE(AsSpanWithoutCrc());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort CalculateDeviceCrc()
|
||||||
|
{
|
||||||
|
UInt128 deviceId = Helper.GetDeviceId();
|
||||||
|
|
||||||
|
ushort deviceIdCrc16 = Helper.CalculateCrc16BE(SpanHelpers.AsByteSpan(ref deviceId));
|
||||||
|
|
||||||
|
return Helper.CalculateCrc16BE(AsSpanWithoutDeviceCrc(), deviceIdCrc16);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AsSpan()
|
||||||
|
{
|
||||||
|
return MemoryMarshal.AsBytes(SpanHelpers.CreateReadOnlySpan(ref this, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AsSpanWithoutCrc()
|
||||||
|
{
|
||||||
|
return AsSpan().Slice(0, Size - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlySpan<byte> AsSpanWithoutDeviceCrc()
|
||||||
|
{
|
||||||
|
return AsSpan().Slice(0, Size - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StoreData BuildDefault(UtilityImpl utilImpl, uint index)
|
||||||
|
{
|
||||||
|
StoreData result = new StoreData
|
||||||
|
{
|
||||||
|
_createId = utilImpl.MakeCreateId()
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreData coreData = new CoreData();
|
||||||
|
|
||||||
|
DefaultMii template = DefaultMii.GetDefaultMii(index);
|
||||||
|
|
||||||
|
coreData.SetDefault();
|
||||||
|
|
||||||
|
coreData.Nickname = template.Nickname;
|
||||||
|
coreData.FontRegion = (FontRegion)template.FontRegion;
|
||||||
|
coreData.FavoriteColor = (byte)template.FavoriteColor;
|
||||||
|
coreData.Gender = (Gender)template.Gender;
|
||||||
|
coreData.Height = (byte)template.Height;
|
||||||
|
coreData.Build = (byte)template.Build;
|
||||||
|
coreData.Type = (byte)template.Type;
|
||||||
|
coreData.RegionMove = (byte)template.RegionMove;
|
||||||
|
coreData.FacelineType = (FacelineType)template.FacelineType;
|
||||||
|
coreData.FacelineColor = (FacelineColor)Helper.Ver3FacelineColorTable[template.FacelineColorVer3];
|
||||||
|
coreData.FacelineWrinkle = (FacelineWrinkle)template.FacelineWrinkle;
|
||||||
|
coreData.FacelineMake = (FacelineMake)template.FacelineMake;
|
||||||
|
coreData.HairType = (HairType)template.HairType;
|
||||||
|
coreData.HairColor = (CommonColor)Helper.Ver3HairColorTable[template.HairColorVer3];
|
||||||
|
coreData.HairFlip = (HairFlip)template.HairFlip;
|
||||||
|
coreData.EyeType = (EyeType)template.EyeType;
|
||||||
|
coreData.EyeColor = (CommonColor)Helper.Ver3EyeColorTable[template.EyeColorVer3];
|
||||||
|
coreData.EyeScale = (byte)template.EyeScale;
|
||||||
|
coreData.EyeAspect = (byte)template.EyeAspect;
|
||||||
|
coreData.EyeRotate = (byte)template.EyeRotate;
|
||||||
|
coreData.EyeX = (byte)template.EyeX;
|
||||||
|
coreData.EyeY = (byte)template.EyeY;
|
||||||
|
coreData.EyebrowType = (EyebrowType)template.EyebrowType;
|
||||||
|
coreData.EyebrowColor = (CommonColor)Helper.Ver3HairColorTable[template.EyebrowColorVer3];
|
||||||
|
coreData.EyebrowScale = (byte)template.EyebrowScale;
|
||||||
|
coreData.EyebrowAspect = (byte)template.EyebrowAspect;
|
||||||
|
coreData.EyebrowRotate = (byte)template.EyebrowRotate;
|
||||||
|
coreData.EyebrowX = (byte)template.EyebrowX;
|
||||||
|
coreData.EyebrowY = (byte)template.EyebrowY;
|
||||||
|
coreData.NoseType = (NoseType)template.NoseType;
|
||||||
|
coreData.NoseScale = (byte)template.NoseScale;
|
||||||
|
coreData.NoseY = (byte)template.NoseY;
|
||||||
|
coreData.MouthType = (MouthType)template.MouthType;
|
||||||
|
coreData.MouthColor = (CommonColor)Helper.Ver3MouthColorTable[template.MouthColorVer3];
|
||||||
|
coreData.MouthScale = (byte)template.MouthScale;
|
||||||
|
coreData.MouthAspect = (byte)template.MouthAspect;
|
||||||
|
coreData.MouthY = (byte)template.MouthY;
|
||||||
|
coreData.BeardColor = (CommonColor)Helper.Ver3HairColorTable[template.BeardColorVer3];
|
||||||
|
coreData.BeardType = (BeardType)template.BeardType;
|
||||||
|
coreData.MustacheType = (MustacheType)template.MustacheType;
|
||||||
|
coreData.MustacheScale = (byte)template.MustacheScale;
|
||||||
|
coreData.MustacheY = (byte)template.MustacheY;
|
||||||
|
coreData.GlassType = (GlassType)template.GlassType;
|
||||||
|
coreData.GlassColor = (CommonColor)Helper.Ver3GlassColorTable[template.GlassColorVer3];
|
||||||
|
coreData.GlassScale = (byte)template.GlassScale;
|
||||||
|
coreData.GlassY = (byte)template.GlassY;
|
||||||
|
coreData.MoleType = (MoleType)template.MoleType;
|
||||||
|
coreData.MoleScale = (byte)template.MoleScale;
|
||||||
|
coreData.MoleX = (byte)template.MoleX;
|
||||||
|
coreData.MoleY = (byte)template.MoleY;
|
||||||
|
|
||||||
|
result.CoreData = coreData;
|
||||||
|
|
||||||
|
result.UpdateCrc();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StoreData BuildRandom(UtilityImpl utilImpl, Age age, Gender gender, Race race)
|
||||||
|
{
|
||||||
|
return BuildFromCoreData(utilImpl, CoreData.BuildRandom(utilImpl, age, gender, race));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StoreData BuildFromCoreData(UtilityImpl utilImpl, CoreData coreData)
|
||||||
|
{
|
||||||
|
StoreData result = new StoreData
|
||||||
|
{
|
||||||
|
CoreData = coreData,
|
||||||
|
_createId = utilImpl.MakeCreateId()
|
||||||
|
};
|
||||||
|
|
||||||
|
result.UpdateCrc();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFromStoreData(StoreData storeData)
|
||||||
|
{
|
||||||
|
this = storeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSource(Source source)
|
||||||
|
{
|
||||||
|
// Only implemented for Element variants.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(StoreData x, StoreData y)
|
||||||
|
{
|
||||||
|
return x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(StoreData x, StoreData y)
|
||||||
|
{
|
||||||
|
return !x.Equals(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return obj is StoreData storeData && Equals(storeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(StoreData cmpObj)
|
||||||
|
{
|
||||||
|
if (!cmpObj.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
result &= CreateId == cmpObj.CreateId;
|
||||||
|
result &= CoreData == cmpObj.CoreData;
|
||||||
|
result &= DataCrc == cmpObj.DataCrc;
|
||||||
|
result &= DeviceCrc == cmpObj.DeviceCrc;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hashCode = new HashCode();
|
||||||
|
|
||||||
|
hashCode.Add(CreateId);
|
||||||
|
hashCode.Add(CoreData);
|
||||||
|
hashCode.Add(DataCrc);
|
||||||
|
hashCode.Add(DeviceCrc);
|
||||||
|
|
||||||
|
return hashCode.ToHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs
Normal file
21
Ryujinx.HLE/HOS/Services/Mii/Types/StoreDataElement.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x48)]
|
||||||
|
struct StoreDataElement : IElement
|
||||||
|
{
|
||||||
|
public StoreData StoreData;
|
||||||
|
public Source Source;
|
||||||
|
|
||||||
|
public void SetFromStoreData(StoreData storeData)
|
||||||
|
{
|
||||||
|
StoreData = storeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSource(Source source)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs
Normal file
17
Ryujinx.HLE/HOS/Services/Mii/Types/Ver3StoreData.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii.Types
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = Size)]
|
||||||
|
struct Ver3StoreData
|
||||||
|
{
|
||||||
|
public const int Size = 0x60;
|
||||||
|
|
||||||
|
private byte _storage;
|
||||||
|
|
||||||
|
public Span<byte> Storage => MemoryMarshal.CreateSpan(ref _storage, Size);
|
||||||
|
|
||||||
|
// TODO: define all getters/setters
|
||||||
|
}
|
||||||
|
}
|
67
Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs
Normal file
67
Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Mii
|
||||||
|
{
|
||||||
|
class UtilityImpl
|
||||||
|
{
|
||||||
|
private uint _x;
|
||||||
|
private uint _y;
|
||||||
|
private uint _z;
|
||||||
|
private uint _w;
|
||||||
|
|
||||||
|
public UtilityImpl()
|
||||||
|
{
|
||||||
|
_x = 123456789;
|
||||||
|
_y = 362436069;
|
||||||
|
|
||||||
|
TimeSpanType time = TimeManager.Instance.TickBasedSteadyClock.GetCurrentRawTimePoint(null);
|
||||||
|
|
||||||
|
_w = (uint)(time.NanoSeconds & uint.MaxValue);
|
||||||
|
_z = (uint)((time.NanoSeconds >> 32) & uint.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint GetRandom()
|
||||||
|
{
|
||||||
|
uint t = (_x ^ (_x << 11));
|
||||||
|
|
||||||
|
_x = _y;
|
||||||
|
_y = _z;
|
||||||
|
_z = _w;
|
||||||
|
_w = (_w ^ (_w >> 19)) ^ (t ^ (t >> 8));
|
||||||
|
|
||||||
|
return _w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetRandom(int end)
|
||||||
|
{
|
||||||
|
return (int)GetRandom((uint)end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetRandom(uint end)
|
||||||
|
{
|
||||||
|
uint random = GetRandom();
|
||||||
|
|
||||||
|
return random - random / end * end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetRandom(uint start, uint end)
|
||||||
|
{
|
||||||
|
uint random = GetRandom();
|
||||||
|
|
||||||
|
return random - random / (1 - start + end) * (1 - start + end) + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetRandom(int start, int end)
|
||||||
|
{
|
||||||
|
return (int)GetRandom((uint)start, (uint)end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CreateId MakeCreateId()
|
||||||
|
{
|
||||||
|
return new CreateId(Guid.NewGuid().ToByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +0,0 @@
|
||||||
namespace Ryujinx.HLE.HOS.Services.Sdb.Mii
|
|
||||||
{
|
|
||||||
[Service("mii:e")]
|
|
||||||
[Service("mii:u")]
|
|
||||||
class IStaticService : IpcService
|
|
||||||
{
|
|
||||||
public IStaticService(ServiceCtx context) { }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,8 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||||
{
|
{
|
||||||
public class SystemStateMgr
|
public class SystemStateMgr
|
||||||
{
|
{
|
||||||
|
public static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000");
|
||||||
|
|
||||||
internal static string[] LanguageCodes = new string[]
|
internal static string[] LanguageCodes = new string[]
|
||||||
{
|
{
|
||||||
"ja",
|
"ja",
|
||||||
|
@ -53,10 +55,8 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||||
|
|
||||||
Account = new AccountUtils();
|
Account = new AccountUtils();
|
||||||
|
|
||||||
UserId defaultUid = new UserId("00000000000000010000000000000000");
|
Account.AddUser(DefaultUserId, "Player");
|
||||||
|
Account.OpenUser(DefaultUserId);
|
||||||
Account.AddUser(defaultUid, "Player");
|
|
||||||
Account.OpenUser(defaultUid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLanguage(SystemLanguage language)
|
public void SetLanguage(SystemLanguage language)
|
||||||
|
|
Reference in a new issue