Ejemplo n.º 1
0
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                throw new ArgumentException("Not enough arguments! Required: file, outdir, (build in x.x.x format), (pattern name to always use)");
            }

            if (!File.Exists(args[0]))
            {
                throw new FileNotFoundException("File not found!");
            }

            var file = MemoryMappedFile.CreateFromFile(args[0], FileMode.Open);

            var maps = EXEParsing.GenerateMap(file.CreateViewAccessor());

            ulong translate(ulong search) => maps.Select(map => map.translateMemoryToFile(search)).FirstOrDefault(r => r != 0x0);

            using (var bin = new BinaryReader(file.CreateViewStream()))
            {
                var chunkSize = 1024;

                // Find version
                var buildPattern       = new byte?[] { 0x42, 0x75, 0x69, 0x6C, 0x64, 0x20, null, null, null, null, null, 0x20, 0x28, null, null, null, null, null, 0x29, 0x20, 0x28 };
                var buildPatternLength = buildPattern.Length;

                var build           = "";
                var patternOverride = "";

                if (args.Length >= 3)
                {
                    build = args[2];
                    if (args.Length >= 4)
                    {
                        patternOverride = args[3];
                    }
                }

                if (build == "")
                {
                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), buildPattern);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            bin.BaseStream.Position = matchPos;
                            bin.ReadBytes(6);
                            var buildNumber = new string(bin.ReadChars(5));
                            bin.ReadBytes(2);
                            var patch = new string(bin.ReadChars(5));
                            build = patch + "." + buildNumber;
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - buildPatternLength;
                        }
                    }
                }

                if (build == "")
                {
                    // Retry with backup pattern (crash log output)
                    bin.BaseStream.Position = 0;

                    buildPattern       = new byte?[] { 0x00, 0x3C, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3E, 0x20 }; // <Version>
                    buildPatternLength = buildPattern.Length;

                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), buildPattern);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            bin.BaseStream.Position = matchPos;
                            bin.ReadBytes(11);
                            build = new string(bin.ReadChars(11));
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - buildPatternLength;
                        }
                    }
                }

                if (build == "")
                {
                    // Retry with RenderService pattern..
                    bin.BaseStream.Position = 0;

                    buildPattern       = new byte?[] { 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, null, null, null, null, null, 0x00 }; // <Version>
                    buildPatternLength = buildPattern.Length;

                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), buildPattern);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            bin.BaseStream.Position = matchPos;
                            bin.ReadBytes(14);
                            build = new string(bin.ReadChars(5));
                            if (args.Length == 3)
                            {
                                build = args[2];
                            }
                            else
                            {
                                Console.WriteLine("Expansion, major and minor version not found in binary. Please enter it in this format X.X.X: ");
                                build = Console.ReadLine() + "." + build;
                            }
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - buildPatternLength;
                        }
                    }
                }

                if (build == "")
                {
                    Console.WriteLine("Build was not found! Please enter a build in this format: X.X.X.XXXXX");
                    build = Console.ReadLine();
                }

                if (build == "8.0.1.26321")
                {
                    Console.WriteLine("Build 8.0.1.26321 has incorrect DBMeta, skipping..");
                    return;
                }
                // Reset position for DBMeta reading
                bin.BaseStream.Position = 0;

                // Extract DBMeta
                var metas = new Dictionary <string, DBMeta>();

                var patternBuilder = new PatternBuilder();

                foreach (var pattern in patternBuilder.patterns)
                {
                    if (patternOverride == "")
                    {
                        // Skip versions of the pattern that aren't for this expansion
                        if (build.StartsWith("1"))
                        {
                            if (!pattern.compatiblePatches.Contains(build.Substring(0, 6)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as it does not list " + build + " as compatible!");
                                continue;
                            }

                            if (!pattern.compatiblePatches.Contains(build.Substring(0, 6)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as it does not list " + build + " as compatible!");
                                continue;
                            }

                            if (pattern.minBuild != 0 && pattern.minBuild > int.Parse(build.Substring(7)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as minimum build " + pattern.minBuild + " exceeds build of " + build.Substring(6));
                                continue;
                            }

                            if (pattern.maxBuild != 0 && int.Parse(build.Substring(7)) > pattern.maxBuild)
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as maximum build " + pattern.maxBuild + " exceeds build of " + build.Substring(6));
                                continue;
                            }
                        }
                        else
                        {
                            if (!pattern.compatiblePatches.Contains(build.Substring(0, 5)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as it does not list " + build + " as compatible!");
                                continue;
                            }

                            if (!pattern.compatiblePatches.Contains(build.Substring(0, 5)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as it does not list " + build + " as compatible!");
                                continue;
                            }

                            if (pattern.minBuild != 0 && pattern.minBuild > int.Parse(build.Substring(6)))
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as minimum build " + pattern.minBuild + " exceeds build of " + build.Substring(6));
                                continue;
                            }

                            if (pattern.maxBuild != 0 && int.Parse(build.Substring(6)) > pattern.maxBuild)
                            {
                                Console.WriteLine("Skipping " + pattern.name + " as maximum build " + pattern.maxBuild + " exceeds build of " + build.Substring(6));
                                continue;
                            }
                        }
                    }
                    else
                    {
                        if (patternOverride != pattern.name)
                        {
                            continue;
                        }
                    }

                    var patternBytes  = ParsePattern(pattern.cur_pattern).ToArray();
                    var patternLength = patternBytes.Length;

                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), patternBytes);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            Console.WriteLine("Pattern " + pattern.name + " matched at " + matchPos);

                            if (pattern.offsets.ContainsKey(Name.FDID))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FDID];
                                var fdid = bin.ReadUInt32();
                                if (fdid < 53183)
                                {
                                    Console.WriteLine("Invalid filedataid " + fdid + ", skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.RECORD_SIZE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.RECORD_SIZE];
                                if (bin.ReadUInt32() == 0)
                                {
                                    Console.WriteLine("Record size is 0, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_NAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_NAME];
                                if (bin.ReadUInt32() < 10)
                                {
                                    Console.WriteLine("Name offset is invalid, skipping match..");
                                    continue;
                                }

                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_NAME];
                                var targetOffset = (long)translate(bin.ReadUInt64());
                                if (targetOffset > bin.BaseStream.Length)
                                {
                                    Console.WriteLine("Name offset is out of range of file, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_FILENAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_FILENAME];
                                if (bin.ReadUInt32() < 10)
                                {
                                    Console.WriteLine("Name offset is invalid, skipping match..");
                                    continue;
                                }

                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_FILENAME];
                                var targetOffset = (long)translate(bin.ReadUInt64());
                                if (targetOffset > bin.BaseStream.Length)
                                {
                                    Console.WriteLine("Name offset is out of range of file, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.NUM_FIELD_IN_FILE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.NUM_FIELD_IN_FILE];
                                if (bin.ReadUInt32() > 5000)
                                {
                                    Console.WriteLine("Num fields in file is over 5000, skipping match..");
                                    continue;
                                }
                            }
                            if (pattern.offsets.ContainsKey(Name.FIELD_TYPES_IN_FILE) && pattern.offsets.ContainsKey(Name.FIELD_SIZES_IN_FILE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FIELD_TYPES_IN_FILE];
                                var fieldTypesInFile = bin.ReadInt64();
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FIELD_SIZES_IN_FILE];
                                var fieldSizesInFileOffs = bin.ReadInt64();
                                if (fieldTypesInFile == fieldSizesInFileOffs)
                                {
                                    Console.WriteLine("Field types in file offset == field sizes in file offset, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_CACHE_FILENAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_CACHE_FILENAME];
                                bin.BaseStream.Position = (long)translate((ulong)bin.ReadInt64());
                                var adbname = bin.ReadCString();

                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_CACHE_FILENAME];

                                if (!adbname.EndsWith("adb"))
                                {
                                    Console.WriteLine("ADB filename does not end in adb, skipping match..");
                                    continue;
                                }
                            }

                            bin.BaseStream.Position = matchPos;
                            var meta = ReadMeta(bin, pattern);

                            if (pattern.offsets.ContainsKey(Name.DB_NAME))
                            {
                                bin.BaseStream.Position = (long)translate((ulong)meta.nameOffset);
                                var filename = bin.ReadCString();
                                if (filename.Contains("DBFilesClient"))
                                {
                                    filename = filename.Substring(filename.IndexOf("\\") + 1);
                                }

                                metas.TryAdd(Path.GetFileNameWithoutExtension(filename), meta);
                            }
                            else if (pattern.offsets.ContainsKey(Name.DB_FILENAME))
                            {
                                bin.BaseStream.Position = (long)translate((ulong)meta.dbFilenameOffs);
                                var name = bin.ReadCString();
                                metas.TryAdd(Path.GetFileNameWithoutExtension(name), meta);
                            }

                            bin.BaseStream.Position = matchPos + patternLength;
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - patternLength;
                        }
                    }

                    bin.BaseStream.Position = 0;
                }

                var outputDirectory = Path.Combine(args[1], build);

                if (!Directory.Exists(outputDirectory))
                {
                    Directory.CreateDirectory(outputDirectory);
                }

                // Process DBMetas
                foreach (var meta in metas)
                {
                    if ((long)translate((ulong)meta.Value.field_offsets_offs) > bin.BaseStream.Length)
                    {
                        Console.WriteLine("Skipping reading of " + meta.Key + " because first offset is way out of range!");
                        continue;
                    }

                    var writer = new StreamWriter(Path.Combine(outputDirectory, meta.Key + ".dbd"));

                    writer.WriteLine("COLUMNS");

                    Console.Write("Writing " + meta.Key + ".dbd..");

                    var fieldCount = 0;
                    if (meta.Value.num_fields == 0 && meta.Value.num_fields_in_file != 0)
                    {
                        fieldCount = meta.Value.num_fields_in_file;
                    }
                    else
                    {
                        fieldCount = meta.Value.num_fields;
                    }

                    var field_offsets       = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_offsets_offs));
                    var field_sizes         = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_sizes_offs));
                    var field_types         = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_types_offs));
                    var field_flags         = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_flags_offs));
                    var field_sizes_in_file = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_sizes_in_file_offs));
                    var field_types_in_file = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_types_in_file_offs));
                    var field_flags_in_file = ReadFieldArray(bin, fieldCount, (long)translate((ulong)meta.Value.field_flags_in_file_offs));
                    var field_names_in_file = ReadFieldOffsetArray(bin, fieldCount, (long)translate((ulong)meta.Value.namesInFileOffs));

                    if (meta.Value.id_column == -1)
                    {
                        writer.WriteLine("int ID");
                    }

                    var columnNames     = new List <string>();
                    var columnTypeFlags = new List <Tuple <int, int> >();

                    for (var i = 0; i < meta.Value.num_fields_in_file; i++)
                    {
                        if (field_flags_in_file.Count == 0)
                        {
                            columnTypeFlags.Add(new Tuple <int, int>(field_types_in_file[i], 0));
                        }
                        else
                        {
                            columnTypeFlags.Add(new Tuple <int, int>(field_types_in_file[i], field_flags_in_file[i]));
                        }
                    }

                    if (meta.Value.num_fields != 0 && (meta.Value.num_fields_in_file != meta.Value.num_fields))
                    {
                        if (meta.Value.num_fields_in_file > field_flags.Count())
                        {
                            columnTypeFlags.Add(new Tuple <int, int>(field_types[meta.Value.num_fields_in_file], 0));
                        }
                        else
                        {
                            columnTypeFlags.Add(new Tuple <int, int>(field_types[meta.Value.num_fields_in_file], field_flags[meta.Value.num_fields_in_file]));
                        }
                    }

                    for (var i = 0; i < columnTypeFlags.Count; i++)
                    {
                        if (field_names_in_file.Count > 0)
                        {
                            bin.BaseStream.Position = (long)translate(field_names_in_file[i]);
                            columnNames.Add(CleanRealName(bin.ReadCString()));
                        }
                        else
                        {
                            columnNames.Add(GenerateName(i, build));
                        }

                        var t = TypeToT(columnTypeFlags[i].Item1, (FieldFlags)columnTypeFlags[i].Item2);
                        if (field_names_in_file.Count > 0)
                        {
                            if (t.Item1 == "locstring")
                            {
                                writer.WriteLine(t.Item1 + " " + columnNames[i] + "_lang");
                            }
                            else
                            {
                                if (t.Item1 == "uint")
                                {
                                    writer.WriteLine("int " + columnNames[i]);
                                }
                                else
                                {
                                    writer.WriteLine(t.Item1 + " " + columnNames[i]);
                                }
                            }
                        }
                        else
                        {
                            if (t.Item1 == "locstring")
                            {
                                writer.WriteLine(t.Item1 + " " + columnNames[i] + "_lang?");
                            }
                            else
                            {
                                if (t.Item1 == "uint")
                                {
                                    writer.WriteLine("int " + columnNames[i] + "?");
                                }
                                else
                                {
                                    writer.WriteLine(t.Item1 + " " + columnNames[i] + "?");
                                }
                            }
                        }
                    }

                    writer.WriteLine();

                    if (meta.Value.layout_hash != 0)
                    {
                        writer.WriteLine("LAYOUT " + meta.Value.layout_hash.ToString("X8").ToUpper());
                    }

                    writer.WriteLine("BUILD " + build);

                    if (meta.Value.sparseTable == 1)
                    {
                        writer.WriteLine("COMMENT table is sparse");
                    }

                    if (meta.Value.id_column == -1)
                    {
                        writer.WriteLine("$noninline,id$ID<32>");
                    }

                    for (var i = 0; i < meta.Value.num_fields_in_file; i++)
                    {
                        var typeFlags = ("int", 32);

                        if (field_flags_in_file.Count == 0)
                        {
                            typeFlags = TypeToT(field_types_in_file[i], 0);
                        }
                        else
                        {
                            typeFlags = TypeToT(field_types_in_file[i], (FieldFlags)field_flags_in_file[i]);
                        }

                        if (meta.Value.id_column == i)
                        {
                            writer.Write("$id$");
                        }

                        if (build.StartsWith("7.3.5") || build.StartsWith("8"))
                        {
                            if (meta.Value.column_8C == i)
                            {
                                writer.Write("$relation$");
                                if (meta.Value.column_90 != i)
                                {
                                    throw new Exception("No column_90 but there is column_8C send help!");
                                }
                            }
                        }

                        writer.Write(columnNames[i]);

                        if (typeFlags.Item1 == "locstring")
                        {
                            writer.Write("_lang");
                        }

                        if (typeFlags.Item2 > 0)
                        {
                            if (typeFlags.Item1 == "uint")
                            {
                                writer.Write("<u" + typeFlags.Item2 + ">");
                            }
                            else
                            {
                                writer.Write("<" + typeFlags.Item2 + ">");
                            }
                        }

                        if (field_sizes_in_file[i] != 1)
                        {
                            // 6.0.1 has sizes in bytes
                            if (build.StartsWith("6.0.1"))
                            {
                                var supposedSize = 0;

                                if ((typeFlags.Item1 == "uint" || typeFlags.Item1 == "int") && typeFlags.Item2 != 32)
                                {
                                    supposedSize = typeFlags.Item2 / 8;
                                }
                                else
                                {
                                    supposedSize = 4;
                                }

                                var fixedSize = field_sizes_in_file[i] / supposedSize;
                                if (fixedSize > 1)
                                {
                                    writer.Write("[" + fixedSize + "]");
                                }
                            }
                            else
                            {
                                writer.Write("[" + field_sizes_in_file[i] + "]");
                            }
                        }

                        writer.WriteLine();
                    }

                    if (meta.Value.num_fields != 0 && (meta.Value.num_fields_in_file != meta.Value.num_fields))
                    {
                        var i         = meta.Value.num_fields_in_file;
                        var typeFlags = TypeToT(columnTypeFlags[i].Item1, (FieldFlags)columnTypeFlags[i].Item2);

                        writer.Write("$noninline,relation$" + columnNames[i]);

                        if (typeFlags.Item1 == "locstring")
                        {
                            writer.Write("_lang");
                        }

                        if (typeFlags.Item2 > 0)
                        {
                            if (typeFlags.Item1 == "uint")
                            {
                                writer.Write("<u" + typeFlags.Item2 + ">");
                            }
                            else if (typeFlags.Item1 == "int")
                            {
                                writer.Write("<" + typeFlags.Item2 + ">");
                            }
                        }

                        if (field_sizes[i] != 1)
                        {
                            writer.Write("[" + field_sizes[i] + "]");
                        }
                    }

                    writer.Flush();
                    writer.Close();

                    Console.Write("..done!\n");
                }
            }

            Environment.Exit(0);
        }
Ejemplo n.º 2
0
        static void Main(string[] args)
        {
            if (args.Length < 2)
            {
                throw new ArgumentException("Not enough arguments! Required: file, outdir");
            }

            if (!File.Exists(args[0]))
            {
                throw new FileNotFoundException("File not found!");
            }

            var file   = MemoryMappedFile.CreateFromFile(args[0], FileMode.Open);
            var stream = file.CreateViewAccessor();

            #region BinaryMagic
            long offset = 0;

            stream.Read(offset, out Header header);
            offset += Marshal.SizeOf(header);

            var maps = new List <Map>();

            for (var i = 0; i < header.commandsCount; i++)
            {
                stream.Read(offset, out Command command);

                if (command.id == 25)
                {
                    var segmentOffset = offset + 4 + 4 + 16; // Segment start + id + size + name;
                    var vmemOffs      = stream.ReadUInt64(segmentOffset);
                    var vmemSize      = stream.ReadUInt64(segmentOffset + 8);
                    var fileOffs      = stream.ReadUInt64(segmentOffset + 16);
                    var fileSize      = stream.ReadUInt64(segmentOffset + 24);

                    var map = new Map
                    {
                        memoryStart = vmemOffs,
                        memoryEnd   = vmemOffs + vmemSize,
                        fileStart   = fileOffs,
                        fileEnd     = fileOffs + fileSize
                    };

                    maps.Add(map);
                }

                offset += command.size;
            }


            Func <UInt64, UInt64> translate = search => maps.Select(map => map.translateMemoryToFile(search)).FirstOrDefault(r => r != 0x0);
            #endregion

            using (var bin = new BinaryReader(file.CreateViewStream()))
            {
                var chunkSize = 1024;

                // Find version
                var buildPattern       = new byte?[] { 0x42, 0x75, 0x69, 0x6C, 0x64, 0x20, null, null, null, null, null, 0x20, 0x28, null, null, null, null, null, 0x29, 0x20, 0x28 };
                var buildPatternLength = buildPattern.Length;

                var build = "";

                while (true)
                {
                    if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                    {
                        break;
                    }

                    var posInStack = Search(bin.ReadBytes(chunkSize), buildPattern);

                    if (posInStack != chunkSize)
                    {
                        var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                        bin.BaseStream.Position = matchPos;
                        bin.ReadBytes(6);
                        var buildNumber = new string(bin.ReadChars(5));
                        bin.ReadBytes(2);
                        var patch = new string(bin.ReadChars(5));
                        build = patch + "." + buildNumber;
                    }
                    else
                    {
                        bin.BaseStream.Position = bin.BaseStream.Position - buildPatternLength;
                    }
                }


                if (build == "")
                {
                    // Retry with backup pattern (crash log output)
                    bin.BaseStream.Position = 0;

                    buildPattern       = new byte?[] { 0x00, 0x3C, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3E, 0x20 }; // <Version>
                    buildPatternLength = buildPattern.Length;

                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), buildPattern);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            bin.BaseStream.Position = matchPos;
                            bin.ReadBytes(11);
                            build = new string(bin.ReadChars(11));
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - buildPatternLength;
                        }
                    }
                }

                if (build == "")
                {
                    throw new Exception("Build was not found!");
                }

                // Reset position for DBMeta reading
                bin.BaseStream.Position = 0;

                // Extract DBMeta
                var metas = new Dictionary <string, DBMeta>();

                var patternBuilder = new PatternBuilder();

                foreach (var pattern in patternBuilder.patterns)
                {
                    // Skip versions of the pattern that aren't for this expansion
                    if (build[0] != pattern.name[0])
                    {
                        continue;
                    }

                    var patternBytes  = ParsePattern(pattern.cur_pattern).ToArray();
                    var patternLength = patternBytes.Length;

                    while (true)
                    {
                        if ((bin.BaseStream.Length - bin.BaseStream.Position) < chunkSize)
                        {
                            break;
                        }

                        var posInStack = Search(bin.ReadBytes(chunkSize), patternBytes);

                        if (posInStack != chunkSize)
                        {
                            var matchPos = bin.BaseStream.Position - chunkSize + posInStack;

                            Console.WriteLine("Pattern " + pattern.name + " matched at " + matchPos);

                            if (pattern.offsets.ContainsKey(Name.FDID))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FDID];
                                if (bin.ReadUInt32() < 53183)
                                {
                                    Console.WriteLine("Invalid filedataid, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.RECORD_SIZE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.RECORD_SIZE];
                                if (bin.ReadUInt32() == 0)
                                {
                                    Console.WriteLine("Record size is 0, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_NAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_NAME];
                                if (bin.ReadUInt32() < 10)
                                {
                                    Console.WriteLine("Name offset is invalid, skipping match..");
                                    continue;
                                }

                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_NAME];
                                var targetOffset = (long)translate(bin.ReadUInt64());
                                if (targetOffset > bin.BaseStream.Length)
                                {
                                    Console.WriteLine("Name offset is out of range of file, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_FILENAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_FILENAME];
                                if (bin.ReadUInt32() < 10)
                                {
                                    Console.WriteLine("Name offset is invalid, skipping match..");
                                    continue;
                                }

                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_FILENAME];
                                var targetOffset = (long)translate(bin.ReadUInt64());
                                if (targetOffset > bin.BaseStream.Length)
                                {
                                    Console.WriteLine("Name offset is out of range of file, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.NUM_FIELD_IN_FILE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.NUM_FIELD_IN_FILE];
                                if (bin.ReadUInt32() > 5000)
                                {
                                    Console.WriteLine("Num fields in file is over 5000, skipping match..");
                                    continue;
                                }
                            }
                            if (pattern.offsets.ContainsKey(Name.FIELD_TYPES_IN_FILE) && pattern.offsets.ContainsKey(Name.FIELD_SIZES_IN_FILE))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FIELD_TYPES_IN_FILE];
                                var fieldTypesInFile = bin.ReadInt64();
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.FIELD_SIZES_IN_FILE];
                                var fieldSizesInFileOffs = bin.ReadInt64();
                                if (fieldTypesInFile == fieldSizesInFileOffs)
                                {
                                    Console.WriteLine("Field types in file offset == field sizes in file offset, skipping match..");
                                    continue;
                                }
                            }

                            if (pattern.offsets.ContainsKey(Name.DB_CACHE_FILENAME))
                            {
                                bin.BaseStream.Position = matchPos + pattern.offsets[Name.DB_CACHE_FILENAME];
                                var name = bin.ReadCString();
                                if (!name.EndsWith("adb"))
                                {
                                    Console.WriteLine("ADB filename does not end in adb, skipping match..");
                                    continue;
                                }
                            }

                            bin.BaseStream.Position = matchPos;
                            var meta = ReadMeta(bin, pattern);

                            if (pattern.offsets.ContainsKey(Name.DB_NAME))
                            {
                                bin.BaseStream.Position = (long)translate((ulong)meta.nameOffset);
                                metas.TryAdd(bin.ReadCString(), meta);
                            }
                            else if (pattern.offsets.ContainsKey(Name.DB_FILENAME))
                            {
                                bin.BaseStream.Position = (long)translate((ulong)meta.dbFilenameOffs);
                                var name = bin.ReadCString();
                                metas.TryAdd(Path.GetFileNameWithoutExtension(name), meta);
                            }

                            bin.BaseStream.Position = matchPos + patternLength;
                        }
                        else
                        {
                            bin.BaseStream.Position = bin.BaseStream.Position - patternLength;
                        }
                    }

                    bin.BaseStream.Position = 0;
                }

                var outputDirectory = Path.Combine(args[1], build);

                if (!Directory.Exists(outputDirectory))
                {
                    Directory.CreateDirectory(outputDirectory);
                }

                // Process DBMetas
                foreach (var meta in metas)
                {
                    if ((long)translate((ulong)meta.Value.field_offsets_offs) > bin.BaseStream.Length)
                    {
                        Console.WriteLine("Skipping reading of " + meta.Key + " because first offset is way out of range!");
                        continue;
                    }

                    var writer = new StreamWriter(Path.Combine(outputDirectory, meta.Key + ".dbd"));

                    writer.WriteLine("COLUMNS");

                    Console.Write("Writing " + meta.Key + ".dbd..");

                    var field_offsets = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_offsets_offs);

                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_offsets.Add(bin.ReadInt32());
                    }

                    var field_sizes = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_sizes_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_sizes.Add(bin.ReadInt32());
                    }

                    var field_types = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_types_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_types.Add(bin.ReadInt32());
                    }

                    var field_flags = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_flags_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_flags.Add(bin.ReadInt32());
                    }

                    var field_sizes_in_file = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_sizes_in_file_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_sizes_in_file.Add(bin.ReadInt32());
                    }

                    var field_types_in_file = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_types_in_file_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_types_in_file.Add(bin.ReadInt32());
                    }

                    // Read field flags in file
                    var field_flags_in_file = new List <int>();
                    bin.BaseStream.Position = (long)translate((ulong)meta.Value.field_flags_in_file_offs);
                    for (var i = 0; i < meta.Value.num_fields; i++)
                    {
                        field_flags_in_file.Add(bin.ReadInt32());
                    }

                    if (meta.Value.id_column == -1)
                    {
                        writer.WriteLine("int ID");
                    }

                    var columnNames     = new List <string>();
                    var columnTypeFlags = new List <Tuple <int, int> >();

                    for (var i = 0; i < meta.Value.num_fields_in_file; i++)
                    {
                        columnTypeFlags.Add(new Tuple <int, int>(field_types_in_file[i], field_flags_in_file[i]));
                    }

                    if (meta.Value.num_fields_in_file != meta.Value.num_fields)
                    {
                        columnTypeFlags.Add(new Tuple <int, int>(field_types[meta.Value.num_fields_in_file], field_flags[meta.Value.num_fields_in_file]));
                    }

                    for (var i = 0; i < columnTypeFlags.Count; i++)
                    {
                        columnNames.Add("field_" + new Random().Next(1, int.MaxValue).ToString().PadLeft(9, '0'));

                        var t = TypeToT(columnTypeFlags[i].Item1, (FieldFlags)columnTypeFlags[i].Item2);
                        if (t.Item1 == "locstring")
                        {
                            writer.WriteLine(t.Item1 + " " + columnNames[i] + "_lang");
                        }
                        else
                        {
                            if (t.Item1 == "uint")
                            {
                                writer.WriteLine("int " + columnNames[i]);
                            }
                            else
                            {
                                writer.WriteLine(t.Item1 + " " + columnNames[i]);
                            }
                        }
                    }

                    writer.WriteLine();

                    writer.WriteLine("LAYOUT " + meta.Value.layout_hash.ToString("X8").ToUpper());
                    writer.WriteLine("BUILD " + build);

                    if (meta.Value.sparseTable == 1)
                    {
                        writer.WriteLine("COMMENT table is sparse");
                    }

                    if (meta.Value.id_column == -1)
                    {
                        writer.WriteLine("$noninline,id$ID<32>");
                    }

                    for (var i = 0; i < meta.Value.num_fields_in_file; i++)
                    {
                        var typeFlags = TypeToT(field_types_in_file[i], (FieldFlags)field_flags_in_file[i]);

                        if (meta.Value.id_column == i)
                        {
                            writer.Write("$id$");
                        }

                        if (build.StartsWith("7.3.5") || build.StartsWith("8.0.1"))
                        {
                            if (meta.Value.column_8C == i)
                            {
                                writer.Write("$relation$");
                                if (meta.Value.column_90 != i)
                                {
                                    throw new Exception("No column_90 but there is column_8C send help!");
                                }
                            }
                        }

                        writer.Write(columnNames[i]);

                        if (typeFlags.Item1 == "locstring")
                        {
                            writer.Write("_lang");
                        }

                        if (typeFlags.Item2 > 0)
                        {
                            if (typeFlags.Item1 == "uint")
                            {
                                writer.Write("<u" + typeFlags.Item2 + ">");
                            }
                            else
                            {
                                writer.Write("<" + typeFlags.Item2 + ">");
                            }
                        }

                        if (field_sizes_in_file[i] != 1)
                        {
                            writer.Write("[" + field_sizes_in_file[i] + "]");
                        }

                        writer.WriteLine();
                    }

                    if (meta.Value.num_fields_in_file != meta.Value.num_fields)
                    {
                        var i         = meta.Value.num_fields_in_file;
                        var typeFlags = TypeToT(field_types[i], (FieldFlags)field_flags[i]);

                        writer.Write("$noninline,relation$" + columnNames[i]);

                        if (typeFlags.Item1 == "locstring")
                        {
                            writer.Write("_lang");
                        }

                        if (typeFlags.Item2 > 0)
                        {
                            if (typeFlags.Item1 == "uint")
                            {
                                writer.Write("<u" + typeFlags.Item2 + ">");
                            }
                            else
                            {
                                writer.Write("<" + typeFlags.Item2 + ">");
                            }
                        }

                        if (field_sizes[i] != 1)
                        {
                            writer.Write("[" + field_sizes[i] + "]");
                        }
                    }

                    writer.Flush();
                    writer.Close();

                    Console.Write("..done!\n");
                }
            }

            Environment.Exit(0);
        }