public static async Task <RomFs> Load(IBinaryDataAccessor data) { var header = new RomFsHeader(await data.ReadArrayAsync(0, 0x6B)); var romfs = new RomFs(data, header); await romfs.Initialize(); return(romfs); }
public RomFs(IBinaryDataAccessor data, RomFsHeader header) { Data = data ?? throw new ArgumentNullException(nameof(data)); Header = header ?? throw new ArgumentNullException(nameof(header)); LevelLocations = new IvfcLevelLocation[] { new IvfcLevelLocation { HashBlockSize = 1 << header.Level1BlockSize, HashOffset = 0x60 }, new IvfcLevelLocation { HashBlockSize = 1 << header.Level2BlockSize }, new IvfcLevelLocation { HashBlockSize = 1 << header.Level3BlockSize } }; BodyOffset = Util.Align64(LevelLocations[0].HashOffset + header.MasterHashSize, LevelLocations[2].HashBlockSize); BodySize = header.Level3HashDataSize; LevelLocations[2].DataOffset = BodyOffset; LevelLocations[2].DataSize = Util.Align64(BodySize, LevelLocations[2].HashBlockSize); LevelLocations[1].HashOffset = Util.Align64(BodyOffset + BodySize, LevelLocations[2].HashBlockSize); LevelLocations[2].HashOffset = LevelLocations[1].HashOffset + header.Level2LogicalOffset - header.Level1LogicalOffset; LevelLocations[1].DataOffset = LevelLocations[2].HashOffset; LevelLocations[1].DataSize = Util.Align64(header.Level2HashDataSize, LevelLocations[1].HashBlockSize); LevelLocations[0].DataOffset = LevelLocations[2].HashOffset; LevelLocations[0].DataSize = Util.Align64(header.Level1HashDataSize, LevelLocations[0].HashBlockSize); // To-do: verify hashes }
/// <summary> /// Builds a new ROM file system from the given directory /// </summary> /// <param name="directory">Directory from which to load the files</param> /// <param name="fileSystem">File system from which to load the files</param> /// <returns>A newly built ROM file system</returns> public static async Task <RomFs> Build(string directory, IFileSystem fileSystem, ProcessingProgressedToken?progressToken = null) { Stream?stream = null; string?tempFilename = null; try { // A memory stream is faster, but an internal limitation means it's unsuitable for files larger than 2GB // We'll fall back to a file stream if our raw data won't fit within 2GB minus 400 MB (for safety, since there's still metadata, hashes, and other partitions) if (fileSystem.GetDirectoryLength(directory) < (int.MaxValue - (400 * Math.Pow(1024, 2)))) { stream = new MemoryStream(); } else { // Do not use IFileSystem; this is for temporary storage since RAM isn't an option tempFilename = Path.GetTempFileName(); stream = File.Open(tempFilename, FileMode.OpenOrCreate, FileAccess.ReadWrite); } RomFsBuilder.RomFsBuilder.BuildRomFS(directory, fileSystem, stream, progressToken); BinaryFile data; if (stream is FileStream) { // We want the BinaryFile class to own the file so it will dispose of it properly // So let's dispose our copy and let it re-open it however it sees fit // And instead of a BinaryFile, use a FileDeletingBinaryFile to delete the file on dispose stream.Dispose(); stream = null; if (string.IsNullOrEmpty(tempFilename)) { // The developer (probably me) made a mistake throw new Exception("Temporary file not found"); } data = new FileDeletingBinaryFile(tempFilename); } else if (stream is MemoryStream memoryStream) { try { // Faster but maybe more memory-intensive var memoryStreamArray = memoryStream.ToArray(); data = new BinaryFile(memoryStreamArray); } catch (OutOfMemoryException) { // Slower but more reliable data = new BinaryFile(memoryStream); } } else { // The developer (probably me) made a mistake throw new Exception("Unexpected type of stream in RomFs.Build"); } var header = new RomFsHeader(await data.ReadArrayAsync(0, 0x6B)); var romFs = new RomFs(data, header); await romFs.Initialize(); return(romFs); } finally { stream?.Dispose(); } }
public static async Task <byte[]> GetSuperblockHash(SHA256 sha, IReadOnlyBinaryDataAccessor data, RomFsHeader header) { var buffer = await data.ReadArrayAsync(0, header.MasterHashSize); return(sha.ComputeHash(buffer)); }