public function __construct()
 {
     $this->basedir = realpath(dirname(__FILE__));
     if (is_executable("{$this->basedir}/../bin/pawncc.exe")) {
         $this->pawncc_path = realpath("{$this->basedir}/../bin/pawncc.exe");
     }
     if (!isset(self::$compiler_args)) {
         self::$compiler_args = array_fill_keys(explode(' ', 'A a C c D d e H i l o O p r S s t v w X XD \\ ^ ;+ (+ ;- (- ; ('), true);
     }
     $this->is_windows = strpos(PHP_OS, 'WIN') !== false;
     if (!$this->is_windows) {
         $path = getenv('PATH');
         if (empty($path)) {
             trigger_error('Unable to read environment variable PATH.', E_USER_ERROR);
         }
         $path = explode(':', $path);
         $path[] = '/opt/local/bin';
         $path[] = '/opt/local/sbin';
         foreach ($path as $search_path) {
             if (@is_executable("{$search_path}/wine")) {
                 $this->wine_dir = $search_path;
                 break;
             }
         }
         if ($this->wine_dir == null) {
             trigger_error('Unable to locate Wine. Make sure it\'s installed.', E_USER_ERROR);
         }
     }
 }
 public function compile()
 {
     $this->clean_directories();
     if (file_exists('gamemodes/build-info.txt')) {
         unlink('gamemodes/build-info.txt');
     }
     register_shutdown_function(array($this, 'clean_directories'));
     $start_time = microtime(true);
     $this->generate_main();
     $pawnc = new PAWNC();
     $pawnc->base_dir = 'gamemodes';
     $pawnc->input_file = 'gamemodes/main.pwn';
     $pawnc->output_file = 'gamemodes/main.amx';
     $pawnc->include_dir = 'include';
     $pawnc->debug = $this->cfg['debug_level'];
     $pawnc->list_file = false;
     $pawnc->short_output_paths = true;
     $pawnc->args['IN_COMPILE_SCRIPT'] = true;
     if (!$this->is_windows) {
         if (false === stripos(`ps aux`, 'wineserver')) {
             echo "Starting wineserver.. ";
             `{$pawnc->wine_dir}/wineserver -p -d0`;
             `{$pawnc->wine_dir}/wine cmd /c exit`;
             echo "done.\n";
         }
     }
     $retval = 0;
     //exit;
     $retval = $pawnc->compile();
     if (!empty($pawnc->output)) {
         // Replace module prefixes back to the module's name in the compiler's output
         $pawnc->output = preg_replace_callback('/M([0-9]+)@/', array($this, 'pawnc_module_prefix'), $pawnc->output);
         echo "{$pawnc->output}\n\n";
     }
     if ($retval != 0) {
         echo "Failed to compile ({$retval}).\n";
         if (file_exists('gamemodes/build-info.txt')) {
             unlink('gamemodes/build-info.txt');
         }
     } else {
         if (!$pawnc->list_file && !$pawnc->asm_file) {
             $amx = new AMX($pawnc->output_file);
             $save = false;
             $pat = '/^M([0-9]+)@/';
             $redirects = array();
             $this_errfunc = 0;
             @date_default_timezone_set(date_default_timezone_get());
             $buildinfo = array('Date: ' . date('r') . "\n", 'AMX size: ' . $this->human_size($amx->header->size));
             foreach ($amx->header->publics as &$public) {
                 if ($public->name == 'PBP_ThisFunctionError') {
                     $this_errfunc = $public->value;
                     break;
                 }
             }
             foreach ($amx->header->publics as &$public) {
                 if (preg_match($pat, $public->name)) {
                     $name = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $public->name);
                     if (strlen($name) > 31) {
                         continue;
                     }
                     $redirects[$name] = true;
                     array_push($amx->header->publics, (object) array('name' => $name, 'value' => $public->value));
                     if ($this_errfunc) {
                         $name = preg_replace($pat, 'this.', $public->name);
                         if (strlen($name) > 31) {
                             continue;
                         }
                         $redirects[$name] = true;
                         array_push($amx->header->publics, (object) array('name' => $name, 'value' => $this_errfunc));
                     }
                 }
             }
             if ($amx->debug) {
                 $pat = '/^M([0-9]+)@/';
                 $uservars = array();
                 $configvars = array();
                 $staticgroups = array();
                 $commands = array();
                 foreach ($amx->header->publics as $entry) {
                     if (isset($redirects[$entry->name])) {
                         continue;
                     }
                     if (preg_match('/^(.*)@Pu_$/', $entry->name, $matches)) {
                         $uservars[] = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $matches[1]);
                     }
                     if (preg_match('/^(.*)@Pc_$/', $entry->name, $matches)) {
                         $configvars[] = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $matches[1]);
                     }
                     if (preg_match('/^@pG_(.*)$/', $entry->name, $matches)) {
                         $staticgroups[] = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $matches[1]);
                     }
                     if (preg_match('/^@_yC(.*)$/', $entry->name, $matches)) {
                         $commands[$matches[1]] = '';
                     }
                     $matching_symbol = false;
                     foreach ($amx->debug->symbols as &$symbol) {
                         if ($symbol->ident == AMX::IDENT_FUNCTION && $symbol->name == $entry->name) {
                             $matching_symbol = true;
                             break;
                         }
                     }
                     if (!$matching_symbol) {
                         echo "NOTICE: Public {$entry->name} has no found matching symbol.\n";
                     }
                 }
                 if (!empty($configvars)) {
                     $buildinfo[] = "\nConfig variables:\n\t* " . implode("\n\t* ", $configvars);
                 }
                 if (!empty($staticgroups)) {
                     $buildinfo[] = "\nStatic groups:\n\t* " . implode("\n\t* ", $staticgroups);
                 }
                 if (!empty($uservars)) {
                     $buildinfo[] = "\nUser variables:\n\t* " . implode("\n\t* ", $uservars);
                 }
                 $commandsstr = "\nCommands:";
                 $maxcmdlen = 0;
                 foreach ($commands as $command => &$description) {
                     $len = strlen($command);
                     if (isset($this->command_descriptions[$command])) {
                         $description = $this->command_descriptions[$command];
                     }
                     if ($len > $maxcmdlen) {
                         $maxcmdlen = $len;
                     }
                 }
                 unset($description);
                 foreach ($commands as $command => $description) {
                     if (!empty($description)) {
                         $description = " - {$description}";
                     }
                     $commandsstr .= sprintf("\n\t* %s%s", str_pad($command, $maxcmdlen), $description);
                 }
                 $buildinfo[] = $commandsstr;
                 $arrays = array();
                 foreach ($amx->debug->symbols as &$symbol) {
                     if ($symbol->ident == AMX::IDENT_ARRAY) {
                         $arrays[] =& $symbol;
                     }
                     // Replace module prefixes back to the module's name in the AMX file's debug information
                     $symbol->name = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $symbol->name, -1, $count);
                 }
                 foreach ($amx->debug->tags as $id => &$tag) {
                     $tag = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $tag, -1, $count);
                 }
                 foreach ($amx->debug->automatons as &$automaton) {
                     $automaton->name = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $automaton->name, -1, $count);
                 }
                 foreach ($amx->debug->states as &$state) {
                     $state->name = preg_replace_callback($pat, array($this, 'pawnc_module_prefix'), $state->name, -1, $count);
                 }
                 if (!$this->is_windows) {
                     $base = escapeshellarg(realpath('.'));
                     $base = trim(`{$pawnc->wine_dir}/winepath -w {$base}`);
                 } else {
                     $base = realpath('.');
                 }
                 $base = str_replace('\\', '/', $base);
                 $base = rtrim($base, '/');
                 foreach ($amx->debug->files as &$file) {
                     $file->name = str_replace('\\', '/', $file->name);
                     $file->name = preg_replace('#^' . preg_quote($base, '#') . '#', '', $file->name);
                     // This will most likely only happen when in UNC paths (Windows tries to fix that..)
                     if (preg_match('#^[a-z]:/#i', $file->name) && preg_match('#^[a-z]:/#i', $base)) {
                         $file->name = preg_replace('#^[a-z]:/' . preg_quote(substr($base, 3)) . '#i', '', $file->name);
                     }
                     $file->name = ltrim($file->name, '/');
                     do {
                         $file->name = preg_replace('#(^|/)([^/]*?)/\\.\\.(/|$)#', '$1', $file->name, -1, $count);
                     } while ($count);
                 }
                 usort($arrays, function ($left, $right) {
                     $leftsize = 1;
                     $rightsize = 1;
                     foreach ($left->dim as $dim) {
                         $leftsize *= $dim->size;
                     }
                     foreach ($right->dim as $dim) {
                         $rightsize *= $dim->size;
                     }
                     return $rightsize - $leftsize;
                 });
                 $topvars = "\n\nLargest variables:";
                 for ($i = 0; $i < 30; $i++) {
                     if (!isset($arrays[$i])) {
                         break;
                     }
                     $symbol =& $arrays[$i];
                     $symbol->size = 1;
                     foreach ($symbol->dim as $dim) {
                         $symbol->size *= $dim->size;
                     }
                     $topvars .= "\n\t" . sprintf("%6s - %s", $this->human_size($symbol->size * 4, 0), $symbol->name);
                 }
                 if (file_exists('gamemodes/build-info.txt')) {
                     $fp = fopen('gamemodes/build-info.txt', 'a');
                     fwrite($fp, $topvars);
                     fclose($fp);
                 }
                 $save = true;
             }
             if ($save) {
                 if (!$amx->save()) {
                     echo "ERROR: Failed to save the modified AMX. It might be corrupted.";
                 }
             }
             file_put_contents('gamemodes/build-info.txt', implode("\n", $buildinfo) . "\n\n" . file_get_contents('gamemodes/build-info.txt'));
             echo "Successfully compiled in " . round(microtime(true) - $start_time, 1) . " seconds; AMX size: " . $this->human_size($amx->header->size) . ".\n";
         } else {
             echo "Done.\n";
         }
     }
 }