Ryujinx/Ryujinx.HLE/HOS/Services/Mii/Types/Nickname.cs
Thog 3b531de670
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>
2020-03-02 09:56:01 +11:00

120 lines
3 KiB
C#

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());
}
}
}