private FileEntry GetFileEntry_( EndianBinaryReader er, RarcHeader header, RarcNode node, int i) { er.Position = 0x20 + header.fileEntriesOffset + node.firstFileEntryOffset + i * 20; var fileEntry = new FileEntry(); fileEntry.id = er.ReadUInt16(); fileEntry.unknown = er.ReadUInt16(); fileEntry.unknown2 = er.ReadUInt16(); fileEntry.filenameOffset = er.ReadUInt16(); fileEntry.dataOffset = er.ReadUInt32(); fileEntry.dataSize = er.ReadUInt32(); fileEntry.zero = er.ReadUInt32(); return(fileEntry); }
private RarcNode GetNode_(EndianBinaryReader er, RarcHeader h, int i) { var node = new RarcNode(); node.type = er.ReadString(Encoding.UTF8, 4); var fileNameOffset = er.ReadUInt32(); var expectedFileNameHash = er.ReadUInt16(); node.numFileEntries = er.ReadUInt16(); node.firstFileEntryOffset = er.ReadUInt32(); var position = er.Position; { er.Position = 0x20 + h.stringTableOffset + fileNameOffset; node.fileName = er.ReadStringNT(Encoding.UTF8); var actualFileNameHash = 0; foreach (var c in node.fileName) { actualFileNameHash *= 3; actualFileNameHash += (byte)c; } Asserts.Equal(expectedFileNameHash, actualFileNameHash, "Node did not have the correct hash!"); } { } er.Position = position; return(node); }
private bool ReadFile_(IFileHierarchyFile rarcFile) { using var er = new EndianBinaryReader(rarcFile.Impl.OpenRead()); var header = new RarcHeader(); header.type = er.ReadString(Encoding.ASCII, 4); if (header.type != "RARC") { return(false); } header.size = er.ReadUInt32(); header.unknown = er.ReadUInt32(); header.dataStartOffset = er.ReadUInt32(); er.ReadUInt32s(header.unknown2); header.numNodes = er.ReadUInt32(); header.firstNodeOffset = er.ReadUInt32(); header.numDirectories = er.ReadUInt32(); header.fileEntriesOffset = er.ReadUInt32(); header.stringTableLength = er.ReadUInt32(); header.stringTableOffset = er.ReadUInt32(); er.ReadUInt32s(header.unknown5); var cwd = Directory.GetCurrentDirectory(); var directoryPath = rarcFile.FullName + "_dir"; { var nodes = new RarcNode[header.numNodes]; for (var i = 0; i < header.numNodes; ++i) { nodes[i] = this.GetNode_(er, header, i); } ; foreach (var node in nodes) { Directory.SetCurrentDirectory(directoryPath); this.DumpNode_(er, node, header); } } Directory.SetCurrentDirectory(cwd); return(true); }
FileEntry getFileEntry(int i, RarcHeader h, BinaryReader f) { f.BaseStream.Seek(h.fileEntriesOffset + (i * sizeoffe) + 0x20, 0); FileEntry ret; ret.id = toWORD(f.ReadUInt16()); f.ReadUInt16(); // unknown f.ReadUInt16(); // unknown2 ret.filenameOffset = toWORD(f.ReadUInt16()); ret.dataOffset = toDWORD(f.ReadUInt32()); ret.dataSize = toDWORD(f.ReadUInt32()); ret.zero = toDWORD(f.ReadUInt32()); return(ret); }
void dumpNode(Node n, RarcHeader h, BinaryReader f, String path) { string nodeName = getString((int)(n.filenameOffset + h.stringTableOffset + 0x20), f); // get the name of this node. toOutput("Unpacking: " + nodeName + "..."); path += "\\" + nodeName; DirectoryInfo dir = Directory.CreateDirectory(path); dir.Create(); for (int i = 0; i < n.numFileEntries; i++) { // Get the current FileEntry FileEntry curr = getFileEntry((int)(n.firstFileEntryOffset + i), h, f); if (curr.id == 0xFFFF) // subdirectory { if (curr.filenameOffset != 0 && curr.filenameOffset != 2) // don't go to "." and ".." { dumpNode(getNode((int)curr.dataOffset, f), h, f, path); // dump the node associated with curr if it's a directory. } } else // file { // If it's a file, the read the data from the offset and write it into a file with the name at the proper offset. string currName = getString((int)(curr.filenameOffset + h.stringTableOffset + 0x20), f); Stream nf = new FileStream(path + "\\" + currName, FileMode.Create, FileAccess.Write); // open new file stream. f.BaseStream.Seek(curr.dataOffset + h.dataStartOffset, 0); // seek to start of file data. // Read and write the data in 1024 byte chunks. UInt32 read = 0; byte[] buff = new byte[1024]; while (read < curr.dataSize) { int rAmount = Math.Min(1024, (int)(curr.dataSize - read)); f.Read(buff, 0, rAmount); nf.Write(buff, 0, rAmount); read += (UInt32)rAmount; } nf.Close(); // make sure to close our stream! } } }
private void DumpNode_(EndianBinaryReader er, RarcNode node, RarcHeader h) { /* * string nodeName = getString(0x20 + n.filenameOffset + h.stringTableOffset, f); * _mkdir(nodeName.c_str()); * _chdir(nodeName.c_str()); * * for (int i = 0; i < n.numFileEntries; ++i) { * RarcDump.FileEntry curr = getFileEntry(n.firstFileEntryOffset + i, h, f); * * if (curr.id == 0xFFFF) //subdirectory * { * if (curr.filenameOffset != 0 && * curr.filenameOffset != 2) //don't go to "." and ".." * dumpNode(getNode(curr.dataOffset, f), h, f); * } else //file * { * string currName = * getString(curr.filenameOffset + h.stringTableOffset + 0x20, f); * cout << nodeName << "/" << currName << endl; * FILE* dest = fopen(currName.c_str(), "wb"); * * u32 read = 0; * u8 buff[1024]; * fseek(f, curr.dataOffset + h.dataStartOffset + 0x20, SEEK_SET); * while (read < curr.dataSize) { * int r = fread(buff, 1, min(1024, curr.dataSize - read), f); * fwrite(buff, 1, r, dest); * read += r; * } * fclose(dest); * } * } * * _chdir(".."); */ }
public void Rarc(string[] args, string saveDesitination) { //This is a hack to enable debugging //args = new string[1]; //args[0] = @"H:\Games\NGC\fsa root\GC4Sword\Boss\boss010.arc_dir\boss010"; //Once complete the above two lines can recieve the chop stringTable = CreateStringTable();//Setup the string table //Get all directories and sub-ones in an array and create an appropriately sized Node array string[] allDirectories = Directory.GetDirectories(args[0], "*", SearchOption.AllDirectories); nodes = new Node[allDirectories.Length + 1]; //Add 1 for the ROOT node numNodesDone = 0; numFilesWithData = 0; lengthOfDataTable = 0; //Fill out the ROOT node nodes[0].type = "ROOT"; nodes[0].filenameOffset = (uint)stringTable.Length; String rootDirName = new FileInfo(args[0]).Name; stringTable = stringTable + rootDirName + (char)0x00; nodes[0].foldernameHash = Hash(rootDirName); string[] files = Directory.GetFileSystemEntries(args[0]); nodes[0].numFileEntries = (ushort)(files.Length + 2); nodes[0].firstFileEntryOffset = 0; numNodesDone++; //One node is complete //Get the total number of subdirectories and files string[] allFiles = Directory.GetFiles(args[0], "*", SearchOption.AllDirectories); int numOfFilesAndDirs = allFiles.Length + allDirectories.Length; //Now set up an array of FileEntrys(Taking into account the "." and ".." file entries for each folder fileEntries = new FileEntry[numOfFilesAndDirs + (allDirectories.Length * 2) + 2]; filesData = new string[allFiles.Length]; //Setup an array to store all the file data paths in totalNumFilesAdded = 0; //How many file entries have been done //CURRENTLY ONLY GOES TWO FOLDERS DEEP //Create FileEntry for each file in current folder string[] folders = ProcessFilesAndFolders(args); //For each folder for (int i = 0; i < folders.Length; i++) { args[0] = folders[i]; CreateNode(args); string[] folders2 = ProcessFilesAndFolders(args); //Do that again for any files/folders in this folder for (int i2 = 0; i2 < folders2.Length; i2++) { args[0] = folders2[i2]; CreateNode(args); ProcessFilesAndFolders(args); } } //Fill out the filename & data offsets for the folder entries with the offset from the appropriate Node for (int n = 0; n < totalNumFilesAdded; n++) { if (fileEntries[n].filenameOffset == 0xFFFE) { uint nodeNum = 0; foreach (Node node in nodes) { if (node.foldernameHash == fileEntries[n].filenameHash) { fileEntries[n].filenameOffset = (ushort)node.filenameOffset; fileEntries[n].dataOffset = nodeNum; } nodeNum++; } } } //Make the data table a mutiple of 16 int numOfPaddingBytes = 0; while ((lengthOfDataTable % 16) != 0) { lengthOfDataTable++; numOfPaddingBytes++; } //Fill out Header information RarcHeader header = new RarcHeader(); header.type = "RARC"; header.numFiles1 = (uint)totalNumFilesAdded; header.numFiles2 = (ushort)totalNumFilesAdded; header.sizeOfDataTable1 = lengthOfDataTable; header.sizeOfDataTable2 = lengthOfDataTable; header.unknown1 = 0x20; header.unknown6 = 0x20; header.unknown8 = 0x100; header.fileEntriesOffset = (numNodesDone * 16) + 0x20; if ((header.fileEntriesOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not { header.fileEntriesOffset += 16; } header.numNodes = numNodesDone; int x = 0; while (0 != ((totalNumFilesAdded * 20) + x) % 16) { x++; } header.stringTableOffset = header.fileEntriesOffset + (uint)((totalNumFilesAdded * 20) + x); if ((header.stringTableOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not { header.stringTableOffset += 16; } while (0 != (stringTable.Length) % 16)//Pad out the string table so the data table starts at a 0based address { stringTable = stringTable + (char)0x00; } header.dataStartOffset = (uint)(header.stringTableOffset + stringTable.Length); if ((header.dataStartOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not { header.dataStartOffset += 16; } header.sizeOfStringTable = (uint)stringTable.Length; header.size = lengthOfDataTable + header.dataStartOffset + 0x20; //Let's write it out FileStream filestreamWriter = new FileStream(saveDesitination, FileMode.Create); BinaryWriter binWriter = new BinaryWriter(filestreamWriter); //First the Header is written binWriter.Write(header.type[0]); binWriter.Write(header.type[1]); binWriter.Write(header.type[2]); binWriter.Write(header.type[3]); byte[] buffer = BitConverter.GetBytes(header.size); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.dataStartOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown4); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown5); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numNodes); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown6); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.fileEntriesOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfStringTable); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.stringTableOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown8); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown9); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); //Write each of the nodes foreach (Node currentNode in nodes) { binWriter.Write(currentNode.type[0]); if (currentNode.type.Length > 1) //Incase the dirname is only 1 char long { binWriter.Write(currentNode.type[1]); } else { filestreamWriter.WriteByte(0x20); } if (currentNode.type.Length > 2) //Incase the dirname is only 2 char long { binWriter.Write(currentNode.type[2]); } else { filestreamWriter.WriteByte(0x20); } if (currentNode.type.Length == 4) //Incase the dirname is only 3 char long { binWriter.Write(currentNode.type[3]); } else { filestreamWriter.WriteByte(0x20); } buffer = BitConverter.GetBytes(currentNode.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.foldernameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.numFileEntries); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.firstFileEntryOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the file entries at their correct offset while (filestreamWriter.Position != (header.fileEntriesOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write all the file entries foreach (FileEntry entry in fileEntries) { buffer = BitConverter.GetBytes(entry.id); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.unknown2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataSize); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.zero); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the string table at its correct offset while (filestreamWriter.Position != (header.stringTableOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write string table Encoding enc = Encoding.UTF8; binWriter.Write(enc.GetBytes(stringTable)); //Pad out the file to get the data table at its correct offset while (filestreamWriter.Position != (header.dataStartOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write files data foreach (string file in filesData) { buffer = File.ReadAllBytes(file); filestreamWriter.Write(buffer, 0, buffer.Length); while ((filestreamWriter.Position % 32) != 0)//Pad out the data so the next file begins on a 0-based multiple of 32 { filestreamWriter.WriteByte(0x00); } } for (int pad = 0; pad < numOfPaddingBytes; pad++)//Write the bytes neccessary to make the entire file divisble by 32 { filestreamWriter.WriteByte(0x00); } binWriter.Close(); filestreamWriter.Close(); Console.WriteLine("Packed and good to go!"); }
public static void CompressRARC(string FullPath, RARC.FileNode Root) { Console.WriteLine("\n>> Compressing " + Root.NodeName + " to " + FullPath); string newFile = Root.NodeName; stringTable = CreateStringTable(); //Setup the string table int directoriesCount = countRARCDirs(Root); nodes = new Node[directoriesCount + 1]; //Add 1 for the ROOT node numNodesDone = 0; numFilesWithData = 0; lengthOfDataTable = 0; //Fill out the ROOT node nodes[0].type = "ROOT"; nodes[0].filenameOffset = (uint)stringTable.Length; String rootDirName = newFile; stringTable = stringTable + rootDirName + (char)0x00; nodes[0].foldernameHash = Hash(rootDirName); nodes[0].numFileEntries = (ushort)(Root.Files.Count + Root.ChildNodes.Count + 2); nodes[0].firstFileEntryOffset = 0; numNodesDone++; //One node is complete //Get the total number of subdirectories and files //string[] allFiles = Directory.GetFiles(args[0], "*", SearchOption.AllDirectories); //int numOfFilesAndDirs = allFiles.Length + directoriesCount; int filesCount = countRARCFiles(Root); int numOfFilesAndDirs = filesCount + directoriesCount; //Now set up an array of FileEntrys(Taking into account the "." and ".." file entries for each folder fileEntries = new FileEntry[numOfFilesAndDirs + (directoriesCount * 2) + 2]; Console.WriteLine("# fileEntries " + (numOfFilesAndDirs + (directoriesCount * 2) + 2)); filesData = new byte[filesCount][]; //Setup an array to store all the file data paths in totalNumFilesAdded = 0; //How many file entries have been done CreateEntries(Root); //Fill out the filename & data offsets for the folder entries with the offset from the appropriate Node for (int n = 0; n < totalNumFilesAdded; n++) { if (fileEntries[n].filenameOffset == 0xFFFE) { uint nodeNum = 0; foreach (Node node in nodes) { if (node.foldernameHash == fileEntries[n].filenameHash) { fileEntries[n].filenameOffset = (ushort)node.filenameOffset; fileEntries[n].dataOffset = nodeNum; } nodeNum++; } } } //Make the data table a mutiple of 16 int numOfPaddingBytes = 0; while ((lengthOfDataTable % 16) != 0) { lengthOfDataTable++; numOfPaddingBytes++; } //Fill out Header information RarcHeader header = new RarcHeader(); header.type = "RARC"; header.numFiles1 = (uint)totalNumFilesAdded; header.numFiles2 = (ushort)totalNumFilesAdded; header.sizeOfDataTable1 = lengthOfDataTable; header.sizeOfDataTable2 = lengthOfDataTable; header.unknown1 = 0x20; header.unknown6 = 0x20; header.unknown8 = 0x100; header.fileEntriesOffset = (numNodesDone * 16) + 0x20; if ((header.fileEntriesOffset % 32) != 0) //Check if it's a multiple of 32 and make it one if it's not { header.fileEntriesOffset += 16; } Console.WriteLine("fileEntriesOffset is: " + header.fileEntriesOffset); Console.WriteLine("totalNumFilesAdded: " + (totalNumFilesAdded + 1)); header.numNodes = numNodesDone; int numFileEntries = (numOfFilesAndDirs + (directoriesCount * 2) + 2); int x = 0; while (0 != (((numFileEntries) * 20) + x) % 16) { x++; } header.stringTableOffset = header.fileEntriesOffset + (uint)((numFileEntries * 20) + x); if ((header.stringTableOffset % 32) != 0) //Check if it's a multiple of 32 and make it one if it's not { header.stringTableOffset += 16; } Console.WriteLine("stringTableOffset is: " + header.stringTableOffset); while (0 != (stringTable.Length) % 16) //Pad out the string table so the data table starts at a 0based address { stringTable = stringTable + (char)0x00; } header.dataStartOffset = (uint)(header.stringTableOffset + stringTable.Length); if ((header.dataStartOffset % 32) != 0) //Check if it's a multiple of 32 and make it one if it's not { header.dataStartOffset += 16; } Console.WriteLine("dataStartOffset is: " + header.dataStartOffset); header.sizeOfStringTable = (uint)stringTable.Length; header.size = lengthOfDataTable + header.dataStartOffset + 0x20; //Let's write it out // Uncomment while testing //FullPath += ".new.arc"; FileStream filestreamWriter = new FileStream(FullPath, FileMode.Create); BinaryWriter binWriter = new BinaryWriter(filestreamWriter); Console.WriteLine("Writing to file: " + FullPath); Console.WriteLine("Writing header..."); //First the Header is written binWriter.Write(header.type[0]); binWriter.Write(header.type[1]); binWriter.Write(header.type[2]); binWriter.Write(header.type[3]); byte[] buffer = BitConverter.GetBytes(header.size); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.dataStartOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown4); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown5); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numNodes); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown6); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.fileEntriesOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfStringTable); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.stringTableOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown8); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown9); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); Console.WriteLine("Writing nodes..."); //Write each of the nodes foreach (Node currentNode in nodes) { Console.WriteLine("Writing " + currentNode.type); binWriter.Write(currentNode.type[0]); if (currentNode.type.Length > 1) //Incase the dirname is only 1 char long { binWriter.Write(currentNode.type[1]); } else { filestreamWriter.WriteByte(0x20); } if (currentNode.type.Length > 2) //Incase the dirname is only 2 char long { binWriter.Write(currentNode.type[2]); } else { filestreamWriter.WriteByte(0x20); } if (currentNode.type.Length == 4) //Incase the dirname is only 3 char long { binWriter.Write(currentNode.type[3]); } else { filestreamWriter.WriteByte(0x20); } buffer = BitConverter.GetBytes(currentNode.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.foldernameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.numFileEntries); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.firstFileEntryOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the file entries at their correct offset while (filestreamWriter.Position < (header.fileEntriesOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write all the file entries foreach (FileEntry entry in fileEntries) { Console.WriteLine(String.Format("Writing fileEntry {0:X6}", entry.filenameOffset)); buffer = BitConverter.GetBytes(entry.id); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.unknown2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataSize); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.zero); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the string table at its correct offset while (filestreamWriter.Position < (header.stringTableOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write string table Encoding enc = Encoding.UTF8; binWriter.Write(enc.GetBytes(stringTable)); //Pad out the file to get the data table at its correct offset while (filestreamWriter.Position < (header.dataStartOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write files data foreach (byte[] file in filesData) { Console.WriteLine("Dumping file data..."); buffer = file; filestreamWriter.Write(buffer, 0, buffer.Length); while ((filestreamWriter.Position % 32) != 0) //Pad out the data so the next file begins on a 0-based multiple of 32 { filestreamWriter.WriteByte(0x00); } } for (int pad = 0; pad < numOfPaddingBytes; pad++) //Write the bytes neccessary to make the entire file divisble by 32 { filestreamWriter.WriteByte(0x00); } binWriter.Close(); filestreamWriter.Close(); Console.WriteLine("Packed and good to go!"); }
public static void CompressRARC(string FullPath, RARC.FileNode Root) { Console.WriteLine("\n>> Compressing " + Root.NodeName + " to " + FullPath); string newFile = Root.NodeName; stringTable = CreateStringTable();//Setup the string table int directoriesCount = countRARCDirs(Root); nodes = new Node[directoriesCount + 1]; //Add 1 for the ROOT node numNodesDone = 0; numFilesWithData = 0; lengthOfDataTable = 0; //Fill out the ROOT node nodes[0].type = "ROOT"; nodes[0].filenameOffset = (uint)stringTable.Length; String rootDirName = newFile; stringTable = stringTable + rootDirName + (char)0x00; nodes[0].foldernameHash = Hash(rootDirName); int filesCount = countRARCFiles (Root); nodes[0].numFileEntries = (ushort)(filesCount + 2); nodes[0].firstFileEntryOffset = 0; numNodesDone++; //One node is complete //Get the total number of subdirectories and files //string[] allFiles = Directory.GetFiles(args[0], "*", SearchOption.AllDirectories); //int numOfFilesAndDirs = allFiles.Length + directoriesCount; int numOfFilesAndDirs = filesCount + directoriesCount; //Now set up an array of FileEntrys(Taking into account the "." and ".." file entries for each folder fileEntries = new FileEntry[numOfFilesAndDirs + (directoriesCount * 2) + 2]; Console.WriteLine("# fileEntries " + (numOfFilesAndDirs + (directoriesCount * 2) + 2)); filesData = new byte[filesCount][]; //Setup an array to store all the file data paths in totalNumFilesAdded = 0; //How many file entries have been done CreateEntries(Root); //Fill out the filename & data offsets for the folder entries with the offset from the appropriate Node for (int n = 0; n < totalNumFilesAdded; n++) { if (fileEntries[n].filenameOffset == 0xFFFE) { uint nodeNum = 0; foreach (Node node in nodes) { if (node.foldernameHash == fileEntries[n].filenameHash) { fileEntries[n].filenameOffset = (ushort)node.filenameOffset; fileEntries[n].dataOffset = nodeNum; } nodeNum++; } } } //Make the data table a mutiple of 16 int numOfPaddingBytes = 0; while ((lengthOfDataTable % 16) != 0) { lengthOfDataTable++; numOfPaddingBytes++; } //Fill out Header information RarcHeader header = new RarcHeader(); header.type = "RARC"; header.numFiles1 = (uint)totalNumFilesAdded; header.numFiles2 = (ushort)totalNumFilesAdded; header.sizeOfDataTable1 = lengthOfDataTable; header.sizeOfDataTable2 = lengthOfDataTable; header.unknown1 = 0x20; header.unknown6 = 0x20; header.unknown8 = 0x100; header.fileEntriesOffset = (numNodesDone * 16) + 0x20; if ((header.fileEntriesOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not header.fileEntriesOffset += 16; Console.WriteLine("fileEntriesOffset is: " + header.fileEntriesOffset); Console.WriteLine("totalNumFilesAdded: " + (totalNumFilesAdded+1)); header.numNodes = numNodesDone; int numFileEntries = (numOfFilesAndDirs + (directoriesCount * 2) + 2); int x = 0; while (0 != (((numFileEntries) * 20) + x) % 16) x++; header.stringTableOffset = header.fileEntriesOffset + (uint)((numFileEntries * 20) + x); if ((header.stringTableOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not header.stringTableOffset += 16; Console.WriteLine("stringTableOffset is: " + header.stringTableOffset); while (0 != (stringTable.Length) % 16)//Pad out the string table so the data table starts at a 0based address stringTable = stringTable + (char)0x00; header.dataStartOffset = (uint)(header.stringTableOffset + stringTable.Length); if ((header.dataStartOffset % 32) != 0)//Check if it's a multiple of 32 and make it one if it's not header.dataStartOffset += 16; Console.WriteLine("dataStartOffset is: " + header.dataStartOffset); header.sizeOfStringTable = (uint)stringTable.Length; header.size = lengthOfDataTable + header.dataStartOffset + 0x20; //Let's write it out // Uncomment while testing //FullPath += ".new.arc"; FileStream filestreamWriter = new FileStream(FullPath, FileMode.Create); BinaryWriter binWriter = new BinaryWriter(filestreamWriter); Console.WriteLine("Writing to file: " + FullPath); Console.WriteLine("Writing header..."); //First the Header is written binWriter.Write(header.type[0]); binWriter.Write(header.type[1]); binWriter.Write(header.type[2]); binWriter.Write(header.type[3]); byte[] buffer = BitConverter.GetBytes(header.size); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.dataStartOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfDataTable2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown4); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown5); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numNodes); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown6); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles1); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.fileEntriesOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.sizeOfStringTable); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.stringTableOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.numFiles2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown8); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(header.unknown9); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); Console.WriteLine("Writing nodes..."); //Write each of the nodes foreach (Node currentNode in nodes) { Console.WriteLine("Writing " + currentNode.type); binWriter.Write(currentNode.type[0]); if (currentNode.type.Length > 1) //Incase the dirname is only 1 char long binWriter.Write(currentNode.type[1]); else filestreamWriter.WriteByte(0x20); if (currentNode.type.Length > 2) //Incase the dirname is only 2 char long binWriter.Write(currentNode.type[2]); else filestreamWriter.WriteByte(0x20); if (currentNode.type.Length == 4) //Incase the dirname is only 3 char long binWriter.Write(currentNode.type[3]); else filestreamWriter.WriteByte(0x20); buffer = BitConverter.GetBytes(currentNode.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.foldernameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.numFileEntries); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(currentNode.firstFileEntryOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the file entries at their correct offset while (filestreamWriter.Position < (header.fileEntriesOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write all the file entries foreach (FileEntry entry in fileEntries) { Console.WriteLine(String.Format("Writing fileEntry {0:X6}",entry.filenameOffset)); buffer = BitConverter.GetBytes(entry.id); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameHash); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.unknown2); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.filenameOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataOffset); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.dataSize); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); buffer = BitConverter.GetBytes(entry.zero); Array.Reverse(buffer); filestreamWriter.Write(buffer, 0, buffer.Length); } //Pad out the file to get the string table at its correct offset while (filestreamWriter.Position < (header.stringTableOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write string table Encoding enc = Encoding.UTF8; binWriter.Write(enc.GetBytes(stringTable)); //Pad out the file to get the data table at its correct offset while (filestreamWriter.Position < (header.dataStartOffset + 32)) { filestreamWriter.WriteByte(0x00); } //Write files data foreach (byte[] file in filesData) { Console.WriteLine("Dumping file data..."); buffer = file; filestreamWriter.Write(buffer, 0, buffer.Length); while ((filestreamWriter.Position % 32) != 0)//Pad out the data so the next file begins on a 0-based multiple of 32 filestreamWriter.WriteByte(0x00); } for (int pad = 0; pad < numOfPaddingBytes; pad++)//Write the bytes neccessary to make the entire file divisble by 32 filestreamWriter.WriteByte(0x00); binWriter.Close(); filestreamWriter.Close(); Console.WriteLine("Packed and good to go!"); }