/// <summary> /// Binds an opcode to a function. /// </summary> /// <typeparam name="Tfunc">A function delegate to bind.</typeparam> /// <param name="ID"></param> /// <param name="Argc"></param> /// <param name="Function"></param> public void Bind <Tfunc>(ushort ID, int Argc, Tfunc Function) { Type T = typeof(Tfunc); ScriptFunctionMeta MetaFunction = new ScriptFunctionMeta(); if (T == typeof(ScriptFunction)) { MetaFunction.Function = (ScriptFunction)Convert.ChangeType(Function, typeof(ScriptFunction)); } else { MetaFunction.BooleanFunction = (ScriptFunctionBoolean)Convert.ChangeType(Function, typeof(ScriptFunctionBoolean)); } MetaFunction.Arguments = Argc; functions.Add(ID, MetaFunction); /*functions.insert( * * { * id, * { * * [=] (ScriptArguments& args) { script_bind.do_unpacked_call(function, args); }, * argc, "opcode", "" * } * });*/ }
/// <summary> /// Finds the function based on an opcode (ID). /// </summary> /// <param name="ID">The opcode (ID) of a function.</param> /// <param name="Func">The function to find.</param> /// <returns></returns> public bool FindOpcode(ushort ID, out ScriptFunctionMeta Func) { try { var it = functions[ID]; } catch (KeyNotFoundException) { Func = new ScriptFunctionMeta(); return(false); } Func = functions[ID]; return(true); }
private void ExecuteThread(SCMThread Thread, int MSPassed) { ///TODO: Fully implement this. /*auto player = state->world->getPlayer(); * * if (player) * {*/ if (Thread.IsMission && Thread.DeathOrArrestCheck /*&& * (player->isWasted() || player->isBusted())*/) { Thread.WastedOrBusted = true; Thread.StackDepth = 0; Thread.ProgramCounter = Thread.Calls[(int)Thread.StackDepth]; } //} // There is 02a1 opcode that is used only during "Kingdom Come", which // basically acts like a wait command, but waiting time can be skipped // by pressing 'X'? PS2 button if (Thread.AllowWaitSkip /*&& getState()->input[0].pressed(GameInputState::Jump)*/) { Thread.WakeCounter = 0; Thread.AllowWaitSkip = false; } if (Thread.WakeCounter > 0) { Thread.WakeCounter = Math.Max(Thread.WakeCounter - MSPassed, 0); } if (Thread.WakeCounter > 0) { return; } while (Thread.WakeCounter == 0) { var PC = Thread.ProgramCounter; var Opcode = file.Read <ushort>(PC); bool IsNegatedConditional = ((Opcode & SCM_NEGATE_CONDITIONAL_MASK) == SCM_NEGATE_CONDITIONAL_MASK); Opcode = (ushort)(Opcode & ~SCM_NEGATE_CONDITIONAL_MASK); ScriptFunctionMeta FoundCode; if (!module.FindOpcode(Opcode, out FoundCode)) { throw new IllegalInstruction <Exception>(Opcode, PC, Thread.Name); } ScriptFunctionMeta Code = FoundCode; PC += sizeof(ushort); List <SCMOpcodeParameter> Parameters = new List <SCMOpcodeParameter>(); bool HasExtraParameters = Code.Arguments < 0; var RequiredParams = Math.Abs(Code.Arguments); for (int p = 0; p < RequiredParams || HasExtraParameters; ++p) { //byte was originally SCMByte ... might have to change to char. var type_r = file.Read <byte>(PC); var type = (SCMType)type_r; if (type_r > 42) { // for implicit strings, we need the byte we just read. type = SCMType.TString; } else { PC += sizeof(byte); } //TODO: Is this correct? //Parameters.push_back(SCMOpcodeParameter{ type, { 0} }); Parameters.Add(new SCMOpcodeParameter(type)); switch (type) { case SCMType.EndOfArgList: HasExtraParameters = false; break; case SCMType.TInt8: lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); Param.Integer = file.Read <byte>(PC); Parameters[Parameters.Count - 1] = Param; } PC += sizeof(byte); break; case SCMType.TInt16: lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); Param.Integer = file.Read <short>(PC); Parameters[Parameters.Count - 1] = Param; } PC += sizeof(byte) * 2; break; case SCMType.TGlobal: { var v = file.Read <ushort>(PC); lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); //TODO: This was originally GlobalData.data() + v... Param.GlobalPtr = (IntPtr)globalData.ToArray().Length + v * SCM_VARIABLE_SIZE; Parameters[Parameters.Count - 1] = Param; } if (v >= file.GlobalsSize) { Debug.WriteLine("ERROR: ScriptMachine.cs: Global out of bounds! " + v.ToString() + " " + file.GlobalsSize.ToString()); } PC += sizeof(byte) * 2; } break; case SCMType.TLocal: { var v = file.Read <ushort>(PC); lock (Parameters) { SCMOpcodeParameter Param = Parameters.LastOrDefault(); //Not sure if this is correct, was originally Locals.data() + v... Param.GlobalPtr = (IntPtr)Thread.Locals.ToArray().Length + v * SCM_VARIABLE_SIZE; Parameters[Parameters.Count - 1] = Param; } if (v >= SCM_THREAD_LOCAL_SIZE) { Debug.WriteLine("Scriptmachine.CS: Local out of bounds!"); } PC += sizeof(byte) * 2; } break; case SCMType.TInt32: lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); Param.Integer = file.Read <int>(PC); Parameters[Parameters.Count - 1] = Param; } PC += sizeof(byte) * 4; break; case SCMType.TString: lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); char[] Str = Param.Str.ToArray(); //Copy a string from the file data to the string. Array.Copy(file.Data.ToArray(), (int)PC, Str, 0, 8); Param.Str = new string(Str); Parameters[Parameters.Count - 1] = Param; } PC += sizeof(byte) * 8; break; case SCMType.TFloat16: lock (Parameters) //Thread safety { SCMOpcodeParameter Param = Parameters.LastOrDefault(); Param.Real = file.Read <short>(PC) / 16f; Parameters[Parameters.Count - 1] = Param; } PC += sizeof(byte) * 2; break; default: throw new UnknownType <Exception>((char)type, PC, Thread.Name); } ; ScriptArguments Sca = new ScriptArguments(Parameters, Thread, this);