/// <summary>
        /// ME3Explorer intercepting function to build the token list. As tokens are read the tokens list will be updated.
        /// This method is used to prevent significant modifications to ReadNextInternal() (originally ReadNext)
        /// </summary>
        /// <returns></returns>
        public BytecodeToken ReadNext(bool dontAddToken = false)
        {
            ME1OpCodes b = (ME1OpCodes)_reader.ReadByte();

            _reader.BaseStream.Position--;

            if (OpCodesThatReturnNextToken.Contains(b))
            {
                BytecodeToken t = new BytecodeToken("", (int)_reader.BaseStream.Position);

                t.OpCode = b;
                ReadTokens.Add(t);
            }

            //if (b == ME1OpCodes.EX_Skip)
            //{
            //    Debugger.Break();
            //}

            //Debug.WriteLine("Read bytecode " + ((byte)b).ToString("X2") + " " + b + " at " + _reader.BaseStream.Position.ToString("X8"));
            BytecodeToken bct = ReadNextInternal();

            //Debug.WriteLine("Bytecode finished: " + ((byte)b).ToString("X2") + " " + b + " from " + _reader.BaseStream.Position.ToString("X8"));

            if (!OpCodesThatReturnNextToken.Contains(b))
            {
                bct.OpCode = b;
                ReadTokens.Add(bct);
            }
            return(bct);
        }
        private BytecodeToken ReadNextInternal()
        {
            int        readerpos = (int)_reader.BaseStream.Position;
            ME1OpCodes b         = (ME1OpCodes)_reader.ReadByte();

            switch (b)
            {
            case ME1OpCodes.EX_LocalVariable:
            case ME1OpCodes.EX_InstanceVariable:
            case ME1OpCodes.EX_NativeParm:
                return(ReadRef(r => r.ObjectName.Instanced));

            case ME1OpCodes.EX_DefaultVariable:
                return(ReadRef(r => $"Default.{r.ObjectName.Instanced}"));

            case ME1OpCodes.EX_Return:
            {
                BytecodeToken returnValue = ReadNext();
                return(new ReturnToken(returnValue, readerpos));
            }

            case ME1OpCodes.EX_Assert:
            {
                _reader.ReadInt16();
                _reader.ReadByte();
                return(WrapNextBytecode(c => new BytecodeToken($"assert({c})", readerpos)));
            }

            case ME1OpCodes.EX_Switch:
            {
                byte          b1         = _reader.ReadByte();
                BytecodeToken switchExpr = ReadNext();
                return(new SwitchToken(switchExpr.ToString(), switchExpr, readerpos));
            }

            case ME1OpCodes.EX_Case:
            {
                short offset = _reader.ReadInt16();
                if (offset == -1)
                {
                    return(new DefaultToken(readerpos));
                }
                BytecodeToken caseExpr = ReadNext();
                return(new CaseToken(caseExpr.ToString(), readerpos));
            }

            case ME1OpCodes.EX_Jump:
            {
                int offset = _reader.ReadInt16();
                return(new UncondJumpToken(offset, readerpos));
            }

            case ME1OpCodes.EX_JumpIfNot:
            {
                short         offset    = _reader.ReadInt16();
                BytecodeToken condition = ReadNext();
                if (IsInvalid(condition))
                {
                    return(WrapErrToken("if (!" + condition, condition));
                }
                return(new JumpIfNotToken(offset, condition, readerpos));
            }

            case ME1OpCodes.EX_LabelTable:
            {
                var token = new LabelTableToken(readerpos);
                while (true)
                {
                    string labelName = ReadName();
                    if (labelName == "None")
                    {
                        break;
                    }
                    int offset = _reader.ReadInt32();
                    token.AddLabel(labelName, offset);
                }
                return(token);
            }

            case ME1OpCodes.EX_GotoLabel:
                return(WrapNextBytecode(op => Token("goto " + op, readerpos)));

            case ME1OpCodes.EX_Self:
                return(Token("self", readerpos));

            case ME1OpCodes.EX_Skip:
                _reader.ReadInt16();
                //Returning readnext causes a new token to be read
                return(ReadNext());

            case ME1OpCodes.EX_EatReturnValue:
                _reader.ReadInt32();
                return(ReadNext());

            case ME1OpCodes.EX_Nothing:
                return(new NothingToken(readerpos));

            case ME1OpCodes.EX_Stop:
                _reader.ReadInt16();
                return(new NothingToken(readerpos));

            case ME1OpCodes.EX_IntZero:
                return(Token("0", readerpos));

            case ME1OpCodes.EX_IntOne:
                return(Token("1", readerpos));

            case ME1OpCodes.EX_True:
                return(Token("true", readerpos));

            case ME1OpCodes.EX_False:
                return(Token("false", readerpos));

            case ME1OpCodes.EX_NoObject:
            case ME1OpCodes.EX_EmptyDelegate:
                return(Token("None", readerpos));

            case ME1OpCodes.EX_Let:
            case ME1OpCodes.EX_LetBool:
            case ME1OpCodes.EX_LetDelegate:
                BytecodeToken lhs = ReadNext();
                if (IsInvalid(lhs))
                {
                    return(lhs);
                }
                BytecodeToken rhs = ReadNext();
                if (IsInvalid(rhs))
                {
                    return(WrapErrToken(lhs + " = " + rhs, rhs));
                }
                return(Token(lhs + " = " + rhs, readerpos));

            case ME1OpCodes.EX_IntConst:
                return(Token(_reader.ReadInt32().ToString(), readerpos));

            case ME1OpCodes.EX_FloatConst:
                return(Token(_reader.ReadSingle().ToString(), readerpos));

            case ME1OpCodes.EX_StringConst:
            {
                var s = ReadAsciiz().Replace("\n", "\\n").Replace("\t", "\\t");
                return(Token($"\"{s}\"", readerpos));
            }

            case ME1OpCodes.EX_ByteConst:
            case ME1OpCodes.EX_IntConstByte:
                return(Token(_reader.ReadByte().ToString(), readerpos));

            case ME1OpCodes.EX_ObjectConst:
            {
                int objectIndex = _reader.ReadInt32();
                var item        = _package.GetEntry(objectIndex);
                if (item == null)
                {
                    return(ErrToken("Unresolved class item " + objectIndex));
                }
                return(Token($"{item.ClassName}'{item.ObjectName.Instanced}'", readerpos));
            }

            case ME1OpCodes.EX_NameConst:
                return(Token($"'{ReadName()}'", readerpos));

            case ME1OpCodes.EX_EndFunctionParms:
                return(new EndParmsToken(")", readerpos));

            case ME1OpCodes.EX_ClassContext:
            case ME1OpCodes.EX_Context:
            {
                var context = ReadNext();
                if (IsInvalid(context))
                {
                    return(context);
                }
                int exprSize = _reader.ReadInt16();
                int bSize    = _reader.ReadByte();
                var value    = ReadNext();
                if (IsInvalid(value))
                {
                    return(WrapErrToken($"{context}.{value}", value));
                }
                return(Token($"{context}.{value}", readerpos));
            }

            case ME1OpCodes.EX_InterfaceContext:
                return(ReadNext());

            case ME1OpCodes.EX_FinalFunction:
            {
                int functionIndex = _reader.ReadInt32();
                var item          = _package.GetEntry(functionIndex);
                if (item == null)
                {
                    return(ErrToken("Unresolved function item " + item));
                }
                string functionName = item.ObjectName.Instanced;
                return(ReadCall(functionName));
            }

            case ME1OpCodes.EX_PrimitiveCast:
            {
                var prefix = _reader.ReadByte();
                var v      = ReadNext();
                return(v);
            }

            case ME1OpCodes.EX_VirtualFunction:
                return(ReadCall(ReadName()));

            case ME1OpCodes.EX_GlobalFunction:
                return(ReadCall("Global." + ReadName()));

            case ME1OpCodes.EX_BoolVariable:
                return(ReadNext());

            case ME1OpCodes.EX_ByteToInt:
                int objectRefIdx = _reader.ReadInt32();
                if (_package.IsEntry(objectRefIdx))
                {
                    return(Token($"ByteToInt({_package.getObjectName(objectRefIdx)})", readerpos));
                }
                else
                {
                    return(Token($"ByteToInt(Unknown reference {objectRefIdx})", readerpos));
                }

            case ME1OpCodes.EX_DynamicCast:
            {
                int typeIndex = _reader.ReadInt32();
                var item      = _package.GetEntry(typeIndex);
                return(WrapNextBytecode(op => Token($"{item.ObjectName.Instanced}({op})", readerpos)));
            }

            case ME1OpCodes.EX_Metacast:
            {
                int typeIndex = _reader.ReadInt32();
                var item      = _package.GetEntry(typeIndex);
                if (item == null)
                {
                    return(ErrToken("Unresolved class item " + typeIndex));
                }
                return(WrapNextBytecode(op => Token($"Class<{item.ObjectName.Instanced}>({op})", readerpos)));
            }

            case ME1OpCodes.EX_StructMember:
            {
                var field      = ReadRef();
                var structType = ReadRef();
                int wSkip      = _reader.ReadInt16();
                var token      = ReadNext();
                if (IsInvalid(token))
                {
                    return(token);
                }
                return(Token($"{token}.{field.ObjectName.Instanced}", readerpos));
            }

            case ME1OpCodes.EX_ArrayElement:
            case ME1OpCodes.EX_DynArrayElement:
            {
                var index = ReadNext();
                if (IsInvalid(index))
                {
                    return(index);
                }
                var array = ReadNext();
                if (IsInvalid(array))
                {
                    return(array);
                }
                return(Token($"{array}[{index}]", readerpos));
            }

            case ME1OpCodes.EX_DynArrayLength:
                return(WrapNextBytecode(op => Token($"{op}.Length", readerpos)));

            case ME1OpCodes.EX_StructCmpEq:
                return(CompareStructs("=="));

            case ME1OpCodes.EX_StructCmpNe:
                return(CompareStructs("!="));

            case ME1OpCodes.EX_EndOfScript:
                return(new EndOfScriptToken(readerpos));

            case ME1OpCodes.EX_EmptyParmValue:
            case ME1OpCodes.EX_GoW_DefaultValue:
                return(new DefaultValueToken("", readerpos));

            case ME1OpCodes.EX_DefaultParmValue:
            {
                var size             = _reader.ReadInt16();
                var offset           = _reader.BaseStream.Position;
                var defaultValueExpr = ReadNext();
                _reader.BaseStream.Position = offset + size;
                return(new DefaultParamValueToken(defaultValueExpr.ToString(), readerpos));
            }

            case ME1OpCodes.EX_LocalOutVariable:
                int valueIndex  = _reader.ReadInt32();
                var packageItem = _package.GetEntry(valueIndex);
                if (packageItem == null)
                {
                    return(ErrToken("Unresolved package item " + packageItem));
                }
                return(Token(packageItem.ObjectName.Instanced, readerpos));

            case ME1OpCodes.EX_Iterator:
                var expr    = ReadNext();
                int loopEnd = _reader.ReadInt16();
                if (IsInvalid(expr))
                {
                    return(WrapErrToken("foreach " + expr, expr));
                }
                return(new ForeachToken(loopEnd, expr, readerpos));

            case ME1OpCodes.EX_IteratorPop:
                return(new IteratorPopToken(readerpos));

            case ME1OpCodes.EX_IteratorNext:
                return(new IteratorNextToken(readerpos));

            case ME1OpCodes.EX_New:
                var outer = ReadNext();
                if (IsInvalid(outer))
                {
                    return(outer);
                }
                var name = ReadNext();
                if (IsInvalid(name))
                {
                    return(name);
                }
                var flags = ReadNext();
                if (IsInvalid(flags))
                {
                    return(flags);
                }
                var cls = ReadNext();
                if (IsInvalid(cls))
                {
                    return(cls);
                }
                return(Token($"new({JoinTokens(outer, name, flags, cls)})", readerpos));

            case ME1OpCodes.EX_VectorConst:
                var f1 = _reader.ReadSingle();
                var f2 = _reader.ReadSingle();
                var f3 = _reader.ReadSingle();
                return(Token($"vect({f1},{f2},{f3})", readerpos));

            case ME1OpCodes.EX_RotationConst:
                var i1 = _reader.ReadInt32();
                var i2 = _reader.ReadInt32();
                var i3 = _reader.ReadInt32();
                return(Token($"rot({i1},{i2},{i3})", readerpos));

            case ME1OpCodes.EX_InterfaceCast:
            {
                var interfaceName = ReadRef();
                return(WrapNextBytecode(op => Token($"{interfaceName.ObjectName.Instanced}({op})", readerpos)));
            }

            case ME1OpCodes.EX_Conditional:
            {
                var condition = ReadNext();
                if (IsInvalid(condition))
                {
                    return(condition);
                }
                var trueSize = _reader.ReadInt16();
                var pos      = _reader.BaseStream.Position;
                var truePart = ReadNext();
                if (IsInvalid(truePart))
                {
                    return(WrapErrToken($"{condition} ? {truePart}", truePart));
                }
                if (_reader.BaseStream.Position != pos + trueSize)
                {
                    return(ErrToken("conditional true part size mismatch"));
                }
                var falseSize = _reader.ReadInt16();
                pos = _reader.BaseStream.Position;
                var falsePart = ReadNext();
                if (IsInvalid(truePart))
                {
                    return(WrapErrToken($"{condition} ? {truePart} : {falsePart}", falsePart));
                }
                Debug.Assert(_reader.BaseStream.Position == pos + falseSize);
                return(Token($"{condition} ? {truePart} : {falsePart}", readerpos));
            }

            case ME1OpCodes.EX_DynArrayFind:
                return(ReadDynArray1ArgMethod("Find"));

            case ME1OpCodes.EX_DynArrayFindStruct:
                return(ReadDynArray2ArgMethod("Find", true));

            case ME1OpCodes.EX_DynArrayRemove:
                return(ReadDynArray2ArgMethod("Remove", false));

            case ME1OpCodes.EX_DynArrayInsert:
                return(ReadDynArray2ArgMethod("Insert", false));

            case ME1OpCodes.EX_DynArrayAddItem:
                return(ReadDynArray1ArgMethod("AddItem"));

            case ME1OpCodes.EX_DynArrayRemoveItem:
                return(ReadDynArray1ArgMethod("RemoveItem"));

            case ME1OpCodes.EX_DynArrayInsertItem:
                return(ReadDynArray2ArgMethod("InsertItem", true));

            case ME1OpCodes.EX_DynArrayIterator:
            {
                var array = ReadNext();
                if (IsInvalid(array))
                {
                    return(array);
                }
                var iteratorVar = ReadNext();
                if (IsInvalid(iteratorVar))
                {
                    return(iteratorVar);
                }
                _reader.ReadInt16();
                var endOffset = _reader.ReadInt16();
                return(new ForeachToken(endOffset, array, iteratorVar, readerpos));
            }

            case ME1OpCodes.EX_DelegateProperty:
            case ME1OpCodes.EX_InstanceDelegate:
                return(Token(ReadName(), readerpos));

            case ME1OpCodes.EX_DelegateFunction:
            {
                var receiver = ReadNext();
                if (IsInvalid(receiver))
                {
                    return(receiver);
                }
                var methodName = ReadName();
                if (receiver.ToString().StartsWith("__") && receiver.ToString().EndsWith("__Delegate"))
                {
                    return(ReadCall(methodName));
                }
                return(ReadCall(receiver + "." + methodName));
            }

            case ME1OpCodes.EX_EqualEqual_DelDel:
            case ME1OpCodes.EX_EqualEqual_DelFunc:
                return(CompareDelegates("=="));

            case ME1OpCodes.EX_NotEqual_DelDel:
                return(CompareDelegates("!="));

            default:
                if ((int)b >= 0x60)
                {
                    return(ReadNativeCall((byte)b));
                }
                return(ErrToken("// unknown bytecode " + ((byte)b).ToString("X2"), (int)b));
            }
        }
Exemple #3
0
        private void hb1_SelectionChanged(object sender, EventArgs e)
        {
            if (!TokenChanging)
            {
                HexBoxSelectionChanging = true;
                int start = (int)ScriptEditor_Hexbox.SelectionStart;
                int len   = (int)ScriptEditor_Hexbox.SelectionLength;
                int size  = (int)ScriptEditor_Hexbox.ByteProvider.Length;
                //TODO: Optimize this so this is only called when data has changed
                byte[] currentData = (ScriptEditor_Hexbox.ByteProvider as DynamicByteProvider).Bytes.ToArray();
                try
                {
                    if (currentData != null && start != -1 && start < size)
                    {
                        string s = $"Byte: {currentData[start]}"; //if selection is same as size this will crash.
                        if (start <= currentData.Length - 4)
                        {
                            int val = BitConverter.ToInt32(currentData, start);
                            s += $", Int: {val}";
                            s += $", Float: {BitConverter.ToSingle(currentData, start)}";
                            if (CurrentLoadedExport.FileRef.isName(val))
                            {
                                s += $", Name: {CurrentLoadedExport.FileRef.getNameEntry(val)}";
                            }
                            if (CurrentLoadedExport.FileRef.Game == MEGame.ME1)
                            {
                                ME1OpCodes m = (ME1OpCodes)currentData[start];
                                s += $", OpCode: {m}";
                            }

                            if (CurrentLoadedExport.FileRef.getEntry(val) is IEntry ent)
                            {
                                string type = ent is IExportEntry ? "Export" : "Import";
                                if (ent.ObjectName == CurrentLoadedExport.ObjectName)
                                {
                                    s += $", {type}: {ent.GetFullPath}";
                                }
                                else
                                {
                                    s += $", {type}: {ent.ObjectName}";
                                }
                            }
                        }
                        s += $" | Start=0x{start.ToString("X8")} ";
                        if (len > 0)
                        {
                            s += $"Length=0x{len.ToString("X8")} ";
                            s += $"End=0x{(start + len - 1).ToString("X8")}";
                        }
                        StatusBar_LeftMostText.Text = s;
                    }
                    else
                    {
                        StatusBar_LeftMostText.Text = "Nothing Selected";
                    }
                }
                catch (Exception)
                {
                }

                //Find which decompiled script block the cursor belongs to
                ListBox        selectedBox      = null;
                List <ListBox> allBoxesToUpdate = new List <ListBox>(new ListBox[] { Function_ListBox, Function_Header, Function_Footer });
                if (start >= 0x20 && start < CurrentLoadedExport.DataSize - 6)
                {
                    Token token = null;
                    foreach (object o in DecompiledScriptBlocks)
                    {
                        if (o is Token x && start >= x.pos && start < (x.pos + x.raw.Length))
                        {
                            token = x;
                            break;
                        }
                    }
                    if (token != null)
                    {
                        Function_ListBox.SelectedItem = token;
                        Function_ListBox.ScrollIntoView(token);
                    }

                    BytecodeSingularToken bst = TokenList.FirstOrDefault(x => x.StartPos == start);
                    if (bst != null)
                    {
                        Tokens_ListBox.SelectedItem = bst;
                        Tokens_ListBox.ScrollIntoView(bst);
                    }
                    selectedBox = Function_ListBox;
                }
                if (start >= 0x0C && start < 0x20)
                {
                    //header
                    int index = (start - 0xC) / 4;
                    Function_Header.SelectedIndex = index;
                    selectedBox = Function_Header;
                }
                if (start > CurrentLoadedExport.DataSize - 6)
                {
                    //footer
                    //yeah yeah I know this is very specific code.
                    if (start == CurrentLoadedExport.DataSize - 6 || start == CurrentLoadedExport.DataSize - 5)
                    {
                        Function_Footer.SelectedIndex = 0;
                    }
                    else
                    {
                        Function_Footer.SelectedIndex = 1;
                    }
                    selectedBox = Function_Footer;
                }

                //deselect the other boxes
                if (selectedBox != null)
                {
                    allBoxesToUpdate.Remove(selectedBox);
                }
                allBoxesToUpdate.ForEach(x => x.SelectedIndex = -1);
                HexBoxSelectionChanging = false;
            }
        }