예제 #1
0
        public void GetChunkSizesForItem_ItemIsDirectory_EmptyList()
        {
            var p4    = new NefsHeaderPart4(this.testItems);
            var sizes = p4.GetChunkSizesForItem(this.dir1);

            Assert.Empty(sizes);
        }
예제 #2
0
        public void GetChunkSizesForItem_ItemIsNotCompressed_EmptyList()
        {
            var p4    = new NefsHeaderPart4(this.testItems);
            var sizes = p4.GetChunkSizesForItem(this.file4NotCompressed);

            Assert.Empty(sizes);
        }
예제 #3
0
        public void NefsHeaderPart4_MultipleItems_EntriesPopulated()
        {
            var p4 = new NefsHeaderPart4(this.testItems);

            // Dir 1 and file 4 should not have entries. Only compressed files have entries in part 4.
            Assert.Equal(3, p4.EntriesByIndex.Count);

            // Total size is (total number of chunks * bytes)
            Assert.Equal(36U, p4.Size);

            // File 1
            var f1Idx = p4.GetIndexForItem(this.file1);

            Assert.Equal(1U, p4.EntriesByIndex[f1Idx].ChunkSizes[0]);
            Assert.Equal(11U, p4.EntriesByIndex[f1Idx].ChunkSizes[1]);
            Assert.Equal(21U, p4.EntriesByIndex[f1Idx].ChunkSizes[2]);
            Assert.Equal(3, p4.EntriesByIndex[f1Idx].ChunkSizes.Count);

            // File 2
            var f2Idx = p4.GetIndexForItem(this.file2);

            Assert.Equal(2U, p4.EntriesByIndex[f2Idx].ChunkSizes[0]);
            Assert.Equal(22U, p4.EntriesByIndex[f2Idx].ChunkSizes[1]);
            Assert.Equal(52U, p4.EntriesByIndex[f2Idx].ChunkSizes[2]);
            Assert.Equal(3, p4.EntriesByIndex[f2Idx].ChunkSizes.Count);

            // File 3
            var f3Idx = p4.GetIndexForItem(this.file3);

            Assert.Equal(3U, p4.EntriesByIndex[f3Idx].ChunkSizes[0]);
            Assert.Equal(13U, p4.EntriesByIndex[f3Idx].ChunkSizes[1]);
            Assert.Equal(23U, p4.EntriesByIndex[f3Idx].ChunkSizes[2]);
            Assert.Equal(3, p4.EntriesByIndex[f3Idx].ChunkSizes.Count);
        }
예제 #4
0
        public void NefsHeaderPart1_MultipleItems_EntriesPopulated()
        {
            var items = new NefsItemList(@"C:\archive.nefs");

            var file1DataSource = new NefsItemListDataSource(items, 123, new NefsItemSize(456, new List <UInt32> {
                11, 12, 13
            }));
            var file1 = new NefsItem(new NefsItemId(0), "file1", new NefsItemId(0), NefsItemType.File, file1DataSource, TestHelpers.CreateUnknownData());

            items.Add(file1);

            var file2DataSource = new NefsItemListDataSource(items, 456, new NefsItemSize(789, new List <UInt32> {
                14, 15, 16
            }));
            var file2 = new NefsItem(new NefsItemId(1), "file2", new NefsItemId(1), NefsItemType.File, file2DataSource, TestHelpers.CreateUnknownData());

            items.Add(file2);

            var dir1DataSource = new NefsEmptyDataSource();
            var dir1           = new NefsItem(new NefsItemId(2), "dir1", new NefsItemId(2), NefsItemType.Directory, dir1DataSource, TestHelpers.CreateUnknownData());

            items.Add(dir1);

            var p4 = new NefsHeaderPart4(items);
            var p1 = new NefsHeaderPart1(items, p4);

            Assert.Equal(3, p1.EntriesById.Count);

            /*
             * dir1
             */

            // Offset to data and index to p4 are both 0 since this is a directory
            Assert.Equal(2, (int)p1.EntriesById[dir1.Id].Id.Value);
            Assert.Equal(0, (int)p1.EntriesById[dir1.Id].OffsetToData);
            Assert.Equal(0, (int)p1.EntriesById[dir1.Id].MetadataIndex);
            Assert.Equal(0, (int)p1.EntriesById[dir1.Id].IndexIntoPart4);

            /*
             * file1
             */

            Assert.Equal(0, (int)p1.EntriesById[file1.Id].Id.Value);
            Assert.Equal(123, (int)p1.EntriesById[file1.Id].OffsetToData);
            Assert.Equal(1, (int)p1.EntriesById[file1.Id].MetadataIndex);
            Assert.Equal(0, (int)p1.EntriesById[file1.Id].IndexIntoPart4);

            /*
             * file2
             */

            Assert.Equal(1, (int)p1.EntriesById[file2.Id].Id.Value);
            Assert.Equal(456, (int)p1.EntriesById[file2.Id].OffsetToData);
            Assert.Equal(2, (int)p1.EntriesById[file2.Id].MetadataIndex);

            // There are 3 chunks for file1, so file2's chunks start right after that (hence p4
            // index == 3)
            Assert.Equal(3, (int)p1.EntriesById[file2.Id].IndexIntoPart4);
        }
예제 #5
0
        public void NefsHeaderPart1_NoItems_EntriesEmpty()
        {
            var items = new NefsItemList(@"C:\archive.nefs");
            var p4    = new NefsHeaderPart4(items);
            var p1    = new NefsHeaderPart1(items, p4);

            Assert.Empty(p1.EntriesById);
        }
예제 #6
0
        public void NefsHeaderPart4_NoItems_EntriesEmpty()
        {
            var items = new NefsItemList(@"C:\archive.nefs");
            var p4    = new NefsHeaderPart4(items);

            Assert.Empty(p4.EntriesByIndex);
            Assert.Equal(0U, p4.Size);
        }
예제 #7
0
        public void GetIndexForItem_ItemIsValue_IndexReturned()
        {
            var p4 = new NefsHeaderPart4(this.testItems);

            Assert.Equal(0U, p4.GetIndexForItem(this.file1));
            Assert.Equal(3U, p4.GetIndexForItem(this.file2));
            Assert.Equal(6U, p4.GetIndexForItem(this.file3));
        }
예제 #8
0
        /// <summary>
        /// Writes the header part to an output stream.
        /// </summary>
        /// <param name="stream">The stream to write to.</param>
        /// <param name="offset">The absolute offset in the stream to write at.</param>
        /// <param name="part4">The data to write.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>An async task.</returns>
        internal async Task WriteHeaderPart4Async(Stream stream, UInt64 offset, NefsHeaderPart4 part4, NefsProgress p)
        {
            stream.Seek((long)offset, SeekOrigin.Begin);

            foreach (var entry in part4.Entries)
            {
                foreach (var chunkSize in entry.ChunkSizes)
                {
                    var data = BitConverter.GetBytes(chunkSize);
                    await stream.WriteAsync(data, 0, data.Length, p.CancellationToken);
                }
            }
        }
예제 #9
0
        public async Task WriteHeaderPart4Async_ValidData_Written()
        {
            var items = new NefsItemList(@"C:\hi.txt");
            var file1 = TestHelpers.CreateItem(0, 0, "file1", 10, 11, new List <UInt32> {
                12, 13
            }, NefsItemType.File);
            var file2 = TestHelpers.CreateItem(1, 1, "file2", 20, 21, new List <UInt32> {
                22, 23
            }, NefsItemType.File);
            var dir1 = TestHelpers.CreateItem(2, 2, "dir1", 0, 0, new List <UInt32> {
                0
            }, NefsItemType.Directory);
            var file3 = TestHelpers.CreateItem(3, 2, "file3", 30, 31, new List <UInt32> {
                32, 33
            }, NefsItemType.File);

            items.Add(file1);
            items.Add(file2);
            items.Add(dir1);
            items.Add(file3);

            var part4 = new NefsHeaderPart4(items);

            /*
             * Write
             */

            var writer = this.CreateWriter();

            byte[] buffer;
            var    offset = 5;

            using (var ms = new MemoryStream())
            {
                await writer.WriteHeaderPart4Async(ms, (uint)offset, part4, new NefsProgress());

                buffer = ms.ToArray();
            }

            /*
             * Verify
             */

            Assert.Equal(24 + offset, buffer.Length);
            Assert.Equal(12, BitConverter.ToInt32(buffer, offset + 0));
            Assert.Equal(13, BitConverter.ToInt32(buffer, offset + 4));
            Assert.Equal(22, BitConverter.ToInt32(buffer, offset + 8));
            Assert.Equal(23, BitConverter.ToInt32(buffer, offset + 12));
            Assert.Equal(32, BitConverter.ToInt32(buffer, offset + 16));
            Assert.Equal(33, BitConverter.ToInt32(buffer, offset + 20));
        }
예제 #10
0
        public void GetChunkSizesForItem_ItemValid_ChunkSizesReturned()
        {
            var p4 = new NefsHeaderPart4(this.testItems);

            var file1Sizes = p4.GetChunkSizesForItem(this.file1);

            Assert.Equal(3, file1Sizes.Count);
            Assert.NotSame(this.file1.DataSource.Size.ChunkSizes, file1Sizes);
            Assert.True(file1Sizes.SequenceEqual(this.file1.DataSource.Size.ChunkSizes));

            var file2Sizes = p4.GetChunkSizesForItem(this.file2);

            Assert.Equal(3, file2Sizes.Count);
            Assert.NotSame(this.file2.DataSource.Size.ChunkSizes, file2Sizes);
            Assert.True(file2Sizes.SequenceEqual(this.file2.DataSource.Size.ChunkSizes));

            var file3Sizes = p4.GetChunkSizesForItem(this.file3);

            Assert.Equal(3, file3Sizes.Count);
            Assert.NotSame(this.file3.DataSource.Size.ChunkSizes, file3Sizes);
            Assert.True(file3Sizes.SequenceEqual(this.file3.DataSource.Size.ChunkSizes));
        }
예제 #11
0
        /// <summary>
        /// Reads the header from an input stream.
        /// </summary>
        /// <param name="originalStream">The stream to read from.</param>
        /// <param name="offset">The offset to the header from the beginning of the stream.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>The loaded header.</returns>
        internal async Task <NefsHeader> ReadHeaderAsync(Stream originalStream, ulong offset, NefsProgress p)
        {
            Stream             stream;
            NefsHeaderIntro    intro = null;
            NefsHeaderIntroToc toc   = null;
            NefsHeaderPart1    part1 = null;
            NefsHeaderPart2    part2 = null;
            NefsHeaderPart3    part3 = null;
            NefsHeaderPart4    part4 = null;
            NefsHeaderPart5    part5 = null;
            NefsHeaderPart6    part6 = null;
            NefsHeaderPart7    part7 = null;
            NefsHeaderPart8    part8 = null;

            // Calc weight of each task (8 parts + intro + table of contents)
            var weight = 1.0f / 10.0f;

            using (p.BeginTask(weight, "Reading header intro"))
            {
                // Decrypt header if needed
                (intro, stream) = await this.ReadHeaderIntroAsync(originalStream, offset, p);
            }

            using (p.BeginTask(weight, "Reading header intro table of contents"))
            {
                toc = await this.ReadHeaderIntroTocAsync(stream, NefsHeaderIntroToc.Offset, p);
            }

            using (p.BeginTask(weight, "Reading header part 1"))
            {
                part1 = await this.ReadHeaderPart1Async(stream, toc.OffsetToPart1, toc.Part1Size, p);
            }

            using (p.BeginTask(weight, "Reading header part 2"))
            {
                part2 = await this.ReadHeaderPart2Async(stream, toc.OffsetToPart2, toc.Part2Size, p);
            }

            using (p.BeginTask(weight, "Reading header part 3"))
            {
                part3 = await this.ReadHeaderPart3Async(stream, toc.OffsetToPart3, toc.Part3Size, p);
            }

            using (p.BeginTask(weight, "Reading header part 4"))
            {
                part4 = await this.ReadHeaderPart4Async(stream, toc.OffsetToPart4, toc.Part4Size, part1, part2, p);
            }

            using (p.BeginTask(weight, "Reading header part 5"))
            {
                part5 = await this.ReadHeaderPart5Async(stream, toc.OffsetToPart5, toc.Part5Size, p);
            }

            using (p.BeginTask(weight, "Reading header part 6"))
            {
                if (toc.OffsetToPart6 == 0)
                {
                    // game.dat files don't have part 6
                    Log.LogDebug("Archive does not have header part 6.");
                    part6 = new NefsHeaderPart6(new List <NefsHeaderPart6Entry>());
                }
                else
                {
                    part6 = await this.ReadHeaderPart6Async(stream, toc.OffsetToPart6, toc.Part6Size, part2, p);
                }
            }

            using (p.BeginTask(weight, "Reading header part 7"))
            {
                if (toc.OffsetToPart6 == 0)
                {
                    // game.dat files don't have part 7. Still checking if part 6 offset is 0. For
                    // some reason, the part 7 offset still has a value, but doesn't appear to be a
                    // correct one, so skipping part 7 as well
                    Log.LogDebug("Archive does not have header part 7.");
                    part7 = new NefsHeaderPart7(new List <NefsHeaderPart7Entry>());
                }
                else
                {
                    part7 = await this.ReadHeaderPart7Async(stream, toc.OffsetToPart7, toc.Part7Size, p);
                }
            }

            using (p.BeginTask(weight, "Reading header part 8"))
            {
                var part8Size = intro.HeaderSize - toc.OffsetToPart8;
                part8 = await this.ReadHeaderPart8Async(stream, toc.OffsetToPart8, part8Size, p);
            }

            // Validate header hash
            if (!this.ValidateHash(stream, offset, intro))
            {
                Log.LogWarning("Header hash does not match expected value.");
            }

            // The header stream must be disposed
            stream.Dispose();

            return(new NefsHeader(intro, toc, part1, part2, part3, part4, part5, part6, part7, part8));
        }
예제 #12
0
        public void GetIndexForItem_ItemIsUncompressed_NegativeOneReturned()
        {
            var p4 = new NefsHeaderPart4(this.testItems);

            Assert.Equal(0xFFFFFFFFU, p4.GetIndexForItem(this.file4NotCompressed));
        }
예제 #13
0
        public void GetIndexForItem_ItemIsDirectory_ZeroReturned()
        {
            var p4 = new NefsHeaderPart4(this.testItems);

            Assert.Equal(0U, p4.GetIndexForItem(this.dir1));
        }
예제 #14
0
        public async Task WriteHeaderPart1Async_ValidData_Written()
        {
            var items = new NefsItemList(@"C:\hi.txt");
            var file1 = TestHelpers.CreateItem(0, 0, "file1", 10, 11, new List <UInt32> {
                12, 13
            }, NefsItemType.File);
            var file2 = TestHelpers.CreateItem(1, 1, "file2", 20, 21, new List <UInt32> {
                22, 23
            }, NefsItemType.File);
            var dir1 = TestHelpers.CreateItem(2, 2, "dir1", 0, 0, new List <UInt32> {
                0
            }, NefsItemType.Directory);

            items.Add(file1);
            items.Add(file2);
            items.Add(dir1);

            var part4 = new NefsHeaderPart4(items);
            var part1 = new NefsHeaderPart1(items, part4);

            /*
             * Write
             */

            var writer = this.CreateWriter();

            byte[] buffer;
            var    offset = 5;

            using (var ms = new MemoryStream())
            {
                await writer.WriteHeaderPart1Async(ms, (uint)offset, part1, new NefsProgress());

                buffer = ms.ToArray();
            }

            /*
             * Verify
             */

            /*
             * file1
             */

            // Data offset (8 bytes)
            Assert.Equal(10, BitConverter.ToInt64(buffer, offset + 0));

            // Metadata index
            Assert.Equal(1, BitConverter.ToInt32(buffer, offset + 8));

            // Index to part 4
            Assert.Equal(0, BitConverter.ToInt32(buffer, offset + 0x0c));

            // Item id
            Assert.Equal(0, BitConverter.ToInt32(buffer, offset + 0x10));

            /*
             * file2
             */

            offset += (int)NefsHeaderPart1Entry.Size;

            // Data offset (8 bytes)
            Assert.Equal(20, BitConverter.ToInt64(buffer, offset + 0));

            // Metadata index
            Assert.Equal(2, BitConverter.ToInt32(buffer, offset + 8));

            // Index to part 4
            Assert.Equal(2, BitConverter.ToInt32(buffer, offset + 0x0c));

            // Item id
            Assert.Equal(1, BitConverter.ToInt32(buffer, offset + 0x10));

            /*
             * dir1
             */

            offset += (int)NefsHeaderPart1Entry.Size;

            // Data offset (8 bytes)
            Assert.Equal(0, BitConverter.ToInt64(buffer, offset + 0));

            // Metadata index
            Assert.Equal(0, BitConverter.ToInt32(buffer, offset + 8));

            // Index to part 4
            Assert.Equal(0, BitConverter.ToInt32(buffer, offset + 0x0c));

            // Item id
            Assert.Equal(2, BitConverter.ToInt32(buffer, offset + 0x10));
        }
예제 #15
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,
            NefsHeader 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, NefsHeader.ChunkSize, p);
            }

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

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

            // Compute header size
            var introSize  = NefsHeaderIntro.Size;
            var tocSize    = NefsHeaderIntroToc.Size;
            var p1Size     = numItems * NefsHeaderPart1Entry.Size;
            var p2Size     = numItems * NefsHeaderPart2Entry.Size;
            var p3Size     = p3.Size;
            var p4Size     = p4.Size;
            var p5Size     = NefsHeaderPart5.Size;
            var p6Size     = numItems * NefsHeaderPart6Entry.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 = NefsHeader.DataOffsetDefault;

            if (headerSize > firstDataOffset)
            {
                firstDataOffset = NefsHeader.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 NefsHeaderPart6(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_Unknown.Value         = sourceHeader.Intro.Unknown0x68;
            intro.Data0x6c_NumberOfItems.Value   = (uint)numItems;
            intro.Data0x70_UnknownZlib.Value     = sourceHeader.Intro.Unknown0x70zlib;
            intro.Data0x78_Unknown.Value         = sourceHeader.Intro.Unknown0x78;

            var toc = new NefsHeaderIntroToc();

            toc.Data0x00_Unknown.Value       = sourceHeader.TableOfContents.Unknown0x00;
            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((uint)p8Size);

            // Create new header object
            var header = new NefsHeader(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));
        }