public static bool RandomizeExport(ExportEntry arg1, RandomizationOption arg2) { // Write debug code here //var hi = arg1.InstancedFullPath; if (arg1.ClassName == "Function") { var unFunc = UE3FunctionReader.ReadFunction(arg1); TextBuilder tb = new TextBuilder(); unFunc.Decompile(tb, false, false); } return(true); }
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); }
private void StartFunctionScan(byte[] data) { DecompiledScriptBlocks.ClearEx(); TokenList.ClearEx(); ScriptHeaderBlocks.ClearEx(); ScriptFooterBlocks.ClearEx(); if (CurrentLoadedExport.FileRef.Game == MEGame.ME3) { var func = new ME3Explorer.Unreal.Classes.Function(data, CurrentLoadedExport, CurrentLoadedExport.ClassName == "State" ? Convert.ToInt32(StartOffset_Changer.Text) : 32); func.ParseFunction(); DecompiledScriptBlocks.Add(func.GetSignature()); DecompiledScriptBlocks.AddRange(func.ScriptBlocks); TokenList.AddRange(func.SingularTokenList); int pos = 12; var functionSuperclass = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptHeaderBlocks.Add(new ScriptHeaderItem("Function superclass", functionSuperclass, pos, functionSuperclass != 0 ? CurrentLoadedExport.FileRef.getEntry(functionSuperclass) : null)); pos += 4; var nextItemCompilingChain = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptHeaderBlocks.Add(new ScriptHeaderItem("Next item in loading chain", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? CurrentLoadedExport : null)); pos += 4; nextItemCompilingChain = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptHeaderBlocks.Add(new ScriptHeaderItem("Children Probe Start", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? CurrentLoadedExport : null)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Unknown 2 (Memory size?)", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Size", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos = CurrentLoadedExport.Data.Length - 6; string flagStr = func.GetFlags(); ScriptFooterBlocks.Add(new ScriptHeaderItem("Native Index", BitConverter.ToInt16(CurrentLoadedExport.Data, pos), pos)); pos += 2; ScriptFooterBlocks.Add(new ScriptHeaderItem("Flags", $"0x{BitConverter.ToInt32(CurrentLoadedExport.Data, pos).ToString("X8")} {func.GetFlags().Substring(6)}", pos)); } else if (CurrentLoadedExport.FileRef.Game == MEGame.ME1) { //Header int pos = 16; var nextItemCompilingChain = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptHeaderBlocks.Add(new ScriptHeaderItem("Next item in loading chain", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? CurrentLoadedExport : null)); pos += 8; nextItemCompilingChain = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptHeaderBlocks.Add(new ScriptHeaderItem("Children Probe Start", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? CurrentLoadedExport : null)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Unknown 1 (??)", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Unknown 2 (Line??)", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Unknown 3 (TextPos??)", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos += 4; ScriptHeaderBlocks.Add(new ScriptHeaderItem("Script Size", BitConverter.ToInt32(CurrentLoadedExport.Data, pos), pos)); pos += 4; BytecodeStart = pos; var func = UE3FunctionReader.ReadFunction(CurrentLoadedExport); func.Decompile(new TextBuilder(), false); //parse bytecode bool defined = func.HasFlag("Defined"); if (defined) { DecompiledScriptBlocks.Add(func.FunctionSignature + " {"); } else { DecompiledScriptBlocks.Add(func.FunctionSignature); } for (int i = 0; i < func.Statements.statements.Count; i++) { Statement s = func.Statements.statements[i]; DecompiledScriptBlocks.Add(s); if (s.Reader != null && i == 0) { //Add tokens read from statement. All of them point to the same reader, so just do only the first one. TokenList.AddRange(s.Reader.ReadTokens.Select(x => x.ToBytecodeSingularToken(pos)).OrderBy(x => x.StartPos)); } } if (defined) { DecompiledScriptBlocks.Add("}"); } //Footer pos = CurrentLoadedExport.Data.Length - (func.HasFlag("Net") ? 17 : 15); string flagStr = func.GetFlags(); ScriptFooterBlocks.Add(new ScriptHeaderItem("Native Index", BitConverter.ToInt16(CurrentLoadedExport.Data, pos), pos)); pos += 2; ScriptFooterBlocks.Add(new ScriptHeaderItem("Operator Precedence", CurrentLoadedExport.Data[pos], pos)); pos++; int functionFlags = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptFooterBlocks.Add(new ScriptHeaderItem("Flags", $"0x{functionFlags.ToString("X8")} {flagStr}", pos)); pos += 4; //if ((functionFlags & func._flagSet.GetMask("Net")) != 0) //{ //ScriptFooterBlocks.Add(new ScriptHeaderItem("Unknown 1 (RepOffset?)", BitConverter.ToInt16(CurrentLoadedExport.Data, pos), pos)); //pos += 2; //} int friendlyNameIndex = BitConverter.ToInt32(CurrentLoadedExport.Data, pos); ScriptFooterBlocks.Add(new ScriptHeaderItem("Friendly Name Table Index", $"0x{friendlyNameIndex.ToString("X8")} {CurrentLoadedExport.FileRef.getNameEntry(friendlyNameIndex)}", pos)); pos += 4; ScriptFooterBlocks.Add(new ScriptHeaderItem("Unknown 2", $"0x{BitConverter.ToInt16(CurrentLoadedExport.Data, pos).ToString("X4")}", pos)); pos += 4; //ME1Explorer.Unreal.Classes.Function func = new ME1Explorer.Unreal.Classes.Function(data, CurrentLoadedExport.FileRef as ME1Package); //try //{ // Function_TextBox.Text = func.ToRawText(); //} //catch (Exception e) //{ // Function_TextBox.Text = "Error parsing function: " + e.Message; //} } else { //Function_TextBox.Text = "Parsing UnrealScript Functions for this game is not supported."; } }
public void TestFunctionParsing() { // This method is essentially test of the BytecodeEditor parser with the actual ui logic all removed GlobalTest.Init(); // Loads compressed packages, save them uncompressed. Load package, save re-compressed, compare results var packagesPath = GlobalTest.GetTestPackagesDirectory(); //var packages = Directory.GetFiles(packagesPath, "*.*", SearchOption.AllDirectories); var packages = Directory.GetFiles(packagesPath, "*.*", SearchOption.AllDirectories); foreach (var p in packages) { if (p.RepresentsPackageFilePath()) { (var game, var platform) = GlobalTest.GetExpectedTypes(p); // Use to skip //if (platform != MEPackage.GamePlatform.Xenon) continue; //if (game != MEGame.ME1) continue; Console.WriteLine($"Opening package {p}"); // Do not use package caching in tests var originalLoadedPackage = MEPackageHandler.OpenMEPackage(p, forceLoadFromDisk: true); foreach (var export in originalLoadedPackage.Exports.Where(x => x.ClassName == "Function" || x.ClassName == "State")) { //Console.WriteLine($" >> Decompiling {export.InstancedFullPath}"); var data = export.Data; var funcBin = ObjectBinary.From <UFunction>(export); //parse it out if (export.FileRef.Game == MEGame.ME3 || export.FileRef.Platform == MEPackage.GamePlatform.PS3) { var func = new Function(data, export); func.ParseFunction(); func.GetSignature(); if (export.ClassName == "Function") { var nativeBackOffset = export.FileRef.Game < MEGame.ME3 ? 7 : 6; var pos = data.Length - nativeBackOffset; string flagStr = func.GetFlags(); var nativeIndex = EndianReader.ToInt16(data, pos, export.FileRef.Endian); pos += 2; var flags = EndianReader.ToInt16(data, pos, export.FileRef.Endian); } else { //State //parse remaining var footerstartpos = 0x20 + funcBin.ScriptStorageSize; var footerdata = data.Slice(footerstartpos, (int)data.Length - (footerstartpos)); var fpos = 0; //ScriptFooterBlocks.Add(new ScriptHeaderItem("Probemask?", "??", fpos + footerstartpos) { length = 8 }); fpos += 0x8; //ScriptFooterBlocks.Add(new ScriptHeaderItem("Unknown 8 FF's", "??", fpos + footerstartpos) { length = 8 }); fpos += 0x8; //ScriptFooterBlocks.Add(new ScriptHeaderItem("Label Table Offset", "??", fpos + footerstartpos) { length = 2 }); fpos += 0x2; var stateFlagsBytes = footerdata.Slice(fpos, 0x4); var stateFlags = (StateFlags)EndianReader.ToInt32(stateFlagsBytes, 0, export.FileRef.Endian); var names = stateFlags.ToString().Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); fpos += 0x4; var numMappedFunctions = EndianReader.ToInt32(footerdata, fpos, export.FileRef.Endian); fpos += 4; for (int i = 0; i < numMappedFunctions; i++) { var name = EndianReader.ToInt32(footerdata, fpos, export.FileRef.Endian); var uindex = EndianReader.ToInt32(footerdata, fpos + 8, export.FileRef.Endian); var funcMapText = $"{export.FileRef.GetNameEntry(name)} => {export.FileRef.GetEntry(uindex)?.FullPath}()"; fpos += 12; } } } else if (export.FileRef.Game == MEGame.ME1 || export.FileRef.Game == MEGame.ME2) { //Header int pos = 16; var nextItemCompilingChain = EndianReader.ToInt32(data, pos, export.FileRef.Endian); //ScriptHeaderBlocks.Add(new ScriptHeaderItem("Next item in loading chain", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? export : null)); pos += 8; nextItemCompilingChain = EndianReader.ToInt32(data, pos, export.FileRef.Endian); //ScriptHeaderBlocks.Add(new ScriptHeaderItem("Children Probe Start", nextItemCompilingChain, pos, nextItemCompilingChain > 0 ? export : null)); pos += 8; var line = EndianReader.ToInt32(data, pos, export.FileRef.Endian); //ScriptHeaderBlocks.Add(new ScriptHeaderItem("Line", EndianReader.ToInt32(data, pos, export.FileRef.Endian), pos)); pos += 4; //EndianReader.ToInt32(data, pos, export.FileRef.Endian) //ScriptHeaderBlocks.Add(new ScriptHeaderItem("TextPos", EndianReader.ToInt32(data, pos, export.FileRef.Endian), pos)); pos += 4; int scriptSize = EndianReader.ToInt32(data, pos, export.FileRef.Endian); //ScriptHeaderBlocks.Add(new ScriptHeaderItem("Script Size", scriptSize, pos)); pos += 4; var BytecodeStart = pos; var func = export.ClassName == "State" ? UE3FunctionReader.ReadState(export, data) : UE3FunctionReader.ReadFunction(export, data); func.Decompile(new TextBuilder(), false); //parse bytecode bool defined = func.HasFlag("Defined"); //if (defined) //{ // DecompiledScriptBlocks.Add(func.FunctionSignature + " {"); //} //else //{ // //DecompiledScriptBlocks.Add(func.FunctionSignature); //} for (int i = 0; i < func.Statements.statements.Count; i++) { Statement s = func.Statements.statements[i]; s.SetPaddingForScriptSize(scriptSize); if (s.Reader != null && i == 0) { //Add tokens read from statement. All of them point to the same reader, so just do only the first one. s.Reader.ReadTokens.Select(x => x.ToBytecodeSingularToken(pos)).OrderBy(x => x.StartPos); } } //if (defined) //{ // DecompiledScriptBlocks.Add("}"); //} //Footer pos = data.Length - (func.HasFlag("Net") ? 17 : 15); string flagStr = func.GetFlags(); //ScriptFooterBlocks.Add(new ScriptHeaderItem("Native Index", EndianReader.ToInt16(data, pos, export.FileRef.Endian), pos)); pos += 2; //ScriptFooterBlocks.Add(new ScriptHeaderItem("Operator Precedence", data[pos], pos)); pos++; int functionFlags = EndianReader.ToInt32(data, pos, export.FileRef.Endian); //ScriptFooterBlocks.Add(new ScriptHeaderItem("Flags", $"0x{functionFlags:X8} {flagStr}", pos)); pos += 4; //if ((functionFlags & func._flagSet.GetMask("Net")) != 0) //{ //ScriptFooterBlocks.Add(new ScriptHeaderItem("Unknown 1 (RepOffset?)", EndianReader.ToInt16(data, pos, export.FileRef.Endian), pos)); //pos += 2; //} int friendlyNameIndex = EndianReader.ToInt32(data, pos, export.FileRef.Endian); var friendlyName = export.FileRef.GetNameEntry(friendlyNameIndex); //ScriptFooterBlocks.Add(new ScriptHeaderItem("Friendly Name", Pcc.GetNameEntry(friendlyNameIndex), pos) { length = 8 }); pos += 8; //ME1Explorer.Unreal.Classes.Function func = new ME1Explorer.Unreal.Classes.Function(data, export.FileRef as ME1Package); //try //{ // Function_TextBox.Text = func.ToRawText(); //} //catch (Exception e) //{ // Function_TextBox.Text = "Error parsing function: " + e.Message; //} } else { //Function_TextBox.Text = "Parsing UnrealScript Functions for this game is not supported."; } } //} } } }
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 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); }