Beispiel #1
0
        /// <summary>
        /// Gets the new expected hash and writes it to the header.
        /// </summary>
        /// <param name="stream">The stream containing the header.</param>
        /// <param name="headerOffset">The offset to the header in the stream.</param>
        /// <param name="header">The header.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>The async task.</returns>
        private async Task UpdateHashAsync(
            Stream stream,
            UInt64 headerOffset,
            Nefs20Header header,
            NefsProgress p)
        {
            // The hash is of the entire header expect for the expected hash
            var firstOffset  = (long)headerOffset;
            var secondOffset = firstOffset + 0x24;
            var headerSize   = (int)header.Intro.HeaderSize;

            // Seek to beginning of header
            stream.Seek(firstOffset, SeekOrigin.Begin);

            // Read magic num
            var dataToHash = new byte[headerSize - 0x20];
            await stream.ReadAsync(dataToHash, 0, 4);

            // Skip expected hash and read rest of header
            stream.Seek(secondOffset, SeekOrigin.Begin);
            stream.Read(dataToHash, 4, headerSize - 0x24);

            // Compute the new expected hash
            using (var hash = SHA256.Create())
            {
                byte[] hashOut = hash.ComputeHash(dataToHash);

                // Write the expected hash
                header.Intro.Data0x04_ExpectedHash.Value = hashOut;
                await header.Intro.Data0x04_ExpectedHash.WriteAsync(stream, headerOffset, p);
            }
        }
        private string GetDebugInfoVersion20(NefsItem item, Nefs20Header h, NefsItemList items)
        {
            var p1         = h.Part1.EntriesByGuid[item.Guid];
            var p2         = h.Part2.EntriesByIndex[(int)p1.IndexPart2];
            var p6         = h.Part6.EntriesByGuid[item.Guid];
            var p7         = h.Part7.EntriesByIndex[(int)p1.IndexPart2];
            var numChunks  = h.TableOfContents.ComputeNumChunks(p2.ExtractedSize);
            var attributes = p6.CreateAttributes();

            return($@"Item Info
-----------------------------------------------------------
Item name:                  {item.FileName}
Item path:                  {items.GetItemFilePath(item.Id)}

Part 1
-----------------------------------------------------------
Offset to data:             {p1.OffsetToData.ToString("X")}
Index in part 2:            {p1.IndexPart2.ToString("X")}
Index in part 4:            {p1.IndexPart4.ToString("X")}
Id:                         {p1.Id.Value.ToString("X")}

Part 2
-----------------------------------------------------------
Directory id:               {p2.DirectoryId.Value.ToString("X")}
First child id:             {p2.FirstChildId.Value.ToString("X")}
Offset in part 3:           {p2.OffsetIntoPart3.ToString("X")}
Extracted size:             {p2.ExtractedSize.ToString("X")}
Id:                         {p2.Id.Value.ToString("X")}

Part 4
-----------------------------------------------------------
Chunks                      {this.PrintChunkSizesToString(h.Part4.CreateChunksList(p1.IndexPart4, numChunks, item.Transform))}

Part 6
-----------------------------------------------------------
0x00:                       {p6.Volume.ToString("X")}
0x02:                       {((byte)p6.Flags).ToString("X")}
0x03:                       {p6.Unknown0x3.ToString("X")}

IsZlib:                     {attributes.V20IsZlib}
IsAes:                      {attributes.V20IsAes}
IsDirectory:                {attributes.IsDirectory}
IsDuplicated:               {attributes.IsDuplicated}
Unknown 0x10:               {attributes.V20Unknown0x10}
Unknown 0x20:               {attributes.V20Unknown0x20}
Unknown 0x40:               {attributes.V20Unknown0x40}
Unknown 0x80:               {attributes.V20Unknown0x80}

Part 7
-----------------------------------------------------------
Sibling id:                 {p7.SiblingId.Value.ToString("X")}
Item id:                    {p7.Id.Value.ToString("X")}
");
        }
Beispiel #3
0
        /// <summary>
        /// Creates a test archive. Does not write an archive to disk. Just creates a <see
        /// cref="NefsArchive"/> object.
        /// </summary>
        /// <param name="filePath">The file path to use for the archive.</param>
        /// <returns>A <see cref="NefsArchive"/>.</returns>
        public static NefsArchive Create(string filePath)
        {
            var items = new NefsItemList(filePath);

            Assert.Empty(items.EnumerateById());
            Assert.Empty(items.EnumerateDepthFirstByName());

            var intro = new NefsHeaderIntro();

            intro.Data0x6c_NumberOfItems.Value = (uint)items.Count;
            var toc = new Nefs20HeaderIntroToc();

            var header = new Nefs20Header(intro, toc, items);

            return(new NefsArchive(header, items));
        }
        /// <summary>
        /// Creates a test archive. Does not write an archive to disk. Just creates a <see
        /// cref="NefsArchive"/> object.
        /// </summary>
        /// <param name="filePath">The file path to use for the archive.</param>
        /// <returns>A <see cref="NefsArchive"/>.</returns>
        public static NefsArchive Create(string filePath)
        {
            var items     = new NefsItemList(filePath);
            var aesString = "44927647059D3D73CDCC8D4C6E808538CAD7622D076A507E16C43A8DD8E3B5AB";

            var file1Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file1Chunks     = NefsDataChunk.CreateChunkList(File1ChunkSizes, TestHelpers.TestTransform);
            var file1DataSource = new NefsItemListDataSource(items, File1Offset, new NefsItemSize(File1ExtractedSize, file1Chunks));
            var file1           = new NefsItem(File1Guid, new NefsItemId(File1ItemId), File1Name, new NefsItemId(File1DirectoryId), file1DataSource, TestHelpers.TestTransform, file1Attributes);

            items.Add(file1);

            var dir1Attributes = new NefsItemAttributes(isDirectory: true);
            var dir1DataSource = new NefsEmptyDataSource();
            var dir1           = new NefsItem(Dir1Guid, new NefsItemId(Dir1ItemId), Dir1Name, new NefsItemId(Dir1DirectoryId), dir1DataSource, null, dir1Attributes);

            items.Add(dir1);

            var file2Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file2Chunks     = NefsDataChunk.CreateChunkList(File2ChunkSizes, TestHelpers.TestTransform);
            var file2DataSource = new NefsItemListDataSource(items, File2Offset, new NefsItemSize(File2ExtractedSize, file2Chunks));
            var file2           = new NefsItem(File2Guid, new NefsItemId(File2ItemId), File2Name, new NefsItemId(File2DirectoryId), file2DataSource, TestHelpers.TestTransform, file2Attributes);

            items.Add(file2);

            var file3Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file3Transform  = new NefsDataTransform(File3ExtractedSize);
            var file3Chunks     = NefsDataChunk.CreateChunkList(File3ChunkSizes, file3Transform);
            var file3DataSource = new NefsItemListDataSource(items, File3Offset, new NefsItemSize(File3ExtractedSize, file3Chunks));
            var file3           = new NefsItem(File3Guid, new NefsItemId(File3ItemId), File3Name, new NefsItemId(File3DirectoryId), file3DataSource, file3Transform, file3Attributes);

            items.Add(file3);

            Assert.Equal((int)NumItems, items.Count);

            var intro = new NefsHeaderIntro();

            intro.Data0x6c_NumberOfItems.Value   = (uint)items.Count;
            intro.Data0x24_AesKeyHexString.Value = Encoding.ASCII.GetBytes(aesString);

            var toc = new Nefs20HeaderIntroToc();

            var header = new Nefs20Header(intro, toc, items);

            return(new NefsArchive(header, items));
        }
        /// <summary>
        /// Creates a test archive. Does not write an archive to disk. Just creates a <see
        /// cref="NefsArchive"/> object.
        /// </summary>
        /// <param name="filePath">The file path to use for the archive.</param>
        /// <returns>A <see cref="NefsArchive"/>.</returns>
        public static NefsArchive Create(string filePath)
        {
            var items = new NefsItemList(filePath);

            var file1Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file1Chunks     = NefsDataChunk.CreateChunkList(File1ChunkSizes, TestHelpers.TestTransform);
            var file1DataSource = new NefsItemListDataSource(items, File1Offset, new NefsItemSize(File1ExtractedSize, file1Chunks));
            var file1           = new NefsItem(File1Guid, new NefsItemId(File1ItemId), File1Name, new NefsItemId(File1DirectoryId), file1DataSource, TestHelpers.TestTransform, file1Attributes);

            items.Add(file1);

            var dir1Attributes = new NefsItemAttributes(isDirectory: true);
            var dir1DataSource = new NefsEmptyDataSource();
            var dir1           = new NefsItem(Dir1Guid, new NefsItemId(Dir1ItemId), Dir1Name, new NefsItemId(Dir1DirectoryId), dir1DataSource, null, dir1Attributes);

            items.Add(dir1);

            var file2Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file2Chunks     = NefsDataChunk.CreateChunkList(File2ChunkSizes, TestHelpers.TestTransform);
            var file2DataSource = new NefsItemListDataSource(items, File2Offset, new NefsItemSize(File2ExtractedSize, file2Chunks));
            var file2           = new NefsItem(File2Guid, new NefsItemId(File2ItemId), File2Name, new NefsItemId(File2DirectoryId), file2DataSource, TestHelpers.TestTransform, file2Attributes);

            items.Add(file2);

            var file3Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file3Chunks     = NefsDataChunk.CreateChunkList(File3ChunkSizes, TestHelpers.TestTransform);
            var file3DataSource = new NefsItemListDataSource(items, File3Offset, new NefsItemSize(File3ExtractedSize, file3Chunks));
            var file3           = new NefsItem(File3Guid, new NefsItemId(File3ItemId), File3Name, new NefsItemId(File3DirectoryId), file3DataSource, TestHelpers.TestTransform, file3Attributes);

            items.Add(file3);

            Assert.Equal((int)NumItems, items.Count);

            var intro = new NefsHeaderIntro();

            intro.Data0x6c_NumberOfItems.Value = (uint)items.Count;

            var toc = new Nefs20HeaderIntroToc();

            var header = new Nefs20Header(intro, toc, items);

            return(new NefsArchive(header, items));
        }
Beispiel #6
0
        /// <summary>
        /// Creates a <see cref="NefsArchive"/> to be used for testing.
        /// </summary>
        /// <param name="filePath">The file path to associate with the archive.</param>
        /// <returns>An archive object.</returns>
        /// <remarks><![CDATA[ Test archive items: /file1 /dir1 /dir1/file2 ]]></remarks>
        internal static NefsArchive CreateTestArchive(string filePath)
        {
            var items = new NefsItemList(filePath);

            var transform = new NefsDataTransform(50, true);

            var file1Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file1Chunks     = NefsDataChunk.CreateChunkList(new List <UInt32> {
                2, 3, 4
            }, transform);
            var file1DataSource = new NefsItemListDataSource(items, 100, new NefsItemSize(20, file1Chunks));
            var file1           = new NefsItem(Guid.NewGuid(), new NefsItemId(0), "file1", new NefsItemId(0), file1DataSource, transform, file1Attributes);

            items.Add(file1);

            var dir1Attributes = new NefsItemAttributes(isDirectory: true);
            var dir1DataSource = new NefsEmptyDataSource();
            var dir1           = new NefsItem(Guid.NewGuid(), new NefsItemId(1), "dir1", new NefsItemId(1), dir1DataSource, null, dir1Attributes);

            items.Add(dir1);

            var file2Attributes = new NefsItemAttributes(v20IsZlib: true);
            var file2Chunks     = NefsDataChunk.CreateChunkList(new List <UInt32> {
                5, 6, 7
            }, transform);
            var file2DataSource = new NefsItemListDataSource(items, 104, new NefsItemSize(15, file2Chunks));
            var file2           = new NefsItem(Guid.NewGuid(), new NefsItemId(2), "file2", dir1.Id, file2DataSource, transform, file2Attributes);

            items.Add(file2);

            var intro  = new NefsHeaderIntro();
            var toc    = new Nefs20HeaderIntroToc();
            var header = new Nefs20Header(intro, toc, items);

            return(new NefsArchive(header, items));
        }
        private string GetDebugInfoVersion20(Nefs20Header h, NefsArchiveSource source)
        {
            var headerPart1String = new StringBuilder();

            foreach (var entry in h.Part1.EntriesByIndex)
            {
                headerPart1String.Append($"0x{entry.OffsetToData.ToString("X")}".PadRight(20));
                headerPart1String.Append($"0x{entry.IndexPart2.ToString("X")}".PadRight(20));
                headerPart1String.Append($"0x{entry.IndexPart4.ToString("X")}".PadRight(20));
                headerPart1String.Append($"0x{entry.Id.Value.ToString("X")}".PadRight(20));
                headerPart1String.Append("\n");
            }

            var headerPart2String = new StringBuilder();

            foreach (var entry in h.Part2.EntriesByIndex)
            {
                headerPart2String.Append($"0x{entry.DirectoryId.Value.ToString("X")}".PadRight(20));
                headerPart2String.Append($"0x{entry.FirstChildId.Value.ToString("X")}".PadRight(20));
                headerPart2String.Append($"0x{entry.OffsetIntoPart3.ToString("X")}".PadRight(20));
                headerPart2String.Append($"0x{entry.ExtractedSize.ToString("X")}".PadRight(20));
                headerPart2String.Append($"0x{entry.Id.Value.ToString("X")}".PadRight(20));
                headerPart2String.Append("\n");
            }

            var headerPart3String = new StringBuilder();

            foreach (var s in h.Part3.FileNames)
            {
                headerPart3String.AppendLine(s);
            }

            var headerPart6String = new StringBuilder();

            foreach (var entry in h.Part6.EntriesByIndex)
            {
                headerPart6String.Append($"0x{entry.Volume.ToString("X")}".PadRight(20));
                headerPart6String.Append($"0x{((byte)entry.Flags).ToString("X")}".PadRight(20));
                headerPart6String.Append($"0x{entry.Unknown0x3.ToString("X")}".PadRight(20));
                headerPart6String.Append("\n");
            }

            var headerPart7String = new StringBuilder();

            foreach (var entry in h.Part7.EntriesByIndex)
            {
                headerPart7String.Append($"0x{entry.SiblingId.Value.ToString("X")}".PadRight(20));
                headerPart7String.Append($"0x{entry.Id.Value.ToString("X")}".PadRight(20));
                headerPart7String.Append("\n");
            }

            return($@"Archive Source
-----------------------------------------------------------
Header source file:         {source.HeaderFilePath}
Header offset:              {source.HeaderOffset.ToString("X")}
Data file path:             {source.DataFilePath}
Is header/data separate:    {source.IsHeaderSeparate}

General Info
-----------------------------------------------------------
Archive Size:               {h.Part5.ArchiveSize.ToString("X")}
Is Header Encrypted?        {h.Intro.IsEncrypted}

Header size:                {h.Intro.HeaderSize.ToString("X")}
Intro size:                 {NefsHeaderIntro.Size.ToString("X")}
Toc size:                   {Nefs20HeaderIntroToc.Size.ToString("X")}
Part 1 size:                {h.TableOfContents.Part1Size.ToString("X")}
Part 2 size:                {h.TableOfContents.Part2Size.ToString("X")}
Part 3 size:                {h.TableOfContents.Part3Size.ToString("X")}
Part 4 size:                {h.TableOfContents.Part4Size.ToString("X")}
Part 8 size:                {(h.Intro.HeaderSize - h.TableOfContents.OffsetToPart8).ToString("X")}

Header Intro
-----------------------------------------------------------
Magic Number:               {h.Intro.MagicNumber.ToString("X")}
Expected SHA-256 hash:      {StringHelper.ByteArrayToString(h.Intro.ExpectedHash)}
AES 256 key hex string:     {StringHelper.ByteArrayToString(h.Intro.AesKeyHexString)}
Header size:                {h.Intro.HeaderSize.ToString("X")}
NeFS version:               {h.Intro.NefsVersion.ToString("X")}
Number of items:            {h.Intro.NumberOfItems.ToString("X")}
Unknown 0x70:               {h.Intro.Unknown0x70zlib.ToString("X")}
Unknown 0x78:               {h.Intro.Unknown0x78.ToString("X")}

Header Table of Contents
-----------------------------------------------------------
Offset to Part 1:           {h.TableOfContents.OffsetToPart1.ToString("X")}
Offset to Part 2:           {h.TableOfContents.OffsetToPart2.ToString("X")}
Offset to Part 3:           {h.TableOfContents.OffsetToPart3.ToString("X")}
Offset to Part 4:           {h.TableOfContents.OffsetToPart4.ToString("X")}
Offset to Part 5:           {h.TableOfContents.OffsetToPart5.ToString("X")}
Offset to Part 6:           {h.TableOfContents.OffsetToPart6.ToString("X")}
Offset to Part 7:           {h.TableOfContents.OffsetToPart7.ToString("X")}
Offset to Part 8:           {h.TableOfContents.OffsetToPart8.ToString("X")}
Num Volumes:                {h.TableOfContents.NumVolumes.ToString("X")}
Hash Block Size (<< 15):    {h.TableOfContents.HashBlockSize.ToString("X")}
Unknown 0x24:               {StringHelper.ByteArrayToString(h.TableOfContents.Unknown0x24)}

Header Part 1
-----------------------------------------------------------
Data Offset         Index Part 2        Index Part 4        Id
{headerPart1String.ToString()}
Header Part 2
-----------------------------------------------------------
Directory Id        First child Id      Part 3 offset       Extracted size      Id
{headerPart2String.ToString()}
Header Part 3
-----------------------------------------------------------
{headerPart3String.ToString()}
Header Part 4
-----------------------------------------------------------
Number of entries:          {h.Part4.EntriesByIndex.Count.ToString("X")}

Header Part 5
-----------------------------------------------------------
Archive size:               {h.Part5.ArchiveSize.ToString("X")}
First data offset:          {h.Part5.FirstDataOffset.ToString("X")}
Archive name string offset: {h.Part5.ArchiveNameStringOffset.ToString("X")}

Header Part 6
-----------------------------------------------------------
{headerPart6String.ToString()}
Header Part 7
-----------------------------------------------------------
{headerPart7String.ToString()}
");
        }
Beispiel #8
0
        /// <summary>
        /// Writes the header to the output stream.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="headerOffset">The offset into the stream to begin.</param>
        /// <param name="header">The header to write.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>The async task.</returns>
        private async Task WriteHeaderAsync(Stream stream, UInt64 headerOffset, Nefs20Header header, NefsProgress p)
        {
            // Calc weight of each task (8 parts + intro + table of contents)
            var weight = 1.0f / 10.0f;

            // Get table of contents
            var toc = header.TableOfContents;

            using (var t = p.BeginTask(weight, "Writing header intro"))
            {
                var offset = headerOffset + Nefs20Header.IntroOffset;
                await this.WriteHeaderIntroAsync(stream, offset, header.Intro, p);
            }

            using (var t = p.BeginTask(weight, "Writing header intro table of contents"))
            {
                var offset = headerOffset + Nefs20HeaderIntroToc.Offset;
                await this.WriteHeaderIntroTocAsync(stream, offset, header.TableOfContents, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 1"))
            {
                var offset = headerOffset + toc.OffsetToPart1;
                await this.WriteHeaderPart1Async(stream, offset, header.Part1, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 2"))
            {
                var offset = headerOffset + toc.OffsetToPart2;
                await this.WriteHeaderPart2Async(stream, offset, header.Part2, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 3"))
            {
                var offset = headerOffset + toc.OffsetToPart3;
                await this.WriteHeaderPart3Async(stream, offset, header.Part3, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 4"))
            {
                var offset = headerOffset + toc.OffsetToPart4;
                await this.WriteHeaderPart4Async(stream, offset, header.Part4, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 5"))
            {
                var offset = headerOffset + toc.OffsetToPart5;
                await this.WriteHeaderPart5Async(stream, offset, header.Part5, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 6"))
            {
                var offset = headerOffset + toc.OffsetToPart6;
                await this.WriteHeaderPart6Async(stream, offset, header.Part6, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 7"))
            {
                var offset = headerOffset + toc.OffsetToPart7;
                await this.WriteHeaderPart7Async(stream, offset, header.Part7, p);
            }

            using (var t = p.BeginTask(weight, "Writing header part 8"))
            {
                var offset = headerOffset + toc.OffsetToPart8;
                await this.WriteHeaderPart8Async(stream, offset, header.Part8, p);
            }
        }
Beispiel #9
0
        /// <summary>
        /// Writes an archive to the specified stream. A new archive obejct is returned that
        /// contains the updated header and item metadata.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="sourceHeader">Donor header information.</param>
        /// <param name="sourceItems">List of items to write. This list is not modified directly.</param>
        /// <param name="workDir">Temp working directory path.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>A new NefsArchive object containing the updated header and item metadata.</returns>
        private async Task <NefsArchive> WriteArchiveAsync(
            Stream stream,
            Nefs20Header sourceHeader,
            NefsItemList sourceItems,
            string workDir,
            NefsProgress p)
        {
            // Setup task weights
            var taskWeightPrepareItems = 0.45f;
            var taskWeightWriteItems   = 0.45f;
            var taskWeightHeader       = 0.1f;

            // Prepare items for writing
            NefsItemList items;

            using (var t = p.BeginTask(taskWeightPrepareItems, "Preparing items"))
            {
                items = await this.PrepareItemsAsync(sourceItems, workDir, p);
            }

            // Determine number of items
            var numItems = items.Count;

            // Update header parts 3 and 4 first (need to know their sizes)
            var p4 = new Nefs20HeaderPart4(items);
            var p3 = new NefsHeaderPart3(items);

            // Compute header size
            var introSize  = NefsHeaderIntro.Size;
            var tocSize    = Nefs20HeaderIntroToc.Size;
            var p1Size     = numItems * NefsHeaderPart1Entry.Size; // TODO : What about duplicates?
            var p2Size     = numItems * NefsHeaderPart2Entry.Size; // TODO : What about duplicates?
            var p3Size     = p3.Size;
            var p4Size     = p4.Size;
            var p5Size     = NefsHeaderPart5.Size;
            var p6Size     = numItems * Nefs20HeaderPart6Entry.Size;
            var p7Size     = numItems * NefsHeaderPart7Entry.Size;
            var p8Size     = sourceHeader.Intro.HeaderSize - sourceHeader.TableOfContents.OffsetToPart8;
            var headerSize = introSize + tocSize + p1Size + p2Size + p3Size + p4Size + p5Size + p6Size + p7Size + p8Size;

            // Determine first data offset. There are two known offset values. If the header is
            // large enough, the second (larger) offset is used.
            var firstDataOffset = Nefs20Header.DataOffsetDefault;

            if (headerSize > firstDataOffset)
            {
                firstDataOffset = Nefs20Header.DataOffsetLarge;
            }

            // Write item data
            UInt64 archiveSize;

            using (var t = p.BeginTask(taskWeightWriteItems, "Writing items"))
            {
                archiveSize = await this.WriteItemsAsync(stream, items, firstDataOffset, p);
            }

            // Update remaining header data
            var p1 = new NefsHeaderPart1(items, p4);
            var p2 = new NefsHeaderPart2(items, p3);
            var p6 = new Nefs20HeaderPart6(items);
            var p7 = new NefsHeaderPart7(items);

            // Compute total archive size
            var p5 = new NefsHeaderPart5();

            p5.Data0x00_ArchiveSize.Value             = archiveSize;
            p5.Data0x08_ArchiveNameStringOffset.Value = p3.OffsetsByFileName[items.DataFileName];
            p5.Data0x0C_FirstDataOffset.Value         = sourceHeader.Part5.FirstDataOffset;

            // Update header intro
            var intro = new NefsHeaderIntro();

            intro.Data0x00_MagicNumber.Value     = sourceHeader.Intro.MagicNumber;
            intro.Data0x24_AesKeyHexString.Value = sourceHeader.Intro.AesKeyHexString;
            intro.Data0x64_HeaderSize.Value      = (uint)headerSize;
            intro.Data0x68_NefsVersion.Value     = sourceHeader.Intro.NefsVersion;
            intro.Data0x6c_NumberOfItems.Value   = (uint)numItems;
            intro.Data0x70_UnknownZlib.Value     = sourceHeader.Intro.Unknown0x70zlib;
            intro.Data0x78_Unknown.Value         = sourceHeader.Intro.Unknown0x78;

            var toc = new Nefs20HeaderIntroToc();

            toc.Data0x00_NumVolumes.Value    = sourceHeader.TableOfContents.NumVolumes;
            toc.Data0x02_HashBlockSize.Value = sourceHeader.TableOfContents.Data0x02_HashBlockSize.Value;
            toc.Data0x04_OffsetToPart1.Value = introSize + tocSize;
            toc.Data0x0c_OffsetToPart2.Value = toc.OffsetToPart1 + (uint)p1Size;
            toc.Data0x14_OffsetToPart3.Value = toc.OffsetToPart2 + (uint)p2Size;
            toc.Data0x18_OffsetToPart4.Value = toc.OffsetToPart3 + (uint)p3Size;
            toc.Data0x1c_OffsetToPart5.Value = toc.OffsetToPart4 + (uint)p4Size;
            toc.Data0x08_OffsetToPart6.Value = toc.OffsetToPart5 + (uint)p5Size;
            toc.Data0x10_OffsetToPart7.Value = toc.OffsetToPart6 + (uint)p6Size;
            toc.Data0x20_OffsetToPart8.Value = toc.OffsetToPart7 + (uint)p7Size;
            toc.Data0x24_Unknown.Value       = sourceHeader.TableOfContents.Unknown0x24;

            // Part 8 - not writing anything for now
            var p8 = new NefsHeaderPart8(p8Size);

            // Create new header object
            var header = new Nefs20Header(intro, toc, p1, p2, p3, p4, p5, p6, p7, p8);

            // Write the header
            using (var t = p.BeginTask(taskWeightHeader, "Writing header"))
            {
                await this.WriteHeaderAsync(stream, 0, header, p);
            }

            // Update hash
            await this.UpdateHashAsync(stream, 0, header, p);

            // Create new archive object
            return(new NefsArchive(header, items));
        }