using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Crtr.Event; using System; using System.Collections.Generic; using System.Globalization; using UnityEngine; namespace Cryville.Crtr { public class PdtEvaluator : PdtEvaluatorBase { static readonly Dictionary _shortops = new Dictionary(); readonly Dictionary _ctxops = new Dictionary(); readonly byte[] _numbuf = new byte[4]; static readonly int _var_w = IdentifierManager.SharedInstance.Request("w"); static readonly int _var_h = IdentifierManager.SharedInstance.Request("h"); static readonly int _var_true = IdentifierManager.SharedInstance.Request("true"); static readonly int _var_false = IdentifierManager.SharedInstance.Request("false"); protected override void GetVariable(int name, out int type, out byte[] value) { if (name == _var_w) { LoadNum(ChartPlayer.hitRect.width); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_h) { LoadNum(ChartPlayer.hitRect.height); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_true) { LoadNum(1); type = PdtInternalType.Number; value = _numbuf; } else if (name == _var_false) { LoadNum(0); type = PdtInternalType.Number; value = _numbuf; } else { var id = new Identifier(name); PropSrc prop; if (ContextEvent != null && ContextEvent.PropSrcs.TryGetValue(name, out prop)) { prop.Get(out type, out value); } else if (ContextState != null && ChartPlayer.motionRegistry.ContainsKey(id)) { var vec = ContextState.GetRawValue(id); new VectorSrc(() => vec).Get(out type, out value); } else if (ContextJudge != null && ContextJudge.TryGetScoreSrc(name, out prop)) { prop.Get(out type, out value); RevokePotentialConstant(); } else if (ContextJudge != null && ContextJudge.TryGetScoreStringSrc(name, out prop)) { prop.Get(out type, out value); RevokePotentialConstant(); } else { PropSrc.Arbitrary result = ContextCascadeLookup(name); if (result != null) { result.Get(out type, out value); } else { type = PdtInternalType.Undefined; LoadIdent(name); value = _numbuf; } } } } unsafe void LoadIdent(int value) { fixed (byte* ptr = _numbuf) *(int*)ptr = value; } unsafe void LoadNum(float value) { fixed (byte* ptr = _numbuf) *(float*)ptr = value; } static readonly int _op_sep = IdentifierManager.SharedInstance.Request(","); static readonly int _func_int_map = IdentifierManager.SharedInstance.Request("int_map"); protected override PdtOperator GetOperator(PdtOperatorSignature sig) { PdtOperator result; if (_shortops.TryGetValue(sig, out result)) { return result; } else if (sig.Name == _op_sep) { result = new op_arr(sig.ParamCount); _shortops.Add(new PdtOperatorSignature(_op_sep, sig.ParamCount), result); return result; } else if (sig.Name == _func_int_map) { result = new func_int_map(sig.ParamCount); _shortops.Add(new PdtOperatorSignature(_func_int_map, sig.ParamCount), result); return result; } else if (_ctxops.TryGetValue(sig.Name, out result)) { return result; } else throw new KeyNotFoundException(string.Format("Undefined operator {0}", sig)); } static readonly int _colop_and = IdentifierManager.SharedInstance.Request("&"); static readonly int _colop_or = IdentifierManager.SharedInstance.Request("|"); protected override bool Collapse(int name, PdtVariableMemory param) { if (name == _colop_and) return param.Type == PdtInternalType.Number && param.AsNumber() <= 0; else if (name == _colop_or) return param.Type != PdtInternalType.Number || param.AsNumber() > 0; else throw new KeyNotFoundException(string.Format("Undefined collapse operator {0}", name)); } public ChartEvent ContextEvent { private get; set; } public ContainerState ContextState { private get; set; } public Transform ContextTransform { private get; set; } public Judge ContextJudge { private get; set; } public PropSrc ContextSelfValue { private get; set; } readonly Dictionary[] ContextCascade = new Dictionary[256]; int _cascadeHeight; public void ContextCascadeInsert() { ContextCascade[_cascadeHeight++].Clear(); } public void ContextCascadeUpdate(int key, PropSrc.Arbitrary value) { ContextCascade[_cascadeHeight - 1][key] = value; } public PropSrc.Arbitrary ContextCascadeLookup(int name) { PropSrc.Arbitrary result; for (int i = _cascadeHeight - 1; i >= 0; i--) { Dictionary cas = ContextCascade[i]; if (cas.TryGetValue(name, out result)) { return result; } } return null; } public void ContextCascadeDiscard() { --_cascadeHeight; } public PdtEvaluator() { for (int i = 0; i < ContextCascade.Length; i++) ContextCascade[i] = new Dictionary(); _ctxops.Add(IdentifierManager.SharedInstance.Request("screen_edge"), new func_screen_edge(() => ContextTransform)); _ctxops.Add(IdentifierManager.SharedInstance.Request("int"), new func_int(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.SharedInstance.Request("clamp"), new func_clamp(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.SharedInstance.Request("min"), new func_min(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.SharedInstance.Request("max"), new func_max(() => ContextSelfValue)); _ctxops.Add(IdentifierManager.SharedInstance.Request("abs"), new func_abs(() => ContextSelfValue)); Func cccb = k => ContextCascadeLookup(k); _ctxops.Add(IdentifierManager.SharedInstance.Request("attack_timing"), new func_attack_timing(cccb)); _ctxops.Add(IdentifierManager.SharedInstance.Request("enter_timing"), new func_enter_timing(cccb)); _ctxops.Add(IdentifierManager.SharedInstance.Request("release_timing"), new func_release_timing(cccb)); _ctxops.Add(IdentifierManager.SharedInstance.Request("leave_timing"), new func_leave_timing(cccb)); _ctxops.Add(IdentifierManager.SharedInstance.Request("contact_timing"), new func_contact_timing(cccb)); } static PdtEvaluator() { _shortops.Add(new PdtOperatorSignature("@", 2), new op_at_2()); _shortops.Add(new PdtOperatorSignature("*", 2), new op_mul_2()); _shortops.Add(new PdtOperatorSignature("/", 2), new op_div_2()); _shortops.Add(new PdtOperatorSignature("%", 2), new op_mod_2()); _shortops.Add(new PdtOperatorSignature("+", 1), new op_add_1()); _shortops.Add(new PdtOperatorSignature("+", 2), new op_add_2()); _shortops.Add(new PdtOperatorSignature("-", 1), new op_sub_1()); _shortops.Add(new PdtOperatorSignature("-", 2), new op_sub_2()); _shortops.Add(new PdtOperatorSignature("=", 2), new op_eq_2()); _shortops.Add(new PdtOperatorSignature("<", 2), new op_lt_2()); _shortops.Add(new PdtOperatorSignature(">", 2), new op_gt_2()); _shortops.Add(new PdtOperatorSignature("!", 1), new op_not_1()); _shortops.Add(new PdtOperatorSignature("frame_seq", 3), new func_frame_seq()); _shortops.Add(new PdtOperatorSignature("in_area", 1), new func_in_area()); } #region Operators #pragma warning disable IDE1006 #region Basic Operators class op_add_1 : PdtOperator { public op_add_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_add_2 : PdtOperator { public op_add_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() + GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_1 : PdtOperator { public op_sub_1() : base(1) { } protected override void Execute() { float result = -GetOperand(0).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_sub_2 : PdtOperator { public op_sub_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() - GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mul_2 : PdtOperator { public op_mul_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() * GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_div_2 : PdtOperator { public op_div_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() / GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_mod_2 : PdtOperator { public op_mod_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() % GetOperand(1).AsNumber(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_eq_2 : PdtOperator { public op_eq_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() == GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_lt_2 : PdtOperator { public op_lt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() < GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_gt_2 : PdtOperator { public op_gt_2() : base(2) { } protected override void Execute() { float result = GetOperand(0).AsNumber() > GetOperand(1).AsNumber() ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_not_1 : PdtOperator { public op_not_1() : base(1) { } protected override void Execute() { float result = GetOperand(0).AsNumber() <= 0 ? 1 : 0; GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(result); } } class op_arr : PdtOperator { public op_arr(int pc) : base(pc) { } protected override void Execute() { var o0 = GetOperand(0); int type = o0.Type; int len = o0.Length; bool blit = !IsBlittable(type); for (var i = 1; i < LoadedOperandCount; i++) { var o = GetOperand(i); if (o.Type != type) throw new InvalidOperationException("Cannot create variant type array"); else if (!IsBlittable(o.Type)) blit = true; len += o.Length; } if (blit) GetReturnFrame(PdtInternalType.Array, len + 2 * sizeof(int)).SetArraySuffix(type, LoadedOperandCount); else GetReturnFrame(PdtInternalType.Vector, len + sizeof(int)).SetArraySuffix(type); } static bool IsBlittable(int type) { return type == PdtInternalType.Number; } } class op_at_2 : PdtOperator { public op_at_2() : base(2) { } protected override void Execute() { int _; var op0 = GetOperand(0); var op1 = (int)Math.Round(GetOperand(1).AsNumber()); if (op0.Type == PdtInternalType.Vector) { op0.GetArraySuffix(out _, out _); if (op1 >= (op0.Length - sizeof(int)) / sizeof(float)) throw new IndexOutOfRangeException(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(op0.AsNumber(op1 * sizeof(float))); } else if (op0.Type == PdtInternalType.Number) { if (op1 != 0) throw new IndexOutOfRangeException(); GetReturnFrame(PdtInternalType.Number, sizeof(float)).SetNumber(op0.AsNumber()); } else throw new InvalidOperationException("Not a vector or number"); } } #endregion #region Basic Functions class func_frame_seq : PdtOperator { public func_frame_seq() : base(3) { } protected override unsafe void Execute() { string pf = GetOperand(0).AsString(); int f = (int)GetOperand(1).AsNumber(); int t = (int)GetOperand(2).AsNumber(); int pfl = pf.Length; int fc = t - f + 1; if (fc <= 0) throw new ArgumentException("Start index is greater than end index"); int l = (f == 0 ? 1 : 0) + pfl * fc; for (int p = 1, i = 1; p < 4; p++, i *= 10) { int j = i * 10 - 1; if (f <= j && t >= i) { int m = Math.Max(f, i); int n = Math.Min(t, j); l += (n - m + 1) * i; } } l *= sizeof(char); l += sizeof(int) * (fc + 2); var ret = GetReturnFrame(PdtInternalType.Array, l); ret.SetArraySuffix(PdtInternalType.String, fc); int o = 0; for (int i = f; i <= t; i++) { var s = pf + i.ToString(CultureInfo.InvariantCulture); ret.SetString(s, o); o += sizeof(int) + s.Length * sizeof(char); } } } class func_in_area : PdtOperator { public func_in_area() : base(1) { } protected override unsafe void Execute() { var arg = GetOperand(0); if (arg.Type == PdtInternalType.Error) { throw new InvalidOperationException("Error"); } else if (arg.Type == PdtInternalType.Number && arg.AsNumber() <= 0) { GetReturnFrame(PdtInternalType.Null, 0); } else { var ret = GetReturnFrame(arg.Type, arg.Length); arg.CopyTo(ret); } } } class func_int_map : PdtOperator { public func_int_map(int pc) : base(pc) { if (pc < 4) throw new ArgumentOutOfRangeException("Too few parameters for int_map"); } protected override unsafe void Execute() { var value = GetOperand(0).AsNumber(); var offset = GetOperand(1).AsNumber(); var step = GetOperand(2).AsNumber(); var index = (int)((value - offset) / step); if (index < 0 || index >= LoadedOperandCount - 3) index = 0; var hit = GetOperand(index + 3); var ret = GetReturnFrame(hit.Type, hit.Length); hit.CopyTo(ret); } } #endregion #region Contextual Functions class func_screen_edge : PdtOperator { readonly Func _ctxcb; public func_screen_edge(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ctx = _ctxcb(); float dist; var ray = new Ray(ctx.position, ctx.rotation * Vector3.forward); ChartPlayer.frustumPlanes[(int)GetOperand(0).AsNumber()].Raycast(ray, out dist); var ret = GetReturnFrame(PdtInternalType.Vector, sizeof(Vector3) + sizeof(int)); var ptr = (Vector3*)ret.TrustedAsOfLength(sizeof(Vector3) + sizeof(int)); *ptr++ = ray.GetPoint(dist); *(int*)ptr = PdtInternalType.Number; } } class func_int : PdtOperator { readonly Func _ctxcb; public func_int(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float v; switch (LoadedOperandCount) { case 0: v = oputil.AsNumber(_ctxcb()); break; case 1: v = GetOperand(0).AsNumber(); break; default: throw new ArgumentException("Argument count not 0 or 1"); } ret.SetNumber(Mathf.Floor(v)); } } class func_clamp : PdtOperator { readonly Func _ctxcb; public func_clamp(Func ctxcb) : base(3) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float min = GetOperand(0).AsNumber(); float value, max; switch (LoadedOperandCount) { case 2: value = oputil.AsNumber(_ctxcb()); max = GetOperand(1).AsNumber(); break; case 3: value = GetOperand(1).AsNumber(); max = GetOperand(2).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Clamp(value, min, max)); } } class func_min : PdtOperator { readonly Func _ctxcb; public func_min(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float a = GetOperand(0).AsNumber(); float b; switch (LoadedOperandCount) { case 1: b = oputil.AsNumber(_ctxcb()); break; case 2: b = GetOperand(1).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Min(a, b)); } } class func_max : PdtOperator { readonly Func _ctxcb; public func_max(Func ctxcb) : base(2) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var a = GetOperand(0).AsNumber(); float b; switch (LoadedOperandCount) { case 1: b = oputil.AsNumber(_ctxcb()); break; case 2: b = GetOperand(1).AsNumber(); break; default: throw new ArgumentException("Argument count not 2 or 3"); } ret.SetNumber(Mathf.Max(a, b)); } } class func_abs : PdtOperator { readonly Func _ctxcb; public func_abs(Func ctxcb) : base(1) { _ctxcb = ctxcb; } protected override unsafe void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); float arg; switch (LoadedOperandCount) { case 0: arg = oputil.AsNumber(_ctxcb()); break; case 1: arg = GetOperand(0).AsNumber(); break; default: throw new ArgumentException("Argument count not 0 or 1"); } ret.SetNumber(Mathf.Abs(arg)); } } #endregion #region Judge Functions static readonly int _var_fn = IdentifierManager.SharedInstance.Request("fn"); static readonly int _var_tn = IdentifierManager.SharedInstance.Request("tn"); static readonly int _var_ft = IdentifierManager.SharedInstance.Request("ft"); static readonly int _var_tt = IdentifierManager.SharedInstance.Request("tt"); static readonly int _var_fv = IdentifierManager.SharedInstance.Request("fv"); static readonly int _var_tv = IdentifierManager.SharedInstance.Request("tv"); abstract class JudgeFunction : PdtOperator { readonly Func _ctxcb; protected JudgeFunction(int pc, Func ctxcb) : base(pc) { _ctxcb = ctxcb; } protected sealed override void Execute() { var ret = GetReturnFrame(PdtInternalType.Number, sizeof(float)); var fn = oputil.AsNumber(_ctxcb(_var_fn)); var tn = oputil.AsNumber(_ctxcb(_var_tn)); var ft = oputil.AsNumber(_ctxcb(_var_ft)); var tt = oputil.AsNumber(_ctxcb(_var_tt)); var fv = oputil.AsVector(_ctxcb(_var_fv)); var tv = oputil.AsVector(_ctxcb(_var_tv)); ret.SetNumber(ExecuteImpl(fn, tn, ft, tt, fv, tv) ? 1 : 0); } protected abstract bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv); } class func_attack_timing : JudgeFunction { public func_attack_timing(Func ctxcb) : base(2, ctxcb) { } protected override bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv) { if (fv != null) return false; var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; return tt > t0 && tt <= t1; } } class func_enter_timing : JudgeFunction { public func_enter_timing(Func ctxcb) : base(1, ctxcb) { } protected override bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv) { if (fv == null || tv == null) return false; var t0 = GetOperand(0).AsNumber() + fn; return ft < t0 && tt >= t0; } } class func_release_timing : JudgeFunction { public func_release_timing(Func ctxcb) : base(2, ctxcb) { } protected override bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv) { if (tv != null) return false; var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; return ft > t0 && ft <= t1; } } class func_leave_timing : JudgeFunction { public func_leave_timing(Func ctxcb) : base(1, ctxcb) { } protected override bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv) { if (fv == null || tv == null) return false; var t1 = GetOperand(0).AsNumber() + tn; return ft < t1 && tt >= t1; } } class func_contact_timing : JudgeFunction { public func_contact_timing(Func ctxcb) : base(2, ctxcb) { } protected override bool ExecuteImpl(float fn, float tn, float ft, float tt, Vector3? fv, Vector3? tv) { var t0 = GetOperand(0).AsNumber() + fn; var t1 = GetOperand(1).AsNumber() + tn; return (fv == null || ft < t1) && (tv == null || tt >= t0); } } #endregion unsafe static class oputil { public static float AsNumber(PropSrc src) { if (src == null) throw new ArgumentNullException("src"); int type; byte[] value; src.Get(out type, out value); if (type != PdtInternalType.Number) throw new ArgumentException("Not a number"); fixed (byte* ptr = value) { return *(float*)ptr; } } public static Vector3? AsVector(PropSrc src) { if (src == null) throw new ArgumentNullException("src"); int type; byte[] value; src.Get(out type, out value); if (type == PdtInternalType.Vector) { fixed (byte* ptr = value) { return *(Vector3*)ptr; } } else if (type == PdtInternalType.Number) { fixed (byte* ptr = value) { return new Vector3(*(float*)ptr, 0, 0); } } else if (type == PdtInternalType.Null) { return null; } else throw new ArgumentException("Not a number"); } } #pragma warning restore IDE1006 #endregion } }