public static void WritePropHeader(this Stream stream, IMEPackage pcc, string propName, PropertyType type, int size) { stream.WriteValueS32(pcc.FindNameOrAdd(propName)); stream.WriteValueS32(0); stream.WriteValueS32(pcc.FindNameOrAdd(type.ToString())); stream.WriteValueS32(0); stream.WriteValueS32(size); stream.WriteValueS32(0); }
/// <summary> /// Trashes an entry. /// </summary> /// <param name="entry">Entry to trash</param> /// <param name="trashContainer">Container for trash. Pass null if you want to create the trash container from the passed in value.</param> /// <param name="packageClassIdx">Idx for package class. Prevents multiple calls to find it</param> /// <returns>New trash container, otherwise will be null</returns> public static ExportEntry TrashEntry(IEntry entry, ExportEntry trashContainer, int packageClassIdx) { IMEPackage pcc = entry.FileRef; if (entry is ImportEntry imp) { if (trashContainer == null) { trashContainer = TrashEntry(new ExportEntry(pcc), null, packageClassIdx); pcc.addExport(trashContainer); trashContainer.indexValue = 0; } imp.idxClassName = pcc.FindNameOrAdd("Package"); imp.idxPackageFile = pcc.FindNameOrAdd("Core"); imp.idxLink = trashContainer.UIndex; imp.idxObjectName = pcc.FindNameOrAdd("Trash"); imp.indexValue = 0; } else if (entry is ExportEntry exp) { MemoryStream trashData = new MemoryStream(); trashData.WriteInt32(-1); trashData.WriteInt32(pcc.findName("None")); trashData.WriteInt32(0); exp.Data = trashData.ToArray(); exp.idxArchtype = 0; exp.idxSuperClass = 0; exp.indexValue = 0; exp.idxClass = packageClassIdx; exp.ObjectFlags &= ~UnrealFlags.EObjectFlags.HasStack; if (trashContainer == null) { exp.idxObjectName = pcc.FindNameOrAdd(UnrealPackageFile.TrashPackageName); exp.idxLink = 0; if (exp.idxLink == exp.UIndex) { Debugger.Break(); } exp.PackageGUID = UnrealPackageFile.TrashPackageGuid; trashContainer = exp; } else { exp.idxLink = trashContainer.UIndex; if (exp.idxLink == exp.UIndex) { //This should not occur Debugger.Break(); } exp.idxObjectName = pcc.FindNameOrAdd("Trash"); exp.PackageGUID = Guid.Empty; } } return(trashContainer); }
public static void WriteEnumProperty(this Stream stream, IMEPackage pcc, string propName, NameReference enumName, NameReference enumValue) { stream.WritePropHeader(pcc, propName, PropertyType.ByteProperty, 8); if (pcc.Game == MEGame.ME3) { stream.WriteValueS32(pcc.FindNameOrAdd(enumName.Name)); stream.WriteValueS32(enumName.Number); } stream.WriteValueS32(pcc.FindNameOrAdd(enumValue.Name)); stream.WriteValueS32(enumValue.Number); }
public static void WriteEnumProperty(this Stream stream, IMEPackage pcc, string propName, string enumName, string enumValue, int index = 0) { stream.WritePropHeader(pcc, propName, PropertyType.ByteProperty, 8); if (pcc.Game == MEGame.ME3) { stream.WriteValueS32(pcc.FindNameOrAdd(enumName)); stream.WriteValueS32(0); } stream.WriteValueS32(pcc.FindNameOrAdd(enumValue)); stream.WriteValueS32(index); }
public override void WriteTo(Stream stream, IMEPackage pcc, bool valueOnly = false) { if (!valueOnly) { stream.WriteValueS32(pcc.FindNameOrAdd(Name)); stream.WriteValueS32(0); stream.WriteValueS32(pcc.FindNameOrAdd(TypeName)); stream.WriteValueS32(0); stream.WriteValueS32(raw.Length); stream.WriteValueS32(0); } stream.WriteBytes(raw); }
public static void WriteStructProperty(this Stream stream, IMEPackage pcc, string propName, string structName, MemoryStream value) { stream.WritePropHeader(pcc, propName, PropertyType.StructProperty, (int)value.Length); stream.WriteValueS32(pcc.FindNameOrAdd(structName)); stream.WriteValueS32(0); stream.WriteStream(value); }
public static void WriteDelegateProperty(this Stream stream, IMEPackage pcc, string propName, ScriptDelegate value) { stream.WritePropHeader(pcc, propName, PropertyType.DelegateProperty, 12); stream.WriteInt32(value.Object); stream.WriteInt32(pcc.FindNameOrAdd(value.FunctionName.Name)); stream.WriteInt32(value.FunctionName.Number); }
public static void WriteNoneProperty(this Stream stream, IMEPackage pcc) { //Debug.WriteLine("Writing none property at 0x" + stream.Position.ToString("X6")); stream.WriteValueS32(pcc.FindNameOrAdd("None")); stream.WriteValueS32(0); }
public static void WriteDelegateProperty(this Stream stream, IMEPackage pcc, string propName, int unk, NameReference value) { stream.WritePropHeader(pcc, propName, PropertyType.DelegateProperty, 12); stream.WriteValueS32(unk); stream.WriteValueS32(pcc.FindNameOrAdd(value.Name)); stream.WriteValueS32(value.Number); }
public static void WriteNameProperty(this Stream stream, IMEPackage pcc, string propName, NameReference value) { //Debug.WriteLine("Writing name property " + propName + ", value: " + value + " at 0x" + stream.Position.ToString("X6")); stream.WritePropHeader(pcc, propName, PropertyType.NameProperty, 8); stream.WriteValueS32(pcc.FindNameOrAdd(value.Name)); stream.WriteValueS32(value.Number); }
/// <summary> /// Trashes an entry. /// </summary> /// <param name="entry">Entry to trash</param> /// <param name="trashContainer">Container for trash. Pass null if you want to create the trash container from the passed in value.</param> /// <param name="packageClass">Package class. Prevents multiple calls to find it</param> /// <returns>New trash container, otherwise will be null</returns> private static ExportEntry TrashEntry(IEntry entry, ExportEntry trashContainer, IEntry packageClass) { IMEPackage pcc = entry.FileRef; if (entry is ImportEntry imp) { if (trashContainer == null) { trashContainer = TrashEntry(new ExportEntry(pcc), null, packageClass); pcc.AddExport(trashContainer); trashContainer.indexValue = 0; } imp.ClassName = "Package"; imp.PackageFile = "Core"; imp.idxLink = trashContainer.UIndex; imp.ObjectName = "Trash"; imp.indexValue = 0; } else if (entry is ExportEntry exp) { MemoryStream trashData = new MemoryStream(); trashData.WriteInt32(-1); trashData.WriteInt32(pcc.FindNameOrAdd("None")); trashData.WriteInt32(0); exp.Data = trashData.ToArray(); exp.Archetype = null; exp.SuperClass = null; exp.indexValue = 0; exp.Class = packageClass; exp.ObjectFlags &= ~UnrealFlags.EObjectFlags.HasStack; exp.ObjectFlags &= ~UnrealFlags.EObjectFlags.ArchetypeObject; exp.ObjectFlags &= ~UnrealFlags.EObjectFlags.ClassDefaultObject; if (trashContainer == null) { exp.ObjectName = UnrealPackageFile.TrashPackageName; exp.idxLink = 0; if (exp.idxLink == exp.UIndex) { Debugger.Break(); } exp.PackageGUID = UnrealPackageFile.TrashPackageGuid; trashContainer = exp; } else { exp.idxLink = trashContainer.UIndex; if (exp.idxLink == exp.UIndex) { //This should not occur Debugger.Break(); } exp.ObjectName = "Trash"; exp.PackageGUID = Guid.Empty; } } return(trashContainer); }
public static void WriteStructProperty(this Stream stream, IMEPackage pcc, string propName, string structName, MemoryStream value) { //Debug.WriteLine("Writing struct property " + propName + ", value: " + value + " at 0x" + stream.Position.ToString("X6")); stream.WritePropHeader(pcc, propName, PropertyType.StructProperty, (int)value.Length); stream.WriteValueS32(pcc.FindNameOrAdd(structName)); stream.WriteValueS32(0); stream.WriteStream(value); }
public static void WriteByteProperty(this Stream stream, IMEPackage pcc, string propName, byte value) { stream.WritePropHeader(pcc, propName, PropertyType.ByteProperty, 1); if (pcc.Game == MEGame.ME3) { stream.WriteValueS32(pcc.FindNameOrAdd("None")); stream.WriteValueS32(0); } stream.WriteByte(value); }
public static void WriteByteProperty(this Stream stream, IMEPackage pcc, string propName, byte value) { //Debug.WriteLine("Writing byte property " + propName + ", value: " + value + " at 0x" + stream.Position.ToString("X6")); stream.WritePropHeader(pcc, propName, PropertyType.ByteProperty, 1); if (pcc.Game == MEGame.ME3) { stream.WriteValueS32(pcc.FindNameOrAdd("None")); stream.WriteValueS32(0); } stream.WriteByte(value); }
public override void WriteTo(Stream stream, IMEPackage pcc, bool valueOnly = false) { if (!valueOnly) { stream.WriteNameProperty(pcc, Name, Value); } else { stream.WriteValueS32(pcc.FindNameOrAdd(Value.Name)); stream.WriteValueS32(Value.Number); } }
//if neccessary, will fill in parents as Package Imports (if the import you need has non-Package parents, don't use this method) public static IEntry getEntryOrAddImport(this IMEPackage pcc, string fullPath, string className = "Class", string packageFile = "Core") { if (string.IsNullOrEmpty(fullPath)) { return(null); } //see if this import exists locally foreach (ImportEntry imp in pcc.Imports) { if (imp.GetFullPath == fullPath) { return(imp); } } //see if this is an export and exists locally foreach (ExportEntry exp in pcc.Exports) { if (exp.GetFullPath == fullPath) { return(exp); } } string[] pathParts = fullPath.Split('.'); IEntry parent = pcc.getEntryOrAddImport(string.Join(".", pathParts.Take(pathParts.Length - 1)), "Package"); var import = new ImportEntry(pcc) { idxLink = parent?.UIndex ?? 0, idxClassName = pcc.FindNameOrAdd(className), idxObjectName = pcc.FindNameOrAdd(pathParts.Last()), idxPackageFile = pcc.FindNameOrAdd(packageFile) }; pcc.addImport(import); return(import); }
public override void WriteTo(Stream stream, IMEPackage pcc, bool valueOnly = false) { if (!valueOnly) { stream.WriteDelegateProperty(pcc, Name, unk, Value); } else { stream.WriteValueS32(unk); stream.WriteValueS32(pcc.FindNameOrAdd(Value.Name)); stream.WriteValueS32(Value.count); } }
public static void TrashEntries(IMEPackage pcc, IEnumerable <IEntry> itemsToTrash) { ExportEntry trashTopLevel = pcc.Exports.FirstOrDefault(x => x.idxLink == 0 && x.ObjectName == UnrealPackageFile.TrashPackageName); ImportEntry packageImport = pcc.Imports.FirstOrDefault(x => x.GetFullPath == "Core.Package"); if (packageImport == null) { ImportEntry coreImport = pcc.Imports.FirstOrDefault(x => x.GetFullPath == "Core"); if (coreImport == null) { //really small file coreImport = new ImportEntry(pcc) { idxObjectName = pcc.FindNameOrAdd("Core"), idxClassName = pcc.FindNameOrAdd("Package"), idxLink = 0, idxPackageFile = pcc.FindNameOrAdd("Core") }; pcc.addImport(coreImport); } //Package isn't an import, could be one of the 2DA files or other small ones packageImport = new ImportEntry(pcc) { idxObjectName = pcc.FindNameOrAdd("Package"), idxClassName = pcc.FindNameOrAdd("Class"), idxLink = coreImport.UIndex, idxPackageFile = pcc.FindNameOrAdd("Core") }; pcc.addImport(packageImport); } foreach (IEntry entry in itemsToTrash) { if (entry == trashTopLevel || entry.ObjectName == "Trash") //don't trash what's already been trashed { continue; } trashTopLevel = TrashEntry(entry, trashTopLevel, packageImport.UIndex); } pcc.RemoveTrailingTrash(); }
/// <summary> /// Imports an export from another package file. /// </summary> /// <param name="mePackage"></param> /// <param name="ex">Export object from the other package to import</param> /// <param name="link">Local parent node UIndex</param> /// <param name="outputEntry">Newly generated export entry reference</param> /// <returns></returns> private static bool importExport(IMEPackage mePackage, ExportEntry ex, int link, out ExportEntry outputEntry) { byte[] prePropBinary; if (ex.HasStack) { if (mePackage.Game < MEGame.ME3) { prePropBinary = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }; } else { prePropBinary = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 }; } } else { int start = ex.GetPropertyStart(); prePropBinary = new byte[start]; } PropertyCollection props = ex.GetProperties(); //store copy of names list in case something goes wrong List <string> names = mePackage.Names.ToList(); try { if (ex.Game != mePackage.Game) { props = EntryPruner.RemoveIncompatibleProperties(ex.FileRef, props, ex.ClassName, mePackage.Game); } } catch (Exception exception) { //restore namelist in event of failure. mePackage.setNames(names); Console.WriteLine($"Error occured while trying to import {ex.ObjectName} : {exception.Message}"); //MessageBox.Show($"Error occured while trying to import {ex.ObjectName} : {exception.Message}"); outputEntry = null; return(false); } //takes care of slight header differences between ME1/2 and ME3 byte[] newHeader = ex.GenerateHeader(mePackage.Game); //for supported classes, this will add any names in binary to the Name table, as well as take care of binary differences for cross-game importing //for unsupported classes, this will just copy over the binary byte[] binaryData = ExportBinaryConverter.ConvertPostPropBinary(ex, mePackage.Game).ToArray(mePackage); int classValue = 0; int archetype = 0; int superclass = 0; //Set class. This will only work if the class is an import, as we can't reliably pull in exports without lots of other stuff. if (ex.idxClass < 0) { //The class of the export we are importing is an import. We should attempt to relink this. ImportEntry portingFromClassImport = ex.FileRef.getImport(ex.idxClass); IEntry newClassImport = getOrAddCrossImportOrPackage(portingFromClassImport.GetFullPath, ex.FileRef, mePackage); classValue = newClassImport.UIndex; } else if (ex.idxClass > 0) { //Todo: Add cross mapping support as multi-mode will allow this to work now ExportEntry portingInClass = ex.FileRef.getUExport(ex.idxClass); ExportEntry matchingExport = mePackage.Exports.FirstOrDefault(x => x.GetIndexedFullPath == portingInClass.GetIndexedFullPath); if (matchingExport != null) { classValue = matchingExport.UIndex; } } //Set superclass if (ex.idxSuperClass < 0) { //The class of the export we are importing is an import. We should attempt to relink this. ImportEntry portingFromClassImport = ex.FileRef.getImport(ex.idxSuperClass); IEntry newClassImport = getOrAddCrossImportOrPackage(portingFromClassImport.GetFullPath, ex.FileRef, mePackage); superclass = newClassImport.UIndex; } else if (ex.idxSuperClass > 0) { //Todo: Add cross mapping support as multi-mode will allow this to work now ExportEntry portingInClass = ex.FileRef.getUExport(ex.idxSuperClass); ExportEntry matchingExport = mePackage.Exports.FirstOrDefault(x => x.GetIndexedFullPath == portingInClass.GetIndexedFullPath); if (matchingExport != null) { superclass = matchingExport.UIndex; } } //Check archetype. if (ex.idxArchtype < 0) { ImportEntry portingFromClassImport = ex.FileRef.getImport(ex.idxArchtype); IEntry newClassImport = getOrAddCrossImportOrPackage(portingFromClassImport.GetFullPath, ex.FileRef, mePackage); archetype = newClassImport.UIndex; } else if (ex.idxArchtype > 0) { ExportEntry portingInClass = ex.FileRef.getUExport(ex.idxArchtype); ExportEntry matchingExport = mePackage.Exports.FirstOrDefault(x => x.GetIndexedFullPath == portingInClass.GetIndexedFullPath); if (matchingExport != null) { archetype = matchingExport.UIndex; } } outputEntry = new ExportEntry(mePackage, prePropBinary, props, binaryData) { Header = newHeader, idxClass = classValue, idxObjectName = mePackage.FindNameOrAdd(ex.FileRef.getNameEntry(ex.idxObjectName)), idxLink = link, idxSuperClass = superclass, idxArchtype = archetype }; mePackage.addExport(outputEntry); return(true); }
public static void WriteNoneProperty(this Stream stream, IMEPackage pcc) { stream.WriteValueS32(pcc.FindNameOrAdd("None")); stream.WriteValueS32(0); }
public static bool AttemptMerge(IMEPackage vanillaPackage, IMEPackage modifiedVanillaPackage, IMEPackage targetPackage) { PackageDelta vanillaToModifiedDelta = PackageDelta.CalculateDelta(vanillaPackage, modifiedVanillaPackage); PackageDelta vanillaToTargetDelta = PackageDelta.CalculateDelta(vanillaPackage, targetPackage); string loggingPrefix = File.Exists(targetPackage.FilePath) ? Path.GetFileName(targetPackage.FilePath) : targetPackage.FilePath; //Check merge conditions var nameConflicts = vanillaToModifiedDelta.NameDeltas.Keys.Intersect(vanillaToTargetDelta.NameDeltas.Keys).ToList(); var importConflicts = vanillaToModifiedDelta.ImportDeltas.Keys.Intersect(vanillaToTargetDelta.ImportDeltas.Keys).ToList(); var exportConflicts = vanillaToModifiedDelta.ExportDeltas.Keys.Intersect(vanillaToTargetDelta.ExportDeltas.Keys).ToList(); Log.Information($@"[{loggingPrefix}] Performing three way merge pre-check"); //Name deltas if (nameConflicts.Count > 0) { //need to check if the conflicts result in same value, in this case it would not be a conflict. foreach (int nameIndex in nameConflicts) { var modifiedName = modifiedVanillaPackage.Names[nameIndex]; var targetName = targetPackage.Names[nameIndex]; if (modifiedName != targetName) { //Differing names in same spots. Log.Information($@"[{loggingPrefix}] Cannot merge files: Name index {nameIndex} is different between modified and target."); return(false); } } } if (importConflicts.Count > 0) { //todo } if (exportConflicts.Count > 0) { //hmm... this will be a tough one. foreach (int exportTableIndex in exportConflicts) { //we will have to check sizes if we ever hope to have way to merge this var modifiedData = modifiedVanillaPackage.Exports[exportTableIndex].Data; var targetData = targetPackage.Exports[exportTableIndex].Data; if (!modifiedData.SequenceEqual(targetData)) { Log.Information($@"[{loggingPrefix}] Cannot merge files: Export table index {exportTableIndex} data is different between modified and target."); return(false); } //We will have to ignore size here somehow... //var modifiedHeader = modifiedVanillaPackage.Exports[exportTableIndex].Header; //var targetHeader = targetPackage.Exports[exportTableIndex].Header; } } //Merge is OK to perform //Apply vanilla to modified delta to target package foreach (var nameDelta in vanillaToModifiedDelta.NameDeltas) { if (nameDelta.Key >= targetPackage.NameCount) { //add it Log.Information($@"[{loggingPrefix}] Adding name {nameDelta.Value}"); targetPackage.FindNameOrAdd(nameDelta.Value); } else { Log.Information($@"[{loggingPrefix}] Updating name index {nameDelta.Key} to {nameDelta.Value}"); targetPackage.replaceName(nameDelta.Key, nameDelta.Value); } } foreach (var exportDelta in vanillaToModifiedDelta.ExportDeltas) { if (exportDelta.Key >= targetPackage.ExportCount) { //add it Log.Information($@"[{loggingPrefix}] Adding export {exportDelta.Value.InstancedFullPath}"); targetPackage.AddExport(exportDelta.Value); //not sure if this is possible } else { //gonna need this reviewed, not entirely sure this is OK to do Log.Information($@"[{loggingPrefix}] Updating export {exportDelta.Value.InstancedFullPath}"); targetPackage.Exports[exportDelta.Key].Data = exportDelta.Value.Data; targetPackage.Exports[exportDelta.Key].Header = exportDelta.Value.Header; } } foreach (var importDelta in vanillaToModifiedDelta.ImportDeltas) { if (importDelta.Key >= targetPackage.ImportCount) { //add it Log.Information($@"[{loggingPrefix}] Adding import {importDelta.Value.InstancedFullPath}"); targetPackage.AddImport(importDelta.Value); //not sure if this is possible } else { Log.Information($@"[{loggingPrefix}] Updating import {importDelta.Value.InstancedFullPath}"); //gonna need this reviewed, not entirely sure this is OK to do //targetPackage.Imports[importDelta.Key].Data = importDelta.Value.Data; targetPackage.Imports[importDelta.Key].Header = importDelta.Value.Header; } } Log.Information($@"[{loggingPrefix}] Finished three way merge"); return(true); }
/// <summary> /// Adds an import from the importingPCC to the destinationPCC with the specified importFullName, or returns the existing one if it can be found. /// This method will look at importingPCC's import upstream chain and check for the most downstream one's existence in destinationPCC, /// including if none can be founc (in which case the entire upstream is copied). It will then create new imports to match the remaining /// downstream ones and return the originally named import, however now located in destinationPCC. /// </summary> /// <param name="importFullName">GetFullPath() of an import from ImportingPCC</param> /// <param name="importingPCC">PCC to import imports from</param> /// <param name="destinationPCC">PCC to add imports to</param> /// <returns></returns> private ImportEntry getOrAddCrossImport(string importFullName, IMEPackage importingPCC, IMEPackage destinationPCC, int?forcedLinkIdx = null) { //This code is kind of ugly, sorry. //see if this import exists locally foreach (ImportEntry imp in destinationPCC.Imports) { if (imp.GetFullPath == importFullName) { return(imp); } } //Import doesn't exist, so we're gonna need to add it //But first we need to figure out what needs to be added upstream as links //Search upstream until we find something, or we can't get any more upstreams ImportEntry mostdownstreamimport = null; string[] importParts = importFullName.Split('.'); if (!forcedLinkIdx.HasValue) { List <int> upstreamLinks = new List <int>(); //0 = top level, 1 = next level... n = what we wanted to import int upstreamCount = 1; ImportEntry upstreamImport = null; //get number of required upstream imports that do not yet exist while (upstreamCount < importParts.Count()) { string upstream = String.Join(".", importParts, 0, importParts.Count() - upstreamCount); foreach (ImportEntry imp in destinationPCC.Imports) { if (imp.GetFullPath == upstream) { upstreamImport = imp; break; } } if (upstreamImport != null) { //We found an upsteam import that already exists break; } upstreamCount++; } IExportEntry donorUpstreamExport = null; if (upstreamImport == null) { //We have to import the entire upstream chain string fullobjectname = importParts[0]; ImportEntry donorTopLevelImport = null; foreach (ImportEntry imp in importingPCC.Imports) //importing side info we will move to our dest pcc { if (imp.GetFullPath == fullobjectname) { donorTopLevelImport = imp; break; } } if (donorTopLevelImport == null) { //This is issue KinkoJiro had. It is aborting relinking at this step. Will need to find a way to //work with exports as parents for imports which will block it. //Update: This has been partially implemented. Debug.WriteLine("No upstream import was found in the source file. It's probably an export: " + importFullName); foreach (IExportEntry exp in destinationPCC.Exports) //importing side info we will move to our dest pcc { //Console.WriteLine(exp.GetFullPath); if (exp.GetFullPath == fullobjectname) { // = imp; //We will need to find a way to cross map this as this will block cross import mapping unless these exports already exist. Debug.WriteLine("FOUND UPSTREAM, AS EXPORT!"); KFreonLib.Debugging.DebugOutput.StartDebugger("Package Editor Relinker"); KFreonLib.Debugging.DebugOutput.PrintLn("Error: Upstream item that is required is an export in the pcc to import from: " + fullobjectname); donorUpstreamExport = exp; upstreamCount--; //level 1 now from the top down //Create new import with this as higher IDK break; } } if (donorUpstreamExport == null) { Debug.WriteLine("An error has occured. Could not find an upstream import or export for relinking: " + fullobjectname + " from " + pcc.FileName); return(null); } } if (donorUpstreamExport == null) { //Create new toplevel import and set that as the most downstream one. (top = bottom at this point) int downstreamPackageFile = destinationPCC.FindNameOrAdd(Path.GetFileNameWithoutExtension(donorTopLevelImport.PackageFile)); int downstreamClassName = destinationPCC.FindNameOrAdd(donorTopLevelImport.ClassName); int downstreamName = destinationPCC.FindNameOrAdd(fullobjectname); mostdownstreamimport = new ImportEntry(destinationPCC); // mostdownstreamimport.idxLink = downstreamLinkIdx; ?? mostdownstreamimport.idxClassName = downstreamClassName; mostdownstreamimport.idxObjectName = downstreamName; mostdownstreamimport.idxPackageFile = downstreamPackageFile; destinationPCC.addImport(mostdownstreamimport); //Add new top level downstream import upstreamImport = mostdownstreamimport; upstreamCount--; //level 1 now from the top down //return null; } } //Have an upstream import, now we need to add downstream imports. while (upstreamCount > 0) { upstreamCount--; string fullobjectname = String.Join(".", importParts, 0, importParts.Count() - upstreamCount); ImportEntry donorImport = null; //Get or create names for creating import and get upstream linkIdx int downstreamName = destinationPCC.FindNameOrAdd(importParts[importParts.Count() - upstreamCount - 1]); foreach (ImportEntry imp in importingPCC.Imports) //importing side info we will move to our dest pcc { if (imp.GetFullPath == fullobjectname) { donorImport = imp; break; } } if (donorImport == null) { throw new Exception("No suitable upstream import was found for porting - this may be an export in the source file that is referenced as a parent or dependency. You should import this object and its parents first. " + fullobjectname + "(as part of " + importFullName + ")"); } int downstreamPackageFile = destinationPCC.FindNameOrAdd(Path.GetFileNameWithoutExtension(donorImport.PackageFile)); int downstreamClassName = destinationPCC.FindNameOrAdd(donorImport.ClassName); mostdownstreamimport = new ImportEntry(destinationPCC); mostdownstreamimport.idxLink = donorUpstreamExport == null ? upstreamImport.UIndex : donorUpstreamExport.UIndex; mostdownstreamimport.idxClassName = downstreamClassName; mostdownstreamimport.idxObjectName = downstreamName; mostdownstreamimport.idxPackageFile = downstreamPackageFile; destinationPCC.addImport(mostdownstreamimport); upstreamImport = mostdownstreamimport; } } else { //get importing import ImportEntry importingImport = importingPCC.Imports.FirstOrDefault(x => x.GetFullPath == importFullName); //this shouldn't be null mostdownstreamimport = new ImportEntry(destinationPCC); mostdownstreamimport.idxLink = forcedLinkIdx.Value; mostdownstreamimport.idxClassName = destinationPCC.FindNameOrAdd(importingImport.ClassName); mostdownstreamimport.idxObjectName = destinationPCC.FindNameOrAdd(importingImport.ObjectName); mostdownstreamimport.idxPackageFile = destinationPCC.FindNameOrAdd(Path.GetFileNameWithoutExtension(importingImport.PackageFile)); destinationPCC.addImport(mostdownstreamimport); } return(mostdownstreamimport); }
public static IEntry GetEntryOrAddImport(IMEPackage Pcc, string importFullName) { //foreach (ImportEntry imp in Pcc.Imports) //{ // if (imp.GetFullPath == importFullName) // { // return imp; // } //} var fullPathMappingList = new List <(string fullpath, IEntry entry)>(); foreach (ImportEntry imp in Pcc.Imports) { fullPathMappingList.Add((imp.GetFullPath, imp)); } foreach (ExportEntry exp in Pcc.Exports) { fullPathMappingList.Add((exp.GetFullPath, exp)); } var directMapping = fullPathMappingList.Where(x => x.fullpath == importFullName).ToList(); if (directMapping.Count == 1) { return(directMapping[0].entry); //direct single match } //Find an upstream entry to attach our import to (we can't add exports) string[] importParts = importFullName.Split('.'); int upstreamCount = 1; IEntry upstreamEntryToAttachTo = null; string upstreamfullpath; while (upstreamCount < importParts.Length) { upstreamfullpath = string.Join(".", importParts, 0, importParts.Length - upstreamCount); var upstreammatchinglist = fullPathMappingList.Where(x => x.fullpath == upstreamfullpath).ToList(); if (upstreammatchinglist.Where(x => x.entry is ExportEntry).HasExactly(1) || upstreammatchinglist.Where(x => x.entry is ImportEntry).HasExactly(1)) { upstreamEntryToAttachTo = upstreammatchinglist[0].entry; break; } /*if (upstreamEntryToAttachTo != null) * { * break; * }*/ upstreamCount++; } //upstreamImport = Pcc.Imports.FirstOrDefault(x => x.GetFullPath == upstream); //Check if this is an export instead /* itemAsImport = Pcc.Exports.FirstOrDefault(x => x.GetFullPath == importFullName && x.indexValue == 0); * if (itemAsImport != null) * { * return itemAsImport; * }*/ //Import doesn't exist, so we're gonna need to add it //But first we need to figure out what needs to be added. //string[] importParts = importFullName.Split('.'); //List<int> upstreamLinks = new List<int>(); //0 = top level, 1 = next level... n = what we wanted to import /*ImportEntry upstreamImport = null; * string upstream = null; * while (upstreamCount < importParts.Count()) * { * upstreamfullpath = string.Join(".", importParts, 0, importParts.Count() - upstreamCount); * upstreamImport = Pcc.Imports.FirstOrDefault(x => x.GetFullPath == upstreamfullpath); * * if (upstreamImport != null) * { * break; * } * upstreamCount++; * }*/ if (upstreamEntryToAttachTo == null) { //There is nothing we can attach to. Debug.WriteLine("cannot find a top level item to attach to for " + importFullName); return(null); } //Have an upstream import, now we need to add downstream imports. ImportEntry mostdownstreamimport = null; while (upstreamCount > 0) { upstreamCount--; string fullobjectname = String.Join(".", importParts, 0, importParts.Length - upstreamCount); Dictionary <string, string> importdbinfo = ImportClassDB[fullobjectname]; int downstreamName = Pcc.FindNameOrAdd(importParts[importParts.Length - upstreamCount - 1]); Debug.WriteLine(Pcc.Names[downstreamName]); int downstreamLinkIdx = upstreamEntryToAttachTo.UIndex; Debug.WriteLine(upstreamEntryToAttachTo.GetFullPath); int downstreamPackageName = Pcc.FindNameOrAdd(importdbinfo["packagefile"]); string downstreamClassName = importdbinfo["fullclasspath"]; int lastPeriodIndex = downstreamClassName.LastIndexOf("."); if (lastPeriodIndex > 0) { downstreamClassName = importdbinfo["fullclasspath"].Substring(lastPeriodIndex + 1); } int downstreamClassNameIdx = Pcc.FindNameOrAdd(downstreamClassName); Debug.WriteLine("Finding name " + downstreamClassName); //ImportEntry classImport = getOrAddImport(); //int downstreamClass = 0; //if (classImport != null) { // downstreamClass = classImport.UIndex; //no recursion pls //} else //{ // throw new Exception("No class was found for importing"); //} mostdownstreamimport = new ImportEntry(Pcc) { idxLink = downstreamLinkIdx, idxClassName = downstreamClassNameIdx, idxObjectName = downstreamName, idxPackageFile = downstreamPackageName }; Pcc.addImport(mostdownstreamimport); upstreamEntryToAttachTo = mostdownstreamimport; } return(mostdownstreamimport); }
public static void ImportProperty(IMEPackage pcc, IMEPackage importpcc, Property p, string className, MemoryStream m, bool inStruct = false) { string name = importpcc.getNameEntry(p.Name); int idxname = pcc.FindNameOrAdd(name); m.Write(BitConverter.GetBytes(idxname), 0, 4); m.Write(new byte[4], 0, 4); if (name == "None") { return; } string type = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 8)); int idxtype = pcc.FindNameOrAdd(type); m.Write(BitConverter.GetBytes(idxtype), 0, 4); m.Write(new byte[4], 0, 4); string name2; int idxname2; int size, count, pos; List <Property> Props; switch (type) { case "IntProperty": case "FloatProperty": case "ObjectProperty": case "StringRefProperty": m.Write(BitConverter.GetBytes(4), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(p.Value.IntValue), 0, 4); break; case "NameProperty": m.Write(BitConverter.GetBytes(8), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(importpcc.getNameEntry(p.Value.IntValue))), 0, 4); //preserve index or whatever the second part of a namereference is m.Write(p.raw, 28, 4); break; case "BoolProperty": m.Write(new byte[8], 0, 8); m.WriteByte((byte)p.Value.IntValue); if (pcc.Game != MEGame.ME3) { m.Write(new byte[3], 0, 3); } break; case "BioMask4Property": m.Write(BitConverter.GetBytes(p.Size), 0, 4); m.Write(new byte[4], 0, 4); m.WriteByte((byte)p.Value.IntValue); break; case "ByteProperty": m.Write(BitConverter.GetBytes(p.Size), 0, 4); m.Write(new byte[4], 0, 4); if (pcc.Game == MEGame.ME3) { name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 24)); idxname2 = pcc.FindNameOrAdd(name2); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); } if (p.Size != 1) { m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(importpcc.getNameEntry(p.Value.IntValue))), 0, 4); m.Write(new byte[4], 0, 4); } else { m.WriteByte(Convert.ToByte(p.Value.IntValue)); } break; case "DelegateProperty": size = BitConverter.ToInt32(p.raw, 16); if (size == 0xC) { name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 28)); idxname2 = pcc.FindNameOrAdd(name2); m.Write(BitConverter.GetBytes(0xC), 0, 4); m.Write(new byte[4], 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); } else { m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); for (int i = 0; i < size; i++) { m.WriteByte(p.raw[24 + i]); } } break; case "StrProperty": name2 = p.Value.StringValue; if (p.Value.StringValue.Length > 0) { name2 += '\0'; } if (p.Value.len < 0) { //unicode m.Write(BitConverter.GetBytes(4 + name2.Length * 2), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(-name2.Length), 0, 4); foreach (char c in name2) { m.WriteByte((byte)c); m.WriteByte(0); } } else { //ascii m.Write(BitConverter.GetBytes(4 + name2.Length), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(name2.Length), 0, 4); foreach (char c in name2) { m.WriteByte((byte)c); } } break; case "StructProperty": size = BitConverter.ToInt32(p.raw, 16); name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 24)); idxname2 = pcc.FindNameOrAdd(name2); pos = 32; Props = new List <Property>(); try { Props = ReadProp(importpcc, p.raw, pos); } catch (Exception) { } m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); if (Props.Count == 0 || Props[0].TypeVal == PropertyType.Unknown) { for (int i = 0; i < size; i++) { m.WriteByte(p.raw[32 + i]); } } else { foreach (Property pp in Props) { ImportProperty(pcc, importpcc, pp, className, m, inStruct); } } break; case "ArrayProperty": size = BitConverter.ToInt32(p.raw, 16); count = BitConverter.ToInt32(p.raw, 24); PropertyInfo info = ME3UnrealObjectInfo.getPropertyInfo(className, name, inStruct); ArrayType arrayType = ME3UnrealObjectInfo.getArrayType(info); pos = 28; List <Property> AllProps = new List <Property>(); if (arrayType == ArrayType.Struct) { for (int i = 0; i < count; i++) { Props = new List <Property>(); try { Props = ReadProp(importpcc, p.raw, pos); } catch (Exception) { } AllProps.AddRange(Props); if (Props.Count != 0) { pos = Props[Props.Count - 1].offend; } } } m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(count), 0, 4); if (AllProps.Count != 0 && (info == null || !UnrealObjectInfo.isImmutable(info.reference, pcc.Game))) { foreach (Property pp in AllProps) { ImportProperty(pcc, importpcc, pp, className, m, inStruct); } } else if (arrayType == ArrayType.Name) { for (int i = 0; i < count; i++) { string s = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 28 + i * 8)); m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(s)), 0, 4); //preserve index or whatever the second part of a namereference is m.Write(p.raw, 32 + i * 8, 4); } } else { m.Write(p.raw, 28, size - 4); } break; default: throw new Exception(type); } }
public static void WriteEnumProperty(this Stream stream, IMEPackage pcc, string propName, NameReference enumName, NameReference enumValue) { stream.WritePropHeader(pcc, propName, PropertyType.ByteProperty, 8); if (pcc.Game == MEGame.ME3) { stream.WriteValueS32(pcc.FindNameOrAdd(enumName.Name)); stream.WriteValueS32(enumName.count); } stream.WriteValueS32(pcc.FindNameOrAdd(enumValue.Name)); stream.WriteValueS32(enumValue.count); }
public static void WriteDelegateProperty(this Stream stream, IMEPackage pcc, string propName, int unk, NameReference value) { stream.WritePropHeader(pcc, propName, PropertyType.DelegateProperty, 12); stream.WriteValueS32(unk); stream.WriteValueS32(pcc.FindNameOrAdd(value.Name)); stream.WriteValueS32(value.count); }
public static void WriteNameProperty(this Stream stream, IMEPackage pcc, string propName, NameReference value) { stream.WritePropHeader(pcc, propName, PropertyType.NameProperty, 8); stream.WriteValueS32(pcc.FindNameOrAdd(value.Name)); stream.WriteValueS32(value.count); }
protected void WriteName(string fullName) { (string name, int number) = StringToNameRef(fullName); WriteInt(Pcc.FindNameOrAdd(name)); WriteInt(number); }
/// <summary> /// Adds an import from the importingPCC to the destinationPCC with the specified importFullName, or returns the existing one if it can be found. /// This will add parent imports and packages as neccesary /// </summary> /// <param name="importFullName">GetFullPath() of an import from ImportingPCC</param> /// <param name="importingPCC">PCC to import imports from</param> /// <param name="destinationPCC">PCC to add imports to</param> /// <param name="forcedLink">force this as parent</param> /// <returns></returns> public static IEntry getOrAddCrossImportOrPackage(string importFullName, IMEPackage importingPCC, IMEPackage destinationPCC, int?forcedLink = null) { if (string.IsNullOrEmpty(importFullName)) { return(null); } //see if this import exists locally foreach (ImportEntry imp in destinationPCC.Imports) { if (imp.GetFullPath == importFullName) { return(imp); } } //see if this is an export Package and exists locally foreach (ExportEntry exp in destinationPCC.Exports) { if (exp.ClassName == "Package" && exp.GetFullPath == importFullName) { return(exp); } } if (forcedLink is int link) { ImportEntry importingImport = importingPCC.Imports.First(x => x.GetFullPath == importFullName); //this shouldn't be null var newImport = new ImportEntry(destinationPCC) { idxLink = link, idxClassName = destinationPCC.FindNameOrAdd(importingImport.ClassName), idxObjectName = destinationPCC.FindNameOrAdd(importingImport.ObjectName), idxPackageFile = destinationPCC.FindNameOrAdd(importingImport.PackageFile) }; destinationPCC.addImport(newImport); return(newImport); } string[] importParts = importFullName.Split('.'); //recursively ensure parent package exists. when importParts.Length == 1, this will return null IEntry parent = getOrAddCrossImportOrPackage(string.Join(".", importParts.Take(importParts.Length - 1)), importingPCC, destinationPCC); foreach (ImportEntry imp in importingPCC.Imports) { if (imp.GetFullPath == importFullName) { var import = new ImportEntry(destinationPCC) { idxLink = parent?.UIndex ?? 0, idxClassName = destinationPCC.FindNameOrAdd(imp.ClassName), idxObjectName = destinationPCC.FindNameOrAdd(imp.ObjectName), idxPackageFile = destinationPCC.FindNameOrAdd(imp.PackageFile) }; destinationPCC.addImport(import); return(import); } } foreach (ExportEntry exp in importingPCC.Exports) { if (exp.ClassName == "Package" && exp.GetFullPath == importFullName) { importExport(destinationPCC, exp, parent?.UIndex ?? 0, out ExportEntry package); return(package); } } throw new Exception($"Unable to add {importFullName} to file! Could not find it!"); }
public static void WriteNameReference(this Stream stream, NameReference name, IMEPackage pcc) { stream.WriteInt32(pcc.FindNameOrAdd(name.Name)); stream.WriteInt32(name.Number); }
public static void ImportProperty(IMEPackage pcc, IMEPackage importpcc, Property p, string className, MemoryStream m, bool inStruct = false) { string name = importpcc.getNameEntry(p.Name); int idxname = pcc.FindNameOrAdd(name); m.Write(BitConverter.GetBytes(idxname), 0, 4); m.Write(new byte[4], 0, 4); if (name == "None") return; string type = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 8)); int idxtype = pcc.FindNameOrAdd(type); m.Write(BitConverter.GetBytes(idxtype), 0, 4); m.Write(new byte[4], 0, 4); string name2; int idxname2; int size, count, pos; List<Property> Props; switch (type) { case "IntProperty": case "FloatProperty": case "ObjectProperty": case "StringRefProperty": m.Write(BitConverter.GetBytes(4), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(p.Value.IntValue), 0, 4); break; case "NameProperty": m.Write(BitConverter.GetBytes(8), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(importpcc.getNameEntry(p.Value.IntValue))), 0, 4); //preserve index or whatever the second part of a namereference is m.Write(p.raw, 28, 4); break; case "BoolProperty": m.Write(new byte[8], 0, 8); m.WriteByte((byte)p.Value.IntValue); if (pcc.Game != MEGame.ME3) { m.Write(new byte[3], 0, 3); } break; case "BioMask4Property": m.Write(BitConverter.GetBytes(p.Size), 0, 4); m.Write(new byte[4], 0, 4); m.WriteByte((byte)p.Value.IntValue); break; case "ByteProperty": m.Write(BitConverter.GetBytes(p.Size), 0, 4); m.Write(new byte[4], 0, 4); if (pcc.Game == MEGame.ME3) { name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 24)); idxname2 = pcc.FindNameOrAdd(name2); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); } if (p.Size != 1) { m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(importpcc.getNameEntry(p.Value.IntValue))), 0, 4); m.Write(new byte[4], 0, 4); } else { m.WriteByte(Convert.ToByte(p.Value.IntValue)); } break; case "DelegateProperty": size = BitConverter.ToInt32(p.raw, 16); if (size == 0xC) { name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 28)); idxname2 = pcc.FindNameOrAdd(name2); m.Write(BitConverter.GetBytes(0xC), 0, 4); m.Write(new byte[4], 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); } else { m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); for (int i = 0; i < size; i++) m.WriteByte(p.raw[24 + i]); } break; case "StrProperty": name2 = p.Value.StringValue + '\0'; if (p.Value.len < 0) { m.Write(BitConverter.GetBytes(4 + name2.Length * 2), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(-name2.Length), 0, 4); foreach (char c in name2) { m.WriteByte((byte)c); m.WriteByte(0); } } else { m.Write(BitConverter.GetBytes(4 + name2.Length), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(name2.Length), 0, 4); foreach (char c in name2) { m.WriteByte((byte)c); } } break; case "StructProperty": size = BitConverter.ToInt32(p.raw, 16); name2 = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 24)); idxname2 = pcc.FindNameOrAdd(name2); pos = 32; Props = new List<Property>(); try { Props = ReadProp(importpcc, p.raw, pos); } catch (Exception) { } m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(idxname2), 0, 4); m.Write(new byte[4], 0, 4); if (Props.Count == 0 || Props[0].TypeVal == PropertyType.Unknown) { for (int i = 0; i < size; i++) m.WriteByte(p.raw[32 + i]); } else { foreach (Property pp in Props) ImportProperty(pcc, importpcc, pp, className, m, inStruct); } break; case "ArrayProperty": size = BitConverter.ToInt32(p.raw, 16); count = BitConverter.ToInt32(p.raw, 24); PropertyInfo info = ME3UnrealObjectInfo.getPropertyInfo(className, name, inStruct); ArrayType arrayType = ME3UnrealObjectInfo.getArrayType(info); pos = 28; List<Property> AllProps = new List<Property>(); if (arrayType == ArrayType.Struct) { for (int i = 0; i < count; i++) { Props = new List<Property>(); try { Props = ReadProp(importpcc, p.raw, pos); } catch (Exception) { } AllProps.AddRange(Props); if (Props.Count != 0) { pos = Props[Props.Count - 1].offend; } } } m.Write(BitConverter.GetBytes(size), 0, 4); m.Write(new byte[4], 0, 4); m.Write(BitConverter.GetBytes(count), 0, 4); if (AllProps.Count != 0 && (info == null || !ME3UnrealObjectInfo.isImmutable(info.reference))) { foreach (Property pp in AllProps) ImportProperty(pcc, importpcc, pp, className, m, inStruct); } else if (arrayType == ArrayType.Name) { for (int i = 0; i < count; i++) { string s = importpcc.getNameEntry(BitConverter.ToInt32(p.raw, 28 + i * 8)); m.Write(BitConverter.GetBytes(pcc.FindNameOrAdd(s)), 0, 4); //preserve index or whatever the second part of a namereference is m.Write(p.raw, 32 + i * 8, 4); } } else { m.Write(p.raw, 28, size - 4); } break; default: throw new Exception(type); } }