public static Stream getStream(Database db, int typeID, int groupID, long instanceID)
        {
            Stream matchChunk = null;

            int searchType = 0;
            if (typeID != -1) { searchType += 1; }
            if (groupID != -1) { searchType += 2; }
            if (instanceID != -1) { searchType += 4; }

            bool foundMatch = false;

            foreach (MadScience.Wrappers.ResourceKey entry in db._Entries.Keys)
            {
                //ResourceKey key = db.Entries.Keys[i];
                //DatabasePackedFile.Entry entry = db.Entries.Keys[i];
                //DatabasePackedFile.Entry entry = db.dbpfEntries[i];
                //MadScience.Wrappers.ResourceKey entry = new MadScience.Wrappers.ResourceKey(keyString);

                switch (searchType)
                {
                    case 7:
                        if (entry.typeId == typeID && entry.groupId == groupID && entry.instanceId == (ulong)instanceID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 6:
                        if (entry.groupId == groupID && entry.instanceId == (ulong)instanceID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 5:
                        if (entry.typeId == typeID && entry.instanceId == (ulong)instanceID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 4:
                        if (entry.instanceId == (ulong)instanceID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 3:
                        if (entry.typeId == typeID && entry.groupId == groupID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 2:
                        if (entry.groupId == groupID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                    case 1:
                        if (entry.typeId == typeID)
                        {
                            //loadedCasPart = entry;
                            matchChunk = db.GetResourceStream(entry);
                            foundMatch = true;
                        }
                        break;
                }
                if (foundMatch)
                {
                    break;
                }

            }

            return matchChunk;
        }
        public PackageType getType(Database db, bool loopAll)
        {
            // Do some quick sanity checks
            switch (db.dbpf.packageType)
            {
                case PackageTypes.genericPackage:
                    break;
                case PackageTypes.corruptBadDownload:
                case PackageTypes.corruptChaavik:
                case PackageTypes.corruptIndex:
                case PackageTypes.corruptPeggy:
                case PackageTypes.corruptNotADBPF:
                case PackageTypes.corruptTXTC:
                    this.isCorrupt = true;
                    this.pType.SubType = "";
                    this.pType.MainType = db.dbpf.packageType;
                    return this.pType;
                case PackageTypes.sims3Store:
                case PackageTypes.sims2Package:
                case PackageTypes.pngThumbnail:
                    this.pType.SubType = "";
                    this.pType.MainType = db.dbpf.packageType;
                    return this.pType;
            }

            rc.Clear();
            this.pType = new PackageType();

            //print(db.dbpf.Entries.Count + " entries found");
            for (int i = 0; i < db.dbpf.Entries.Count; i++)
            {
                DatabasePackedFile.Entry entry = db.dbpf.Entries[i];

                if ((entry.Key.typeId == (uint)ResourceTypes.NULL) && (entry.Key.groupId == (uint)ResourceTypes.NULL) && (entry.Key.instanceId == (uint)ResourceTypes.NULL))
                {
                    // Check the first 4 bytes of the stream
                    Stream checkDbpf = db.GetResourceStream(entry.Key);
                    string magic = MadScience.StreamHelpers.ReadStringASCII(checkDbpf, 4);
                    if (magic == "DBPF" || magic == "DBBF") // DBPF & DBBF
                    {
                        this.isCorrupt = true;
                        this.pType.MainType = PackageTypes.corruptRecursive;
                        this.pType.SubType = "This package contains another package inside it.";
                        if (!loopAll) return this.pType;
                    }
                    checkDbpf.Close();
                }

                if (entry.Key.typeId == (uint)ResourceTypes.TXTC)
                {
                    int isValid = checkValidEntry(entry, db);
                    if (isValid > 0)
                    {
                        if (isValid == 2)
                        {
                            this.isCorrupt = true;
                            this.pType.MainType = PackageTypes.corruptTXTC;
                            if (!loopAll) return this.pType;
                        }
                    }
                }

                if (entry.Key.typeId == (uint)ResourceTypes.PTRN)
                {
                    if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.patternGeneric;
                    if (!loopAll) return this.pType;
                }

                if (Enum.IsDefined(typeof(ResourceTypes), entry.Key.typeId))
                {
                    if (rc.ContainsKey(Enum.GetName(typeof(ResourceTypes), entry.Key.typeId)))
                    {
                        rc[Enum.GetName(typeof(ResourceTypes), entry.Key.typeId)]++;
                    }
                    else
                    {
                        rc.Add(Enum.GetName(typeof(ResourceTypes), entry.Key.typeId), 1);
                    }
                }

            }

            //print("Done");

            if (rc.ContainsKey("WLOT") && rc.ContainsKey("UNKW1"))
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.neighbourhood;
                this.isCorrupt = true;
                return this.pType;
            }

            if (rc.ContainsKey("WLTL") && rc.ContainsKey("ARY2"))
            {
                this.pType.MainType = PackageTypes.lot;
                return this.pType;
            }

            if (rc.ContainsKey("SIMO") && rc.ContainsKey("SIME") && rc.ContainsKey("SNAP") && rc.ContainsKey("SNAPL"))
            {
                this.pType.MainType = PackageTypes.sim;
                return this.pType;
            }

            //this.pType.MainType = PackageTypes.genericPackage;
            // Check Objects
            if (rc.ContainsKey("OBJD"))
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.objectGeneric;
                Stream objStream = MadScience.Package.Search.getStream(db, 0x319E4F1D, -1, -1);
                if (StreamHelpers.isValidStream(objStream))
                {
                    OBJD objd = new OBJD(objStream);
                    this.pType.SubType = objd.ToString();
                    objd = null;

                }
                return this.pType;
            }
            if (rc.ContainsKey("S3SA"))
            {
                if (this.pType.MainType == PackageTypes.genericPackage)	this.pType.MainType = PackageTypes.coremod;
            }

            if (rc.ContainsKey("CASP"))
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.casPartGeneric;

                Stream casStream = MadScience.Package.Search.getStream(db, 0x034AEECB, -1, -1);
                if (StreamHelpers.isValidStream(casStream))
                {
                    casPartFile cFile = new casPartFile();
                    cFile.Load(casStream);

                    this.pType.SubType = cFile.clothingType();

                    switch (cFile.casType())
                    {
                        case "Hair":
                            this.pType.MainType = PackageTypes.casPartHair;
                            break;
                        case "Scalp":
                            break;
                        case "Face Overlay":
                            switch (cFile.clothingType())
                            {
                                case "Lipstick":
                                case "Eyeshadow":
                                case "Eyeliner":
                                case "Blush":
                                case "Makeup":
                                case "Mascara":
                                    this.pType.MainType = PackageTypes.casPartMakeup;
                                    break;
                                default:
                                    this.pType.MainType = PackageTypes.casPartFaceOverlay;
                                    break;
                            }
                            break;
                        case "Body":
                            this.pType.MainType = PackageTypes.casPartClothing;
                            this.pType.SubType = cFile.clothingCategory();

                            // Check the TYPE of clothing we have
                            switch (cFile.clothingType())
                            {
                                case "Body":
                                case "Top":
                                case "Bottom":
                                case "Shoes":
                                    // Check the age too
                                    // If we have Toddler OR Child OR Teen, plus other ages
                                    bool ageCorrupt = false;
                                    //if ((cFile.cFile.ageGender.baby || cFile.cFile.ageGender.toddler || cFile.cFile.ageGender.child || cFile.cFile.ageGender.teen) && (cFile.cFile.ageGender.youngAdult || cFile.cFile.ageGender.adult || cFile.cFile.ageGender.elder))
                                    //{
                                    //	ageCorrupt = true;
                                    //}
                                    // If we have Baby AND any other age...
                                    if (cFile.cFile.ageGender.baby && (cFile.cFile.ageGender.toddler || cFile.cFile.ageGender.child || cFile.cFile.ageGender.teen || cFile.cFile.ageGender.youngAdult || cFile.cFile.ageGender.adult || cFile.cFile.ageGender.elder))
                                    {
                                        ageCorrupt = true;
                                    }
                                    // If we have Toddler AND any other age...
                                    if (cFile.cFile.ageGender.toddler && (cFile.cFile.ageGender.child || cFile.cFile.ageGender.teen || cFile.cFile.ageGender.youngAdult || cFile.cFile.ageGender.adult || cFile.cFile.ageGender.elder))
                                    {
                                        ageCorrupt = true;
                                    }
                                    // If we have Child AND any other age
                                    if (cFile.cFile.ageGender.child && (cFile.cFile.ageGender.teen || cFile.cFile.ageGender.youngAdult || cFile.cFile.ageGender.adult || cFile.cFile.ageGender.elder))
                                    {
                                        ageCorrupt = true;
                                    }
                                    // If we have Teen AND any other age
                                    if (cFile.cFile.ageGender.teen && (cFile.cFile.ageGender.youngAdult || cFile.cFile.ageGender.adult || cFile.cFile.ageGender.elder))
                                    {
                                        ageCorrupt = true;
                                    }

                                    if (ageCorrupt)
                                    {
                                        this.isCorrupt = true;
                                        this.pType.MainType = PackageTypes.corruptBadAges;
                                        if (!loopAll) return this.pType;
                                    }
                                    break;
                                default:
                                    break;
                            }

                            break;
                        case "Accessory":
                            this.pType.MainType = PackageTypes.casPartAccessory;
                            break;
                    }
                    this.pType.SubType += " (" + cFile.ageGender() + ")";

                }
                return this.pType;
            }

            if (rc.ContainsKey("FBLN") && rc.ContainsKey("FACE") && rc.ContainsKey("BOND"))
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.casSlider;
                return this.pType;
            }

            if (rc.ContainsKey("_IMG") && rc.Count == 1)
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.textureReplacement;
            }

            if (rc.Count == 1 && (rc.ContainsKey("_XML") || rc.ContainsKey("_XML2")))
            {
                if (this.pType.MainType == PackageTypes.genericPackage) this.pType.MainType = PackageTypes.xmltuningmod;
            }

            return this.pType;
        }
        private bool checkTXTCEntry(DatabasePackedFile.Entry entry, Database db)
        {
            if (entry.Key.typeId == 0x033A1435)
            {
                //textBox1.Text += "  " + entry.Key.ToString() + Environment.NewLine;
                // textBox1.Text += "    Checking for corrupted TGI offset... ";

                // Quick and dirty way
                Stream TXTC = db.GetResourceStream(entry.Key);
                // Read offset, first 4 bytes after ID
                StreamHelpers.ReadValueU32(TXTC);

                uint offset = StreamHelpers.ReadValueU32(TXTC);
                // textBox1.Text += offset.ToString() + "...";

                // Seek to this offset + 8 and read the number there.
                TXTC.Seek(offset + 8, SeekOrigin.Begin);

                uint numTGIs = StreamHelpers.ReadValueU8(TXTC);
                // textBox1.Text += numTGIs.ToString() + " TGIs... ";

                // Since each TGI is 16 bytes we can calculate how many bytes they are.
                uint tgiSize = numTGIs * 16;
                uint tgiOffsetEnd = offset + 8 + 1 + tgiSize;

                //textBox1.Text += "TGI block end is at " + tgiOffsetEnd.ToString() + "...";

                if (tgiOffsetEnd == TXTC.Length)
                {
                    TXTC = null;
                    return true;
                }
                else
                {
                    TXTC = null;
                    return false;
                }

            }

            return true;
        }
        private void btnDumpFromFullbuild2_Click(object sender, EventArgs e)
        {
            // Go through the list of DDS files and dump them
            string s3root = MadScience.Helpers.findSims3Root();

            if (s3root != "")
            {

                bool hasShownDialog = false;

                toolStripProgressBar1.Visible = true;
                toolStripProgressBar1.Minimum = 0;
                toolStripProgressBar1.Value = 0;
                toolStripStatusLabel1.Text = "Searching for textures... please wait";
                statusStrip1.Refresh();

                Stream fbuild2 = File.Open(Path.Combine(s3root, Helpers.getGameSubPath("\\GameData\\Shared\\Packages\\FullBuild2.package")), FileMode.Open, FileAccess.Read, FileShare.Read);
                MadScience.Wrappers.Database db = new MadScience.Wrappers.Database(fbuild2, true);

                toolStripProgressBar1.Maximum = casPartSrc.tgi64list.Count;

                Dictionary<ulong, string> keyNames = new Dictionary<ulong, string>();
                long nowTicks = DateTime.Now.Ticks;
                Console.WriteLine("Started at: " + nowTicks);
                foreach (MadScience.Wrappers.ResourceKey entry in db._Entries.Keys)
                {
                    //DatabasePackedFile.Entry entry = db.dbpfEntries[i];
                    if (entry.typeId == (int)0x0166038C)
                    {
                        keyNames = Helpers.getKeyNames(db.GetResourceStream(entry));
                        break;
                    }
                }

                int numFound = 0;
                folderBrowserDialog1.SelectedPath = "";

                for (int j = 0; j < casPartSrc.tgi64list.Count; j++)
                {
                    toolStripProgressBar1.Value++;
                    //keyName tgi = new keyName((tgi64)casPartSrc.tgi64list[j]);
                    MadScience.Wrappers.ResourceKey tgi = casPartSrc.tgi64list[j];
                    if (tgi.typeId == (int)0x736884F1) // VPXY
                    {
                        // Find the meshes, then dump any textures the meshes reference too

                        // Use the VPXY to get the mesh lod
                        Stream vpxyStream = KeyUtils.findKey(tgi, 0);

                        //int numFound = 0;
                        //folderBrowserDialog1.SelectedPath = "";

                        if (vpxyStream != null)
                        {
                            VPXYFile vpxyFile = new VPXYFile(vpxyStream);
                            // Get the first VPXY internal link
                            if (vpxyFile.vpxy.linkEntries.Count >= 1 && vpxyFile.vpxy.linkEntries[0].tgiList.Count >= 1)
                            {
                                for (int i = 0; i < vpxyFile.vpxy.linkEntries[0].tgiList.Count; i++)
                                {
                                    //meshStreams.Add(KeyUtils.findKey(vpxyFile.vpxy.linkEntries[0].tgiList[i], 0));
                                    //KeyUtils.findKey(vpxyFile.vpxy.linkEntries[0].tgiList[i], 0);

                                    //ResourceKey entry = vpxyFile.vpxy.linkEntries[0].tgiList[i];

                                    Stream meshFile = KeyUtils.findKey(vpxyFile.vpxy.linkEntries[0].tgiList[i], 0);

                                    SimGeomFile sgf = new SimGeomFile(meshFile);

                                    if (!hasShownDialog)
                                    {
                                        folderBrowserDialog1.Description = "Please select a folder to save the extracted textures to.";
                                        folderBrowserDialog1.ShowDialog();
                                        hasShownDialog = true;
                                    }
                                    if (folderBrowserDialog1.SelectedPath != "")
                                    {
                                        string extension = "";
                                        for (int k = 0; k < sgf.simgeom.keytable.keys.Count; k++)
                                        {
                                            ResourceKey entry = sgf.simgeom.keytable.keys[k];

                                            if (entry.typeId == 0x00B2D882)
                                            {
                                                extension = ".dds";

                                                string fileNameToSave = "";
                                                if (keyNames.ContainsKey(entry.instanceId))
                                                {
                                                    fileNameToSave = keyNames[entry.instanceId];
                                                    if (fileNameToSave.Contains("0x") == false) { fileNameToSave += "_0x" + entry.instanceId.ToString("X16"); }
                                                }
                                                else
                                                {
                                                    fileNameToSave = entry.typeId.ToString("X8") + "_" + entry.groupId.ToString("X8") + "_" + entry.instanceId.ToString("X16");
                                                }

                                                FileStream saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                                                StreamHelpers.CopyStream(KeyUtils.findKey(entry, 2, db), saveFile);
                                                saveFile.Close();
                                            }
                                        }
                                        //numFound++;
                                    }
                                    meshFile.Close();

                                    //output.Close();
                                }

                            }

                        }
                    }
                    if (tgi.typeId == (int)0x00B2D882) // DDS
                    {
                        //Stream textureStream = KeyUtils.searchForKey(tgi.ToString(), 2);
                        Stream textureStream = KeyUtils.findKey(tgi);
                        if (textureStream != null)
                        {
                            string fileNameToSave = "";
                            if (keyNames.ContainsKey(tgi.instanceId))
                            {
                                fileNameToSave = keyNames[tgi.instanceId];
                            }
                            else
                            {
                                fileNameToSave = tgi.typeId.ToString("X8") + "_" + tgi.groupId.ToString("X8") + "_" + tgi.instanceId.ToString("X16");
                            }

                            if (!hasShownDialog)
                            {
                                folderBrowserDialog1.Description = "Please select a folder to save the extracted textures to.";
                                folderBrowserDialog1.ShowDialog();
                                hasShownDialog = true;
                            }
                            if (folderBrowserDialog1.SelectedPath != "")
                            {

                                Stream output = db.GetResourceStream(tgi);
                                FileStream saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + ".dds"), FileMode.Create, FileAccess.Write);
                                StreamHelpers.CopyStream(output, saveFile);
                                saveFile.Close();
                                output.Close();
                                numFound++;
                            }

                        }
                    }
                }

                toolStripProgressBar1.Value = 0;
                toolStripStatusLabel1.Text = numFound + " textures found.";
                toolStripProgressBar1.Visible = false;
                statusStrip1.Refresh();

                fbuild2.Close();

            }
        }
        private Image extractCASThumbnail(string meshName)
        {
            //Console.WriteLine("Started " + meshName + " at " + DateTime.Now.ToString());

            Helpers.logMessageToFile("Attemping to extract thumbnail for " + meshName);

            Image tempImage = null;

            if (String.IsNullOrEmpty(sims3root))
            {
                sims3root = MadScience.Helpers.findSims3Root();
            }

            string thumbnailPackage = Helpers.getGameSubPath(@"\Thumbnails\CasThumbnails.package");
            Helpers.logMessageToFile(Path.Combine(sims3root, thumbnailPackage));
            if (sims3root != "" && File.Exists(Path.Combine(sims3root, thumbnailPackage)))
            {
                // Open CAS Thumbnails package
                Stream cast = File.Open(Path.Combine(sims3root, thumbnailPackage), FileMode.Open, FileAccess.Read, FileShare.Read);
                Database castdb = new Database(cast, true);

                ulong instanceid = MadScience.StringHelpers.HashFNV64(meshName);

                if (casThumbsKeyList.Count == 0)
                {
                    Helpers.logMessageToFile("Populating casThumbs entry lists from " + castdb._Entries.Count.ToString() + " entries");
                    foreach (MadScience.Wrappers.ResourceKey entry in castdb._Entries.Keys)
                    {
                        //DatabasePackedFile.Entry entry = castdb.dbpfEntries[i];
                        if (entry.groupId == 0x00000000 && entry.typeId == 0x626f60ce)
                        {
                            casThumbsKeyList.Add(entry.instanceId, entry);
                        }
                        if (entry.groupId == 0x00000001 && entry.typeId == 0x626f60ce)
                        {
                            casThumbsKeyList2.Add(entry.instanceId, entry);
                        }
                    }
                    Helpers.logMessageToFile("casThumbs now contains " + casThumbsKeyList.Count.ToString() + " entries");
                    Helpers.logMessageToFile("casThumbs2 now contains " + casThumbsKeyList2.Count.ToString() + " entries");
                }

                bool foundPic = false;
                ResourceKey temp = new ResourceKey();
                if (casThumbsKeyList.ContainsKey(instanceid))
                {
                    temp = casThumbsKeyList[instanceid];
                    foundPic = true;
                }
                else
                {
                    if (casThumbsKeyList2.ContainsKey(instanceid))
                    {
                        temp = casThumbsKeyList2[instanceid];
                        foundPic = true;
                    }
                }

                if (foundPic)
                {
                    tempImage = Image.FromStream(castdb.GetResourceStream(temp));
                    try
                    {
                        tempImage.Save(Path.Combine(Application.StartupPath, Path.Combine("cache", meshName + ".png")), System.Drawing.Imaging.ImageFormat.Png);
                    }
                    catch (Exception ex)
                    {
                    }

                }
                else
                {
                    Helpers.logMessageToFile("Couldn't find a match for " + meshName + " (0x" + instanceid.ToString("X16") + ")");
                }

                cast.Close();
                //dbpf = null;
                castdb = null;
            }
            else
            {
                Helpers.logMessageToFile(@"Can't find sims3root or Thumbnails\CasThumbnails.package");
            }

            //Console.WriteLine("Stopped " + meshName + " at " + DateTime.Now.ToString());

            return tempImage;
        }
        private void button9_Click(object sender, EventArgs e)
        {
            // Go through the list of DDS files and dump them
            string s3root = MadScience.Helpers.findSims3Root();

            if (s3root != "")
            {

                bool hasShownDialog = false;

                toolStripProgressBar1.Visible = true;
                toolStripProgressBar1.Minimum = 0;
                toolStripProgressBar1.Value = 0;
                toolStripStatusLabel1.Text = "Searching for meshes... please wait";
                statusStrip1.Refresh();

                Stream fbuild0 = File.Open(Path.Combine(s3root, Helpers.getGameSubPath("\\GameData\\Shared\\Packages\\FullBuild0.package")), FileMode.Open, FileAccess.Read, FileShare.Read);
                MadScience.Wrappers.Database db = new MadScience.Wrappers.Database(fbuild0, true);

                toolStripProgressBar1.Maximum = db._Entries.Count;

                Dictionary<ulong, string> keyNames = new Dictionary<ulong, string>();
                long nowTicks = DateTime.Now.Ticks;
                Console.WriteLine("Started at: " + nowTicks);
                foreach (MadScience.Wrappers.ResourceKey entry in db._Entries.Keys)
                {
                    //MadScience.Wrappers.ResourceKey entry = new MadScience.Wrappers.ResourceKey(keyString);
                    //DatabasePackedFile.Entry entry = db.dbpfEntries[i];
                    if (entry.typeId == (int)0x0166038C)
                    {
                        keyNames = Helpers.getKeyNames(db.GetResourceStream(entry));
                        break;
                    }
                }

                // Get the Mesh links for the first LOD
                //List<Stream> meshStreams = new List<Stream>();

                // Use the VPXY to get the mesh lod
                Stream vpxyStream = KeyUtils.findKey(casPartSrc.tgi64list[casPartSrc.tgiIndexVPXY], 0);

                int numFound = 0;
                folderBrowserDialog1.SelectedPath = "";

                if (vpxyStream != null)
                {
                    VPXYFile vpxyFile = new VPXYFile(vpxyStream);
                    // Get the first VPXY internal link
                    if (vpxyFile.vpxy.linkEntries.Count >= 1) // && vpxyFile.vpxy.linkEntries[0].tgiList.Count >= 1)
                    {
                        for (int p = 0; p < vpxyFile.vpxy.linkEntries.Count; p++)
                        {
                            for (int i = 0; i < vpxyFile.vpxy.linkEntries[p].tgiList.Count; i++)
                            {
                                //meshStreams.Add(KeyUtils.findKey(vpxyFile.vpxy.linkEntries[0].tgiList[i], 0));
                                //KeyUtils.findKey(vpxyFile.vpxy.linkEntries[0].tgiList[i], 0);

                                ResourceKey entry = vpxyFile.vpxy.linkEntries[p].tgiList[i];

                                string fileNameToSave = "";
                                if (keyNames.ContainsKey(entry.instanceId))
                                {
                                    fileNameToSave = keyNames[entry.instanceId];
                                    if (fileNameToSave.Contains("0x") == false) { fileNameToSave += "_0x" + entry.instanceId.ToString("X16"); }
                                }
                                else
                                {
                                    fileNameToSave = entry.typeId.ToString("X8") + "_" + entry.groupId.ToString("X8") + "_" + entry.instanceId.ToString("X16");
                                }

                                Stream output = KeyUtils.findKey(entry, 0);

                                if (!hasShownDialog)
                                {
                                    folderBrowserDialog1.Description = "Please select a folder to save the extracted meshes to.";
                                    folderBrowserDialog1.ShowDialog();
                                    hasShownDialog = true;
                                }
                                if (folderBrowserDialog1.SelectedPath != "")
                                {
                                    string extension = "";
                                    if (entry.typeId == 0x015A1849)
                                    {
                                        extension = ".simgeom";
                                    }

                                    FileStream saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                                    StreamHelpers.CopyStream(output, saveFile);
                                    saveFile.Close();
                                    numFound++;
                                }
                                output.Close();
                            }
                        }

                    }

                    if (numFound > 0)
                    {
                        string fileNameToSave2 = "";
                        ResourceKey entry = casPartSrc.tgi64list[casPartSrc.tgiIndexVPXY];
                        if (keyNames.ContainsKey(entry.instanceId))
                        {
                            fileNameToSave2 = keyNames[entry.instanceId];
                            if (fileNameToSave2.Contains("0x") == false) { fileNameToSave2 += "_0x" + entry.instanceId.ToString("X16"); }
                        }
                        else
                        {
                            fileNameToSave2 = entry.typeId.ToString("X8") + "_" + entry.groupId.ToString("X8") + "_" + entry.instanceId.ToString("X16");
                        }

                        FileStream saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave2) + ".vpxy", FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(vpxyStream, saveFile, true);
                        saveFile.Close();

                    }

                    vpxyStream.Close();

                }

                if (debugModeToolStripMenuItem.Checked)
                {
                    Stream bodyBlendFatStream = KeyUtils.findKey(casPartSrc.tgi64list[casPartSrc.tgiIndexBlendInfoFat].ToString(), 0);
                    Stream bodyBlendFitStream = KeyUtils.findKey(casPartSrc.tgi64list[casPartSrc.tgiIndexBlendInfoFit].ToString(), 0);
                    Stream bodyBlendThinStream = KeyUtils.findKey(casPartSrc.tgi64list[casPartSrc.tgiIndexBlendInfoThin].ToString(), 0);
                    Stream bodyBlendSpecialStream = KeyUtils.findKey(casPartSrc.tgi64list[casPartSrc.tgiIndexBlendInfoSpecial].ToString(), 0);

                    if (!hasShownDialog)
                    {
                        folderBrowserDialog1.Description = "Please select a folder to save the extracted meshes to.";
                        folderBrowserDialog1.ShowDialog();
                        hasShownDialog = true;
                    }
                    if (folderBrowserDialog1.SelectedPath != "")
                    {
                        string extension = ".bodyblend";

                        string fileNameToSave = "";
                        fileNameToSave = txtMeshName.Text + "_fit";
                        FileStream saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bodyBlendFitStream, saveFile);
                        saveFile.Close();

                        fileNameToSave = txtMeshName.Text + "_fat";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bodyBlendFatStream, saveFile);
                        saveFile.Close();

                        fileNameToSave = txtMeshName.Text + "_thin";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bodyBlendThinStream, saveFile);
                        saveFile.Close();

                        fileNameToSave = txtMeshName.Text + "_special";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bodyBlendSpecialStream, saveFile);
                        saveFile.Close();

                        // Just grab the one BGEO for now
                        bodyBlendFatStream.Seek(0, SeekOrigin.Begin);
                        FacialBlend fblend = new FacialBlend(bodyBlendFatStream);
                        Stream bgeoStream = KeyUtils.findKey(fblend.blendTgi, 0);
                        bgeoStream.Seek(0, SeekOrigin.Begin);
                        extension = ".blendgeom";
                        fileNameToSave = txtMeshName.Text + "_fat";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bgeoStream, saveFile);
                        saveFile.Close();

                        bodyBlendFitStream.Seek(0, SeekOrigin.Begin);
                        fblend = new FacialBlend(bodyBlendFitStream);
                        bgeoStream = KeyUtils.findKey(fblend.blendTgi, 0);
                        bgeoStream.Seek(0, SeekOrigin.Begin);
                        fileNameToSave = txtMeshName.Text + "_fit";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bgeoStream, saveFile);
                        saveFile.Close();

                        bodyBlendThinStream.Seek(0, SeekOrigin.Begin);
                        fblend = new FacialBlend(bodyBlendThinStream);
                        bgeoStream = KeyUtils.findKey(fblend.blendTgi, 0);
                        bgeoStream.Seek(0, SeekOrigin.Begin);
                        extension = ".blendgeom";
                        fileNameToSave = txtMeshName.Text + "_thin";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bgeoStream, saveFile);
                        saveFile.Close();

                        bodyBlendSpecialStream.Seek(0, SeekOrigin.Begin);
                        fblend = new FacialBlend(bodyBlendSpecialStream);
                        bgeoStream = KeyUtils.findKey(fblend.blendTgi, 0);
                        bgeoStream.Seek(0, SeekOrigin.Begin);
                        extension = ".blendgeom";
                        fileNameToSave = txtMeshName.Text + "_special";
                        saveFile = new FileStream(Path.Combine(folderBrowserDialog1.SelectedPath, fileNameToSave + extension), FileMode.Create, FileAccess.Write);
                        StreamHelpers.CopyStream(bgeoStream, saveFile);
                        saveFile.Close();

                        //numFound++;
                    }
                }
                toolStripProgressBar1.Value = 0;
                toolStripProgressBar1.Visible = false;
                toolStripStatusLabel1.Text = numFound.ToString() + " meshes found";
                statusStrip1.Refresh();

                fbuild0.Close();
            }
        }
        public static bool fixRecursive(Database db)
        {
            uint numFixed = 0;

            // textBox1.Text += "Checking for corrupted TXTC entries... " + Environment.NewLine;
            for (int i = 0; i < db.dbpf.Entries.Count; i++)
            {
                DatabasePackedFile.Entry entry = db.dbpf.Entries[i];
                if ((entry.Key.typeId == 0x0) && (entry.Key.groupId == 0x0) && (entry.Key.instanceId == 0x0))
                {
                    // Check the first 4 bytes of the stream
                    Stream checkDbpf = db.GetResourceStream(entry.Key);
                    string magic = MadScience.StreamHelpers.ReadStringASCII(checkDbpf, 4);
                    if (magic == "DBPF" || magic == "DBBF") // DBPF & DBBF
                    {
                        db.DeleteResource(entry.Key);
                        numFixed++;
                    }
                    checkDbpf.Close();
                }

            }

            if (numFixed == 0)
            {
                //textBox1.Text += Environment.NewLine;
                //textBox1.Text += "This file appears OK!";
                return false;
            }
            else
            {
                //this.filesFixed++;
                //textBox1.Text += Environment.NewLine;
                //textBox1.Text += "This file had some corrupted TXTCs! They are now fixed.";
                //textBox1.Text += "Fixed " + filename + Environment.NewLine;
                //saveToolStripMenuItem.Enabled = true;
                db.Commit(true);
            }

            return true;
        }
        public static bool fixTXTR(Database db)
        {
            uint numFixed = 0;

            // textBox1.Text += "Checking for corrupted TXTC entries... " + Environment.NewLine;
            for (int i = 0; i < db.dbpf.Entries.Count; i++)
            {
                DatabasePackedFile.Entry entry = db.dbpf.Entries[i];
                if (entry.Key.typeId == 0x033A1435)
                {
                    //textBox1.Text += "  " + entry.Key.ToString() + Environment.NewLine;
                    // textBox1.Text += "    Checking for corrupted TGI offset... ";

                    // Quick and dirty way
                    Stream TXTC = db.GetResourceStream(entry.Key);
                    // Read offset, first 4 bytes after ID
                    StreamHelpers.ReadValueU32(TXTC);

                    uint offset = StreamHelpers.ReadValueU32(TXTC);
                    // textBox1.Text += offset.ToString() + "...";

                    // Seek to this offset + 8 and read the number there.
                    TXTC.Seek(offset + 8, SeekOrigin.Begin);

                    uint numTGIs = StreamHelpers.ReadValueU8(TXTC);
                    // textBox1.Text += numTGIs.ToString() + " TGIs... ";

                    // Since each TGI is 16 bytes we can calculate how many bytes they are.
                    uint tgiSize = numTGIs * 16;
                    uint tgiOffsetEnd = offset + 8 + 1 + tgiSize;

                    //textBox1.Text += "TGI block end is at " + tgiOffsetEnd.ToString() + "...";

                    if (tgiOffsetEnd == TXTC.Length)
                    {
                        return false;
                    }
                    else
                    {
                        // Try offset - 1
                        offset = offset - 1;

                        // Seek to this offset + 8 and read the number there.
                        TXTC.Seek(offset + 8, SeekOrigin.Begin);

                        numTGIs = StreamHelpers.ReadValueU8(TXTC);
                        // textBox1.Text += numTGIs.ToString() + " TGIs... ";

                        // Since each TGI is 16 bytes we can calculate how many bytes they are.
                        tgiSize = numTGIs * 16;
                        tgiOffsetEnd = offset + 8 + 1 + tgiSize;

                        //textBox1.Text += "TGI block end is at " + tgiOffsetEnd.ToString() + "...";

                        if (tgiOffsetEnd == TXTC.Length)
                        {
                            //   textBox1.Text += "Correct!";
                            // Offset it minus 1

                            TXTC.Seek(4, SeekOrigin.Begin);
                            StreamHelpers.WriteValueU32(TXTC, offset);

                            MemoryStream newTXTC = new MemoryStream();
                            StreamHelpers.CopyStream(TXTC, newTXTC, true);

                            db.SetResourceStream(entry.Key, newTXTC);

                            //    textBox1.Text += Environment.NewLine;
                            //     textBox1.Text += "The above resource has been fixed.";

                            numFixed++;

                            newTXTC.Close();
                        }
                        else
                        {
                            //   textBox1.Text += "Incorrect!";
                        }
                    }

                    // textBox1.Text += Environment.NewLine;

                    TXTC = null;
                }
            }

            if (numFixed == 0)
            {
                //textBox1.Text += Environment.NewLine;
                //textBox1.Text += "This file appears OK!";
                return false;
            }
            else
            {
                //this.filesFixed++;
                //textBox1.Text += Environment.NewLine;
                //textBox1.Text += "This file had some corrupted TXTCs! They are now fixed.";
                //textBox1.Text += "Fixed " + filename + Environment.NewLine;
                //saveToolStripMenuItem.Enabled = true;
                db.Commit(true);
            }

            return true;
        }