public static void Process(Context ctx) { var accessNeeded = FileAccess.Read; if (ctx.Options.SignSave || ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null || ctx.Options.RepackSource != null || ctx.Options.TrimSave) { accessNeeded = FileAccess.ReadWrite; } using (var file = new LocalStorage(ctx.Options.InFile, accessNeeded)) { bool signNeeded = ctx.Options.SignSave; var save = new SaveDataFileSystem(ctx.KeySet, file, ctx.Options.IntegrityLevel, true); FileSystemClient fs = ctx.FsClient; fs.Register("save".ToU8Span(), save); if (ctx.Options.Validate) { save.Verify(ctx.Logger); } if (ctx.Options.OutDir != null) { fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.OutDir)); FsUtils.CopyDirectoryWithProgress(fs, "save:/".ToU8Span(), "output:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure(); fs.Unmount("output".ToU8Span()); } if (ctx.Options.DebugOutDir != null) { string dir = ctx.Options.DebugOutDir; ExportSaveDebug(ctx, dir, save); } try { if (ctx.Options.ReplaceFileDest != null && ctx.Options.ReplaceFileSource != null) { string destFilename = ctx.Options.ReplaceFileDest; if (!destFilename.StartsWith("/")) { destFilename = '/' + destFilename; } using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read)) { save.OpenFile(out IFile outFile, destFilename.ToU8String(), OpenMode.ReadWrite).ThrowIfFailure(); using (outFile) { inFile.GetSize(out long inFileSize).ThrowIfFailure(); outFile.GetSize(out long outFileSize).ThrowIfFailure(); if (inFileSize != outFileSize) { outFile.SetSize(inFileSize).ThrowIfFailure(); } inFile.CopyTo(outFile, ctx.Logger); ctx.Logger.LogMessage($"Replaced file {destFilename}"); } } signNeeded = true; } if (ctx.Options.RepackSource != null) { fs.Register("input".ToU8Span(), new LocalFileSystem(ctx.Options.RepackSource)); fs.CleanDirectoryRecursively("save:/".ToU8Span()); fs.Commit("save".ToU8Span()); FsUtils.CopyDirectoryWithProgress(fs, "input:/".ToU8Span(), "save:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure(); fs.Commit("save".ToU8Span()); fs.Unmount("input".ToU8Span()); signNeeded = true; } } finally { if (signNeeded) { if (save.Commit(ctx.KeySet).IsSuccess()) { ctx.Logger.LogMessage( $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}"); } else { ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?"); } signNeeded = false; } } if (ctx.Options.TrimSave) { save.FsTrim(); signNeeded = true; ctx.Logger.LogMessage("Trimmed save file"); } if (signNeeded) { if (save.Commit(ctx.KeySet).IsSuccess()) { ctx.Logger.LogMessage( $"Successfully signed save file with key {ctx.KeySet.DeviceUniqueSaveMacKeys[0].ToString()}"); } else { ctx.Logger.LogMessage("Unable to sign save file. Do you have all the required keys?"); } fs.Unmount("save".ToU8Span()); return; } if (ctx.Options.ListFiles) { foreach (DirectoryEntryEx entry in save.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } } ctx.Logger.LogMessage(save.Print(ctx.KeySet)); //ctx.Logger.LogMessage(PrintFatLayout(save.SaveDataFileSystemCore)); fs.Unmount("save".ToU8Span()); } }
public static void Process(Context ctx) { using (IStorage file = new LocalStorage(ctx.Options.InFile, FileAccess.Read)) { var nca = new Nca(ctx.Keyset, file); Nca baseNca = null; var ncaHolder = new NcaHolder { Nca = nca }; if (ctx.Options.HeaderOut != null) { using (var outHeader = new FileStream(ctx.Options.HeaderOut, FileMode.Create, FileAccess.ReadWrite)) { nca.OpenDecryptedHeaderStorage().Slice(0, 0xc00).CopyToStream(outHeader); } } if (ctx.Options.BaseNca != null) { IStorage baseFile = new LocalStorage(ctx.Options.BaseNca, FileAccess.Read); baseNca = new Nca(ctx.Keyset, baseFile); } for (int i = 0; i < 3; i++) { if (ctx.Options.SectionOut[i] != null) { OpenStorage(i).WriteAllBytes(ctx.Options.SectionOut[i], ctx.Logger); } if (ctx.Options.SectionOutDir[i] != null) { FileSystemClient fs = ctx.FsClient; string mountName = $"section{i}"; fs.Register(mountName.ToU8Span(), OpenFileSystem(i)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.SectionOutDir[i])); FsUtils.CopyDirectoryWithProgress(fs, (mountName + ":/").ToU8Span(), "output:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure(); fs.Unmount(mountName.ToU8Span()); fs.Unmount("output".ToU8Span()); } if (ctx.Options.Validate && nca.SectionExists(i)) { if (nca.Header.GetFsHeader(i).IsPatchSection() && baseNca != null) { ncaHolder.Validities[i] = baseNca.VerifySection(nca, i, ctx.Logger); } else { ncaHolder.Validities[i] = nca.VerifySection(i, ctx.Logger); } } } if (ctx.Options.ListRomFs && nca.CanOpenSection(NcaSectionType.Data)) { IFileSystem romfs = OpenFileSystemByType(NcaSectionType.Data); foreach (DirectoryEntryEx entry in romfs.EnumerateEntries()) { ctx.Logger.LogMessage(entry.FullPath); } } if (ctx.Options.RomfsOutDir != null || ctx.Options.RomfsOut != null || ctx.Options.ReadBench) { if (!nca.SectionExists(NcaSectionType.Data)) { ctx.Logger.LogMessage("NCA has no RomFS section"); return; } if (ctx.Options.RomfsOut != null) { OpenStorageByType(NcaSectionType.Data).WriteAllBytes(ctx.Options.RomfsOut, ctx.Logger); } if (ctx.Options.RomfsOutDir != null) { FileSystemClient fs = ctx.FsClient; fs.Register("rom".ToU8Span(), OpenFileSystemByType(NcaSectionType.Data)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.RomfsOutDir)); FsUtils.CopyDirectoryWithProgress(fs, "rom:/".ToU8Span(), "output:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure(); fs.Unmount("rom".ToU8Span()); fs.Unmount("output".ToU8Span()); } if (ctx.Options.ReadBench) { long bytesToRead = 1024L * 1024 * 1024 * 5; IStorage storage = OpenStorageByType(NcaSectionType.Data); storage.GetSize(out long sectionSize).ThrowIfFailure(); var dest = new NullStorage(sectionSize); int iterations = (int)(bytesToRead / sectionSize) + 1; ctx.Logger.LogMessage(iterations.ToString()); ctx.Logger.StartNewStopWatch(); for (int i = 0; i < iterations; i++) { storage.CopyTo(dest, ctx.Logger); ctx.Logger.LogMessage(ctx.Logger.GetRateString()); } ctx.Logger.PauseStopWatch(); ctx.Logger.LogMessage(ctx.Logger.GetRateString()); } } if (ctx.Options.ExefsOutDir != null || ctx.Options.ExefsOut != null) { if (nca.Header.ContentType != NcaContentType.Program) { ctx.Logger.LogMessage("NCA's content type is not \"Program\""); return; } if (!nca.SectionExists(NcaSectionType.Code)) { ctx.Logger.LogMessage("Could not find an ExeFS section"); return; } if (ctx.Options.ExefsOut != null) { OpenStorageByType(NcaSectionType.Code).WriteAllBytes(ctx.Options.ExefsOut, ctx.Logger); } if (ctx.Options.ExefsOutDir != null) { FileSystemClient fs = ctx.FsClient; fs.Register("code".ToU8Span(), OpenFileSystemByType(NcaSectionType.Code)); fs.Register("output".ToU8Span(), new LocalFileSystem(ctx.Options.ExefsOutDir)); FsUtils.CopyDirectoryWithProgress(fs, "code:/".ToU8Span(), "output:/".ToU8Span(), logger: ctx.Logger).ThrowIfFailure(); fs.Unmount("code".ToU8Span()); fs.Unmount("output".ToU8Span()); } } if (ctx.Options.PlaintextOut != null) { nca.OpenDecryptedNca().WriteAllBytes(ctx.Options.PlaintextOut, ctx.Logger); } if (!ctx.Options.ReadBench) { ctx.Logger.LogMessage(ncaHolder.Print()); } IStorage OpenStorage(int index) { if (ctx.Options.Raw) { if (baseNca != null) { return(baseNca.OpenRawStorageWithPatch(nca, index)); } return(nca.OpenRawStorage(index)); } if (baseNca != null) { return(baseNca.OpenStorageWithPatch(nca, index, ctx.Options.IntegrityLevel)); } return(nca.OpenStorage(index, ctx.Options.IntegrityLevel)); } IFileSystem OpenFileSystem(int index) { if (baseNca != null) { return(baseNca.OpenFileSystemWithPatch(nca, index, ctx.Options.IntegrityLevel)); } return(nca.OpenFileSystem(index, ctx.Options.IntegrityLevel)); } IStorage OpenStorageByType(NcaSectionType type) { return(OpenStorage(Nca.GetSectionIndexFromType(type, nca.Header.ContentType))); } IFileSystem OpenFileSystemByType(NcaSectionType type) { return(OpenFileSystem(Nca.GetSectionIndexFromType(type, nca.Header.ContentType))); } } }