LOCALE_EN, 'enGB' => LOCALE_EN, 'enUS' => LOCALE_EN, 'frFR' => LOCALE_FR, 'deDE' => LOCALE_DE, 'esES' => LOCALE_ES, 'esMX' => LOCALE_ES, 'ruRU' => LOCALE_RU ); public static function init() { self::$win = substr(PHP_OS, 0, 3) == 'WIN'; if ($_ = getopt('d', ['log::', 'locales::', 'mpqDataDir::', 'delete'])) { // optional logging if (!empty($_['log'])) self::$logFile = trim($_['log']); // alternative data source (no quotes, use forward slash) if (!empty($_['mpqDataDir'])) self::$srcDir = str_replace(['\\', '"', '\''], ['/', '', ''], $_['mpqDataDir']); // optional limit handled locales if (!empty($_['locales'])) { // engb and enus are identical for all intents and purposes $from = ['engb', 'esmx']; $to = ['enus', 'eses']; $_['locales'] = str_ireplace($from, $to, strtolower($_['locales'])); self::$locales = array_intersect(Util::$localeStrings, explode(',', $_['locales'])); } if (isset($_['d']) || isset($_['delete'])) self::$tmpDBC = true; } if (!self::$locales) self::$locales = array_filter(Util::$localeStrings); // restrict actual locales foreach (self::$locales as $idx => $str) if (!defined('CFG_LOCALES') || CFG_LOCALES & (1 << $idx)) self::$localeIds[] = $idx; } /*******************/ /* MPQ-file access */ /*******************/ /* the problem 1) paths provided in dbc files are case-insensitive and random 2) paths to the actual textures contained in the mpq archives are case-insensitive and random unix systems will throw a fit if you try to get from one to the other, so lets save the paths from 2) and cast it to lowercase lookups will be done in lowercase. A successfull match will return the real path. */ private static function buildFileList() { self::log(); self::log('reading MPQdata from '.self::$srcDir.' to list for first time use...'); $setupDirs = glob('setup/*'); foreach ($setupDirs as $sd) { if (substr(self::$srcDir, -1) == '/') self::$srcDir = substr(self::$srcDir, 0, -1); if (substr($sd, -1) == '/') $sd = substr($sd, 0, -1); if (strtolower($sd) == strtolower(self::$srcDir)) { self::$srcDir = $sd.'/'; break; } } try { $iterator = new RecursiveDirectoryIterator(self::$srcDir); $iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS); foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST) as $path) { $_ = str_replace('\\', '/', $path->getPathname()); self::$mpqFiles[strtolower($_)] = $_; } self::log('done'); self::log(); } catch (UnexpectedValueException $e) { self::log('- mpqData dir '.self::$srcDir.' does not exist', self::LOG_ERROR); return false; } return true; } public static function fileExists(&$file) { // read mpq source file structure to tree if (!self::$mpqFiles) if (!self::buildFileList()) return false; // backslash to forward slash $_ = strtolower(str_replace('\\', '/', $file)); // remove trailing slash if (substr($_, -1, 1) == '/') $_ = substr($_, 0, -1); if (isset(self::$mpqFiles[$_])) { $file = self::$mpqFiles[$_]; return true; } return false; } public static function filesInPath($path, $useRegEx = false) { $result = []; // read mpq source file structure to tree if (!self::$mpqFiles) if (!self::buildFileList()) return []; // backslash to forward slash $_ = strtolower(str_replace('\\', '/', $path)); foreach (self::$mpqFiles as $lowerFile => $realFile) { if (!$useRegEx && strstr($lowerFile, $_)) $result[] = $realFile; else if ($useRegEx && preg_match($path, $lowerFile)) $result[] = $realFile; } return $result; } /***********/ /* logging */ /***********/ public static function red($str) { return "\e[31m".$str."\e[0m"; } public static function green($str) { return "\e[32m".$str."\e[0m"; } public static function yellow($str) { return "\e[33m".$str."\e[0m"; } public static function bold($str) { return "\e[1m".$str."\e[0m"; } public static function log($txt = '', $lvl = -1) { if (self::$logFile && !self::$logHandle) { if (!file_exists(self::$logFile)) self::$logHandle = fopen(self::$logFile, 'w'); else { $logFileParts = pathinfo(self::$logFile); $i = 1; while (file_exists($logFileParts['dirname'].'/'.$logFileParts['filename'].$i.(isset($logFileParts['extension']) ? '.'.$logFileParts['extension'] : ''))) $i++; self::$logFile = $logFileParts['dirname'].'/'.$logFileParts['filename'].$i.(isset($logFileParts['extension']) ? '.'.$logFileParts['extension'] : ''); self::$logHandle = fopen(self::$logFile, 'w'); } } $msg = "\n"; if ($txt) { $msg = str_pad(date('H:i:s'), 10); switch ($lvl) { case self::LOG_ERROR: // red error $msg .= '['.self::red('ERR').'] '; break; case self::LOG_WARN: // yellow warn $msg .= '['.self::yellow('INFO').'] '; break; case self::LOG_OK: // green success $msg .= '['.self::green('OK').'] '; break; default: $msg .= ' '; } $msg .= $txt."\n"; } // remove highlights for logging & win $raw = preg_replace(["/\e\[\d+m/", "/\e\[0m/"], '', $msg); echo self::$win ? $raw : $msg; if (self::$logHandle) fwrite(self::$logHandle, $raw); flush(); } /*****************/ /* file handling */ /*****************/ public static function writeFile($file, $content) { $success = false; if ($handle = @fOpen($file, "w")) { if (fWrite($handle, $content)) { $success = true; self::log(sprintf(ERR_NONE, self::bold($file)), self::LOG_OK); } else self::log(sprintf(ERR_WRITE_FILE, self::bold($file)), self::LOG_ERROR); fClose($handle); } else self::log(sprintf(ERR_CREATE_FILE, self::bold($file)), self::LOG_ERROR); if ($success) @chmod($file, self::FILE_ACCESS); return $success; } public static function writeDir($dir) { if (is_dir($dir)) { if (!is_writable($dir) && !@chmod($dir, self::FILE_ACCESS)) self::log('cannot write into output directory '.$dir, self::LOG_ERROR); return is_writable($dir); } if (@mkdir($dir, self::FILE_ACCESS, true)) return true; self::log('could not create output directory '.$dir, self::LOG_ERROR); return false; } public static function loadDBC($name) { if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'dbc_'.$name) && DB::Aowow()->selectCell('SELECT count(1) FROM ?#', 'dbc_'.$name)) return true; $dbc = new DBC($name, self::$tmpDBC); if ($dbc->error) return false; if ($dbc->readFromFile()) { $dbc->writeToDB(); return true; } self::log('SqlGen::generate() - required DBC '.$name.'.dbc found neither in DB nor as file!', self::LOG_ERROR); return false; } /**************/ /* read input */ /**************/ public static function readInput(&$fields, $singleChar = false) { // prevent default output readline_callback_handler_install('', function() { }); foreach ($fields as $name => $data) { $vars = ['desc', 'isHidden', 'validPattern']; foreach ($vars as $idx => $v) $$v = isset($data[$idx]) ? $data[$idx] : false; $charBuff = ''; if ($desc) echo "\n".$desc.": "; while (true) { $r = [STDIN]; $w = $e = null; $n = stream_select($r, $w, $e, 200000); if ($n && in_array(STDIN, $r)) { $char = stream_get_contents(STDIN, 1); $keyId = ord($char); if ($keyId == self::CHR_TAB) // ignore this one continue; if ($keyId == self::CHR_ESC) { echo chr(self::CHR_BELL); return false; } else if ($keyId == self::CHR_BACKSPACE) { if (!$charBuff) continue; $charBuff = substr($charBuff, 0, -1); echo chr(self::CHR_BACK)." ".chr(self::CHR_BACK); } else if ($keyId == self::CHR_RETURN) { $fields[$name] = $charBuff; break; } else if (!$validPattern || preg_match($validPattern, $char)) { $charBuff .= $char; if (!$isHidden) echo $char; if ($singleChar) { $fields[$name] = $charBuff; break; } } } } } echo chr(self::CHR_BELL); foreach ($fields as $f) if (strlen($f)) return true; $fields = null; return true; } } ?>