Beispiel #1
0
        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);
        }
Beispiel #2
0
        /// <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));
        }