Example #1
0
        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);
        }
Example #2
0
        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
        }
Example #3
0
        /// <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();
            }
        }
Example #4
0
        public static async Task <byte[]> GetSuperblockHash(SHA256 sha, IReadOnlyBinaryDataAccessor data, RomFsHeader header)
        {
            var buffer = await data.ReadArrayAsync(0, header.MasterHashSize);

            return(sha.ComputeHash(buffer));
        }