forked from Mirror/Ryujinx
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.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Mii;
|
||||
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
|
||||
using Ryujinx.HLE.HOS.Services.Settings;
|
||||
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
|
||||
|
||||
TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
|
||||
|
||||
DatabaseImpl.Instance.InitializeDatabase(device);
|
||||
}
|
||||
|
||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LibHac.Account;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -73,9 +74,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||
return HashCode.Combine(Low, High);
|
||||
}
|
||||
|
||||
public Uid ToLibHacUid()
|
||||
public readonly Uid ToLibHacUid()
|
||||
{
|
||||
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+
|
||||
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 static readonly UserId DefaultUserId = new UserId("00000000000000010000000000000000");
|
||||
|
||||
internal static string[] LanguageCodes = new string[]
|
||||
{
|
||||
"ja",
|
||||
|
@ -53,10 +55,8 @@ namespace Ryujinx.HLE.HOS.SystemState
|
|||
|
||||
Account = new AccountUtils();
|
||||
|
||||
UserId defaultUid = new UserId("00000000000000010000000000000000");
|
||||
|
||||
Account.AddUser(defaultUid, "Player");
|
||||
Account.OpenUser(defaultUid);
|
||||
Account.AddUser(DefaultUserId, "Player");
|
||||
Account.OpenUser(DefaultUserId);
|
||||
}
|
||||
|
||||
public void SetLanguage(SystemLanguage language)
|
||||
|
|
Reference in a new issue