示例#1
0
        public void LoadStaticMesh(int index)
        {
            try
            {
                stm = ObjectBinary.From <StaticMesh>(Pcc.GetUExport(index));
                //var mesh = ObjectBinary.From <StaticMesh>(Pcc.getUExport(index));
                // Load meshes for the LODs
                preview?.Dispose();
                preview = new ModelPreview(view.Context.Device, stm, 0, view.Context.TextureCache);
                RefreshChosenMaterialsList();
                CenterView();

                // Update treeview
                treeView1.BeginUpdate();
                treeView1.Nodes.Clear();
                //treeView1.Nodes.Add(stm.ToTree());
                //treeView1.Nodes[0].Expand();
                treeView1.EndUpdate();
                MaterialBox.Visible              = false;
                MaterialApplyButton.Visible      = false;
                MaterialIndexBox.Visible         = false;
                MaterialIndexApplyButton.Visible = false;
            }
            catch (Exception e)
            {
                MessageBox.Show(ExceptionHandlerDialogWPF.FlattenException(e));
            }
        }
示例#2
0
        public void LoadSkeletalMesh(int uindex)
        {
            DisableLODs();
            UnCheckLODs();
            try
            {
                skm = new SkeletalMesh(Pcc.GetUExport(uindex));

                // Load preview model
                preview?.Dispose();
                preview = new ModelPreview(view.Context.Device, skm, view.Context.TextureCache);
                RefreshChosenMaterialsList();
                CenterView();

                // Update treeview
                treeView1.BeginUpdate();
                treeView1.Nodes.Clear();
                treeView1.Nodes.Add(skm.ToTree());
                treeView1.Nodes[0].Expand();
                treeView1.EndUpdate();
                lODToolStripMenuItem.Visible  = true;
                lOD0ToolStripMenuItem.Enabled = true;
                lOD0ToolStripMenuItem.Checked = true;
                if (skm.LODModels.Count > 1)
                {
                    lOD1ToolStripMenuItem.Enabled = true;
                }
                if (skm.LODModels.Count > 2)
                {
                    lOD2ToolStripMenuItem.Enabled = true;
                }
                if (skm.LODModels.Count > 3)
                {
                    lOD3ToolStripMenuItem.Enabled = true;
                }
                MaterialBox.Visible              = false;
                MaterialApplyButton.Visible      = false;
                MaterialIndexBox.Visible         = false;
                MaterialIndexApplyButton.Visible = false;
            }
            catch (Exception e)
            {
                MessageBox.Show(ExceptionHandlerDialogWPF.FlattenException(e));
            }
        }
示例#3
0
        private static string relinkUIndex(IMEPackage importingPCC, ExportEntry relinkingExport, ref int uIndex, string propertyName,
                                           OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, string prefix, bool importExportDependencies = false)
        {
            if (uIndex == 0)
            {
                return(null); //do not relink 0
            }

            IMEPackage destinationPcc = relinkingExport.FileRef;

            if (importingPCC == destinationPcc && uIndex < 0)
            {
                return(null); //do not relink same-pcc imports.
            }
            int sourceObjReference = uIndex;

            //Debug.WriteLine($"{prefix} Relinking:{propertyName}");

            if (crossPCCObjectMappingList.TryGetValue(entry => entry.UIndex == sourceObjReference, out IEntry targetEntry))
            {
                //relink
                uIndex = targetEntry.UIndex;

                //Debug.WriteLine($"{prefix} Relink hit: {sourceObjReference}{propertyName} : {targetEntry.FullPath}");
            }
            else if (uIndex < 0) //It's an unmapped import
            {
                //objProperty is currently pointing to importingPCC as that is where we read the properties from
                int n         = uIndex;
                int origvalue = n;
                //Debug.WriteLine("Relink miss, attempting JIT relink on " + n + " " + rootNode.Text);
                if (importingPCC.IsImport(n))
                {
                    //Get the original import
                    ImportEntry origImport         = importingPCC.GetImport(n);
                    string      origImportFullName = origImport.FullPath;
                    //Debug.WriteLine("We should import " + origImport.GetFullPath);

                    IEntry crossImport          = null;
                    string linkFailedDueToError = null;
                    try
                    {
                        crossImport = EntryImporter.GetOrAddCrossImportOrPackage(origImportFullName, importingPCC, destinationPcc);
                    }
                    catch (Exception e)
                    {
                        //Error during relink
                        DebugOutput.StartDebugger("PCC Relinker");
                        DebugOutput.PrintLn("Exception occured during relink: ");
                        DebugOutput.PrintLn(ExceptionHandlerDialogWPF.FlattenException(e));
                        DebugOutput.PrintLn("You may want to consider discarding this sessions' changes as relinking was not able to properly finish.");
                        linkFailedDueToError = e.Message;
                    }

                    if (crossImport != null)
                    {
                        crossPCCObjectMappingList.Add(origImport, crossImport); //add to mapping to speed up future relinks
                        uIndex = crossImport.UIndex;
                        // Debug.WriteLine($"Relink hit: Dynamic CrossImport for {origvalue} {importingPCC.GetEntry(origvalue).FullPath} -> {uIndex}");
                    }
                    else
                    {
                        string path = importingPCC.GetEntry(uIndex) != null?importingPCC.GetEntry(uIndex).FullPath : "Entry not found: " + uIndex;

                        if (linkFailedDueToError != null)
                        {
                            Debug.WriteLine($"Relink failed: CrossImport porting failed for {relinkingExport.ObjectName.Instanced} {relinkingExport.UIndex}: {propertyName} ({uIndex}): {importingPCC.GetEntry(origvalue).FullPath}");
                            return($"Relink failed for {prefix}{propertyName} {uIndex} in export {path}({relinkingExport.UIndex}): {linkFailedDueToError}");
                        }

                        if (destinationPcc.GetEntry(uIndex) != null)
                        {
                            Debug.WriteLine($"Relink failed: CrossImport porting failed for {relinkingExport.ObjectName.Instanced} {relinkingExport.UIndex}: {propertyName} ({uIndex}): {importingPCC.GetEntry(origvalue).FullPath}");
                            return($"Relink failed: CrossImport porting failed for {prefix}{propertyName} {uIndex} {destinationPcc.GetEntry(uIndex).FullPath} in export {relinkingExport.FullPath}({relinkingExport.UIndex})");
                        }

                        return($"Relink failed: New export does not exist - this is probably a bug in cross import code for {prefix}{propertyName} {uIndex} in export {relinkingExport.FullPath}({relinkingExport.UIndex})");
                    }
                }
            }
            else
            {
                //It's an export
                //Attempt lookup
                ExportEntry sourceExport  = importingPCC.GetUExport(uIndex);
                string      fullPath      = sourceExport.FullPath;
                int         indexValue    = sourceExport.indexValue;
                IEntry      existingEntry = destinationPcc.Exports.FirstOrDefault(x => x.FullPath == fullPath && indexValue == x.indexValue);
                existingEntry ??= destinationPcc.Imports.FirstOrDefault(x => x.FullPath == fullPath);
                if (existingEntry != null)
                {
                    //Debug.WriteLine($"Relink hit [EXPERIMENTAL]: Existing entry in file was found, linking to it:  {uIndex} {sourceExport.InstancedFullPath} -> {existingEntry.InstancedFullPath}");
                    uIndex = existingEntry.UIndex;
                }
                else if (importExportDependencies)
                {
                    if (!crossPCCObjectMappingList.TryGetValue(sourceExport.Parent, out IEntry parent))
                    {
                        parent = EntryImporter.GetOrAddCrossImportOrPackage(sourceExport.ParentFullPath, importingPCC, destinationPcc, true, crossPCCObjectMappingList);
                    }
                    ExportEntry importedExport = EntryImporter.ImportExport(destinationPcc, sourceExport, parent?.UIndex ?? 0, true, crossPCCObjectMappingList);
                    uIndex = importedExport.UIndex;
                }
                else
                {
                    string path = importingPCC.GetEntry(uIndex)?.FullPath ?? $"Entry not found: {uIndex}";
                    Debug.WriteLine($"Relink failed in {relinkingExport.ObjectName.Instanced} {relinkingExport.UIndex}: {propertyName} {uIndex} {path}");
                    return($"Relink failed: {prefix}{propertyName} {uIndex} in export {relinkingExport.FullPath}({relinkingExport.UIndex})");
                }
            }

            return(null);
        }
示例#4
0
        private void GenerateME1FileList()
        {
            // AUTO DLC MOUNTING

            // 1. GET LIST OF DLC DIRECTORIES, SET MAIN VARIABLES
            //IList() me1DLCs; // set list of directorys
            string DLCDirectory = ME1Directory.DLCPath;

            string[] dlcList = Directory.GetDirectories(DLCDirectory, "*.*", SearchOption.TopDirectoryOnly);

            var dlcTable = new Dictionary <int, string>();


            // 2. READ AUTOLOAD.INI FROM EACH DLC.  BUILD TABLE OF DIRECTORIES & MOUNTS
            foreach (string dlcDir in dlcList)
            {
                if (dlcDir.EndsWith("DLC_UNC", StringComparison.InvariantCultureIgnoreCase))
                {
                    dlcTable.Add(1, "DLC_UNC");
                }
                else if (dlcDir.EndsWith("DLC_VEGAS", StringComparison.InvariantCultureIgnoreCase))
                {
                    dlcTable.Add(2, "DLC_VEGAS");
                }
                else
                {
                    string autoLoadPath = Path.Combine(dlcDir, "autoload.ini");  //CHECK IF FILE EXISTS?
                    if (File.Exists(autoLoadPath))
                    {
                        IniFile dlcAutoload = new IniFile(autoLoadPath);
                        string  name        = Path.GetFileName(dlcDir);
                        int     mount       = Convert.ToInt32(dlcAutoload.ReadValue("ME1DLCMOUNT", "ModMount"));
                        dlcTable.Add(mount, name);
                    }
                }
            }
            // ADD BASEGAME = 0
            dlcTable.Add(0, "BioGame");


            // 3. REMOVE ALL SEEKFREEPCPATHs/DLCMOVIEPATHS FROM $DOCUMENTS$\BIOWARE\MASS EFFECT\CONFIG\BIOENGINE.ini
            string userDocs      = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            var    bioEnginePath = Path.Combine(userDocs, "BioWare", "Mass Effect", "Config", "BIOEngine.ini");

            try
            {
                File.SetAttributes(bioEnginePath, File.GetAttributes(bioEnginePath) & ~FileAttributes.ReadOnly);
            }
            catch (IOException e)
            {
                MessageBox.Show($"BioEngine not found. Run config or game to set it up. {ExceptionHandlerDialogWPF.FlattenException(e)}");
                return;
            }


            var BioEngine = new IniFile(bioEnginePath);

            //Clean out seekfreepaths and moviepaths
            while (BioEngine.ReadValue("Core.System", "SeekFreePCPaths") != "")
            {
                BioEngine.RemoveKey("Core.System", "SeekFreePCPaths");
            }

            while (BioEngine.ReadValue("Core.System", "DLC_MoviePaths") != "")
            {
                BioEngine.RemoveKey("Core.System", "DLC_MoviePaths");
            }

            // 4. ADD SEEKFREE PATHS IN REVERSE ORDER (HIGHEST= BIOGAME, ETC).
            //SORT INTO REVERSE ORDER 0 => HIGHEST FOR BIOENGINE
            foreach (KeyValuePair <int, string> item in dlcTable.OrderBy(k => k.Key))
            {
                if (item.Key == 0)
                {
                    //The @"string\thing" allows you to use \ instead of \\. Very good if you are using paths. Though most times you should use Path.Combine() as it will prevent you missing one by accident
                    BioEngine.WriteNewValue("Core.System", "SeekFreePCPaths", @"..\BioGame\CookedPC");
                }
                else
                {
                    BioEngine.WriteNewValue("Core.System", "SeekFreePCPaths", $@"..\DLC\{item.Value}\CookedPC");
                    if (Directory.Exists(Path.Combine(ME1Directory.DLCPath, item.Value, "Movies")))
                    {
                        BioEngine.WriteNewValue("Core.System", "DLC_MoviePaths", $@"..\DLC\{item.Value}\Movies"); //Add MoviePath if present
                    }
                }
            }



            // 5. BUILD FILEINDEX.TXT FILE FOR EACH DLC AND BASEGAME
            // BACKUP BASEGAME Fileindex.txt => Fileindex.bak if not done already.
            var fileIndexBackupFile = Path.Combine(ME1Directory.cookedPath, "FileIndex.bak");

            if (!File.Exists(fileIndexBackupFile))
            {
                //This might fail as the game will be installed into a write-protected directory for most users by default
                try
                {
                    File.Copy(Path.Combine(ME1Directory.cookedPath, "FileIndex.txt"), fileIndexBackupFile);
                }
                catch (IOException e)
                {
                    MessageBox.Show($"Error backup up FileIndex.txt:\n{ExceptionHandlerDialogWPF.FlattenException((e))}");
                    return;
                }
            }

            // CALL FUNCTION TO BUILD EACH FILEINDEX.  START WITH HIGHEST DLC MOUNT -> ADD TO MASTER FILE LIST
            // DO NOT ADD DUPLICATES
            TOCTasks.ClearEx();

            var masterList = new List <string>();

            foreach (KeyValuePair <int, string> fileListStem in dlcTable.OrderByDescending(k => k.Key))
            {
                if (fileListStem.Value == "BioGame")
                {
                    //Using a list is pass by reference so our copy and the function's copy will be the same.
                    //Note this does not work with primitive types like int (unless using the ref keyword), or immutable types like string.
                    //(Immutable in c# = they can't be changed. modifying a string will return a new string instead)
                    GenerateFileList(Path.Combine(ME1Directory.BioGamePath, "CookedPC"), masterList);
                }
                else
                {
                    GenerateFileList(Path.Combine(ME1Directory.DLCPath, fileListStem.Value, "CookedPC"), masterList);
                }
            }



            //7. FINAL MESSAGE ON TOC TASKS
            TOCTasks.Add(new ListBoxTask
            {
                Header     = "Done",
                Icon       = EFontAwesomeIcon.Solid_Check,
                Foreground = Brushes.Green,
                Spinning   = false
            });
        }
示例#5
0
        private string relinkObjectProperty(IMEPackage importingPCC, IMEPackage destinationPCC, ObjectProperty objProperty, List <KeyValuePair <int, int> > crossPCCObjectMappingList)
        {
            if (objProperty.Value == 0)
            {
                return(null); //do not relink 0
            }
            if (importingPCC == destinationPCC && objProperty.Value < 0)
            {
                return(null); //do not relink same-pcc imports.
            }
            int sourceObjReference = objProperty.Value;

            if (sourceObjReference > 0)
            {
                sourceObjReference--; //make 0 based for mapping.
            }
            if (sourceObjReference < 0)
            {
                sourceObjReference++; //make 0 based for mapping.
            }
            //if (objProperty.Name != null)
            //{
            //    Debug.WriteLine(objProperty.Name);
            //}
            KeyValuePair <int, int> mapping = crossPCCObjectMappingList.Where(pair => pair.Key == sourceObjReference).FirstOrDefault();
            var defaultKVP = default(KeyValuePair <int, int>); //struct comparison

            if (!mapping.Equals(defaultKVP))
            {
                //relink
                int newval = 0;
                if (mapping.Value > 0)
                {
                    newval = mapping.Value + 1; //reincrement
                }
                else if (mapping.Value < 0)
                {
                    newval = mapping.Value - 1; //redecrement
                }
                objProperty.Value = (newval);
                IEntry entry = destinationPCC.getEntry(newval);
                string s     = "";
                if (entry != null)
                {
                    s = entry.GetFullPath;
                }
                Debug.WriteLine("Relink hit: " + sourceObjReference + objProperty.Name + ": " + s);
            }
            else if (objProperty.Value < 0) //It's an unmapped import
            {
                //objProperty is currently pointing to importingPCC as that is where we read the properties from
                int n               = objProperty.Value;
                int origvalue       = n;
                int importZeroIndex = Math.Abs(n) - 1;
                //Debug.WriteLine("Relink miss, attempting JIT relink on " + n + " " + rootNode.Text);
                if (n < 0 && importZeroIndex < importingPCC.ImportCount)
                {
                    //Get the original import
                    ImportEntry origImport         = importingPCC.getImport(importZeroIndex);
                    string      origImportFullName = origImport.GetFullPath;
                    //Debug.WriteLine("We should import " + origImport.GetFullPath);

                    ImportEntry crossImport          = null;
                    string      linkFailedDueToError = null;
                    try
                    {
                        crossImport = getOrAddCrossImport(origImportFullName, importingPCC, destinationPCC);
                    }
                    catch (Exception e)
                    {
                        //Error during relink
                        KFreonLib.Debugging.DebugOutput.StartDebugger("PCC Relinker");
                        DebugOutput.PrintLn("Exception occured during relink: ");
                        DebugOutput.PrintLn(ExceptionHandlerDialogWPF.FlattenException(e));
                        DebugOutput.PrintLn("You may want to consider discarding this sessions' changes as relinking was not able to properly finish.");
                        linkFailedDueToError = e.Message;
                    }
                    if (crossImport != null)
                    {
                        //cache item. Imports are stored +1, Exports-1. Someday I will go back and make this just 0 indexed
                        crossPCCObjectMappingList.Add(new KeyValuePair <int, int>(sourceObjReference, crossImport.UIndex + 1)); //add to mapping to speed up future relinks
                        objProperty.Value = crossImport.UIndex;
                        Debug.WriteLine("Relink hit: Dynamic CrossImport for " + origvalue + " " + importingPCC.getEntry(origvalue).GetFullPath + " -> " + objProperty.Value);
                    }
                    else
                    {
                        if (linkFailedDueToError != null)
                        {
                            Debug.WriteLine("Relink failed: CrossImport porting failed for " + objProperty.Name + " " + objProperty.Value + ": " + importingPCC.getEntry(origvalue).GetFullPath);
                            return("Relink failed for " + objProperty.Name + " " + objProperty.Value + ": " + linkFailedDueToError);
                        }
                        else
                        if (destinationPCC.getEntry(objProperty.Value) != null)
                        {
                            Debug.WriteLine("Relink failed: CrossImport porting failed for " + objProperty.Name + " " + objProperty.Value + ": " + importingPCC.getEntry(origvalue).GetFullPath);
                            return("Relink failed: CrossImport porting failed for " + objProperty.Name + " " + objProperty.Value + " " + destinationPCC.getEntry(objProperty.Value).GetFullPath);
                        }
                        else
                        {
                            return("Relink failed: New export does not exist - this is probably a bug in cross import code for " + objProperty.Name + " " + objProperty.Value);
                        }
                    }
                }
            }
            else
            {
                string path = importingPCC.getEntry(objProperty.Value) != null?importingPCC.getEntry(objProperty.Value).GetFullPath : "Entry not found: " + objProperty.Value;

                Debug.WriteLine("Relink failed: " + objProperty.Name + " " + objProperty.Value + " " + path);
                return("Relink failed: " + objProperty.Name + " " + objProperty.Value + " " + path);
            }
            return(null);
        }
示例#6
0
        private void saveByReconstructing(string path, bool isSaveAs)
        {
            try
            {
                if (exports.Any(exp => exp.ClassName == "Level"))
                {
                    Flags |= EPackageFlags.Map;
                }

                //UDK does not like it when exports have the forced export flag, which is common in ME files
                foreach (ExportEntry export in exports)
                {
                    //if (export.ClassName != "Package")
                    {
                        export.ExportFlags &= ~EExportFlags.ForcedExport;
                    }
                }

                var ms = new MemoryStream();

                //just for positioning. We write over this later when the header values have been updated
                WriteHeader(ms);

                //name table
                NameOffset = (int)ms.Position;
                NameCount  = Gen0NameCount = names.Count;
                foreach (string name in names)
                {
                    ms.WriteUnrealStringASCII(name);
                    ms.WriteInt32(0);
                    ms.WriteInt32(458768);
                }

                //import table
                ImportOffset = (int)ms.Position;
                ImportCount  = imports.Count;
                foreach (ImportEntry e in imports)
                {
                    ms.WriteFromBuffer(e.Header);
                }

                //export table
                ExportOffset = (int)ms.Position;
                ExportCount  = Gen0NetworkedObjectCount = Gen0ExportCount = exports.Count;
                foreach (ExportEntry e in exports)
                {
                    e.HeaderOffset = (uint)ms.Position;
                    ms.WriteFromBuffer(e.Header);
                }

                //dependency table
                DependencyTableOffset = (int)ms.Position;
                ms.WriteZeros(4 * ExportCount);

                importExportGuidsOffset = (int)ms.Position;
                importGuidsCount        = exportGuidsCount = 0;

                //generate thumbnails
                ThumbnailTable.Clear();
                foreach (ExportEntry export in exports)
                {
                    if (export.IsTexture())
                    {
                        var tex = new Texture2D(export);
                        var mip = tex.GetTopMip();
                        ThumbnailTable.Add(new Thumbnail
                        {
                            ClassName = export.ClassName,
                            PathName  = export.InstancedFullPath,
                            Width     = mip.width,
                            Height    = mip.height,
                            Data      = tex.GetPNG(mip)
                        });
                    }
                }

                //write thumbnails
                foreach (Thumbnail thumbnail in ThumbnailTable)
                {
                    thumbnail.Offset = (int)ms.Position;
                    ms.WriteInt32(thumbnail.Width);
                    ms.WriteInt32(thumbnail.Height);
                    ms.WriteInt32(thumbnail.Data.Length);
                    ms.WriteFromBuffer(thumbnail.Data);
                    //File.WriteAllBytes(Path.Combine(Path.GetDirectoryName(FilePath), $"{thumbnail.PathName}({thumbnail.ClassName}).png"), thumbnail.Data);
                }
                thumbnailTableOffset = (int)ms.Position;
                ms.WriteInt32(ThumbnailTable.Count);
                foreach (Thumbnail thumbnail in ThumbnailTable)
                {
                    ms.WriteUnrealStringASCII(thumbnail.ClassName);
                    ms.WriteUnrealStringASCII(thumbnail.PathName);
                    ms.WriteInt32(thumbnail.Offset);
                }

                FullHeaderSize = (int)ms.Position;

                //export data
                foreach (ExportEntry e in exports)
                {
                    UpdateUDKOffsets(e, (int)ms.Position);
                    e.DataOffset = (int)ms.Position;

                    ms.WriteFromBuffer(e.Data);
                    //update size and offset in already-written header
                    long pos = ms.Position;
                    ms.JumpTo(e.HeaderOffset + 32);
                    ms.WriteInt32(e.DataSize); //DataSize might have been changed by UpdateOffsets
                    ms.WriteInt32(e.DataOffset);
                    ms.JumpTo(pos);
                }

                //re-write header with updated values
                ms.JumpTo(0);
                WriteHeader(ms);


                File.WriteAllBytes(path, ms.ToArray());
                if (!isSaveAs)
                {
                    AfterSave();
                }
            }
            catch (Exception ex) when(!App.IsDebug)
            {
                MessageBox.Show($"Error saving {FilePath}:\n{ExceptionHandlerDialogWPF.FlattenException(ex)}");
            }
        }
示例#7
0
        private void saveByReconstructing(string path, bool isSaveAs)
        {
            try
            {
                var ms = new MemoryStream();

                //just for positioning. We write over this later when the header values have been updated
                WriteHeader(ms);

                //name table
                NameOffset = (int)ms.Position;
                NameCount  = Gen0NameCount = names.Count;
                foreach (string name in names)
                {
                    switch (Game)
                    {
                    case MEGame.ME1:
                        ms.WriteUnrealStringASCII(name);
                        ms.WriteInt32(0);
                        ms.WriteInt32(458768);
                        break;

                    case MEGame.ME2:
                        ms.WriteUnrealStringASCII(name);
                        ms.WriteInt32(-14);
                        break;

                    case MEGame.ME3:
                        ms.WriteUnrealStringUnicode(name);
                        break;
                    }
                }

                //import table
                ImportOffset = (int)ms.Position;
                ImportCount  = imports.Count;
                foreach (ImportEntry e in imports)
                {
                    ms.WriteFromBuffer(e.Header);
                }

                //export table
                ExportOffset = (int)ms.Position;
                ExportCount  = Gen0ExportCount = exports.Count;
                foreach (ExportEntry e in exports)
                {
                    e.HeaderOffset = (uint)ms.Position;
                    ms.WriteFromBuffer(e.Header);
                }

                DependencyTableOffset = (int)ms.Position;
                ms.WriteInt32(0);//zero-count DependencyTable
                FullHeaderSize = ImportExportGuidsOffset = (int)ms.Position;

                //export data
                foreach (ExportEntry e in exports)
                {
                    switch (Game)
                    {
                    case MEGame.ME1:
                        UpdateME1Offsets(e, (int)ms.Position);
                        break;

                    case MEGame.ME2:
                        UpdateME2Offsets(e, (int)ms.Position);
                        break;

                    case MEGame.ME3:
                        UpdateME3Offsets(e, (int)ms.Position);
                        break;
                    }

                    e.DataOffset = (int)ms.Position;


                    ms.WriteFromBuffer(e.Data);
                    //update size and offset in already-written header
                    long pos = ms.Position;
                    ms.JumpTo(e.HeaderOffset + 32);
                    ms.WriteInt32(e.DataSize); //DataSize might have been changed by UpdateOffsets
                    ms.WriteInt32(e.DataOffset);
                    ms.JumpTo(pos);
                }

                //re-write header with updated values
                ms.JumpTo(0);
                WriteHeader(ms);


                File.WriteAllBytes(path, ms.ToArray());
                if (!isSaveAs)
                {
                    AfterSave();
                }
            }
            catch (Exception ex) when(!App.IsDebug)
            {
                MessageBox.Show($"Error saving {FilePath}:\n{ExceptionHandlerDialogWPF.FlattenException(ex)}");
            }
        }