/// <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); }
/// <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."); } }
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); } }
/// <summary> /// Create a new ArcContainer from the contents of the given directory. /// </summary> /// <param name="inputDir">The path of the directory from which the files should be added.</param> public ArcContainer(string inputDir) { Root = new ArcFileSystemDirectory("", inputDir); }
/// <summary> /// Create a new empty ArcContainer. /// </summary> public ArcContainer() { Root = new ArcFileSystemDirectory(""); }