diff --git a/src/LibHac.Nand/LibHac.Nand.csproj b/src/LibHac.Nand/LibHac.Nand.csproj index 789a0132..80b4ddab 100644 --- a/src/LibHac.Nand/LibHac.Nand.csproj +++ b/src/LibHac.Nand/LibHac.Nand.csproj @@ -2,6 +2,7 @@ netcoreapp2.1;net46 + 7.3 diff --git a/src/hactoolnet/CliParser.cs b/src/hactoolnet/CliParser.cs index 52c090cb..03394f98 100644 --- a/src/hactoolnet/CliParser.cs +++ b/src/hactoolnet/CliParser.cs @@ -46,6 +46,7 @@ namespace hactoolnet new CliOption("normaldir", 1, (o, a) => o.NormalDir = a[0]), new CliOption("securedir", 1, (o, a) => o.SecureDir = a[0]), new CliOption("logodir", 1, (o, a) => o.LogoDir = a[0]), + new CliOption("repack", 1, (o, a) => o.RepackSource = a[0]), new CliOption("listapps", 0, (o, a) => o.ListApps = true), new CliOption("listtitles", 0, (o, a) => o.ListTitles = true), new CliOption("listncas", 0, (o, a) => o.ListNcas = true), @@ -235,6 +236,7 @@ namespace hactoolnet sb.AppendLine(" --sign Sign the save file. (Requires device_key in key file)"); sb.AppendLine(" --trim Trim garbage data in the save file. (Requires device_key in key file)"); sb.AppendLine(" --listfiles List files in save file."); + sb.AppendLine(" --repack Replaces the contents of the save data with the specified directory."); sb.AppendLine(" --replacefile Replaces a file in the save data"); sb.AppendLine("NDV0 (Delta) options:"); sb.AppendLine(" Input delta patch can be a delta NCA file or a delta fragment file."); diff --git a/src/hactoolnet/Options.cs b/src/hactoolnet/Options.cs index c99db2c4..353c5253 100644 --- a/src/hactoolnet/Options.cs +++ b/src/hactoolnet/Options.cs @@ -39,6 +39,7 @@ namespace hactoolnet public string LogoDir; public string ReplaceFileSource; public string ReplaceFileDest; + public string RepackSource; public bool ListApps; public bool ListTitles; public bool ListNcas; diff --git a/src/hactoolnet/ProcessSave.cs b/src/hactoolnet/ProcessSave.cs index d111def5..5d9488fa 100644 --- a/src/hactoolnet/ProcessSave.cs +++ b/src/hactoolnet/ProcessSave.cs @@ -16,6 +16,8 @@ namespace hactoolnet { using (var file = new LocalStorage(ctx.Options.InFile, FileAccess.ReadWrite)) { + bool signNeeded = ctx.Options.SignSave; + var save = new SaveDataFileSystem(ctx.Keyset, file, ctx.Options.IntegrityLevel, true); if (ctx.Options.Validate) @@ -30,52 +32,61 @@ namespace hactoolnet if (ctx.Options.DebugOutDir != null) { - // todo string dir = ctx.Options.DebugOutDir; ExportSaveDebug(ctx, dir, save); } - if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null) + try { - string destFilename = ctx.Options.ReplaceFileDest; - if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename; - - using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read)) + if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null) { - using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite)) + string destFilename = ctx.Options.ReplaceFileDest; + if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename; + + using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read)) { - if (inFile.GetSize() != outFile.GetSize()) + using (IFile outFile = save.OpenFile(destFilename, OpenMode.ReadWrite)) { - outFile.SetSize(inFile.GetSize()); + if (inFile.GetSize() != outFile.GetSize()) + { + outFile.SetSize(inFile.GetSize()); + } + + inFile.CopyTo(outFile, ctx.Logger); + + ctx.Logger.LogMessage($"Replaced file {destFilename}"); } - - inFile.CopyTo(outFile, ctx.Logger); - - ctx.Logger.LogMessage($"Replaced file {destFilename}"); } + + signNeeded = true; } - if (save.Commit(ctx.Keyset)) + if (ctx.Options.RepackSource != null) { - ctx.Logger.LogMessage("Successfully signed save file"); - } - else - { - ctx.Logger.LogMessage("ERROR: Unable to sign save file. Do you have all the required keys?"); - } + var source = new LocalFileSystem(ctx.Options.RepackSource); - return; + save.CleanDirectoryRecursively("/"); + save.Commit(ctx.Keyset); + source.CopyFileSystem(save); + + signNeeded = true; + } + } + finally + { + save.Commit(ctx.Keyset); } - if (ctx.Options.SignSave || ctx.Options.TrimSave) + if (ctx.Options.TrimSave) { - if (ctx.Options.TrimSave) - { - save.FsTrim(); - ctx.Logger.LogMessage("Trimmed save file"); - } + save.FsTrim(); + signNeeded = true; + ctx.Logger.LogMessage("Trimmed save file"); + } + if (signNeeded) + { if (save.Commit(ctx.Keyset)) { ctx.Logger.LogMessage("Successfully signed save file"); @@ -90,14 +101,14 @@ namespace hactoolnet if (ctx.Options.ListFiles) { - foreach (DirectoryEntry entry in save.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)) + foreach (DirectoryEntry entry in save.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } } ctx.Logger.LogMessage(save.Print()); - //ctx.Logger.LogMessage(PrintFatLayout(save)); + //ctx.Logger.LogMessage(PrintFatLayout(save.SaveDataFileSystemCore)); } }