diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi.meta new file mode 100644 index 00000000..df632aea --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4094856a5db24f142a14fda03f1215a2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs new file mode 100644 index 00000000..648bbd62 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs @@ -0,0 +1,39 @@ +#if UNITY_SWITCH +using nn.account; +#endif + +public class AxiNS +{ + static AxiNS _instance; + public static AxiNS instance + { + get + { + if (_instance == null) + _instance = new AxiNS(); + return _instance; + } + } + + public AxiNSUser user; + public AxiNSMount mount; + public AxiNSIO io; + AxiNS() + { + user = new AxiNSUser(); + mount = new AxiNSMount(); + io = new AxiNSIO(); + } + + /// <summary> + /// ��ʼ�����������Ŀ��һʱ���ʼ������֤�ȳ�ʼ����ʹ��ijЩ�������Ų����ˣ� + /// </summary> + public void Init() + { +#if UNITY_SWITCH + if (!user.GetUserID(out Uid uid)) + return; + mount.MountSave(uid); +#endif + } +} diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs.meta new file mode 100644 index 00000000..4b945141 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNS.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52541c757d45c4c488726bcc39f73ba6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs new file mode 100644 index 00000000..b4c4af29 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs @@ -0,0 +1,367 @@ +public static class AxiNSErrCode +{ +#if UNITY_SWITCH + public static string GetErrorInfo(this nn.Result result) + { + if (result.IsSuccess()) + return "NoErr"; + return GetErrorDetails(result.GetModule(), result.GetDescription()); + } +#endif + /// <summary> + /// ����ģ�� ID ������ ID ���������� Switch ������ĺ��塢����ԭ����취�� + /// </summary> + /// <param name="moduleId">ģ�� ID</param> + /// <param name="descriptionId">���� ID</param> + /// <returns>���������롢���塢����ԭ����취���ַ���</returns> + public static string GetErrorDetails(int moduleId, int descriptionId) + { + string errorCode = $"2{moduleId:D3}-{descriptionId:D4}"; // ��ʽ��Ϊ 2XXX-YYYY + string meaning = "δ֪����"; + string causeAndSolution = "δ֪����������־����ϵ������֧�֡�"; + + switch (moduleId) + { + case 2: // nn::fs (�ļ�ϵͳ) + switch (descriptionId) + { + case 1: + meaning = "ResultPathNotFound"; + causeAndSolution = "·��δ�ҵ������·���Ƿ���ȷ��ȷ����Ŀ¼���ڡ�ʹ�� nn.fs.Directory.Create ������Ŀ¼��"; + break; + case 2: + meaning = "ResultPermissionDenied"; + causeAndSolution = "Ȩ�ޱ��ܾ��������� Atmosphere ������ save:/ ��дȨ�ޡ����Ե��� Atmosphere ���û�ʹ�� sd:/ ���ص㡣"; + break; + case 3: + meaning = "ResultPathAlreadyExists"; + causeAndSolution = "·���Ѵ��ڡ����·���Ƿ�ռ�ã�ɾ���������������ļ�/Ŀ¼��"; + break; + case 5: + meaning = "ResultTargetLocked"; + causeAndSolution = "Ŀ�걻�������������ļ�����ʹ���У��ر���س�������ԡ�"; + break; + case 7: + meaning = "ResultTargetNotFound"; + causeAndSolution = "Ŀ��δ�ҵ���ȷ��Ŀ���ļ�/Ŀ¼�Ƿ���ڣ����·��ƴд��"; + break; + } + break; + + case 5: // nn::err (������) + switch (descriptionId) + { + case 3: + meaning = "microSD ����ش���"; + causeAndSolution = "������������������ microSD �����Ƴ� microSD �������²��룬���������"; + break; + } + break; + + case 16: // nn::oe (����ϵͳ����) + switch (descriptionId) + { + case 247: + meaning = "microSD ���洢����"; + causeAndSolution = "microSD �����ݡ����� microSD ������Ĭ�ϴ洢λ������Ϊϵͳ�ڴ档"; + break; + case 390: + meaning = "��֧�ֵ� microSD ��"; + causeAndSolution = "microSD ����ʽ����֧�֡���ʽ��Ϊ exFAT �� FAT32 �����ԡ�"; + break; + case 601: + meaning = "microSD ��������"; + causeAndSolution = "microSD ���������������ݺ��ʽ������������¿���"; + break; + } + break; + + case 21: // nn::settings (����) + switch (descriptionId) + { + case 3: + meaning = "ϵͳ����δ���»���"; + causeAndSolution = "ϵͳ�̼��汾���ɻ�������ϵͳ�̼��������°�װϵͳ��"; + break; + } + break; + + case 101: // nn::fssrv (�ļ�ϵͳ����) + switch (descriptionId) + { + case 1: + meaning = "ϵͳ����"; + causeAndSolution = "ͨ��ϵͳ�������� Switch���������������ϵ������֧�֡�"; + break; + case 2: + meaning = "�̼���"; + causeAndSolution = "ϵͳ�̼������Ը���ϵͳ����ָ��������ã�ע�ⱸ�����ݣ���"; + break; + } + break; + + case 107: // nn::nim (���簲װ����) + switch (descriptionId) + { + case 427: + meaning = "�̼���"; + causeAndSolution = "ϵͳ�̼�������ϵͳ�̼��������°�װϵͳ��"; + break; + case 445: + meaning = "Ӳ�����������"; + causeAndSolution = "������Ӳ������ڵ������ݡ�ɾ���������ݣ����� Switch������Ч��ϵ������֧�֡�"; + break; + } + break; + + case 110: // nn::socket (��������) + switch (descriptionId) + { + case 1000: + meaning = "��������ʧ��"; + causeAndSolution = "�����ӵ����硣����������ӣ�����·�����������л�����������Ƶ�Σ�2.4GHz/5GHz����"; + break; + case 2003: + meaning = "������������ʧ��"; + causeAndSolution = "�����ź������ȶ�������·�������Ƴ������������·������"; + break; + case 2004: + meaning = "�������ò�֧��"; + causeAndSolution = "���簲ȫ���Ͳ���֧�֡�Switch ֧�� WEP/WPA/WPA2������·�������ú����ԡ�"; + break; + case 2091: + meaning = "������������ʧ��"; + causeAndSolution = "�����������⡣��������Ƿ��ã����� Switch ��·������"; + break; + case 3127: + meaning = "��������ʧ��"; + causeAndSolution = "���粻�ȶ��������������ӣ�����·����������ϵ�����ṩ�̡�"; + break; + } + break; + + case 115: // nn::mii (Mii ���) + switch (descriptionId) + { + case 96: + meaning = "Mii ���ݴ���"; + causeAndSolution = "Mii ������ɾ�������´��� Mii�������ϵͳ��"; + break; + } + break; + + case 139: // nn::nfp (NFC/Amiibo) + switch (descriptionId) + { + case 6: + meaning = "Amiibo ��ȡ����"; + causeAndSolution = "Amiibo ��ȡʧ�ܡ���� Amiibo �Ƿ�������ϵͳ���������� Amiibo��"; + break; + } + break; + + case 153: // nn::ir (��������ͷ) + switch (descriptionId) + { + case 321: + meaning = "��������ͷ��ȡ����"; + causeAndSolution = "��������ͷ����ȡ���������ͷ�Ƿ��ڵ�����ྵͷ������ϵ������֧�֡�"; + break; + case 1540: + meaning = "��������ͷӲ������"; + causeAndSolution = "��������ͷӲ�����ϡ���ϵ������֧�ֽ���ά�ޡ�"; + break; + } + break; + + case 155: // nn::account (�˻�����) + switch (descriptionId) + { + case 8006: + meaning = "�������������˻�"; + causeAndSolution = "�������������жϡ�����������ӣ��Ժ����ԣ���鿴����������״̬ҳ�档"; + break; + } + break; + + case 160: // nn::online (���߷���) + switch (descriptionId) + { + case 103: + meaning = "����������ƥ��"; + causeAndSolution = "���粻�ȶ������� Switch������������ӣ����Ժ����ԡ�"; + break; + case 202: + meaning = "�����ӵ����߷���"; + causeAndSolution = "�����������ά�����鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + case 8006: + meaning = "���Ӳ���ʧ��"; + causeAndSolution = "�������⡣����·����������������ã������������硣"; + break; + } + break; + + case 162: // nn::application (Ӧ�ó���) + switch (descriptionId) + { + case 2: + meaning = "��������"; + causeAndSolution = "���������������ǵ������ݻ�̼����⡣ɾ���������ݣ�����ϵͳ�������°�װ������"; + break; + case 101: + meaning = "������Ҫ����"; + causeAndSolution = "��Ϸ��������Ҫ���¡�����������²���װ��"; + break; + } + break; + + case 168: // nn::sys (ϵͳ) + switch (descriptionId) + { + case 0: + meaning = "��Ҫ��������"; + causeAndSolution = "������Ҫ���¡�������Ϸ��ϵͳ�̼���"; + break; + case 2: + meaning = "ϵͳ����"; + causeAndSolution = "ϵͳ������������Ӳ�����⡣���� Switch������Ч��ϵ������֧�֡�"; + break; + } + break; + + case 205: // nn::camera (����ͷ) + switch (descriptionId) + { + case 123: + meaning = "����ͷ��ȡ����"; + causeAndSolution = "����ͷ����ȡ���������ͷ�Ƿ��ڵ�����ྵͷ������ϵ������֧�֡�"; + break; + } + break; + + case 306: // nn::ngc (������Ϸ����) + switch (descriptionId) + { + case 501: + meaning = "����������ƥ��"; + causeAndSolution = "���������жϡ����� Switch������������ӣ����Ժ����ԡ�"; + break; + case 502: + meaning = "ƥ�����ʧ��"; + causeAndSolution = "���粻�ȶ��������������ӣ�����·����������ϵ�����ṩ�̡�"; + break; + case 820: + meaning = "���߷�����"; + causeAndSolution = "�����������ά�����鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + } + break; + + case 613: // nn::eShop (eShop) + switch (descriptionId) + { + case 1400: + meaning = "��ʹ�����ÿ�����"; + causeAndSolution = "���ÿ���Ϣ����� eShop �������⡣������ÿ���Ϣ���Ժ����ԣ�����ϵ������֧�֡�"; + break; + case 6838: + meaning = "eShop ����ʧ��"; + causeAndSolution = "��������� eShop ά��������������ӣ��鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + } + break; + + case 618: // nn::ngc (������Ϸ����) + switch (descriptionId) + { + case 6: + meaning = "����������ƥ��"; + causeAndSolution = "���粻�ȶ������� Switch������������ӣ����Ժ����ԡ�"; + break; + case 201: + meaning = "ƥ������"; + causeAndSolution = "���Լ����ƥ���������Ժ����ԣ����������ƥ�䡣"; + break; + case 501: + meaning = "ƥ�����ʧ��"; + causeAndSolution = "���������жϡ����� Switch������������ӣ����Ժ����ԡ�"; + break; + } + break; + + case 801: // nn::sns (�罻�������) + switch (descriptionId) + { + case 7002: + meaning = "���ϴ���ͼ�� Twitter"; + causeAndSolution = "�����������ά�����鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + case 7199: + meaning = "���ϴ���Ƭ�� Facebook"; + causeAndSolution = "�����������ά�����鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + } + break; + + case 810: // nn::account (�˻�����) + switch (descriptionId) + { + case 1224: + meaning = "����¼�������˻�"; + causeAndSolution = "�������������жϡ�����������ӣ��Ժ����ԣ���鿴����������״̬ҳ�档"; + break; + case 1500: + meaning = "����¼ Facebook �˻�"; + causeAndSolution = "�����������ά�������� Switch���Ժ����ԡ�"; + break; + } + break; + + case 811: // nn::account (�˻�����) + switch (descriptionId) + { + case 1006: + meaning = "�������������˻�"; + causeAndSolution = "��������� DNS ������������ӣ����Ը��� DNS���� 8.8.8.8�������Ժ����ԡ�"; + break; + case 5001: + meaning = "������ eShop"; + causeAndSolution = "eShop �����жϡ��鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + } + break; + + case 813: // nn::eShop (eShop) + switch (descriptionId) + { + case 0: + meaning = "eShop ����ʧ��"; + causeAndSolution = "eShop �����жϡ��鿴����������״̬ҳ�棬�Ժ����ԡ�"; + break; + case 2470: + meaning = "���״���ʧ��"; + causeAndSolution = "���ÿ���Ϣ����� eShop �������⡣������ÿ���Ϣ���Ժ����ԣ�����ϵ������֧�֡�"; + break; + } + break; + + case 819: // nn::online (���߷���) + switch (descriptionId) + { + case 3: + meaning = "��������ͣ"; + causeAndSolution = "ͬһ�˻�����һ̨�豸��ʹ�á��ر������豸�ϵ���������ʹ�ò�ͬ�˻���"; + break; + } + break; + + default: + meaning = "δ֪ģ��"; + causeAndSolution = "δʶ���ģ�� ID��������־����ϵ������֧�֡�"; + break; + } + + return $"������: {errorCode}\n����: {meaning}\n����ԭ����취: {causeAndSolution}"; + } +} diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs.meta new file mode 100644 index 00000000..d734ad71 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSErrCode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e28b69692cb1bb4a9d8ddb91274fa50 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs new file mode 100644 index 00000000..8edbe405 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs @@ -0,0 +1,394 @@ +#if UNITY_SWITCH +using nn.fs; +#endif + +public class AxiNSIO +{ + string save_name => AxiNS.instance.mount.SaveMountName; + public string save_path => $"{save_name}:/"; +#if UNITY_SWITCH + private FileHandle fileHandle = new nn.fs.FileHandle(); +#endif + /// <summary> + /// ���Path�Ƿ���� + /// </summary> + /// <param name="filePath"></param> + /// <returns></returns> + public bool CheckPathExists(string filePath) + { +#if !UNITY_SWITCH + return false; +#else + nn.fs.EntryType entryType = 0; + nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, filePath); + //result.abortUnlessSuccess(); + //����쳣������ı�Ť + return nn.fs.FileSystem.ResultPathAlreadyExists.Includes(result); +#endif + } + /// <summary> + /// ���Path�Ƿ��� + /// </summary> + /// <param name="filePath"></param> + /// <returns></returns> + public bool CheckPathNotFound(string filePath) + { +#if !UNITY_SWITCH + return false; +#else + nn.fs.EntryType entryType = 0; + nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, filePath); + //����쳣������ı�Ť + return nn.fs.FileSystem.ResultPathNotFound.Includes(result); +#endif + } + /// <summary> + /// ����Ŀ¼��Ŀ¼����Ҳ�᷵��true + /// </summary> + /// <param name="filePath"></param> + /// <returns></returns> + public bool CreateDir(string filePath) + { +#if !UNITY_SWITCH + return false; +#else + // ʹ�÷�װ�������ʹ�����Ŀ¼ + if (!EnsureParentDirectory(filePath, true)) + { + UnityEngine.Debug.LogError($"��ȷ����Ŀ¼���ļ�д��ȡ��: {filePath}"); + return false; + } + return true; +#endif + } + /// <summary> + /// ���沢�����ļ������Ŀ¼�����ڻ����Զ�����Ŀ¼�� + /// </summary> + /// <param name="filePath"></param> + /// <param name="bw"></param> + /// <returns></returns> + public bool FileToSaveWithCreate(string filePath, System.IO.MemoryStream ms) + { + return FileToSaveWithCreate(filePath, ms.ToArray()); + } + /// <summary> + /// ���沢�����ļ������Ŀ¼�����ڻ����Զ�����Ŀ¼�� + /// </summary> + /// <param name="filePath"></param> + /// <param name="data"></param> + /// <returns></returns> + public bool FileToSaveWithCreate(string filePath, byte[] data) + { +#if !UNITY_SWITCH + return false; +#else + if (!AxiNS.instance.mount.SaveIsMount) + { + UnityEngine.Debug.LogError($"Save ��δ���أ����洢 {filePath}"); + return false; + } + + nn.Result result; +#if UNITY_SWITCH && !UNITY_EDITOR + // ��ֹ�û��ڱ���ʱ���˳���Ϸ + // Switch ���� 0080 + UnityEngine.Switch.Notification.EnterExitRequestHandlingSection(); +#endif + // ʹ�÷�װ�������ʹ�����Ŀ¼ + if (!EnsureParentDirectory(filePath, true)) + { + UnityEngine.Debug.LogError($"��ȷ����Ŀ¼���ļ�д��ȡ��: {filePath}"); + return false; + } + + //string directoryPath = System.IO.Path.GetDirectoryName(filePath.Replace(save_path, "")); + //string fullDirectoryPath = $"{save_path}{directoryPath}"; + //UnityEngine.Debug.Log($"��鸸Ŀ¼: {fullDirectoryPath}"); + + //nn.fs.EntryType entryType = 0; + //result = nn.fs.FileSystem.GetEntryType(ref entryType, fullDirectoryPath); + //if (!result.IsSuccess() && nn.fs.FileSystem.ResultPathNotFound.Includes(result)) + //{ + // UnityEngine.Debug.Log($"��Ŀ¼ {fullDirectoryPath} �����ڣ����Դ��� (�ж����� result=>{result.ToString()})"); + // result = nn.fs.Directory.Create(fullDirectoryPath); + // if (!result.IsSuccess()) + // { + // UnityEngine.Debug.LogError($"������Ŀ¼ʧ��: {result.GetErrorInfo()}"); + // return false; + // } + // UnityEngine.Debug.Log($"��Ŀ¼ {fullDirectoryPath} �����ɹ�"); + //} + //else if (result.IsSuccess() && entryType != nn.fs.EntryType.Directory) + //{ + // UnityEngine.Debug.LogError($"·�� {fullDirectoryPath} �Ѵ��ڣ�������Ŀ¼"); + // return false; + //} + //else if (!result.IsSuccess()) + //{ + // UnityEngine.Debug.LogError($"��鸸Ŀ¼ʧ��: {result.GetErrorInfo()}"); + // return false; + //} + + if (CheckPathNotFound(filePath)) + { + UnityEngine.Debug.Log($"�ļ�({filePath})��������Ҫ����"); + result = nn.fs.File.Create(filePath, data.Length); //this makes a file the size of your save journal. You may want to make a file smaller than this. + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"�����ļ�ʧ�� {filePath} : " + result.GetErrorInfo()); + return false; + } + } + else + UnityEngine.Debug.Log($"�ļ�({filePath})���ڣ����ش���"); + + result = File.Open(ref fileHandle, filePath, OpenFileMode.Write); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"ʧ�� File.Open(ref filehandle, {filePath}, OpenFileMode.Write): " + result.GetErrorInfo()); + return false; + } + UnityEngine.Debug.Log($"�ɹ� File.Open(ref filehandle, {filePath}, OpenFileMode.Write)"); + + //nn.fs.WriteOption.Flush Ӧ�þ��Ǹ���д�� + result = nn.fs.File.Write(fileHandle, 0, data, data.Length, nn.fs.WriteOption.Flush); // Writes and flushes the write at the same time + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError("д���ļ�ʧ��: " + result.GetErrorInfo()); + return false; + } + UnityEngine.Debug.Log("д���ļ��ɹ�: " + filePath); + + nn.fs.File.Close(fileHandle); + + //������ύ������û����ʵд�� + result = FileSystem.Commit(save_name); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) ʧ��: " + result.GetErrorInfo()); + return false; + } + UnityEngine.Debug.Log($"FileSystem.Commit({save_name}) �ɹ�: "); + + +#if UNITY_SWITCH && !UNITY_EDITOR + // ֹͣ��ֹ�û��˳���Ϸ + UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection(); +#endif + + return true; +#endif + } + /// <summary> + /// ���沢�����ļ������Ŀ¼�����ڻ����Զ�����Ŀ¼�� + /// </summary> + /// <param name="filePath"></param> + /// <param name="bw"></param> + /// <returns></returns> + public bool LoadSwitchDataFile(string filename, ref System.IO.MemoryStream ms) + { + if (LoadSwitchDataFile(filename, out byte[] outputData)) + { + System.IO.BinaryWriter writer = new System.IO.BinaryWriter(ms); + writer.Write(outputData); + return true; + } + return false; + } + public bool LoadSwitchDataFile(string filename, out byte[] outputData) + { +#if !UNITY_SWITCH + outputData = null; + return false; +#else + outputData = null; + if (!AxiNS.instance.mount.SaveIsMount) + { + UnityEngine.Debug.LogError($"Save ��δ���أ�����ȡ {filename}"); + return false; + } + if (CheckPathNotFound(filename)) + return false; + + nn.Result result; + result = nn.fs.File.Open(ref fileHandle, filename, nn.fs.OpenFileMode.Read); + if (result.IsSuccess() == false) + { + UnityEngine.Debug.LogError($"nn.fs.File.Open ʧ�� {filename} : result=>{result.GetErrorInfo()}"); + return false; // Could not open file. This can be used to detect if this is the first time a user has launched your game. + // (However, be sure you are not getting this error due to your file being locked by another process, etc.) + } + UnityEngine.Debug.Log($"nn.fs.File.Open �ɹ� {filename}"); + long iFileSize = 0; + result = nn.fs.File.GetSize(ref iFileSize, fileHandle); + if (result.IsSuccess() == false) + { + UnityEngine.Debug.LogError($"nn.fs.File.GetSize ʧ�� {filename} : result=>{result.GetErrorInfo()}"); + return false; + } + UnityEngine.Debug.Log($"nn.fs.File.GetSize �ɹ� {filename},size=>{iFileSize}"); + + byte[] loadedData = new byte[iFileSize]; + result = nn.fs.File.Read(fileHandle, 0, loadedData, iFileSize); + if (result.IsSuccess() == false) + { + UnityEngine.Debug.LogError($"nn.fs.File.Read ʧ�� {filename} : result=>{result.GetErrorInfo()}"); + return false; + } + UnityEngine.Debug.Log($"nn.fs.File.Read �ɹ� {filename}"); + + nn.fs.File.Close(fileHandle); + + //for (int i = 0; i < loadedData.Length; i++) + //{ + // UnityEngine.Debug.Log($"data[{i}]:{loadedData[i]}"); + //} + + outputData = loadedData; + return true; +#endif + } + public bool DeletePathFile(string filename) + { +#if !UNITY_SWITCH + return false; +#else + //TODO +#endif + } + public bool DeletePathDir(string filename) + { +#if !UNITY_SWITCH + return false; +#else + //TODO +#endif + } + bool EnsureParentDirectory(string filePath, bool bAutoCreateDir = true) + { +#if !UNITY_SWITCH + return false; +#else + // ����У�� + if (string.IsNullOrEmpty(filePath)) + { + UnityEngine.Debug.LogError($"��Ч������filePath={filePath}"); + return false; + } + + // ��ȡ·��ǰ���� save:/��sd:/�� + int prefixEndIndex = filePath.IndexOf(":/"); + if (prefixEndIndex == -1) + { + UnityEngine.Debug.LogError($"�ļ�·�� {filePath} ��ʽ��Ч��δ�ҵ� ':/' ǰ"); + return false; + } + string pathPrefix = filePath.Substring(0, prefixEndIndex + 2); // ��ȡǰ������ "save:/" + string relativePath = filePath.Substring(prefixEndIndex + 2); // �Ƴ�ǰ���õ����·�� + + // ������״̬ + if (!IsMountPointAccessible(pathPrefix)) + { + UnityEngine.Debug.LogError($"���ص� {pathPrefix} δ���أ�������·�� {filePath}"); + return false; + } + + // ��ȡ��Ŀ¼·�� + string directoryPath = System.IO.Path.GetDirectoryName(relativePath); // ��ȡ��Ŀ¼���·�� + if (string.IsNullOrEmpty(directoryPath)) + { + UnityEngine.Debug.Log($"�ļ�·�� {filePath} ���贴����Ŀ¼��λ�ڸ�Ŀ¼��"); + return true; // ��Ŀ¼���贴�� + } + + string fullDirectoryPath = $"{pathPrefix}{directoryPath}"; // ƴ��������Ŀ¼·�� + UnityEngine.Debug.Log($"��鸸Ŀ¼: {fullDirectoryPath}"); + + // ���·���Ƿ���ڼ������� + nn.fs.EntryType entryType = 0; + nn.Result result = nn.fs.FileSystem.GetEntryType(ref entryType, fullDirectoryPath); + if (!result.IsSuccess() && nn.fs.FileSystem.ResultPathNotFound.Includes(result)) + { + if (bAutoCreateDir) + { + // ·�������ڣ����Դ��� + UnityEngine.Debug.Log($"��Ŀ¼ {fullDirectoryPath} �����ڣ����Դ��� (�ж����� result=>{result.ToString()})"); + result = nn.fs.Directory.Create(fullDirectoryPath); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"������Ŀ¼ʧ��: {result.GetErrorInfo()}"); + return false; + } + UnityEngine.Debug.Log($"��Ŀ¼ {fullDirectoryPath} �����ɹ�"); + return true; + } + return false; + } + else if (result.IsSuccess() && entryType != nn.fs.EntryType.Directory) + { + // ·�����ڣ�������Ŀ¼ + UnityEngine.Debug.LogError($"·�� {fullDirectoryPath} �Ѵ��ڣ�������Ŀ¼"); + return false; + } + else if (!result.IsSuccess()) + { + // �������� + UnityEngine.Debug.LogError($"��鸸Ŀ¼ʧ��: {result.GetErrorInfo()}"); + return false; + } + // ·����������Ŀ¼ + UnityEngine.Debug.Log($"��Ŀ¼ {fullDirectoryPath} �Ѵ�������Ч"); + return true; + +#endif + } + /// <summary> + /// ���ָ�����ص��Ƿ�ɷ��� + /// </summary> + /// <param name="pathPrefix">·��ǰ������ "save:/" �� "sd:/"</param> + /// <returns>���ص��Ƿ�ɷ���</returns> + bool IsMountPointAccessible(string pathPrefix) + { +#if !UNITY_SWITCH + return false; +#else + if (string.IsNullOrEmpty(pathPrefix)) + { + UnityEngine.Debug.LogError($"��Ч���ص�: {pathPrefix}"); + return false; + } + + // ����ǰ�жϹ��ص����Ͳ�������״̬ + if (pathPrefix == $"{save_name}:/") + { + if (!AxiNS.instance.mount.SaveIsMount) + { + UnityEngine.Debug.LogError($"{save_name}:/ δ����"); + return false; + } + return true; + } + else if (pathPrefix == "sd:/") + { + long freeSpace = 0; + // ��� SD ������״̬��ʾ���������ʵ��ʵ�ֵ����� + nn.Result result = nn.fs.FileSystem.GetFreeSpaceSize(ref freeSpace, "sd:/"); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"sd:/ δ���ػ�������: {result.GetErrorInfo()}"); + return false; + } + return true; + } + else + { + UnityEngine.Debug.LogWarning($"δ֪���ص� {pathPrefix}���ٶ��ѹ���"); + return true; // �������ص������ʵ������ʵ�� + } +#endif + } +} diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs.meta new file mode 100644 index 00000000..7fb9bc07 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d910a015a6b6561418bdff7f2c48cffa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs new file mode 100644 index 00000000..0d1bae7c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs @@ -0,0 +1,124 @@ +#if UNITY_SWITCH +using nn.account; +#endif +public class AxiNSMount +{ + static bool bInMount = false; + internal static string m_SaveMountName; + static bool bInMountForDebug = false; + internal static string m_SaveMountForDebugName; + + public bool SaveIsMount => bInMount; + public string SaveMountName + { + get + { + if (!bInMount) + return string.Empty; + else + return m_SaveMountName; + } + } + +#if UNITY_SWITCH + public bool MountSave(Uid userId, string mountName = "save") + { + if (bInMount) + return true; + + if (!nn.fs.SaveData.IsExisting(userId)) + { + UnityEngine.Debug.LogError($"{userId.ToString()}�浵�����ڣ�"); + return false; + } + UnityEngine.Debug.Log($"{userId.ToString()}�浵ȷ�����ڣ�"); + + nn.Result result; + result = nn.fs.SaveData.Mount(mountName, userId); + //result.abortUnlessSuccess(); + + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"MountSave->����{mountName}:/ ʧ��: " + result.ToString()); + return false; + } + UnityEngine.Debug.Log($"MountSave->����{mountName}:/ �ɹ� "); + m_SaveMountName = mountName; + bInMount = true; + return true; + } + #endif + + public bool MountSDForDebug(string mountName = "sd") + { +#if !UNITY_SWITCH + return false; +#else + if (bInMountForDebug) + return true; + nn.Result result; + result = nn.fs.SdCard.MountForDebug(mountName); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"nn_fs_MountSdCardForDebug->����{mountName}:/ ʧ��: " + result.ToString()); + return false; + } + UnityEngine.Debug.Log($"nn_fs_MountSdCardForDebug->����{mountName}:/ �ɹ� "); + m_SaveMountForDebugName = mountName; + bInMountForDebug = true; + return true; +#endif + } + + public bool MountSD(string mountName = "sd") + { +#if !UNITY_SWITCH + return false; +#else + if (bInMountForDebug) + return true; + nn.Result result; + result = nn.fs.SdCard.Mount(mountName); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"nn_fs_MountSdCard->����{mountName}:/ ʧ��: " + result.ToString()); + return false; + } + UnityEngine.Debug.Log($"nn_fs_MountSdCard->����{mountName}:/ �ɹ� "); + m_SaveMountForDebugName = mountName; + bInMountForDebug = true; + return true; +#endif + } + + + public void UnmountSave() + { +#if UNITY_SWITCH + if (!bInMount) + { + UnityEngine.Debug.LogError($"{m_SaveMountName}:/ û�б����أ�����ж��"); + return; + } + nn.fs.FileSystem.Unmount(m_SaveMountName); + UnityEngine.Debug.LogError($"UnmountSaveForDebufa->��ж��{m_SaveMountName}:/ "); + bInMount = false; +#endif + } + + public void UnmountSaveForDebug() + { +#if UNITY_SWITCH + if (!bInMountForDebug) + { + UnityEngine.Debug.LogError($"{m_SaveMountForDebugName}:/ û�б����أ�����ж��"); + return; + } + nn.fs.FileSystem.Unmount(m_SaveMountForDebugName); + UnityEngine.Debug.LogError($"UnmountSaveForDebufa->��ж��{m_SaveMountForDebugName}:/ "); + bInMountForDebug = false; +#endif + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs.meta new file mode 100644 index 00000000..6af050fc --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 48826c5dc8959ff4db8c6a51b6568bb7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs new file mode 100644 index 00000000..2deaf883 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs @@ -0,0 +1,106 @@ +#if UNITY_SWITCH +using nn.account; +#endif + +public class AxiNSUser +{ + bool m_bInit = false; + bool m_bGotOpenPreselectedUser = false; + +#if UNITY_SWITCH + Uid m_UserId; + nn.account.UserHandle mUserHandle; + nn.account.Nickname m_NickName; +#endif + + #region ������ӿڣ�ȷ���ڲ���ȫ������������� + +#if UNITY_SWITCH + public bool GetUserID(out Uid uid) + { + InitPreselectedUserInfo(); + if (!m_bGotOpenPreselectedUser) + { + uid = Uid.Invalid; + return false; + } + uid = m_UserId; + return true; + } +#endif + public bool GetNickName(out string NickName) + { +#if !UNITY_SWITCH + NickName = ""; + return true; +#else + InitPreselectedUserInfo(); + if (!m_bGotOpenPreselectedUser) + { + NickName = string.Empty; + return false; + } + NickName = m_NickName.ToString(); + return true; +#endif + } +#endregion + /// <summary> + /// ��ʼ��Accountģ��� + /// </summary> + void InitNSAccount() + { +#if UNITY_SWITCH + if (m_bInit) + return; + //�����ȳ�ʼ��NS��Account ��Ȼ���ü��� + nn.account.Account.Initialize(); + m_bInit = true; +#endif + } + + /// <summary> + /// ��ȡԤѡ�û� + /// </summary> + void InitPreselectedUserInfo() + { +#if UNITY_SWITCH + if (m_bGotOpenPreselectedUser) + return; + + InitNSAccount(); + nn.Result result; + mUserHandle = new nn.account.UserHandle(); + if (!nn.account.Account.TryOpenPreselectedUser(ref mUserHandle)) + { + UnityEngine.Debug.LogError("��Ԥѡ���û�ʧ��."); + return; + } + UnityEngine.Debug.Log("��Ԥѡ�û��ɹ�."); + result = nn.account.Account.GetUserId(ref m_UserId, mUserHandle); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"GetUserIdʧ��: {result.ToString()}"); + return; + } + + if (m_UserId == Uid.Invalid) + { + UnityEngine.Debug.LogError("����ȡ�û� ID"); + return; + } + UnityEngine.Debug.Log($"��ȡ�û� ID:{m_UserId.ToString()}"); + + result = nn.account.Account.GetNickname(ref m_NickName, m_UserId); + //result.abortUnlessSuccess(); + if (!result.IsSuccess()) + { + UnityEngine.Debug.LogError($"GetNicknameʧ��: {result.ToString()}"); + return; + } + UnityEngine.Debug.Log($"��ȡ�û� NickName ID:{m_NickName.ToString()}"); + m_bGotOpenPreselectedUser = true; +#endif + } +} diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs.meta new file mode 100644 index 00000000..af807677 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSUser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95c2e164c69c6cc4887a194d6eba0cc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: