Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.IO;
- namespace MTFExtractor {
- struct DataEntry {
- public string Path;
- public int Offset;
- public int Size;
- }
- class MtfExtractor {
- List<string> filesToExtract;
- List<DataEntry> dataEntries;
- public MtfExtractor(List<string> mtfFiles) {
- filesToExtract = mtfFiles;
- }
- public void Execute() {
- foreach (var f in filesToExtract) {
- Console.WriteLine("Processing {0}...", f);
- try {
- using (var fileStream = new FileStream(f, FileMode.Open))
- using (var binaryReader = new BinaryReader(fileStream)) {
- ProcessFile(binaryReader);
- }
- }
- catch (Exception e) {
- var errorMessage = string.Format("Error processing file {0} : {1}\n", f, e.Message);
- File.AppendAllText("log.txt", errorMessage);
- Console.WriteLine(errorMessage);
- }
- }
- }
- void ProcessFile(BinaryReader binaryReader) {
- dataEntries = new List<DataEntry>();
- int numEntries = binaryReader.ReadInt32();
- Console.WriteLine("{0} entries found.", numEntries);
- for (int i = 0; i < numEntries; ++i) {
- CreateEntry(binaryReader);
- }
- for (int i = 0; i < numEntries; ++i) {
- ProcessEntry(dataEntries[i], binaryReader);
- }
- }
- void ProcessEntry(DataEntry dataEntry, BinaryReader binaryReader) {
- binaryReader.BaseStream.Seek(dataEntry.Offset, SeekOrigin.Begin);
- var fileInfo = new FileInfo(dataEntry.Path);
- fileInfo.Directory.Create();
- using (var file = File.Create(dataEntry.Path)) {
- ProcessData(binaryReader, file, dataEntry);
- }
- }
- void ProcessData(BinaryReader binaryReader, FileStream file, DataEntry entry) {
- // Check if it's compressed or not
- int idSize = 4;
- var ID = binaryReader.ReadBytes(idSize);
- // If it's compressed, the first two bytes are AF BE or AE BE
- if (ID[1] == 0xBE && (ID[0] == 0xAF || ID[0] == 0xAE)) {
- ProcessCompressedData(binaryReader, file, entry);
- }
- else {
- // otherwise these bytes are part of the data, so seek back
- binaryReader.BaseStream.Seek(-idSize, SeekOrigin.Current);
- ProcessUncompressedData(binaryReader, file, entry);
- }
- }
- // If it's uncompressed, just copy-paste the data in the destination file.
- void ProcessUncompressedData(BinaryReader binaryReader, FileStream file, DataEntry entry) {
- file.Write(binaryReader.ReadBytes(entry.Size), 0, entry.Size);
- }
- // If it's compressed... argh.
- // Decompression pseudo-code from http://wiki.xentax.com/index.php/Darkstone
- void ProcessCompressedData(BinaryReader binaryReader, FileStream file, DataEntry entry) {
- // Skip "numBlocks" and "flags", they're of no use
- binaryReader.BaseStream.Seek(8, SeekOrigin.Current);
- var decompressedBytes = new byte[entry.Size];
- var outputIndex = 0;
- // "This is repeated until decompressed buffer reach the Decompressed File Length in MTF header."
- while (outputIndex < entry.Size) {
- // "First byte of each chunk describes what to do with next data you read."
- byte indicator = binaryReader.ReadByte();
- // "You need to check every bit:"
- for (int j = 0; j < 8; ++j) {
- if ((indicator & (1 << j)) != 0) {
- // "If bit=1 then just copy 1 byte from compressed buffer to decompressed buffer"
- decompressedBytes[outputIndex++] = binaryReader.ReadByte();
- }
- else {
- // "If bit=0 then read 10 bits for X and 6 bits for Y" - "07 0C means X = 3 and Y = 7"
- // This is very confusing. What is really meant is:
- // Read 2 bytes, swap them (reading an Int16 in Little-Endian does the trick)
- ushort word = (ushort)binaryReader.ReadInt16();
- // For some reason, if and only if word == 0, we end up writing past our output buffer.
- // It appears we can safely ignore all of these.
- if (word == 0) {
- break;
- }
- byte X = (byte)(word >> 10); // The first 6 bits are then X
- ushort Y = (ushort)(word & 0x3FF); // And the last 10 bits are Y
- // "copy X+3 bytes from offset Y of decompressed buffer at the end of decompressed buffer"
- for (int k = 0; k < X + 3; ++k) {
- decompressedBytes[outputIndex] = decompressedBytes[outputIndex - Y];
- ++outputIndex;
- }
- }
- }
- }
- file.Write(decompressedBytes, 0, decompressedBytes.Length);
- }
- void CreateEntry(BinaryReader binaryReader) {
- int stringLength = binaryReader.ReadInt32();
- var entry = new DataEntry {
- Path = UnsafeAsciiBytesToString(binaryReader.ReadBytes(stringLength)),
- Offset = binaryReader.ReadInt32(),
- Size = binaryReader.ReadInt32()
- };
- dataEntries.Add(entry);
- }
- string UnsafeAsciiBytesToString(byte[] buffer) {
- unsafe {
- fixed (byte* pAscii = buffer) {
- return new string((sbyte*)pAscii);
- }
- }
- }
- static void Main(string[] args) {
- var mtfFiles = new List<string>();
- // If file specified, extract that file, otherwise extract all mtf files in current working directory
- if (args.Length == 1) {
- mtfFiles.Add(args[0]);
- }
- else {
- foreach (var f in Directory.EnumerateFiles(Environment.CurrentDirectory)) {
- if (f.EndsWith(".MTF", StringComparison.CurrentCultureIgnoreCase)) {
- mtfFiles.Add(f);
- }
- }
- }
- new MtfExtractor(mtfFiles).Execute();
- Console.WriteLine("All done!");
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement