public void ParseFunction()
 {
     HeaderText = "";
     HeaderText += "Childindex : " + child + "\n";
     HeaderText += "Unknown1 : " + unk1 + "\n";
     HeaderText += "Unknown2 : " + unk2 + "\n";
     HeaderText += "Script Size : " + size + "\n";
     HeaderText += GetFlags() + "\n";
     HeaderText += "Native Index: " + nativeindex;
     var parsedData = Bytecode.ParseBytecode(script, export);
     ScriptBlocks = parsedData.Item1;
     SingularTokenList = parsedData.Item2;
 }
Exemple #2
0
        public override List <(UIndex, string)> GetUIndexes(MEGame game)
        {
            List <(UIndex, string)> uIndices = base.GetUIndexes(game);

            uIndices.Add((Children, "ChildListStart"));

            if (Export.ClassName == "Function")
            {
                if (Export.Game == MEGame.ME3)
                {
                    try
                    {
                        (List <Token> tokens, _) = Bytecode.ParseBytecode(ScriptBytes, Export);
                        foreach (var t in tokens)
                        {
                            {
                                var refs = t.inPackageReferences.Where(x => x.type == Token.INPACKAGEREFTYPE_ENTRY);
                                uIndices.AddRange(refs.Select(x => (new UIndex(x.value), $"Reference inside of function at 0x{x.position:X}")));
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine($"Error decompiling function {Export.FullPath}: {e.Message}");
                    }
                }
                else
                {
                    try
                    {
                        var func = UE3FunctionReader.ReadFunction(Export);
                        func.Decompile(new TextBuilder(), false); //parse bytecode
                        var entryRefs = func.EntryReferences;
                        uIndices.AddRange(entryRefs.Select(x =>
                                                           (new UIndex(x.Value.UIndex), $"Reference inside of function at 0x{x.Key:X}")));
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine($"Error decompiling function {Export.FullPath}: {e.Message}");
                    }
                }
            }

            return(uIndices);
        }
Exemple #3
0
        public static List <string> Relink(ExportEntry sourceExport, ExportEntry relinkingExport, OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, bool importExportDependencies = false)
        {
            var        relinkFailedReport = new List <string>();
            IMEPackage sourcePcc          = sourceExport.FileRef;

            //Relink stack
            if (relinkingExport.HasStack)
            {
                byte[] stack = relinkingExport.GetStack();

                int    uIndex       = BitConverter.ToInt32(stack, 0);
                string relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: Node",
                                                   crossPCCObjectMappingList, "", importExportDependencies);
                if (relinkResult is null)
                {
                    stack.OverwriteRange(0, BitConverter.GetBytes(uIndex));
                }
                else
                {
                    relinkFailedReport.Add(relinkResult);
                }

                uIndex       = BitConverter.ToInt32(stack, 4);
                relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: StateNode",
                                            crossPCCObjectMappingList, "", importExportDependencies);
                if (relinkResult is null)
                {
                    stack.OverwriteRange(4, BitConverter.GetBytes(uIndex));
                }
                else
                {
                    relinkFailedReport.Add(relinkResult);
                }

                relinkingExport.SetStack(stack);
            }

            //Relink Properties
            PropertyCollection transplantProps = sourceExport.GetProperties();

            relinkFailedReport.AddRange(relinkPropertiesRecursive(sourcePcc, relinkingExport, transplantProps, crossPCCObjectMappingList, "", importExportDependencies));
            relinkingExport.WriteProperties(transplantProps);

            //Relink Binary
            try
            {
                if (relinkingExport.Game != sourcePcc.Game && (relinkingExport.IsClass || relinkingExport.ClassName == "State" || relinkingExport.ClassName == "Function"))
                {
                    relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed. Cannot port {relinkingExport.ClassName} between games!");
                    return(relinkFailedReport);
                }

                if (ObjectBinary.From(relinkingExport) is ObjectBinary objBin)
                {
                    List <(UIndex, string)> indices = objBin.GetUIndexes(relinkingExport.FileRef.Game);

                    foreach ((UIndex uIndex, string propName) in indices)
                    {
                        string result = relinkUIndex(sourcePcc, relinkingExport, ref uIndex.value, $"(Binary Property: {propName})", crossPCCObjectMappingList, "",
                                                     importExportDependencies);
                        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));
                            }
                        }
                        else
                        {
                            relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed. {relinkingExport.ClassName} contains script, " +
                                                   $"which cannot be relinked for {relinkingExport.Game}");
                        }
                    }

                    relinkingExport.setBinaryData(objBin.ToBytes(relinkingExport.FileRef, relinkingExport.DataOffset + relinkingExport.propsEnd()));
                    return(relinkFailedReport);
                }

                byte[] binarydata = relinkingExport.getBinaryData();

                if (binarydata.Length > 0)
                {
                    switch (relinkingExport.ClassName)
                    {
                    //todo: make a WwiseEvent ObjectBinary class
                    case "WwiseEvent":
                    {
                        void relinkAtPosition(int binaryPosition, string propertyName)
                        {
                            int    uIndex       = BitConverter.ToInt32(binarydata, binaryPosition);
                            string relinkResult = relinkUIndex(sourcePcc, relinkingExport, ref uIndex, propertyName,
                                                               crossPCCObjectMappingList, "", importExportDependencies);

                            if (relinkResult is null)
                            {
                                binarydata.OverwriteRange(binaryPosition, BitConverter.GetBytes(uIndex));
                            }
                            else
                            {
                                relinkFailedReport.Add(relinkResult);
                            }
                        }

                        if (relinkingExport.FileRef.Game == MEGame.ME3)
                        {
                            int count = BitConverter.ToInt32(binarydata, 0);
                            for (int j = 0; j < count; j++)
                            {
                                relinkAtPosition(4 + (j * 4), $"(Binary Property: WwiseStreams[{j}])");
                            }

                            relinkingExport.setBinaryData(binarydata);
                        }
                        else if (relinkingExport.FileRef.Game == MEGame.ME2)
                        {
                            int parsingPos = 4;
                            int linkCount  = BitConverter.ToInt32(binarydata, parsingPos);
                            parsingPos += 4;
                            for (int j = 0; j < linkCount; j++)
                            {
                                int bankcount = BitConverter.ToInt32(binarydata, parsingPos);
                                parsingPos += 4;
                                for (int k = 0; k < bankcount; k++)
                                {
                                    relinkAtPosition(parsingPos, $"(Binary Property: link[{j}].WwiseBanks[{k}])");

                                    parsingPos += 4;
                                }

                                int wwisestreamcount = BitConverter.ToInt32(binarydata, parsingPos);
                                parsingPos += 4;
                                for (int k = 0; k < wwisestreamcount; k++)
                                {
                                    relinkAtPosition(parsingPos, $"(Binary Property: link[{j}].WwiseStreams[{k}])");

                                    parsingPos += 4;
                                }
                            }

                            relinkingExport.setBinaryData(binarydata);
                        }
                    }
                    break;

                    case "DominantDirectionalLightComponent":
                    case "SphericalHarmonicLightComponent":
                    case "DominantPointLightComponent":
                    case "StaticLightCollectionActor":
                    case "DominantSpotLightComponent":
                    case "DirectionalLightComponent":
                    case "StaticMeshCollectionActor":
                    case "TerrainWeightMapTexture":
                    case "PhysicsAssetInstance":
                    case "PointLightComponent":
                    case "ShadowMapTexture2D":
                    case "SpotLightComponent":
                    case "LightMapTexture2D":
                    case "SkyLightComponent":
                    case "TextureFlipBook":
                    case "BrushComponent":
                    case "FaceFXAnimSet":
                    case "TextureMovie":
                    case "AnimSequence":
                    case "RB_BodySetup":
                    case "MorphTarget":
                    case "ShadowMap1D":
                    case "WwiseStream":
                    case "WwiseBank":
                    case "Texture2D":
                        //these classes have binary but do not need relinking
                        break;

                    default:
                        if (binarydata.Any(b => b != 0))
                        {
                            relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} has unparsed binary. " +
                                                   $"This binary may contain items that need to be relinked. Come to the Discord server " +
                                                   $"(click ME3Tweaks logo in main window for invite) and ask devs to parse this class.");
                        }

                        break;
                    }
                }
            }
            catch (Exception e) when(!App.IsDebug)
            {
                relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed due to exception: {e.Message}");
            }

            return(relinkFailedReport);
        }
Exemple #4
0
        public void ParseFunction()
        {
            HeaderText = "";
            if (export.FileRef.TryGetEntry(FunctionSuperclass, out var _fs))
            {
                HeaderText += $"Function Superclass: {_fs.UIndex} {_fs.InstancedFullPath}\n";
            }
            if (export.FileRef.TryGetEntry(NextItemInLoadingChain, out var _ni))
            {
                HeaderText += $"Next Item in loading chain: {_ni.UIndex} {_ni.InstancedFullPath}\n";
            }
            if (export.FileRef.TryGetEntry(ChildProbeStart, out var _cps))
            {
                HeaderText += $"Child Probe Start: {_cps.UIndex} {_cps.InstancedFullPath}\n";
            }
            HeaderText += $"Script Disk Size: {DiskSize}\n";
            HeaderText += $"Script Memory Size: {MemorySize}\n";
            HeaderText += $"Native Index: {nativeindex}\n";
            HeaderText += GetSignature();
            HeaderText += "\n";

            var parsedData = Bytecode.ParseBytecode(script, export);

            ScriptBlocks      = parsedData.Item1;
            SingularTokenList = parsedData.Item2;

            // Calculate memory offsets
            List <int> objRefPositions = ScriptBlocks.SelectMany(tok => tok.inPackageReferences)
                                         .Where(tup => tup.type == Unreal.Token.INPACKAGEREFTYPE_ENTRY)
                                         .Select(tup => tup.position).ToList();
            int calculatedLength = DiskSize + 4 * objRefPositions.Count;

            DiskToMemPosMap = new int[DiskSize];
            int iDisk = 0;
            int iMem  = 0;

            foreach (int objRefPosition in objRefPositions)
            {
                while (iDisk < objRefPosition + 4)
                {
                    DiskToMemPosMap[iDisk] = iMem;
                    iDisk++;
                    iMem++;
                }
                iMem += 4;
            }
            while (iDisk < DiskSize)
            {
                DiskToMemPosMap[iDisk] = iMem;
                iDisk++;
                iMem++;
            }

            foreach (Token t in ScriptBlocks)
            {
                var diskPos = t.pos - 32;
                if (diskPos >= 0 && diskPos < DiskToMemPosMap.Length)
                {
                    t.memPos = DiskToMemPosMap[diskPos];
                }
            }
        }
        public static int ReplaceAllReferencesToThisOne(this IEntry baseEntry, IEntry replacementEntry)
        {
            int rcount = 0;
            int selectedEntryUIndex = baseEntry.UIndex;
            int replacementUIndex   = replacementEntry.UIndex;
            var references          = baseEntry.GetEntriesThatReferenceThisOne();

            foreach ((IEntry entry, List <string> propsList) in references)
            {
                if (entry is ExportEntry exp)
                {
                    if (propsList.Any(l => l.StartsWith("Property:")))
                    {
                        var newprops = replacePropertyReferences(exp.GetProperties(), selectedEntryUIndex, replacementUIndex, ref rcount);
                        exp.WriteProperties(newprops);
                    }
                    else
                    {
                        if (propsList.Any(l => l.StartsWith("(Binary prop:")) && !exp.IsDefaultObject && ObjectBinary.From(exp) is ObjectBinary objBin)
                        {
                            List <(UIndex, string)> indices = objBin.GetUIndexes(exp.FileRef.Game);
                            foreach ((UIndex uIndex, _) in indices)
                            {
                                if (uIndex.value == selectedEntryUIndex)
                                {
                                    uIndex.value = replacementUIndex;
                                    rcount++;
                                }
                            }

                            //script relinking is not covered by standard binary relinking
                            if (objBin is UStruct uStruct && uStruct.ScriptBytes.Length > 0)
                            {
                                if (exp.Game == MEGame.ME3)
                                {
                                    (List <Token> tokens, _) = Bytecode.ParseBytecode(uStruct.ScriptBytes, exp);
                                    foreach (Token token in tokens)
                                    {
                                        foreach ((int pos, int type, int value) in token.inPackageReferences)
                                        {
                                            switch (type)
                                            {
                                            case Token.INPACKAGEREFTYPE_ENTRY when value == selectedEntryUIndex:
                                                uStruct.ScriptBytes.OverwriteRange(pos, BitConverter.GetBytes(replacementUIndex));
                                                rcount++;
                                                break;
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    var func = entry.ClassName == "State" ? UE3FunctionReader.ReadState(exp) : UE3FunctionReader.ReadFunction(exp);
                                    func.Decompile(new TextBuilder(), false);

                                    foreach ((long position, IEntry ent) in func.EntryReferences)
                                    {
                                        if (ent.UIndex == selectedEntryUIndex && position < uStruct.ScriptBytes.Length)
                                        {
                                            uStruct.ScriptBytes.OverwriteRange((int)position, BitConverter.GetBytes(replacementUIndex));
                                            rcount++;
                                        }
                                    }
                                }
                            }

                            exp.WriteBinary(objBin);
                        }
                    }
                }
            }

            return(rcount);
        public static void ScanStuff(PackageEditorWPF pewpf)
        {
            //var filePaths = MELoadedFiles.GetOfficialFiles(MEGame.ME3);//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME2));//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME1));
            //var filePaths = MELoadedFiles.GetAllFiles(game);
            /*"Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc", "SFXGame.pcc" */
            var filePaths = new[] { "Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc" }.Select(f => Path.Combine(ME3Directory.CookedPCPath, f));
            var interestingExports = new List <EntryStringPair>();
            var foundClasses       = new HashSet <string>(); //new HashSet<string>(BinaryInterpreterWPF.ParsableBinaryClasses);
            var foundProps         = new Dictionary <string, string>();


            var unkOpcodes = new List <int>();//Enumerable.Range(0x5B, 8).ToList();

            unkOpcodes.Add(0);
            unkOpcodes.Add(1);
            var unkOpcodesInfo = unkOpcodes.ToDictionary(i => i, i => new OpcodeInfo());
            var comparisonDict = new Dictionary <string, (byte[] original, byte[] newData)>();

            var extraInfo = new HashSet <string>();

            pewpf.IsBusy   = true;
            pewpf.BusyText = "Scanning";
            Task.Run(() =>
            {
                //preload base files for faster scanning
                using var baseFiles = MEPackageHandler.OpenMEPackages(EntryImporter.FilesSafeToImportFrom(MEGame.ME3)
                                                                      .Select(f => Path.Combine(ME3Directory.CookedPCPath, f)));
                baseFiles.Add(
                    MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "BIOP_MP_COMMON.pcc")));

                foreach (string filePath in filePaths)
                {
                    //ScanShaderCache(filePath);
                    //ScanMaterials(filePath);
                    //ScanStaticMeshComponents(filePath);
                    //ScanLightComponents(filePath);
                    //ScanLevel(filePath);
                    //if (findClass(filePath, "ShaderCache", true)) break;
                    //findClassesWithBinary(filePath);
                    ScanScripts2(filePath);
                    //if (interestingExports.Count > 0)
                    //{
                    //    break;
                    //}
                    //if (resolveImports(filePath)) break;
                }
            }).ContinueWithOnUIThread(prevTask =>
            {
                pewpf.IsBusy = false;
                interestingExports.Add(new EntryStringPair(null, string.Join("\n", extraInfo)));
                var listDlg = new ListDialog(interestingExports, "Interesting Exports", "", pewpf)
                {
                    DoubleClickEntryHandler = entryItem =>
                    {
                        if (entryItem?.Entry is IEntry entryToSelect)
                        {
                            PackageEditorWPF p = new PackageEditorWPF();
                            p.Show();
                            p.LoadFile(entryToSelect.FileRef.FilePath, entryToSelect.UIndex);
                            p.Activate();
                            if (comparisonDict.TryGetValue($"{entryToSelect.UIndex} {entryToSelect.FileRef.FilePath}", out (byte[] original, byte[] newData)val))
                            {
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "original.bin"), val.original);
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "new.bin"), val.newData);
                            }
                        }
                    }
                };
                listDlg.Show();
            });

            #region extra scanning functions

            bool findClass(string filePath, string className, bool withBinary = false)
            {
                Debug.WriteLine($" {filePath}");
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    //if (!pcc.IsCompressed) return false;

                    var exports = pcc.Exports.Where(exp => !exp.IsDefaultObject && exp.IsA(className));
                    foreach (ExportEntry exp in exports)
                    {
                        try
                        {
                            //Debug.WriteLine($"{exp.UIndex}: {filePath}");
                            var originalData = exp.Data;
                            exp.WriteBinary(ObjectBinary.From(exp));
                            var newData = exp.Data;
                            if (!originalData.SequenceEqual(newData))
                            {
                                interestingExports.Add(exp);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "original.bin"), originalData);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "new.bin"), newData);
                                return(true);
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exception}"));
                            return(true);
                        }
                    }
                }

                return(false);
            }

            void findClassesWithBinary(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                    {
                        try
                        {
                            if (!foundClasses.Contains(exp.ClassName) && exp.propsEnd() < exp.DataSize)
                            {
                                if (ObjectBinary.From(exp) != null)
                                {
                                    foundClasses.Add(exp.ClassName);
                                }
                                else if (exp.GetBinaryData().Any(b => b != 0))
                                {
                                    foundClasses.Add(exp.ClassName);
                                    interestingExports.Add(exp);
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exp.UIndex}: {filePath}\n{exception}"));
                        }
                    }
                }
            }

            void ScanShaderCache(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    ExportEntry shaderCache = pcc.Exports.FirstOrDefault(exp => exp.ClassName == "ShaderCache");
                    if (shaderCache == null)
                    {
                        return;
                    }
                    int oldDataOffset = shaderCache.DataOffset;

                    try
                    {
                        MemoryStream binData = new MemoryStream(shaderCache.Data);
                        binData.JumpTo(shaderCache.propsEnd() + 1);

                        int nameList1Count = binData.ReadInt32();
                        binData.Skip(nameList1Count * 12);

                        int namelist2Count = binData.ReadInt32(); //namelist2
                        binData.Skip(namelist2Count * 12);

                        int shaderCount = binData.ReadInt32();
                        for (int i = 0; i < shaderCount; i++)
                        {
                            binData.Skip(24);
                            int nextShaderOffset = binData.ReadInt32() - oldDataOffset;
                            binData.Skip(14);
                            if (binData.ReadInt32() != 1111577667) //CTAB
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 4}: {filePath}"));
                                return;
                            }

                            binData.JumpTo(nextShaderOffset);
                        }

                        int vertexFactoryMapCount = binData.ReadInt32();
                        binData.Skip(vertexFactoryMapCount * 12);

                        int materialShaderMapCount = binData.ReadInt32();
                        for (int i = 0; i < materialShaderMapCount; i++)
                        {
                            binData.Skip(16);

                            int switchParamCount = binData.ReadInt32();
                            binData.Skip(switchParamCount * 32);

                            int componentMaskParamCount = binData.ReadInt32();
                            //if (componentMaskParamCount != 0)
                            //{
                            //    interestingExports.Add($"{i}: {filePath}");
                            //    return;
                            //}

                            binData.Skip(componentMaskParamCount * 44);

                            int normalParams = binData.ReadInt32();
                            if (normalParams != 0)
                            {
                                interestingExports.Add(new EntryStringPair(null, $"{i}: {filePath}"));
                                return;
                            }

                            binData.Skip(normalParams * 29);

                            int unrealVersion   = binData.ReadInt32();
                            int licenseeVersion = binData.ReadInt32();
                            if (unrealVersion != 684 || licenseeVersion != 194)
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 8}: {filePath}"));
                                return;
                            }

                            int nextMaterialShaderMapOffset = binData.ReadInt32() - oldDataOffset;
                            binData.JumpTo(nextMaterialShaderMapOffset);
                        }
                    }
                    catch (Exception exception)
                    {
                        Console.WriteLine(exception);
                        interestingExports.Add(new EntryStringPair(null, $"{filePath}\n{exception}"));
                    }
                }
            }

            void ScanScripts(string filePath)
            {
                using IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath);
                foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                {
                    try
                    {
                        if ((exp.ClassName == "State" || exp.ClassName == "Function") &&
                            ObjectBinary.From(exp) is UStruct uStruct)
                        {
                            byte[] data = exp.Data;
                            (_, List <BytecodeSingularToken> tokens) = Bytecode.ParseBytecode(uStruct.ScriptBytes, exp);
                            foreach (var token in tokens)
                            {
                                if (token.CurrentStack.Contains("UNKNOWN") || token.OpCodeString.Contains("UNKNOWN"))
                                {
                                    interestingExports.Add(exp);
                                }

                                if (unkOpcodes.Contains(token.OpCode))
                                {
                                    int    refUIndex = EndianReader.ToInt32(data, token.StartPos + 1, pcc.Endian);
                                    IEntry entry     = pcc.GetEntry(refUIndex);
                                    if (entry != null && (entry.ClassName == "ByteProperty"))
                                    {
                                        var info = unkOpcodesInfo[token.OpCode];
                                        info.Usages.Add(pcc.FilePath, exp.UIndex, token.StartPos);
                                        info.PropTypes.Add(refUIndex switch
                                        {
                                            0 => "Null",
                                            _ when entry != null => entry.ClassName,
                                            _ => "Invalid"
                                        });
                                        if (entry != null)
                                        {
                                            if (entry.Parent == exp)
                                            {
                                                info.PropLocations.Add("Local");
                                            }
                                            else if (entry.Parent == (exp.Parent.ClassName == "State" ? exp.Parent.Parent : exp.Parent))
                                            {
                                                info.PropLocations.Add("ThisClass");
                                            }
                                            else if (entry.Parent.ClassName == "Function")
                                            {
                                                info.PropLocations.Add("OtherFunction");
                                            }
                                            else if (exp.Parent.IsA(entry.Parent.ObjectName))
                                            {
                                                info.PropLocations.Add("AncestorClass");
                                            }
                                            else
                                            {
                                                info.PropLocations.Add("OtherClass");
                                            }
                                        }
                                    }
                                }
                            }
Exemple #7
0
        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);
        }