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));
}
}