aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/Mii/StaticService
diff options
context:
space:
mode:
authorThog <me@thog.eu>2020-03-01 23:56:02 +0100
committerGitHub <noreply@github.com>2020-03-02 09:56:01 +1100
commit3b531de670a011c40b269eaba33e2fea8f438859 (patch)
tree1c0ac3711297340b8b256ac27ca9baea2aa3969a /Ryujinx.HLE/HOS/Services/Mii/StaticService
parent7d1a294eae00b8f6e752ec34f794ba09ea80974a (diff)
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>
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Mii/StaticService')
-rw-r--r--Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs266
-rw-r--r--Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs423
2 files changed, 689 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs b/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs
new file mode 100644
index 00000000..4b5ed0d0
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Mii/StaticService/DatabaseServiceImpl.cs
@@ -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);
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
new file mode 100644
index 00000000..4ce531f4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Mii/StaticService/IDatabaseService.cs
@@ -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);
+ }
+}