public void CreateHeaderFromFile_ItemIsNotCompressed_ItemCreated() { var nefs = TestArchiveNotModified.Create(@"C:\archive.nefs"); var itemId = new NefsItemId(TestArchiveNotModified.File3ItemId); var item = NefsItem.CreateFromHeader(itemId, nefs.Header, nefs.Items); // File3 is not compressed var expected = nefs.Items.GetItem(itemId); Assert.Equal(expected.CompressedSize, item.CompressedSize); Assert.Equal(expected.DirectoryId, item.DirectoryId); Assert.Equal(expected.ExtractedSize, item.ExtractedSize); Assert.Equal(expected.FileName, item.FileName); Assert.Equal(expected.Id, item.Id); Assert.Equal(expected.State, item.State); Assert.Equal(NefsItemType.File, item.Type); Assert.Equal(@"C:\archive.nefs", item.DataSource.FilePath); Assert.Equal(expected.DataSource.Offset, item.DataSource.Offset); Assert.False(item.DataSource.ShouldCompress); Assert.Single(item.DataSource.Size.ChunkSizes); Assert.Equal(expected.ExtractedSize, item.DataSource.Size.ChunkSizes[0]); Assert.Equal(expected.ExtractedSize, item.DataSource.Size.ExtractedSize); Assert.False(item.DataSource.Size.IsCompressed); Assert.Equal(expected.CompressedSize, item.DataSource.Size.Size); }
public void NefsHeaderPart7_MultipleItems_EntriesPopulated() { var items = new NefsItemList(@"C:\archive.nefs"); var file1Id = new NefsItemId(31); var file1Chunks = NefsDataChunk.CreateChunkList(new List <UInt32> { 11, 12, 13 }, TestHelpers.TestTransform); var file1DataSource = new NefsItemListDataSource(items, 123, new NefsItemSize(456, file1Chunks)); var file1 = TestHelpers.CreateFile(file1Id.Value, file1Id.Value, "file1", file1DataSource); items.Add(file1); var file2Id = new NefsItemId(41); var file2Chunks = NefsDataChunk.CreateChunkList(new List <UInt32> { 14, 15, 16 }, TestHelpers.TestTransform); var file2DataSource = new NefsItemListDataSource(items, 456, new NefsItemSize(789, file2Chunks)); var file2 = TestHelpers.CreateFile(file2Id.Value, file2Id.Value, "file2", file2DataSource); items.Add(file2); var dir2Id = new NefsItemId(51); var dir1 = TestHelpers.CreateDirectory(dir2Id.Value, dir2Id.Value, "dir1"); items.Add(dir1); var p7 = new NefsHeaderPart7(items); Assert.Equal(3, p7.EntriesByIndex.Count); // NOTES : Part 7 items are ordered in the same way that part 2 items are ordered (depth // first by name). /* * dir1 */ Assert.Equal(51U, p7.EntriesByIndex[0].Id.Value); Assert.Equal(51U, p7.EntriesByIndex[0].SiblingId.Value); /* * file1 */ Assert.Equal(31U, p7.EntriesByIndex[1].Id.Value); Assert.Equal(41U, p7.EntriesByIndex[1].SiblingId.Value); /* * file2 */ Assert.Equal(41U, p7.EntriesByIndex[2].Id.Value); Assert.Equal(51U, p7.EntriesByIndex[2].SiblingId.Value); }
public async Task CloseArchiveAsync_ArchiveModifiedAndUserSavesFail_ArchiveSavedAndClosed() { var w = this.CreateWorkspace(); var closed = false; w.ArchiveClosed += (o, e) => closed = true; // Open an archive var archivePath = @"C:\archive.nefs"; var archive = TestHelpers.CreateTestArchive(archivePath); this.nefsReaderMock.Setup(r => r.ReadArchiveAsync(It.IsAny <NefsArchiveSource>(), It.IsAny <NefsProgress>())) .ReturnsAsync(archive); this.fileSystem.AddFile(archivePath, new MockFileData("hi")); await w.OpenArchiveAsync(archivePath); Assert.Same(archive, w.Archive); Assert.False(w.ArchiveIsModified); // Modify archvie var itemId = new NefsItemId(0); var item = w.Archive.Items.GetItems(itemId).First(); var cmd = new RemoveFileCommand(item, item.State); w.Execute(cmd); // Mock user choosing to save before closing this.MockMessageBox(DialogResult.Yes); // Mock the save to fail this.nefsWriterMock.Setup(n => n.WriteArchiveAsync( It.IsAny <string>(), It.IsAny <NefsArchive>(), It.IsAny <NefsProgress>())) .ThrowsAsync(new IOException()); // Close the archive var result = await w.CloseArchiveAsync(); Assert.False(result); Assert.False(closed); Assert.Same(archive, w.Archive); Assert.True(w.ArchiveIsModified); Assert.Equal(archivePath, w.ArchiveSource.DataFilePath); Assert.Equal(archivePath, w.ArchiveSource.HeaderFilePath); // Verify writer was called this.nefsWriterMock.Verify( n => n.WriteArchiveAsync( It.IsAny <string>(), It.IsAny <NefsArchive>(), It.IsAny <NefsProgress>()), Times.Once()); }
public async Task CloseArchiveAsync_ArchiveModifiedAndUserDoesNotSave_ArchiveClosedWithoutSaving() { var w = this.CreateWorkspace(); var closed = false; w.ArchiveClosed += (o, e) => closed = true; // Open an archive var archivePath = @"C:\archive.nefs"; var archive = TestHelpers.CreateTestArchive(archivePath); this.nefsReaderMock.Setup(r => r.ReadArchiveAsync(It.IsAny <NefsArchiveSource>(), It.IsAny <NefsProgress>())) .ReturnsAsync(archive); this.fileSystem.AddFile(archivePath, new MockFileData("hi")); await w.OpenArchiveAsync(archivePath); Assert.Same(archive, w.Archive); Assert.False(w.ArchiveIsModified); // Modify archvie var itemId = new NefsItemId(0); var item = w.Archive.Items.GetItems(itemId).First(); var cmd = new RemoveFileCommand(item, item.State); w.Execute(cmd); // Mock user choosing NOT to save before closing this.MockMessageBox(DialogResult.No); // Close the archive var result = await w.CloseArchiveAsync(); Assert.True(result); Assert.True(closed); Assert.Null(w.Archive); Assert.False(w.ArchiveIsModified); Assert.Null(w.ArchiveSource); // Verify not saved this.nefsWriterMock.Verify( n => n.WriteArchiveAsync( It.IsAny <string>(), It.IsAny <NefsArchive>(), It.IsAny <NefsProgress>()), Times.Never()); }
/// <summary> /// Reads header part 6 from an input stream. /// </summary> /// <param name="stream">The stream to read from.</param> /// <param name="offset">The offset to the header part from the beginning of the stream.</param> /// <param name="size">The size of the header part.</param> /// <param name="part2"> /// Header part 2. This is used to lookup item ids since part 6 metadata does not store item ids. /// </param> /// <param name="p">Progress info.</param> /// <returns>The loaded header part.</returns> internal async Task <NefsHeaderPart6> ReadHeaderPart6Async(Stream stream, uint offset, uint size, NefsHeaderPart2 part2, NefsProgress p) { var entries = new List <NefsHeaderPart6Entry>(); var ids = new HashSet <NefsItemId>(); // Validate inputs if (!this.ValidateHeaderPartStream(stream, offset, size, "6")) { return(new NefsHeaderPart6(entries)); } // Get entries in part 6 var numEntries = size / NefsHeaderPart6Entry.Size; var entryOffset = offset; for (var i = 0; i < numEntries; ++i) { using (p.BeginTask(1.0f / numEntries)) { // Make sure there is a corresponding index in part 2 if (i >= part2.EntriesByIndex.Count) { Log.LogError($"Could not find matching item entry for part 6 index {i} in part 2."); continue; } // Check for duplicate item ids var id = new NefsItemId(part2.EntriesByIndex[i].Id.Value); if (ids.Contains(id)) { Log.LogError($"Found duplicate item id in part 6: {id.Value}"); continue; } var entry = new NefsHeaderPart6Entry(id); await FileData.ReadDataAsync(stream, entryOffset, entry, p); ids.Add(id); entries.Add(entry); entryOffset += NefsHeaderPart6Entry.Size; } } return(new NefsHeaderPart6(entries)); }
public async Task SaveArchiveAsync_SaveFailed_NotSaved() { var w = this.CreateWorkspace(); var saved = false; w.ArchiveSaved += (o, e) => saved = true; // Open an archive var archivePath = @"C:\archive.nefs"; var archive = TestHelpers.CreateTestArchive(archivePath); this.nefsReaderMock.Setup(r => r.ReadArchiveAsync(It.IsAny <NefsArchiveSource>(), It.IsAny <NefsProgress>())) .ReturnsAsync(archive); this.fileSystem.AddFile(archivePath, new MockFileData("hi")); await w.OpenArchiveAsync(archivePath); Assert.Same(archive, w.Archive); Assert.False(w.ArchiveIsModified); // Modify archvie var itemId = new NefsItemId(0); var item = w.Archive.Items.GetItems(itemId).First(); var cmd = new RemoveFileCommand(item, item.State); w.Execute(cmd); // Mock save falied this.nefsWriterMock.Setup(n => n.WriteArchiveAsync( It.IsAny <string>(), It.IsAny <NefsArchive>(), It.IsAny <NefsProgress>())) .ThrowsAsync(new IOException()); // Save archive var result = await w.SaveArchiveAsync(archivePath); Assert.False(result); Assert.False(saved); Assert.Same(archive, w.Archive); Assert.True(w.ArchiveIsModified); Assert.Equal(archivePath, w.ArchiveSource.DataFilePath); Assert.Equal(archivePath, w.ArchiveSource.HeaderFilePath); }
public async Task CloseArchiveAsync_ArchiveModifiedAndUserCancels_ArchiveNotClosed() { var w = this.CreateWorkspace(); var closed = false; w.ArchiveClosed += (o, e) => closed = true; // Open an archive var archivePath = @"C:\archive.nefs"; var archive = TestHelpers.CreateTestArchive(archivePath); this.nefsReaderMock.Setup(r => r.ReadArchiveAsync(It.IsAny <NefsArchiveSource>(), It.IsAny <NefsProgress>())) .ReturnsAsync(archive); this.fileSystem.AddFile(archivePath, new MockFileData("hi")); await w.OpenArchiveAsync(archivePath); Assert.Same(archive, w.Archive); Assert.False(w.ArchiveIsModified); // Modify archvie var itemId = new NefsItemId(0); var item = w.Archive.Items.GetItems(itemId).First(); var cmd = new RemoveFileCommand(item, item.State); w.Execute(cmd); // Mock user cancelling the request to save before closing this.MockMessageBox(DialogResult.Cancel); // Close the archive var result = await w.CloseArchiveAsync(); Assert.False(result); Assert.False(closed); Assert.Same(archive, w.Archive); Assert.True(w.ArchiveIsModified); Assert.Equal(archivePath, w.ArchiveSource.DataFilePath); Assert.Equal(archivePath, w.ArchiveSource.HeaderFilePath); }
/// <summary> /// Reads header part 2 from an input stream. /// </summary> /// <param name="stream">The stream to read from.</param> /// <param name="offset">The offset to the header part from the beginning of the stream.</param> /// <param name="size">The size of the header part.</param> /// <param name="p">Progress info.</param> /// <returns>The loaded header part.</returns> internal async Task <NefsHeaderPart2> ReadHeaderPart2Async(Stream stream, uint offset, uint size, NefsProgress p) { var entries = new List <NefsHeaderPart2Entry>(); var ids = new HashSet <NefsItemId>(); // Validate inputs if (!this.ValidateHeaderPartStream(stream, offset, size, "2")) { return(new NefsHeaderPart2(entries)); } // Get entries in part 2 var numEntries = size / NefsHeaderPart2Entry.Size; var entryOffset = offset; for (var i = 0; i < numEntries; ++i) { using (p.BeginTask(1.0f / numEntries)) { var entry = new NefsHeaderPart2Entry(); await FileData.ReadDataAsync(stream, entryOffset, entry, p); // Check for duplicate item ids var id = new NefsItemId(entry.Id.Value); if (ids.Contains(id)) { Log.LogError($"Found duplicate item id in part 2: {id.Value}"); continue; } ids.Add(id); entries.Add(entry); entryOffset += NefsHeaderPart2Entry.Size; } } return(new NefsHeaderPart2(entries)); }
public void Clone_ItemCloned() { var nefs = TestArchiveNotModified.Create(@"C:\archive.nefs"); var itemId = new NefsItemId(TestArchiveNotModified.File3ItemId); var item = NefsItem.CreateFromHeader(itemId, nefs.Header, nefs.Items); item.UpdateState(NefsItemState.Replaced); var clone = item.Clone() as NefsItem; Assert.Equal(item.CompressedSize, clone.CompressedSize); Assert.Same(item.DataSource, clone.DataSource); Assert.Equal(item.DirectoryId, clone.DirectoryId); Assert.Equal(item.ExtractedSize, clone.ExtractedSize); Assert.Equal(item.FileName, clone.FileName); Assert.Equal(item.Id, clone.Id); Assert.Equal(item.Part6Unknown0x00, clone.Part6Unknown0x00); Assert.Equal(item.Part6Unknown0x01, clone.Part6Unknown0x01); Assert.Equal(item.Part6Unknown0x02, clone.Part6Unknown0x02); Assert.Equal(item.Part6Unknown0x03, clone.Part6Unknown0x03); Assert.Equal(item.State, clone.State); Assert.Equal(item.Type, clone.Type); }
private NefsItemList CreateItems(string dataFilePath, NefsHeader h) { var items = new NefsItemList(dataFilePath); var numItems = h.TableOfContents.Part1Size / NefsHeaderPart1Entry.Size; for (var i = 0; i < numItems; ++i) { // Create the item var id = new NefsItemId((uint)i); try { var item = NefsItem.CreateFromHeader(id, h, items); items.Add(item); } catch (Exception) { Log.LogError($"Failed to create item {id}, skipping."); } } return(items); }
/// <summary> /// Gets the file name of an item. /// </summary> /// <param name="id">The item id.</param> /// <returns>The item's file name.</returns> public string GetItemFileName(NefsItemId id) { var offsetIntoPart3 = this.Part2.EntriesById[id].Data0x08_OffsetIntoPart3.Value; return(this.Part3.FileNamesByOffset[offsetIntoPart3]); }
/// <summary> /// Gets the directory id for an item. If the item is in the root directory, the directory /// id will equal the item's id. /// </summary> /// <param name="id">The item id.</param> /// <returns>The directory id.</returns> public NefsItemId GetItemDirectoryId(NefsItemId id) { return(new NefsItemId(this.Part2.EntriesById[id].Data0x00_DirectoryId.Value)); }
public void NefsHeaderPart7_MultipleItems_EntriesPopulated() { var items = new NefsItemList(@"C:\archive.nefs"); var file1Id = new NefsItemId(31); var file1DataSource = new NefsItemListDataSource(items, 123, new NefsItemSize(456, new List <UInt32> { 11, 12, 13 })); var file1UnknownData = new NefsItemUnknownData { Part6Unknown0x00 = 1, Part6Unknown0x01 = 2, Part6Unknown0x02 = 3, Part6Unknown0x03 = 4, }; var file1 = new NefsItem(file1Id, "file1", file1Id, NefsItemType.File, file1DataSource, file1UnknownData); items.Add(file1); var file2Id = new NefsItemId(41); var file2DataSource = new NefsItemListDataSource(items, 456, new NefsItemSize(789, new List <UInt32> { 14, 15, 16 })); var file2UnknownData = new NefsItemUnknownData { Part6Unknown0x00 = 7, Part6Unknown0x01 = 8, Part6Unknown0x02 = 9, Part6Unknown0x03 = 10, }; var file2 = new NefsItem(file2Id, "file2", file2Id, NefsItemType.File, file2DataSource, file2UnknownData); items.Add(file2); var dir2Id = new NefsItemId(51); var dir1DataSource = new NefsEmptyDataSource(); var dir1UnknownData = new NefsItemUnknownData { Part6Unknown0x00 = 13, Part6Unknown0x01 = 14, Part6Unknown0x02 = 15, Part6Unknown0x03 = 16, }; var dir1 = new NefsItem(dir2Id, "dir1", dir2Id, NefsItemType.Directory, dir1DataSource, dir1UnknownData); items.Add(dir1); var p7 = new NefsHeaderPart7(items); Assert.Equal(3, p7.EntriesById.Count); /* * file1 */ Assert.Equal(31U, p7.EntriesById[file1.Id].Id.Value); Assert.Equal(41U, p7.EntriesById[file1.Id].SiblingId.Value); /* * file2 */ Assert.Equal(41U, p7.EntriesById[file2.Id].Id.Value); Assert.Equal(51U, p7.EntriesById[file2.Id].SiblingId.Value); /* * dir1 */ Assert.Equal(51U, p7.EntriesById[dir1.Id].Id.Value); Assert.Equal(51U, p7.EntriesById[dir1.Id].SiblingId.Value); }
/// <summary> /// Initializes a new instance of the <see cref="NefsHeaderPart4Entry"/> class. /// </summary> /// <param name="id">The id of the item this entry is for.</param> internal NefsHeaderPart4Entry(NefsItemId id) { this.Id = id; }
/// <summary> /// Reads header part 4 from an input stream. /// </summary> /// <param name="stream">The stream to read from.</param> /// <param name="offset">The offset to the header part from the beginning of the stream.</param> /// <param name="size">The size of the header part.</param> /// <param name="part1">Header part 1.</param> /// <param name="part2">Header part 2.</param> /// <param name="p">Progress info.</param> /// <returns>The loaded header part.</returns> internal async Task <NefsHeaderPart4> ReadHeaderPart4Async( Stream stream, uint offset, uint size, NefsHeaderPart1 part1, NefsHeaderPart2 part2, NefsProgress p) { var entries = new Dictionary <uint, NefsHeaderPart4Entry>(); // Validate inputs if (!this.ValidateHeaderPartStream(stream, offset, size, "4")) { return(new NefsHeaderPart4(entries)); } // Get the chunk sizes for each item in the archive var numItems = part1.EntriesById.Count; for (var i = 0; i < numItems; ++i) { using (p.BeginTask(1.0f / numItems)) { var id = new NefsItemId((uint)i); // Part 1 entry if (!part1.EntriesById.ContainsKey(id)) { Log.LogError($"Failed to find part 1 entry for item {id} when reading part 4."); continue; } var p1 = part1.EntriesById[id]; // Part 2 entry if (!part2.EntriesById.ContainsKey(id)) { Log.LogError($"Failed to find part 2 entry for item {id} when reading part 4."); continue; } var p2 = part2.EntriesById[id]; // Create part 4 entry var entry = new NefsHeaderPart4Entry(id); // Check if item has part 4 entry if (p1.IndexIntoPart4 == 0xFFFFFFFF) { // Item is most likely not compressed or has no data continue; } if (p2.Data0x0c_ExtractedSize.Value == 0) { // Item is probably a directory continue; } // Get number of chunks var numChunks = (int)Math.Ceiling(p2.Data0x0c_ExtractedSize.Value / (double)NefsHeader.ChunkSize); if (numChunks == 0) { Log.LogError($"Item {p1.Id} contains no compressed chunks but was expected to."); continue; } // Seek stream to start of chunk sizes for this item var itemOffset = offset + p1.OffsetIntoPart4; if ((long)itemOffset + NefsHeaderPart4.DataSize > stream.Length) { Log.LogError($"Item {p1.Id} has part 4 entry that is outside the bounds of header part 4."); continue; } // Seek stream stream.Seek((long)itemOffset, SeekOrigin.Begin); // Process the chunk sizes for (var chunkIdx = 0; chunkIdx < numChunks; ++chunkIdx) { var bytes = new byte[NefsHeaderPart4.DataSize]; await stream.ReadAsync(bytes, 0, NefsHeaderPart4.DataSize); entry.ChunkSizes.Add(BitConverter.ToUInt32(bytes, 0)); } // Record entry entries.Add(p1.IndexIntoPart4, entry); } } // Return part 4 return(new NefsHeaderPart4(entries)); }
/// <summary> /// Initializes a new instance of the <see cref="NefsHeaderPart6Entry"/> class. /// </summary> /// <param name="id">The item id this entry is for.</param> public NefsHeaderPart6Entry(NefsItemId id) { this.Id = id; }