/// <summary>
        /// Installs the specified r2d to the specified export. Specify an asset name if you wish to use a specific asset in the RTexture2D object.
        /// </summary>
        /// <param name="r2d"></param>
        /// <param name="export"></param>
        /// <param name="asset"></param>
        public static void InstallTexture(RTexture2D r2d, ExportEntry export, string asset = null)
        {
            // If no asset was specified pick a random asset
            asset ??= r2d.FetchRandomTextureAsset();

            // 2. Is this asset already parsed?
            if (r2d.InstantiatedItems.TryGetValue(asset, out var instantiated))
            {
                // It's already been instantiated. Just use this data instead
                MERLog.Information($@"Writing out cached asset {asset} for {export.InstancedFullPath}");
                export.WritePropertiesAndBinary(instantiated.props, instantiated.texData);
            }
            else
            {
                MERLog.Information($@"Installing texture asset {asset} for {export.InstancedFullPath}");

                // 3. Asset has not been setup yet. Write out the precomputed data.
                export.WriteBinary(GetTextureAssetBinary(asset));

                // 4. Read in the data so it's in the correct context.
                var ut2d = ObjectBinary.From <UTexture2D>(export);

                // 5. Move compressed mips to TFC
                foreach (var mip in ut2d.Mips)
                {
                    if (mip.IsCompressed)
                    {
                        mip.DataOffset = TFCBuilder.WriteMip(r2d, mip.Mip); // If it's compressed it needs to be stored externally.
                        if (mip.StorageType == StorageTypes.pccLZO)
                        {
                            mip.StorageType = StorageTypes.extLZO;
                        }
                        if (mip.StorageType == StorageTypes.pccZlib)
                        {
                            mip.StorageType = StorageTypes.extZlib;
                        }
                    }
                }

                // 6. Setup the properties
                var props = export.GetProperties();
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips.Count - 1, @"MipTailBaseIdx"));
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeX, @"SizeX"));
                props.AddOrReplaceProp(new IntProperty(ut2d.Mips[0].SizeY, @"SizeY"));
                props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCNameProp : ActiveBuilder.DLCTFCNameProp);
                props.AddOrReplaceProp(r2d.PreMountTexture ? ActiveBuilder.BGTFCGuidProp : ActiveBuilder.DLCTFCGuidProp);
                if (r2d.LODGroup != null)
                {
                    // Write a new LOD property
                    props.AddOrReplaceProp(r2d.LODGroup);
                }


                // 7. Commit the export
                export.WritePropertiesAndBinary(props, ut2d);

                // 8. Cache the work that's been done so we don't need to it again.
                r2d.InstantiatedItems[asset] = (props, ut2d);
            }
        }
Example #2
0
        public static void SetCollectionActorLocation(ExportEntry component, float x, float y, float z, List <ExportEntry> collectionitems = null, ExportEntry collectionactor = null)
        {
            if (collectionactor == null)
            {
                if (!(component.HasParent && component.Parent.ClassName.Contains("CollectionActor")))
                {
                    return;
                }
                collectionactor = (ExportEntry)component.Parent;
            }

            collectionitems ??= GetCollectionItems(collectionactor);

            if (collectionitems?.Count > 0)
            {
                var idx = collectionitems.FindIndex(o => o != null && o.UIndex == component.UIndex);
                if (idx >= 0)
                {
                    var binData = (StaticCollectionActor)ObjectBinary.From(collectionactor);

                    Matrix m = binData.LocalToWorldTransforms[idx];
                    m.TranslationVector = new Vector3(x, y, z);
                    binData.LocalToWorldTransforms[idx] = m;

                    collectionactor.WriteBinary(binData);
                }
            }
        }
        //private static bool CanRandomize(ExportEntry export) => !export.IsDefaultObject && export.ClassName == @"TextureMovie" && ExportNameToAssetMapping.ContainsKey(export.ObjectName.Name);
        //public static bool RandomizeExport(ExportEntry export, RandomizationOption option)
        //{
        //    if (!CanRandomize(export)) return false;
        //    var assets = ExportNameToAssetMapping[export.ObjectName.Name];
        //    byte[] tmAsset = GetTextureMovieAssetBinary(assets[ThreadSafeRandom.Next(assets.Count)]);
        //    var tm = ObjectBinary.From<TextureMovie>(export);
        //    tm.EmbeddedData = tmAsset;
        //    tm.DataSize = tmAsset.Length;
        //    export.WriteBinary(tm);
        //    return true;
        //}

        // ME2 only has few texture movies so these are used
        public static bool RandomizeExportDirect(ExportEntry export, RandomizationOption option, byte[] tmAsset)
        {
            var tm = ObjectBinary.From <TextureMovie>(export);

            tm.EmbeddedData = tmAsset;
            tm.DataSize     = tmAsset.Length;
            export.WriteBinary(tm);
            return(true);
        }
Example #4
0
        private static void InstallDynamicAnimSetRefForSkeletalMesh(ExportEntry export, RBioEvtSysTrackGesture.Gesture gesture)
        {
            // We have parent sequence data
            var skmDynamicAnimSets = export.GetProperty <ArrayProperty <ObjectProperty> >("AnimSets") ?? new ArrayProperty <ObjectProperty>("AnimSets");

            // Check to see if there is any item that uses our bioanimset
            var bioAnimSet = gesture.GetBioAnimSet(export.FileRef);

            if (bioAnimSet != null)
            {
                ExportEntry skmBioDynamicAnimSet = null;
                foreach (var skmDynAnimSet in skmDynamicAnimSets)
                {
                    var kEntry        = skmDynAnimSet.ResolveToEntry(export.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq
                    var associatedset = kEntry.GetProperty <ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef);
                    if (associatedset == bioAnimSet)
                    {
                        // It's this one
                        skmBioDynamicAnimSet = kEntry;
                        break;
                    }
                }

                if (skmBioDynamicAnimSet == null)
                {
                    // We need to generate a new one
                    PropertyCollection props = new PropertyCollection();
                    props.Add(new NameProperty(gesture.GestureSet, "m_nmOrigSetName"));
                    props.Add(new ArrayProperty <ObjectProperty>("Sequences"));
                    props.Add(new ObjectProperty(bioAnimSet, "m_pBioAnimSetData"));
                    skmBioDynamicAnimSet = ExportCreator.CreateExport(export.FileRef, $"BioDynamicAnimSet", "BioDynamicAnimSet", export);

                    // Write a blank count of 0 - we will update this in subsequent call
                    // This must be here to ensure parser can read it
                    skmBioDynamicAnimSet.WritePropertiesAndBinary(props, new byte[4]);
                    skmDynamicAnimSets.Add(new ObjectProperty(skmBioDynamicAnimSet)); // Add new export to sequence's list of biodynamicanimsets
                    export.WriteProperty(skmDynamicAnimSets);
                }

                var currentObjs = skmBioDynamicAnimSet.GetProperty <ArrayProperty <ObjectProperty> >("Sequences");
                if (currentObjs.All(x => x.Value != gesture.Entry.UIndex))
                {
                    // We need to add our item to it
                    currentObjs.Add(new ObjectProperty(gesture.Entry));
                    var bin = ObjectBinary.From <BioDynamicAnimSet>(skmBioDynamicAnimSet);
                    bin.SequenceNamesToUnkMap[gesture.GestureAnim] = 1; // Not sure what the value should be, or if game actually reads this
                    // FIX IT IF WE EVER FIGURE IT OUT!
                    skmBioDynamicAnimSet.WriteProperty(currentObjs);
                    skmBioDynamicAnimSet.WriteBinary(bin);
                }
            }
        }
Example #5
0
 private void DisableConfigFlag(IMEPackage package, ExportEntry export, Mod installingMod)
 {
     if (ObjectBinary.From(export) is UProperty ob)
     {
         Log.Information($@"Disabling config flag on {export.InstancedFullPath}");
         ob.PropertyFlags &= ~UnrealFlags.EPropertyFlags.Config;
         export.WriteBinary(ob);
     }
     else
     {
         throw new Exception(M3L.GetString(M3L.string_interp_mergefile_notPropertyExportConfigFlag, export.InstancedFullPath, package.FilePath));
     }
 }
        private static void TestR(ExportEntry export)
        {
            if (/*export.UIndex == 36224 && */ export.ClassName == "Material")
            {
                var obj = ObjectBinary.From <Material>(export);
                foreach (var v in obj.SM3MaterialResource.UniformPixelVectorExpressions)
                {
                    if (v is MaterialUniformExpressionVectorParameter vp)
                    {
                        vp.DefaultA = ThreadSafeRandom.NextFloat(0, 1);
                        vp.DefaultR = ThreadSafeRandom.NextFloat();
                        vp.DefaultG = ThreadSafeRandom.NextFloat();
                        vp.DefaultB = ThreadSafeRandom.NextFloat();
                    }
                    else if (v is MaterialUniformExpressionAppendVector av)
                    {
                    }
                }

                foreach (var v in obj.SM3MaterialResource.UniformPixelVectorExpressions)
                {
                }
                export.WriteBinary(obj);
            }

            if (export.UIndex == 37354 || export.UIndex == 37355)
            {
                var vParms = VectorParameter.GetVectorParameters(export);
                if (vParms != null)
                {
                    foreach (var vParm in vParms)
                    {
                        CFVector4 nv = new CFVector4();
                        nv.W = ThreadSafeRandom.NextFloat(2000);
                        nv.X = ThreadSafeRandom.NextFloat(2000);
                        nv.Y = ThreadSafeRandom.NextFloat(2000);
                        nv.Z = ThreadSafeRandom.NextFloat(2000);
                        vParm.ParameterValue = nv;
                    }
                }
            }
        }
Example #7
0
        public static bool RandomizeExport(ExportEntry exp, RandomizationOption option)
        {
            if (!CanRandomize(exp, out var shiftDirection, out var min, out var max))
            {
                return(false);
            }
            var shiftAmt    = ThreadSafeRandom.NextFloat(min, max);
            var morphTarget = ObjectBinary.From <MorphTarget>(exp);

            foreach (var lm in morphTarget.MorphLODModels)
            {
                foreach (var v in lm.Vertices)
                {
                    var posDelta = v.PositionDelta;
                    if (shiftDirection == 0) // IN OUT
                                             //posDelta.X += shiftAmt;
                    {
                        posDelta.X *= shiftAmt;
                    }
                    if (shiftDirection == 1) // LEFT RIGHT
                                             //posDelta.Y += shiftAmt;
                    {
                        posDelta.Y *= shiftAmt;
                    }
                    if (shiftDirection == 2) // UP DOWN
                                             //posDelta.Z += shiftAmt;
                    {
                        posDelta.Z *= shiftAmt;
                    }
                    v.PositionDelta = posDelta; // Require reassignment
                }
            }


            exp.WriteBinary(morphTarget);
            return(true);
        }
        public static SkeletalMesh FuzzSkeleton(ExportEntry export, RandomizationOption option)
        {
            // Goofs up the RefSkeleton values
            var objBin = ObjectBinary.From <SkeletalMesh>(export);

            foreach (var bone in objBin.RefSkeleton)
            {
                if (!bone.Name.Name.Contains("eye", StringComparison.InvariantCultureIgnoreCase) &&
                    !bone.Name.Name.Contains("sneer", StringComparison.InvariantCultureIgnoreCase) &&
                    !bone.Name.Name.Contains("nose", StringComparison.InvariantCultureIgnoreCase) &&
                    !bone.Name.Name.Contains("brow", StringComparison.InvariantCultureIgnoreCase) &&
                    !bone.Name.Name.Contains("jaw", StringComparison.InvariantCultureIgnoreCase))
                {
                    continue;
                }
                var v3 = bone.Position;
                v3.X         *= ThreadSafeRandom.NextFloat(1 - (option.SliderValue / 2), 1 + option.SliderValue);
                v3.Y         *= ThreadSafeRandom.NextFloat(1 - (option.SliderValue / 2), 1 + option.SliderValue);
                v3.Z         *= ThreadSafeRandom.NextFloat(1 - (option.SliderValue / 2), 1 + option.SliderValue);
                bone.Position = v3;
            }
            export.WriteBinary(objBin);
            return(objBin);
        }
        public void serializeTalkfileToExport(ExportEntry export, bool savePackage = false)
        {
            /* converts Huffmann Tree to binary form */
            byte[] treeBuffer = ConvertHuffmanTreeToBuffer();

            var encodedStrings = new List <EncodedString>();
            int i = 0;

            foreach (var entry in _inputData)
            {
                if (entry.Flags == 0)
                {
                    if (entry.StringID > 0)
                    {
                        entry.Index = -1;
                    }
                    else
                    {
                        entry.Index = 0;
                    }
                }
                else
                {
                    entry.Index = i;
                    i++;
                    var binaryData   = new List <BitArray>();
                    int binaryLength = 0;

                    /* for every character in a string, put it's binary code into data array */
                    foreach (char c in entry.ASCIIData)
                    {
                        binaryData.Add(_huffmanCodes[c]);
                        binaryLength += _huffmanCodes[c].Count;
                    }
                    byte[] buffer = BitArrayListToByteArray(binaryData, binaryLength);
                    encodedStrings.Add(new EncodedString(entry.ASCIIData.Length, buffer.Length, buffer));
                }
            }


            /* writing properties */
            export.WriteProperty(new IntProperty(_inputData.Count, "m_nHashTableSize"));

            MemoryStream m = new MemoryStream();

            /* writing entries */
            m.Write(BitConverter.GetBytes(_inputData.Count), 0, 4);
            foreach (TLKStringRef entry in _inputData)
            {
                m.Write(BitConverter.GetBytes(entry.StringID), 0, 4);
                m.Write(BitConverter.GetBytes(entry.Flags), 0, 4);
                m.Write(BitConverter.GetBytes(entry.Index), 0, 4);
            }

            /* writing HuffmanTree */
            m.Write(treeBuffer, 0, treeBuffer.Length);

            /* writing data */
            m.Write(BitConverter.GetBytes(encodedStrings.Count), 0, 4);
            foreach (EncodedString enc in encodedStrings)
            {
                m.Write(BitConverter.GetBytes(enc.stringLength), 0, 4);
                m.Write(BitConverter.GetBytes(enc.encodedLength), 0, 4);
                m.Write(enc.binaryData, 0, enc.encodedLength);
            }

            byte[] buff = m.ToArray();
            export.WriteBinary(buff);
            if (savePackage)
            {
                export.FileRef.Save(export.FileRef.FilePath);
            }
        }
        public static bool RandomizeExport(ExportEntry exp, RandomizationOption option)
        {
            if (!CanRandomize(exp))
            {
                return(false);
            }
            //if (exp.UIndex != 1999) return false;
            try
            {
                //Log.Information($"[{Path.GetFileNameWithoutExtension(exp.FileRef.FilePath)}] Randomizing FaceFX export {exp.UIndex}");
                //var d = exp.Data;
                var animSet = ObjectBinary.From <FaceFXAnimSet>(exp);
                for (int i = 0; i < animSet.Lines.Count(); i++)
                {
                    var faceFxline = animSet.Lines[i];
                    //if (true)

                    bool randomizedBoneList = false;
                    if (ThreadSafeRandom.Next(10 - (int)option.SliderValue) == 0)
                    {
                        //Randomize the names used for animation
                        faceFxline.AnimationNames.Shuffle();
                        randomizedBoneList = true;
                    }
                    if (!randomizedBoneList || ThreadSafeRandom.Next(16 - (int)option.SliderValue) == 0) // if bonelist was shuffled, or if 1 in 16 chance while it was also shuffled.
                    {
                        //Randomize the points
                        for (int j = 0; j < faceFxline.Points.Count; j++)
                        {
                            bool isLast       = j == faceFxline.Points.Count - 1;
                            var  currentPoint = faceFxline.Points[j];
                            switch (option.SliderValue)
                            {
                            case 1:     //A few broken bones
                                currentPoint.weight += ThreadSafeRandom.NextFloat(-.25, .25);
                                break;

                            case 2:     //A significant option.SliderValue of broken bones
                                currentPoint.weight += ThreadSafeRandom.NextFloat(-.5, .5);
                                break;

                            case 3:     //That's not how the face is supposed to work
                                if (ThreadSafeRandom.Next(5) == 0)
                                {
                                    currentPoint.weight = ThreadSafeRandom.NextFloat(-3, 3);
                                }
                                else if (isLast && ThreadSafeRandom.Next(3) == 0)
                                {
                                    currentPoint.weight = ThreadSafeRandom.NextFloat(.7, .7);
                                }
                                else
                                {
                                    currentPoint.weight *= 8;
                                }

                                break;

                            case 4:     //:O
                                if (ThreadSafeRandom.Next(12) == 0)
                                {
                                    currentPoint.weight = ThreadSafeRandom.NextFloat(-5, 5);
                                }
                                else if (isLast && ThreadSafeRandom.Next(2) == 0)
                                {
                                    currentPoint.weight = ThreadSafeRandom.NextFloat(.9, .9);
                                }
                                else
                                {
                                    currentPoint.weight *= 5;
                                }
                                //Debug.WriteLine(currentPoint.weight);

                                break;

                            case 5:     //Utter madness
                                currentPoint.weight = ThreadSafeRandom.NextFloat(-10, 10);
                                break;

                            default:
                                Debugger.Break();
                                break;
                            }
                            //if (currentPoint.weight == 0)
                            //Debug.WriteLine($"Weight is 0, {faceFxline.NameAsString}, exp {exp.UIndex} {exp.FullPath}");
                            faceFxline.Points[j] = currentPoint; //Reassign the struct
                        }
                    }

                    //Debugging only: Get list of all animation names
                    //for (int j = 0; j < faceFxline.animations.Length; j++)
                    //{
                    //    var animationName = animSet.Header.Names[faceFxline.animations[j].index]; //animation name
                    //    faceFxBoneNames.Add(animationName);
                    //}
                }

                //var dataBefore = exp.Data;
                exp.WriteBinary(animSet);
                //var dataAfter = exp.Data;
                //if (dataBefore.SequenceEqual(dataAfter))
                //{
                //Debugger.Break();
                //}
                //else
                //{
                //    Log.Information($"[{Path.GetFileNameWithoutExtension(exp.FileRef.FilePath)}] Randomized FaceFX for export " + exp.UIndex);
                // }
                return(true);
            }
            catch (Exception e)
            {
                //Do nothing for now.
                Log.Error("AnimSet error! " + App.FlattenException((e)));
            }
            return(false);
        }
Example #11
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);
        }
Example #12
0
        /*
         * private static void VerifyGesturesWork(ExportEntry trackExport)
         * {
         *  var gestures = RBioEvtSysTrackGesture.GetGestures(trackExport);
         *  var defaultPose = RBioEvtSysTrackGesture.GetDefaultPose(trackExport);
         *
         *  var gesturesToCheck = gestures.Append(defaultPose).ToList();
         *
         *  // Get the containing sequence
         *  var owningSequence = SeqTools.GetParentSequence(trackExport);
         *  while (owningSequence.ClassName != "Sequence")
         *  {
         *      owningSequence = owningSequence.Parent as ExportEntry;
         *      var parSeq = SeqTools.GetParentSequence(owningSequence);
         *      if (parSeq != null)
         *      {
         *          owningSequence = parSeq;
         *      }
         *  }
         *
         *  var kismetBioDynamicAnimSets = owningSequence.GetProperty<ArrayProperty<ObjectProperty>>("m_aBioDynAnimSets");
         *  if (kismetBioDynamicAnimSets == null)
         *  {
         *      // We don't have any animsets!
         *      throw new Exception("Track's sequence is missing animsets property!");
         *  }
         *
         *  // Get a list of all supported animations
         *  List<Gesture> supportedGestures = new List<Gesture>();
         *  foreach (var kbdas in kismetBioDynamicAnimSets)
         *  {
         *      var sequenceBioDynamicAnimSet = kbdas.ResolveToEntry(trackExport.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq
         *      var associatedset = sequenceBioDynamicAnimSet.GetProperty<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(trackExport.FileRef);
         *
         *  }
         *
         *  // Check all gestures
         *  foreach (var gesture in gesturesToCheck)
         *  {
         *      var bioAnimSet = gesture.GetBioAnimSet(trackExport.FileRef);
         *
         *  }
         *
         *
         *
         * }
         *
         * internal class TestingBioDynamicAnimSet
         * {
         *  public NameReference OrigSetName { get; }
         *  public List<string> SupportedGesturesFullPaths { get; }
         *  public IEntry BioAnimSetData { get; }
         *
         *  internal TestingBioDynamicAnimSet(ExportEntry export)
         *  {
         *      var props = export.GetProperties();
         *      OrigSetName = props.GetProp<NameProperty>("m_nmOrigSetName").Value;
         *      BioAnimSetData = props.GetProp<ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef);
         *      SupportedGesturesFullPaths = props.GetProp<ArrayProperty<ObjectProperty>>("Sequences").Select(x => x.ResolveToEntry(export.FileRef).InstancedFullPath).ToList();
         *  }
         * }
         */
        private static void InstallDynamicAnimSetRefForSeq(ref ExportEntry owningSequence, ExportEntry export, Gesture gesture)
        {
            // Find owning sequence
            if (owningSequence == null)
            {
                owningSequence = export;
            }
            while (owningSequence.ClassName != "Sequence")
            {
                owningSequence = owningSequence.Parent as ExportEntry;
                var parSeq = SeqTools.GetParentSequence(owningSequence);
                if (parSeq != null)
                {
                    owningSequence = parSeq;
                }
            }

            // We have parent sequence data
            var kismetBioDynamicAnimSets = owningSequence.GetProperty <ArrayProperty <ObjectProperty> >("m_aBioDynAnimSets")
                                           ?? new ArrayProperty <ObjectProperty>("m_aBioDynamicAnimSets");

            // Check to see if there is any item that uses our bioanimset
            var bioAnimSet = gesture.GetBioAnimSet(export.FileRef);

            if (bioAnimSet != null)
            {
                ExportEntry kismetBDAS = null;
                foreach (var kbdas in kismetBioDynamicAnimSets)
                {
                    var kEntry        = kbdas.ResolveToEntry(export.FileRef) as ExportEntry; // I don't think these can be imports as they're part of the seq
                    var associatedset = kEntry.GetProperty <ObjectProperty>("m_pBioAnimSetData").ResolveToEntry(export.FileRef);
                    if (associatedset == bioAnimSet)
                    {
                        // It's this one
                        kismetBDAS = kEntry;
                        break;
                    }
                }

                if (kismetBDAS == null)
                {
                    // We need to generate a new one
                    PropertyCollection props = new PropertyCollection();
                    props.Add(new NameProperty(gesture.GestureSet, "m_nmOrigSetName"));
                    props.Add(new ArrayProperty <ObjectProperty>("Sequences"));
                    props.Add(new ObjectProperty(bioAnimSet, "m_pBioAnimSetData"));
                    kismetBDAS            = ExportCreator.CreateExport(export.FileRef, $"KIS_DYN_{gesture.GestureSet}", "BioDynamicAnimSet", owningSequence);
                    kismetBDAS.indexValue = 0;

                    // Write a blank count of 0 - we will update this in subsequent call
                    // This must be here to ensure parser can read it
                    kismetBDAS.WritePropertiesAndBinary(props, new byte[4]);
                    kismetBioDynamicAnimSets.Add(new ObjectProperty(kismetBDAS)); // Add new export to sequence's list of biodynamicanimsets
                    owningSequence.WriteProperty(kismetBioDynamicAnimSets);
                }

                var currentObjs = kismetBDAS.GetProperty <ArrayProperty <ObjectProperty> >("Sequences");
                if (currentObjs.All(x => x.Value != gesture.Entry.UIndex))
                {
                    // We need to add our item to it
                    currentObjs.Add(new ObjectProperty(gesture.Entry));
                    var bin = ObjectBinary.From <BioDynamicAnimSet>(kismetBDAS);
                    bin.SequenceNamesToUnkMap[gesture.GestureAnim] = 1; // Not sure what the value should be, or if game actually reads this
                    // FIX IT IF WE EVER FIGURE IT OUT!
                    kismetBDAS.WriteProperty(currentObjs);
                    kismetBDAS.WriteBinary(bin);
                }
            }
        }
Example #13
0
        public static void RunGame2EmailMerge(GameTarget target)
        {
            M3MergeDLC.RemoveMergeDLC(target);
            var loadedFiles = MELoadedFiles.GetFilesLoadedInGame(target.Game, gameRootOverride: target.TargetPath);

            // File to base modifications on
            using IMEPackage pcc = MEPackageHandler.OpenMEPackage(loadedFiles[@"BioD_Nor103_Messages.pcc"]);

            // Path to Message templates file - different files for ME2/LE2
            string ResourcesFilePath = $@"MassEffectModManagerCore.modmanager.emailmerge.{target.Game}.103Message_Template_{target.Game}";

            using IMEPackage resources = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(ResourcesFilePath));

            // Startup file to place conditionals and transitions into
            using IMEPackage startup = MEPackageHandler.OpenMEPackageFromStream(Utilities.GetResourceStream(
                                                                                    $@"MassEffectModManagerCore.modmanager.mergedlc.{target.Game}.Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc"));


            var emailInfos        = new List <ME2EmailMergeFile>();
            var jsonSupercedances = M3Directories.GetFileSupercedances(target, new[] { @".json" });

            if (jsonSupercedances.TryGetValue(EMAIL_MERGE_MANIFEST_FILE, out var jsonList))
            {
                jsonList.Reverse();
                foreach (var dlc in jsonList)
                {
                    var jsonFile = Path.Combine(M3Directories.GetDLCPath(target), dlc, target.Game.CookedDirName(),
                                                EMAIL_MERGE_MANIFEST_FILE);
                    emailInfos.Add(JsonConvert.DeserializeObject <ME2EmailMergeFile>(File.ReadAllText(jsonFile)));
                }
            }

            // Sanity checks
            if (!emailInfos.Any() || !emailInfos.SelectMany(e => e.Emails).Any())
            {
                return;
            }

            if (emailInfos.Any(e => e.Game != target.Game))
            {
                throw new Exception("ME2 email merge manifest targets incorrect game");
            }

            // Startup File
            // Could replace this with full instanced path in M3 implementation
            ExportEntry stateEventMapExport = startup.Exports
                                              .First(e => e.ClassName == "BioStateEventMap" && e.ObjectName == "StateTransitionMap");
            BioStateEventMap StateEventMap    = stateEventMapExport.GetBinaryData <BioStateEventMap>();
            ExportEntry      ConditionalClass =
                startup.FindExport($@"PlotManager{M3MergeDLC.MERGE_DLC_FOLDERNAME}.BioAutoConditionals");

            #region Sequence Exports
            // Send message - All email conditionals are checked and emails transitions are triggered
            ExportEntry SendMessageContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_Messages");
            ExportEntry LastSendMessage      = KismetHelper.GetSequenceObjects(SendMessageContainer).OfType <ExportEntry>()
                                               .FirstOrDefault(e =>
            {
                var outbound = KismetHelper.GetOutboundLinksOfNode(e);
                return(outbound.Count == 1 && outbound[0].Count == 0);
            });
            ExportEntry TemplateSendMessage          = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate");
            ExportEntry TemplateSendMessageBoolCheck = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Send_MessageTemplate_BoolCheck");


            // Mark Read - email ints are set to read
            // This is the only section that does not gracefully handle different DLC installations - DLC_CER is required atm
            ExportEntry MarkReadContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read");
            ExportEntry LastMarkRead      = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read.DLC_CER");
            ExportEntry MarkReadOutLink   = KismetHelper.GetOutboundLinksOfNode(LastMarkRead)[0][0].LinkedOp as ExportEntry;
            KismetHelper.RemoveOutputLinks(LastMarkRead);

            ExportEntry TemplateMarkRead           = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_ReadTemplate");
            ExportEntry TemplateMarkReadTransition = resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Mark_Read_Transition");


            // Display Messages - Str refs are passed through to GUI
            ExportEntry DisplayMessageContainer =
                pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages");
            ExportEntry DisplayMessageOutLink =
                pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_Messages.SeqCond_CompareBool_0");

            ExportEntry LastDisplayMessage = SeqTools.FindOutboundConnectionsToNode(DisplayMessageOutLink, KismetHelper.GetSequenceObjects(DisplayMessageContainer).OfType <ExportEntry>())[0];
            KismetHelper.RemoveOutputLinks(LastDisplayMessage);
            var         DisplayMessageVariableLinks = LastDisplayMessage.GetProperty <ArrayProperty <StructProperty> >("VariableLinks");
            ExportEntry TemplateDisplayMessage      =
                resources.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Display_MessageTemplate");

            // Archive Messages - Message ints are set to 3
            ExportEntry ArchiveContainer = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message");
            ExportEntry ArchiveSwitch    = pcc.FindExport(@"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.SeqAct_Switch_0");
            ExportEntry ArchiveOutLink   =
                pcc.FindExport(
                    @"TheWorld.PersistentLevel.Main_Sequence.Archive_Message.BioSeqAct_PMCheckConditional_1");
            ExportEntry ExampleSetInt  = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch)[0][0].LinkedOp as ExportEntry;
            ExportEntry ExamplePlotInt = SeqTools.GetVariableLinksOfNode(ExampleSetInt)[0].LinkedNodes[0] as ExportEntry;
            #endregion

            int messageID      = KismetHelper.GetOutboundLinksOfNode(ArchiveSwitch).Count + 1;
            int currentSwCount = ArchiveSwitch.GetProperty <IntProperty>("LinkCount").Value;

            foreach (var emailMod in emailInfos)
            {
                string modName = "DLC_MOD_" + emailMod.ModName;

                foreach (var email in emailMod.Emails)
                {
                    string emailName = modName + "_" + email.EmailName;

                    // Create send transition
                    int transitionId  = WriteTransition(StateEventMap, email.StatusPlotInt);
                    int conditionalId = WriteConditional(email.TriggerConditional);

                    #region SendMessage
                    //////////////
                    // SendMessage
                    //////////////

                    // Create seq object
                    var SMTemp = emailMod.InMemoryBool.HasValue ? TemplateSendMessageBoolCheck : TemplateSendMessage;
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         SMTemp, pcc, SendMessageContainer, true, new RelinkerOptionsPackage(), out var outSendEntry);

                    var newSend = outSendEntry as ExportEntry;

                    // Set name, comment, add to sequence
                    newSend.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newSend, SendMessageContainer);
                    KismetHelper.SetComment(newSend, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newSend.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    // Set Trigger Conditional
                    var pmCheckConditionalSM = newSend.GetChildren()
                                               .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckConditional" && e is ExportEntry);
                    if (pmCheckConditionalSM is ExportEntry conditional)
                    {
                        conditional.WriteProperty(new IntProperty(conditionalId, "m_nIndex"));
                        KismetHelper.SetComment(conditional, "Time for " + email.EmailName + "?");
                    }

                    // Set Send Transition
                    var pmExecuteTransitionSM = newSend.GetChildren()
                                                .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry);
                    if (pmExecuteTransitionSM is ExportEntry transition)
                    {
                        transition.WriteProperty(new IntProperty(transitionId, "m_nIndex"));
                        KismetHelper.SetComment(transition, "Send " + email.EmailName + " message.");
                    }

                    // Set Send Transition
                    if (emailMod.InMemoryBool.HasValue)
                    {
                        var pmCheckStateSM = newSend.GetChildren()
                                             .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMCheckState" && e is ExportEntry);
                        if (pmCheckStateSM is ExportEntry checkState)
                        {
                            checkState.WriteProperty(new IntProperty(emailMod.InMemoryBool.Value, "m_nIndex"));
                            KismetHelper.SetComment(checkState, "Is " + emailMod.ModName + " installed?");
                        }
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastSendMessage, "Out", newSend);
                    LastSendMessage = newSend;
                    #endregion

                    #region MarkRead
                    ///////////
                    // MarkRead
                    ///////////

                    // Create seq object
                    var MRTemp = email.ReadTransition.HasValue ? TemplateMarkReadTransition : TemplateMarkRead;
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         MRTemp, pcc, MarkReadContainer, true, new RelinkerOptionsPackage(), out var outMarkReadEntry);
                    var newMarkRead = outMarkReadEntry as ExportEntry;

                    // Set name, comment, add to sequence
                    newMarkRead.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newMarkRead, MarkReadContainer);
                    KismetHelper.SetComment(newMarkRead, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newMarkRead.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    // Set Plot Int
                    var storyManagerIntMR = newMarkRead.GetChildren()
                                            .FirstOrDefault(e => e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry);
                    if (storyManagerIntMR is ExportEntry plotIntMR)
                    {
                        plotIntMR.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                        KismetHelper.SetComment(plotIntMR, email.EmailName);
                    }

                    if (email.ReadTransition.HasValue)
                    {
                        var pmExecuteTransitionMR = newMarkRead.GetChildren()
                                                    .FirstOrDefault(e => e.ClassName == "BioSeqAct_PMExecuteTransition" && e is ExportEntry);
                        if (pmExecuteTransitionMR is ExportEntry transitionMR)
                        {
                            transitionMR.WriteProperty(new IntProperty(email.ReadTransition.Value, "m_nIndex"));
                            KismetHelper.SetComment(transitionMR, "Trigger " + email.EmailName + " read transition");
                        }
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastMarkRead, "Out", newMarkRead);
                    LastMarkRead = newMarkRead;
                    #endregion

                    #region DisplayEmail
                    ////////////////
                    // Display Email
                    ////////////////

                    // Create seq object
                    EntryImporter.ImportAndRelinkEntries(EntryImporter.PortingOption.CloneTreeAsChild,
                                                         TemplateDisplayMessage, pcc, DisplayMessageContainer, true, new RelinkerOptionsPackage(), out var outDisplayMessage);
                    var newDisplayMessage = outDisplayMessage as ExportEntry;

                    // Set name, comment, variable links, add to sequence
                    newDisplayMessage.ObjectName = new NameReference(emailName);
                    KismetHelper.AddObjectToSequence(newDisplayMessage, DisplayMessageContainer);
                    newDisplayMessage.WriteProperty(DisplayMessageVariableLinks);
                    KismetHelper.SetComment(newDisplayMessage, emailName);
                    if (target.Game == MEGame.ME2)
                    {
                        newDisplayMessage.WriteProperty(new StrProperty(emailName, "ObjName"));
                    }

                    var displayChildren = newDisplayMessage.GetChildren();

                    // Set Plot Int
                    var storyManagerIntDE = displayChildren.FirstOrDefault(e =>
                                                                           e.ClassName == "BioSeqVar_StoryManagerInt" && e is ExportEntry);
                    if (storyManagerIntDE is ExportEntry plotIntDE)
                    {
                        plotIntDE.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                    }

                    // Set Email ID
                    var emailIdDE = displayChildren.FirstOrDefault(e =>
                                                                   e.ClassName == "SeqVar_Int" && e is ExportEntry);
                    if (emailIdDE is ExportEntry EmailIDDE)
                    {
                        EmailIDDE.WriteProperty(new IntProperty(messageID, "IntValue"));
                    }

                    // Set Title StrRef
                    var titleStrRef = displayChildren.FirstOrDefault(e =>
                                                                     e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Title StrRef");
                    if (titleStrRef is ExportEntry Title)
                    {
                        Title.WriteProperty(new StringRefProperty(email.TitleStrRef, "m_srValue"));
                    }

                    // Set Description StrRef
                    var descStrRef = displayChildren.FirstOrDefault(e =>
                                                                    e.ClassName == "BioSeqVar_StrRef" && e is ExportEntry ee && ee.GetProperty <NameProperty>("VarName").Value == "Desc StrRef");
                    if (descStrRef is ExportEntry Desc)
                    {
                        Desc.WriteProperty(new StringRefProperty(email.DescStrRef, "m_srValue"));
                    }

                    // Hook up output links
                    KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", newDisplayMessage);
                    LastDisplayMessage = newDisplayMessage;
                    #endregion

                    #region ArchiveEmail
                    ////////////////
                    // Archive Email
                    ////////////////

                    var NewSetInt = EntryCloner.CloneEntry(ExampleSetInt);
                    KismetHelper.AddObjectToSequence(NewSetInt, ArchiveContainer);
                    KismetHelper.CreateOutputLink(NewSetInt, "Out", ArchiveOutLink);

                    KismetHelper.CreateNewOutputLink(ArchiveSwitch, "Link " + (messageID - 1), NewSetInt);

                    var NewPlotInt = EntryCloner.CloneEntry(ExamplePlotInt);
                    KismetHelper.AddObjectToSequence(NewPlotInt, ArchiveContainer);
                    NewPlotInt.WriteProperty(new IntProperty(email.StatusPlotInt, "m_nIndex"));
                    NewPlotInt.WriteProperty(new StrProperty(emailName, "m_sRefName"));

                    var linkedVars = SeqTools.GetVariableLinksOfNode(NewSetInt);
                    linkedVars[0].LinkedNodes = new List <IEntry>()
                    {
                        NewPlotInt
                    };
                    SeqTools.WriteVariableLinksToNode(NewSetInt, linkedVars);

                    messageID++;
                    currentSwCount++;
                    #endregion
                }
            }
            KismetHelper.CreateOutputLink(LastMarkRead, "Out", MarkReadOutLink);
            KismetHelper.CreateOutputLink(LastDisplayMessage, "Out", DisplayMessageOutLink);
            ArchiveSwitch.WriteProperty(new IntProperty(currentSwCount, "LinkCount"));

            stateEventMapExport.WriteBinary(StateEventMap);

            // Save Messages file into DLC
            var cookedDir   = Path.Combine(M3Directories.GetDLCPath(target), M3MergeDLC.MERGE_DLC_FOLDERNAME, target.Game.CookedDirName());
            var outMessages = Path.Combine(cookedDir, @"BioD_Nor103_Messages.pcc");
            pcc.Save(outMessages);

            // Save Startup file into DLC
            var startupF = Path.Combine(cookedDir, $@"Startup_{M3MergeDLC.MERGE_DLC_FOLDERNAME}.pcc");
            startup.Save(startupF);
        }
        public static void CompactShaderCaches(IMEPackage mePackage)
        {
            var staticParamSetsInFile = new HashSet <StaticParameterSet>();

            //figure out which MaterialShaderMaps to keep
            foreach (ExportEntry export in mePackage.Exports)
            {
                if (export.ClassName == "Material")
                {
                    staticParamSetsInFile.Add((StaticParameterSet)ObjectBinary.From <Material>(export).SM3MaterialResource.ID);
                }
                else if (export.IsA("MaterialInstance") && export.GetProperty <BoolProperty>("bHasStaticPermutationResource"))
                {
                    staticParamSetsInFile.Add(ObjectBinary.From <MaterialInstance>(export).SM3StaticParameterSet);
                }
            }

            var compactedShaderCache = new ShaderCache
            {
                Shaders            = new OrderedMultiValueDictionary <Guid, Shader>(),
                MaterialShaderMaps = new OrderedMultiValueDictionary <StaticParameterSet, MaterialShaderMap>()
            };

            //add MaterialShaderMaps
            foreach (ExportEntry shaderCacheExport in mePackage.Exports.Where(exp => exp.ClassName == "ShaderCache"))
            {
                var shaderCache = ObjectBinary.From <ShaderCache>(shaderCacheExport);
                compactedShaderCache.ShaderTypeCRCMap        = shaderCache.ShaderTypeCRCMap;
                compactedShaderCache.VertexFactoryTypeCRCMap = shaderCache.VertexFactoryTypeCRCMap;
                foreach ((StaticParameterSet key, MaterialShaderMap msm) in shaderCache.MaterialShaderMaps)
                {
                    if (staticParamSetsInFile.Any(sps => sps == key) && !compactedShaderCache.MaterialShaderMaps.ContainsKey(key))
                    {
                        compactedShaderCache.MaterialShaderMaps.Add(key, msm);
                    }
                }
            }

            //Figure out which shaders to keep
            var shaderGuids = new HashSet <Guid>();

            foreach ((_, MaterialShaderMap materialShaderMap) in compactedShaderCache.MaterialShaderMaps)
            {
                foreach ((_, ShaderReference shaderRef) in materialShaderMap.Shaders)
                {
                    shaderGuids.Add(shaderRef.Id);
                }

                foreach (MeshShaderMap meshShaderMap in materialShaderMap.MeshShaderMaps)
                {
                    foreach ((_, ShaderReference shaderRef) in meshShaderMap.Shaders)
                    {
                        shaderGuids.Add(shaderRef.Id);
                    }
                }
            }

            ExportEntry firstShaderCache = null;

            //add Shaders
            foreach (ExportEntry shaderCacheExport in mePackage.Exports.Where(exp => exp.ClassName == "ShaderCache"))
            {
                var shaderCache = ObjectBinary.From <ShaderCache>(shaderCacheExport);
                foreach ((Guid key, Shader shader) in shaderCache.Shaders)
                {
                    if (shaderGuids.Contains(key) && !compactedShaderCache.Shaders.ContainsKey(key))
                    {
                        compactedShaderCache.Shaders.Add(key, shader);
                    }
                }

                if (firstShaderCache == null)
                {
                    firstShaderCache = shaderCacheExport;
                }
                else
                {
                    EntryPruner.TrashEntryAndDescendants(shaderCacheExport);
                }
            }

            firstShaderCache.WriteBinary(compactedShaderCache);
        }