public static void GenerateINFOAddTopicScripts(ESMAnalyzer esmAnalyzer, BuildTracker buildTracker, IBuildTarget tifBuildTarget) { TES5TypeInferencer typeInferencer = new TES5TypeInferencer(esmAnalyzer); TES5ObjectCallFactory objectCallFactory = new TES5ObjectCallFactory(typeInferencer); TES4TopicsToTES5GlobalVariableFinder globalVariableFinder = new TES4TopicsToTES5GlobalVariableFinder(); TES5GlobalVariables globalVariables = esmAnalyzer.GlobalVariables; var builtTIFs = buildTracker.GetBuiltScripts(BuildTargetFactory.TIFName); foreach (TES4Record infoRecord in esmAnalyzer.GetRecords().Where(r => r.RecordType == TES4RecordType.INFO)) { TES4SubrecordData[] names = infoRecord.GetSubrecords("NAME").ToArray(); if (names.Any()) { string fragment0Name = TES5FragmentFactory.GetFragmentName(0); string nameTES5FormIDHex = (infoRecord.FormID + 0x01000000).ToString("x8"); string scriptName = TES5ReferenceFactory.tif_Prefix + "_" + nameTES5FormIDHex; TES5Script? infoTIF = builtTIFs.Where(s => s.Key == scriptName).Select(s => s.Value.Script).FirstOrDefault(); TES5FunctionCodeBlock fragment0; if (infoTIF != null) { fragment0 = infoTIF.BlockList.Blocks.OfType <TES5FunctionCodeBlock>().Where(b => b.BlockName == fragment0Name).First(); } else { TES5ScriptHeader scriptHeader = TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(scriptName, TES5BasicType.T_TOPICINFO, TES5TypeFactory.TES4_Prefix, true); TES5GlobalScope globalScope = new TES5GlobalScope(scriptHeader); TES5FunctionScope functionScope = new TES5FunctionScope(fragment0Name); functionScope.AddParameter(new TES5SignatureParameter("akSpeakerRef", TES5BasicType.T_OBJECTREFERENCE, true)); TES5LocalScope localScope = new TES5LocalScope(functionScope); TES5CodeScope codeScope = TES5CodeScopeFactory.CreateCodeScope(localScope); fragment0 = new TES5FunctionCodeBlock(functionScope, codeScope, TES5VoidType.Instance, false, true); TES5BlockList blockList = new TES5BlockList() { fragment0 }; infoTIF = new TES5Script(globalScope, blockList, true); string outputPath = tifBuildTarget.GetTranspileToPath(scriptName); TES5Target target = new TES5Target(infoTIF, outputPath); buildTracker.RegisterBuiltScript(tifBuildTarget, target); } foreach (TES4SubrecordData name in names) { int formID = infoRecord.ExpandBytesIntoFormID(name); TES4Record addedTopic = esmAnalyzer.GetRecordByFormID(formID); Tuple <int, string>?globalVariable = globalVariableFinder.GetGlobalVariableNullable(addedTopic.FormID); string globalVariableEditorID = globalVariable != null ? globalVariable.Item2 : globalVariableFinder.GetGlobalVariableEditorID(addedTopic.GetEditorID()); Nullable <int> globalVariableTES5FormID = globalVariable != null ? globalVariable.Item1 : (Nullable <int>)null; TES5Property topicAddedProperty = TES5PropertyFactory.ConstructWithTES5FormID(globalVariableEditorID, TES5BasicType.T_GLOBALVARIABLE, globalVariableEditorID, globalVariableTES5FormID); infoTIF.GlobalScope.AddPropertyIfNotExists(topicAddedProperty); TES5Reference topicAddedReference = TES5ReferenceFactory.CreateReferenceToVariableOrProperty(topicAddedProperty); fragment0.AddChunk(objectCallFactory.CreateObjectCall(topicAddedReference, "SetValueInt", new TES5ObjectCallArguments() { new TES5Integer(1) })); } } } }
/* * 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)); }