public function createCodeChunk(TES4VariableDeclarationList $chunk, TES5CodeScope $codeScope)
 {
     foreach ($chunk->getVariableList() as $variable) {
         switch ($variable->getVariableType()) {
             case TES4Type::T_FLOAT():
                 $property = new TES5LocalVariable($variable->getVariableName(), TES5BasicType::T_FLOAT());
                 break;
             case TES4Type::T_INT():
             case TES4Type::T_SHORT():
             case TES4Type::T_LONG():
                 $property = new TES5LocalVariable($variable->getVariableName(), TES5BasicType::T_INT());
                 break;
             case TES4Type::T_REF():
                 //most basic one, if something from inherited class is used, we will set to the inheriting class
                 $property = new TES5LocalVariable($variable->getVariableName(), TES5BasicType::T_FORM());
                 break;
             default:
                 throw new ConversionException("Unknown local variable declaration type.");
         }
         $codeScope->getLocalScope()->addVariable($property);
     }
     return null;
 }
 /**
  * Create an pre-defined property from a ref VariableDeclaration
  * @param TES4VariableDeclaration $declaration
  * @return TES5Variable
  */
 private function createPropertyFromReference(TES4VariableDeclaration $declaration)
 {
     return new TES5Property($declaration->getVariableName(), TES5BasicType::T_FORM(), $declaration->getVariableName());
 }
Ejemplo n.º 3
0
 public function getType()
 {
     return TES5BasicType::T_FORM();
 }
 /**
  * Create a generic-purpose reference.
  * @param $referenceName
  * @param TES5GlobalScope $globalScope
  * @param TES5MultipleScriptsScope $multipleScriptsScope
  * @param TES5LocalScope $localScope
  * @return \Ormin\OBSLexicalParser\TES5\AST\Object\TES5ObjectProperty|TES5PlayerReference|TES5Reference
  */
 public function createReference($referenceName, TES5GlobalScope $globalScope, TES5MultipleScriptsScope $multipleScriptsScope, TES5LocalScope $localScope)
 {
     //Papyrus compiler somehow treats properties with ,,temp" in them in a special way, so we change them to tmp to accomodate that.
     if (preg_match('#temp#', $referenceName)) {
         $referenceName = preg_replace("#temp#i", "tmp", $referenceName);
     }
     if (strtolower($referenceName) == "player") {
         return $this->createReferenceToPlayer();
     }
     if (preg_match("#([0-9a-zA-Z]+)\\.([0-9a-zA-Z]+)#i", $referenceName, $matches)) {
         $mainReference = $this->createReference($matches[1], $globalScope, $multipleScriptsScope, $localScope);
         $propertyReference = $this->objectPropertyFactory->createObjectProperty($multipleScriptsScope, $mainReference, $matches[2]);
         //Todo rethink the prefix adding
         return $propertyReference;
     }
     $property = $localScope->getVariableByName($referenceName);
     if ($property === null) {
         $property = $globalScope->getPropertyByName($referenceName);
         //todo rethink how to unify the prefix searching
         if ($property === null) {
             foreach ($this->special_conversions as $special_conversion_name => $special_type) {
                 if ($special_conversion_name == $referenceName) {
                     $property = new TES5Property($referenceName, $special_type, $referenceName);
                     break;
                 }
             }
             if ($property === null) {
                 if (!$globalScope->hasGlobalVariable($referenceName)) {
                     $property = new TES5Property($referenceName, TES5BasicType::T_FORM(), $referenceName);
                 } else {
                     $property = new TES5Property($referenceName, TES5BasicType::T_GLOBALVARIABLE(), $referenceName);
                 }
             }
             $globalScope->add($property);
         }
     }
     return new TES5Reference($property);
 }
 private function convertArithmeticExpression(TES4Expression $expression, TES5CodeScope $codeScope, TES5GlobalScope $globalScope, TES5MultipleScriptsScope $multipleScriptsScope)
 {
     $sets = [[$expression->getLeftValue(), $expression->getRightValue()], [$expression->getRightValue(), $expression->getLeftValue()]];
     /**
      * Scenario 1 - Special functions converted on expression level
      * @var TES4Value[] $set
      */
     foreach ($sets as $set) {
         if (!$set[0] instanceof TES4Callable) {
             continue;
         }
         $function = $set[0]->getFunction();
         switch (strtolower($function->getFunctionCall()->getFunctionName())) {
             case "getweaponanimtype":
                 $calledOn = $this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope);
                 $leftValue = $this->createObjectCall($this->createObjectCall($calledOn, "GetEquippedWeapon", $multipleScriptsScope), "GetWeaponType", $multipleScriptsScope);
                 switch ((int) $set[1]->getData()) {
                     case 0:
                         $targetedWeaponTypes = [0];
                         break;
                     case 1:
                         $targetedWeaponTypes = [1, 2, 3, 4];
                         break;
                     case 2:
                         $targetedWeaponTypes = [5, 6, 8];
                         break;
                     case 3:
                         $targetedWeaponTypes = [7, 9];
                         break;
                     default:
                         throw new ConversionException("GetWeaponAnimType() - Unknown weapon type in expression");
                 }
                 $expressions = [];
                 foreach ($targetedWeaponTypes as $targetedWeaponType) {
                     $expressions[] = $this->expressionFactory->createArithmeticExpression($leftValue, TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), new TES5Integer($targetedWeaponType));
                 }
                 $resultExpression = $expressions[0];
                 unset($expressions[0]);
                 while (!empty($expressions)) {
                     $resultExpression = $this->expressionFactory->createLogicalExpression($resultExpression, TES5LogicalExpressionOperator::OPERATOR_OR(), array_pop($expressions));
                 }
                 return $resultExpression;
             case "getdetected":
                 $inversedArgument = new TES5ObjectCallArguments();
                 $inversedArgument->add($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope));
                 $leftValue = $this->createObjectCall($this->createReadReference($function->getArguments()->getValue(0)->getData(), $globalScope, $multipleScriptsScope, $codeScope->getLocalScope()), "isDetectedBy", $multipleScriptsScope, $inversedArgument);
                 $rightValue = new TES5Integer((int) $set[1]->getData() == 0 ? 0 : 1);
                 $expression = $this->expressionFactory->createArithmeticExpression($leftValue, TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), $rightValue);
                 return $expression;
             case "getdetectionlevel":
                 if (!$set[1]->hasFixedValue()) {
                     throw new ConversionException("Cannot convert getDetectionLevel calls with dynamic comparision");
                 }
                 $boolean = new TES5Bool($set[1]->getData() == 3);
                 //true only if the compared value was 3
                 $inversedArgument = new TES5ObjectCallArguments();
                 $inversedArgument->add($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope));
                 $expression = $this->expressionFactory->createArithmeticExpression($this->createObjectCall($this->createReadReference($function->getArguments()->getValue(0)->getData(), $globalScope, $multipleScriptsScope, $codeScope->getLocalScope()), "isDetectedBy", $multipleScriptsScope, $inversedArgument), TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), $boolean);
                 return $expression;
             case "getcurrentaiprocedure":
                 if (!$set[1]->hasFixedValue()) {
                     throw new ConversionException("Cannot convert getCurrentAIProcedure() calls with dynamic comparision");
                 }
                 switch ((int) $set[1]->getData()) {
                     case 4:
                         //ref.getSleepState() == 3
                         $expression = $this->expressionFactory->createArithmeticExpression($this->createObjectCall($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope), "IsInDialogueWithPlayer", $multipleScriptsScope), TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), new TES5Bool((bool) $expression->getOperator() == TES4ArithmeticExpressionOperator::OPERATOR_EQUAL()));
                         return $expression;
                     case 8:
                         //ref.getSleepState() == 3
                         $expression = $this->expressionFactory->createArithmeticExpression($this->createObjectCall($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope), "getSleepState", $multipleScriptsScope), TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), new TES5Integer(3));
                         return $expression;
                     case 13:
                         //ref.getSleepState() == 3
                         $expression = $this->expressionFactory->createArithmeticExpression($this->createObjectCall($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope), "IsInCombat", $multipleScriptsScope), TES5ArithmeticExpressionOperator::OPERATOR_EQUAL(), new TES5Bool((bool) $expression->getOperator() == TES4ArithmeticExpressionOperator::OPERATOR_EQUAL()));
                         return $expression;
                     case 0:
                     case 7:
                     case 15:
                     case 17:
                         //@INCONSISTENCE Wander.. idk how to check it tbh. We return always true. Think about better representation
                         return new TES5Bool((bool) $expression->getOperator() == TES4ArithmeticExpressionOperator::OPERATOR_EQUAL());
                     default:
                         throw new ConversionException("Cannot convert GetCurrentAiProcedure - unknown TES4 procedure number arg " . (int) $set[1]->getData());
                 }
                 break;
             case "isidleplaying":
             case "getknockedstate":
             case "gettalkedtopc":
                 return new TES5Bool(true);
                 //This is so unimportant that i think it's not worth to find a good alternative and waste time.
             case "getsitting":
                 //WARNING: Needs to implement Horse sittings, too.
                 //SEE: http://www.gameskyrim.com/papyrus-isridinghorse-function-t255012.html
                 switch ((int) $set[1]->getData()) {
                     case 0:
                         $goTo = 0;
                         break;
                     case 1:
                     case 2:
                     case 11:
                     case 12:
                         $goTo = 2;
                         break;
                     case 3:
                     case 13:
                         $goTo = 3;
                         break;
                     case 4:
                     case 14:
                         $goTo = 4;
                         break;
                     default:
                         throw new ConversionException("GetSitting - unknown state found");
                 }
                 //ref.getSleepState() == 3
                 $expression = $this->expressionFactory->createArithmeticExpression($this->createObjectCall($this->createCalledOnReferenceOfCalledFunction($set[0], $codeScope, $globalScope, $multipleScriptsScope), "GetSitState", $multipleScriptsScope), TES5ArithmeticExpressionOperator::memberByValue($expression->getOperator()->value()), new TES5Integer($goTo));
                 return $expression;
         }
     }
     $leftValue = $this->createValue($expression->getLeftValue(), $codeScope, $globalScope, $multipleScriptsScope);
     $rightValue = $this->createValue($expression->getRightValue(), $codeScope, $globalScope, $multipleScriptsScope);
     $tes5sets = [[$leftValue, $rightValue], [$rightValue, $leftValue]];
     $objectReferenceType = TES5BasicType::T_FORM();
     //used just to make sure.
     $operator = TES5ArithmeticExpressionOperator::memberByValueWithDefault($expression->getOperator()->value(), null);
     /**
      * @var TES5Value[] $tes5set
      * Scenario 2: Comparision of ObjectReferences to integers ( quick formid check )
      */
     foreach ($tes5sets as $tes5set) {
         if ($tes5set[0]->getType() == $objectReferenceType || TES5InheritanceGraphAnalyzer::isExtending($tes5set[0]->getType(), $objectReferenceType)) {
             if ($tes5set[1]->getType() == TES5BasicType::T_INT()) {
                 $tes5set[0] = $this->createObjectCall($tes5set[0], "GetFormID", $multipleScriptsScope);
                 return $this->expressionFactory->createArithmeticExpression($tes5set[0], $operator, $tes5set[1]);
             }
         } else {
             if ($tes5set[0]->getType() == TES5TypeFactory::void()) {
                 if ($tes5set[1] instanceof TES5Integer || $tes5set[1] instanceof TES5Float) {
                     if ($tes5set[1]->getValue() == 0) {
                         return $this->expressionFactory->createArithmeticExpression($tes5set[0], $operator, new TES5None());
                     }
                 }
             }
         }
     }
     return $this->expressionFactory->createArithmeticExpression($leftValue, $operator, $rightValue);
 }
 public function createFromBlockType($blockType, TES5GlobalScope $globalScope)
 {
     $localScope = new TES5LocalScope();
     switch ($blockType) {
         case 'OnUpdate':
             break;
         case 'OnActivate':
             $localScope->addVariable(new TES5LocalVariable("akActivateRef", TES5BasicType::T_OBJECTREFERENCE(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnInit':
             break;
         case 'OnSell':
             $localScope->addVariable(new TES5LocalVariable("akSeller", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnDeath':
             $localScope->addVariable(new TES5LocalVariable("akKiller", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnLoad':
             break;
         case 'OnObjectEquipped':
             $localScope->addVariable(new TES5LocalVariable("akBaseObject", TES5BasicType::T_FORM(), [TES5LocalVariableParameterMeaning::CONTAINER()]));
             $localScope->addVariable(new TES5LocalVariable("akReference", TES5BasicType::T_OBJECTREFERENCE()));
             break;
         case 'OnTriggerEnter':
             $localScope->addVariable(new TES5LocalVariable("akActivateRef", TES5BasicType::T_OBJECTREFERENCE(), [TES5LocalVariableParameterMeaning::ACTIVATOR(), TES5LocalVariableParameterMeaning::CONTAINER()]));
             break;
         case 'OnEquipped':
             $localScope->addVariable(new TES5LocalVariable("akActor", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR(), TES5LocalVariableParameterMeaning::CONTAINER()]));
             break;
         case 'OnUnequipped':
             $localScope->addVariable(new TES5LocalVariable("akActor", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnContainerChanged':
             $localScope->addVariable(new TES5LocalVariable("akNewContainer", TES5BasicType::T_OBJECTREFERENCE()));
             $localScope->addVariable(new TES5LocalVariable("akOldContainer", TES5BasicType::T_OBJECTREFERENCE()));
             break;
         case 'OnTrigger':
             $localScope->addVariable(new TES5LocalVariable("akActivateRef", TES5BasicType::T_OBJECTREFERENCE(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnHit':
             $localScope->addVariable(new TES5LocalVariable("akAggressor", TES5BasicType::T_OBJECTREFERENCE(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             $localScope->addVariable(new TES5LocalVariable("akSource", TES5BasicType::T_FORM()));
             $localScope->addVariable(new TES5LocalVariable("akProjectile", TES5BasicType::T_PROJECTILE()));
             $localScope->addVariable(new TES5LocalVariable("abPowerAttack", TES5BasicType::T_BOOL()));
             $localScope->addVariable(new TES5LocalVariable("abSneakAttack", TES5BasicType::T_BOOL()));
             $localScope->addVariable(new TES5LocalVariable("abBashAttack", TES5BasicType::T_BOOL()));
             $localScope->addVariable(new TES5LocalVariable("abHitBlocked", TES5BasicType::T_BOOL()));
             break;
         case 'OnCombatStateChanged':
             $localScope->addVariable(new TES5LocalVariable("akTarget", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             $localScope->addVariable(new TES5LocalVariable("aeCombatState", TES5BasicType::T_INT()));
             break;
         case 'OnPackageStart':
             $localScope->addVariable(new TES5LocalVariable("akNewPackage", TES5BasicType::T_PACKAGE()));
             break;
         case 'OnPackageDone':
             $localScope->addVariable(new TES5LocalVariable("akDonePackage", TES5BasicType::T_PACKAGE()));
             break;
         case 'OnPackageEnd':
             $localScope->addVariable(new TES5LocalVariable("akOldPackage", TES5BasicType::T_PACKAGE()));
             break;
         case 'OnPackageChange':
             $localScope->addVariable(new TES5LocalVariable("akOldPackage", TES5BasicType::T_PACKAGE()));
             break;
         case 'OnMagicEffectApply':
             $localScope->addVariable(new TES5LocalVariable("akCaster", TES5BasicType::T_OBJECTREFERENCE(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             $localScope->addVariable(new TES5LocalVariable("akMagicEffect", TES5BasicType::T_MAGICEFFECT()));
             break;
         case 'OnReset':
             break;
         case 'OnEffectStart':
             $localScope->addVariable(new TES5LocalVariable("akTarget", TES5BasicType::T_ACTOR()));
             $localScope->addVariable(new TES5LocalVariable("akCaster", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         case 'OnEffectFinish':
             $localScope->addVariable(new TES5LocalVariable("akTarget", TES5BasicType::T_ACTOR()));
             $localScope->addVariable(new TES5LocalVariable("akCaster", TES5BasicType::T_ACTOR(), [TES5LocalVariableParameterMeaning::ACTIVATOR()]));
             break;
         default:
             throw new ConversionException("Cannot find new block type local scope variables out of " . $blockType);
     }
     return $localScope;
 }
 protected function execute(InputInterface $input, OutputInterface $output)
 {
     set_time_limit(10800);
     $target = 'Standalone';
     $errorLog = fopen("graph_error_log", "w+");
     $log = fopen("graph_debug_log", "w+");
     $buildTarget = BuildTargetFactory::get($target);
     if (count(array_slice(scandir($buildTarget->getWorkspacePath()), 2)) > 0 || count(array_slice(scandir($buildTarget->getTranspiledPath()), 2)) > 0 || count(array_slice(scandir($buildTarget->getArtifactsPath()), 2)) > 0) {
         $output->writeln("Target " . $target . " current build dir not clean, archive it manually.");
         return;
     }
     $sourceFiles = array_slice(scandir($buildTarget->getSourcePath()), 2);
     $inferencer = new TES5TypeInferencer(new ESMAnalyzer(new TypeMapper()), $buildTarget->getSourcePath());
     $dependencyGraph = [];
     $usageGraph = [];
     $progressBar = new CliProgressBar(count($sourceFiles));
     $progressBar->display();
     foreach ($sourceFiles as $sourceFile) {
         try {
             $scriptName = substr($sourceFile, 0, -4);
             $AST = $buildTarget->getAST($buildTarget->getSourceFromPath($scriptName));
             /**
              * @var TES4ObjectProperty[] $propertiesAccesses
              */
             $propertiesAccesses = [];
             $AST->filter(function ($data) use(&$propertiesAccesses) {
                 if ($data instanceof TES4ObjectProperty) {
                     $propertiesAccesses[] = $data;
                 }
             });
             /**
              * @var TES5Property[] $preparedProperties
              */
             $preparedProperties = [];
             /**
              * @var TES5Type[] $preparedPropertiesTypes
              */
             $preparedPropertiesTypes = [];
             foreach ($propertiesAccesses as $property) {
                 preg_match("#([0-9a-zA-Z]+)\\.([0-9a-zA-Z]+)#i", $property->getData(), $matches);
                 $propertyName = $matches[1];
                 $propertyKeyName = strtolower($propertyName);
                 if (!isset($preparedProperties[$propertyKeyName])) {
                     $preparedProperty = new TES5Property($propertyName, TES5BasicType::T_FORM(), $matches[1]);
                     $preparedProperties[$propertyKeyName] = $preparedProperty;
                     $inferencingType = $inferencer->resolveInferenceTypeByReferenceEdid($preparedProperty);
                     $preparedPropertiesTypes[$propertyKeyName] = $inferencingType;
                 } else {
                     $preparedProperty = $preparedProperties[$propertyKeyName];
                     $inferencingType = $inferencer->resolveInferenceTypeByReferenceEdid($preparedProperty);
                     if ($inferencingType != $preparedPropertiesTypes[$propertyKeyName]) {
                         throw new ConversionException("Cannot settle up the properties types - conflict.");
                     }
                 }
             }
             fwrite($log, $scriptName . " - " . count($preparedProperties) . " prepared" . PHP_EOL);
             foreach ($preparedProperties as $preparedPropertyKey => $preparedProperty) {
                 //Only keys are lowercased.
                 $lowerPropertyType = strtolower($preparedPropertiesTypes[$preparedPropertyKey]->value());
                 $lowerScriptType = strtolower($scriptName);
                 if (!isset($dependencyGraph[$lowerPropertyType])) {
                     $dependencyGraph[$lowerPropertyType] = [];
                 }
                 $dependencyGraph[$lowerPropertyType][] = $lowerScriptType;
                 if (!isset($usageGraph[$lowerScriptType])) {
                     $usageGraph[$lowerScriptType] = [];
                 }
                 $usageGraph[$lowerScriptType][] = $lowerPropertyType;
                 fwrite($log, 'Registering a dependency from ' . $scriptName . ' to ' . $preparedPropertiesTypes[$preparedPropertyKey]->value() . PHP_EOL);
             }
             $progressBar->progress();
         } catch (\Exception $e) {
             fwrite($errorLog, $sourceFile . PHP_EOL . $e->getMessage());
             continue;
         }
     }
     $progressBar->end();
     $graph = new TES5ScriptDependencyGraph($dependencyGraph, $usageGraph);
     file_put_contents('app/graph', serialize($graph));
     fclose($errorLog);
     fclose($log);
 }