Ejemplo n.º 1
0
        private void LoadGameObjects(Resource resource)
        {
            if (!resource.Regions.TryGetValue("Templates", out Region templates))
            {
                // TODO - log error
                return;
            }

            if (!templates.Children.TryGetValue("GameObjects", out List <LSLib.LS.Node> gameObjects))
            {
                // TODO - log error
                return;
            }

            foreach (var gameObject in gameObjects)
            {
                if (gameObject.Attributes.TryGetValue("MapKey", out NodeAttribute objectGuid) &&
                    gameObject.Attributes.TryGetValue("Name", out NodeAttribute objectName) &&
                    gameObject.Attributes.TryGetValue("Type", out NodeAttribute objectType))
                {
                    LSLib.LS.Story.Compiler.ValueType type = null;
                    switch ((string)objectType.Value)
                    {
                    case "item": type = Compiler.Context.LookupType("ITEMGUID"); break;

                    case "character": type = Compiler.Context.LookupType("CHARACTERGUID"); break;

                    case "trigger": type = Compiler.Context.LookupType("TRIGGERGUID"); break;

                    default:
                        // TODO - log unknown type
                        break;
                    }

                    if (type != null)
                    {
                        var gameObjectInfo = new GameObjectInfo
                        {
                            Name = objectName.Value + "_" + objectGuid.Value,
                            Type = type
                        };
                        Compiler.Context.GameObjects[(string)objectGuid.Value] = gameObjectInfo;
                    }
                }
            }
        }
Ejemplo n.º 2
0
        private void VerifyIRBinaryCondition(IRRule rule, IRBinaryCondition condition, Int32 conditionIndex)
        {
            ValueType lhs = condition.LValue.Type,
                      rhs = condition.RValue.Type;

            // Don't raise compiler errors if the untyped value is a variable,
            // as we already have a separate rule-level error for untyped variables.
            if ((lhs == null && condition.LValue is IRVariable) ||
                (rhs == null && condition.RValue is IRVariable))
            {
                return;
            }

            if (condition.LValue is IRVariable &&
                condition.RValue is IRVariable &&
                (condition.LValue as IRVariable).Index == (condition.RValue as IRVariable).Index
                // This bug was fixed in DOS2 DE
                && Game == TargetGame.DOS2
                // There is a known bug in the main campaign that we have to ignore
                && rule.Goal.Name != "EndGame_PrisonersDilemma")
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.BinaryOperationSameRhsLhs,
                                  "Same variable used on both sides of a binary expression; this will result in an invalid compare in runtime");
                return;
            }

            VerifyIRBinaryConditionValue(rule, condition.LValue, conditionIndex);
            VerifyIRBinaryConditionValue(rule, condition.RValue, conditionIndex);

            if (!AreIntrinsicTypesCompatible(lhs.IntrinsicTypeId, rhs.IntrinsicTypeId))
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.LocalTypeMismatch,
                                  "Type of left expression ({0}) differs from type of right expression ({1})",
                                  TypeToName(lhs.IntrinsicTypeId), TypeToName(rhs.IntrinsicTypeId));
                return;
            }

            if (IsRiskyComparison(lhs.IntrinsicTypeId, rhs.IntrinsicTypeId))
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.RiskyComparison,
                                  "Comparison between {0} and {1} may trigger incorrect behavior",
                                  TypeToName(lhs.IntrinsicTypeId), TypeToName(rhs.IntrinsicTypeId));
                return;
            }

            if (IsGuidAliasToAliasCast(lhs, rhs))
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.GuidAliasMismatch,
                                  "GUID alias type of left expression ({0}) differs from type of right expression ({1})",
                                  TypeToName(lhs.TypeId), TypeToName(rhs.TypeId));
                return;
            }

            // Using greater than/less than operators for strings and GUIDs is probably a mistake.
            if ((lhs.IntrinsicTypeId == Value.Type.String ||
                 lhs.IntrinsicTypeId == Value.Type.GuidString) &&
                (condition.Op == RelOpType.Greater ||
                 condition.Op == RelOpType.GreaterOrEqual ||
                 condition.Op == RelOpType.Less ||
                 condition.Op == RelOpType.LessOrEqual))
            {
                Context.Log.Warn(condition.Location,
                                 DiagnosticCode.StringLtGtComparison,
                                 "String comparison using operator {0} - probably a mistake?",
                                 condition.Op);
                return;
            }
        }
Ejemplo n.º 3
0
        private void VerifyIRConstant(IRConstant constant)
        {
            if (constant.Type.IntrinsicTypeId == Value.Type.GuidString)
            {
                var       nameWithoutType = constant.StringValue;
                ValueType type            = null;

                // Check if the value is prefixed by any of the known GUID subtypes.
                // If a match is found, verify that the type of the constant matched the GUID subtype.
                var underscore = constant.StringValue.IndexOf('_');
                if (underscore != -1)
                {
                    var prefix = constant.StringValue.Substring(0, underscore);
                    type = Context.LookupType(prefix);
                    if (type != null)
                    {
                        nameWithoutType = constant.StringValue.Substring(underscore + 1);
                        if (constant.Type.TypeId > CompilationContext.MaxIntrinsicTypeId &&
                            type.TypeId != constant.Type.TypeId)
                        {
                            Context.Log.Error(constant.Location,
                                              DiagnosticCode.GuidAliasMismatch,
                                              "GUID constant \"{0}\" has inferred type {1}",
                                              constant.StringValue, constant.Type.Name);
                        }
                    }
                    else if (prefix.Contains("GUID"))
                    {
                        Context.Log.Warn(constant.Location,
                                         DiagnosticCode.GuidPrefixNotKnown,
                                         "GUID constant \"{0}\" is prefixed with unknown type {1}",
                                         constant.StringValue, prefix);
                    }
                }

                var guid = constant.StringValue.Substring(constant.StringValue.Length - 36);
                if (!Context.GameObjects.TryGetValue(guid, out GameObjectInfo objectInfo))
                {
                    Context.Log.Warn(constant.Location,
                                     DiagnosticCode.UnresolvedGameObjectName,
                                     "Object \"{0}\" could not be resolved",
                                     constant.StringValue);
                }
                else
                {
                    if (objectInfo.Name != nameWithoutType)
                    {
                        Context.Log.Warn(constant.Location,
                                         DiagnosticCode.GameObjectNameMismatch,
                                         "Constant \"{0}\" references game object with different name (\"{1}\")",
                                         nameWithoutType, objectInfo.Name);
                    }

                    if (constant.Type.TypeId != (uint)Value.Type.GuidString &&
                        objectInfo.Type.TypeId != (uint)Value.Type.GuidString &&
                        constant.Type.TypeId != objectInfo.Type.TypeId)
                    {
                        Context.Log.Warn(constant.Location,
                                         DiagnosticCode.GameObjectTypeMismatch,
                                         "Constant \"{0}\" of type {1} references game object of type {2}",
                                         constant.StringValue, constant.Type.Name, objectInfo.Type.Name);
                    }
                }
            }
        }
Ejemplo n.º 4
0
        private void VerifyIRFuncCondition(IRRule rule, IRFuncCondition condition, int conditionIndex)
        {
            // TODO - Merge FuncCondition and IRStatement base?
            // Base --> IRParameterizedCall --> FuncCond: has (NOT) field
            var func = Context.LookupSignature(condition.Func.Name);

            if (func == null)
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.UnresolvedSymbol,
                                  "Symbol \"{0}\" could not be resolved",
                                  condition.Func.Name);
                return;
            }

            if (!func.FullyTyped)
            {
                Context.Log.Error(condition.Location,
                                  DiagnosticCode.UnresolvedSignature,
                                  "Signature of \"{0}\" could not be determined",
                                  condition.Func.Name);
                return;
            }

            func.Read = true;

            if (conditionIndex == 0)
            {
                switch (rule.Type)
                {
                case RuleType.Proc:
                    if (func.Type != FunctionType.Proc)
                    {
                        Context.Log.Error(condition.Location,
                                          DiagnosticCode.InvalidSymbolInInitialCondition,
                                          "Initial proc condition can only be a PROC name; \"{0}\" is a {1}",
                                          condition.Func.Name, func.Type);
                        return;
                    }
                    break;

                case RuleType.Query:
                    if (func.Type != FunctionType.UserQuery)
                    {
                        Context.Log.Error(condition.Location,
                                          DiagnosticCode.InvalidSymbolInInitialCondition,
                                          "Initial query condition can only be a user-defined QRY name; \"{0}\" is a {1}",
                                          condition.Func.Name, func.Type);
                        return;
                    }
                    break;

                case RuleType.Rule:
                    if (func.Type != FunctionType.Event &&
                        func.Type != FunctionType.Database)
                    {
                        Context.Log.Error(condition.Location,
                                          DiagnosticCode.InvalidSymbolInInitialCondition,
                                          "Initial rule condition can only be an event or a DB; \"{0}\" is a {1}",
                                          condition.Func.Name, func.Type);
                        return;
                    }
                    break;

                default:
                    throw new Exception("Unknown rule type");
                }
            }
            else
            {
                if (func.Type != FunctionType.SysQuery &&
                    func.Type != FunctionType.Query &&
                    func.Type != FunctionType.Database &&
                    func.Type != FunctionType.UserQuery)
                {
                    Context.Log.Error(condition.Location,
                                      DiagnosticCode.InvalidFunctionTypeInCondition,
                                      "Subsequent rule conditions can only be queries or DBs; \"{0}\" is a {1}",
                                      condition.Func.Name, func.Type);
                    return;
                }
            }

            int index = 0;

            foreach (var param in func.Params)
            {
                var       condParam = condition.Params[index];
                ValueType type      = condParam.Type;

                if (type == null)
                {
                    Context.Log.Error(condParam.Location,
                                      DiagnosticCode.InternalError,
                                      "No type information available for func condition arg");
                    continue;
                }

                VerifyIRValue(rule, condParam);
                VerifyIRValueCall(rule, condParam, func, index, conditionIndex, condition.Not);
                VerifyParamCompatibility(func, index, param, condParam);

                index++;
            }
        }
Ejemplo n.º 5
0
        private void VerifyIRStatement(IRRule rule, IRStatement statement)
        {
            if (statement.Func == null)
            {
                return;
            }

            var func = Context.LookupSignature(statement.Func.Name);

            if (func == null)
            {
                Context.Log.Error(statement.Location,
                                  DiagnosticCode.UnresolvedSymbol,
                                  "Symbol \"{0}\" could not be resolved",
                                  statement.Func.Name);
                return;
            }

            if (!func.FullyTyped)
            {
                Context.Log.Error(statement.Location,
                                  DiagnosticCode.UnresolvedSignature,
                                  "Signature of \"{0}\" could not be determined",
                                  statement.Func.Name);
                return;
            }

            if (func.Type != FunctionType.Database &&
                func.Type != FunctionType.Call &&
                func.Type != FunctionType.SysCall &&
                func.Type != FunctionType.Proc)
            {
                Context.Log.Error(statement.Location,
                                  DiagnosticCode.InvalidSymbolInStatement,
                                  "KB rule actions can only reference databases, calls and PROCs; \"{0}\" is a {1}",
                                  statement.Func.Name, func.Type);
                return;
            }

            if (statement.Not &&
                func.Type != FunctionType.Database)
            {
                Context.Log.Error(statement.Location,
                                  DiagnosticCode.CanOnlyDeleteFromDatabase,
                                  "KB rule NOT actions can only reference databases; \"{0}\" is a {1}",
                                  statement.Func.Name, func.Type);
                return;
            }

            if (statement.Not)
            {
                func.Deleted = true;
            }
            else
            {
                func.Inserted = true;
            }

            int index = 0;

            foreach (var param in func.Params)
            {
                var ele = statement.Params[index];

                ValueType type = ele.Type;
                if (type == null)
                {
                    Context.Log.Error(ele.Location,
                                      DiagnosticCode.InternalError,
                                      "No type information available for statement argument");
                    continue;
                }

                VerifyIRValue(rule, ele);
                VerifyIRValueCall(rule, ele, func, index, -1, statement.Not);
                VerifyParamCompatibility(func, index, param, ele);

                index++;
            }
        }