/// <summary> /// Creates an item for testing. /// </summary> /// <param name="id">The item id.</param> /// <param name="dirId">The directory id.</param> /// <param name="fileName">The item name.</param> /// <param name="dataOffset">Data offset.</param> /// <param name="extractedSize">Extracted size.</param> /// <param name="chunkSizes">Compressed chunks sizes.</param> /// <param name="type">The item type.</param> /// <returns>The new item.</returns> internal static NefsItem CreateItem( uint id, uint dirId, string fileName, UInt64 dataOffset, UInt32 extractedSize, IReadOnlyList <UInt32> chunkSizes, NefsItemType type) { var attributes = new NefsItemAttributes( isDirectory: type == NefsItemType.Directory, v16IsTransformed: true); var transform = TestTransform; var chunks = NefsDataChunk.CreateChunkList(chunkSizes, transform); var size = new NefsItemSize(extractedSize, chunks); var dataSource = new NefsFileDataSource(@"C:\source.txt", dataOffset, size, extractedSize != chunkSizes.LastOrDefault()); return(new NefsItem( Guid.NewGuid(), new NefsItemId(id), fileName, new NefsItemId(dirId), dataSource, transform, attributes)); }
/// <summary> /// Creates an item for testing. /// </summary> /// <param name="id">The item id.</param> /// <param name="dirId">The directory id.</param> /// <param name="fileName">The item name.</param> /// <param name="dataOffset">Data offset.</param> /// <param name="extractedSize">Extracted size.</param> /// <param name="chunkSizes">Compressed chunks sizes.</param> /// <param name="type">The item type.</param> /// <returns>The new item.</returns> internal static NefsItem CreateItem( uint id, uint dirId, string fileName, UInt64 dataOffset, UInt32 extractedSize, IReadOnlyList <UInt32> chunkSizes, NefsItemType type) { var size = new NefsItemSize(extractedSize, chunkSizes); var dataSource = new NefsFileDataSource(@"C:\source.txt", dataOffset, size, extractedSize != chunkSizes.LastOrDefault()); return(new NefsItem( new NefsItemId(id), fileName, new NefsItemId(dirId), type, dataSource, CreateUnknownData())); }
/// <summary> /// Initializes a new instance of the <see cref="NefsItem"/> class. /// </summary> /// <param name="id">The item id (index).</param> /// <param name="fileName">The file name within the archive.</param> /// <param name="directoryId">The directory id the item is in.</param> /// <param name="type">The type of item.</param> /// <param name="dataSource">The data source for the item's data.</param> /// <param name="unknownData">Unknown metadata.</param> /// <param name="state">The item state.</param> public NefsItem( NefsItemId id, string fileName, NefsItemId directoryId, NefsItemType type, INefsDataSource dataSource, NefsItemUnknownData unknownData, NefsItemState state = NefsItemState.None) { this.Id = id; this.DirectoryId = directoryId; this.Type = type; this.DataSource = dataSource ?? throw new ArgumentNullException(nameof(dataSource)); this.State = state; // Unknown data this.Part6Unknown0x00 = unknownData.Part6Unknown0x00; this.Part6Unknown0x01 = unknownData.Part6Unknown0x01; this.Part6Unknown0x02 = unknownData.Part6Unknown0x02; this.Part6Unknown0x03 = unknownData.Part6Unknown0x03; // Save file name this.FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); }
/// <summary> /// Loads a NeFS item with the specified id from the archive. /// </summary> /// <param name="file">The file stream to load from.</param> /// <param name="archive">The NeFS archive this item is in.</param> /// <param name="id">The id of the item in the archive to load.</param> public NefsItem(FileStream file, NefsArchive archive, UInt32 id) { /* Validate inputs */ if (file == null) { throw new ArgumentNullException("File stream required to load NeFS item."); } if (archive == null) { throw new ArgumentNullException("NeFS archive object required to load an item."); } _archive = archive; _id = id; /* Get header entries related to this item */ _pt1Entry = archive.Header.Part1.GetEntry(id); _pt2Entry = archive.Header.Part2.GetEntry(_pt1Entry); _pt5Entry = archive.Header.Part5.GetEntry(id); try { _pt6Entry = archive.Header.Part6.GetEntry(id); } catch (Exception ex) { try { /* Some items share a part 2 entry, so try to find the corresponding part 6 entry */ _pt6Entry = archive.Header.Part6.GetEntry(_pt2Entry.Id); } catch (Exception ex2) { // TODO : Handle when an item doesn't have a part 6 entry _pt6Entry = archive.Header.Part6.GetEntry(0); } } /* Determine item type */ _type = (_pt1Entry.OffsetToData == 0) ? NefsItemType.Directory : NefsItemType.File; /* Get the filename */ _fileName = archive.Header.Part3.GetFilename(_pt2Entry.FilenameOffset); /* Hash the filename */ _fileNameHash = FilePathHelper.HashStringMD5(_fileName); /* Get offsets */ _dataOffset = _pt1Entry.OffsetToData; _offsetIntoPt2Raw = _pt1Entry.OffsetIntoPt2Raw; _offsetIntoPt4Raw = _pt1Entry.OffsetIntoPt4Raw; /* Get extracted size */ _extractedSize = _pt2Entry.ExtractedSize; /* * Build the file path inside this archive * for example: "rootDir/childDir/file.xml". */ _filePathInArchive = _fileName; var currentItem = _pt2Entry; /* The root directory's id is equal to its parent directory id */ while (currentItem.Id != currentItem.DirectoryId) { var pt1Entry = archive.Header.Part1.GetEntry(currentItem.DirectoryId); var dir = archive.Header.Part2.GetEntry(pt1Entry); var dirName = archive.Header.Part3.GetFilename(dir.FilenameOffset); _filePathInArchive = Path.Combine(dirName, _filePathInArchive); currentItem = dir; } /* Hash the file path in archive */ _filePathInArchiveHash = FilePathHelper.HashStringMD5(_filePathInArchive); // // Get the compressed file chunk offsets // if (_pt1Entry.OffsetIntoPt4Raw == 0xFFFFFFFF) { // TODO : Not sure exactly what this value means yet // For now, just set compressed size as extracted size with not compressed chunk sizes _compressedSize = ExtractedSize; } else { var numChunks = (UInt32)Math.Ceiling(ExtractedSize / (double)CHUNK_SIZE); if (numChunks > 0) { var firstChunkSizeEntry = _archive.Header.Part4.Offset + _pt1Entry.OffsetIntoPt4; UInt32Type chunkOffset; for (int i = 0; i < numChunks; i++) { chunkOffset = new UInt32Type(i * 4); chunkOffset.Read(file, firstChunkSizeEntry); _chunkSizes.Add(chunkOffset.Value); } _compressedSize = _chunkSizes.Last(); } } }