/// <summary> /// <para>This constructor is used solely for extracting XML /// data from a BAR file without leaving the XMB file sitting /// around on the hard disk.</para> /// /// <para>The method works by saving the file in its native /// XMB form, and then setting the class attributes to focus /// on extracting that file. Then the saveXMB method will /// extract from this temporary file, and once this class is /// disposed of and the destructor is called the temporary /// file will be deleted.</para> /// </summary> /// <param name="entry">The BAR file entry to be saved to XML.</param> /// <param name="saveDirectory">The directory to save the new XML /// file to (leave blank for the same directory as the program).</param> public XMBReader(BarEntry entry, string saveDirectory) { // Set up instance variables... xmbFilePath = entry.fileName + ".tmp"; xmlFilePath = saveDirectory + "\\" + entry.fileName.Substring(0, entry.fileName.LastIndexOf('.')); fileLength = 0; // Create a BarReader and save the file we need // to the temporary destination (as the XMB file) BarReader reader = new BarReader(entry.barFilePath); reader.saveFile(entry,xmbFilePath); // Set the temporary file flag isTemporaryFile = true; }
/// <summary> /// Saves a file to program's location, this method /// works simply by calling the main saveFile method and /// specifying a blank string in place of the path. /// </summary> /// <param name="file">BarEntry object detailing the file to be saved</param> private void saveFile(BarEntry file) { saveFile(file, ""); }
/// <summary> /// The load method will load the BAR file that has been /// specified by the constructor. It returns a bool /// value specifying whether the operation has been /// successful. /// </summary> /// <returns>True if the BAR file has been loaded /// successfully, otherwise false.</returns> public bool load() { // We will be throwing exceptions about the // place, so we'd best try to be safe... try { // Does the BAR file even exist? if (!File.Exists(barFilePath)) throw new Exception("BAR file does not exist!"); // Declare some variables uint numFiles, filetableOffset, numRootFiles; int tempInt; string tempString, rootName; // Instantiate and open the filestream // to the bar file. input = new FileStream(barFilePath, FileMode.Open); // Now, start checking through the first // few bytes of the file to see if this is // a valid BAR file. // BAR files always start with a four byte string "ESPN" tempString = ByteReader.readString(input, 4); if (tempString != "ESPN") throw new Exception("'ESPN' not detected - Not a valid BAR file!"); // Afterwards they feature a four byte integer value "2" tempInt = ByteReader.readInt(input, 4); if (tempInt != 2) throw new Exception("'2' not detected - Not a valid BAR file!"); // Next they feature a four byte hex value of 0x44332211 tempInt = ByteReader.readInt(input, 4); if (tempInt != 0x44332211) throw new Exception("'0x44332211' not detected - Not a valid BAR file!"); // Lastly they always feature a large block of zero values bool allZeroes = true; for (int i = 0; i < 66; ++i) { tempInt = ByteReader.readInt(input, 4); if (tempInt != 0) allZeroes = false; } if (!allZeroes) throw new Exception("Successive zeroes not detected - Not a valid BAR file!"); // Unknown value - This is possibly a checksum, for // now however it is ignored. tempInt = ByteReader.readInt(input, 4); // Now the number of files stored within the BAR file // are located. numFiles = ByteReader.readUInt(input, 4); // Are there actually any files? if (numFiles == 0) throw new Exception("No files stored - This is an empty / invalid BAR file!"); // Next is the offset for the file table, where the // contents of this BAR file are located. filetableOffset = ByteReader.readUInt(input, 4); // Is this actually a number? if (filetableOffset == 0) throw new Exception("File table offset blank - Not a valid BAR file!"); // Now another 4 bytes of zeroes are located before // the actual data starts. tempInt = ByteReader.readInt(input, 4); if (tempInt != 0) throw new Exception("Zeroes not detected - Not a valid BAR file!"); // If an exception has not been thrown thus far then // this is a valid BAR file and we can hopefully // proceed to the file table and start learning what // is locked inside! // Start by going to the location of the file table... input.Seek(filetableOffset, SeekOrigin.Begin); // Now we should be able to read the root name of this // BAR file. rootName = ByteReader.readFullString(input); // The number of files should also be listed here, get // it and check it matched the number of files given // earlier. numRootFiles = ByteReader.readUInt(input, 4); if (numRootFiles != numFiles) throw new Exception("File total mismatch - Not a valid BAR file!"); // Now we can start looping throough the files in the // file table, saving their details in the files List. for (int fileIndex = 0; fileIndex < numFiles; fileIndex++) { // Create a new BAR Entry to take the data BarEntry file = new BarEntry(); // Read in the offset for this file (its location // within the bar file) file.offset = ByteReader.readUInt(input, 4); // Check the file size for this file, it is // listed twice so compare the values are equal. uint length1 = ByteReader.readUInt(input, 4); uint length2 = ByteReader.readUInt(input, 4); // Only proceed if they are equal... if (length1 == length2) { // This file is apparently valid, so // store the length. file.fileSize = length1; // Now store the date information... file.year = ByteReader.readUShort(input, 2); file.month = ByteReader.readUShort(input, 2); file.dayOfWeek = ByteReader.readUShort(input, 2); file.day = ByteReader.readUShort(input, 2); file.hour = ByteReader.readUShort(input, 2); file.minute = ByteReader.readUShort(input, 2); file.second = ByteReader.readUShort(input, 2); file.msecond = ByteReader.readUShort(input, 2); // If there's no data stored, as // indicated with the 0xCCCC pattern, // clear all date information. if (file.year == 0xCCCC) file.clearDate(); // Get the path of the file and set it file.filePath = ByteReader.readFullString(input); // Then set the full path of the BAR file file.barFilePath = barFilePath; // Lastly add this file to the List files.Add(file); } } // Have we made it this far? We must have // been successful, return true. return true; } catch (Exception e) { // An error has occurred. Return false! return false; } finally { // Make sure that whatever happens the file // stream is closed! if (input != null) input.Close(); } }
/// <summary> /// Saves a file that is stored in the BAR file to an external file. The name /// and path for the file is specified by the file itself, however the root of that /// path can be defined by the user (or if left blank will be the directory the /// program is running). /// </summary> /// <param name="file">The BarEntry object detailing the file to be saved</param> /// <param name="savePath">The path where the item is to be saved (blank for the current directory)</param> public void saveFile(BarEntry file, string savePath) { // Could be doing some risky stuff here, so try... try { // Firstly, is the file parameter null? if (file == null) throw new Exception("Supplied file was null - Could not save!"); // Instantiate the input stream, opening the BAR // file specified in the barFilePath variable input = new FileStream(barFilePath, FileMode.Open); // Locate the file within the BAR file. input.Seek(file.offset, SeekOrigin.Begin); // Work out the full path of the file to be saved. string newPath = savePath; // Work out the directory path where the file is to be saved... string dirPath = newPath.Substring(0, newPath.LastIndexOf('\\') + 1); // ...Now check that the directories exist! If the // directories don't exist then the path must be created! // Of course, that's only if we *need* directories. if (dirPath != "") { if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); } // Now using a FileStream / BinaryWriter combo, write the data as it is // read from the BAR file to the new file (which will be overwritten if // it already exists, perhaps a bool check method could be used to make // sure the user doesn't overwrite something they've modded?) using (FileStream output = new FileStream(newPath, FileMode.Create)) { using (BinaryWriter writer = new BinaryWriter(output)) { // How many bytes are left? Well currently all of them! uint bytesRemaining = file.fileSize; // Define a 128K buffer (this is what AoE3Ed uses and // it seems to work perfectly). uint bufferSize = 128 * 1024; byte[] buffer = new byte[bufferSize]; // Now loop through the file as long as there are bytes // remaining. while (bytesRemaining > 0) { // If the buffer is still smaller than the file // then use it as is, then remove the buffer size // from bytes remaining. Otherwise resize the // buffer and read only what is needed before // setting the buffer to zero to end the loop. if (bytesRemaining >= bufferSize) { input.Read(buffer, 0, (int)bufferSize); bytesRemaining -= bufferSize; } else { buffer = new byte[bytesRemaining]; input.Read(buffer, 0, (int)bytesRemaining); bytesRemaining = 0; } // Write the contents of the buffer to the file! writer.Write(buffer); } // Once we're finished we need to close the writer. writer.Close(); } } } catch (Exception e) { // Have we caught an exception? Tell the user // about it... Console.Out.WriteLine(e.StackTrace); } finally { // Ensure the input is closed input.Close(); } }