public static void Serialize <TKey, TValue>(this SerializingContainer2 sc, ref OrderedMultiValueDictionary <TKey, TValue> dict, SerializeDelegate <TKey> serializeKey, SerializeDelegate <TValue> serializeValue) { int count = dict?.Count ?? 0; sc.Serialize(ref count); if (sc.IsLoading) { dict = new OrderedMultiValueDictionary <TKey, TValue>(count); for (int i = 0; i < count; i++) { TKey key = default; serializeKey(sc, ref key); TValue value = default; serializeValue(sc, ref value); dict.Add(key, value); } } else { for (int i = 0; i < count; i++) { var key = dict[i].Key; serializeKey(sc, ref key); var value = dict[i].Value; serializeValue(sc, ref value); } } }
/// <summary> /// Attempts to relink unreal property data and object pointers in binary when cross porting an export /// </summary> public static List <EntryStringPair> RelinkAll(IDictionary <IEntry, IEntry> crossPccObjectMap, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { var relinkReport = new List <EntryStringPair>(); //relink each modified export //We must convert this to a list, as this list will be updated as imports are cross mapped during relinking. //This process speeds up same-relinks later. //This is a list because otherwise we would get a concurrent modification exception. //Since we only enumerate exports and append imports to this list we will not need to worry about recursive links //I am sure this won't come back to be a pain for me. var crossPCCObjectMappingList = new OrderedMultiValueDictionary <IEntry, IEntry>(crossPccObjectMap); //can't be a foreach since we might append things to the list // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < crossPCCObjectMappingList.Count; i++) { (IEntry src, IEntry dest) = crossPCCObjectMappingList[i]; if (src is ExportEntry sourceExport && dest is ExportEntry relinkingExport) { relinkReport.AddRange(Relink(sourceExport, relinkingExport, crossPCCObjectMappingList, importExportDependencies, relinkerCache)); } } crossPccObjectMap.Clear(); crossPccObjectMap.AddRange(crossPCCObjectMappingList); return(relinkReport); }
private static List <EntryStringPair> relinkPropertiesRecursive(IMEPackage importingPCC, ExportEntry relinkingExport, PropertyCollection transplantProps, OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, string prefix, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { var relinkResults = new List <EntryStringPair>(); foreach (Property prop in transplantProps) { //Debug.WriteLine($"{prefix} Relink recursive on {prop.Name}"); if (prop is StructProperty structProperty) { relinkResults.AddRange(relinkPropertiesRecursive(importingPCC, relinkingExport, structProperty.Properties, crossPCCObjectMappingList, $"{prefix}{structProperty.Name}.", importExportDependencies, relinkerCache)); } else if (prop is ArrayProperty <StructProperty> structArrayProp) { for (int i = 0; i < structArrayProp.Count; i++) { StructProperty arrayStructProperty = structArrayProp[i]; relinkResults.AddRange(relinkPropertiesRecursive(importingPCC, relinkingExport, arrayStructProperty.Properties, crossPCCObjectMappingList, $"{prefix}{arrayStructProperty.Name}[{i}].", importExportDependencies, relinkerCache)); } } else if (prop is ArrayProperty <ObjectProperty> objArrayProp) { foreach (ObjectProperty objProperty in objArrayProp) { int uIndex = objProperty.Value; var result = relinkUIndex(importingPCC, relinkingExport, ref uIndex, objProperty.Name, crossPCCObjectMappingList, prefix, importExportDependencies, relinkerCache); objProperty.Value = uIndex; if (result != null) { relinkResults.Add(result); } } } else if (prop is ObjectProperty objectProperty) { int uIndex = objectProperty.Value; var result = relinkUIndex(importingPCC, relinkingExport, ref uIndex, objectProperty.Name, crossPCCObjectMappingList, prefix, importExportDependencies, relinkerCache); objectProperty.Value = uIndex; if (result != null) { relinkResults.Add(result); } } else if (prop is DelegateProperty delegateProp) { int uIndex = delegateProp.Value.Object; var result = relinkUIndex(importingPCC, relinkingExport, ref uIndex, delegateProp.Name, crossPCCObjectMappingList, prefix, importExportDependencies, relinkerCache); delegateProp.Value = new ScriptDelegate(uIndex, delegateProp.Value.FunctionName); if (result != null) { relinkResults.Add(result); } } } return(relinkResults); }
public OrderedMultiValueDictionary <NameReference, PropertyCollection> CameraList; //PropertyCollection is struct of type BioStageCamera, which contains nothing that needs relinking protected override void Serialize(SerializingContainer2 sc) { if (sc.Game != MEGame.ME3) { if (sc.IsLoading) { CameraList = new OrderedMultiValueDictionary <NameReference, PropertyCollection>(); } return; } long startPos = sc.ms.Position; sc.Serialize(ref length); if (length == 0 && (sc.IsLoading || CameraList.Count == 0)) { if (sc.IsLoading) { CameraList = new OrderedMultiValueDictionary <NameReference, PropertyCollection>(); } return; } NameReference arrayName = "m_aCameraList"; sc.Serialize(ref arrayName); int dummy = 0; sc.Serialize(ref dummy); if (sc.IsLoading) { int count = sc.ms.ReadInt32(); sc.ms.SkipInt32(); CameraList = new OrderedMultiValueDictionary <NameReference, PropertyCollection>(count); for (int i = 0; i < count; i++) { CameraList.Add(sc.ms.ReadNameReference(sc.Pcc), PropertyCollection.ReadProps(Export, sc.ms, "BioStageCamera", true, entry: Export)); } } else { sc.ms.WriteInt32(CameraList.Count); sc.ms.WriteInt32(0); foreach ((NameReference name, PropertyCollection props) in CameraList) { sc.ms.WriteNameReference(name, sc.Pcc); props.WriteTo(sc.ms, sc.Pcc); } } if (sc.IsSaving) { long endPos = sc.ms.Position; sc.ms.JumpTo(startPos); sc.ms.WriteInt32((int)(endPos - startPos - 4)); sc.ms.JumpTo(endPos); } }
protected override void Serialize(SerializingContainer2 sc) { if (sc.Pcc.Platform != MEPackage.GamePlatform.PC) { return; //We do not support non-PC shader cache } byte platform = 0; sc.Serialize(ref platform); sc.Serialize(ref ShaderTypeCRCMap, SCExt.Serialize, SCExt.Serialize); if (sc.Game == MEGame.ME3 && sc.IsLoading) { int nameMapCount = sc.ms.ReadInt32(); sc.ms.Skip(nameMapCount * 12); } else if (sc.Game == MEGame.ME3 && sc.IsSaving) { sc.ms.Writer.WriteInt32(0); } if (sc.Game == MEGame.ME1) { sc.Serialize(ref VertexFactoryTypeCRCMap, SCExt.Serialize, SCExt.Serialize); } if (sc.IsLoading) { int shaderCount = sc.ms.ReadInt32(); Shaders = new OrderedMultiValueDictionary <Guid, Shader>(shaderCount); for (int i = 0; i < shaderCount; i++) { Shader shader = null; sc.Serialize(ref shader); Shaders.Add(shader.Guid, shader); } } else { sc.ms.Writer.WriteInt32(Shaders.Count); foreach ((_, Shader shader) in Shaders) { var temp = shader; sc.Serialize(ref temp); } } if (sc.Game != MEGame.ME1) { sc.Serialize(ref VertexFactoryTypeCRCMap, SCExt.Serialize, SCExt.Serialize); } sc.Serialize(ref MaterialShaderMaps, SCExt.Serialize, SCExt.Serialize); if (sc.Game != MEGame.ME2) { int dummy = 0; sc.Serialize(ref dummy); } }
public static OrderedMultiValueDictionary <string, PropertyInfo> GetAllProperties(Mod.MEGame game, string typeName) { var props = new OrderedMultiValueDictionary <string, PropertyInfo>(); ClassInfo info = GetClassOrStructInfo(game, typeName); while (info != null) { props.AddRange(info.properties); info = GetClassOrStructInfo(game, info.baseClass); } return(props); }
private static List <EntryStringPair> RelinkToken(Token t, byte[] script, ExportEntry sourceExport, ExportEntry destinationExport, OrderedMultiValueDictionary <IEntry, IEntry> crossFileRefObjectMap, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { var relinkFailedReport = new List <EntryStringPair>(); Debug.WriteLine($"Attempting function relink on token at position {t.pos}. Number of listed relinkable items {t.inPackageReferences.Count}"); foreach ((int pos, int type, int value) in t.inPackageReferences) { switch (type) { case Token.INPACKAGEREFTYPE_NAME: int newValue = destinationExport.FileRef.FindNameOrAdd(sourceExport.FileRef.GetNameEntry(value)); Debug.WriteLine($"Function relink hit @ 0x{t.pos + pos:X6}, cross ported a name: {sourceExport.FileRef.GetNameEntry(value)}"); script.OverwriteRange(pos, BitConverter.GetBytes(newValue)); break; case Token.INPACKAGEREFTYPE_ENTRY: relinkAtPosition(pos, value, $"(Script at @ 0x{t.pos + pos:X6}: {t.text})"); break; } } return(relinkFailedReport); void relinkAtPosition(int binaryPosition, int uIndex, string propertyName) { var relinkResult = relinkUIndex(sourceExport.FileRef, destinationExport, ref uIndex, propertyName, crossFileRefObjectMap, "", importExportDependencies, relinkerCache); if (relinkResult is null) { script.OverwriteRange(binaryPosition, BitConverter.GetBytes(uIndex)); } else { relinkFailedReport.Add(relinkResult); } } }
private static List <EntryStringPair> RelinkUnhoodEntryReference(IEntry entry, long position, byte[] script, ExportEntry sourceExport, ExportEntry destinationExport, OrderedMultiValueDictionary <IEntry, IEntry> crossFileRefObjectMap, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { var relinkFailedReport = new List <EntryStringPair>(); Debug.WriteLine($"Attempting function relink on token entry reference {entry.FullPath} at position {position}"); int uIndex = entry.UIndex; var relinkResult = relinkUIndex(sourceExport.FileRef, destinationExport, ref uIndex, $"Entry {entry.FullPath} at 0x{position:X8}", crossFileRefObjectMap, "", importExportDependencies, relinkerCache); if (relinkResult is null) { script.OverwriteRange((int)position, BitConverter.GetBytes(uIndex)); } else { relinkFailedReport.Add(relinkResult); } return(relinkFailedReport); }
public UIndex ArtPlaceable2; //ME1 protected override void Serialize(SerializingContainer2 sc) { sc.Serialize(ref Self); sc.Serialize(ref Actors, SCExt.Serialize); sc.Serialize(ref URL); sc.Serialize(ref Model); sc.Serialize(ref ModelComponents, SCExt.Serialize); sc.Serialize(ref GameSequences, SCExt.Serialize); sc.Serialize(ref TextureToInstancesMap, SCExt.Serialize, SCExt.Serialize); if (sc.Game == MEGame.UDK) { sc.Serialize(ref MeshComponentsWithDynamiclighting, SCExt.Serialize, SCExt.Serialize); } else { MeshComponentsWithDynamiclighting = new OrderedMultiValueDictionary <UIndex, uint>(); } if (sc.Game >= MEGame.ME3) { sc.Serialize(ref ApexMesh, SCExt.Serialize); } else if (sc.IsLoading) { ApexMesh = Array.Empty <byte>(); } int byteSize = 1; sc.Serialize(ref byteSize); sc.Serialize(ref CachedPhysBSPData, SCExt.Serialize); sc.Serialize(ref CachedPhysSMDataMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CachedPhysSMDataStore, SCExt.Serialize); sc.Serialize(ref CachedPhysPerTriSMDataMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CachedPhysPerTriSMDataStore, SCExt.Serialize); sc.Serialize(ref CachedPhysBSPDataVersion); sc.Serialize(ref CachedPhysSMDataVersion); sc.Serialize(ref ForceStreamTextures, SCExt.Serialize, SCExt.Serialize); if (sc.Game == MEGame.UDK) { KCachedConvexData dummy = new KCachedConvexData { CachedConvexElements = Array.Empty <KCachedConvexDataElement>() }; sc.Serialize(ref dummy); int dummyInt = 0; sc.Serialize(ref dummyInt); } sc.Serialize(ref NavListStart); sc.Serialize(ref NavListEnd); sc.Serialize(ref CoverListStart); sc.Serialize(ref CoverListEnd); if (sc.Game >= MEGame.ME3) { sc.Serialize(ref PylonListStart); sc.Serialize(ref PylonListEnd); } if (sc.Game == MEGame.ME3) { sc.Serialize(ref guidToIntMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CoverLinks, SCExt.Serialize); sc.Serialize(ref intToByteMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref guidToIntMap2, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref NavPoints, SCExt.Serialize); sc.Serialize(ref numbers, SCExt.Serialize); } else if (sc.IsLoading) { PylonListStart = new UIndex(0); PylonListEnd = new UIndex(0); guidToIntMap = new OrderedMultiValueDictionary <Guid, int>(); CoverLinks = new List <UIndex>(); intToByteMap = new OrderedMultiValueDictionary <int, byte>(); guidToIntMap2 = new OrderedMultiValueDictionary <Guid, int>(); NavPoints = new List <UIndex>(); numbers = new List <int>(); } sc.Serialize(ref CrossLevelActors, SCExt.Serialize); if (sc.Game == MEGame.UDK) { int dummy = 0; sc.Serialize(ref dummy); sc.Serialize(ref dummy); sc.Serialize(ref dummy); } if (sc.Game == MEGame.ME1) { sc.Serialize(ref ArtPlaceable1); sc.Serialize(ref ArtPlaceable2); } else if (sc.IsLoading) { ArtPlaceable1 = new UIndex(0); ArtPlaceable2 = new UIndex(0); } if (sc.Game == MEGame.UDK && sc.IsSaving) { sc.ms.Writer.WriteBoolInt(false); //PrecomputedLightVolume bIsInitialized sc.ms.BaseStream.WriteZeros(28); //Zero-ed PrecomputedVisibilityHandler sc.ms.BaseStream.WriteZeros(45); //unk data } if (sc.Game == MEGame.ME3) { //PrecomputedLightVolume bool bIsInitialized = false; sc.Serialize(ref bIsInitialized); //should always be false, but just in case; if (bIsInitialized) { throw new Exception($"PersistentLevel has a PreComputedLightVolume! Level in: {sc.Pcc.FilePath}"); } } }
public UIndex ArtPlaceable2; //ME1 protected override void Serialize(SerializingContainer2 sc) { sc.Serialize(ref Self); sc.Serialize(ref Actors, SCExt.Serialize); sc.Serialize(ref URL); sc.Serialize(ref Model); sc.Serialize(ref ModelComponents, SCExt.Serialize); sc.Serialize(ref GameSequences, SCExt.Serialize); sc.Serialize(ref TextureToInstancesMap, SCExt.Serialize, SCExt.Serialize); if (sc.Game == MEGame.ME3) { sc.Serialize(ref ApexMesh, SCExt.Serialize); } else if (sc.IsLoading) { ApexMesh = Array.Empty <byte>(); } int byteSize = 1; sc.Serialize(ref byteSize); sc.Serialize(ref CachedPhysBSPData, SCExt.Serialize); sc.Serialize(ref CachedPhysSMDataMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CachedPhysSMDataStore, SCExt.Serialize); sc.Serialize(ref CachedPhysPerTriSMDataMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CachedPhysPerTriSMDataStore, SCExt.Serialize); sc.Serialize(ref CachedPhysBSPDataVersion); sc.Serialize(ref CachedPhysSMDataVersion); sc.Serialize(ref ForceStreamTextures, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref NavListStart); sc.Serialize(ref NavListEnd); sc.Serialize(ref CoverListStart); sc.Serialize(ref CoverListEnd); if (sc.Game == MEGame.ME3) { sc.Serialize(ref PylonListStart); sc.Serialize(ref PylonListEnd); sc.Serialize(ref guidToIntMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref CoverLinks, SCExt.Serialize); sc.Serialize(ref intToByteMap, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref guidToIntMap2, SCExt.Serialize, SCExt.Serialize); sc.Serialize(ref NavPoints, SCExt.Serialize); sc.Serialize(ref numbers, SCExt.Serialize); } else if (sc.IsLoading) { PylonListStart = new UIndex(0); PylonListEnd = new UIndex(0); guidToIntMap = new OrderedMultiValueDictionary <Guid, int>(); CoverLinks = new UIndex[0]; intToByteMap = new OrderedMultiValueDictionary <int, byte>(); guidToIntMap2 = new OrderedMultiValueDictionary <Guid, int>(); NavPoints = new UIndex[0]; numbers = new int[0]; } sc.Serialize(ref CrossLevelActors, SCExt.Serialize); if (sc.Game == MEGame.ME3) { //PrecomputedLightVolume bool bIsInitialized = false; sc.Serialize(ref bIsInitialized); //should always be false, but just in case; if (bIsInitialized) { throw new Exception($"PersistentLevel has a PreComputedLightVolume! Level in: {sc.Pcc.FilePath}"); } } if (sc.Game == MEGame.ME1) { sc.Serialize(ref ArtPlaceable1); sc.Serialize(ref ArtPlaceable2); } else if (sc.IsLoading) { ArtPlaceable1 = new UIndex(0); ArtPlaceable2 = new UIndex(0); } }
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 static List <EntryStringPair> Relink(ExportEntry sourceExport, ExportEntry relinkingExport, OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, bool importExportDependencies = false, RelinkerCache relinkerCache = null) { var relinkFailedReport = new List <EntryStringPair>(); IMEPackage sourcePcc = sourceExport.FileRef; byte[] prePropBinary = relinkingExport.GetPrePropBinary(); //Relink stack if (relinkingExport.HasStack) { int uIndex = BitConverter.ToInt32(prePropBinary, 0); var relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: Node", crossPCCObjectMappingList, "", importExportDependencies, relinkerCache); if (relinkResult is null) { prePropBinary.OverwriteRange(0, BitConverter.GetBytes(uIndex)); } else { relinkFailedReport.Add(relinkResult); } uIndex = BitConverter.ToInt32(prePropBinary, 4); relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: StateNode", crossPCCObjectMappingList, "", importExportDependencies, relinkerCache); if (relinkResult is null) { prePropBinary.OverwriteRange(4, BitConverter.GetBytes(uIndex)); } else { relinkFailedReport.Add(relinkResult); } } //Relink Component's TemplateOwnerClass else if (relinkingExport.TemplateOwnerClassIdx is var toci && toci >= 0) { int uIndex = BitConverter.ToInt32(prePropBinary, toci); var relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "TemplateOwnerClass", crossPCCObjectMappingList, "", importExportDependencies, relinkerCache); if (relinkResult is null) { prePropBinary.OverwriteRange(toci, BitConverter.GetBytes(uIndex)); } else { relinkFailedReport.Add(relinkResult); } } //Relink Properties PropertyCollection props = relinkingExport.GetProperties(); relinkFailedReport.AddRange(relinkPropertiesRecursive(sourcePcc, relinkingExport, props, crossPCCObjectMappingList, "", importExportDependencies, relinkerCache)); //Relink Binary try { if (relinkingExport.Game != sourcePcc.Game && (relinkingExport.IsClass || relinkingExport.ClassName == "State" || relinkingExport.ClassName == "Function")) { relinkFailedReport.Add(new EntryStringPair(relinkingExport, $"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed. Cannot port {relinkingExport.ClassName} between games!")); } else if (ObjectBinary.From(relinkingExport) is ObjectBinary objBin) { List <(UIndex, string)> indices = objBin.GetUIndexes(relinkingExport.FileRef.Game); foreach ((UIndex uIndex, string propName) in indices) { var result = relinkUIndex(sourcePcc, relinkingExport, ref uIndex.value, $"(Binary Property: {propName})", crossPCCObjectMappingList, "", importExportDependencies, relinkerCache); if (result != null) { relinkFailedReport.Add(result); } } //UStruct is abstract baseclass for Class, State, and Function, and can have script in it if (objBin is UStruct uStructBinary && uStructBinary.ScriptBytes.Length > 0) { if (relinkingExport.Game == MEGame.ME3) { (List <Token> tokens, _) = Bytecode.ParseBytecode(uStructBinary.ScriptBytes, sourceExport); foreach (Token token in tokens) { relinkFailedReport.AddRange(RelinkToken(token, uStructBinary.ScriptBytes, sourceExport, relinkingExport, crossPCCObjectMappingList, importExportDependencies, relinkerCache)); } } else { var func = sourceExport.ClassName == "State" ? UE3FunctionReader.ReadState(sourceExport) : UE3FunctionReader.ReadFunction(sourceExport); func.Decompile(new TextBuilder(), false); //parse bytecode var nameRefs = func.NameReferences; var entryRefs = func.EntryReferences; foreach ((long position, NameReference nameRef) in nameRefs) { if (position < uStructBinary.ScriptBytes.Length) { RelinkNameReference(nameRef.Name, position, uStructBinary.ScriptBytes, relinkingExport); } } foreach ((long position, IEntry entry) in entryRefs) { if (position < uStructBinary.ScriptBytes.Length) { relinkFailedReport.AddRange(RelinkUnhoodEntryReference(entry, position, uStructBinary.ScriptBytes, sourceExport, relinkingExport, crossPCCObjectMappingList, importExportDependencies, relinkerCache)); } } } } relinkingExport.WritePrePropsAndPropertiesAndBinary(prePropBinary, props, objBin); return(relinkFailedReport); } } catch (Exception e) when(!CoreLib.IsDebug) { relinkFailedReport.Add(new EntryStringPair(relinkingExport, $"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed due to exception: {e.Message}")); } relinkingExport.WritePrePropsAndProperties(prePropBinary, props); return(relinkFailedReport); }
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); }