/// <summary> /// Validates that the next string is a certain value and throws an exception if it is not. /// </summary> /// <param name="value">value to be validated</param> /// <param name="reader">BinaryReader instance</param> public static void ValidateNextString(string value, BinaryReader reader) { string label = ReadCString(reader); if (!label.ToUpperInvariant().Equals(value.ToUpperInvariant())) { // Turn on debugging so we can log the exception. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "Error reading file at position {2}. Expecting '{0}'. Got '{1}'", value, label, reader.BaseStream.Position - label.Length - 4)); throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, "Error reading file at position {2}. Expecting '{0}'. Got '{1}'", value, label, reader.BaseStream.Position - label.Length - 4)); } }
/// <summary> /// Parses the raw data and converts to internal data. /// </summary> private void ParseRawData() { // First create a memory stream so we can decode the binary data as needed. using (BinaryReader reader = new BinaryReader(new MemoryStream(this.rawData, false))) { int offset = 0; try { this.ParseItemBlock(offset, reader); } catch (ArgumentException) { throw; } try { string outfile = string.Concat(Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName), " Export.txt"); using (StreamWriter outStream = new StreamWriter(outfile, false)) { outStream.WriteLine("Number of Sacks = {0}", this.numberOfSacks); if (!this.sack.IsEmpty) { outStream.WriteLine(); outStream.WriteLine("SACK 0"); int itemNumber = 0; foreach (Item item in this.sack) { object[] params1 = new object[20]; params1[0] = itemNumber; params1[1] = item.ToString(); params1[2] = item.PositionX; params1[3] = item.PositionY; params1[4] = item.Seed; outStream.WriteLine(" {0,5:n0} {1}", params1); itemNumber++; } } } } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error Exporting - '{0} Export.txt'", Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName))); TQDebug.DebugWriteLine(exception.ToString()); } } }
/// <summary> /// Writes a record to a file. /// </summary> /// <param name="baseFolder">string holding the base folder path</param> /// <param name="record">Record we are writing</param> /// <param name="destinationFileName">Filename for the new file.</param> public void Write(string baseFolder, string record, string destinationFileName) { try { if (!this.fileHasBeenRead) { this.ReadARCToC(); } string dataID = string.Concat(Path.GetFileNameWithoutExtension(this.FileName), "\\", record); byte[] data = this.GetData(dataID); if (data == null) { return; } string destination = baseFolder; if (!destination.EndsWith("\\", StringComparison.OrdinalIgnoreCase)) { destination = string.Concat(destination, "\\"); } destination = string.Concat(destination, destinationFileName); // If there is a sub directory in the arc file then we need to create it. if (!Directory.Exists(Path.GetDirectoryName(destination))) { Directory.CreateDirectory(Path.GetDirectoryName(destination)); } using (FileStream outStream = new FileStream(destination, FileMode.Create, FileAccess.Write)) { outStream.Write(data, 0, data.Length); } } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(exception.ToString()); return; } }
/// <summary> /// Reads the ARC file table of contents to determine if the file is readable. /// </summary> /// <returns>True if able to read the ToC</returns> public bool Read() { try { if (!this.fileHasBeenRead) { this.ReadARCToC(); } return(this.directoryEntries != null); } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } // Write the exception to the debug log. TQDebug.DebugWriteLine(exception.ToString()); return(false); } }
/// <summary> /// Parses the raw binary data for use within TQVault /// </summary> private void ParseRawData() { // First create a memory stream so we can decode the binary data as needed. using (MemoryStream stream = new MemoryStream(this.rawData, false)) { using (BinaryReader reader = new BinaryReader(stream)) { // Find the block pairs until we find the block that contains the item data. int blockNestLevel = 0; int currentOffset = 0; int itemOffset = 0; int equipmentOffset = 0; // vaults start at the item data with no crap bool foundItems = this.IsVault; bool foundEquipment = this.IsVault; while ((!foundItems || !foundEquipment) && (currentOffset = this.FindNextBlockDelim(currentOffset)) != -1) { if (this.rawData[currentOffset] == beginBlockPattern[0]) { // begin block ++blockNestLevel; currentOffset += beginBlockPattern.Length; // skip past the 4 bytes of noise after begin_block currentOffset += 4; // Seek our stream to the correct position stream.Seek(currentOffset, SeekOrigin.Begin); // Now get the string for this block string blockName = TQData.ReadCString(reader).ToUpperInvariant(); // Assign loc to our new stream position currentOffset = (int)stream.Position; // See if we accidentally got a begin_block or end_block if (blockName.Equals("BEGIN_BLOCK")) { blockName = "(NONAME)"; currentOffset -= beginBlockPattern.Length; } else if (blockName.Equals("END_BLOCK")) { blockName = "(NONAME)"; currentOffset -= endBlockPattern.Length; } else if (blockName.Equals("ITEMPOSITIONSSAVEDASGRIDCOORDS")) { currentOffset += 4; itemOffset = currentOffset; // skip value for itemPositionsSavedAsGridCoords foundItems = true; } else if (blockName.Equals("USEALTERNATE")) { currentOffset += 4; equipmentOffset = currentOffset; // skip value for useAlternate foundEquipment = true; } // Print the string with a nesting level indicator ////string levelString = new string ('-', System.Math.Max(0,blockNestLevel*2-2)); ////out.WriteLine ("{0} {2:n0} '{1}'", levelString, blockName, loc); } else { // end block --blockNestLevel; currentOffset += endBlockPattern.Length; ////if (blockNestLevel < 0) ////{ //// out.WriteLine ("{0:n0} Block Nest Level < 0!!!", loc); ////} } } ////out.WriteLine ("Final Block Level = {0:n0}", blockNestLevel); if (foundItems) { try { this.ParseItemBlock(itemOffset, reader); } catch (ArgumentException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error parsing player file Item Block - '{0}'", this.PlayerName)); TQDebug.DebugWriteLine(exception.ToString()); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Error parsing player file Item Block- '{0}'", this.PlayerName), exception); } try { string outfile = string.Concat(Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName), " Export.txt"); using (StreamWriter outStream = new StreamWriter(outfile, false)) { outStream.WriteLine("Number of Sacks = {0}", this.numberOfSacks); int sackNumber = 0; foreach (SackCollection sack in this.sacks) { if (!sack.IsEmpty) { outStream.WriteLine(); outStream.WriteLine("SACK {0}", sackNumber); int itemNumber = 0; foreach (Item item in sack) { object[] params1 = new object[20]; params1[0] = itemNumber; params1[1] = item.ToString(); params1[2] = item.PositionX; params1[3] = item.PositionY; params1[4] = item.Seed; ////params1[5] = outStream.WriteLine(" {0,5:n0} {1}", params1); itemNumber++; } } sackNumber++; } } } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "Error writing Export file - '{0}'", string.Concat(Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName), " Export.txt"))); TQDebug.DebugWriteLine(exception.ToString()); } } // Process the equipment block if (foundEquipment && !this.IsVault) { try { this.ParseEquipmentBlock(equipmentOffset, reader); } catch (ArgumentException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error parsing player file Equipment Block - '{0}'", this.PlayerName)); TQDebug.DebugWriteLine(exception.ToString()); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Error parsing player file Equipment Block - '{0}'", this.PlayerName), exception); } try { string outfile = string.Concat(Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName), " Equipment Export.txt"); using (StreamWriter outStream = new StreamWriter(outfile, false)) { if (!this.EquipmentSack.IsEmpty) { int itemNumber = 0; foreach (Item item in this.EquipmentSack) { object[] params1 = new object[20]; params1[0] = itemNumber; params1[1] = item.ToString(); params1[2] = item.PositionX; params1[3] = item.PositionY; params1[4] = item.Seed; ////params1[5] = outStream.WriteLine(" {0,5:n0} {1}", params1); itemNumber++; } } } } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "Error writing Export file - '{0}'", string.Concat(Path.Combine(TQData.TQVaultSaveFolder, this.PlayerName), " Equipment Export.txt"))); TQDebug.DebugWriteLine(exception.ToString()); } } } } }
/// <summary> /// Reads the ARZ file. /// </summary> /// <returns>true on success</returns> public bool Read() { StreamWriter outStream = null; if (TQDebug.DatabaseDebugLevel > 2) { outStream = new StreamWriter("arzOut.txt", false); } try { // ARZ header file format // // 0x000000 int32 // 0x000004 int32 start of dbRecord table // 0x000008 int32 size in bytes of dbRecord table // 0x00000c int32 numEntries in dbRecord table // 0x000010 int32 start of string table // 0x000014 int32 size in bytes of string table FileStream instream = new FileStream(this.fileName, FileMode.Open, FileAccess.Read, FileShare.Read); BinaryReader reader = new BinaryReader(instream); try { int[] header = new int[6]; for (int i = 0; i < 6; ++i) { header[i] = reader.ReadInt32(); if (outStream != null) { outStream.WriteLine("Header[{0}] = {1:n0} (0x{1:X})", i, header[i]); } } int firstTableStart = header[1]; int firstTableCount = header[3]; int secondTableStart = header[4]; this.ReadStringTable(secondTableStart, reader, outStream); this.ReadRecordTable(firstTableStart, firstTableCount, reader, outStream); // 4 final int32's from file // first int32 is numstrings in the stringtable // second int32 is something ;) // 3rd and 4th are crap (timestamps maybe?) for (int i = 0; i < 4; ++i) { int val = reader.ReadInt32(); if (outStream != null) { outStream.WriteLine("{0:n0} 0x{0:X}", val); } } } catch (IOException) { throw; } finally { reader.Close(); } } catch (IOException exception) { if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } // Write the exception to the debug log. TQDebug.DebugWriteLine(exception.ToString()); return(false); } finally { if (outStream != null) { outStream.Close(); } } return(true); }
/// <summary> /// Decompresses an individual record. /// </summary> /// <param name="arzFile">ARZ file which we are decompressing.</param> /// <returns>decompressed DBRecord.</returns> public DBRecordCollection Decompress(ArzFile arzFile) { // record variables have this format: // 0x00 int16 specifies data type: // 0x0000 = int - data will be an int32 // 0x0001 = float - data will be a Single // 0x0002 = string - data will be an int32 that is index into string table // 0x0003 = bool - data will be an int32 // 0x02 int16 specifies number of values (usually 1, but sometimes more (for arrays) // 0x04 int32 key string ID (the id into the string table for this variable name // 0x08 data value byte[] data = this.DecompressBytes(arzFile); int numberOfDWords = data.Length / 4; if (data.Length % 4 != 0) { // Turn on debugging so we can log the exception. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error in ARZFile - {0}", arzFile.fileName)); TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, data Length = {1} which is not a multiple of 4", this.ID, (int)data.Length)); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, data Length = {1} which is not a multiple of 4", this.ID, (int)data.Length)); } DBRecordCollection record = new DBRecordCollection(this.ID, this.RecordType); // Create a memory stream to read the binary data using (BinaryReader inReader = new BinaryReader(new MemoryStream(data, false))) { int i = 0; while (i < numberOfDWords) { short dataType = inReader.ReadInt16(); short valCount = inReader.ReadInt16(); int variableID = inReader.ReadInt32(); string variableName = arzFile.Getstring(variableID); if (variableName == null) { // Turn on debugging so we can log the exception. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error in ARZFile - {0}", arzFile.fileName)); TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable is NULL", this.ID)); throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable is NULL", this.ID)); } if (dataType < 0 || dataType > 3) { // Turn on debugging so we can log the exception. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error in ARZFile - {0}", arzFile.fileName)); TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable {1}, bad dataType {2}", this.ID, variableName, dataType)); throw new ArgumentOutOfRangeException(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable {1}, bad dataType {2}", this.ID, variableName, dataType)); } Variable v = new Variable(variableName, (VariableDataType)dataType, valCount); if (valCount < 1) { // Turn on debugging so we can log the exception. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error in ARZFile - {0}", arzFile.fileName)); TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable {1}, bad valCount {2}", this.ID, variableName, valCount)); throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Error while parsing arz record {0}, variable {1}, bad valCount {2}", this.ID, variableName, valCount)); } // increment our dword count i += 2 + valCount; for (int j = 0; j < valCount; ++j) { switch (v.DataType) { case VariableDataType.Integer: case VariableDataType.Boolean: { int val = inReader.ReadInt32(); v[j] = val; break; } case VariableDataType.Float: { float val = inReader.ReadSingle(); v[j] = val; break; } case VariableDataType.StringVar: { int id = inReader.ReadInt32(); string val = arzFile.Getstring(id); if (val == null) { val = string.Empty; } else { val = val.Trim(); } v[j] = val; break; } default: { int val = inReader.ReadInt32(); v[j] = val; break; } } } record.Set(v); } } return(record); }
/// <summary> /// Read the table of contents of the ARC file /// </summary> private void ReadARCToC() { // Format of an ARC file // 0x08 - 4 bytes = # of files // 0x0C - 4 bytes = # of parts // 0x18 - 4 bytes = offset to directory structure // // Format of directory structure // 4-byte int = offset in file where this part begins // 4-byte int = size of compressed part // 4-byte int = size of uncompressed part // these triplets repeat for each part in the arc file // After these triplets are a bunch of null-terminated strings // which are the sub filenames. // After the subfilenames comes the subfile data: // 4-byte int = 3 == indicates start of subfile item (maybe compressed flag??) // 1 == maybe uncompressed flag?? // 4-byte int = offset in file where first part of this subfile begins // 4-byte int = compressed size of this file // 4-byte int = uncompressed size of this file // 4-byte crap // 4-byte crap // 4-byte crap // 4-byte int = numParts this file uses // 4-byte int = part# of first part for this file (starting at 0). // 4-byte int = length of filename string // 4-byte int = offset in directory structure for filename this.fileHasBeenRead = true; if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "ARCFile.ReadARCToC({0})", this.FileName)); } try { using (FileStream arcFile = new FileStream(this.FileName, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(arcFile)) { if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "File Length={0}", arcFile.Length)); } // check the file header if (reader.ReadByte() != 0x41) { return; } if (reader.ReadByte() != 0x52) { return; } if (reader.ReadByte() != 0x43) { return; } if (arcFile.Length < 0x21) { return; } reader.BaseStream.Seek(0x08, SeekOrigin.Begin); int numEntries = reader.ReadInt32(); int numParts = reader.ReadInt32(); if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "numEntries={0}, numParts={1}", numEntries, numParts)); } ARCPartEntry[] parts = new ARCPartEntry[numParts]; ARCDirEntry[] records = new ARCDirEntry[numEntries]; if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine("Seeking to tocOffset location"); } reader.BaseStream.Seek(0x18, SeekOrigin.Begin); int tocOffset = reader.ReadInt32(); if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "tocOffset = {0}", tocOffset)); } // Make sure all 3 entries exist for the toc entry. if (arcFile.Length < (tocOffset + 12)) { return; } // Read in all of the part data reader.BaseStream.Seek(tocOffset, SeekOrigin.Begin); int i; for (i = 0; i < numParts; ++i) { parts[i] = new ARCPartEntry(); parts[i].FileOffset = reader.ReadInt32(); parts[i].CompressedSize = reader.ReadInt32(); parts[i].RealSize = reader.ReadInt32(); if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "parts[{0}]", i)); TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, " fileOffset={0}, compressedSize={1}, realSize={2}", parts[i].FileOffset, parts[i].CompressedSize, parts[i].RealSize)); } } // Now record this offset so we can come back and read in the filenames // after we have read in the file records int fileNamesOffset = (int)arcFile.Position; // Now seek to the location where the file record data is // This offset is from the end of the file. int fileRecordOffset = 44 * numEntries; if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "fileNamesOffset = {0}. Seeking to {1} to read file record data.", fileNamesOffset, fileRecordOffset)); } arcFile.Seek(-1 * fileRecordOffset, SeekOrigin.End); for (i = 0; i < numEntries; ++i) { records[i] = new ARCDirEntry(); // storageType = 3 - compressed / 1- non compressed int storageType = reader.ReadInt32(); if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "StorageType={0}", storageType)); } // Added by VillageIdiot to support stored types records[i].StorageType = storageType; records[i].FileOffset = reader.ReadInt32(); records[i].CompressedSize = reader.ReadInt32(); records[i].RealSize = reader.ReadInt32(); int crap = reader.ReadInt32(); // crap if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Crap2={0}", crap)); } crap = reader.ReadInt32(); // crap if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Crap3={0}", crap)); } crap = reader.ReadInt32(); // crap if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Crap4={0}", crap)); } int numberOfParts = reader.ReadInt32(); if (numberOfParts < 1) { records[i].Parts = null; if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "File {0} is not compressed.", i)); } } else { records[i].Parts = new ARCPartEntry[numberOfParts]; } int firstPart = reader.ReadInt32(); crap = reader.ReadInt32(); // filename length if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Filename Length={0}", crap)); } crap = reader.ReadInt32(); // filename offset if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Filename Offset={0}", crap)); TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "record[{0}]", i)); TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, " offset={0} compressedSize={1} realSize={2}", records[i].FileOffset, records[i].CompressedSize, records[i].RealSize)); if (storageType != 1 && records[i].IsActive) { TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, " numParts={0} firstPart={1} lastPart={2}", records[i].Parts.Length, firstPart, firstPart + records[i].Parts.Length - 1)); } else { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, " INACTIVE firstPart={0}", firstPart)); } } if (storageType != 1 && records[i].IsActive) { for (int ip = 0; ip < records[i].Parts.Length; ++ip) { records[i].Parts[ip] = parts[ip + firstPart]; } } } // Now read in the record names arcFile.Seek(fileNamesOffset, SeekOrigin.Begin); byte[] buffer = new byte[2048]; ASCIIEncoding ascii = new ASCIIEncoding(); for (i = 0; i < numEntries; ++i) { // only Active files have a filename entry if (records[i].IsActive) { // For each string, read bytes until I hit a 0x00 byte. if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Reading entry name {0:n0}", i)); } int bufferSize = 0; while ((buffer[bufferSize++] = reader.ReadByte()) != 0x00) { if (buffer[bufferSize - 1] == 0x03) { // File is null? arcFile.Seek(-1, SeekOrigin.Current); // backup bufferSize--; buffer[bufferSize] = 0x00; if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine("Null file - inactive?"); } break; } if (bufferSize >= buffer.Length) { TQDebug.DebugWriteLine("ARCFile.ReadARCToC() Error - Buffer size of 2048 has been exceeded."); if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine("Buffer contents:\n"); for (int j = 0; j < bufferSize; ++j) { TQDebug.DebugWrite(string.Format(CultureInfo.InvariantCulture, "0x{0:X}", buffer[j])); } TQDebug.DebugWriteLine(String.Empty); } } } if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "Read {0:n0} bytes for name. Converting to string.", bufferSize)); } string newfile; if (bufferSize >= 1) { // Now convert the buffer to a string char[] chars = new char[ascii.GetCharCount(buffer, 0, bufferSize - 1)]; ascii.GetChars(buffer, 0, bufferSize - 1, chars, 0); newfile = new string(chars); } else { newfile = string.Format(CultureInfo.InvariantCulture, "Null File {0}", i); } records[i].FileName = TQData.NormalizeRecordPath(newfile); if (TQDebug.ArcFileDebugLevel > 2) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Name {0:n0} = '{1}'", i, records[i].FileName)); } } } // Now convert the array of records into a Dictionary. Dictionary <string, ARCDirEntry> dictionary = new Dictionary <string, ARCDirEntry>(numEntries); if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine("Creating Dictionary"); } for (i = 0; i < numEntries; ++i) { if (records[i].IsActive) { dictionary.Add(records[i].FileName, records[i]); } } this.directoryEntries = dictionary; if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine("Exiting ARCFile.ReadARCToC()"); } } } } catch (IOException exception) { // Turn on debugging. if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } // Write the errors to the debug log. TQDebug.DebugWriteLine("ARCFile.ReadARCToC() - Error reading arcfile"); TQDebug.DebugWriteLine(exception.ToString()); } }
/// <summary> /// Extracts the decoded ARC file contents into a folder. /// </summary> /// <param name="destination">Destination folder for the files.</param> /// <returns>true if successful, false on error.</returns> public bool ExtractArcFile(string destination) { try { if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine("ARCFile.ReadARCFile()"); } if (!this.fileHasBeenRead) { this.ReadARCToC(); } foreach (ARCDirEntry dirEntry in this.directoryEntries.Values) { string dataID = string.Concat(Path.GetFileNameWithoutExtension(this.FileName), "\\", dirEntry.FileName); if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Concat("Directory Filename = ", dirEntry.FileName)); TQDebug.DebugWriteLine(string.Concat("dataID = ", dataID)); } byte[] data = this.GetData(dataID); string filename = destination; if (!filename.EndsWith("\\", StringComparison.Ordinal)) { filename = string.Concat(filename, "\\"); } filename = string.Concat(filename, dirEntry.FileName); // If there is a sub directory in the arc file then we need to create it. if (!Directory.Exists(Path.GetDirectoryName(filename))) { Directory.CreateDirectory(Path.GetDirectoryName(filename)); } if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Concat("Creating File - ", filename)); } using (FileStream outStream = new FileStream(filename, FileMode.Create, FileAccess.Write)) { outStream.Write(data, 0, data.Length); } } if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine("Exiting ARCFile.ReadARCFile()"); } return(true); } catch (IOException exception) { // Turn on debugging if (!TQDebug.DebugEnabled) { TQDebug.DebugEnabled = true; } // Write the errors to the debug log. TQDebug.DebugWriteLine("ARCFile.ReadARCFile() - Error reading arcfile"); TQDebug.DebugWriteLine(exception.ToString()); return(false); } }
/// <summary> /// Reads data from an ARC file and puts it into a Byte array (or NULL if not found) /// </summary> /// <param name="dataId">The string ID for the data which we are retieving.</param> /// <returns>Returns byte array of the data corresponding to the string ID.</returns> public byte[] GetData(string dataId) { if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "ARCFile.GetData({0})", dataId)); } if (!this.fileHasBeenRead) { this.ReadARCToC(); } if (this.directoryEntries == null) { if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error - Could not read {0}", this.FileName)); } // could not read the file return(null); } // First normalize the filename dataId = TQData.NormalizeRecordPath(dataId); if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Normalized dataID = {0}", dataId)); } // Find our file in the toc. // First strip off the leading folder since it is just the ARC name int firstPathDelim = dataId.IndexOf('\\'); if (firstPathDelim != -1) { dataId = dataId.Substring(firstPathDelim + 1); } // Now see if this file is in the toc. ARCDirEntry directoryEntry; if (directoryEntries.ContainsKey(dataId)) { directoryEntry = this.directoryEntries[dataId]; } else { // record not found if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format(CultureInfo.InvariantCulture, "Error - {0} not found.", dataId)); } return(null); } // Now open the ARC file and read in the record. using (FileStream arcFile = new FileStream(this.FileName, FileMode.Open, FileAccess.Read)) { // Allocate memory for the uncompressed data byte[] data = new byte[directoryEntry.RealSize]; // Now process each part of this record int startPosition = 0; // First see if the data was just stored without compression. if ((directoryEntry.StorageType == 1) && (directoryEntry.CompressedSize == directoryEntry.RealSize)) { if (TQDebug.ArcFileDebugLevel > 1) { TQDebug.DebugWriteLine(string.Format( CultureInfo.InvariantCulture, "Offset={0} Size={1}", directoryEntry.FileOffset, directoryEntry.RealSize)); } arcFile.Seek(directoryEntry.FileOffset, SeekOrigin.Begin); arcFile.Read(data, 0, directoryEntry.RealSize); } else { // The data was compressed so we attempt to decompress it. foreach (ARCPartEntry partEntry in directoryEntry.Parts) { // seek to the part we want arcFile.Seek(partEntry.FileOffset, SeekOrigin.Begin); // Ignore the zlib compression method. arcFile.ReadByte(); // Ignore the zlib compression flags. arcFile.ReadByte(); // Create a deflate stream. using (DeflateStream deflate = new DeflateStream(arcFile, CompressionMode.Decompress, true)) { int bytesRead; int partLength = 0; while ((bytesRead = deflate.Read(data, startPosition, data.Length - startPosition)) > 0) { startPosition += bytesRead; partLength += bytesRead; // break out of the read loop if we have processed this part completely. if (partLength >= partEntry.RealSize) { break; } } } } } if (TQDebug.ArcFileDebugLevel > 0) { TQDebug.DebugWriteLine("Exiting ARCFile.GetData()"); } return(data); } }