diff --git a/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack.meta b/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack.meta
index 7182dbd8..b3e69672 100644
--- a/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack.meta
+++ b/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 611bc182f939ea147a72b08613e2d2ba
+guid: 164952f99969ca942b4761b200d7e381
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs b/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs
index c9e192f0..e623c351 100644
--- a/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs
+++ b/AxibugEmuOnline.Client/Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
@@ -82,7 +83,7 @@ namespace AxibugEmuOnline.Editors
{
BuildReport report = BuildPipeline.BuildPlayer(options);
}
- catch(Exception ex)
+ catch (Exception ex)
{
Debug.LogError($"[AxibugNSPTools] Unity Build NSP 错误:{ex.ToString()}");
return;
@@ -147,13 +148,13 @@ namespace AxibugEmuOnline.Editors
#region 清理临时目录
CleanDirectory(Path.Combine(nspParentDir, "repacker_extract"));
CleanDirectory(Path.Combine(Path.GetTempPath(), "NCA"));
- CleanDirectory(Path.Combine(WorkRoot, "hacpack_backup"));
+ CleanDirectory(Path.Combine(nspParentDir, "hacpack_backup"));
#endregion
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"解包NSP文件", 0.2f);
#region 解包NSP文件
string extractPath = Path.Combine(nspParentDir, "repacker_extract");
- ExecuteCommand($"{tools["authoringTool"]} extract -o \"{extractPath}\" \"{nspFilePath}\"");
+ ExecuteCommand($"{tools["authoringTool"]} extract -o \"{extractPath}\" \"{nspFilePath}\"", nspParentDir);
string controlPath = null;
string programPath = null;
@@ -167,34 +168,43 @@ namespace AxibugEmuOnline.Editors
#region 重建NCA/NSP
string tmpPath = Path.Combine(Path.GetTempPath(), "NCA");
- EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NCA", 0.6f);
- string programNCA = BuildProgramNCA(tmpPath, titleID, programPath);
- EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NCA", 0.7f);
- string controlNCA = BuildControlNCA(tmpPath, titleID, controlPath);
- EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NCA", 0.8f);
- BuildMetaNCA(tmpPath, titleID, programNCA, controlNCA);
+ EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Program NCA", 0.3f);
+ string programNCA = BuildProgramNCA(tmpPath, titleID, programPath, nspParentDir);
+ EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Control NCA", 0.4f);
+ string controlNCA = BuildControlNCA(tmpPath, titleID, controlPath, nspParentDir);
+ EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建 Meta NCA", 0.5f);
+ BuildMetaNCA(tmpPath, titleID, programNCA, controlNCA, nspParentDir);
+ EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 0.6f);
+ string outputNSP = BuildFinalNSP(nspFilePath, nspParentDir, tmpPath, titleID, nspParentDir);
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 0.9f);
- string outputNSP = BuildFinalNSP(nspFilePath, nspParentDir, tmpPath, titleID);
- EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 1f);
Debug.Log($"[AxibugNSPTools]Repacking completed: {outputNSP}");
- EditorUtility.ClearProgressBar();
- #endregion
+
+ #endregion
+
+ EditorUtility.DisplayProgressBar("AxibugNSPTools", $"清理临时目录", 1);
+ #region 清理临时目录
+ CleanDirectory(Path.Combine(nspParentDir, "repacker_extract"));
+ CleanDirectory(Path.Combine(Path.GetTempPath(), "NCA"));
+ CleanDirectory(Path.Combine(nspParentDir, "hacpack_backup"));
+ #endregion
+ System.Diagnostics.Process.Start("explorer", "/select,\"" + outputNSP.Trim() + "\"");
+ EditorUtility.ClearProgressBar();
}
+
+
#region 辅助方法
static string GetUserInput()
{
Console.Write("Enter the NSP filepath: ");
return Console.ReadLine();
}
-
static string ExtractTitleID(string path)
{
var match = Regex.Match(path, @"0100[\dA-Fa-f]{12}");
return match.Success ? match.Value : null;
}
-
static void CleanDirectory(string path)
{
if (Directory.Exists(path))
@@ -215,8 +225,9 @@ namespace AxibugEmuOnline.Editors
}
}
- static string ExecuteCommand(string command)
+ static string ExecuteCommand(string command, string workdir)
{
+ Debug.Log($"调用cmd=>{command}");
var process = new System.Diagnostics.Process()
{
StartInfo = new System.Diagnostics.ProcessStartInfo
@@ -228,7 +239,8 @@ namespace AxibugEmuOnline.Editors
UseShellExecute = false,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8, // 明确指定编码
- StandardErrorEncoding = Encoding.UTF8
+ StandardErrorEncoding = Encoding.UTF8,
+ WorkingDirectory = workdir
}
};
@@ -275,36 +287,36 @@ namespace AxibugEmuOnline.Editors
#endregion
#region NCA构建逻辑
- static string BuildProgramNCA(string tmpPath, string titleID, string programDir)
+ static string BuildProgramNCA(string tmpPath, string titleID, string programDir, string workdir)
{
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
$"--type nca --ncatype program --exefsdir \"{programDir}/fs0\" " +
$"--romfsdir \"{programDir}/fs1\" --logodir \"{programDir}/fs2\"";
- string output = ExecuteCommand($"{tools["hacPack"]} {args}");
+ string output = ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
return ParseNCAOutput(output, "Program");
}
- static string BuildControlNCA(string tmpPath, string titleID, string controlDir)
+ static string BuildControlNCA(string tmpPath, string titleID, string controlDir, string workdir)
{
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
$"--type nca --ncatype control --romfsdir \"{controlDir}/fs0\"";
- string output = ExecuteCommand($"{tools["hacPack"]} {args}");
+ string output = ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
return ParseNCAOutput(output, "Control");
}
- static void BuildMetaNCA(string tmpPath, string titleID, string programNCA, string controlNCA)
+ static void BuildMetaNCA(string tmpPath, string titleID, string programNCA, string controlNCA, string workdir)
{
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
$"--type nca --ncatype meta --titletype application " +
$"--programnca \"{programNCA}\" --controlnca \"{controlNCA}\"";
- ExecuteCommand($"{tools["hacPack"]} {args}");
+ ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
}
- static string BuildFinalNSP(string origPath, string parentDir, string tmpPath, string titleID)
+ static string BuildFinalNSP(string origPath, string parentDir, string tmpPath, string titleID, string workdir)
{
string outputPath = origPath.Replace(".nsp", "_repacked.nsp");
if (File.Exists(outputPath)) File.Delete(outputPath);
@@ -312,7 +324,7 @@ namespace AxibugEmuOnline.Editors
string args = $"-k \"{prodKeysPath}\" -o \"{parentDir}\" --titleid {titleID} " +
$"--type nsp --ncadir \"{tmpPath}\"";
- ExecuteCommand($"{tools["hacPack"]} {args}");
+ ExecuteCommand($"{tools["hacPack"]} {args}", workdir);
File.Move(Path.Combine(parentDir, $"{titleID}.nsp"), outputPath);
return outputPath;
}
@@ -321,7 +333,9 @@ namespace AxibugEmuOnline.Editors
{
var line = output.Split('\n')
.FirstOrDefault(l => l.Contains($"Created {type} NCA:"));
- return line?.Split(':').Last().Trim();
+ //return line?.Split(':').Last().Trim();
+ return line?.Substring(line.IndexOf("NCA:") + "NCA:".Length).Trim();
+
}
#endregion
}
diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs
index 6431408c..7e20444b 100644
--- a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs
+++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSIO.cs
@@ -1,5 +1,7 @@
#if UNITY_SWITCH
using nn.fs;
+using System.Security.Cryptography;
+
#endif
public class AxiNSIO
@@ -253,18 +255,194 @@ public class AxiNSIO
#if !UNITY_SWITCH
return false;
#else
- //TODO
+
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // This next line prevents the user from quitting the game while saving.
+ // This is required for Nintendo Switch Guideline 0080
+ UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
#endif
- }
- public bool DeletePathDir(string filename)
+
+ if (CheckPathNotFound(filename))
+ return false;
+ nn.Result result;
+ result = nn.fs.File.Delete(filename);
+ if (result.IsSuccess() == false)
+ {
+ UnityEngine.Debug.LogError($"nn.fs.File.Delete 失败 {filename} : result=>{result.GetErrorInfo()}");
+ return false;
+ }
+ result = nn.fs.FileSystem.Commit(save_name);
+ if (!result.IsSuccess())
+ {
+ UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + result.GetErrorInfo());
+ return false;
+ }
+ return true;
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // End preventing the user from quitting the game while saving.
+ UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
+#endif
+
+#endif
+ }
+ public bool DeletePathDir(string filename)
{
#if !UNITY_SWITCH
return false;
#else
- //TODO
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // This next line prevents the user from quitting the game while saving.
+ // This is required for Nintendo Switch Guideline 0080
+ UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
#endif
- }
- bool EnsureParentDirectory(string filePath, bool bAutoCreateDir = true)
+
+ if (CheckPathNotFound(filename))
+ return false;
+ nn.Result result;
+ result = nn.fs.Directory.Delete(filename);
+ if (result.IsSuccess() == false)
+ {
+ UnityEngine.Debug.LogError($"nn.fs.File.Delete 失败 {filename} : result=>{result.GetErrorInfo()}");
+ return false;
+ }
+ result = nn.fs.FileSystem.Commit(save_name);
+ if (!result.IsSuccess())
+ {
+ UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + result.GetErrorInfo());
+ return false;
+ }
+ return true;
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // End preventing the user from quitting the game while saving.
+ UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
+#endif
+#endif
+ }
+
+ ///
+ /// 递归删除目录
+ ///
+ ///
+ ///
+ public bool DeleteRecursivelyPathDir(string filename)
+ {
+#if !UNITY_SWITCH
+ return false;
+#else
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // This next line prevents the user from quitting the game while saving.
+ // This is required for Nintendo Switch Guideline 0080
+ UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
+#endif
+
+ if (CheckPathNotFound(filename))
+ return false;
+ nn.Result result;
+ result = nn.fs.Directory.DeleteRecursively(filename);
+ if (result.IsSuccess() == false)
+ {
+ UnityEngine.Debug.LogError($"nn.fs.File.DeleteRecursively 失败 {filename} : result=>{result.GetErrorInfo()}");
+ return false;
+ }
+ result = nn.fs.FileSystem.Commit(save_name);
+ if (!result.IsSuccess())
+ {
+ UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + result.GetErrorInfo());
+ return false;
+ }
+ return true;
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // End preventing the user from quitting the game while saving.
+ UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
+#endif
+#endif
+ }
+
+ ///
+ /// 递归删除情况
+ ///
+ ///
+ ///
+ public bool CleanRecursivelyPathDir(string filename)
+ {
+#if !UNITY_SWITCH
+ return false;
+#else
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // This next line prevents the user from quitting the game while saving.
+ // This is required for Nintendo Switch Guideline 0080
+ UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
+#endif
+
+ if (CheckPathNotFound(filename))
+ return false;
+ nn.Result result;
+ result = nn.fs.Directory.CleanRecursively(filename);
+ if (result.IsSuccess() == false)
+ {
+ UnityEngine.Debug.LogError($"nn.fs.File.DeleteRecursively 失败 {filename} : result=>{result.GetErrorInfo()}");
+ return false;
+ }
+ result = nn.fs.FileSystem.Commit(save_name);
+ if (!result.IsSuccess())
+ {
+ UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + result.GetErrorInfo());
+ return false;
+ }
+ return true;
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // End preventing the user from quitting the game while saving.
+ UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
+#endif
+#endif
+ }
+
+
+ public bool RenameDir(string oldpath,string newpath)
+ {
+#if !UNITY_SWITCH
+ return false;
+#else
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // This next line prevents the user from quitting the game while saving.
+ // This is required for Nintendo Switch Guideline 0080
+ UnityEngine.Switch.Notification.EnterExitRequestHandlingSection();
+#endif
+
+ if (CheckPathNotFound(oldpath))
+ return false;
+
+ nn.Result result;
+ result = nn.fs.Directory.Rename(oldpath, newpath);
+ if (result.IsSuccess() == false)
+ {
+ UnityEngine.Debug.LogError($"nn.fs.File.Rename 失败 {oldpath} to {newpath} : result=>{result.GetErrorInfo()}");
+ return false;
+ }
+ result = nn.fs.FileSystem.Commit(save_name);
+ if (!result.IsSuccess())
+ {
+ UnityEngine.Debug.LogError($"FileSystem.Commit({save_name}) 失败: " + result.GetErrorInfo());
+ return false;
+ }
+ return true;
+
+#if UNITY_SWITCH && !UNITY_EDITOR
+ // End preventing the user from quitting the game while saving.
+ UnityEngine.Switch.Notification.LeaveExitRequestHandlingSection();
+#endif
+#endif
+ }
+ bool EnsureParentDirectory(string filePath, bool bAutoCreateDir = true)
{
#if !UNITY_SWITCH
return false;
diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs
index 0d1bae7c..a634a432 100644
--- a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs
+++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSMount.cs
@@ -5,10 +5,12 @@ public class AxiNSMount
{
static bool bInMount = false;
internal static string m_SaveMountName;
- static bool bInMountForDebug = false;
- internal static string m_SaveMountForDebugName;
+ static bool bInSdCardMount = false;
+ internal static string m_SdCardMountName;
+ static bool bInSdCardDebugMount = false;
+ internal static string m_SdCardDebugMountName;
- public bool SaveIsMount => bInMount;
+ public bool SaveIsMount => bInMount;
public string SaveMountName
{
get
@@ -47,14 +49,14 @@ public class AxiNSMount
bInMount = true;
return true;
}
- #endif
+#endif
- public bool MountSDForDebug(string mountName = "sd")
+ public bool MountSDForDebug(string mountName = "dbgsd")
{
#if !UNITY_SWITCH
return false;
#else
- if (bInMountForDebug)
+ if (bInSdCardDebugMount)
return true;
nn.Result result;
result = nn.fs.SdCard.MountForDebug(mountName);
@@ -65,35 +67,31 @@ public class AxiNSMount
return false;
}
UnityEngine.Debug.Log($"nn_fs_MountSdCardForDebug->挂载{mountName}:/ 成功 ");
- m_SaveMountForDebugName = mountName;
- bInMountForDebug = true;
+ m_SdCardDebugMountName = mountName;
+ bInSdCardDebugMount = true;
return true;
#endif
}
-
public bool MountSD(string mountName = "sd")
{
#if !UNITY_SWITCH
return false;
#else
- if (bInMountForDebug)
+ if (bInSdCardMount)
return true;
nn.Result result;
- result = nn.fs.SdCard.Mount(mountName);
- //result.abortUnlessSuccess();
+ result = AxiNSSDCard.Mount(mountName);
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;
+ m_SdCardMountName = mountName;
+ bInSdCardMount = true;
return true;
#endif
}
-
-
public void UnmountSave()
{
#if UNITY_SWITCH
@@ -107,18 +105,30 @@ public class AxiNSMount
bInMount = false;
#endif
}
-
- public void UnmountSaveForDebug()
+ public void UnmountSDCardForDebug()
{
#if UNITY_SWITCH
- if (!bInMountForDebug)
+ if (!bInSdCardDebugMount)
{
- UnityEngine.Debug.LogError($"{m_SaveMountForDebugName}:/ 没有被挂载,无需卸载");
+ UnityEngine.Debug.LogError($"{m_SdCardDebugMountName}:/ 没有被挂载,无需卸载");
return;
}
- nn.fs.FileSystem.Unmount(m_SaveMountForDebugName);
- UnityEngine.Debug.LogError($"UnmountSaveForDebufa->已卸载{m_SaveMountForDebugName}:/ ");
- bInMountForDebug = false;
+ nn.fs.FileSystem.Unmount(m_SdCardDebugMountName);
+ UnityEngine.Debug.LogError($"UnmountSDCardForDebug->已卸载{m_SdCardDebugMountName}:/ ");
+ bInSdCardDebugMount = false;
#endif
- }
+ }
+ public void UnmountSDCard()
+ {
+#if UNITY_SWITCH
+ if (!bInSdCardMount)
+ {
+ UnityEngine.Debug.LogError($"{m_SdCardMountName}:/ 没有被挂载,无需卸载");
+ return;
+ }
+ nn.fs.FileSystem.Unmount(m_SdCardMountName);
+ UnityEngine.Debug.LogError($"UnmountSDCard->已卸载{m_SdCardMountName}:/ ");
+ bInSdCardMount = false;
+#endif
+ }
}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs
new file mode 100644
index 00000000..f1fe28e6
--- /dev/null
+++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs
@@ -0,0 +1,23 @@
+#if UNITY_SWITCH
+using nn.account;
+#endif
+public class AxiNSSDCard
+{
+#if UNITY_SWITCH
+
+#if DEVELOPMENT_BUILD || NN_FS_SD_CARD_FOR_DEBUG_ENABLE
+ [DllImport(Nn.DllName,
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "nn_fs_MountSdCard")]
+ public static extern nn.Result Mount(string name);
+#else
+
+ public static nn.Result Mount(string name)
+ {
+ return new nn.Result();
+ }
+#endif
+
+
+#endif
+}
\ No newline at end of file
diff --git a/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs.meta b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs.meta
new file mode 100644
index 00000000..1b9591a5
--- /dev/null
+++ b/AxibugEmuOnline.Client/Assets/Plugins/AxiNSApi/AxiNSSDCard.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 21fa04ba4da10d74aafd65dd138478b7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: