Exemple #1
0
        public void Save(string filename)
        {
            long bk;

            labelCount = 0;

            if (type == 2)
            {
                using (BinaryWriterX br = new BinaryWriterX(File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)))
                {
                    br.Write(pckEntries.Count);
                    for (int i = 0; i < pckEntries.Count; i++)
                    {
                        br.WriteStruct <pckEntryStruct>(pckEntries[i]);
                    }
                    for (int i = 0; i < pckEntries.Count; i++)
                    {
                        int offset = (int)br.BaseStream.Length;

                        int basis = 0;
                        if (pckCrc32Count[i] != -1)
                        {
                            br.Write((short)0x64);
                            br.Write((short)pckCrc32Count[i]);
                            br.BaseStream.Position += pckCrc32Count[i] * 4;
                            basis = pckCrc32Count[i] * 4 + 4;
                        }

                        BinaryReaderX br2 = createCfg(i);
                        br2.BaseStream.Position = 0;
                        br.Write(br2.ReadBytes((int)br2.BaseStream.Length));

                        bk = br.BaseStream.Position;
                        br.BaseStream.Position = i * 3 * 4 + 4 + 4;
                        br.Write(offset);
                        br.Write((int)br2.BaseStream.Length + 4 + pckCrc32Count[i] * 4);

                        br.BaseStream.Position = bk;
                    }
                    br.Close();
                }
            }
            else if (type == 1)
            {
                using (BinaryWriterX br = new BinaryWriterX(File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)))
                {
                    BinaryReaderX br2 = createCfg(0);
                    br2.BaseStream.Position = 0;
                    br.Write(br2.ReadBytes((int)br2.BaseStream.Length));
                    br.Close();
                }
            }
        }
Exemple #2
0
        public static string Pack(string filename, string path)
        {
            string result = "Files successfully packed.";

            if (Directory.Exists(path))
            {
                FileStream    fs = System.IO.File.Create(filename);
                BinaryWriterX bw = new BinaryWriterX(fs);

                try
                {
                    string[] files = Directory.GetFiles(path, "*.msbt");

                    UInt32 offsetsLength = ((UInt32)files.Length + 1) * (sizeof(UInt32) * 2);
                    UInt32 runningTotal  = 0;

                    foreach (string file in files)
                    {
                        FileInfo fi = new FileInfo(file);

                        UInt32 offset = offsetsLength + runningTotal;
                        UInt32 size   = (UInt32)fi.Length;
                        runningTotal += (UInt32)fi.Length;

                        bw.Write(offset);
                        bw.Write(size);
                    }

                    bw.Write(0x00000000);
                    bw.Write(0x00000000);

                    foreach (string file in files)
                    {
                        FileStream    fs2 = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
                        BinaryReaderX br  = new BinaryReaderX(fs2);

                        bw.Write(br.ReadBytes((Int32)fs2.Length));
                        br.Close();
                    }
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    bw.Close();
                }
            }

            return(result);
        }
Exemple #3
0
        /// <summary>
        /// Creates an RARC archive from the given directory and settings.
        /// </summary>
        /// <param name="directory">The directory to be converted to an RARC archive.</param>
        /// <param name="archiveType">The type of archive this will be treated as.</param>
        /// <param name="compressionType">The compression type of the final archive, if one is desired.</param>
        public RARC(string directory, ArchiveType archiveType = ArchiveType.MemoryArchive, CompressionType compressionType = CompressionType.None)
        {
            if (!Directory.Exists(directory))
            {
                throw new ArgumentException("Constructor expects a valid directory!", nameof(directory));
            }

            var archiveName = $"{Path.GetFileName(directory)}.arc";

            if (compressionType != CompressionType.None)
            {
                archiveName += compressionType == CompressionType.SZP ? ".szp" : ".szs";
            }
            var archiveFileName = Path.Combine(Path.GetDirectoryName(directory), archiveName);

            using var writer = new BinaryWriterX(File.Create(archiveFileName), ByteOrder.BigEndian);
            Header           = new RARCHeader();
            CreateArchive(writer, directory, archiveType, compressionType);
            writer.Close();

            // Compress the archive if requested
            if (compressionType != CompressionType.None)
            {
                var fileData = File.ReadAllBytes(archiveFileName);
                switch (compressionType)
                {
                case CompressionType.SZP:
                    var szpData = Yay0.Compress(fileData);
                    if (szpData.Length < fileData.Length)
                    {
                        File.WriteAllBytes(archiveFileName, szpData);
                    }
                    break;

                case CompressionType.SZS:
                    var szsData = Yaz0.Compress(fileData);
                    if (szsData.Length < fileData.Length)
                    {
                        File.WriteAllBytes(archiveFileName, szsData);
                    }
                    break;
                }
            }
        }
Exemple #4
0
        // Saving
        public bool Save(string filename)
        {
            bool result = false;

            try
            {
                using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    BinaryWriterX bw = new BinaryWriterX(fs);

                    // Byte Order
                    bw.ByteOrder = Header.ByteOrderMark[0] > Header.ByteOrderMark[1] ? ByteOrder.LittleEndian : ByteOrder.BigEndian;

                    // Header
                    bw.WriteASCII(Header.Identifier);
                    bw.Write(Header.ByteOrderMark);
                    bw.Write(Header.Unknown1);
                    bw.Write((byte)Header.EncodingByte);
                    bw.Write(Header.Unknown2);
                    bw.Write(Header.NumberOfSections);
                    bw.Write(Header.Unknown3);
                    bw.Write(Header.FileSize);
                    bw.Write(Header.Unknown4);

                    foreach (string section in SectionOrder)
                    {
                        if (section == "LBL1")
                        {
                            WriteLBL1(bw);
                        }
                        else if (section == "NLI1")
                        {
                            WriteNLI1(bw);
                        }
                        else if (section == "ATO1")
                        {
                            WriteATO1(bw);
                        }
                        else if (section == "ATR1")
                        {
                            WriteATR1(bw);
                        }
                        else if (section == "TSY1")
                        {
                            WriteTSY1(bw);
                        }
                        else if (section == "TXT2")
                        {
                            WriteTXT2(bw);
                        }
                    }

                    // Update FileSize
                    long fileSize = bw.BaseStream.Position;
                    bw.BaseStream.Seek(Header.FileSizeOffset, SeekOrigin.Begin);
                    bw.Write((uint)fileSize);

                    bw.Close();

                    result = true;
                }
            }
            catch (Exception)
            { }

            return(result);
        }
Exemple #5
0
        // Manipulation
        //TODO: Manipulation functions

        // Saving
        public bool Save(string filename)
        {
            bool result = false;

            try
            {
                using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    BinaryWriterX bw = new BinaryWriterX(fs);

                    // Header
                    bw.WriteASCII(Header.Identifier + "\0");
                    bw.Write(Header.Unknown1);
                    bw.Write(Header.Unknown2);
                    bw.Write(Header.Unknown3);
                    bw.Write(Header.NumberOfLabels);
                    bw.Write(Header.NumberOfOffsets);
                    bw.Write(Header.Unknown4);
                    long dataSizeOffset = bw.BaseStream.Position;
                    bw.Write(Header.DataSize);
                    bw.Write(Header.NameLength);
                    bw.WriteASCII(Header.Name + "\0");

                    foreach (Label label in Labels)
                    {
                        bw.Write(label.ID);
                        bw.Write(label.Unknown1);
                        bw.Write(label.Unknown2);
                        bw.Write(label.Unknown3);
                        bw.Write(label.Unknown4);
                    }

                    bw.Write(Unknown1024);

                    // Read in the label names
                    foreach (Label label in Labels)
                    {
                        bw.WriteASCII(label.Name);
                        bw.Write(new byte[] { 0x0 });
                    }

                    // Read in the text data
                    uint dataSize = 0;
                    foreach (Label label in Labels)
                    {
                        bw.Write(label.Text);
                        bw.Write(new byte[] { 0x0 });
                        dataSize += (uint)label.Text.Length + 1;
                    }

                    // Update DataSize
                    bw.BaseStream.Seek(dataSizeOffset, SeekOrigin.Begin);
                    bw.Write(dataSize);

                    bw.Close();

                    result = true;
                }
            }
            catch (Exception)
            { }

            return(result);
        }
Exemple #6
0
        // Injector

        private void workerInjector_DoWork(object sender, DoWorkEventArgs e)
        {
            List <Entry> injectedEntries = new List <Entry>();

            // Sort Entries
            if (_kup.OptimizeStrings)
            {
                _kup.Entries = _kup.Entries.Select(o => o).OrderByDescending(o => _kup.Encoding.GetByteCount(o.EditedText)).ThenBy(o => o.EditedText).ToList();
            }

            // Bound Setup
            foreach (Bound bound in _kup.StringBounds)
            {
                bound.NextAvailableOffset = bound.StartLong;
            }

            // Copy Injection File
            File.Copy(txtFilename.Text, txtFilename.Text + ".injected", true);
            FileInfo codeFile = new FileInfo(txtFilename.Text + ".injected");

            // Begin Injection
            FileStream    fs = new FileStream(codeFile.FullName, FileMode.Open, FileAccess.Write, FileShare.Read);
            BinaryWriterX bw = new BinaryWriterX(fs, ByteOrder.LittleEndian);

            _workerInjector.ReportProgress(0, "STATUS|Injecting strings...");

            bool outOfSpace = false;
            int  count = 0, optimizedCount = 0;

            foreach (Entry entry in _kup.Entries)
            {
                byte[] editedText = _kup.Encoding.GetBytes(entry.EditedText);
                count++;

                if (entry.Relocatable)
                {
                    // Optimization pass
                    bool optimized = false;

                    if (_kup.OptimizeStrings)
                    {
                        foreach (Entry injectedEntry in injectedEntries)
                        {
                            if (injectedEntry.EditedText.EndsWith(entry.EditedText))
                            {
                                byte[] injectedText = _kup.Encoding.GetBytes(injectedEntry.EditedText);
                                // Update the pointer
                                foreach (Pointer pointer in entry.Pointers)
                                {
                                    bw.BaseStream.Seek(pointer.AddressLong, SeekOrigin.Begin);
                                    bw.Write((uint)(injectedEntry.InjectedOffsetLong + (injectedText.Length - editedText.Length) + _kup.RamOffsetUInt));
                                }
                                optimized = true;
                                optimizedCount++;
                                break;
                            }
                        }
                    }

                    if (!optimized)
                    {
                        // Select bound
                        Bound bound = null;
                        foreach (Bound stringBound in _kup.StringBounds)
                        {
                            if (!stringBound.Full && stringBound.Injectable && editedText.Length < stringBound.SpaceRemaining)
                            {
                                bound = stringBound;
                                break;
                            }
                        }

                        if (bound != null)
                        {
                            // Update the pointer
                            foreach (Pointer pointer in entry.Pointers)
                            {
                                bw.BaseStream.Seek(pointer.AddressLong, SeekOrigin.Begin);
                                bw.Write((uint)(bound.NextAvailableOffset + _kup.RamOffsetUInt));
                            }

                            // Write the string
                            bw.BaseStream.Seek(bound.NextAvailableOffset, SeekOrigin.Begin);
                            bw.Write(editedText);
                            entry.InjectedOffsetLong = bound.NextAvailableOffset;
                            if (_kup.Encoding.IsSingleByte)
                            {
                                bw.Write(new byte[] { 0x0 });
                            }
                            else
                            {
                                bw.Write(new byte[] { 0x0, 0x0 });
                            }
                            bound.NextAvailableOffset = bw.BaseStream.Position;

                            injectedEntries.Add(entry);
                        }
                        else
                        {
                            // Ran out of injection space
                            outOfSpace = true;
                            break;
                        }
                    }
                }
                else
                {
                    // In place string update
                    bw.BaseStream.Seek(entry.OffsetLong, SeekOrigin.Begin);
                    var start = bw.BaseStream.Position;
                    bw.Write(editedText, 0, Math.Min(editedText.Length, entry.MaxLength));
                    if (_kup.Encoding.IsSingleByte)
                    {
                        if (entry.MaxLength - (bw.BaseStream.Position - start) > 0)
                        {
                            bw.Write(new byte[] { 0x0 });
                        }
                    }
                    else
                    {
                        if (entry.MaxLength - (bw.BaseStream.Position - start) > 1)
                        {
                            bw.Write(new byte[] { 0x0, 0x0 });
                        }
                    }
                }

                _workerInjector.ReportProgress((int)(((double)count) / ((double)_kup.Entries.Count) * prgBottom.Maximum), "BOTTOM");
            }

            if (outOfSpace)
            {
                _workerInjector.ReportProgress(0, "STATUS|The injector has run out of space to inject strings.");
                _workerInjector.ReportProgress(0, "STATUS|Injected " + injectedEntries.Count + " strings...");
                if (_kup.OptimizeStrings)
                {
                    _workerInjector.ReportProgress(0, "STATUS|Optimized " + optimizedCount + " strings...");
                }
                _workerInjector.ReportProgress(0, "STATUS|" + (_kup.Entries.Count - count) + " strings were not injected.");
            }
            else
            {
                _workerInjector.ReportProgress(0, "STATUS|Injected " + injectedEntries.Count + " strings...");
                if (_kup.OptimizeStrings)
                {
                    _workerInjector.ReportProgress(0, "STATUS|Optimized " + optimizedCount + " strings...");
                }
            }

            bw.Close();
        }
Exemple #7
0
        public bool Save(string filename)
        {
            bool result = false;

            try
            {
                using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    BinaryWriterX bw = new BinaryWriterX(fs);

                    // Header
                    bw.WriteASCII(Header.Identifier);
                    uint fileSizeOffset = (uint)bw.BaseStream.Position;
                    bw.Write(Header.FileSize);
                    bw.Write(Header.NumberOfEntries);
                    bw.Write(Header.Version);
                    uint labelsOffset = 0;
                    if (Header.Version == 0x11)
                    {
                        bw.Write(Header.HasLabels);
                        labelsOffset = (uint)bw.BaseStream.Position;
                        bw.Write(Header.LabelsOffset - Header.Size);
                    }

                    uint entryStart = Header.Size;
                    uint textStart  = (uint)bw.BaseStream.Position + (uint)(Labels.Count * 2 * 8);

                    // Text
                    bw.BaseStream.Seek(textStart, SeekOrigin.Begin);
                    for (int i = 0; i < Header.NumberOfEntries; i++)
                    {
                        Label label = Labels[i];
                        label.TextOffset = (uint)bw.BaseStream.Position - Header.Size;
                        bw.Write(FileEncoding.GetBytes(label.Text));
                        bw.Write(new byte[] { 0x0, 0x0 });
                    }

                    // Extra
                    for (int i = 0; i < Header.NumberOfEntries; i++)
                    {
                        Label label = Labels[i];
                        label.ExtraOffset = (uint)bw.BaseStream.Position - Header.Size;
                        bw.Write(FileEncoding.GetBytes(label.Extra));
                        bw.Write(new byte[] { 0x0, 0x0 });
                    }

                    // Pad to the nearest 8 bytes
                    PaddingWrite(bw, Header.Version == 0x11 ? 8 : 4);

                    // Set label offset variables
                    uint labelsOffsets = (uint)bw.BaseStream.Position;
                    uint labelsStart   = (uint)bw.BaseStream.Position + (uint)(Labels.Count * 4);

                    // Grab the new LabelsOffset
                    if (Header.HasLabels == 0x0101)
                    {
                        Header.LabelsOffset = (uint)bw.BaseStream.Position - Header.Size;
                    }
                    else
                    {
                        Header.LabelsOffset = 0;
                    }

                    // Text Offsets
                    bw.BaseStream.Seek(entryStart, SeekOrigin.Begin);
                    for (int i = 0; i < Header.NumberOfEntries; i++)
                    {
                        Label label = Labels[i];
                        bw.Write(label.TextID);
                        bw.Write(label.TextOffset);
                    }
                    // Extra Offsets
                    for (int i = 0; i < Header.NumberOfEntries; i++)
                    {
                        Label label = Labels[i];
                        bw.Write(label.ExtraID);
                        bw.Write(label.ExtraOffset);
                    }

                    // Labels
                    if (Header.HasLabels == 0x0101)
                    {
                        // Label Names
                        bw.BaseStream.Seek(labelsStart, SeekOrigin.Begin);
                        for (int i = 0; i < Header.NumberOfEntries; i++)
                        {
                            Label label = Labels[i];
                            label.NameOffset = (uint)bw.BaseStream.Position - Header.Size;
                            bw.WriteASCII(label.Name);
                            bw.Write((byte)0x0);
                        }

                        // Pad to the nearest 8 bytes
                        PaddingWrite(bw, Header.Version == 0x11 ? 8 : 4);

                        // Label Offsets
                        bw.BaseStream.Seek(labelsOffsets, SeekOrigin.Begin);
                        for (int i = 0; i < Header.NumberOfEntries; i++)
                        {
                            bw.Write(Labels[i].NameOffset);
                        }

                        // Update LabelsOffset
                        bw.BaseStream.Seek(labelsOffset, SeekOrigin.Begin);
                        bw.Write(Header.LabelsOffset);
                    }

                    // Update FileSize
                    Header.FileSize = (uint)bw.BaseStream.Length;
                    bw.BaseStream.Seek(fileSizeOffset, SeekOrigin.Begin);
                    bw.Write(Header.FileSize);

                    bw.Close();
                }

                result = true;
            }
            catch (Exception)
            { }

            return(result);
        }
Exemple #8
0
        /// <summary>
        /// Writes the current <see cref="CpkTable"/> data to the output <see cref="Stream"/>.
        /// </summary>
        /// <param name="output"></param>
        public void Save(Stream output)
        {
            using (var bw = new BinaryWriterX(output, true))
            {
                var valuesOffset = 0;
                var binaryOffset = 0;
                var nameOffset   = 0;

                // Write the main header
                bw.WriteType(Header);

                // Switch to BE
                bw.ByteOrder = ByteOrder.BigEndian;

                // Write the table info
                bw.WriteType(TableInfo);

                #region Strings

                var strings      = new Dictionary <string, int>();
                var stringStream = new BinaryWriterX(new MemoryStream());

                void AddString(string str)
                {
                    if (strings.ContainsKey(str))
                    {
                        return;
                    }
                    strings.Add(str, (int)stringStream.BaseStream.Position);
                    stringStream.WriteString(str, Encoding.ASCII, false);
                }

                // NULL
                AddString("<NULL>");

                // Table name
                AddString(Name);

                // Column names and constant value strings
                foreach (var column in Columns)
                {
                    AddString(column.Name);
                    if (column.Storage == CpkColumnStorage.Const && column.Type == CpkDataType.String)
                    {
                        AddString((string)column.Value.Value);
                    }
                }

                // Row column value strings
                foreach (var row in Rows)
                {
                    foreach (var column in Columns)
                    {
                        if (column.Storage == CpkColumnStorage.Row && column.Type == CpkDataType.String)
                        {
                            AddString((string)row[column.Name].Value);
                        }
                    }
                }

                // Reset string stream position
                stringStream.BaseStream.Position = 0;

                #endregion

                #region Data

                // TODO: Handle saving binary data columns

                #endregion

                // Write out columns
                foreach (var column in Columns)
                {
                    var flags = (byte)((int)column.Storage ^ (int)column.Type);
                    bw.Write(flags);
                    bw.Write(strings[column.Name]);
                    switch (column.Storage)
                    {
                    case CpkColumnStorage.Const:
                        WriteValue(bw, column.Value, column, strings);
                        break;
                    }
                }

                // Write out rows
                foreach (var row in Rows)
                {
                    foreach (var column in Columns)
                    {
                        if (column.Storage == CpkColumnStorage.Row)
                        {
                            WriteValue(bw, row[column.Name], column, strings);
                        }
                    }
                }

                // Update Strings Offset and write out the stings
                TableInfo.StringsOffset = (int)bw.BaseStream.Position - 0x18;
                stringStream.BaseStream.CopyTo(bw.BaseStream);
                stringStream.Close();

                // Align to nearest 8 bytes
                bw.WriteAlignment(8);
            }
        }
Exemple #9
0
        public static string Extract(string filename, string path, bool overwrite = true)
        {
            string result = "Files successfully extracted.";

            if (File.Exists(filename) && new FileInfo(filename).Length > 0)
            {
                FileStream    fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None);
                BinaryReaderX br = new BinaryReaderX(fs);

                try
                {
                    // Read in file headers
                    List <FileEntry> entries = new List <FileEntry>();
                    FileEntry        entry   = new FileEntry();
                    uint             index   = 0;

                    while (1 == 1 && br.BaseStream.Position < br.BaseStream.Length)
                    {
                        entry.Offset = br.ReadUInt32();
                        entry.Size   = br.ReadUInt32();
                        entry.Index  = index;

                        if (entry.Offset == 0 && entry.Size == 0)
                        {
                            break;
                        }
                        else
                        {
                            entries.Add(entry);
                        }

                        entry = new FileEntry();
                        index++;
                    }

                    // Extract!
                    for (int i = 0; i < entries.Count; i++)
                    {
                        FileEntry fe        = entries[i];
                        string    extension = ".msbt";
                        string    outFile   = Path.Combine(path, fe.Index.ToString("00000000") + extension);

                        Debug.Print("[" + fe.Offset.ToString("X8") + "] " + fe.Index + extension);

                        if (!File.Exists(outFile) || (File.Exists(outFile) && overwrite))
                        {
                            FileStream    fsr = new FileStream(outFile, FileMode.Create, FileAccess.Write, FileShare.None);
                            BinaryWriterX bw  = new BinaryWriterX(fsr);

                            br.BaseStream.Seek(fe.Offset, SeekOrigin.Begin);
                            bw.Write(br.ReadBytes((int)fe.Size));
                            bw.Close();
                        }
                    }

                    result = entries.Count + " files were found and " + entries.Count + " files were successfully extracted!";
                }
                catch (InvalidUMSBTException ibex)
                {
                    result = ibex.Message;
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    br.Close();
                }
            }

            return(result);
        }
Exemple #10
0
        public static string Extract(string filename, string path, bool overwrite = true)
        {
            string result = "Files successfully extracted.";

            if (File.Exists(filename) && new FileInfo(filename).Length > 0)
            {
                FileStream    fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None);
                BinaryReaderX br = new BinaryReaderX(fs);

                try
                {
                    string magic = Encoding.ASCII.GetString(br.ReadBytes(4));

                    if (magic != "BG4\0")
                    {
                        throw new InvalidBG4Exception("The file provided is not a valid BG4 archive.");
                    }

                    ushort Constant1                 = br.ReadUInt16();
                    ushort NumberOfHeaders           = br.ReadUInt16();
                    uint   SizeOfHeaders             = br.ReadUInt32();
                    ushort NumberOfHeadersDerived    = br.ReadUInt16();
                    ushort NumberOfHeadersMultiplier = br.ReadUInt16();

                    // Read in file headers
                    List <FileEntry> entries = new List <FileEntry>();
                    FileEntry        entry   = new FileEntry();

                    for (int i = 0; i < NumberOfHeaders; i++)
                    {
                        uint offsetTemp = br.ReadUInt32();
                        if ((offsetTemp & 0x80000000) == 0x80000000)
                        {
                            entry.Compressed = true;
                        }
                        entry.Offset    = entry.Compressed ? (offsetTemp ^ 0x80000000) : offsetTemp;
                        entry.Size      = br.ReadUInt32();
                        entry.Unknown1  = br.ReadUInt32();
                        entry.NameIndex = br.ReadUInt16();

                        if (entry.Unknown1 != 0xFFFFFFFF)
                        {
                            entries.Add(entry);
                        }

                        entry = new FileEntry();
                    }

                    // Sort the file entries into NameIndex order
                    entries.Sort();

                    // Read in file names
                    bool          eofn      = false;
                    List <string> filenames = new List <string>();

                    while (!eofn)
                    {
                        if (br.PeekString(2) == Encoding.ASCII.GetString(new byte[] { 0xFF, 0xFF }))
                        {
                            eofn = true;
                        }
                        else
                        {
                            bool   eos  = false;
                            string name = string.Empty;

                            while (!eos)
                            {
                                byte chr = br.ReadByte();

                                if (chr == 0x00)
                                {
                                    eos = true;
                                }
                                else
                                {
                                    name += (char)chr;
                                }
                            }

                            if (name != "(invalid)")
                            {
                                filenames.Add(name);
                            }
                        }
                    }

                    // Extract!
                    for (int i = 0; i < entries.Count; i++)
                    {
                        FileEntry fe        = entries[i];
                        string    extension = (!Regex.IsMatch(filenames[i], @"\..*?$") ? ".bin" : string.Empty);

                        if (extension != string.Empty)
                        {
                            br.BaseStream.Seek(fe.Offset, SeekOrigin.Begin);
                            magic = Encoding.ASCII.GetString(br.ReadBytes(8));

                            if (magic.StartsWith("MsgStdBn"))
                            {
                                extension = ".msbt";
                            }
                            else if (magic.StartsWith("BCH"))
                            {
                                extension = ".bch";
                            }
                            else if (magic.StartsWith("PTX"))
                            {
                                extension = ".ptx";
                            }

                            // TODO: Add more known magic/extension pairs
                        }

                        Debug.Print("[" + fe.Offset.ToString("X8") + "] " + fe.NameIndex + " (" + fe.Unknown1 + ") " + filenames[i] + extension);

                        FileInfo      fi  = new FileInfo(filename);
                        FileStream    fsr = new FileStream(Path.Combine(path, filenames[i] + extension), FileMode.Create, FileAccess.Write, FileShare.None);
                        BinaryWriterX bw  = new BinaryWriterX(fsr);

                        br.BaseStream.Seek(fe.Offset, SeekOrigin.Begin);
                        bw.Write(br.ReadBytes((int)fe.Size));
                        bw.Close();
                    }

                    result = NumberOfHeaders + " header(s) were scanned and " + entries.Count + " files were successfully extracted!";
                }
                catch (InvalidBG4Exception ibex)
                {
                    result = ibex.Message;
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
                finally
                {
                    br.Close();
                }
            }

            return(result);
        }