public function __construct(TES5ObjectPropertyFactory $objectPropertyFactory) { $this->objectPropertyFactory = $objectPropertyFactory; //Those are used to hook in the internal Skyblivion systems. $special_conversions = ['TES4AttrStrength' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrIntelligence' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrWillpower' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrAgility' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrSpeed' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrEndurance' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrPersonality' => TES5BasicType::T_GLOBALVARIABLE(), 'TES4AttrLuck' => TES5BasicType::T_GLOBALVARIABLE(), 'tContainer' => TES5TypeFactory::memberByValue("TES4Container", TES5BasicType::T_QUEST()), 'tTimer' => TES5TypeFactory::memberByValue("TES4TimerHelper", TES5BasicType::T_QUEST()), 'tGSPLocalTimer' => TES5BasicType::T_FLOAT(), 'TES4CyrodiilCrimeFaction' => TES5BasicType::T_FACTION(), self::MESSAGEBOX_VARIABLE_CONST => TES5BasicType::T_INT()]; $this->special_conversions = $special_conversions; }
public function __construct($scriptName, $edid, TES5Type $scriptType, $scriptNamePrefix, $isHidden = false) { $this->scriptName = $scriptName; $this->edid = $edid; $this->scriptNamePrefix = $scriptNamePrefix; $this->scriptType = TES5TypeFactory::memberByValue($scriptName, $scriptType); $this->basicScriptType = $scriptType; $this->isHidden = $isHidden; $this->inheritanceAnalyzer = new TES5InheritanceGraphAnalyzer(); }
/** * @param TES5Variable $variable * @return \Ormin\OBSLexicalParser\TES5\Types\TES5CustomType * @throws ConversionException */ public function resolveInferenceTypeByReferenceEdid(TES5Variable $variable) { $base = $variable->getReferenceEdid(); $tryAs = [$base, $base . 'Script']; if (strtolower(substr($base, -3, 3)) == "ref") { $tryAsRef = substr($base, 0, -3); $tryAs[] = $tryAsRef; $tryAs[] = $tryAsRef . 'Script'; } $tryAs = array_unique($tryAs); foreach ($tryAs as $try) { if (in_array(strtolower($try), $this->otherScripts)) { return TES5TypeFactory::memberByValue($try); } } //If it's not found, we're forced to scan the ESM to see, how to resolve the ref name to script type return $this->esmAnalyzer->resolveScriptTypeByItsAttachedName($variable->getReferenceEdid()); }
/** * @param $attachedName * @return \Eloquent\Enumeration\ValueMultitonInterface|\Ormin\OBSLexicalParser\TES5\Types\TES5CustomType|\Ormin\OBSLexicalParser\TES5\Types\TES5VoidType * @todo REFACTOR, it's really ugly! * @throws ConversionException */ public function resolveScriptTypeByItsAttachedName($attachedName) { if (!isset($this->attachedNameCache[strtolower($attachedName)])) { if (preg_match("#REFR................EDID..(?i)" . $attachedName . "(?-i)\\x{00}NAME..(....)#s", $this->esm, $refrFormidMatches) || preg_match("#ACRE................EDID..(?i)" . $attachedName . "(?-i)\\x{00}NAME..(....)#s", $this->esm, $acreFormidMatches) || preg_match("#ACHR................EDID..(?i)" . $attachedName . "(?-i)\\x{00}NAME..(....)#s", $this->esm, $achrFormidMatches)) { if (!empty($refrFormidMatches)) { $formidMatches = $refrFormidMatches; $searchedFormType = "(?!SCPT)[A-Z]{4}"; //TODO - this can be a specific list of objects, perhaps do a search of this list? } else { if (!empty($acreFormidMatches)) { $formidMatches = $acreFormidMatches; $searchedFormType = "CREA"; } else { $formidMatches = $achrFormidMatches; $searchedFormType = "NPC_"; } } //We have a REFR, we have to unpack it and match the formid $targetFormid = $formidMatches[1]; $targetFormidString = ""; for ($i = 0; $i < 4; ++$i) { $hexCharacter = dechex(ord(substr($targetFormid, $i, 1))); if (strlen($hexCharacter) == 1) { $hexCharacter = '0' . $hexCharacter; } $targetFormidString .= "\\x{" . $hexCharacter . "}"; } if ($searchedFormType != "NPC_") { preg_match("#" . $searchedFormType . "........" . $targetFormidString . ".*?SCRI..(....)#s", $this->esm, $matches); } else { if (preg_match("#NPC_(....)...." . $targetFormidString . "#s", $this->esm, $failoverMatches, PREG_OFFSET_CAPTURE)) { $baseDataOffset = $failoverMatches[0][1] + 24; $dataLengthMatch = $failoverMatches[1][0]; $dataLength = 0; for ($i = 0; $i < 4; ++$i) { $dataLength += ord($dataLengthMatch[$i]) * pow(256, $i); } $dataLength -= 4; $gzippedData = substr($this->esm, $baseDataOffset, $dataLength); $ungzippedData = gzuncompress($gzippedData); preg_match("#SCRI..(....)#si", $ungzippedData, $matches); } } } else { //Just go with usual matching via EDID preg_match("#EDID..(?i)" . $attachedName . "(?-i)\\x{00}.*?SCRI..(....)#s", $this->esm, $matches); } if (empty($matches)) { throw new ConversionException("Cannot resolve script type by searching its base form edid " . $attachedName); } $hex = $matches[1]; $hexString = ""; $hexFormid = ""; for ($i = 0; $i < 4; ++$i) { $hexCharacter = dechex(ord(substr($hex, $i, 1))); if (strlen($hexCharacter) == 1) { $hexCharacter = '0' . $hexCharacter; } $hexString .= "\\x{" . $hexCharacter . "}"; $hexFormid .= $hexCharacter; } preg_match("#SCPT........" . $hexString . "....EDID..([a-zA-Z0-9]+)#si", $this->esm, $dataMatches); if (empty($dataMatches)) { throw new ConversionException("For EDID " . $attachedName . " and script formid " . $hexFormid . " we couldn't find any scripts in ESM."); } $customType = TES5TypeFactory::memberByValue($dataMatches[1]); $this->attachedNameCache[strtolower($attachedName)] = $customType; } return $this->attachedNameCache[strtolower($attachedName)]; }
public static function map($type) { switch ($type) { case "AACT": $propertyType = 'Action'; break; case "ACTI": $propertyType = 'Activator'; break; case "ACHR": case "ACRE": case "NPC_": $propertyType = 'Actor'; break; case "AMMO": $propertyType = 'Ammo'; break; case "APPA": $propertyType = 'Apparatus'; break; case "CREA": $propertyType = "Actor"; break; case "CLMT": $propertyType = "Climate"; break; case "CLOT": case "ARMO": $propertyType = 'Armor'; break; case "ASTP": $propertyType = 'AssociationType'; break; case "BOOK": $propertyType = 'Book'; break; case "CELL": $propertyType = 'Cell'; break; case "CLAS": $propertyType = 'Class'; break; case "COBJ": $propertyType = 'ConstructibleObject'; break; case "CONT": $propertyType = 'Container'; break; case "DOOR": $propertyType = 'Door'; break; case "EFSH": $propertyType = 'EffectShader'; break; case "ENCH": $propertyType = 'Enchantment'; break; case "ECNZ": $propertyType = 'EncounterZone'; break; case "EXPL": $propertyType = 'Explosion'; break; case "FACT": $propertyType = 'Faction'; break; case "FLOR": $propertyType = 'Flora'; break; case "FLST": $propertyType = 'FormList'; break; case "FURN": $propertyType = 'Furniture'; break; case "GLOB": $propertyType = 'GlobalVariable'; break; case "HAZD": $propertyType = 'Hazard'; break; case "IMAD": $propertyType = 'ImageSpaceModifier'; break; case "IPDS": $propertyType = 'ImpactDataSet'; break; case "INGR": $propertyType = 'Ingredient'; break; case "KEYM": $propertyType = 'Key'; break; case "KYWD": $propertyType = 'Keyword'; break; case "LVLN": $propertyType = 'LeveledActor'; break; case "LVLI": $propertyType = 'LeveledItem'; break; case "LVSP": $propertyType = 'LeveledSpell'; break; case "LIGH": $propertyType = 'Light'; break; case "LCTN": $propertyType = 'Location'; break; case "LCRT": $propertyType = 'LocationRefType'; break; case "MGEF": $propertyType = 'MagicEffect'; break; case "MATH": $propertyType = 'Math'; break; case "MESG": $propertyType = 'Message'; break; case "MISC": $propertyType = 'MiscObject'; break; case "MUSC": $propertyType = 'MusicType'; break; case "REFR": $propertyType = 'ObjectReference'; break; case "OTFT": $propertyType = 'Outfit'; break; case "PACK": $propertyType = 'Package'; break; case "PERK": $propertyType = 'Perk'; break; case "ALCH": $propertyType = 'Potion'; break; case "PROJ": $propertyType = 'Projectile'; break; case "QUST": $propertyType = 'Quest'; break; case "RACE": $propertyType = 'Race'; break; case "SCEN": $propertyType = 'Scene'; break; case "SCRL": $propertyType = 'Scroll'; break; case "SHOU": $propertyType = 'Shout'; break; case "SLGM": $propertyType = 'SoulGem'; break; case "SOUN": $propertyType = 'Sound'; break; case "SNCT": $propertyType = 'SoundCategory'; break; case "SPEL": $propertyType = 'Spell'; break; case "STAT": $propertyType = 'Static'; break; case "TACT": $propertyType = 'TalkingActivator'; break; case "DIAL": $propertyType = 'Topic'; break; case "INFO": $propertyType = 'TopicInfo'; break; case "RFCT": $propertyType = 'VisualEffect'; break; case "VTYP": $propertyType = 'VoiceType'; break; case "WEAP": $propertyType = 'Weapon'; break; case "WTHR": $propertyType = 'Weather'; break; case "WOOP": $propertyType = 'WordOfPower'; break; case "WRLD": $propertyType = 'WorldSpace'; break; default: throw new ConversionException("Unknown " . $type . " formid reference."); } return TES5TypeFactory::memberByValue($propertyType); }
public function getType() { return TES5TypeFactory::memberByValue($this->getName()); }
/** * @param TES5ObjectCall $objectCall * @return mixed|TES5Type * @throws \Ormin\OBSLexicalParser\TES5\Exception\ConversionException * */ public static function findTypeByMethod(TES5ObjectCall $objectCall) { $methodName = $objectCall->getFunctionName(); $possibleMatches = []; foreach (self::$callReturns as $type => $methods) { foreach ($methods as $method => $functionData) { if (strtolower($method) == strtolower($methodName)) { $possibleMatches[] = TES5TypeFactory::memberByValue($type); } } } $calledOn = $objectCall->getAccessedObject()->getReferencesTo(); $extendingMatches = []; $actualType = $calledOn->getPropertyType()->getNativeType(); /** * @var TES5Type[] $possibleMatches */ foreach ($possibleMatches as $possibleMatch) { if ($possibleMatch == $actualType) { return $possibleMatch; //if the possible match matches the actual basic type, it means that it surely IS one of those. } //Ok, so are those matches somehow connected at all? if (TES5InheritanceGraphAnalyzer::isExtending($possibleMatch, $actualType) || TES5InheritanceGraphAnalyzer::isExtending($actualType, $possibleMatch)) { $extendingMatches[] = $possibleMatch; } } switch (count($extendingMatches)) { case 0: $concatTypes = []; foreach ($possibleMatches as $possibleMatch) { $concatTypes[] = $possibleMatch->value(); } throw new ConversionException("Cannot find any possible type for method " . $methodName . ", trying to extend " . $actualType->value() . " with following types: " . implode(', ', $concatTypes)); case 1: return current($extendingMatches); default: //We analyze the property name and check inside the ESM analyzer. $formType = ESMAnalyzer::instance()->getFormTypeByEDID($calledOn->getReferenceEdid()); if (!in_array($formType, $extendingMatches)) { throw new ConversionException("ESM <-> Inheritance Graph conflict, ESM returned " . $formType->value() . ", which is not present in possible matches from inheritance graph"); } return $formType; } }
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); }