初版归档
This commit is contained in:
commit
701a645ea4
8
Assets/AxiProjectTools/AxiNSPack.meta
Normal file
8
Assets/AxiProjectTools/AxiNSPack.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 611bc182f939ea147a72b08613e2d2ba
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/AxiProjectTools/AxiNSPack/Editors.meta
Normal file
8
Assets/AxiProjectTools/AxiNSPack/Editors.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbe37300d75dbd641be2e6dca83a913c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
327
Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs
Normal file
327
Assets/AxiProjectTools/AxiNSPack/Editors/AxibugNSPTools.cs
Normal file
@ -0,0 +1,327 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AxibugEmuOnline.Editors
|
||||
{
|
||||
public class AxibugNSPTools : Editor
|
||||
{
|
||||
static string WorkRoot = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack"));
|
||||
static string switch_keys = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack/switch_keys"));
|
||||
static string hacpack_root = Path.GetFullPath(Path.Combine(Application.dataPath, "AxiProjectTools/AxiNSPack/hacpack"));
|
||||
static Dictionary<string, string> tools = new Dictionary<string, string>();
|
||||
static string prodKeysPath;
|
||||
|
||||
[MenuItem("Axibug移植工具/Switch/AxibugNSPTools/RepackNSP(仅重新构建NPS)")]
|
||||
static void RepackNSP()
|
||||
{
|
||||
if (!CheckEnvironmentVariable())
|
||||
return;
|
||||
|
||||
string path = EditorUtility.OpenFilePanel(
|
||||
title: "选择 .nsp 文件",
|
||||
directory: Path.Combine(Application.dataPath, ".."), // 默认路径为项目 Assets 目录
|
||||
extension: "nsp" // 限制文件类型为 .nsp
|
||||
);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
|
||||
RepackNSP(path);
|
||||
}
|
||||
|
||||
[MenuItem("Axibug移植工具/Switch/AxibugNSPTools/Build With RepackNSP(打包NSP并重新构建NPS)")]
|
||||
static void BuildWithRepackNSP()
|
||||
{
|
||||
if (!CheckEnvironmentVariable())
|
||||
return;
|
||||
|
||||
if (!EditorUtility.DisplayDialog("确认包信息", $"继续打包?", "继续", "取消"))
|
||||
return;
|
||||
|
||||
var levels = new List<string>();
|
||||
foreach (var scene in EditorBuildSettings.scenes)
|
||||
{
|
||||
if (scene.enabled)
|
||||
levels.Add(scene.path);
|
||||
}
|
||||
|
||||
var buildOpt = EditorUserBuildSettings.development ? BuildOptions.Development : BuildOptions.None;
|
||||
if (EditorUserBuildSettings.buildWithDeepProfilingSupport)
|
||||
buildOpt |= BuildOptions.EnableDeepProfilingSupport;
|
||||
if (EditorUserBuildSettings.allowDebugging)
|
||||
buildOpt |= BuildOptions.AllowDebugging;
|
||||
|
||||
//勾选构建NSP包
|
||||
EditorUserBuildSettings.switchCreateRomFile = true;
|
||||
|
||||
#if UNITY_2018_1_OR_NEWER && !UNITY_6000_0_OR_NEWER
|
||||
string titleid = PlayerSettings.Switch.applicationID;
|
||||
#else
|
||||
string titleid = "null";
|
||||
#endif
|
||||
string targetName = $"{Application.productName}_{titleid}.nsp";
|
||||
|
||||
string _locationPathName = $"Output/NSPBuild/{targetName}";
|
||||
var options = new BuildPlayerOptions
|
||||
{
|
||||
scenes = levels.ToArray(),
|
||||
locationPathName = _locationPathName,
|
||||
target = BuildTarget.Switch,
|
||||
options = buildOpt
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
BuildReport report = BuildPipeline.BuildPlayer(options);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools] Unity Build NSP 错误:{ex.ToString()}");
|
||||
return;
|
||||
}
|
||||
|
||||
string NSPFullPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", _locationPathName));
|
||||
RepackNSP(NSPFullPath);
|
||||
}
|
||||
|
||||
static bool CheckEnvironmentVariable()
|
||||
{
|
||||
// 获取环境变量(需要添加环境变量检查)
|
||||
string sdkRoot = Environment.GetEnvironmentVariable("NINTENDO_SDK_ROOT");
|
||||
if (string.IsNullOrEmpty(sdkRoot))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]请先正确配置环境变量:NINTENDO_SDK_ROOT,(若已配置,则保证配置后彻底重启Unity Hub和Unity)");
|
||||
return false;
|
||||
}
|
||||
|
||||
#region 获取prod.keys文件路径
|
||||
prodKeysPath = Path.Combine(
|
||||
switch_keys,
|
||||
"prod.keys"
|
||||
);
|
||||
|
||||
if (!File.Exists(prodKeysPath))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]未找到 prod.keys! 请先准备文件,path:{prodKeysPath}");
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RepackNSP(string nspFile)
|
||||
{
|
||||
#region 初始化工具路径
|
||||
// 获取环境变量(需要添加环境变量检查)
|
||||
string sdkRoot = Environment.GetEnvironmentVariable("NINTENDO_SDK_ROOT");
|
||||
tools["authoringTool"] = Path.Combine(sdkRoot, "Tools/CommandLineTools/AuthoringTool/AuthoringTool.exe");
|
||||
tools["hacPack"] = Path.Combine(hacpack_root, "hacpack");
|
||||
#endregion
|
||||
|
||||
#region 处理NSP文件路径
|
||||
string nspFilePath = nspFile;
|
||||
string nspFileName = Path.GetFileName(nspFilePath);
|
||||
string nspParentDir = Path.GetDirectoryName(nspFilePath);
|
||||
#endregion
|
||||
|
||||
#region 提取Title ID
|
||||
string titleID = ExtractTitleID(nspFilePath);
|
||||
if (string.IsNullOrEmpty(titleID))
|
||||
{
|
||||
Debug.LogError($"[AxibugNSPTools]NSP文件名一部分,必须包含TitleID");
|
||||
return;
|
||||
}
|
||||
Debug.Log($"[AxibugNSPTools]Using Title ID: {titleID}");
|
||||
#endregion
|
||||
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"清理临时目录", 0);
|
||||
#region 清理临时目录
|
||||
CleanDirectory(Path.Combine(nspParentDir, "repacker_extract"));
|
||||
CleanDirectory(Path.Combine(Path.GetTempPath(), "NCA"));
|
||||
CleanDirectory(Path.Combine(WorkRoot, "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}\"");
|
||||
|
||||
string controlPath = null;
|
||||
string programPath = null;
|
||||
FindNACPAndNPDPaths(extractPath, ref controlPath, ref programPath);
|
||||
if (controlPath == null || programPath == null)
|
||||
{
|
||||
Debug.LogError("[AxibugNSPTools] Critical directory structure not found!");
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#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", $"重建NSP", 0.9f);
|
||||
string outputNSP = BuildFinalNSP(nspFilePath, nspParentDir, tmpPath, titleID);
|
||||
EditorUtility.DisplayProgressBar("AxibugNSPTools", $"重建NSP", 1f);
|
||||
Debug.Log($"[AxibugNSPTools]Repacking completed: {outputNSP}");
|
||||
EditorUtility.ClearProgressBar();
|
||||
#endregion
|
||||
}
|
||||
|
||||
#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))
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
while (Directory.Exists(path)) ; // 等待删除完成
|
||||
}
|
||||
}
|
||||
|
||||
static void FindNACPAndNPDPaths(string basePath, ref string controlPath, ref string programPath)
|
||||
{
|
||||
foreach (var dir in Directory.GetDirectories(basePath))
|
||||
{
|
||||
if (File.Exists(Path.Combine(dir, "fs0/control.nacp")))
|
||||
controlPath = dir;
|
||||
if (File.Exists(Path.Combine(dir, "fs0/main.npdm")))
|
||||
programPath = dir;
|
||||
}
|
||||
}
|
||||
|
||||
static string ExecuteCommand(string command)
|
||||
{
|
||||
var process = new System.Diagnostics.Process()
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/C {command}",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true, // 增加错误流重定向
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
StandardOutputEncoding = Encoding.UTF8, // 明确指定编码
|
||||
StandardErrorEncoding = Encoding.UTF8
|
||||
}
|
||||
};
|
||||
|
||||
var outputBuilder = new StringBuilder();
|
||||
var errorBuilder = new StringBuilder();
|
||||
|
||||
// 使用事件处理程序捕获实时输出
|
||||
process.OutputDataReceived += (sender, args) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(args.Data))
|
||||
{
|
||||
outputBuilder.AppendLine(args.Data);
|
||||
Debug.Log($"[AxibugNSPTools]{args.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
process.ErrorDataReceived += (sender, args) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(args.Data))
|
||||
{
|
||||
errorBuilder.AppendLine(args.Data);
|
||||
Debug.LogError($"[AxibugNSPTools]{args.Data}");
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
|
||||
// 开始异步读取输出
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
// 等待进程退出(此时流已关闭)
|
||||
process.WaitForExit();
|
||||
|
||||
// 将错误信息附加到主输出
|
||||
if (errorBuilder.Length > 0)
|
||||
{
|
||||
outputBuilder.AppendLine("\nError Output:");
|
||||
outputBuilder.Append(errorBuilder);
|
||||
}
|
||||
|
||||
return outputBuilder.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region NCA构建逻辑
|
||||
static string BuildProgramNCA(string tmpPath, string titleID, string programDir)
|
||||
{
|
||||
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}");
|
||||
return ParseNCAOutput(output, "Program");
|
||||
}
|
||||
|
||||
static string BuildControlNCA(string tmpPath, string titleID, string controlDir)
|
||||
{
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
|
||||
$"--type nca --ncatype control --romfsdir \"{controlDir}/fs0\"";
|
||||
|
||||
string output = ExecuteCommand($"{tools["hacPack"]} {args}");
|
||||
|
||||
return ParseNCAOutput(output, "Control");
|
||||
}
|
||||
|
||||
static void BuildMetaNCA(string tmpPath, string titleID, string programNCA, string controlNCA)
|
||||
{
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{tmpPath}\" --titleid {titleID} " +
|
||||
$"--type nca --ncatype meta --titletype application " +
|
||||
$"--programnca \"{programNCA}\" --controlnca \"{controlNCA}\"";
|
||||
|
||||
ExecuteCommand($"{tools["hacPack"]} {args}");
|
||||
}
|
||||
|
||||
static string BuildFinalNSP(string origPath, string parentDir, string tmpPath, string titleID)
|
||||
{
|
||||
string outputPath = origPath.Replace(".nsp", "_repacked.nsp");
|
||||
if (File.Exists(outputPath)) File.Delete(outputPath);
|
||||
|
||||
string args = $"-k \"{prodKeysPath}\" -o \"{parentDir}\" --titleid {titleID} " +
|
||||
$"--type nsp --ncadir \"{tmpPath}\"";
|
||||
|
||||
ExecuteCommand($"{tools["hacPack"]} {args}");
|
||||
File.Move(Path.Combine(parentDir, $"{titleID}.nsp"), outputPath);
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
static string ParseNCAOutput(string output, string type)
|
||||
{
|
||||
var line = output.Split('\n')
|
||||
.FirstOrDefault(l => l.Contains($"Created {type} NCA:"));
|
||||
return line?.Split(':').Last().Trim();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55aa3f0466c30bc4683cdbdc4dd75940
|
||||
8
Assets/AxiProjectTools/AxiNSPack/hacpack.meta
Normal file
8
Assets/AxiProjectTools/AxiNSPack/hacpack.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d90c85ddb14ad7e4e9a6242ba135da0b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/AxiProjectTools/AxiNSPack/hacpack/Tools.meta
Normal file
8
Assets/AxiProjectTools/AxiNSPack/hacpack/Tools.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b31e2ae7250c09548a777d4dcdfe2d1f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7be57cd4293e9dc4297ea9b83fe08b18
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1252f6d74d67ee48af0a0342aecc981
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Application>
|
||||
<Title>
|
||||
<Language>AmericanEnglish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>BritishEnglish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Japanese</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>French</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>German</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>LatinAmericanSpanish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Spanish</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Italian</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Dutch</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>CanadianFrench</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Portuguese</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Title>
|
||||
<Language>Russian</Language>
|
||||
<Name>Homebrew Menu</Name>
|
||||
<Publisher>Yellows8</Publisher>
|
||||
</Title>
|
||||
<Isbn/>
|
||||
<StartupUserAccount>Required</StartupUserAccount>
|
||||
<UserAccountSwitchLock>Disable</UserAccountSwitchLock>
|
||||
<ParentalControl>None</ParentalControl>
|
||||
<SupportedLanguage>AmericanEnglish</SupportedLanguage>
|
||||
<SupportedLanguage>BritishEnglish</SupportedLanguage>
|
||||
<SupportedLanguage>Japanese</SupportedLanguage>
|
||||
<SupportedLanguage>French</SupportedLanguage>
|
||||
<SupportedLanguage>German</SupportedLanguage>
|
||||
<SupportedLanguage>LatinAmericanSpanish</SupportedLanguage>
|
||||
<SupportedLanguage>Spanish</SupportedLanguage>
|
||||
<SupportedLanguage>Italian</SupportedLanguage>
|
||||
<SupportedLanguage>Dutch</SupportedLanguage>
|
||||
<SupportedLanguage>CanadianFrench</SupportedLanguage>
|
||||
<SupportedLanguage>Russian</SupportedLanguage>
|
||||
<Screenshot>Allow</Screenshot>
|
||||
<VideoCapture>Disable</VideoCapture>
|
||||
<PresenceGroupId>0x0104444444441001</PresenceGroupId>
|
||||
<DisplayVersion>2.0</DisplayVersion>
|
||||
<Rating>
|
||||
<Organization>CERO</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>ESRB</Organization>
|
||||
<Age>10</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>USK</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGI</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGIPortugal</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>PEGIBBFC</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>Russian</Organization>
|
||||
<Age>12</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>ACB</Organization>
|
||||
<Age>13</Age>
|
||||
</Rating>
|
||||
<Rating>
|
||||
<Organization>OFLC</Organization>
|
||||
<Age>13</Age>
|
||||
</Rating>
|
||||
<DataLossConfirmation>Required</DataLossConfirmation>
|
||||
<PlayLogPolicy>All</PlayLogPolicy>
|
||||
<SaveDataOwnerId>0x0104444444441001</SaveDataOwnerId>
|
||||
<UserAccountSaveDataSize>0x0000000003e00000</UserAccountSaveDataSize>
|
||||
<UserAccountSaveDataJournalSize>0x0000000000180000</UserAccountSaveDataJournalSize>
|
||||
<DeviceSaveDataSize>0x0000000000000000</DeviceSaveDataSize>
|
||||
<DeviceSaveDataJournalSize>0x0000000000000000</DeviceSaveDataJournalSize>
|
||||
<BcatDeliveryCacheStorageSize>0x0000000000000000</BcatDeliveryCacheStorageSize>
|
||||
<ApplicationErrorCodeCategory/>
|
||||
<AddOnContentBaseId>0x0104444444442001</AddOnContentBaseId>
|
||||
<LogoType>Nintendo</LogoType>
|
||||
<LocalCommunicationId>0x0104444444441001</LocalCommunicationId>
|
||||
<LogoHandling>Auto</LogoHandling>
|
||||
<SeedForPseudoDeviceId>0x0000000000000000</SeedForPseudoDeviceId>
|
||||
<BcatPassphrase/>
|
||||
<AddOnContentRegistrationType>AllOnLaunch</AddOnContentRegistrationType>
|
||||
<UserAccountSaveDataSizeMax>0x0000000000000000</UserAccountSaveDataSizeMax>
|
||||
<UserAccountSaveDataJournalSizeMax>0x0000000000000000</UserAccountSaveDataJournalSizeMax>
|
||||
<DeviceSaveDataSizeMax>0x0000000000000000</DeviceSaveDataSizeMax>
|
||||
<DeviceSaveDataJournalSizeMax>0x0000000000000000</DeviceSaveDataJournalSizeMax>
|
||||
<TemporaryStorageSize>0x0000000000000000</TemporaryStorageSize>
|
||||
<CacheStorageSize>0x0000000000000000</CacheStorageSize>
|
||||
<CacheStorageJournalSize>0x0000000000000000</CacheStorageJournalSize>
|
||||
<CacheStorageDataAndJournalSizeMax>0x0000000000000000</CacheStorageDataAndJournalSizeMax>
|
||||
<CacheStorageIndexMax>0x0000000000000000</CacheStorageIndexMax>
|
||||
<Hdcp>None</Hdcp>
|
||||
<CrashReport>Deny</CrashReport>
|
||||
<RuntimeAddOnContentInstall>Deny</RuntimeAddOnContentInstall>
|
||||
<PlayLogQueryableApplicationId>0x0000000000000000</PlayLogQueryableApplicationId>
|
||||
<PlayLogQueryCapability>None</PlayLogQueryCapability>
|
||||
<Repair>None</Repair>
|
||||
<Attribute>None</Attribute>
|
||||
<ProgramIndex>0</ProgramIndex>
|
||||
<RequiredNetworkServiceLicenseOnLaunch>None</RequiredNetworkServiceLicenseOnLaunch>
|
||||
</Application>
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42c1295c31de3a948825b9e8e9e8184f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Assets/AxiProjectTools/AxiNSPack/hacpack/hacpack.exe
Normal file
BIN
Assets/AxiProjectTools/AxiNSPack/hacpack/hacpack.exe
Normal file
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b1b3ff7954facb409d3ba6f9840f762
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/AxiProjectTools/AxiNSPack/switch_keys.meta
Normal file
8
Assets/AxiProjectTools/AxiNSPack/switch_keys.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 409c6e8e5ead0ac4991ea6c243e407dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08bd0c8a53daacb4ea23b14dde156354
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
140
README.md
Normal file
140
README.md
Normal file
@ -0,0 +1,140 @@
|
||||
*本文仅作技术研究,因为合规原因,本文不提供任何法律敏感和版权相关的诸如SDK的文件。
|
||||
|
||||
### 1. Nintendo SDK
|
||||
|
||||
> 这个我不会提供任何文件,或者建议您使用任天堂官方授权的开发套件(笑)
|
||||
|
||||
准备好Nintendo SDK之后,配置如下环境变量:
|
||||
NINTENDO_SDK_ROOT -> 你的路径\NintendoSDK
|
||||
NINTENDO_SDK_NEX_ROOT -> 你的路径\NintendoSDK-NEX
|
||||
NINTENDO_SDK_NPLN_ROOT -> 你的路径\NintendoSDK-NPLN
|
||||
NINTENDO_SDK_PIA_ROOT -> 你的路径\NintendoSDK-Pia6
|
||||
|
||||
### 安装dotnet SDK
|
||||
|
||||
> 因为Nintendo SDK依赖donetsdk的,在Nintendo Sdk中也直接包含dotnet sdk的安装文件(是doNet6).您也可以自行安装
|
||||
|
||||
这个建议一定要安装,缺少这个也是Unity打包异常的原因之一,否则你难以查找原因。
|
||||
|
||||
### Unity Switch Support
|
||||
|
||||
> 同样,这个我不会提供任何文件,或者建议您使用任天堂官方授权的开发套件
|
||||
|
||||
准备和SDK对应版本的Unity Switch Support
|
||||
|
||||
安装Unity-AAAA版本
|
||||
安装UnitySetup-Nintendo-Switch-Support-for-Editor-AAAA-SDK-BBBB-xxxxxxx.exe
|
||||
|
||||
> 占位符:
|
||||
> AAAA是Unity版本号
|
||||
> BBBB是Nintendo SDK版本号
|
||||
|
||||
|
||||
### 关于Unity License
|
||||
|
||||
Nintendo Switch 并不需要特殊License或Unity Pro License,有一个License 就行
|
||||
|
||||
### Unity 开发过程
|
||||
|
||||
这个就和其他常见Unity游戏开发方式,忽略……
|
||||
|
||||
有一些,平台差异的踩坑,类似正常的PC,IOS,Android的unity开发过程中一样,不同平台开发,有一些特定的平台兼容问题,或必须的差异做法。Switch也一样,有一些不同的兼容情况需要处理,后续踩坑,会详细讲述。但这个是游戏本身内容开发方面的,本文不在此赘述。
|
||||
|
||||
### 打包到Switch
|
||||
|
||||
> 因为Unity是针对官方授权开发而来,NSP是提交给Nintendo,由Nintendo审核签名后分发,才会是一个完整带NCA头文件信息的可安装NSP。
|
||||
|
||||
> 并不是Android那样,输出.apk,就万事大吉。
|
||||
|
||||
你大概率是没有Nintendo官方授权的开发套件的,按照官方套件,你打包出来的程序,只能在官方提供的开发机和测试机上运行,零售机是无法运行的。
|
||||
|
||||
要安装到零售机,切是已经折腾过的零售机(Hacked)
|
||||
而且先决条件是,你提供的NSP是没有NCA签名等基本信息的,解决这个之后,才有安装的可能。
|
||||
|
||||
不解决的话,通过MTP之类的安装方式,都会报错,无法安装:
|
||||
> Invalid NCA Magic
|
||||
|
||||
# 正片开始
|
||||
|
||||
在此之前,请你先确保你Unity开发环境,能够选择Switch平台,成功Build出文件。
|
||||
勾选 Create NSP File 是输入.nsp文件
|
||||
不勾选 则是输出的文件夹形式的你的程序文件
|
||||
|
||||
若Unity并不能选择Switch平台,请检查你的Switch Support是否准备好并安装,
|
||||
若打包的时候并不能找到SDK,请检查你的Nintendo SDK是否准备好,并配置好环境变量。
|
||||
若IL2CPP问题,请按照Nintendo SDK解决。
|
||||
还有先解决基本问题,比如你的代码,在Build上不要报错,或者Switch的一些宏之类的。
|
||||
|
||||
能输出文件,好,现在才可以往下看。
|
||||
|
||||
# 打包使其让零售机(已折腾的)可以安装和运行
|
||||
|
||||
### 前置1.提取固件Key
|
||||
|
||||
> 这是敏感操作,本文只是表示我自己的操作,对于操作的影响,本文不负任何责任。
|
||||
|
||||
你需要提取你SysNADA的key,这个固件密钥,是构建NCA等信息的关键。
|
||||
请不要在网络上寻找key,即便找到也是不可用的。
|
||||
请自行提取。
|
||||
|
||||
下载 lockpick_rcm.bin,复制到TF卡\bootloader\payloads
|
||||
(如果你的折腾机已经整合,可以不下载)
|
||||
|
||||
hekate界面,选择PayLoad(有些中文汉化了,叫有效载荷)
|
||||
PayLoad中选择lockpick_rcm
|
||||
进入lockpick_rcm之后,音量加减按钮是菜单上下选择,电源键是确定。
|
||||
我们选择 Dump from SysNADA,很快会将你的key保存到SDK上。
|
||||
退出Lockpick
|
||||
|
||||
在SDK中会有这三个文件,将他拷贝到你的电脑上。
|
||||
sd:/switch/prod.keys
|
||||
sd:/switch/title.keys
|
||||
sd:/switch/dev.keys
|
||||
> 你可以使用它,因为你合法拥有你的switch设备,但请仅自己提取使用,不要再网上分享或传播你的文件。避免一些法律风险,和避免您的设备敏感信息被泄露。
|
||||
|
||||
### 前置2.确定一个titleid
|
||||
|
||||
自己定一个titleid,确保和现有所有已发行的游戏或者自制游戏的TitleID不一样,
|
||||
可以去GBAtemp之类的论坛,查重。
|
||||
|
||||
并在Unity PlayerSetting中设置TitleID
|
||||
|
||||
# 使用 AxibugNSPTools 重新打包NSP
|
||||
|
||||
- 这工具由我提供,**工具内不提供也不包含任何Nintendo SDK版权相关的任何内容。**只是当你合法拥有Nintendo SDK时,本工具全自动化帮你调用。
|
||||
- **即本工具调用和操作的任何文件,都是您提供的合法文件,本工具不包含。给合法授权的开发者使用的。请遵守相关规定。**
|
||||
|
||||
本工具自动化调用您提供的合法Nintendo SDK获取包的详细信息,并基于您提供的合法prod.keys,生成NAC,帮助您调用hacpack,最终repack一个新的NSP,使其可以在已经被折腾的Switch上,成功安装NSP文件。
|
||||
|
||||
- 拷贝keys
|
||||
|
||||
AxibugNSPTools 是编写给Unity的工具插件,AxibugNSPTools,到您的项目目录.
|
||||
|
||||
将您合法提取的prod.keys、title.keys、dev.keys拷贝到Assets/AxiProjectTools/AxiNSPack/switch_keys目录中。
|
||||
(注:您的项目若有提交git等公开仓库时,请忽略Assets/AxiProjectTools/AxiNSPack/switch_keys/*.keys , 以免泄露由您设备提取的key)
|
||||
|
||||
*若没有prod.keys,程序repack时会提示您。
|
||||
|
||||
· 确保先前配置的Nintendo_SDK_ROOT环境变量
|
||||
|
||||
*若没有Nintendo_SDK_ROOT环境变量,程序repack时会提示您。
|
||||
|
||||
#### **功能1.** :RepackNSP(仅重新构建NPS)
|
||||
|
||||
这个是把现有的Unity 输出的NSP(且无其他任何修改),重新构建,使其repack一个新的NSP,折腾后的零售Switch可以直接安装。
|
||||
|
||||
Unity Editor菜单上选择:Axibug移植工具/Switch/AxibugNSPTools/RepackNSP(仅重新构建NPS)
|
||||
|
||||
等待执行完毕
|
||||
|
||||
通过MTP等方式安装到Switch测试
|
||||
|
||||
#### **功能2.** :Build With RepackNSP(打包NSP并重新构建NPS)
|
||||
|
||||
这个是自动调用Unity Build一个NSP之后,再自动调用RepackNSP,重新构建,使其repack一个新的NSP,折腾后的零售Switch可以直接安装。
|
||||
|
||||
Unity Editor菜单上选择:Axibug移植工具/Switch/AxibugNSPTools/Build With RepackNSP(打包NSP并重新构建NPS)
|
||||
|
||||
等待执行完毕
|
||||
|
||||
通过MTP等方式安装到Switch测试
|
||||
Loading…
Reference in New Issue
Block a user