示例#1
0
        public NCCH(Stream input)
        {
            _stream = input;
            using (var br = new BinaryReaderX(input, true))
            {
                //NCCH Header
                ncchHeader = br.ReadStruct <Header>();

                if (ncchHeader.ncchSize != input.Length / mediaUnitSize)
                {
                    throw new Exception("Size in header is not the same as the file.");
                }

                //ExtHeader
                if (ncchHeader.exHeaderSize != 0)
                {
                    var exHeaderOffset = br.BaseStream.Position;
                    exHeader = br.ReadStruct <ExHeader>();

                    Files.Add(new ArchiveFileInfo
                    {
                        State    = ArchiveFileState.Archived,
                        FileName = "ExHeader.bin",
                        FileData = new SubStream(br.BaseStream, exHeaderOffset, ncchHeader.exHeaderSize * 2)
                    });
                }

                //Plain Region
                if (ncchHeader.plainRegionOffset != 0 && ncchHeader.plainRegionSize != 0)
                {
                    br.BaseStream.Position = ncchHeader.plainRegionOffset * mediaUnitSize;
                    plainRegion            = br.ReadBytes(ncchHeader.plainRegionSize * mediaUnitSize);

                    Files.Add(new ArchiveFileInfo
                    {
                        State    = ArchiveFileState.Archived,
                        FileName = "PlainRegion.bin",
                        FileData = new SubStream(br.BaseStream, ncchHeader.plainRegionOffset * mediaUnitSize, ncchHeader.plainRegionSize * mediaUnitSize)
                    });
                }

                //Logo Region
                if (ncchHeader.logoRegionOffset != 0 && ncchHeader.logoRegionSize != 0)
                {
                    br.BaseStream.Position = ncchHeader.logoRegionOffset * mediaUnitSize;
                    logoRegion             = br.ReadBytes(ncchHeader.logoRegionSize * mediaUnitSize);

                    Files.Add(new ArchiveFileInfo
                    {
                        State    = ArchiveFileState.Archived,
                        FileName = "Logo.icn",
                        FileData = new SubStream(br.BaseStream, ncchHeader.logoRegionOffset * mediaUnitSize, ncchHeader.logoRegionSize * mediaUnitSize)
                    });
                }

                //ExeFS
                int exeFSHeaderSize = 0x200;
                if (ncchHeader.exeFSOffset != 0 && ncchHeader.exeFSSize != 0)
                {
                    br.BaseStream.Position = ncchHeader.exeFSOffset * mediaUnitSize;
                    exeFS = new ExeFS(br.BaseStream);

                    foreach (var exeFsFile in exeFS.fileHeader)
                    {
                        if (exeFsFile.offset == 0 && exeFsFile.size == 0)
                        {
                            break;
                        }
                        Files.Add(new ExeFSFileInfo
                        {
                            State      = ArchiveFileState.Archived,
                            FileName   = "ExeFS\\" + exeFsFile.name,
                            FileData   = new SubStream(br.BaseStream, ncchHeader.exeFSOffset * mediaUnitSize + exeFSHeaderSize + exeFsFile.offset, exeFsFile.size),
                            compressed = exeFsFile.name == ".code" && (exHeader.sci.flag & 0x1) == 1
                        });
                    }
                }

                //RomFS
                if (ncchHeader.romFSOffset != 0 && ncchHeader.romFSSize != 0)
                {
                    br.BaseStream.Position = ncchHeader.romFSOffset * mediaUnitSize;
                    romFS = new RomFS(br.BaseStream);
                    foreach (var romFSFile in romFS.files)
                    {
                        Files.Add(new ArchiveFileInfo
                        {
                            State    = ArchiveFileState.Archived,
                            FileName = "RomFS" + romFSFile.fileName,
                            FileData = new SubStream(br.BaseStream, romFSFile.fileOffset, romFSFile.fileSize)
                        });
                    }
                }
            }
        }
示例#2
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);
        }