Beispiel #1
0
        public void TrackRemoteScript(TES5ScriptHeader scriptHeader)
        {
            this.trackedScript = scriptHeader;
            ITES5Type ourNativeType    = this.propertyType.NativeType;
            ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType;

            /*
             * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway
             */
            if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(remoteNativeType, ourNativeType))
            {
                return;
            }

            /*
             * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly
             */
            else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType))
            {
                this.trackedScript.SetNativeType(ourNativeType);
            }
            else
            {
                //WTM:  Note:  Special Cases:  Let some script transpile without throwing.
                if (!(
                        (ourNativeType == TES5BasicType.T_ACTIVATOR && remoteNativeType == TES5BasicType.T_OBJECTREFERENCE && (scriptHeader.EDID == "SE01MetronomeScript" || scriptHeader.EDID == "SE11TrigZoneHowlingHallsEnterSCRIPT")) ||
                        (ourNativeType == TES5BasicType.T_ACTORBASE && remoteNativeType == TES5BasicType.T_ACTOR && (scriptHeader.EDID == "HieronymusLexScript" || scriptHeader.EDID == "SERunsInCirclesSCRIPT"))))
                {
                    throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ":  The definitions of local property type and remote property type have diverged.  (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value + ")");
                }
            }
        }
        public override void TrackRemoteScript(TES5ScriptHeader scriptHeader)
        {
            this.trackedScript = scriptHeader;
            ITES5Type ourNativeType    = this.propertyType.NativeType;
            ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType;

            /*
             * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway
             */
            if (ourNativeType == remoteNativeType || TES5InheritanceGraphAnalyzer.IsExtending(remoteNativeType, ourNativeType))
            {
                return;
            }

            /*
             * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly
             */
            else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType))
            {
                this.trackedScript.SetNativeType(ourNativeType);
            }
            else
            {
                throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ":  The definitions of local property type and remote property type have diverged.  (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value);
            }
        }
Beispiel #3
0
 /*
  * @throws ConversionException
  */
 public void SetNativeType(ITES5Type scriptType, bool ensureNewTypeExtendsCurrentType = true)
 {
     if (ensureNewTypeExtendsCurrentType && !TES5InheritanceGraphAnalyzer.IsExtending(scriptType, this.ScriptType.NativeType))
     {
         throw new ConversionException("Cannot set script type to non-extending type - current native type " + this.ScriptType.NativeType.Value + ", new type " + scriptType.Value);
     }
     this.ScriptType.NativeType = scriptType.NativeType;
 }
Beispiel #4
0
        /*
         * Try to inference variable"s type with type.
         *
         *
         *  Needed for proxifying the properties to other scripts
         *  - Will return true if inferencing succeeded, false otherwise.
         * @throws ConversionException
         */
        private bool InferenceType(ITES5VariableOrProperty variable, ITES5Type type, TES5MultipleScriptsScope multipleScriptsScope)
        {
            if (!TES5InheritanceGraphAnalyzer.IsExtending(type, variable.TES5Type.NativeType))
            {
                return(false);
            }

            variable.TES5Type = type;
            return(true);
        }
Beispiel #5
0
        private void InferenceTypeOfMethodArguments(TES5ObjectCall objectCall, TES5MultipleScriptsScope multipleScriptsScope)
        {
            /*
             * Inference the arguments
             */
            int       argumentIndex = 0;
            ITES5Type calledOnType  = objectCall.AccessedObject.TES5Type.NativeType;

            foreach (ITES5Value argument in objectCall.Arguments)
            {
                /*
                 * Get the argument type according to TES5Inheritance graph.
                 */
                ITES5Type argumentTargetType = TES5InheritanceGraphAnalyzer.FindTypeByMethodParameter(calledOnType, objectCall.FunctionName, argumentIndex);
                if (argument.TES5Type != argumentTargetType)
                {
                    /*
                     * todo - maybe we should move getReferencesTo() to TES5Value and make all of the rest TES5Values just have null references as they do not reference anything? :)
                     */
                    ITES5Referencer referencerArgument = argument as ITES5Referencer;
                    if (referencerArgument != null && TES5InheritanceGraphAnalyzer.IsExtending(argumentTargetType, argument.TES5Type.NativeType))
                    { //HACKY!
                        this.InferenceType(referencerArgument.ReferencesTo, argumentTargetType, multipleScriptsScope);
                    }
                    else
                    {
                        //So there"s one , one special case where we actually have to cast a var from one to another even though they are not ,,inheriting" from themselves, because they are primitives.
                        //Scenario: there"s an T_INT argument, and we feed it with a T_FLOAT variable reference. It won"t work :(
                        //We need to cast it on call level ( NOT inference it ) to make it work and not break other possible scenarios ( more specifically, when a float would be inferenced to int and there"s a
                        //float assigment somewhere in the code )
                        if (argumentTargetType == TES5BasicType.T_INT && argument.TES5Type == TES5BasicType.T_FLOAT)
                        {
                            TES5Castable argumentCastable = argument as TES5Castable;
                            if (argumentCastable != null)
                            { //HACKY! When we"ll clean up this interface, it will dissapear :)
                                argumentCastable.ManualCastTo = argumentTargetType;
                            }
                        }
                        else if (
                            !TES5InheritanceGraphAnalyzer.IsExtending(argument.TES5Type, argumentTargetType) &&
                            !TES5InheritanceGraphAnalyzer.IsNumberTypeOrBoolAndInt(argument.TES5Type, argumentTargetType) &&
                            !(argument is TES5None && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(argumentTargetType, (new TES5None()).TES5Type)))
                        {
                            bool expected = objectCall.AccessedObject.TES5Type.OriginalName == "TES4TimerHelper" && objectCall.FunctionName == "LegacySay";
                            throw new ConversionException("Argument type mismatch at " + objectCall.FunctionName + " index " + argumentIndex + ".  Expected " + argumentTargetType.OriginalName + ".  Found " + argument.TES5Type.OriginalName + ".", expected: expected);
                        }
                    }
                }
                argumentIndex++;
            }
        }
Beispiel #6
0
        public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope)
        {
            TES4FunctionArguments   functionArguments = function.Arguments;
            TES5LocalScope          localScope        = codeScope.LocalScope;
            TES5ObjectCallArguments arguments         = new TES5ObjectCallArguments()
            {
                calledOn
            };
            ITES5Value argument1 = this.valueFactory.CreateValue(functionArguments[0], codeScope, globalScope, multipleScriptsScope);

            if (argument1.TES5Type != TES5BasicType.T_TOPIC)
            {
                TES5Castable argument1Castable = argument1 as TES5Reference;
                if (argument1Castable != null && TES5InheritanceGraphAnalyzer.IsExtending(TES5BasicType.T_TOPIC, argument1.TES5Type))
                {
                    argument1Castable.ManualCastTo = TES5BasicType.T_TOPIC;
                }
            }
            arguments.Add(argument1);
            ITES4StringValue optionalFlag = functionArguments.GetOrNull(2);

            if (optionalFlag != null)
            {
                string optionalFlagDataString = optionalFlag.StringValue;
                if (this.analyzer.GetFormTypeByEDID(optionalFlagDataString).Value != TES4RecordType.REFR.Name)
                {
                    this.metadataLogService.WriteLine("ADD_SPEAK_AS_ACTOR", new string[] { optionalFlagDataString });
                    optionalFlag = new TES4ApiToken(optionalFlag.Data + "Ref");
                }

                arguments.Add(this.valueFactory.CreateValue(optionalFlag, codeScope, globalScope, multipleScriptsScope));
            }
            else
            {
                arguments.Add(new TES5None());
            }

            arguments.Add(new TES5Bool(true));
            ITES5Referencer timerReference = this.referenceFactory.CreateTimerReadReference(globalScope, multipleScriptsScope, localScope);

            return(this.objectCallFactory.CreateObjectCall(timerReference, "LegacySay", multipleScriptsScope, arguments));
        }
Beispiel #7
0
 /*
  * Try to inference variable"s type with type.
  *
  *
  *  Needed for proxifying the properties to other scripts
  *  - Will return true if inferencing succeeded, false otherwise.
  * @throws ConversionTypeMismatchException
  */
 private void InferenceType(ITES5VariableOrProperty variable, TES5BasicType type)
 {
     if (!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsTypeOrIsNumberType(type, variable.TES5Type.NativeType, false))
     {
         if (TES5InheritanceGraphAnalyzer.IsExtending(variable.TES5Type.NativeType, type))
         {
             return;
         }
         throw new ConversionTypeMismatchException("Could not extend " + variable.TES5Type.NativeType.Value + " to " + type.Value + ".");
     }
     if (variable.TES5Type.AllowInference)
     {
         variable.TES5Type = type;
     }
     else if (variable.TES5Type.AllowNativeTypeInference)
     {
         variable.TES5Type.NativeType = type;
     }
     else
     {
         throw new ConversionTypeMismatchException(variable.Name + " (" + variable.TES5DeclaredType.OriginalName + " : " + variable.TES5DeclaredType.NativeType.Name + ") could not be inferenced to a " + type.Name + " because inference was not allowed.");
     }
 }
Beispiel #8
0
        /*
         * Joins N QF subfragments into one QF fragment that can be properly binded into Skyrim VM
         * @throws ConversionException
         */
        public TES5Target JoinQFFragments(IBuildTarget target, string resultingFragmentName, List <QuestStageScript> subfragmentsTrees)
        {
            int      tes4FormID = GetTES4FormID(resultingFragmentName);
            StageMap stageMap   = StageMapBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID);

            /*
             * We need script fragment for objective handling for each stage, so when parsing the script fragments,
             * we"ll be marking them there, and intersecting this with stage.
             * This will give us an array of stages which don"t have script fragment, but will need it anyways
             * for objective handling.
             */
            TES5ScriptHeader resultingScriptHeader = TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(resultingFragmentName, TES5BasicType.T_QUEST, TES5TypeFactory.TES4_Prefix, true);
            TES5GlobalScope  resultingGlobalScope  = new TES5GlobalScope(resultingScriptHeader);

            string[] referenceAliases = ReferenceAliasBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID).ToArray();
            foreach (string propertyName in referenceAliases)
            {
                resultingGlobalScope.AddProperty(TES5PropertyFactory.ConstructWithoutFormID(propertyName, TES5BasicType.T_REFERENCEALIAS, propertyName));
            }

            List <QuestStageBlock> questStageBlocks        = new List <QuestStageBlock>();
            HashSet <int>          implementedStages       = new HashSet <int>();
            HashSet <string>       propertiesNamesDeclared = new HashSet <string>();

            foreach (var subfragment in subfragmentsTrees)
            {
                TES5Target      subfragmentsTree       = subfragment.Script;
                TES5Script      subfragmentScript      = subfragmentsTree.Script;
                TES5GlobalScope subfragmentGlobalScope = subfragmentScript.GlobalScope;
                foreach (TES5Property subfragmentProperty in subfragmentGlobalScope.Properties)
                {
                    /*
                     * Move over the properties to the new global scope
                     */
                    if (propertiesNamesDeclared.Add(subfragmentProperty.Name))
                    {
                        resultingGlobalScope.AddProperty(subfragmentProperty);
                    }
                    else
                    {
                        if (subfragmentProperty.IsPlayerRef)
                        {
                            continue;
                        }
                        //WTM:  Change:  I don't think renaming these properties actually helps anything.

                        /*
                         * string propertyName = GeneratePropertyName(subfragmentScript.ScriptHeader, subfragmentProperty);
                         * subfragmentProperty.Rename(propertyName);
                         * if (!propertiesNamesDeclared.Add(subfragmentProperty.Name))
                         * {
                         *  throw new ConversionException(nameof(propertiesNamesDeclared) + " already contained property " + subfragmentProperty.Name + ".");
                         * }
                         */
                        //WTM:  Change:  I'm trying to unify properties and include extended type declarations.
                        TES5Property existingProperty = resultingGlobalScope.GetPropertyByName(subfragmentProperty.Name);
                        if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(existingProperty.TES5Type, subfragmentProperty.TES5Type))
                        {
                            continue;
                        }
                        if (TES5InheritanceGraphAnalyzer.IsExtending(subfragmentProperty.TES5Type, existingProperty.TES5Type))
                        {
                            existingProperty.TES5Type = subfragmentProperty.TES5Type;
                            continue;
                        }
                        if (TES5InheritanceGraphAnalyzer.IsExtending(existingProperty.TES5Type, subfragmentProperty.TES5Type.NativeType))
                        {
                            subfragmentProperty.TES5Type.NativeType = existingProperty.TES5Type.NativeType;
                            existingProperty.TES5Type = subfragmentProperty.TES5Type;
                            continue;
                        }
                        throw new ConversionException("Types were not compatible for property " + subfragmentProperty.Name + ":  " + subfragmentProperty.TES5Type.Value + " should extend " + existingProperty.TES5Type.Value + " (" + existingProperty.TES5Type.NativeType.Value + ").");
                    }
                }

                List <ITES5CodeBlock> subfragmentBlocks = subfragmentScript.BlockList.Blocks;
                if (subfragmentBlocks.Count != 1)
                {
                    throw new ConversionException("Wrong QF fragment, actual function count: " + subfragmentBlocks.Count + "..");
                }

                ITES5CodeBlock subfragmentBlock = subfragmentBlocks[0];
                if (subfragmentBlock.FunctionScope.BlockName != TES5FragmentFactory.GetFragmentName(0))
                {
                    throw new ConversionException("Wrong QF fragment funcname, actual function name: " + subfragmentBlock.FunctionScope.BlockName + ".");
                }

                string newFragmentFunctionName = TES5FragmentFactory.GetFragmentName(subfragment.Stage, subfragment.LogIndex);

                subfragmentBlock.FunctionScope.Rename(newFragmentFunctionName);
                List <int>?stageMapOfStage = stageMap.TryGetStageTargetsMap(subfragment.Stage);
                if (stageMapOfStage != null)
                {
                    var objectiveCodeChunks = objectiveHandlingFactory.GenerateObjectiveHandling(subfragmentBlock, resultingGlobalScope, stageMapOfStage);
                    foreach (var newCodeChunk in objectiveCodeChunks)
                    {
                        subfragmentBlock.AddChunk(newCodeChunk);
                    }
                }

                questStageBlocks.Add(new QuestStageBlock(subfragment.Stage, subfragment.LogIndex, subfragmentBlock));
                implementedStages.Add(subfragment.Stage);
            }

            /*
             * Diff to find stages which we still need to mark
             */
            int[] nonDoneStages = stageMap.StageIDs.Where(stageID => !implementedStages.Contains(stageID)).ToArray();
            foreach (int nonDoneStage in nonDoneStages)
            {
                TES5FunctionCodeBlock fragment = objectiveHandlingFactory.CreateEnclosedFragment(resultingGlobalScope, nonDoneStage, stageMap.GetStageTargetsMap(nonDoneStage));
                questStageBlocks.Add(new QuestStageBlock(nonDoneStage, 0, fragment));
            }

            this.mappedTargetsLogService.WriteScriptName(resultingFragmentName);
            foreach (var kvp in stageMap.MappedTargetsIndex)
            {
                var originalTargetIndex = kvp.Key;
                var mappedTargetIndexes = kvp.Value;
                this.mappedTargetsLogService.WriteLine(originalTargetIndex, mappedTargetIndexes);
            }

            TES5BlockList resultingBlockList = new TES5BlockList(questStageBlocks.OrderBy(b => b.StageID).ThenBy(b => b.LogIndex).Select(b => b.CodeBlock));
            TES5Script    resultingTree      = new TES5Script(resultingGlobalScope, resultingBlockList, true);
            string        outputPath         = target.GetTranspileToPath(resultingFragmentName);

            return(new TES5Target(resultingTree, outputPath));
        }
Beispiel #9
0
 private bool ReferenceIsIntAndValueExtendsForm()
 {
     return(this.Reference.TES5Type == TES5BasicType.T_INT && TES5InheritanceGraphAnalyzer.IsExtending(this.value.TES5Type, TES5BasicType.T_FORM));
 }