/// <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")} "); }
/// <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)); }
/// <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()} "); }
/// <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); } }
/// <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)); }