public async Task CanRebuildRomfs(string filename) { var fs = new PhysicalFileSystem(); using (var originalRom = new ThreeDsRom()) { await originalRom.OpenFile(filename); using (var newRom = new ThreeDsRom(await RomFs.Build("/RomFS", originalRom))) { await AssertDirectoriesEqual("/RomFS", originalRom, "/RomFS", newRom); } } }
public async void ExeFsHashesValid(string filename) { using var rom = new ThreeDsRom(); await rom.OpenFile(filename); rom.Partitions.Should().NotBeNull(); foreach (var partition in rom.Partitions) { if (partition.ExeFs != null) { partition.ExeFs.AreAllHashesValid().Should().Be(true); } } }
private async Task AssertDirectoriesEqual(string directory1, ThreeDsRom fileSystem1, string directory2, ThreeDsRom filesystem2) { // Assume directory1 is good. It's sourced by a regular, non-rebuilt ROM, which should be covered by its own tests. await(fileSystem1 as IFileSystem).GetFiles(directory1, "*", false).RunAsyncForEach(async file => { var otherFile = Path.Combine(directory2, file.Replace(directory1, "").TrimStart('/')); var data1 = await fileSystem1.GetFileReference(file).ReadArrayAsync(); var data2 = await filesystem2.GetFileReference(otherFile).ReadArrayAsync(); data1.Length.Should().Be(data2.Length, $"because file '{file}' should have the same size as file '{otherFile}' in both directories"); UnsafeCompare(data1, data2).Should().BeTrue(); }); }
public async Task CanRebuildRomfs(string filename) { using var originalRom = new ThreeDsRom(); await originalRom.OpenFile(filename); for (int i = 0; i < originalRom.Partitions.Length; i++) { var partition = originalRom.Partitions[i]?.RomFs; if (partition != null) { var romFsDirName = "/" + originalRom.GetRomFsDirectoryName(i); using var newRom = new ThreeDsRom(await RomFs.Build(romFsDirName, originalRom), i); await AssertDirectoriesEqual(romFsDirName, originalRom, romFsDirName, newRom); } } }
public async Task CanRebuildExefs(string filename) { using var originalRom = new ThreeDsRom(); await originalRom.OpenFile(filename); for (int i = 0; i < originalRom.Partitions.Length; i++) { var exefs = originalRom.Partitions[i]?.ExeFs; if (exefs != null) { var exeFsDirName = "/" + ThreeDsRom.GetExeFsDirectoryName(i); using var data = new BinaryFile(exefs.ToByteArray()); using var newRom = new ThreeDsRom(await ExeFs.Load(data), i); await AssertDirectoriesEqual(exeFsDirName, originalRom, exeFsDirName, newRom); } } }
public async Task CanRebuildExheader(string filename) { using var originalRom = new ThreeDsRom(); await originalRom.OpenFile(filename); for (int i = 0; i < originalRom.Partitions.Length; i++) { if (originalRom.Partitions[i]?.ExHeader != null) { var exheader = originalRom.Partitions[i].ExHeader; var exheaderData = exheader.ToByteArray(); using var exheaderBinary = new BinaryFile(exheaderData); var newExheader = await NcchExtendedHeader.Load(exheaderBinary); newExheader.Should().NotBeNull(); newExheader.Should().BeEquivalentTo(exheader, $"because the exheader in partition {i} should have been rebuilt correctly"); } } }
public async Task VerifyFileSystemInterface(string filename) { using var rom = new ThreeDsRom(); await rom.OpenFile(filename); var romAsProvider = rom as IFileSystem; var files = romAsProvider.GetFiles("/", "*", false); foreach (var file in files) { romAsProvider.FileExists(file).Should().BeTrue("File '" + file + "' should exist"); } var directories = romAsProvider.GetDirectories("/", false); foreach (var dir in directories) { romAsProvider.DirectoryExists(dir).Should().BeTrue("File '" + dir + "' should exist"); } }
public async void ReadsNcchPartitions(string filename) { using var rom = new ThreeDsRom(); await rom.OpenFile(filename); rom.Partitions.Should().NotBeNull(); foreach (var partition in rom.Partitions) { if (partition.Header != null) { partition.Header.Magic.Should().Be("NCCH"); if (partition.RomFs != null) { partition.RomFs.Header.Should().NotBeNull(); partition.RomFs.Header.Magic.Should().Be("IVFC"); partition.RomFs.Header.MagicNumber.Should().Be(0x10000); } } } }
public async Task ExtractRomFsFiles(string filename) { var progressReportToken = new ProgressReportToken(); using var rom = new ThreeDsRom(); await rom.OpenFile(filename); var extractionTask = rom.ExtractFiles("./extracted-" + Path.GetFileName(filename), progressReportToken); //// Awaiting the task and handling the progressReportToken makes everything wait on Debug.WriteLine, slowing things down a lot //// So we asynchronously poll //while (!extractionTask.IsCompleted && !extractionTask.IsFaulted) //{ // Debug.WriteLine("Extraction progress: " + Math.Round(progressReportToken.Progress * 100, 2).ToString()); // await Task.Delay(200); //} await extractionTask; Debug.WriteLine("Extraction complete!"); }
public async Task CanRebuildNcchPartitions(string filename) { using var originalRom = new ThreeDsRom(); await originalRom.OpenFile(filename); var originalRomFs = originalRom as IFileSystem; for (int i = 0; i < originalRom.Partitions.Length; i++) { if (originalRom.Partitions[i]?.Header != null) { var romFsDirName = "/" + originalRom.GetRomFsDirectoryName(i); var exeFsDirName = "/" + ThreeDsRom.GetExeFsDirectoryName(i); var headerFilename = "/" + ThreeDsRom.GetHeaderFileName(i); var exHeaderFilename = "/" + ThreeDsRom.GetExHeaderFileName(i); var plainRegionFilename = "/" + ThreeDsRom.GetPlainRegionFileName(i); var logoFilename = "/" + ThreeDsRom.GetLogoFileName(i); if (!originalRomFs.FileExists(exHeaderFilename)) { exHeaderFilename = null; } if (!originalRomFs.DirectoryExists(romFsDirName)) { romFsDirName = null; } if (!originalRomFs.DirectoryExists(exeFsDirName)) { exeFsDirName = null; } if (!originalRomFs.DirectoryExists(plainRegionFilename)) { plainRegionFilename = null; } if (!originalRomFs.DirectoryExists(logoFilename)) { logoFilename = null; } var tempFilename = "ncch-rebuild-" + Path.GetFileName(filename) + ".cxi"; try { using var newPartition = await NcchPartition.Build(headerFilename, exHeaderFilename, exeFsDirName, romFsDirName, plainRegionFilename, logoFilename, originalRom); using var savedPartitionStream = new FileStream(tempFilename, FileMode.OpenOrCreate); using var savedPartitionFile = new BinaryFile(savedPartitionStream); long fileSize = (long)Math.Pow(2, Math.Ceiling(Math.Log(newPartition.Header.RomFsSize * 0x200) / Math.Log(2))); savedPartitionFile.SetLength(fileSize); await newPartition.WriteBinary(savedPartitionFile); using var rebuiltPartition = await NcchPartition.Load(savedPartitionFile); using var newRom = new ThreeDsRom(rebuiltPartition, i); if (romFsDirName != null) { await AssertDirectoriesEqual(romFsDirName, originalRom, romFsDirName, newRom); } if (exeFsDirName != null) { await AssertDirectoriesEqual(exeFsDirName, originalRom, exeFsDirName, newRom); } if (exHeaderFilename != null) { var originalFile = (originalRom as IFileSystem).ReadAllBytes(exHeaderFilename); var newFile = (newRom as IFileSystem).ReadAllBytes(exHeaderFilename); UnsafeCompare(originalFile, newFile).Should().BeTrue(); } if (plainRegionFilename != null) { var originalFile = (originalRom as IFileSystem).ReadAllBytes(plainRegionFilename); var newFile = (newRom as IFileSystem).ReadAllBytes(plainRegionFilename); UnsafeCompare(originalFile, newFile).Should().BeTrue(); } if (logoFilename != null) { var originalFile = (originalRom as IFileSystem).ReadAllBytes(logoFilename); var newFile = (newRom as IFileSystem).ReadAllBytes(logoFilename); UnsafeCompare(originalFile, newFile).Should().BeTrue(); } } finally { if (File.Exists(tempFilename)) { // Comment this line out to keep it around for debugging purposes //File.Delete(tempFilename); } } } } }
/// <summary> /// Loads a ROM from the given path if supported /// </summary> /// <param name="path"></param> /// <param name="fileSystem"></param> /// <returns>The ROM that was loaded, or null if the given path is not supported</returns> public static async Task <IModTarget?> LoadRom(string path, IFileSystem fileSystem) { if (fileSystem.FileExists(path)) { bool is3dsRom; BinaryFile?file = null; bool disposeFile = true; try { file = new BinaryFile(path, fileSystem); is3dsRom = await ThreeDsRom.IsThreeDsRom(file).ConfigureAwait(false); if (is3dsRom) { var rom = await ThreeDsRom.Load(file).ConfigureAwait(false); // To-do: check title ID to ensure it's a PSMD ROM // For now, just assume it's supported by our class disposeFile = false; return(new PsmdRom(rom)); } else { return(null); } } finally { if (disposeFile && file != null) { file.Dispose(); } } } else if (fileSystem.DirectoryExists(path)) { // Is it a Switch ROM or a 3DS ROM? // To-do: check title ID to ensure it's the correct type of ROM if (fileSystem.FileExists(Path.Combine(path, "ExeFS", "code.bin"))) { // It's a 3DS ROM return(new PsmdRom(path, fileSystem)); } else if (fileSystem.FileExists(Path.Combine(path, "exefs", "main"))) { // It's a Switch ROM return(new RtdxRom(path, fileSystem)); } else { // We couldn't find the executable and can make no conclusion return(null); } } else { return(null); } }