public static ClassInfo generateClassInfo(ExportEntry export, bool isStruct = false) { IMEPackage pcc = export.FileRef; ClassInfo info = new ClassInfo { baseClass = export.SuperClassName, exportIndex = export.UIndex, ClassName = export.ObjectName }; if (!isStruct) { UClass classBinary = ObjectBinary.From <UClass>(export); info.isAbstract = classBinary.ClassFlags.HasFlag(UnrealFlags.EClassFlags.Abstract); } if (pcc.FilePath.Contains("BioGame")) { info.pccPath = new string(pcc.FilePath.Skip(pcc.FilePath.LastIndexOf("BioGame") + 8).ToArray()); } else { info.pccPath = pcc.FilePath; //used for dynamic resolution of files outside the game directory. } int nextExport = BitConverter.ToInt32(export.Data, isStruct ? 0x18 : 0x10); while (nextExport > 0) { var entry = pcc.GetUExport(nextExport); if (entry.ClassName != "ScriptStruct" && entry.ClassName != "Enum" && entry.ClassName != "Function" && entry.ClassName != "Const" && entry.ClassName != "State") { if (!info.properties.ContainsKey(entry.ObjectName)) { PropertyInfo p = getProperty(entry); if (p != null) { info.properties.Add(entry.ObjectName, p); } } } nextExport = BitConverter.ToInt32(entry.Data, 0x10); } return(info); }
protected ActorNode(int idx, float x, float y, IMEPackage p, PathingGraphEditor grapheditor, bool drawAsPolygon = false, bool drawRotationLine = false, bool drawAsCylinder = false) { pcc = p; g = grapheditor; index = idx; export = pcc.GetUExport(index); comment = new SText(GetComment(), commentColor, false) { X = 0 }; comment.Y = 52 + comment.Height; comment.Pickable = false; if (drawRotationLine) { float theta = 0; if (export.GetProperty <StructProperty>("Rotation") is StructProperty rotation) { theta = rotation.GetProp <IntProperty>("Yaw").Value.UnrealRotationUnitsToDegrees(); } float circleX1 = (float)(25 + 20 * Math.Cos((theta + 5) * Math.PI / 180)); float circleY1 = (float)(25 + 20 * Math.Sin((theta + 5) * Math.PI / 180)); float circleX2 = (float)(25 + 20 * Math.Cos((theta - 5) * Math.PI / 180)); float circleY2 = (float)(25 + 20 * Math.Sin((theta - 5) * Math.PI / 180)); float circleTipX = (float)(25 + 25 * Math.Cos(theta * Math.PI / 180)); float circleTipY = (float)(25 + 25 * Math.Sin(theta * Math.PI / 180)); PPath directionShape = PPath.CreatePolygon(new[] { new PointF(25, 25), new PointF(circleX1, circleY1), new PointF(circleTipX, circleTipY), new PointF(circleX2, circleY2) }); directionShape.Pen = Pens.BlanchedAlmond; directionShape.Brush = directionBrush; directionShape.Pickable = false; AddChild(directionShape); } this.AddChild(comment); this.Pickable = true; Bounds = new RectangleF(0, 0, 50, 50); SetShape(drawAsPolygon, drawAsCylinder); if (drawAsPolygon && export.GetProperty <StructProperty>("PrePivot") is StructProperty pivotvector) { x = x - pivotvector.Properties.GetProp <FloatProperty>("X"); y = y - pivotvector.Properties.GetProp <FloatProperty>("Y"); } TranslateBy(x, y); }
public void loadData(int _uIndex = 0) { if (_uIndex != 0) { uIndex = _uIndex; } BinaryReader r = new BinaryReader(new MemoryStream(pcc.GetUExport(uIndex).Data)); //skip properties r.BaseStream.Seek(12, SeekOrigin.Begin); if (r.BaseStream.Length > 12) { int count = r.ReadInt32(); talkFiles = new List <TalkFile>(count); for (int i = 0; i < count; i++) { int langRef = r.ReadInt32(); r.ReadInt64(); talkFiles.Add(new TalkFile(pcc, r.ReadInt32(), true, langRef, uIndex)); talkFiles.Add(new TalkFile(pcc, r.ReadInt32(), false, langRef, uIndex)); } for (int i = 0; i < talkFiles.Count; i++) { if (talkFiles[i].language == "Int" && !talkFiles[i].male) { selectedTLK = i; break; } } } else { talkFiles = new List <TalkFile>(); } }
private static PropertyInfo getProperty(ExportEntry entry) { IMEPackage pcc = entry.FileRef; string reference = null; PropertyType type; switch (entry.ClassName) { case "IntProperty": type = PropertyType.IntProperty; break; case "StringRefProperty": type = PropertyType.StringRefProperty; break; case "FloatProperty": type = PropertyType.FloatProperty; break; case "BoolProperty": type = PropertyType.BoolProperty; break; case "StrProperty": type = PropertyType.StrProperty; break; case "NameProperty": type = PropertyType.NameProperty; break; case "DelegateProperty": type = PropertyType.DelegateProperty; break; case "ClassProperty": case "ObjectProperty": case "ComponentProperty": type = PropertyType.ObjectProperty; reference = pcc.getObjectName(BitConverter.ToInt32(entry.Data, entry.Data.Length - 4)); break; case "StructProperty": type = PropertyType.StructProperty; reference = pcc.getObjectName(BitConverter.ToInt32(entry.Data, entry.Data.Length - 4)); break; case "BioMask4Property": case "ByteProperty": type = PropertyType.ByteProperty; reference = pcc.getObjectName(BitConverter.ToInt32(entry.Data, entry.Data.Length - 4)); break; case "ArrayProperty": type = PropertyType.ArrayProperty; PropertyInfo arrayTypeProp = getProperty(pcc.GetUExport(BitConverter.ToInt32(entry.Data, 44))); if (arrayTypeProp != null) { switch (arrayTypeProp.Type) { case PropertyType.ObjectProperty: case PropertyType.StructProperty: case PropertyType.ArrayProperty: reference = arrayTypeProp.Reference; break; case PropertyType.ByteProperty: //if (arrayTypeProp.reference == "") if (arrayTypeProp.Reference == "Class") { reference = arrayTypeProp.Type.ToString(); } else { reference = arrayTypeProp.Reference; } break; case PropertyType.IntProperty: case PropertyType.FloatProperty: case PropertyType.NameProperty: case PropertyType.BoolProperty: case PropertyType.StrProperty: case PropertyType.StringRefProperty: case PropertyType.DelegateProperty: reference = arrayTypeProp.Type.ToString(); break; case PropertyType.None: case PropertyType.Unknown: default: Debugger.Break(); return(null); } } else { return(null); } break; case "InterfaceProperty": default: return(null); } bool transient = ((UnrealFlags.EPropertyFlags)BitConverter.ToUInt64(entry.Data, 24)).HasFlag(UnrealFlags.EPropertyFlags.Transient); return(new PropertyInfo(type, reference, transient)); }
//call this method to regenerate ME2ObjectInfo.json //Takes a long time (10 minutes maybe?). Application will be completely unresponsive during that time. public static void generateInfo() { var NewClasses = new Dictionary <string, ClassInfo>(); var NewStructs = new Dictionary <string, ClassInfo>(); var NewEnums = new Dictionary <string, List <NameReference> >(); var NewSequenceObjects = new Dictionary <string, SequenceObjectInfo>(); foreach (string filePath in MELoadedFiles.GetOfficialFiles(MEGame.ME2)) { if (Path.GetExtension(filePath) == ".pcc") { using IMEPackage pcc = MEPackageHandler.OpenME2Package(filePath); for (int j = 1; j <= pcc.ExportCount; j++) { ExportEntry exportEntry = pcc.GetUExport(j); string className = exportEntry.ClassName; if (className == "Enum") { generateEnumValues(exportEntry, NewEnums); } else if (className == "Class") { string objectName = exportEntry.ObjectName; if (!NewClasses.ContainsKey(objectName)) { NewClasses.Add(objectName, generateClassInfo(exportEntry)); } } else if (className == "ScriptStruct") { string objectName = exportEntry.ObjectName; if (!NewStructs.ContainsKey(objectName)) { NewStructs.Add(objectName, generateClassInfo(exportEntry, isStruct: true)); } } else if (exportEntry.IsOrInheritsFrom("SequenceObject")) { if (!NewSequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo)) { seqObjInfo = new SequenceObjectInfo(); NewSequenceObjects.Add(className, seqObjInfo); } int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion"); if (objInstanceVersion > seqObjInfo.ObjInstanceVersion) { seqObjInfo.ObjInstanceVersion = objInstanceVersion; } } } //Debug.WriteLine("Releasing " + pcc.FileName); } } //CUSTOM ADDITIONS try { NewClasses.Add("LightMapTexture2D", new ClassInfo { baseClass = "Texture2D", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName }); NewClasses["StaticMesh"] = new ClassInfo { baseClass = "Object", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName, properties = { new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseFullPrecisionUVs", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("BodySetup", new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")), new KeyValuePair <string, PropertyInfo>("LODDistanceRatio", new PropertyInfo(PropertyType.FloatProperty)), new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("LightMapResolution", new PropertyInfo(PropertyType.IntProperty)), } }; } catch (Exception) { } //SFXPhysicalMaterialDecals missing items ClassInfo sfxpmd = NewClasses["SFXPhysicalMaterialDecals"]; string[] decalComponentArrays = { "HeavyPistol", "AutoPistol", "HandCannon", "SMG", "Shotgun", "HeavyShotgun", "FlakGun", "AssaultRifle", "Needler", "Machinegun", "SniperRifle", "AntiMatRifle", "MassCannon", "ParticleBeam" }; foreach (string decal in decalComponentArrays) { sfxpmd.properties.Add(decal, new PropertyInfo(PropertyType.ArrayProperty, "DecalComponent")); } NewClasses["SFXWeapon"].properties.Add("InstantHitDamageTypes", new PropertyInfo(PropertyType.ArrayProperty, "Class")); File.WriteAllText(jsonPath, JsonConvert.SerializeObject(new { SequenceObjects = NewSequenceObjects, Classes = NewClasses, Structs = NewStructs, Enums = NewEnums }, Formatting.Indented)); }
public static PropertyCollection getDefaultStructValue(string className, bool stripTransients) { bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME2); if (Structs.ContainsKey(className)) { ClassInfo info = Structs[className]; try { PropertyCollection structProps = new PropertyCollection(); ClassInfo tempInfo = info; while (tempInfo != null) { foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties) { if (stripTransients && propInfo.Transient) { continue; } if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is UProperty uProp) { structProps.Add(uProp); } } if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo)) { tempInfo = null; } } structProps.Add(new NoneProperty()); string filepath = Path.Combine(ME2Directory.gamePath, "BioGame", info.pccPath); if (File.Exists(info.pccPath)) { filepath = info.pccPath; //Used for dynamic lookup } else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { filepath = App.CustomResourceFilePath(MEGame.ME2); } if (File.Exists(filepath)) { using (IMEPackage importPCC = MEPackageHandler.OpenME2Package(filepath)) { var exportToRead = importPCC.GetUExport(info.exportIndex); byte[] buff = exportToRead.Data.Skip(0x30).ToArray(); PropertyCollection defaults = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className); foreach (var prop in defaults) { structProps.TryReplaceProp(prop); } } } return(structProps); } catch { return(null); } } return(null); }
/// <summary> /// Ports a power into a package /// </summary> /// <param name="targetPackage"></param> /// <param name="powerInfo"></param> /// <param name="additionalPowers">A list of additioanl powers that are referenced when this powerinfo is an import only power (prevent re-opening package)</param> /// <returns></returns> public static IEntry PortPowerIntoPackage(IMEPackage targetPackage, PowerInfo powerInfo, out IMEPackage sourcePackage) { if (powerInfo.IsCorrectedPackage) { var sourceData = MERUtilities.GetEmbeddedStaticFilesBinaryFile("correctedloadouts.powers." + powerInfo.PackageFileName); sourcePackage = MEPackageHandler.OpenMEPackageFromStream(new MemoryStream(sourceData)); } else { sourcePackage = NonSharedPackageCache.Cache.GetCachedPackage(powerInfo.PackageFileName); } if (sourcePackage != null) { var sourceExport = sourcePackage.GetUExport(powerInfo.SourceUIndex); if (!sourceExport.InheritsFrom("SFXPower") || sourceExport.IsDefaultObject) { throw new Exception("Wrong setup!"); } if (sourceExport.Parent != null && sourceExport.Parent.ClassName != "Package") { throw new Exception("Cannot port power - parent object is not Package!"); } var newParent = EntryExporter.PortParents(sourceExport, targetPackage); IEntry newEntry; if (powerInfo.ImportOnly) { //Debug.WriteLine($"ImportOnly in file {targetPackage.FilePath}"); if (powerInfo.RequiresStartupPackage) { ThreadSafeDLCStartupPackage.AddStartupPackage(Path.GetFileNameWithoutExtension(powerInfo.PackageFileName)); if (powerInfo.IsCorrectedPackage) { // File must be added to MERFS DLC var outP = Path.Combine(MERFileSystem.DLCModCookedPath, powerInfo.PackageFileName); if (!File.Exists(outP)) { sourcePackage.Save(outP, true); } } } newEntry = PackageTools.CreateImportForClass(sourceExport, targetPackage, newParent); // Port in extra imports so the calling class can reference them as necessary. foreach (var addlPow in powerInfo.AdditionalRequiredPowers) { var addlSourceExp = sourcePackage.FindExport(addlPow); PackageTools.CreateImportForClass(addlSourceExp, targetPackage, EntryExporter.PortParents(addlSourceExp, targetPackage)); } } else { #if DEBUG // DEBUG ONLY----------------------------------- //var defaults = sourceExport.GetDefaults(); //defaults.RemoveProperty("VFX"); //var vfx = defaults.GetProperty<ObjectProperty>("VFX").ResolveToEntry(sourcePackage) as ExportEntry; //vxx.RemoveProperty("PlayerCrust"); //vfx.FileRef.GetUExport(1544).RemoveProperty("oPrefab"); ////vfx = defaults.FileRef.GetUExport(6211); // Prefab ////vfx.RemoveProperty("WorldImpactVisualEffect"); //MERPackageCache cached = new MERPackageCache(); //EntryExporter.ExportExportToPackage(vfx, targetPackage, out newEntry, cached); //PackageTools.AddReferencesToWorld(targetPackage, new [] {newEntry as ExportEntry}); //return null; // END DEBUG ONLY-------------------------------- #endif List <EntryStringPair> relinkResults = null; if ((powerInfo.IsCorrectedPackage || (PackageTools.IsPersistentPackage(powerInfo.PackageFileName) && MERFileSystem.GetPackageFile(powerInfo.PackageFileName.ToLocalizedFilename()) == null))) { // Faster this way, without having to check imports Dictionary <IEntry, IEntry> crossPCCObjectMap = new Dictionary <IEntry, IEntry>(); // Not sure what this is used for these days. SHould probably just be part of the method relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceExport, targetPackage, newParent, true, out newEntry, crossPCCObjectMap); } else { // MEMORY SAFE (resolve imports to exports) MERPackageCache cache = new MERPackageCache(); relinkResults = EntryExporter.ExportExportToPackage(sourceExport, targetPackage, out newEntry, cache); } if (relinkResults.Any()) { Debugger.Break(); } } return(newEntry); } return(null); // No package was found }
public static PropertyCollection GetSequenceObjectDefaults(IMEPackage pcc, ClassInfo info) { MEGame game = pcc.Game; PropertyCollection defaults = new PropertyCollection(); if (info.ClassName == "Sequence") { defaults.Add(new ArrayProperty <ObjectProperty>("SequenceObjects")); } else if (!info.IsA(SequenceVariableName, game)) { ArrayProperty <StructProperty> varLinksProp = null; ArrayProperty <StructProperty> outLinksProp = null; ArrayProperty <StructProperty> eventLinksProp = null; ArrayProperty <StructProperty> inLinksProp = null; Dictionary <string, ClassInfo> classes = UnrealObjectInfo.GetClasses(game); try { ClassInfo classInfo = info; while (classInfo != null && (varLinksProp is null || outLinksProp is null || eventLinksProp is null || game == MEGame.ME1 && inLinksProp is null)) { string filepath = Path.Combine(MEDirectories.GetBioGamePath(game), classInfo.pccPath); Stream loadStream = null; if (File.Exists(classInfo.pccPath)) { loadStream = new MemoryStream(File.ReadAllBytes(classInfo.pccPath)); } else if (classInfo.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { loadStream = Utilities.GetCustomAppResourceStream(game); } else if (File.Exists(filepath)) { loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } else if (game == MEGame.ME1) { filepath = Path.Combine(ME1Directory.DefaultGamePath, classInfo.pccPath); //for files from ME1 DLC if (File.Exists(filepath)) { loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } } if (loadStream != null) { using IMEPackage importPCC = MEPackageHandler.OpenMEPackageFromStream(loadStream); ExportEntry classExport = importPCC.GetUExport(classInfo.exportIndex); UClass classBin = ObjectBinary.From <UClass>(classExport); ExportEntry classDefaults = importPCC.GetUExport(classBin.Defaults); foreach (var prop in classDefaults.GetProperties()) { if (varLinksProp == null && prop.Name == "VariableLinks" && prop is ArrayProperty <StructProperty> vlp) { varLinksProp = vlp; //relink ExpectedType foreach (StructProperty varLink in varLinksProp) { if (varLink.GetProp <ObjectProperty>("ExpectedType") is ObjectProperty expectedTypeProp && importPCC.TryGetEntry(expectedTypeProp.Value, out IEntry expectedVar) && EntryImporterExtended.EnsureClassIsInFile(pcc, expectedVar.ObjectName) is IEntry portedExpectedVar) { expectedTypeProp.Value = portedExpectedVar.UIndex; } } } if (outLinksProp == null && prop.Name == "OutputLinks" && prop is ArrayProperty <StructProperty> olp) { outLinksProp = olp; } if (eventLinksProp == null && prop.Name == "EventLinks" && prop is ArrayProperty <StructProperty> elp) { eventLinksProp = elp; //relink ExpectedType foreach (StructProperty eventLink in eventLinksProp) { if (eventLink.GetProp <ObjectProperty>("ExpectedType") is ObjectProperty expectedTypeProp && importPCC.TryGetEntry(expectedTypeProp.Value, out IEntry expectedVar) && EntryImporterExtended.EnsureClassIsInFile(pcc, expectedVar.ObjectName) is IEntry portedExpectedVar) { expectedTypeProp.Value = portedExpectedVar.UIndex; } } } if (game == MEGame.ME1 && inLinksProp is null && prop.Name == "InputLinks" && prop is ArrayProperty <StructProperty> ilp) { inLinksProp = ilp; } } } classes.TryGetValue(classInfo.baseClass, out classInfo); } } catch { // ignored } if (varLinksProp != null) { defaults.Add(varLinksProp); } if (outLinksProp != null) { defaults.Add(outLinksProp); } if (eventLinksProp != null) { defaults.Add(eventLinksProp); } if (inLinksProp != null) { defaults.Add(inLinksProp); } //remove links if empty if (defaults.GetProp <ArrayProperty <StructProperty> >("OutputLinks") is { } outLinks&& outLinks.IsEmpty()) { defaults.Remove(outLinks); } if (defaults.GetProp <ArrayProperty <StructProperty> >("VariableLinks") is { } varLinks&& varLinks.IsEmpty()) { defaults.Remove(varLinks); } if (defaults.GetProp <ArrayProperty <StructProperty> >("EventLinks") is { } eventLinks&& eventLinks.IsEmpty()) { defaults.Remove(eventLinks); } if (defaults.GetProp <ArrayProperty <StructProperty> >("InputLinks") is { } inputLinks&& inputLinks.IsEmpty()) { defaults.Remove(inputLinks); } } int objInstanceVersion = UnrealObjectInfo.getSequenceObjectInfo(game, info.ClassName)?.ObjInstanceVersion ?? 1; defaults.Add(new IntProperty(objInstanceVersion, "ObjInstanceVersion")); return(defaults); }
//call this method to regenerate ME1ObjectInfo.json //Takes a long time (10 to 20 minutes maybe?). Application will be completely unresponsive during that time. public static void generateInfo() { Classes = new Dictionary <string, ClassInfo>(); Structs = new Dictionary <string, ClassInfo>(); Enums = new Dictionary <string, List <NameReference> >(); SequenceObjects = new Dictionary <string, SequenceObjectInfo>(); foreach (string file in MELoadedFiles.GetOfficialFiles(MEGame.ME1)) { if (Path.GetExtension(file) == ".upk" || Path.GetExtension(file) == ".sfm" || Path.GetExtension(file) == ".u") { Debug.WriteLine($"File: {file}"); using IMEPackage pcc = MEPackageHandler.OpenME1Package(file); for (int j = 1; j <= pcc.ExportCount; j++) { ExportEntry exportEntry = pcc.GetUExport(j); string className = exportEntry.ClassName; if (className == "Enum") { generateEnumValues(exportEntry); } else if (className == "Class") { string objectName = exportEntry.ObjectName; Debug.WriteLine($"Generating information for {objectName}"); if (!Classes.ContainsKey(objectName)) { Classes.Add(objectName, generateClassInfo(exportEntry)); } } else if (className == "ScriptStruct") { string objectName = exportEntry.ObjectName; if (!Structs.ContainsKey(exportEntry.ObjectName)) { Structs.Add(objectName, generateClassInfo(exportEntry, isStruct: true)); } } else if (exportEntry.IsOrInheritsFrom("SequenceObject")) { if (!SequenceObjects.TryGetValue(className, out SequenceObjectInfo seqObjInfo)) { seqObjInfo = new SequenceObjectInfo(); SequenceObjects.Add(className, seqObjInfo); } int objInstanceVersion = exportEntry.GetProperty <IntProperty>("ObjInstanceVersion"); if (objInstanceVersion > seqObjInfo.ObjInstanceVersion) { seqObjInfo.ObjInstanceVersion = objInstanceVersion; } } } } } //CUSTOM ADDITIONS Classes["LightMapTexture2D"] = new ClassInfo { baseClass = "Texture2D", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName }; Classes["StaticMesh"] = new ClassInfo { baseClass = "Object", exportIndex = 0, pccPath = UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName, properties = { new KeyValuePair <string, PropertyInfo>("UseSimpleRigidBodyCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleLineCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("UseSimpleBoxCollision", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("ForceDoubleSidedShadowVolumes", new PropertyInfo(PropertyType.BoolProperty)), new KeyValuePair <string, PropertyInfo>("BodySetup", new PropertyInfo(PropertyType.ObjectProperty, "RB_BodySetup")), new KeyValuePair <string, PropertyInfo>("LODDistanceRatio", new PropertyInfo(PropertyType.FloatProperty)), new KeyValuePair <string, PropertyInfo>("LightMapCoordinateIndex", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("LightMapResolution", new PropertyInfo(PropertyType.IntProperty)), new KeyValuePair <string, PropertyInfo>("SoundCue", new PropertyInfo(PropertyType.ObjectProperty, "SoundCue")), } }; File.WriteAllText(jsonPath, JsonConvert.SerializeObject(new { SequenceObjects, Classes, Structs, Enums }, Formatting.Indented)); }
public static string GenerateUDKFileForLevel(string udkPath, IMEPackage pcc) { #region AssetPackage string meshPackageName = $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}Meshes"; string meshFile = Path.Combine(udkPath, @"UDKGame\Content\Shared\", $"{meshPackageName}.upk"); MEPackageHandler.CreateAndSavePackage(meshFile, MEGame.UDK); using IMEPackage meshPackage = MEPackageHandler.OpenUDKPackage(meshFile); meshPackage.getEntryOrAddImport("Core.Package"); IEntry defMat = meshPackage.getEntryOrAddImport("EngineMaterials.DefaultMaterial", "Material", "Engine"); var allMats = new HashSet <int>(); var relinkMap = new Dictionary <IEntry, IEntry>(); #region StaticMeshes List <ExportEntry> staticMeshes = pcc.Exports.Where(exp => exp.ClassName == "StaticMesh").ToList(); foreach (ExportEntry mesh in staticMeshes) { var mats = new Queue <int>(); StaticMesh stm = ObjectBinary.From <StaticMesh>(mesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { mats.Enqueue(meshElement.Material); allMats.Add(meshElement.Material); meshElement.Material = 0; } } if (pcc.GetEntry(stm.BodySetup) is ExportEntry rbBodySetup) { rbBodySetup.RemoveProperty("PhysMaterial"); } mesh.WriteBinary(stm); IEntry newParent = EntryImporter.GetOrAddCrossImportOrPackage(mesh.ParentFullPath, pcc, meshPackage); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, mesh, meshPackage, newParent, false, out IEntry ent, relinkMap); ExportEntry portedMesh = (ExportEntry)ent; stm = ObjectBinary.From <StaticMesh>(portedMesh); foreach (StaticMeshRenderData lodModel in stm.LODModels) { foreach (StaticMeshElement meshElement in lodModel.Elements) { meshElement.Material = mats.Dequeue(); } } portedMesh.WriteBinary(stm); } #endregion #region Materials using (IMEPackage udkResources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetCustomAppResourceStream(MEGame.UDK))) { ExportEntry normDiffMat = udkResources.Exports.First(exp => exp.ObjectName == "NormDiffMat"); foreach (int matUIndex in allMats) { if (pcc.GetEntry(matUIndex) is ExportEntry matExp) { List <IEntry> textures = new MaterialInstanceConstant(matExp).Textures; ExportEntry diff = null; ExportEntry norm = null; foreach (IEntry texEntry in textures) { if (texEntry is ExportEntry texport) { if (texport.ObjectName.Name.ToLower().Contains("diff")) { diff = texport; } else if (texport.ObjectName.Name.ToLower().Contains("norm")) { norm = texport; } } } if (diff == null) { relinkMap[matExp] = defMat; continue; } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, diff, meshPackage, null, false, out IEntry ent); diff = (ExportEntry)ent; diff.RemoveProperty("TextureFileCacheName"); diff.RemoveProperty("TFCFileGuid"); diff.RemoveProperty("LODGroup"); } if (norm != null) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, norm, meshPackage, null, false, out IEntry ent); norm = (ExportEntry)ent; norm.RemoveProperty("TextureFileCacheName"); norm.RemoveProperty("TFCFileGuid"); norm.RemoveProperty("LODGroup"); } EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, normDiffMat, meshPackage, null, true, out IEntry matEnt); ExportEntry newMat = (ExportEntry)matEnt; newMat.ObjectName = matExp.ObjectName; Material matBin = ObjectBinary.From <Material>(newMat); matBin.SM3MaterialResource.UniformExpressionTextures = new UIndex[] { norm?.UIndex ?? 0, diff.UIndex }; newMat.WriteBinary(matBin); relinkMap[matExp] = newMat; if (newMat.GetProperty <ArrayProperty <ObjectProperty> >("Expressions") is {} expressionsProp&& expressionsProp.Count >= 2) { ExportEntry diffExpression = meshPackage.GetUExport(expressionsProp[0].Value); ExportEntry normExpression = meshPackage.GetUExport(expressionsProp[1].Value); diffExpression.WriteProperty(new ObjectProperty(diff.UIndex, "Texture")); normExpression.WriteProperty(new ObjectProperty(norm?.UIndex ?? 0, "Texture")); } } else if (pcc.GetEntry(matUIndex) is ImportEntry matImp) { relinkMap[matImp] = defMat; } } var relinkMapping = new OrderedMultiValueDictionary <IEntry, IEntry>(relinkMap); foreach (ExportEntry stmExport in staticMeshes) { if (relinkMap.TryGetValue(stmExport, out IEntry destEnt) && destEnt is ExportEntry destExp) { Relinker.Relink(stmExport, destExp, relinkMapping); } } } #endregion meshPackage.Save(); #endregion var staticMeshActors = new List <ExportEntry>(); var lightActors = new List <ExportEntry>(); string tempPackagePath = Path.Combine(App.ExecFolder, $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); File.Copy(Path.Combine(App.ExecFolder, "empty.udk"), tempPackagePath, true); using IMEPackage udkPackage = MEPackageHandler.OpenUDKPackage(tempPackagePath); { var topLevelMeshPackages = new List <IEntry>(); foreach (ExportEntry exportEntry in staticMeshes) { IEntry imp = udkPackage.getEntryOrAddImport($"{exportEntry.FullPath}", "StaticMesh", "Engine", exportEntry.ObjectName.Number); while (imp.Parent != null) { imp = imp.Parent; } if (!topLevelMeshPackages.Contains(imp)) { topLevelMeshPackages.Add(imp); } } ExportEntry levelExport = udkPackage.Exports.First(exp => exp.ClassName == "Level"); List <int> actorsInLevel = ObjectBinary.From <Level>(pcc.Exports.First(exp => exp.ClassName == "Level")).Actors.Select(u => u.value).ToList(); var componentToMatrixMap = new Dictionary <int, Matrix>(); foreach (int uIndex in actorsInLevel) { if (pcc.GetEntry(uIndex) is ExportEntry stcExp) { if (stcExp.ClassName == "StaticMeshCollectionActor") { StaticMeshCollectionActor stmc = ObjectBinary.From <StaticMeshCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("StaticMeshComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stmc.LocalToWorldTransforms[i]; } } else if (stcExp.ClassName == "StaticLightCollectionActor") { StaticLightCollectionActor stlc = ObjectBinary.From <StaticLightCollectionActor>(stcExp); var components = stcExp.GetProperty <ArrayProperty <ObjectProperty> >("LightComponents"); for (int i = 0; i < components.Count; i++) { componentToMatrixMap[components[i].Value] = stlc.LocalToWorldTransforms[i]; } } } } #region StaticMeshActors { var emptySMCBin = new StaticMeshComponent(); IEntry staticMeshActorClass = udkPackage.getEntryOrAddImport("Engine.StaticMeshActor"); udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor", "StaticMeshActor", "Engine"); IEntry staticMeshComponentArchetype = udkPackage.getEntryOrAddImport("Engine.Default__StaticMeshActor.StaticMeshComponent0", "StaticMeshComponent", "Engine"); int smaIndex = 2; int smcIndex = 2; foreach (ExportEntry smc in pcc.Exports.Where(exp => exp.ClassName == "StaticMeshComponent")) { if (smc.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex) && parent.IsA("StaticMeshActorBase")) { StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp = null; smc.CondenseArchetypes(); if (!(smc.GetProperty <ObjectProperty>("StaticMesh") is { } meshProp) || !pcc.IsUExport(meshProp.Value)) { continue; } smc.WriteBinary(emptySMCBin); smc.RemoveProperty("bBioIsReceivingDecals"); smc.RemoveProperty("bBioForcePrecomputedShadows"); //smc.RemoveProperty("bUsePreComputedShadows"); smc.RemoveProperty("bAcceptsLights"); smc.RemoveProperty("IrrelevantLights"); smc.RemoveProperty("Materials"); //should make use of this? smc.ObjectName = new NameReference("StaticMeshComponent", smcIndex++); if (parent.ClassName == "StaticMeshCollectionActor") { if (!componentToMatrixMap.TryGetValue(smc.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); //smc.WriteProperty(CommonStructs.Matrix(m, "CachedParentToWorld")); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sma = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, staticMeshActorClass.UIndex)) { ObjectName = new NameReference("StaticMeshActor", smaIndex++), Class = staticMeshActorClass, Parent = levelExport }; sma.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sma); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, smc, udkPackage, sma, true, out IEntry result); var props = new PropertyCollection { new ObjectProperty(result.UIndex, "StaticMeshComponent"), new NameProperty(new NameReference(Path.GetFileNameWithoutExtension(smc.FileRef.FilePath), smc.UIndex), "Tag"), new ObjectProperty(result.UIndex, "CollisionComponent") }; if (locationProp != null) { props.Add(locationProp); } if (rotationProp != null) { props.Add(rotationProp); } if (scaleProp != null) { props.Add(scaleProp); } sma.WriteProperties(props); staticMeshActors.Add(sma); } } IEntry topMeshPackageImport = udkPackage.getEntryOrAddImport(meshPackageName, "Package"); foreach (IEntry mp in topLevelMeshPackages) { mp.Parent = topMeshPackageImport; } } #endregion #region LightActors { IEntry pointLightClass = udkPackage.getEntryOrAddImport("Engine.PointLight"); IEntry spotLightClass = udkPackage.getEntryOrAddImport("Engine.SpotLight"); IEntry directionalLightClass = udkPackage.getEntryOrAddImport("Engine.DirectionalLight"); int plaIndex = 1; int plcIndex = 1; int slaIndex = 1; int slcIndex = 1; int dlaIndex = 1; int dlcIndex = 1; foreach (ExportEntry lightComponent in pcc.Exports) { if (!(lightComponent.Parent is ExportEntry parent && actorsInLevel.Contains(parent.UIndex))) { continue; } StructProperty locationProp; StructProperty rotationProp; StructProperty scaleProp; switch (lightComponent.ClassName) { case "PointLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("PointLightComponent", plcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry pla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, pointLightClass.UIndex)) { ObjectName = new NameReference("PointLight", plaIndex++), Class = pointLightClass, Parent = levelExport }; pla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(pla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, pla, true, out IEntry portedPLC); var plsProps = new PropertyCollection { new ObjectProperty(portedPLC.UIndex, "LightComponent"), new NameProperty("PointLight", "Tag"), }; if (locationProp != null) { plsProps.Add(locationProp); } if (rotationProp != null) { plsProps.Add(rotationProp); } if (scaleProp != null) { plsProps.Add(scaleProp); } pla.WriteProperties(plsProps); lightActors.Add(pla); break; case "SpotLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("SpotLightComponent", slcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry sla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, spotLightClass.UIndex)) { ObjectName = new NameReference("SpotLight", slaIndex++), Class = spotLightClass, Parent = levelExport }; sla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(sla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, sla, true, out IEntry portedSLC); var slaProps = new PropertyCollection { new ObjectProperty(portedSLC.UIndex, "LightComponent"), new NameProperty("SpotLight", "Tag"), }; if (locationProp != null) { slaProps.Add(locationProp); } if (rotationProp != null) { slaProps.Add(rotationProp); } if (scaleProp != null) { slaProps.Add(scaleProp); } sla.WriteProperties(slaProps); lightActors.Add(sla); break; case "DirectionalLightComponent": lightComponent.CondenseArchetypes(); lightComponent.ObjectName = new NameReference("DirectionalLightComponent", dlcIndex++); if (parent.ClassName == "StaticLightCollectionActor") { if (!componentToMatrixMap.TryGetValue(lightComponent.UIndex, out Matrix m)) { continue; } (Vector3 posVec, Vector3 scaleVec, Rotator rotator) = m.UnrealDecompose(); locationProp = CommonStructs.Vector3Prop(posVec, "Location"); rotationProp = CommonStructs.RotatorProp(rotator, "Rotation"); scaleProp = CommonStructs.Vector3Prop(scaleVec, "DrawScale3D"); } else { locationProp = parent.GetProperty <StructProperty>("Location"); rotationProp = parent.GetProperty <StructProperty>("Rotation"); scaleProp = parent.GetProperty <StructProperty>("DrawScale3D"); if (parent.GetProperty <FloatProperty>("DrawScale")?.Value is float scale) { Vector3 scaleVec = Vector3.One; if (scaleProp != null) { scaleVec = CommonStructs.GetVector3(scaleProp); } scaleProp = CommonStructs.Vector3Prop(scaleVec * scale, "DrawScale3D"); } } ExportEntry dla = new ExportEntry(udkPackage, EntryImporter.CreateStack(MEGame.UDK, directionalLightClass.UIndex)) { ObjectName = new NameReference("DirectionalLight", dlaIndex++), Class = directionalLightClass, Parent = levelExport }; dla.ObjectFlags |= UnrealFlags.EObjectFlags.HasStack; udkPackage.AddExport(dla); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.AddSingularAsChild, lightComponent, udkPackage, dla, true, out IEntry portedDLC); var dlaProps = new PropertyCollection { new ObjectProperty(portedDLC.UIndex, "LightComponent"), new NameProperty("DirectionalLight", "Tag"), }; if (locationProp != null) { dlaProps.Add(locationProp); } if (rotationProp != null) { dlaProps.Add(rotationProp); } if (scaleProp != null) { dlaProps.Add(scaleProp); } dla.WriteProperties(dlaProps); lightActors.Add(dla); break; } } } UDKifyLights(udkPackage); #endregion Level level = ObjectBinary.From <Level>(levelExport); level.Actors = levelExport.GetChildren().Where(ent => ent.IsA("Actor")).Select(ent => new UIndex(ent.UIndex)).ToList(); levelExport.WriteBinary(level); udkPackage.Save(); } string resultFilePath = Path.Combine(udkPath, @"UDKGame\Content\Maps\", $"{Path.GetFileNameWithoutExtension(pcc.FilePath)}.udk"); using (IMEPackage udkPackage2 = MEPackageHandler.OpenUDKPackage(Path.Combine(App.ExecFolder, "empty.udk"))) { ExportEntry levelExport = udkPackage2.Exports.First(exp => exp.ClassName == "Level"); Level levelBin = ObjectBinary.From <Level>(levelExport); foreach (ExportEntry actor in staticMeshActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } foreach (ExportEntry actor in lightActors) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild, actor, udkPackage2, levelExport, true, out IEntry result); levelBin.Actors.Add(result.UIndex); } levelExport.WriteBinary(levelBin); udkPackage2.Save(resultFilePath); } File.Delete(tempPackagePath); return(resultFilePath); }
public void CreateLiveEditFile() { string filePath = LiveFaceFxEditorFilePath; File.Copy(Path.Combine(App.ExecFolder, "ME3EmptyLevel.pcc"), filePath); LiveFile = MEPackageHandler.OpenMEPackage(filePath); for (int i = 0; i < LiveFile.Names.Count; i++) { if (LiveFile.Names[i].Equals("ME3EmptyLevel")) { LiveFile.replaceName(i, Path.GetFileNameWithoutExtension(filePath)); } } var packguid = Guid.NewGuid(); var package = LiveFile.GetUExport(1); package.PackageGUID = packguid; LiveFile.PackageGuid = packguid; EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, SourceAnimSet.Export.Parent, LiveFile, null, true, out _); var gender = SourceAnimSet.Export.ObjectNameString.Last() switch { 'F' => FaceFXGender.Female, 'M' => FaceFXGender.Male, _ => FaceFXGender.NonSpkr }; ActorTag = null; try { if (gender is not FaceFXGender.NonSpkr) { bool isFemale = gender is FaceFXGender.Female; var bioConv = LiveFile.Exports.First(exp => exp.Class.ObjectName == "BioConversation"); var LiveFileAnimSet = LiveFile.FindExport(SourceAnimSet.Export.InstancedFullPath); var propName = isFemale ? "m_aMaleFaceSets" : "m_aFemaleFaceSets"; int idx = bioConv.GetProperty <ArrayProperty <ObjectProperty> >(propName).FindIndex(objProp => objProp.Value == LiveFileAnimSet.UIndex); if (idx is 0) { //player ActorTag = $"Player_{(isFemale ? 'F' : 'M')}"; IEntry ent; using (IMEPackage soldierFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, "SFXCharacterClass_Soldier.pcc"))) { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, soldierFile.GetUExport(isFemale ? 10327 : 10330), LiveFile, LiveFile.GetUExport(3), true, out ent); } ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); actor.WriteProperty(new NameProperty(ActorTag, "Tag")); using (IMEPackage clothingFile = MEPackageHandler.OpenME3Package(Path.Combine(ME3Directory.CookedPCPath, $"BIOG_HM{(isFemale ? "F" : "M")}_ARM_CTH_R.pcc"))) { var clothingPackage = EntryImporter.GetOrAddCrossImportOrPackage("CTHa", clothingFile, LiveFile); EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, clothingFile.GetUExport(isFemale ? 1625 : 1966), LiveFile, clothingPackage, true, out ent); } ExportEntry bodyComponent = LiveFile.GetUExport(actor.GetProperty <ObjectProperty>("BodyMesh").Value); bodyComponent.WriteProperty(new ObjectProperty(ent.UIndex, "SkeletalMesh")); } else if (idx is 1) { //owner using IMEPackage parentFile = getParentFile(); foreach (ExportEntry export in parentFile.Exports.Where(exp => exp.ClassName is "SFXSeqAct_StartConversation" or "SFXSeqAct_StartAmbientConv")) { if (export.GetProperty <ObjectProperty>("Conv") is ObjectProperty convProp && parentFile.TryGetImport(convProp.Value, out var convImport) && convImport.ObjectName == bioConv.ObjectName) { ExportEntry seqVar = parentFile.GetUExport(export.GetProperty <ArrayProperty <StructProperty> >("VariableLinks")[0].GetProp <ArrayProperty <ObjectProperty> >("LinkedVariables")[0].Value); if (seqVar.ClassName == "BioSeqVar_ObjectFindByTag") { ActorTag = seqVar.GetProperty <NameProperty>("m_sObjectTagToFind").Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { importTaggedActor(parentFile); } } else { EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, parentFile.GetUExport(seqVar.GetProperty <ObjectProperty>("ObjValue").Value), LiveFile, LiveFile.GetUExport(3), true, out var ent); ExportEntry actor = (ExportEntry)ent; LiveFile.AddToLevelActorsIfNotThere(actor); ActorTag = actor.GetProperty <NameProperty>("Tag")?.Value.Name; if (ActorTag is null) { ActorTag = "ConvoOwner"; actor.WriteProperty(new NameProperty(ActorTag, "Tag")); } } break; } } } else { ActorTag = bioConv.GetProperty <ArrayProperty <NameProperty> >("m_aSpeakerList")[idx - 2].Value; if (!ActorTag.StartsWith("hench_", StringComparison.OrdinalIgnoreCase)) { using IMEPackage parentFile = getParentFile(); importTaggedActor(parentFile); } } } else { //find nonspkr linkage somehow } }
public static bool RandomizePackageActorsInConversation(IMEPackage package, RandomizationOption option) { var conversationStartExports = package.Exports.Where(CanRandomizeSeqActStartConvo).ToList(); if (!conversationStartExports.Any()) { return(false); } MERPackageCache localCache = new MERPackageCache(); foreach (var convStart in conversationStartExports) { //if (convStart.UIndex < 13638) // continue; if (!CanRandomizeConversationStart(convStart)) { continue; } var bioConvImportProp = convStart.GetProperty <ObjectProperty>("Conv"); if (bioConvImportProp == null) { continue; // Some conversation starts are just filler stubs and are missing data like Nor_340a/bZaeed } var bioConvImport = bioConvImportProp.ResolveToEntry(package) as ImportEntry; List <string> inputTags = new(); // Shuffle the inputs to the conversation. We will store these and then have to update the Owner and Player tags // I think...... var seqLinks = SeqTools.GetVariableLinksOfNode(convStart); // Dictionary of linked nodes, and var links that point to them. List <ExportEntry> shufflableNodes = new List <ExportEntry>(); List <SeqTools.VarLinkInfo> applicableLinks = new(); ExportEntry ownerEntry = null; ExportEntry playerEntry = null; var sequenceObjects = SeqTools.GetAllSequenceElements(convStart).OfType <ExportEntry>().ToList(); foreach (var varilink in seqLinks) { if (CanLinkBeRandomized(varilink, out _)) { var connectedItem = varilink.LinkedNodes[0] as ExportEntry; if (!shufflableNodes.Contains(connectedItem)) { // Check to make sure node has a value if (connectedItem.ClassName == "SeqVar_Object") { var objValue = connectedItem.GetProperty <ObjectProperty>("ObjValue"); if (objValue == null && !MERSeqTools.IsAssignedBioPawn(convStart, connectedItem, sequenceObjects)) { continue; // This is not a shufflable node, it is never assigned anything } } shufflableNodes.Add(connectedItem); } if (varilink.LinkedNodes[0].ClassName == "SeqVar_Player") { playerEntry = varilink.LinkedNodes[0] as ExportEntry; } else if (varilink.LinkDesc == "Owner") { ownerEntry = varilink.LinkedNodes[0] as ExportEntry; } applicableLinks.Add(varilink); } } if (shufflableNodes.Count <= 1) { continue; // We cannot edit this one } if (shufflableNodes.Count == 2 && playerEntry != null && ownerEntry != null) { continue; // We can't swap owner and player due to hardcoded owner and player tags } // Build shuffle map bool retry = true; Dictionary <int, int> shuffleMap = null; while (retry) { shuffleMap = new Dictionary <int, int>(); var reAssigned = shufflableNodes.ToList(); reAssigned.Shuffle(); for (int i = 0; i < shufflableNodes.Count; i++) { var sourceEntry = shufflableNodes[i]; var remapEntry = reAssigned.PullFirstItem(); int retryCount = reAssigned.Count; while (retryCount > 0 && (sourceEntry == remapEntry || (ownerEntry == sourceEntry && remapEntry == ownerEntry))) { reAssigned.Add(remapEntry); sourceEntry = reAssigned.PullFirstItem(); retryCount--; } if (sourceEntry == ownerEntry && remapEntry == playerEntry) { // Cannot set player to owner break; // We must force a retry } shuffleMap[sourceEntry.UIndex] = remapEntry.UIndex; } if (shuffleMap.Count != shufflableNodes.Count) { continue; // Try again } // We did it retry = false; } // Apply the shuffle map Dictionary <NameReference, ActorLookup> findActorMap = new(); foreach (var varilink in applicableLinks) { var repointedItem = shuffleMap[varilink.LinkedNodes[0].UIndex]; //if (varilink.LinkedNodes[0] == playerEntry || repointedItem == playerEntry.UIndex) //{ // Build FindActor mapping for player // it should be: // Player -> [Some actor, like Pup1_1] // Pup1_1 -> Player // When parsing the interpdatas, change the findactor's // by this methodology BuildActorMap(findActorMap, varilink, repointedItem); //} // Actor map for updating the bioconversation //Debug.WriteLine($"Shuffle actor on varilink in convo: {varilink.LinkedNodes[0].ObjectName.Instanced} => {package.GetUExport(repointedItem).ObjectName.Instanced}"); varilink.LinkedNodes[0] = package.GetUExport(repointedItem); } //SeqTools.PrintVarLinkInfo(seqLinks); // Write the updated links out. SeqTools.WriteVariableLinksToNode(convStart, seqLinks); // Update the localizations foreach (var loc in Localizations) { var bioConversation = EntryImporter.ResolveImport(bioConvImport, MERFileSystem.GetGlobalCache(), localCache, loc); var conv = new ConversationExtended(bioConversation); conv.LoadConversation(null, true); // Step 1. Update tags via map var allConvEntries = conv.ReplyList.ToList(); allConvEntries.AddRange(conv.EntryList); foreach (var convNode in allConvEntries) { // Update speaker if (convNode.IsReply) { // Player. We can't do anything (or can we?) } else { // Non-player node. We can change the tag var speakerTag = convNode.SpeakerTag; // Even though it is dictionary, since it is NameRef, it is considered case sensitive. We have to use case insensitive check var newName = findActorMap.FirstOrDefault(x => x.Key.Instanced.Equals(speakerTag.SpeakerName, StringComparison.InvariantCultureIgnoreCase)).Value; if (newName != null && newName.FindActor.Name != null) { var newTagIdx = conv.Speakers.FirstOrDefault(x => x.SpeakerName.Equals(newName.FindActor.Instanced, StringComparison.InvariantCultureIgnoreCase)); if (newTagIdx != null) { convNode.SpeakerIndex = newTagIdx.SpeakerID; } else { var newSpeaker = new SpeakerExtended(conv.Speakers.Count - 3, new NameReference(newName.FindActor.Name.ToLower(), newName.FindActor.Number)); newSpeaker.FaceFX_Male = speakerTag.FaceFX_Male; newSpeaker.FaceFX_Female = speakerTag.FaceFX_Male; conv.Speakers.Add(newSpeaker); convNode.SpeakerIndex = newSpeaker.SpeakerID; //Debugger.Break(); } } else { //Debugger.Break(); } } // Update interpolation data if (convNode.Interpdata == null) { continue; } var interpData = convNode.Interpdata; InterpData id = new InterpData(interpData); var convo = id.InterpGroups.FirstOrDefault(x => x.GroupName == "Conversation"); if (convo != null) { foreach (var it in convo.Tracks) { var props = it.Export.GetProperties(); var findActor = props.GetProp <NameProperty>("m_nmFindActor"); if (findActor != null && findActor.Value.Name != "Owner" && findActorMap.TryGetValue(findActor.Value, out var newInfo) && newInfo.FindActor.Name != null) { //Debug.WriteLine($"Updating find actor info: {findActor.Value.Instanced} -> {newInfo.FindActor.Instanced}"); findActor.Value = newInfo.FindActor; if (newInfo.FindMode != null) { props.AddOrReplaceProp(new EnumProperty(newInfo.FindMode.ToString(), "EActorTrackFindActorMode", MERFileSystem.Game, "m_eFindActorMode")); } else { props.RemoveNamedProperty("m_eFindActorMode"); } } var lookAtKeys = props.GetProp <ArrayProperty <StructProperty> >("m_aLookAtKeys"); if (lookAtKeys != null) { foreach (var lookAtS in lookAtKeys) { var lookAt = lookAtS.GetProp <NameProperty>("nmFindActor"); if (lookAt.Value.Name != "Owner" && findActorMap.TryGetValue(lookAt.Value, out var newInfoLA) && newInfoLA.FindActor.Name != null) { //Debug.WriteLine($"Updating lookat find actor info: {lookAt.Value.Instanced} -> {newInfoLA.FindActor.Instanced}"); if (newInfoLA.FindActor.Name == null) { Debugger.Break(); } lookAt.Value = newInfoLA.FindActor; var lookatFindMode = newInfoLA.FindMode?.ToString(); lookatFindMode ??= "ActorTrack_FindActorByTag"; // if it's null, set it to the default. As this is struct, the property must exist lookAtS.Properties.AddOrReplaceProp(new EnumProperty(lookatFindMode, "EActorTrackFindActorMode", MERFileSystem.Game, "eFindActorMode")); } } } it.Export.WriteProperties(props); //if (IsAllowedFindActor(findActor)) //{ // if (actorsToFind.All(x => x.FindActor.Instanced != findActor.Value.Instanced)) // { // ActorLookup al = new ActorLookup() { FindActor = findActor.Value }; // var lookupType = it.Export.GetProperty<EnumProperty>("m_eFindActorMode"); // if (lookupType != null && Enum.TryParse<EActorTrackFindActorMode>(lookupType.Value.Name, out var result)) // { // al.FindMode = result; // } // actorsToFind.Add(al); // } // // add track // tracksToUpdate.Add(it); //} } } } conv.SerializeNodes(); // Write the updated info back MERFileSystem.SavePackage(bioConversation.FileRef); } } //// Step 2. Build the remapping //if (actorsToFind.Count <= 1) // return false; // Nothing to randomize //// Instanced name to NameReference //Dictionary<string, ActorLookup> actorRemap = new Dictionary<string, ActorLookup>(); //var shuffledActors = actorsToFind.ToList(); //var unshuffledActors = actorsToFind.Select(x => x.FindActor.Instanced).ToList(); //shuffledActors.Shuffle(); //Debug.WriteLine(""); //while (shuffledActors.Count > 0) //{ // var sourceItem = unshuffledActors.PullFirstItem(); // var remappedItem = shuffledActors.PullFirstItem(); // actorRemap[sourceItem] = remappedItem; // Debug.WriteLine($"Remapping actor: {sourceItem} => {remappedItem.FindActor.Instanced}"); //} //// Step 3. Update all tracks with new remap //foreach (var track in tracksToUpdate) //{ // var props = track.Export.GetProperties(); // var findActor = props.GetProp<NameProperty>("m_nmFindActor"); // if (IsAllowedFindActor(findActor)) // { // if (actorRemap[findActor.Value.Instanced].FindMode != null) // { // props.AddOrReplaceProp(new EnumProperty(actorRemap[findActor.Value.Instanced].FindMode.ToString(), "EActorTrackFindActorMode", MERFileSystem.Game, "m_eFindActorMode")); // } // else // { // props.RemoveNamedProperty("m_eFindActorNode"); // } // findActor.Value = actorRemap[findActor.Value.Instanced].FindActor; // track.Export.WriteProperties(props); // } //} return(true); }
public static HashSet <int> GetReferencedEntries(this IMEPackage pcc, bool getreferenced = true, bool getactorrefs = false, ExportEntry startatexport = null) { var result = new HashSet <int>(); Level level = null; Stack <IEntry> entriesToEvaluate = new Stack <IEntry>(); HashSet <IEntry> entriesEvaluated = new HashSet <IEntry>(); HashSet <IEntry> entriesReferenced = new HashSet <IEntry>(); if (startatexport != null) //Start at object { entriesToEvaluate.Push(startatexport); entriesReferenced.Add(startatexport); entriesEvaluated.Add(pcc.GetUExport(startatexport.idxLink)); //Do not go up the chain if parsing an export } else if (pcc.Exports.FirstOrDefault(exp => exp.ClassName == "Level") is ExportEntry levelExport) //Evaluate level with only actors, model+components, sequences and level class being processed. { level = ObjectBinary.From <Level>(levelExport); entriesEvaluated.Add(null); //null stops future evaluations entriesEvaluated.Add(levelExport); entriesReferenced.Add(levelExport); var levelclass = levelExport.Class; entriesToEvaluate.Push(levelclass); entriesReferenced.Add(levelclass); foreach (int actoridx in level.Actors) { var actor = pcc.GetEntry(actoridx); entriesToEvaluate.Push(actor); entriesReferenced.Add(actor); } var model = pcc.GetEntry(level.Model?.value ?? 0); entriesToEvaluate.Push(model); entriesReferenced.Add(model); foreach (var comp in level.ModelComponents) { var compxp = pcc.GetEntry(comp); entriesToEvaluate.Push(compxp); entriesReferenced.Add(compxp); } foreach (var seq in level.GameSequences) { var seqxp = pcc.GetEntry(seq); entriesToEvaluate.Push(seqxp); entriesReferenced.Add(seqxp); } var localpackage = pcc.Exports.FirstOrDefault(x => x.ClassName == "Package" && x.ObjectName.ToString().ToLower() == Path.GetFileNameWithoutExtension(pcc.FilePath).ToLower()); // Make sure world, localpackage, shadercache are all marked as referenced. entriesToEvaluate.Push(localpackage); entriesReferenced.Add(localpackage); var world = levelExport.Parent; entriesToEvaluate.Push(world); entriesReferenced.Add(world); var shadercache = pcc.Exports.FirstOrDefault(x => x.ClassName == "ShaderCache"); if (shadercache != null) { entriesEvaluated.Add(shadercache); entriesReferenced.Add(shadercache); entriesToEvaluate.Push(shadercache.Class); entriesReferenced.Add(shadercache.Class); } } else { return(result); //If this has no level it is a reference / seekfree package and shouldn't be compacted. } var theserefs = new HashSet <IEntry>(); while (!entriesToEvaluate.IsEmpty()) { var ent = entriesToEvaluate.Pop(); try { if (entriesEvaluated.Contains(ent) || (ent?.UIndex ?? 0) == 0 || (getactorrefs && !ent.InstancedFullPath.Contains("PersistentLevel"))) { continue; } entriesEvaluated.Add(ent); if (ent.idxLink != 0) { theserefs.Add(pcc.GetEntry(ent.idxLink)); } if (ent.UIndex < 0) { continue; } var exp = pcc.GetUExport(ent.UIndex); //find header references only if doing non-actors if (!getactorrefs) { if ((exp.Archetype?.UIndex ?? 0) != 0) { theserefs.Add(exp.Archetype); } if ((exp.Class?.UIndex ?? 0) != 0) { theserefs.Add(exp.Class); } if ((exp.SuperClass?.UIndex ?? 0) != 0) { theserefs.Add(exp.SuperClass); } if (exp.HasComponentMap) { foreach (var kvp in exp.ComponentMap) { //theserefs.Add(pcc.GetEntry(kvp.Value)); //THIS IS INCORRECT SHOULD NOT BE ON UINDEX } } } else { exp.CondenseArchetypes(); } //find property references findPropertyReferences(exp.GetProperties(), exp); //find binary references if (!exp.IsDefaultObject && ObjectBinary.From(exp) is ObjectBinary objBin) { List <(UIndex, string)> indices = objBin.GetUIndexes(exp.FileRef.Game); foreach ((UIndex uIndex, string propName) in indices) { if (uIndex != exp.UIndex) { theserefs.Add(pcc.GetEntry(uIndex)); } } } foreach (var reference in theserefs) { if (!entriesEvaluated.Contains(reference)) { entriesToEvaluate.Push(reference); entriesReferenced.Add(reference); } } theserefs.Clear(); } catch (Exception e) { Console.WriteLine($"Error getting references {ent.UIndex} {ent.ObjectName.Instanced}: {e.Message}"); } } if (getreferenced) { foreach (var entry in entriesReferenced) { result.Add(entry?.UIndex ?? 0); } } else { foreach (var xp in pcc.Exports) { if (!entriesReferenced.Contains(xp)) { result.Add(xp?.UIndex ?? 0); } } foreach (var im in pcc.Imports) { if (!entriesReferenced.Contains(im)) { result.Add(im?.UIndex ?? 0); } } } return(result); void findPropertyReferences(PropertyCollection props, ExportEntry exp) { foreach (Property prop in props) { switch (prop) { case ObjectProperty objectProperty: if (objectProperty.Value != 0 && objectProperty.Value != exp.UIndex) { theserefs.Add(pcc.GetEntry(objectProperty.Value)); } break; case DelegateProperty delegateProperty: if (delegateProperty.Value.Object != 0 && delegateProperty.Value.Object != exp.UIndex) { theserefs.Add(pcc.GetEntry(delegateProperty.Value.Object)); } break; case StructProperty structProperty: findPropertyReferences(structProperty.Properties, exp); break; case ArrayProperty <ObjectProperty> arrayProperty: for (int i = 0; i < arrayProperty.Count; i++) { ObjectProperty objProp = arrayProperty[i]; if (objProp.Value != 0 && objProp.Value != exp.UIndex) { theserefs.Add(pcc.GetEntry(objProp.Value)); } } break; case ArrayProperty <StructProperty> arrayProperty: for (int i = 0; i < arrayProperty.Count; i++) { StructProperty structProp = arrayProperty[i]; findPropertyReferences(structProp.Properties, exp); } break; } } } }
public static PropertyCollection getDefaultStructValue(string className, bool stripTransients) { bool isImmutable = UnrealObjectInfo.IsImmutable(className, MEGame.ME2); if (Structs.ContainsKey(className)) { ClassInfo info = Structs[className]; try { PropertyCollection structProps = new PropertyCollection(); ClassInfo tempInfo = info; while (tempInfo != null) { foreach ((string propName, PropertyInfo propInfo) in tempInfo.properties) { if (stripTransients && propInfo.Transient) { continue; } if (getDefaultProperty(propName, propInfo, stripTransients, isImmutable) is Property uProp) { structProps.Add(uProp); } } if (!Structs.TryGetValue(tempInfo.baseClass, out tempInfo)) { tempInfo = null; } } structProps.Add(new NoneProperty()); string filepath = null; if (ME2Directory.BioGamePath != null) { filepath = Path.Combine(ME2Directory.BioGamePath, info.pccPath); } Stream loadStream = null; if (File.Exists(info.pccPath)) { filepath = info.pccPath; loadStream = new MemoryStream(File.ReadAllBytes(info.pccPath)); } else if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { filepath = "GAMERESOURCES_ME2"; loadStream = Utilities.LoadFileFromCompressedResource("GameResources.zip", CoreLib.CustomResourceFileName(MEGame.ME2)); } else if (filepath != null && File.Exists(filepath)) { loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } #if AZURE else if (MiniGameFilesPath != null && File.Exists(Path.Combine(MiniGameFilesPath, info.pccPath))) { // Load from test minigame folder. This is only really useful on azure where we don't have access to // games filepath = Path.Combine(MiniGameFilesPath, info.pccPath); loadStream = new MemoryStream(File.ReadAllBytes(filepath)); } #endif if (loadStream != null) { using (IMEPackage importPCC = MEPackageHandler.OpenMEPackageFromStream(loadStream, filepath, useSharedPackageCache: true)) { var exportToRead = importPCC.GetUExport(info.exportIndex); byte[] buff = exportToRead.Data.Skip(0x30).ToArray(); PropertyCollection defaults = PropertyCollection.ReadProps(exportToRead, new MemoryStream(buff), className); foreach (var prop in defaults) { structProps.TryReplaceProp(prop); } } } return(structProps); } catch { return(null); } } return(null); }
public static Class ConvertClass(UClass uClass, bool decompileBytecode, FileLib lib = null) { IMEPackage pcc = uClass.Export.FileRef; VariableType parent = new VariableType(pcc.GetEntry(uClass.SuperClass)?.ObjectName.Instanced ?? "object"); VariableType outer = new VariableType(pcc.GetEntry(uClass.OuterClass)?.ObjectName.Instanced ?? parent.Name); // TODO: components var interfaces = new List <VariableType>(); foreach ((UIndex interfaceUIndex, UIndex _) in uClass.Interfaces) { interfaces.Add(new VariableType(pcc.GetEntry(interfaceUIndex)?.ObjectName.Instanced ?? "UNK_INTERFACE")); } var Types = new List <VariableType>(); var Vars = new List <VariableDeclaration>(); var Funcs = new List <Function>(); var States = new List <State>(); var nextItem = uClass.Children; while (pcc.TryGetUExport(nextItem, out ExportEntry nextChild)) { var objBin = ObjectBinary.From(nextChild); switch (objBin) { case UConst uConst: Types.Add(new Const(uConst.Export.ObjectName.Instanced, uConst.Value) { Literal = new ClassOutlineParser(new TokenStream <string>(new StringLexer(uConst.Value))).ParseConstValue() }); nextItem = uConst.Next; break; case UEnum uEnum: Types.Add(ConvertEnum(uEnum)); nextItem = uEnum.Next; break; case UFunction uFunction: Funcs.Add(ConvertFunction(uFunction, uClass, decompileBytecode)); nextItem = uFunction.Next; break; case UProperty uProperty: Vars.Add(ConvertVariable(uProperty)); nextItem = uProperty.Next; break; case UScriptStruct uScriptStruct: Types.Add(ConvertStruct(uScriptStruct)); nextItem = uScriptStruct.Next; break; case UState uState: nextItem = uState.Next; States.Add(ConvertState(uState, uClass, decompileBytecode)); break; default: nextItem = 0; break; } } var propObject = pcc.GetUExport(uClass.Defaults); var defaultProperties = ConvertDefaultProperties(propObject); Class AST = new Class(uClass.Export.ObjectName.Instanced, parent, outer, uClass.ClassFlags, interfaces, Types, Vars, Funcs, States, defaultProperties) { ConfigName = uClass.ClassConfigName, Package = uClass.Export.Parent is null?Path.GetFileNameWithoutExtension(pcc.FilePath) : uClass.Export.ParentInstancedFullPath, }; // Ugly quick fix: foreach (var member in Types) { member.Outer = AST; } foreach (var member in Vars) { member.Outer = AST; } foreach (var member in Funcs) { member.Outer = AST; } foreach (var member in States) { member.Outer = AST; } var virtFuncLookup = new CaseInsensitiveDictionary <ushort>(); for (ushort i = 0; i < uClass.FullFunctionsList.Length; i++) { virtFuncLookup.Add(uClass.FullFunctionsList[i].GetEntry(pcc)?.ObjectName, i); } AST.VirtualFunctionLookup = virtFuncLookup; return(AST); }
public static IEntry EnsureClassIsInFile(IMEPackage pcc, string className) { //check to see class is already in file foreach (ImportEntry import in pcc.Imports) { if (import.IsClass && import.ObjectName == className) { return(import); } } foreach (ExportEntry export in pcc.Exports) { if (export.IsClass && export.ObjectName == className) { return(export); } } ClassInfo info = UnrealObjectInfo.GetClassOrStructInfo(pcc.Game, className); //backup some package state so we can undo changes if something goes wrong int exportCount = pcc.ExportCount; int importCount = pcc.ImportCount; List <string> nameListBackup = pcc.Names.ToList(); try { if (EntryImporter.IsSafeToImportFrom(info.pccPath, pcc.Game)) { string package = Path.GetFileNameWithoutExtension(info.pccPath); return(pcc.getEntryOrAddImport($"{package}.{className}")); } //It's a class that's defined locally in every file that uses it. Stream loadStream = null; if (info.pccPath == UnrealObjectInfo.Me3ExplorerCustomNativeAdditionsName) { loadStream = Utilities.GetCustomAppResourceStream(pcc.Game); //string resourceFilePath = App.CustomResourceFilePath(pcc.Game); //if (File.Exists(resourceFilePath)) //{ // sourceFilePath = resourceFilePath; //} } else { string testPath = Path.Combine(MEDirectories.GetBioGamePath(pcc.Game), info.pccPath); if (File.Exists(testPath)) { loadStream = new MemoryStream(File.ReadAllBytes(testPath)); } else if (pcc.Game == MEGame.ME1) { testPath = Path.Combine(ME1Directory.DefaultGamePath, info.pccPath); if (File.Exists(testPath)) { loadStream = new MemoryStream(File.ReadAllBytes(testPath)); } } } if (loadStream == null) { //can't find file to import from. This may occur if user does not have game or neccesary dlc installed return(null); } using IMEPackage sourcePackage = MEPackageHandler.OpenMEPackageFromStream(loadStream); if (!sourcePackage.IsUExport(info.exportIndex)) { return(null); //not sure how this would happen } ExportEntry sourceClassExport = sourcePackage.GetUExport(info.exportIndex); if (sourceClassExport.ObjectName != className) { return(null); } //Will make sure that, if the class is in a package, that package will exist in pcc IEntry parent = EntryImporter.GetOrAddCrossImportOrPackage(sourceClassExport.ParentFullPath, sourcePackage, pcc); var relinkResults = EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneAllDependencies, sourceClassExport, pcc, parent, true, out IEntry result); if (relinkResults?.Count > 0) { ListDialog ld = new ListDialog(relinkResults, "Relink report", "The following items failed to relink.", null); ld.Show(); } return(result); } catch (Exception e) { //remove added entries var entriesToRemove = new List <IEntry>(); for (int i = exportCount; i < pcc.Exports.Count; i++) { entriesToRemove.Add(pcc.Exports[i]); } for (int i = importCount; i < pcc.Imports.Count; i++) { entriesToRemove.Add(pcc.Imports[i]); } EntryPruner.TrashEntries(pcc, entriesToRemove); pcc.restoreNames(nameListBackup); return(null); } }
/// <summary> /// Copy ME2 Static art and collision into an ME3 file. /// By Kinkojiro /// </summary> /// <param name="Game">Target Game</param> /// <param name="BioPSource">Source BioP</param> /// <param name="tgtOutputfolder">OutputFolder</param> /// <param name="BioArtsToCopy">List of level source file locations</param> /// <param name="ActorsToMove">Dicitionary: key Actors, value filename, entry uid</param> /// <param name="AssetsToMove">Dictionary key: AssetInstancedPath, value filename, isimport, entry uid</param> /// <param name="fromreload">is reloaded json</param> public static async Task <List <string> > ConvertLevelToGame(MEGame Game, IMEPackage BioPSource, string tgtOutputfolder, string tgttfc, Action <string> callbackAction, LevelConversionData conversionData = null, bool fromreload = false, bool createtestlevel = false) { //VARIABLES / VALIDATION var actorclassesToMove = new List <string>() { "BlockingVolume", "SpotLight", "SpotLightToggleable", "PointLight", "PointLightToggleable", "SkyLight", "HeightFog", "LenseFlareSource", "StaticMeshActor", "BioTriggerStream", "BioBlockingVolume" }; var actorclassesToSubstitute = new Dictionary <string, string>() { { "BioBlockingVolume", "Engine.BlockingVolume" } }; var archetypesToSubstitute = new Dictionary <string, string>() { { "Default__BioBlockingVolume", "Default__BlockingVolume" } }; var fails = new List <string>(); string busytext = null; if ((BioPSource.Game == MEGame.ME2 && ME2Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME1 && ME1Directory.DefaultGamePath == null) || (BioPSource.Game == MEGame.ME3 && ME3Directory.DefaultGamePath == null) || BioPSource.Game == MEGame.UDK) { fails.Add("Source Game Directory not found"); return(fails); } //Get filelist from BioP, Save a copy in outputdirectory, Collate Actors and asset information if (!fromreload) { busytext = "Collating level files..."; callbackAction?.Invoke(busytext); conversionData = new LevelConversionData(Game, BioPSource.Game, null, null, null, new ConcurrentDictionary <string, string>(), new ConcurrentDictionary <string, (string, int)>(), new ConcurrentDictionary <string, (string, int, List <string>)>()); var supportedExtensions = new List <string> { ".pcc", ".u", ".upk", ".sfm" }; if (Path.GetFileName(BioPSource.FilePath).ToLowerInvariant().StartsWith("biop_") && BioPSource.Exports.FirstOrDefault(x => x.ClassName == "BioWorldInfo") is ExportEntry BioWorld) { string biopname = Path.GetFileNameWithoutExtension(BioPSource.FilePath); conversionData.GameLevelName = biopname.Substring(5, biopname.Length - 5); var lsks = BioWorld.GetProperty <ArrayProperty <ObjectProperty> >("StreamingLevels").ToList(); if (lsks.IsEmpty()) { fails.Add("No files found in level."); return(fails); } foreach (var l in lsks) { var lskexp = BioPSource.GetUExport(l.Value); var filename = lskexp.GetProperty <NameProperty>("PackageName"); if ((filename?.Value.ToString().ToLowerInvariant().StartsWith("bioa") ?? false) || (filename?.Value.ToString().ToLowerInvariant().StartsWith("biod") ?? false)) { var filePath = Directory.GetFiles(ME2Directory.DefaultGamePath, $"{filename.Value.Instanced}.pcc", SearchOption.AllDirectories).FirstOrDefault(); conversionData.FilesToCopy.TryAdd(filename.Value.Instanced, filePath); } } conversionData.BioPSource = $"{biopname}_{BioPSource.Game}"; BioPSource.Save(Path.Combine(tgtOutputfolder, $"{conversionData.BioPSource}.pcc")); BioPSource.Dispose(); } else { fails.Add("Requires an BioP to work with."); return(fails); } busytext = "Collating actors and assets..."; callbackAction?.Invoke(busytext); Parallel.ForEach(conversionData.FilesToCopy, (pccref) => { using IMEPackage pcc = MEPackageHandler.OpenMEPackage(pccref.Value); var sourcelevel = pcc.Exports.FirstOrDefault(l => l.ClassName == "Level"); if (ObjectBinary.From(sourcelevel) is Level levelbin) { foreach (var act in levelbin.Actors) { if (act < 1) { continue; } var actor = pcc.GetUExport(act); if (actorclassesToMove.Contains(actor.ClassName)) { conversionData.ActorsToMove.TryAdd($"{pccref.Key}.{actor.InstancedFullPath}", (pccref.Key, act)); HashSet <int> actorrefs = pcc.GetReferencedEntries(true, true, actor); foreach (var r in actorrefs) { var objref = pcc.GetEntry(r); if (objref != null) { if (objref.InstancedFullPath.Contains("PersistentLevel")) //remove components of actors { continue; } string instancedPath = objref.InstancedFullPath; if (objref.idxLink == 0) { instancedPath = $"{pccref.Key}.{instancedPath}"; } var added = conversionData.AssetsToMove.TryAdd(instancedPath, (pccref.Key, r, new List <string>() { pccref.Key })); if (!added) { conversionData.AssetsToMove[instancedPath].Item3.FindOrAdd(pccref.Key); if (r > 0 && conversionData.AssetsToMove[instancedPath].Item2 < 0) //Replace imports with exports if possible { var currentlist = conversionData.AssetsToMove[instancedPath].Item3; conversionData.AssetsToMove[instancedPath] = (pccref.Key, r, currentlist); } }
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); }
private static EntryStringPair relinkUIndex(IMEPackage importingPCC, ExportEntry relinkingExport, ref int uIndex, string propertyName, OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, string prefix, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { 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, relinkerCache: relinkerCache); } catch (Exception e) { //Error during relink 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(new EntryStringPair(relinkingExport, $"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(new EntryStringPair(relinkingExport, $"Relink failed: CrossImport porting failed for {prefix}{propertyName} {uIndex} {destinationPcc.GetEntry(uIndex).FullPath} in export {relinkingExport.FullPath}({relinkingExport.UIndex})")); } return(new EntryStringPair(relinkingExport, $"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 { bool importingFromGlobalFile = false; //It's an export //Attempt lookup ExportEntry sourceExport = importingPCC.GetUExport(uIndex); string instancedFullPath = sourceExport.InstancedFullPath; string sourceFilePath = sourceExport.FileRef.FilePath; if (EntryImporter.IsSafeToImportFrom(sourceFilePath, destinationPcc.Game)) { importingFromGlobalFile = true; instancedFullPath = $"{Path.GetFileNameWithoutExtension(sourceFilePath)}.{instancedFullPath}"; } IEntry existingEntry = null; if (relinkerCache != null) { relinkerCache.destInstancedFullPathToEntryMap.TryGetValue(instancedFullPath, out existingEntry); } else { existingEntry = destinationPcc.Exports.FirstOrDefault(x => x.InstancedFullPath == instancedFullPath); existingEntry ??= destinationPcc.Imports.FirstOrDefault(x => x.InstancedFullPath == instancedFullPath); } 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 (importingFromGlobalFile) { uIndex = EntryImporter.GetOrAddCrossImportOrPackageFromGlobalFile(sourceExport.FullPath, importingPCC, destinationPcc, crossPCCObjectMappingList, relinkerCache: relinkerCache).UIndex; } else { if (!crossPCCObjectMappingList.TryGetValue(sourceExport.Parent, out IEntry parent)) { parent = EntryImporter.GetOrAddCrossImportOrPackage(sourceExport.ParentFullPath, importingPCC, destinationPcc, true, crossPCCObjectMappingList, relinkerCache: relinkerCache); } ExportEntry importedExport = EntryImporter.ImportExport(destinationPcc, sourceExport, parent?.UIndex ?? 0, true, crossPCCObjectMappingList, relinkerCache: relinkerCache); 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(new EntryStringPair(relinkingExport, $"Relink failed: {prefix}{propertyName} {uIndex} in export {relinkingExport.FullPath}({relinkingExport.UIndex})")); } } return(null); }