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;
     }
 }