private Node ProcessDirectory( uint directoryIndex, DataReader reader, ParIndex index, ref bool[] processedDirectories) { if (processedDirectories[directoryIndex]) { return(null); } _ = reader.Stream.Seek(0x20 + (directoryIndex * 0x40), System.IO.SeekOrigin.Begin); string name = reader.ReadString(0x40).TrimEnd('\0'); if (string.IsNullOrEmpty(name)) { name = "."; } _ = reader.Stream.Seek(index.DirectoryStartOffset + (directoryIndex * 0x20), System.IO.SeekOrigin.Begin); var directoryInfo = reader.Read <ParDirectoryInfo>() as ParDirectoryInfo; var directory = new Node(name, new NodeContainerFormat()) { Tags = { ["SubdirectoryCount"] = directoryInfo.SubdirectoryCount, ["SubdirectoryStartIndex"] = directoryInfo.SubdirectoryStartIndex, ["FileCount"] = directoryInfo.FileCount, ["FileStartIndex"] = directoryInfo.FileStartIndex, ["RawAttributes"] = directoryInfo.RawAttributes, }, }; for (uint i = directoryInfo.SubdirectoryStartIndex; i < directoryInfo.SubdirectoryStartIndex + directoryInfo.SubdirectoryCount; i++) { Node child = ProcessDirectory(i, reader, index, ref processedDirectories); if (child != null) { directory.Add(child); } } for (uint i = directoryInfo.FileStartIndex; i < directoryInfo.FileStartIndex + directoryInfo.FileCount; i++) { _ = reader.Stream.Seek(0x20 + (0x40 * index.DirectoryCount) + (i * 0x40), System.IO.SeekOrigin.Begin); string fileName = reader.ReadString(0x40).TrimEnd('\0'); _ = reader.Stream.Seek(index.FileStartOffset + (i * 0x20), System.IO.SeekOrigin.Begin); var fileInfo = reader.Read <ParFileInfo>() as ParFileInfo; long offset = ((long)fileInfo.ExtendedOffset << 32) | fileInfo.DataOffset; DataStream stream = DataStreamFactory.FromStream(reader.Stream, offset, fileInfo.CompressedSize); var binaryFormat = new ParFile(fileInfo, stream); var file = new Node(fileName, binaryFormat) { Tags = { ["RawAttributes"] = fileInfo.RawAttributes, ["Timestamp"] = fileInfo.Timestamp, }, }; directory.Add(file); } processedDirectories[directoryIndex] = true; return(directory); }
/// <summary> /// Converts a NodeContainerFormat into a BinaryFormat. /// </summary> /// <param name="source">Input format.</param> /// <returns>The node container format.</returns> /// <exception cref="ArgumentNullException">Thrown if source is null.</exception> public virtual BinaryFormat Convert(NodeContainerFormat source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } // Reorder nodes source.Root.SortChildren((x, y) => string.CompareOrdinal(x.Name.ToLowerInvariant(), y.Name.ToLowerInvariant())); // Fill node indexes FillNodeIndexes(source.Root); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); DataStream stream = _writerParameters.OutputStream ?? DataStreamFactory.FromMemory(); stream.Position = 0; var writer = new DataWriter(stream) { DefaultEncoding = Encoding.GetEncoding(1252), Endianness = _writerParameters.Endianness == Endianness.LittleEndian ? EndiannessMode.LittleEndian : EndiannessMode.BigEndian, }; var directories = new List <Node>(); var files = new List <Node>(); uint fileOffset = 0; uint maxFileSize = 0; foreach (Node node in Navigator.IterateNodes(source.Root)) { if (!node.IsContainer) { uint compressedSize = (uint)node.Stream.Length; fileOffset = RoundSize(fileOffset, compressedSize); if (compressedSize > maxFileSize) { maxFileSize = compressedSize; } files.Add(node); } else { directories.Add(node); } } if (maxFileSize >= 0xFFFFFFFF) { throw new FormatException("Can not add files over 4GB"); } var header = new FileHeader { Magic = "PARC", PlatformId = _writerParameters.PlatformId, Endianness = _writerParameters.Endianness, SizeExtended = 0, Relocated = 0, Version = _writerParameters.Version, Size = 0, }; uint directoryStartOffset = (uint)(0x20 + (0x40 * (directories.Count + files.Count))); uint fileStartOffset = (uint)(directoryStartOffset + (0x20 * directories.Count)); var index = new ParIndex { DirectoryCount = (uint)directories.Count, DirectoryStartOffset = directoryStartOffset, FileCount = (uint)files.Count, FileStartOffset = fileStartOffset, }; uint headerSize = RoundSize((uint)((0x20 * files.Count) + fileStartOffset)); writer.Stream.SetLength(RoundSize(fileOffset + headerSize)); uint currentOffset = headerSize; if (_writerParameters.WriteDataSize) { header.Size = headerSize + fileOffset; } writer.WriteOfType(header); writer.WriteOfType(index); for (int i = 0; i < directories.Count; i++) { Node node = directories[i]; writer.Write(node.Name, 0x40, false); long returnPosition = writer.Stream.Position; _ = writer.Stream.Seek(directoryStartOffset + (i * 0x20), System.IO.SeekOrigin.Begin); var directoryInfo = new ParDirectoryInfo { SubdirectoryCount = (uint)node.Tags["SubdirectoryCount"], SubdirectoryStartIndex = (uint)node.Tags["SubdirectoryStartIndex"], FileCount = (uint)node.Tags["FileCount"], FileStartIndex = (uint)node.Tags["FileStartIndex"], RawAttributes = (uint)node.Tags["RawAttributes"], }; writer.WriteOfType(directoryInfo); writer.WritePadding(0x00, 0x20); _ = writer.Stream.Seek(returnPosition, System.IO.SeekOrigin.Begin); } for (int i = 0; i < files.Count; i++) { Node node = files[i]; writer.Write(node.Name, 0x40, false); long returnPosition = writer.Stream.Position; _ = writer.Stream.Seek(fileStartOffset + (i * 0x20), System.IO.SeekOrigin.Begin); ParFile file = node.GetFormatAs <ParFile>(); currentOffset = RoundOffset(currentOffset, file.FileInfo.CompressedSize); file.FileInfo.DataOffset = currentOffset; file.FileInfo.ExtendedOffset = 0; if (node.Tags.ContainsKey("RawAttributes")) { file.FileInfo.RawAttributes = node.Tags["RawAttributes"]; } else if (node.Tags.ContainsKey("FileInfo")) { FileInfo info = node.Tags["FileInfo"]; file.FileInfo.RawAttributes = (uint)info.Attributes; } else { file.FileInfo.RawAttributes = 0x20; } if (node.Tags.ContainsKey("Timestamp")) { file.FileInfo.Timestamp = node.Tags["Timestamp"]; } else if (node.Tags.ContainsKey("FileInfo")) { DateTime baseDate = new DateTime(1970, 1, 1); FileInfo info = node.Tags["FileInfo"]; file.FileInfo.Timestamp = (uint)(info.LastWriteTime - baseDate).TotalSeconds; } else { DateTime baseDate = new DateTime(1970, 1, 1); file.FileInfo.Timestamp = (uint)(DateTime.Now - baseDate).TotalSeconds; } writer.WriteOfType(file.FileInfo); _ = writer.Stream.Seek(currentOffset, SeekOrigin.Begin); node.Stream.WriteTo(writer.Stream); currentOffset += file.FileInfo.CompressedSize; _ = writer.Stream.Seek(returnPosition, System.IO.SeekOrigin.Begin); } return(new BinaryFormat(stream)); }