コード例 #1
0
ファイル: RomFs.cs プロジェクト: Smash-Nerd/DotNet3dsToolkit
        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);
        }
コード例 #2
0
 public NcchPartition(RomFs romfs = null, ExeFs exefs = null, NcchHeader header = null, NcchExtendedHeader exheader = null, string plainRegion = null, byte[] logo = null)
 {
     RomFs       = romfs;
     ExeFs       = exefs;
     Header      = header;
     ExHeader    = exheader;
     PlainRegion = plainRegion;
     Logo        = logo;
 }
コード例 #3
0
 public async Task Initialize(IReadOnlyBinaryDataAccessor data)
 {
     if (Header != null && Header.RomFsSize > 0)
     {
         if (Header.ExeFsOffset > 0 && Header.ExeFsSize > 0)
         {
             ExeFs = await ExeFs.Load(data.GetReadOnlyDataReference((long)Header.ExeFsOffset *MediaUnitSize, (long)Header.ExeFsSize *MediaUnitSize));
         }
         if (Header.RomFsOffset > 0 && Header.RomFsOffset > 0)
         {
             RomFs = await RomFs.Load(data.GetReadOnlyDataReference((long)Header.RomFsOffset *MediaUnitSize, (long)Header.RomFsSize *MediaUnitSize));
         }
         if (Header.ExHeaderSize > 0)
         {
             ExHeader = data.GetReadOnlyDataReference(0x200, Header.ExHeaderSize);
         }
     }
 }
コード例 #4
0
        public async Task Initialize(IReadOnlyBinaryDataAccessor data)
        {
            this.RawData = data;
            if (Header != null && Header.RomFsSize > 0)
            {
                if (Header.ExeFsOffset > 0 && Header.ExeFsSize > 0)
                {
                    ExeFs = await ExeFs.Load(data.Slice((long)Header.ExeFsOffset *MediaUnitSize, (long)Header.ExeFsSize *MediaUnitSize));
                }
                if (Header.RomFsOffset > 0 && Header.RomFsOffset > 0)
                {
                    RomFs = await RomFs.Load(data.Slice((long)Header.RomFsOffset *MediaUnitSize, (long)Header.RomFsSize *MediaUnitSize));
                }
                if (Header.ExHeaderSize > 0)
                {
                    ExHeader = await NcchExtendedHeader.Load(data.Slice(0x200, Header.ExHeaderSize));
                }

                PlainRegion = await data.ReadStringAsync(Header.PlainRegionOffset *MediaUnitSize, Header.PlainRegionSize *MediaUnitSize, Encoding.ASCII);

                Logo = await data.ReadArrayAsync(Header.LogoRegionOffset *MediaUnitSize, Header.LogoRegionSize *MediaUnitSize);
            }
        }
コード例 #5
0
        /// <summary>
        /// Builds a new NCCH partition from the given directory
        /// </summary>
        /// <param name="fileSystem">File system from which to load the files</param>
        /// <returns>A newly built NCCH partition</returns>
        public static async Task <NcchPartition> Build(string headerFilename, string exHeaderFilename, string?exeFsDirectory, string?romFsDiretory, string?plainRegionFilename, string?logoFilename, IFileSystem fileSystem, ProcessingProgressedToken?progressToken = null)
        {
            ProcessingProgressedToken?exefsToken = null;
            ProcessingProgressedToken?romfsToken = null;

            void ReportProgress()
            {
                if (progressToken != null)
                {
                    progressToken.TotalFileCount     = (exefsToken?.TotalFileCount + romfsToken?.TotalFileCount).GetValueOrDefault();
                    progressToken.ProcessedFileCount = (exefsToken?.ProcessedFileCount + romfsToken?.ProcessedFileCount).GetValueOrDefault();
                }
            };

            Task <ExeFs?> exeFsTask;

            if (!string.IsNullOrEmpty(exeFsDirectory))
            {
                if (progressToken != null)
                {
                    exefsToken = new ProcessingProgressedToken();
                    exefsToken.FileCountChanged += (sender, e) => ReportProgress();
                }
                exeFsTask = Task.Run <ExeFs?>(async() => await ExeFs.Build(exeFsDirectory, fileSystem, exefsToken).ConfigureAwait(false));
            }
            else
            {
                exeFsTask = Task.FromResult <ExeFs?>(null);
            }

            Task <RomFs?> romFsTask;

            if (!string.IsNullOrEmpty(romFsDiretory))
            {
                if (progressToken != null)
                {
                    romfsToken = new ProcessingProgressedToken();
                    romfsToken.FileCountChanged += (sender, e) => ReportProgress();
                }
                romFsTask = Task.Run <RomFs?>(async() => await RomFs.Build(romFsDiretory, fileSystem, romfsToken).ConfigureAwait(false));
            }
            else
            {
                romFsTask = Task.FromResult <RomFs?>(null);
            }

            var header = new NcchHeader(fileSystem.ReadAllBytes(headerFilename));

            NcchExtendedHeader?exHeader = null;

            if (!string.IsNullOrEmpty(exHeaderFilename))
            {
                using var exHeaderData = new BinaryFile(fileSystem.ReadAllBytes(exHeaderFilename));
                exHeader = await NcchExtendedHeader.Load(exHeaderData);
            }

            string?plainRegion = null;

            if (!string.IsNullOrEmpty(plainRegionFilename))
            {
                plainRegion = fileSystem.ReadAllText(plainRegionFilename);
            }

            byte[]? logo = null;
            if (!string.IsNullOrEmpty(logoFilename))
            {
                logo = fileSystem.ReadAllBytes(logoFilename);
            }

            return(new NcchPartition(await romFsTask, await exeFsTask, header, exHeader, plainRegion, logo));
        }
コード例 #6
0
 public void Dispose()
 {
     RomFs?.Dispose();
 }
コード例 #7
0
        /// <summary>
        /// Writes the current state of the NCCH partition to the given binary data accessor
        /// </summary>
        /// <param name="data">Data accessor to receive the binary data</param>
        /// <returns>A long representing the total length of data written</returns>
        public async Task <long> WriteBinary(IWriteOnlyBinaryDataAccessor data)
        {
            // Get the data
            var exheader = ExHeader?.ToByteArray();

            var plainRegion       = !string.IsNullOrEmpty(PlainRegion) ? Encoding.ASCII.GetBytes(PlainRegion) : null;
            var plainRegionOffset = 0;
            var logoRegionOffset  = 0;

            var exeFs       = ExeFs?.ToByteArray();
            var exeFsOffset = 0;

            var romFs       = RomFs?.Data;
            var romFsOffset = 0;

            // Write the data
            var offset = 0x200; // Skip the header, write it last

            if (exheader != null)
            {
                await data.WriteAsync(offset, exheader);

                offset += exheader.Length;
            }
            if (plainRegion != null)
            {
                plainRegionOffset = offset;
                await data.WriteAsync(offset, plainRegion);

                offset += plainRegion.Length;

                var padding = new byte[0x200 - plainRegion.Length % 0x200];
                await data.WriteAsync(offset, padding);

                offset += padding.Length;
            }
            if (Logo != null)
            {
                logoRegionOffset = offset;
                await data.WriteAsync(offset, Logo);

                offset += Logo.Length;

                var padding = new byte[0x200 - Logo.Length % 0x200];
                await data.WriteAsync(offset, padding);

                offset += padding.Length;
            }
            if (exeFs != null)
            {
                exeFsOffset = offset;
                await data.WriteAsync(offset, exeFs);

                offset += exeFs.Length;

                var padding = new byte[0x200 - exeFs.Length % 0x200];
                await data.WriteAsync(offset, padding);

                offset += padding.Length;
            }
            if (romFs != null)
            {
                romFsOffset = offset;
                const int bufferSize = 1024 * 1024;
                for (int i = 0; i < romFs.Length; i += bufferSize)
                {
                    int length = (int)Math.Min(bufferSize, romFs.Length - i);
                    var block  = await romFs.ReadArrayAsync(i, length);

                    await data.WriteAsync(offset, block);

                    offset += length;
                }

                var padding = new byte[0x200 - romFs.Length % 0x200];
                await data.WriteAsync(offset, padding);

                offset += padding.Length;
            }

            // Create a new header
            using var sha = SHA256.Create();

            var header = NcchHeader.Copy(this.Header);

            header.Signature           = new byte[0x100];                              // We lack the 3DS's private key, so leave out the signature
            header.ContentSize         = (offset + MediaUnitSize - 1) / MediaUnitSize; // offset/MediaUnitSize, but rounding up
            header.ContentLockSeedHash = 0;                                            // Unknown, left blank by SciresM's 3DS Builder
            if (Logo != null)
            {
                header.LogoRegionHash = sha.ComputeHash(Logo);
            }
            else
            {
                header.LogoRegionHash = new byte[0x20];
            }

            if (exheader != null)
            {
                header.ExHeaderHash = NcchExtendedHeader.GetSuperblockHash(sha, exheader);
                header.ExHeaderSize = NcchExtendedHeader.ExHeaderDataSize;
            }
            else
            {
                header.ExHeaderHash = new byte[0x20];
                header.ExHeaderSize = 0;
            }

            header.PlainRegionOffset   = (plainRegionOffset + MediaUnitSize - 1) / MediaUnitSize;
            header.PlainRegionSize     = ((plainRegion?.Length ?? 0) + MediaUnitSize - 1) / MediaUnitSize;
            header.LogoRegionOffset    = (logoRegionOffset + MediaUnitSize - 1) / MediaUnitSize;
            header.LogoRegionSize      = ((Logo?.Length ?? 0) + MediaUnitSize - 1) / MediaUnitSize;
            header.ExeFsOffset         = (exeFsOffset + MediaUnitSize - 1) / MediaUnitSize;
            header.ExeFsSize           = ((exeFs?.Length ?? 0) + MediaUnitSize - 1) / MediaUnitSize;
            header.ExeFsHashRegionSize = 1; // Static 0x200 for exefs superblock
            header.RomFsOffset         = (romFsOffset + MediaUnitSize - 1) / MediaUnitSize;
            header.RomFsSize           = ((int)(romFs?.Length ?? 0) + MediaUnitSize - 1) / MediaUnitSize;
            header.RomFsHashRegionSize = ((RomFs?.Header?.MasterHashSize ?? 0) + MediaUnitSize - 1) / MediaUnitSize;
            header.ExeFsSuperblockHash = ExeFs?.GetSuperblockHash() ?? new byte[0x20];
            header.RomFsSuperblockHash = RomFs != null ? await RomFs.GetSuperblockHash(sha, romFs, RomFs.Header) : new byte[0x20];

            var headerData = await header.ToBinary().ReadArrayAsync();

            await data.WriteAsync(0, headerData);

            return(offset);
        }
コード例 #8
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();
            }
        }
コード例 #9
0
 public NcchPartition(RomFs romfs = null, ExeFs exefs = null, IReadOnlyBinaryDataAccessor exheader = null)
 {
     RomFs    = romfs;
     ExeFs    = exefs;
     ExHeader = exheader;
 }