public void OneImageToRuleThemAll(ImageFile im, string archiveDir, byte[] imgData)
        {
            ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgData);

            // starts from the smaller image
            for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--)
            {
                ImageFile newImageFile = imgMipMap.imageList[i];

                if (newImageFile.imgSize.height < 4 || newImageFile.imgSize.width < 4)
                    continue;

                ImageEngineFormat newImageFileFormat = Textures.Methods.ParseFormat(newImageFile.format);

                //NEW Check for correct format
                if (texFormat != newImageFileFormat)
                {
                    //MessageBox.Show("Warning! The input image is of the wrong format! Aborting");
                    throw new FormatException("Different image format, original is " + texFormat + ", new is " + newImageFile.subtype());
                }

                // if the image size exists inside the texture2d image list then we have to replace it
                if (privateimgList.Exists(img => img.imgSize == newImageFile.imgSize))
                {
                    // ...but at least for now I can reuse my replaceImage function... ;)
                    replaceImage(newImageFile.imgSize.ToString(), newImageFile, archiveDir);
                }
                else // if the image doesn't exists then we have to add it
                {
                    // ...and use my addBiggerImage function! :P
                    addBiggerImage(newImageFile, archiveDir);
                }
            }

            while (privateimgList[0].imgSize.width > imgMipMap.imageList[0].imgSize.width)
            {
                privateimgList.RemoveAt(0);
                numMipMaps--;
                if (properties.ContainsKey("MipTailBaseIdx"))
                    properties["MipTailBaseIdx"].Value.IntValue--;
            }
            if (properties.ContainsKey("SizeX"))
                properties["SizeX"].Value.IntValue = (int)imgMipMap.imageList[0].imgSize.width;
            if (properties.ContainsKey("SizeY"))
                properties["SizeY"].Value.IntValue = (int)imgMipMap.imageList[0].imgSize.height;
        }
Exemple #2
0
        public void OneImageToRuleThemAll(ImageFile im, string archiveDir, byte[] imgData)
        {
            ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgData);

            if (Class == class2 || Class == class3)
                ChangeFormat(imgMipMap.imageList[0].format);

            // starts from the smaller image
            for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--)
            {
                ImageFile newImageFile = imgMipMap.imageList[i];

                if (!Methods.CheckTextureFormat(texFormat, newImageFile.format))
                    throw new FormatException("Different image format, original is " + texFormat + ", new is " + newImageFile.subtype());

                // if the image size exists inside the texture2d image list then we have to replace it
                if (privateimgList.Exists(img => img.imgSize == newImageFile.imgSize))
                {
                    // ...but at least for now I can reuse my replaceImage function... ;)
                    replaceImage(newImageFile.imgSize.ToString(), newImageFile, archiveDir);
                }
                else if (newImageFile.imgSize.width > privateimgList[0].imgSize.width) // if the image doesn't exists then we have to add it
                {
                    // ...and use my addBiggerImage function! :P
                    addBiggerImage(newImageFile, archiveDir);
                }
                //else
                //    addMissingImage(newImageFile.imgSize.ToString(), newImageFile.fileName);
                // else ignore the image
            }

            // Remove higher res versions and fix up properties
            while (privateimgList[0].imgSize.width > imgMipMap.imageList[0].imgSize.width)
            //while (imgMipMap.imageList.Count + 2 < imgList.Count)
            {
                privateimgList.RemoveAt(0);
                numMipMaps--;
            }
            if (properties.ContainsKey("SizeX"))
                properties["SizeX"].Value.IntValue = (int)privateimgList[0].imgSize.width;
            if (properties.ContainsKey("SizeY"))
                properties["SizeY"].Value.IntValue = (int)privateimgList[0].imgSize.height;
            if (properties.ContainsKey("MipTailBaseIdx"))
                properties["MipTailBaseIdx"].Value.IntValue = privateimgList.Count + 1;
        }
Exemple #3
0
        public void OneImageToRuleThemAll(string imageFileName, string archiveDir, out string newTextureGroup)
        {
            newTextureGroup = null;
            ImageMipMapHandler imgMipMap = new ImageMipMapHandler(imageFileName, null);

            // starts from the smaller image
            for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--)
            {
                ImageFile newImageFile = imgMipMap.imageList[i];

                // insert images only with size > 64
                if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64)
                    continue;

                // write the new image into a file (I know that's crappy solution, I'll find another one later...)
                using (FileStream newImageStream = File.Create(newImageFile.fileName))
                {
                    byte[] buffer = newImageFile.ToArray();
                    newImageStream.Write(buffer, 0, buffer.Length);
                }

                // if the image size exists inside the texture2d image list then we have to replace it
                if (imgList.Exists(img => img.imgSize == newImageFile.imgSize))
                {
                    // ...but at least for now I can reuse my replaceImage function... ;)
                    replaceImage(newImageFile.imgSize.ToString(), newImageFile.fileName, archiveDir);
                }
                else // if the image doesn't exists then we have to add it
                {
                    // ...and use my addBiggerImage function! :P
                    addBiggerImage(newImageFile.fileName, archiveDir);
                }

                File.Delete(newImageFile.fileName);
            }
            
            
            // check that Texture2D has a TextureGroup
            if (!properties.ContainsKey("LODGroup"))
                return;

            // extracting values from LODGroup Property
            PropertyReader.Property LODGroup = properties["LODGroup"];
            string textureGroupName = pccRef.Names[LODGroup.Value.IntValue];

            string newTextureGroupName = "TEXTUREGROUP_Shadowmap";
            textureGroupName = newTextureGroupName;
            int nameIndex = pccRef.FindNameOrAdd(newTextureGroupName);
            using (MemoryStream rawStream = new MemoryStream(LODGroup.raw))
            {
                rawStream.Seek(32, SeekOrigin.Begin);
                rawStream.WriteValueS32(nameIndex);
                //rawStream.Seek(32, SeekOrigin.Begin);
                rawStream.WriteValueS32(0);
                properties["LODGroup"].raw = rawStream.ToArray();
            }
            //LODGroup.Value.IntValue = pccRef.Names.FindIndex(name => name == newTextureGroupName);

            /*MessageBox.Show("Texturegroup Name: " + textureGroupName);
            ImageSize maxImageSize = imgList.Max(image => image.imgSize);
            int textureGroupValue = (int)Math.Max(maxImageSize.width, maxImageSize.height) + 1;

            // open Engine.pcc file and edit TextureGroup enumerator
            {
                PCCObject pccEngine = new PCCObject(ME3Directory.cookedPath + "Engine.pcc");
                int idxTexGroups = pccEngine.Exports.FindIndex(export => export.ObjectName == "TextureGroup");

                TextureGroup texGroup = new TextureGroup(pccEngine, pccEngine.Exports[idxTexGroups].Data);
                if (texGroup.ExistsTextureGroup(textureGroupName, textureGroupValue))
                    return;
                else
                {
                    if (!pccEngine.Names.Exists(name => name == newTextureGroupName))
                        pccEngine.Names.Add(newTextureGroupName);

                    newTextureGroup = textureGroupName + "_" + (textureGroupValue - 1);

                    texGroup.Add(textureGroupName, textureGroupValue);
                    MessageBox.Show("Now editing texgroup enum");
                    pccEngine.Exports[idxTexGroups].Data = texGroup.ToArray();
                    MessageBox.Show("Now saving engine.pcc");
                    pccEngine.saveToFile(true, ME3Directory.cookedPath + "Engine.pcc");
                    MessageBox.Show("Saved engine.pcc");


                }
            }*/
        }
Exemple #4
0
        public void OneImageToRuleThemAll(string imageFileName)
        {
            ImageMipMapHandler imgMipMap = new ImageMipMapHandler(imageFileName, null);

            if (Class == class2 || Class == class3)
                ChangeFormat(imgMipMap.imageList[0].format);

            // starts from the smaller image
            for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--)
            {
                ImageFile newImageFile = imgMipMap.imageList[i];

                if (texFormat != newImageFile.format && texFormat != "PF_" + newImageFile.format && newImageFile.format != "ATI2")
                    throw new FormatException("Different image format, original is " + texFormat + ", new is " + newImageFile.subtype());

                // write the new image into a file (I know that's crappy solution, I'll find another one later...)
                using (FileStream newImageStream = File.Create(newImageFile.fileName))
                {
                    byte[] buffer = newImageFile.ToArray();
                    newImageStream.Write(buffer, 0, buffer.Length);
                }

                // if the image size exists inside the texture2d image list then we have to replace it
                if (imgList.Exists(img => img.imgSize == newImageFile.imgSize))
                {
                    // ...but at least for now I can reuse my replaceImage function... ;)
                    replaceImage(newImageFile.imgSize.ToString(), newImageFile.fileName);
                }
                else if (newImageFile.imgSize.width > imgList[0].imgSize.width) // if the image doesn't exists then we have to add it
                {
                    // ...and use my addBiggerImage function! :P
                    addBiggerImage(newImageFile.fileName);
                }
                // else ignore the image

                File.Delete(newImageFile.fileName);
            }

            // Remove higher res versions and fix up properties
            while (imgList[0].imgSize.width > imgMipMap.imageList[0].imgSize.width)
            {
                imgList.RemoveAt(0);
                numMipMaps--;
            }
            if (properties.ContainsKey("SizeX"))
                properties["SizeX"].Value.IntValue = (int)imgList[0].imgSize.width;
            if (properties.ContainsKey("SizeY"))
                properties["SizeY"].Value.IntValue = (int)imgList[0].imgSize.height;
            if (properties.ContainsKey("MipTailBaseIdx"))
                properties["MipTailBaseIdx"].Value.IntValue = imgList.Count + 1;
            
        }
Exemple #5
0
        // New methods!
        public void OneSizeFitsAll(String inputFile, bool resFix = false)
        {
            // forced lowresfix
            // resFix = true;

            if (!File.Exists(inputFile))
                throw new FileNotFoundException("Input texture not found at: " + inputFile);

            bool containsmips = true;
            ImageMipMapHandler mipmaps = null;

            if (imgList.Count > 1)
            {
                try{ mipmaps = new ImageMipMapHandler(inputFile, null); }
                catch (FormatException) { containsmips = false; }
            }
            else
                containsmips = false;

            ImageInfo existingImg = imgList.First(img => img.storageType != storage.empty);
            if (containsmips)
            {
                if ((float)mipmaps.imageList[0].imgSize.width / (float)mipmaps.imageList[0].imgSize.height != (float)existingImg.imgSize.width / (float)existingImg.imgSize.height)
                    throw new FormatException("Input texture not correct aspect ratio");

                if (mipmaps.imageList[0].format == "PF_R8G8B8") // Convert to 32-bit if necessary
                {
                    for (int i = 0; i < mipmaps.imageList.Count; i++)
                        mipmaps.imageList[i] = new DDS(null, mipmaps.imageList[i].imgSize, "A8R8G8B8", ImageMipMapHandler.ConvertTo32bit(mipmaps.imageList[i].resize(), (int)mipmaps.imageList[i].imgSize.width, (int)mipmaps.imageList[i].imgSize.height));
                }

                if (Class == class2 || Class == class3) // Allow format modification if one of the derived classes. Don't need the single level check since we're replacing all levels
                    ChangeFormat(mipmaps.imageList[0].format);

                if (texFormat == "PF_NormalMap_HQ") // Check formats
                {
                    if (mipmaps.imageList[0].format != "ATI2")
                        throw new FormatException("Texture not in correct format - Expected ATI2");
                }
                else if (String.Compare(texFormat, "PF_" + mipmaps.imageList[0].format, true) != 0)
                    throw new FormatException("Texture not in correct format - Expected " + texFormat);

                for (int i = mipmaps.imageList.Count - 1; i >= 0; i--)
                {
                    if (imgList.Exists(img => img.imgSize == mipmaps.imageList[i].imgSize))
                        ReplaceImage(mipmaps.imageList[i]);
                    else if (mipmaps.imageList[i].imgSize.width > imgList.First().imgSize.width && mipmaps.imageList[i].imgSize.height > imgList.First().imgSize.height)
                        UpscaleImage(mipmaps.imageList[i]);
                    //else
                    //    AddMissingImage(mipmaps.imageList[i]);
                    // Else ignore missing values
                }

                while (imgList[0].imgSize.width > mipmaps.imageList[0].imgSize.width) // Remove any existing higher levels
                    imgList.RemoveAt(0);
            }
            else
            {
                ImageFile ddsfile = new DDS(inputFile, null);
                
                if ((float)ddsfile.imgSize.width / (float)ddsfile.imgSize.height != (float)existingImg.imgSize.width / (float)existingImg.imgSize.height) // Check dimensions
                    throw new FormatException("Input texture not correct aspect ratio");

                if (ddsfile.format == "R8G8B8")
                    ddsfile = new DDS(null, ddsfile.imgSize, "A8R8G8B8", ImageMipMapHandler.ConvertTo32bit(ddsfile.resize(), (int)ddsfile.imgSize.width, (int)ddsfile.imgSize.height));

                if (imgList.Count == 1 && (Class == class2 || Class == class3)) // Since this is single level replacement, only allow format change if a single level texture with required class
                    ChangeFormat(ddsfile.format);

                if (texFormat == "PF_NormalMap_HQ") // Check format
                {
                    if (ddsfile.format != "ATI2")
                        throw new FormatException("Texture not in correct format - Expected ATI2");
                }
                else if (String.Compare(texFormat, "PF_" + ddsfile.format, true) != 0)
                    throw new FormatException("Texture not in correct format - Expected " + texFormat);

                if (imgList.Count == 1 && imgList[0].imgSize != ddsfile.imgSize) // If img doesn't exist and it's a single level texture, use hard replace
                    HardReplaceImage(ddsfile);
                else if (imgList.Exists(img => img.imgSize == ddsfile.imgSize)) // Catches the rest of the single levels and every one which has an existing reference for that level
                    ReplaceImage(ddsfile);
                else if (ddsfile.imgSize.width > imgList[0].imgSize.width) // Add a greater image
                    UpscaleImage(ddsfile);
                //else if (ddsfile.imgSize.width < imgList.Last().imgSize.width) // Add a smaller image
                //    AddMissingImage(ddsfile);
            }

            if (imgList.Count > 1 && resFix)
                LowResFix();

            // Fix up properties
            if (properties.ContainsKey("SizeX"))
                properties["SizeX"].Value.IntValue = (int)imgList.First(img => img.storageType != storage.empty).imgSize.width;
            if (properties.ContainsKey("SizeY"))
                properties["SizeY"].Value.IntValue = (int)imgList.First(img => img.storageType != storage.empty).imgSize.height;
            if (properties.ContainsKey("MipTailBaseIdx"))
                properties["MipTailBaseIdx"].Value.IntValue = imgList.Count - 1;
            numMipMaps = (uint)imgList.Count;
        }
Exemple #6
0
        public void OneImageToRuleThemAll(string archiveDir, ImageFile im, out string newTextureGroup, byte[] imgData)
        {
            newTextureGroup = null;
            ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgData);

            // starts from the smaller image
            for (int i = imgMipMap.imageList.Count - 1; i >= 0; i--)
            {
                ImageFile newImageFile = imgMipMap.imageList[i];

                // insert images only with size > 64
                if (newImageFile.imgSize.width < 64 && newImageFile.imgSize.height < 64)
                    continue;

                // if the image size exists inside the texture2d image list then we have to replace it
                if (imgList.Exists(img => img.imgSize == newImageFile.imgSize))
                {
                    // ...but at least for now I can reuse my replaceImage function... ;)
                    replaceImage(newImageFile.imgSize.ToString(), newImageFile, archiveDir);
                }
                else // if the image doesn't exists then we have to add it
                {
                    // ...and use my addBiggerImage function! :P
                    addBiggerImage(newImageFile, archiveDir);
                }

                File.Delete(newImageFile.fileName);
            }

            // add texturegroup_world inside GamerSettings.ini in order to overwrite values
            ImageSize maxSize = imgList.Max(image => image.imgSize);
            uint maxValue = Math.Max(maxSize.width, maxSize.height);
            string section = "SystemSettings";
            string key = "texturegroup_shadowmap";
            string newValue = "(MinLODSize=128,MaxLODSize=" + maxValue + ",LODBias=0)";
            IniFile iniFile = new IniFile(ME3Directory.GamerSettingsIniFile);
            string oldValue = iniFile.IniReadValue(section, key);
            if (oldValue == "")
            {
                iniFile.IniWriteValue(section, key, newValue);
            }
            else
            {
                char[] delimiters = new char[] { '=', ',' };
                uint maxLODSize = Convert.ToUInt32(oldValue.Split(delimiters)[3]);
                if (maxValue > maxLODSize)
                    iniFile.IniWriteValue(section, key, newValue);
            }

            // check that Texture2D has a TextureGroup
            if (!properties.ContainsKey("LODGroup"))
                return;

            // extracting values from LODGroup Property
            PropertyReader.Property LODGroup = properties["LODGroup"];
            string textureGroupName = pccRef.Names[LODGroup.Value.IntValue];

            string newTextureGroupName = "TEXTUREGROUP_Shadowmap";
            textureGroupName = newTextureGroupName;
            if (!pccRef.Names.Exists(name => name == newTextureGroupName))
                pccRef.Names.Add(newTextureGroupName);
            using (MemoryStream rawStream = new MemoryStream(LODGroup.raw))
            {
                rawStream.Seek(32, SeekOrigin.Begin);
                rawStream.WriteValueS32(pccRef.Names.FindIndex(name => name == newTextureGroupName));
                //rawStream.Seek(32, SeekOrigin.Begin);
                rawStream.WriteValueS32(0);
                properties["LODGroup"].raw = rawStream.ToArray();
            }
        }
Exemple #7
0
        private void ReplaceButton_Click(object sender, EventArgs e)
        {
            if (MainListView.SelectedIndices.Count != 0 && MainListView.SelectedIndices[0] >= 0)
            {
                // KFreon: Select mip to replace
                List<string> names = new List<string>();
                int index = GetSelectedTexInd();
                TreeTexInfo tex = Tree.GetTex(index);
                Textures.ITexture2D tex2D = tex.Textures[0];
                for (int i = 0; i < tex2D.imgList.Count; i++)
                {
                    bool pccstored = tex2D.imgList[i].CompareStorage("pccSto");
                    names.Add("Image: " + tex2D.imgList[i].imgSize + " stored inside " + (pccstored ? "PCC file" : "Archive file") + " at offset " + ((pccstored) ? (tex2D.imgList[i].offset + tex2D.pccOffset).ToString() : tex2D.imgList[i].offset.ToString()));
                }

                string selectedImage = "";
                using (Helpers.SelectionForm sf = new Helpers.SelectionForm(names, "Select ONE image to replace.", "Replace Image Selector", false))
                {
                    sf.ShowDialog();
                    if (sf.SelectedInds.Count == 0)
                        return;
                    else if (sf.SelectedInds.Count == 1)
                        selectedImage = sf.SelectedItems[0];
                    else
                    {
                        MessageBox.Show("You must select ONLY ONE image to replace.");
                        return;
                    }
                }
                string imgsize = selectedImage.Split(' ')[1];
                string replacingfile = "";
                using (OpenFileDialog ofd = new OpenFileDialog())
                {
                    ofd.Title = "Select Image to replace with";
                    ofd.Filter = "Image File|*.dds";

                    if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                        replacingfile = ofd.FileName;
                    else
                        return;
                }

                byte[] imgData = File.ReadAllBytes(replacingfile);
                ImageMipMapHandler mip = new ImageMipMapHandler("", imgData);
                string[] sizes = imgsize.Split('x');
                ImageFile im = mip.imageList.First(img => img.imgSize.width.ToString() == sizes[0] && img.imgSize.height.ToString() == sizes[1]);

                DebugOutput.PrintLn("Replacing image in " + tex.TexName + " at " + imgsize + " with " + replacingfile);

                tex2D.replaceImage(imgsize, im, pathBIOGame);
                UpdateModifiedTex(tex2D, tex, index);
                DebugOutput.PrintLn("Image replaced.");

                StatusUpdater.UpdateText("Image replaced!");
                MainProgressBar.Value = MainProgressBar.Maximum;

                DisplayTextureProperties(tex2D, tex2D.GenerateImageInfo());

                RegenerateThumbnail(tex, index, false);

                if (ModMakerMode)
                {
                    AddModJob(tex2D, replacingfile);
                    StatusUpdater.UpdateText("Replacement complete and job added to modmaker!");
                }

                if (TPFMode)
                {
                    AddTPFToolsJob(replacingfile, tex.Hash);
                    StatusUpdater.UpdateText("Replacement Complete and job added to TPFTools!");
                }
            }
        }
        public void InstallTexture(string texname, List<string> pccs, List<int> IDs, byte[] imgdata)
        {
            string fulpath = pccs[0];
            string temppath = (WhichGame == 1) ? Path.GetDirectoryName(pathBIOGame) : pathBIOGame;
            if (!fulpath.Contains(temppath))
                fulpath = Path.Combine(temppath, fulpath);


            // KFreon: Skip files that don't exist
            if (!File.Exists(fulpath))
                return;

            PCCObjects.IPCCObject pcc = PCCObjects.Creation.CreatePCCObject(fulpath, WhichGame);

            if ((pcc.Exports[IDs[0]].ClassName != "Texture2D" && pcc.Exports[IDs[0]].ClassName != "LightMapTexture2D" && pcc.Exports[IDs[0]].ClassName != "TextureFlipBook") || String.Compare(pcc.Exports[IDs[0]].ObjectName, texname, true) != 0)
                throw new InvalidDataException("Export is not correct class or name!");

            //Load the texture from the pcc
            Textures.ITexture2D tex2D = null;
            tex2D = pcc.CreateTexture2D(IDs[0], pathBIOGame);
            tex2D.allPccs = pccs;
            tex2D.expIDs = IDs;
            int noImg = tex2D.imgList.Count;

            DebugOutput.PrintLn("Now replacing textures in texture: " + tex2D.texName, true);
            Debug.WriteLine("Now replacing textures in texture: " + tex2D.texName + "  ID: " + IDs[0]);
            WriteDebug("Now replacing textures in texture: " + tex2D.texName + "  ID: " + IDs[0]);

            ImageFile im = null;
            try
            {
                im = new DDS("", imgdata);
            }
            catch
            {
                Console.WriteLine("");
            }



            // KFreon: TESTING
            Debug.WriteLine("First pcc: " + fulpath + "    ArcName: " + tex2D.arcName);
            WriteDebug("First pcc: " + fulpath + "    ArcName: " + tex2D.arcName);



            //The texture is a single image, therefore use replace function
            if (noImg == 1)
            {
                string imgSize = tex2D.imgList[0].imgSize.width.ToString() + "x" + tex2D.imgList[0].imgSize.height.ToString();
                try
                {
                    tex2D.replaceImage(imgSize, im, pathBIOGame);
                }
                catch
                {
                    // KFreon:  If replace fails, it's single image thus use the singleimageupscale function
                    tex2D.singleImageUpscale(im, pathBIOGame);
                }
            }
            //If the texture has multiple images, then check the input texture for MIPMAPS
            else
            {
                bool hasMips = true;
                ImageFile imgFile = im;
                try { ImageMipMapHandler imgMipMap = new ImageMipMapHandler("", imgdata); }
                catch { hasMips = false; }
                if (!hasMips)
                {
                    //string fileformat = Path.GetExtension(texFile);
                    //imgFile = new DDS(texFile);
                    string imgSize = imgFile.imgSize.width.ToString() + "x" + imgFile.imgSize.height.ToString();
                    try
                    {
                        //Try replacing the image. If it doesn't exist then it'll throw and error and you'll need to upscale the image
                        tex2D.replaceImage(imgSize, imgFile, pathBIOGame);
                    }
                    catch { tex2D.addBiggerImage(imgFile, pathBIOGame); }
                }
                else
                {
                    try
                    {
                        tex2D.OneImageToRuleThemAll(imgFile, pathBIOGame, imgdata);
                    }
                    catch (Exception e)
                    {
                        if (e.Message.Contains("Format"))
                        {
                            MessageBox.Show(texname + " is in the wrong format." + Environment.NewLine + Environment.NewLine + e.Message);
                            return;
                        }
                    }
                }
            }

            Debug.WriteLine("After replace: " + tex2D.arcName);
            WriteDebug("After replace: " + tex2D.arcName);


            DebugOutput.PrintLn("Replacement complete. Now saving pcc: " + pcc.pccFileName, true);

            //tex2D.DumpTexture(@"R:\commit.txt");

            PCCObjects.IExportEntry expEntry = pcc.Exports[IDs[0]];
            expEntry.SetData(tex2D.ToArray(expEntry.DataOffset, pcc));
            expEntry.hasChanged = true;
            pcc.Exports[IDs[0]] = expEntry;

            pcc.saveToFile(pcc.pccFileName);

            /*File.Delete(loc + "tex.dds");
            File.Delete(loc + "data.bin");*/

            int modCount = tex2D.allPccs.Count;

            // KFreon: Elapsed time stuff
            int start = Environment.TickCount;

            // KFreon: Start threading of save function (max 10 threads)
            //ParallelOptions po = new ParallelOptions();
            //po.MaxDegreeOfParallelism = 2;
            if (modCount > 1)
                //Parallel.For(1, modCount, po, (item, loopstate) =>
                for (int item = 1; item < modCount; item++)
                {
                    Debug.WriteLine(pccs[item] +  "   " + IDs[item]);
                    WriteDebug(pccs[item] + "   " + IDs[item]);
                    if (!SaveFile(pccs, IDs, tex2D, item))
                        break;
                    /*loopstate.Stop();
            });*/
                }
            Debug.WriteLine("");
            WriteDebug("");
            // KFreon: More timer stuff
            TimeSpan ts = TimeSpan.FromMilliseconds(Environment.TickCount - start);
            Console.WriteLine(ts.Duration().ToString());
            GC.Collect();
            DebugOutput.Print("All PCC updates finished. ");
        }