Пример #1
0
        /// <summary>
        /// Generates a ArcFileSystemEntry from a U8Node.
        /// </summary>
        /// <param name="inputBinaryStream">The input stream containing the ARC container.</param>
        /// <param name="stringTableBase">Offset in the ARC of the string table.</param>
        /// <param name="nodes">Array of all U8Nodes in the ARC.</param>
        /// <param name="nodeIdx">
        /// The index of the node to parse.
        /// This will be updated to the index of the next node in the directory.
        /// </param>
        /// <returns>The ArcFileSystemEntry corresponding to the U8Node.</returns>
        private ArcFileSystemEntry ParseNode(EndianBinaryReader inputBinaryStream, long stringTableBase,
                                             U8Node[] nodes, ref int nodeIdx)
        {
            U8Node currentNode = nodes[nodeIdx++];

            inputBinaryStream.BaseStream.Position = stringTableBase + currentNode.NameOffset;
            string name = inputBinaryStream.ReadAsciiString();

            if (currentNode.Type == U8Node.TYPE_FILE)
            {
                inputBinaryStream.BaseStream.Position = currentNode.DataOffset;
                byte[] data = inputBinaryStream.ReadBytesOrThrow(currentNode.Size);
                return(new ArcFileSystemFile(name, data));
            }
            else if (currentNode.Type == U8Node.TYPE_DIRECTORY)
            {
                ArcFileSystemDirectory dir = new ArcFileSystemDirectory(name);

                /* n.Size contains the index of the end entry in the directory.
                * Since parseNode() updates nodeIdx to the next entry in the directory,
                * we should entries until we reach the end of the directory. */
                while (nodeIdx != currentNode.Size)
                {
                    dir.Entries.Add(ParseNode(inputBinaryStream, stringTableBase, nodes, ref nodeIdx));
                }

                return(dir);
            }
            else
            {
                throw new InvalidArcFileException("Invalid node Type in ARC container.");
            }
        }
Пример #2
0
        public static U8Node Build(string input_directory)
        {
            var node = new U8Node();

            Pack(input_directory, node);
            return(node);
        }
Пример #3
0
        /// <summary>
        /// Create a new ArcContainer from the given .arc file.
        /// </summary>
        /// <param name="inputStream">Stream containing the .arc file.</param>
        public ArcContainer(Stream inputStream)
        {
            EndianBinaryReader inputBinaryStream = new EndianBinaryReader(EndianBitConverter.Big, inputStream);

            if (inputBinaryStream.ReadUInt32() != ArcMagic)
            {
                throw new InvalidArcFileException("Invalid magic number.");
            }
            int rootNodeOffset          = inputBinaryStream.ReadInt32();
            int nodesAndStringTableSize = inputBinaryStream.ReadInt32();
            int dataOffset = inputBinaryStream.ReadInt32();

            // Hackily get all the nodes and the offset of the string table
            inputStream.Position = rootNodeOffset;

            U8Node rootNode = U8Node.Read(inputBinaryStream);

            U8Node[] nodes = new U8Node[rootNode.Size];
            nodes[0] = rootNode;
            for (int i = 1; i < rootNode.Size; i++)
            {
                nodes[i] = U8Node.Read(inputBinaryStream);
            }

            long stringTableBase = inputStream.Position;

            int nodeIdx = 0;

            Root = (ArcFileSystemDirectory)ParseNode(inputBinaryStream, stringTableBase, nodes, ref nodeIdx);
        }
Пример #4
0
        /// <summary>
        /// Create a directory node.
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parentNode"></param>
        /// <param name="childCount"></param>        /// <returns></returns>
        private U8Node createDirNode(string dirName, uint parentNode)
        {
            U8Node dirNode = new U8Node();

            dirNode.type_name       = 0x01000000 | (uint)_stringTable.Count;
            dirNode.data_offset     = parentNode;
            dirNode.compressed_size = 0;    // initialized later
            dirNode.file_size       = 0;

            // Append the name to the string table.
            string name = Path.GetFileName(dirName);

            byte[] utfBytes = Encoding.UTF8.GetBytes(name);
            _stringTable.AddRange(utfBytes);
            _stringTable.Add(0);

            _nodes.Add(dirNode);
            dirNode.node_idx = (uint)_nodes.Count - 1;
            uint nodeIdxPlusOne = dirNode.node_idx + 1;

            // Update the "last child index" value of all parent nodes.
            if (_nodes.Count > 1)
            {
                U8Node nextParent = _nodes[(int)parentNode];
                U8Node curParent;
                do
                {
                    curParent = nextParent;
                    curParent.compressed_size = nodeIdxPlusOne;
                    nextParent = _nodes[(int)curParent.data_offset];
                } while (nextParent != curParent);
            }

            return(dirNode);
        }
Пример #5
0
        /// <summary>
        /// Create a file node.
        /// </summary>
        /// <param name="parentNode">Parent node.</param>
        /// <param name="srcFilename">Source filename.</param>
        /// <returns></returns>
        private U8Node createFileNode(U8Node parentNode, string srcFilename)
        {
            U8Node fileNode = new U8Node();

            fileNode.type_name = (uint)_stringTable.Count;
            // These are initialized later.
            fileNode.data_offset     = 0;
            fileNode.compressed_size = 0;
            fileNode.file_size       = 0;
            fileNode.srcFilename     = srcFilename;

            // Append the name to the string table.
            string name = Path.GetFileName(srcFilename);

            byte[] utfBytes = Encoding.UTF8.GetBytes(name);
            _stringTable.AddRange(utfBytes);
            _stringTable.Add(0);

            _nodes.Add(fileNode);
            fileNode.node_idx = (uint)_nodes.Count - 1;
            uint nodeIdxPlusOne = fileNode.node_idx + 1;

            // Update the "last child index" value of all parent nodes.
            U8Node nextParent = parentNode;
            U8Node curParent;

            do
            {
                curParent = nextParent;
                curParent.compressed_size = nodeIdxPlusOne;
                nextParent = _nodes[(int)curParent.data_offset];
            } while (nextParent != curParent);

            return(fileNode);
        }
Пример #6
0
        private U8Directory ReadFileTable(uint nodeOffset)
        {
            U8Directory root = new U8Directory(null, ROOT_DIR);

            filestream.Position = nodeOffset;
            U8Node rootNode = new U8Node()
            {
                type       = filestream.ReadUInt8(),
                nameOffset = filestream.ReadUInt24BE(),
                dataOffset = filestream.ReadUInt32BE(),
                size       = filestream.ReadUInt32BE()
            };

            if (rootNode.type != DIR)
            {
                throw new InvalidDataException("Root node of U8 archive was not a directory");
            }

            var stringTableOffset = nodeOffset + 12 * rootNode.size;
            var lastNodes         = new Stack <uint>();

            lastNodes.Push(rootNode.size);
            U8Directory currentDir = root;

            for (var i = 1; i < rootNode.size; i++)
            {
                if (i == lastNodes.Peek())
                {
                    lastNodes.Pop();
                    currentDir = currentDir.Parent as U8Directory;
                }

                var node = new U8Node()
                {
                    type       = filestream.ReadUInt8(),
                    nameOffset = filestream.ReadUInt24BE(),
                    dataOffset = filestream.ReadUInt32BE(),
                    size       = filestream.ReadUInt32BE()
                };
                var pos = filestream.Position;
                filestream.Position = stringTableOffset + node.nameOffset;
                var name = filestream.ReadASCIINullTerminated();
                filestream.Position = pos;
                if (node.type == DIR)
                {
                    var newDir = new U8Directory(currentDir, name);
                    currentDir.AddDir(newDir);
                    currentDir = newDir;
                    lastNodes.Push(node.size);
                }
                else
                {
                    currentDir.AddFile(new OffsetFile(name, currentDir, filestream, node.dataOffset, node.size));
                }
            }
            return(root);
        }
Пример #7
0
            public int Size;       // For directories, the index of the end node.

            public static U8Node Read(EndianBinaryReader s)
            {
                U8Node node = new U8Node();
                uint   tmp  = s.ReadUInt32();

                node.Type       = (byte)(tmp >> 24);
                node.NameOffset = (int)(tmp & 0x00FFFFFF);
                node.DataOffset = s.ReadInt32();
                node.Size       = s.ReadInt32();
                return(node);
            }
Пример #8
0
        public void ImportU8()
        {
            if (Program.OpenFile(FileFilters.U8Import, out string path) > 0)
            {
                U8Node       node = NodeFactory.FromFile(null, path) as U8Node;
                U8FolderNode n    = new U8FolderNode();
                foreach (ResourceNode r in node.Children)
                {
                    n.AddChild(r);
                }

                n.Name = node.Name;
                ((U8FolderNode)_resource).AddChild(n);

                BaseWrapper w = FindResource(n, true);
                w.EnsureVisible();
                w.TreeView.SelectedNode = w;
            }
        }
Пример #9
0
        /// <summary>
        /// Recursively add files and subdirectories from the specified source directory.
        /// </summary>
        /// <param name="parentNode">Parent node.</param>
        /// <param name="srcDirectory">Source directory.</param>
        protected void addSubdirNodes(U8Node parentNode, string srcDirectory)
        {
            // Get files in the specified directory, non-recursive.
            string[] files = Directory.GetFiles(srcDirectory, "*", SearchOption.TopDirectoryOnly);
            Array.Sort(files);
            foreach (string file in files)
            {
                createFileNode(parentNode, file);
            }

            // Get subdirectories in this directory.
            string[] subdirs = Directory.GetDirectories(srcDirectory, "*", SearchOption.TopDirectoryOnly);
            Array.Sort(subdirs);
            foreach (string subdir in subdirs)
            {
                U8Node dirNode = createDirNode(subdir, parentNode.node_idx);
                addSubdirNodes(dirNode, subdir);
            }
        }
Пример #10
0
        private void GenerateNodes(ArcFileSystemEntry currentEntry, List <U8Node> nodes, List <ArcFileSystemEntry> entries,
                                   ref int currentNodeId, int parentNodeId, ref int currentStringTableOffset, ref int currentDataOffset)
        {
            // Add the node and the entry to the enumeration
            U8Node node = new U8Node();

            nodes.Add(node);
            entries.Add(currentEntry);

            int thisNodeId = currentNodeId;

            currentNodeId++;

            // Reserve space for the name in the string table
            node.NameOffset           = currentStringTableOffset;
            currentStringTableOffset += currentEntry.Name.Length + 1;

            if (currentEntry is ArcFileSystemDirectory)
            {
                ArcFileSystemDirectory currentDirectory = (ArcFileSystemDirectory)currentEntry;

                foreach (ArcFileSystemEntry subEntry in currentDirectory.Entries)
                {
                    GenerateNodes(subEntry, nodes, entries, ref currentNodeId, thisNodeId, ref currentStringTableOffset, ref currentDataOffset);
                }

                node.Type       = U8Node.TYPE_DIRECTORY;
                node.DataOffset = parentNodeId;
                node.Size       = currentNodeId;
            }
            else if (currentEntry is ArcFileSystemFile)
            {
                ArcFileSystemFile currentFile = (ArcFileSystemFile)currentEntry;

                node.Type       = U8Node.TYPE_FILE;
                node.DataOffset = currentDataOffset;
                node.Size       = currentFile.Data.Length;

                currentDataOffset = PaddingUtils.Align(currentDataOffset + currentFile.Data.Length, 0x20);
            }
        }
Пример #11
0
        public void ImportU8()
        {
            string path;

            if (Program.OpenFile("U8 Archive (*.arc)|*.arc|" +
                                 "Compressed U8 Archive (*.szs)|*.szs|" +
                                 "Archive Pair (*.pair)|*.pair", out path) > 0)
            {
                U8Node       node = NodeFactory.FromFile(null, path) as U8Node;
                U8FolderNode n    = new U8FolderNode();
                foreach (ResourceNode r in node.Children)
                {
                    n.AddChild(r);
                }
                n.Name = node.Name;
                ((U8FolderNode)_resource).AddChild(n);

                BaseWrapper w = this.FindResource(n, true);
                w.EnsureVisible();
                w.TreeView.SelectedNode = w;
            }
        }
Пример #12
0
        /// <summary>
        /// Write the ARC file using the specified file list.
        /// </summary>
        /// <param name="arcFile">ARC file.</param>
        /// <param name="srcDirectory">Source directory.</param>
        public void WriteArc(string arcFile, string srcDirectory)
        {
            // Standard U8 header
            // To be filled in:
            // - $0008 (BE32): Total size of node table and string table.
            // - $000C (BE32): Start of data.
            byte[] U8Header = new byte[32]
            {
                0x55, 0xAA, 0x38, 0x2D, 0x00, 0x00, 0x00, 0x20,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0xE4, 0xF9, 0x12, 0x00, 0x00, 0x00, 0x04, 0x02,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
            };

            // Create the root node.
            _nodes.Clear();
            _stringTable.Clear();
            U8Node rootNode = createDirNode("", 0);

            // Add nodes recursively.
            addSubdirNodes(rootNode, srcDirectory);

            // Update the U8 header for the string table length
            // and data offset.
            int tableLength = (_nodes.Count * 16) + _stringTable.Count;
            int dataOffset  = U8Header.Length + tableLength;

            dataOffset = ((dataOffset + 0x1F) & ~0x1F);

            byte[] b_tableLength = BitConverter.GetBytes((uint)tableLength);
            byte[] b_dataOffset  = BitConverter.GetBytes((uint)dataOffset);
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(b_tableLength);
                Array.Reverse(b_dataOffset);
            }
            Array.Copy(b_tableLength, 0, U8Header, 0x08, 4);
            Array.Copy(b_dataOffset, 0, U8Header, 0x0C, 4);

            // Write stuff.
            using (FileStream fs = File.Create(arcFile))
            {
                // Write files, uncompressed, starting at the data offset.
                // NOTE: Files are aligned on 32-byte offsets.
                fs.Seek(dataOffset, SeekOrigin.Begin);

                foreach (U8Node node in _nodes)
                {
                    if ((node.type_name & 0xFF000000) != 0)
                    {
                        // Directory node.
                        continue;
                    }

                    // Open the file.
                    using (FileStream fs_src = File.OpenRead(node.srcFilename))
                    {
                        node.data_offset = (uint)fs.Position;
                        node.file_size   = (uint)fs_src.Length;

                        // Compress the source data using zlib.
                        using (MemoryStream memStream = new MemoryStream())
                        {
                            using (ZlibStream zStream = new ZlibStream(memStream, CompressionLevel.Fastest, true))
                            {
                                fs_src.CopyTo(zStream);
                            }

                            node.compressed_size = (uint)memStream.Length;
                            if (memStream.Length > 0)
                            {
                                // Write the compressed data.
                                memStream.Seek(0, SeekOrigin.Begin);
                                memStream.CopyTo(fs);
                            }
                        }

                        // Make sure we're aligned to 32 bytes.
                        alignFileStreamTo32Bytes(fs);
                    }
                }

                // Write the U8 header.
                fs.Seek(0, SeekOrigin.Begin);
                fs.Write(U8Header, 0, U8Header.Length);

                // Write the nodes.
                foreach (U8Node node in _nodes)
                {
                    byte[] b_type_name       = BitConverter.GetBytes(node.type_name);
                    byte[] b_data_offset     = BitConverter.GetBytes(node.data_offset);
                    byte[] b_compressed_size = BitConverter.GetBytes(node.compressed_size);
                    byte[] b_file_size       = BitConverter.GetBytes(node.file_size);

                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(b_type_name);
                        Array.Reverse(b_data_offset);
                        Array.Reverse(b_compressed_size);
                        Array.Reverse(b_file_size);
                    }

                    fs.Write(b_type_name, 0, b_type_name.Length);
                    fs.Write(b_data_offset, 0, b_data_offset.Length);
                    fs.Write(b_compressed_size, 0, b_compressed_size.Length);
                    fs.Write(b_file_size, 0, b_file_size.Length);
                }

                // Write the string table.
                fs.Write(_stringTable.ToArray(), 0, _stringTable.Count);

                fs.Flush();
                fs.Close();
            }
        }