<?php
///////////////////////////////////////////////////////////////////////////////////////////////////
//  .
///////////////////////////////////////////////////////////////////////////////////////////////////

/*
   fnmatch   .
  
  IN $pattern - string, .
  IN $string  - string, .
  
  Return      - bool, true -   ,   false.
*/
function fnmatchEx($pattern, $string)
{
  return @preg_match('#^'.strtr(preg_quote($pattern, '#'), array('\\*' => '.*', '\\?' => '.?')).'$#i', $string);
}

/*
   
  
  IN $custVars - array,   => .
  IN $str      - string,   .
  
  Return       - string,   .
*/
function replaceVarables($custVars, $str)
{
  return str_replace(array_keys($custVars), array_values($custVars), $str);
}

/*
      $config.
  
  IN $name - string,  .
*/
function configBool($name)
{
  if(empty($GLOBALS['config'][$name]))return FALSE;
  return is_numeric($GLOBALS['config'][$name]) && $GLOBALS['config'][$name] == 0 ? FALSE : TRUE;
}

/*
       .
  
  IN $app      - string,  .
  IN $platform -  .
  
  Return       - bool.
*/
function platformEnabled($app, $platform)
{
  $platforms = @explode(',', $GLOBALS['config'][$app.'_platforms']);
  if(is_array($platforms) && count($platforms) > 0)foreach($platforms as $pf)if(strcmp($platform, trim($pf)) == 0)return true;
  return false;  
}

/*
      H-.
  
  Return - string, .
*/
function headerOfHfile()
{
  return "#pragma once\r\n\r\n//Autogenerated file!\r\n\r\n";  
}

/*
   RC4.
  
  IN $data - string,   .
  IN $key  - string,  .
*/
function rc4($data, $key)
{
  $hash       = array();
  $box        = array();
  $ret        = '';  
  $keyLength  = strlen($key);
  $dataLength = strlen($data);
  
  for($x = 0; $x < 256; $x++)
  {
    $hash[$x] = ord($key[$x % $keyLength]);
    $box[$x]  = $x;
  }

  for($y = $x = 0; $x < 256; $x++)
  {
    $y       = ($y + $box[$x] + $hash[$x]) % 256;
    $tmp     = $box[$x];
    $box[$x] = $box[$y];
    $box[$y] = $tmp;
  }

  for($z = $y = $x = 0; $x < $dataLength; $x++)
  {
    $z = ($z + 1) % 256;
    $y = ($y + $box[$z]) % 256;

    $tmp     = $box[$z];
    $box[$z] = $box[$y];
    $box[$y] = $tmp;

    $k       = $box[(($box[$z] + $box[$y]) % 256)];
    $ret    .= chr(ord($data[$x]) ^ $k);
  }

  return $ret;
}

/*
   XOR.

  IN $data - string,   .
  IN $key  - string,  .
*/
function xorCrypt($data, $key)
{
  $ret        = '';
  $keyLength  = strlen($key);
  $dataLength = strlen($data);

  for($i = 0, $j = 0; $i < $dataLength; $i++)
  {
    $ret .= chr(ord($data[$i]) ^ ord($key[$j]));
    if(++$j === $keyLength)$j = 0;
  }

  return $ret;
}

/*
      C   BYTE.
  
  IN $data   - string, .
  IN $xorKey - string,    0.
  
  Return     - string,   {0xXX, 0xXX, ...}
*/
function binDataToCArray($data, $xorKey)
{
  if(!empty($xorKey))$data = xorCrypt($data, $xorKey);

  $str = "{\r\n  ";
  $s = strlen($data);

  for($i = 0, $p = 0; $i < $s; $i++)
  {
    if($i > 0)$str .= ', ';
    if(++$p == 16){$str .= "\r\n  "; $p = 1;}
    $str .= '0x'.strtoupper(bin2hex($data[$i]));
  }
  return $str."\r\n};";
}

/*
      C   BYTE.
  
  IN $data   - string,  .
  IN $xorKey - string,    0.
  
  Return     - string,   {0xXX, 0xXX, ...}
*/
function binFileToCArray($file, $xorKey)
{
  $str = file_get_contents($file);  
  if($str === FALSE || strlen($str) == 0)fatalError('Failed to open file "'.$file.'", or file empty.');
  return binDataToCArray($str, $xorKey);
}

/*
   .
  
  IN $min - int,   .
  IN $max - int,   .
  
  Return  - string, .
*/
function generatePassword($min, $max)
{
  $chars = '123456qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM@#$*7890';
  $cc = strlen($chars) - 1;
  $len = rand($min, $max);
  $s = '';
  for($i = 0; $i < $len; $i++)$s .= substr($chars, rand(0, $cc), 1);
  return $s;
}

/*
   VA /  map-.
  
  IN $app      - string,  .
  IN $platform - string,  .
  IN $name     - string,    .
  
  Return       - int -  .
*/
function getVaFromMap($app, $platform, $name)
{
  $fileName = $GLOBALS['dir']['temp'].'\\'.$app.'\\'.$platform.'\\'.$app.'.map';  
  $list = @file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES | FILE_TEXT);
  if($list === false)fatalError("Failed to open file \"{$fileName}\".");
  
  foreach($list as &$l)if(preg_match("|^ [0-9a-f]{4}:[0-9a-f]{8} +\?(.+?)\@(.*?)\@.+ +([0-9a-f]{8}) +(.+)$|u", $l, $m) === 1)
  {
    if(strcmp($name, $m[1]) === 0)return hexdec($m[3]);
  }
  fatalError("Failed to find VA of \"{$name}\".");
}

/*
   ROR13  .
  
  IN $str - string, .
  
  Return - string,    0x%08X.
*/
function getRor13Hash($str)
{
  $hash = exec(replaceVarables(array('%STRING%'  => $str), $GLOBALS['commandLine']['buildtools']['ror13']), $output, $r);
  if($r != 0)die(1);    
  $hash = mb_convert_encoding($hash, "UTF-8", "UTF-16LE");
  if(preg_match("|^0x[0-9A-F]{8}$|", $hash) !== 1)fatalError("ROR13 failed.");
  return $hash;
}

/*
   RVA    DataDirectory.
  
  IN $file  - string, .
  IN $index - int   DataDirectory.
  OUT $rva  - int, RVA.
  OUT $size - int, .
*/
function getPeDataDirectory($file, $index, &$rva, &$size)
{
  $data = exec(replaceVarables(array('%FILE%' => $file, '%INDEX%' => $index), $GLOBALS['commandLine']['buildtools']['pedatadirectory']), $output, $r);
  if($r != 0)die(1);    
  
  $data = mb_convert_encoding($data, "UTF-8", "UTF-16LE");
  $data = explode(' ', $data);
  if(!is_array($data) || count($data) != 2 || preg_match("|^0x[0-9A-F]{8}$|", $data[0]) !== 1 || preg_match("|^0x[0-9A-F]{8}$|", $data[1]) !== 1)
  {
    fatalError("Failed to get DataDirectory from \"{$file}\".");
  }
  
  $rva  = hexdec($data[0]);
  $size = hexdec($data[1]); 
}

/*
   DWORD  PE-.
  
  IN $file   - string, .
  IN $offset - int, VA  .
  IN $dword  - int, DWORD  .
*/
function patchPeDword($file, $va, $dword)
{
  exec(replaceVarables(array('%FILE%' => $file, '%VA%' => $va, '%DWORD%' => $dword), $GLOBALS['commandLine']['buildtools']['pepatch']), $output, $r);
  if($r != 0)fatalError("Failed to patch PE-file \"{$file}\".");
}

/*
   Asm   C-.
  
  IN $x          - int,  32  64.
  IN $prefix     - string,     .
  IN $sourceFile - string,  asm-.
*/
function asmToCArray($x, $prefix, $sourceFile)
{
  $output = $GLOBALS['dir']['temp'].'\asmcode.bin';

  passthru(replaceVarables(array('%OUTPUT%' => $output, '%SOURCE%' => $sourceFile), $GLOBALS['commandLine']['fasm']['win'.$x]), $r);
  
  if($r != 0)die(1);    
  if(@filesize($output) == 0)fatalError('Output size of asm code can\'t be 0 bytes');
  
  return "#define ".strtoupper($prefix)."{$x}\r\n". 
         "static const BYTE ".$prefix."{$x}[] =\r\n".binFileToCArray($output, 0)."\r\n\r\n";
}

/*
   define     PHP.
  
  IN $list - array,   $list[$name] = $value.
  IN $type - string,  "c"  C, "php"  PHP.
  
  Return   -  defines   
*/
function generateXDefines($list, $type)
{
  $str = '';
  if(strcasecmp($type, 'c') === 0)  
  {
    foreach($list as $k => $v)$str .= "#define ".$k." ".$v."\r\n";
    return $str;
  }
  else if(strcasecmp($type, 'php') === 0)  
  {
    foreach($list as $k => $v)$str .= "define('".$k."', ".$v.");\r\n";
    return $str;
  }
  
  fatalError('Failed to generate defines, unknown language "'.$type.'".');  
}

/*
   .
  
  IN $dirs   - array, .
  IN $output - string,  ,  .
*/
function packDir($dirs, $output)
{
  $pass = generatePassword(40, 50);
  passthru(replaceVarables(array('%OUTPUT%'  => $output.'.7z',
                                 '%PASS%'    => $pass,
                                 '%SOURCE%'  => implode('" "', $dirs)),
                                 $GLOBALS['commandLine']['pack']['dir']), $r);
  if($r != 0)die(1);    
  writeFile($output.'.password.txt', $pass);
}

/*
   .
  
  IN $files  - array, .
  IN $output - string,  ,  .
*/
function packFile($file, $output)
{
  $pass = generatePassword(20, 32);
  passthru(replaceVarables(array('%OUTPUT%'  => $output.'.7z',
                                 '%PASS%'    => $pass,
                                 '%SOURCE%'  => implode('" "', $files)),
                                 $GLOBALS['commandLine']['pack']['file']), $r);
  if($r != 0)die(1);    
  writeFile($output.'.password.txt', $pass);
}

/*
   .
  
  IN $file - string,    .
  IN $data - string,     .
*/
function writeFile($file, $data)
{ 
  writeLn('......writing - '.basename($file));
  $ok = 0;
  if(($f = @fopen($file, 'wb')))
  {
    if(@fwrite($f, $data) === strlen($data))$ok = 1;
    @fclose($f);
  }
  if($ok != 1)fatalError("Failed write to file \"{$file}\".");  
}

/*
   .
  
  IN $file - string,    .
  
  Return   -  .
*/
function getFileContext($file)
{ 
  $data = @file_get_contents($file);
  if($data === FALSE)fatalError("Failed to read from file \"{$file}\".");
  return $data;
}

/*
   .
  
  IN $source - string,  .
  IN $dest   - string,  .
*/
function copyFile($source, $dest)
{
  writeLn('......copying - '.$source.' => '.$dest);
  if(!@copy($source, $dest))fatalError('Failed to copy file.');
}

/*
   ,   .
  
  IN $source - string,  .
  IN $dest   - string,  .
*/
function copyFileIfExists($source, $dest)
{
  if(@file_exists($source))copyFile($source, $dest);
}

/*
    
  
  IN $dir - string,    .
*/
function createSubDir($dir)
{
  $ll = explode('\\', $dir);
  $cur = '';
  foreach($ll as $d)
  {
    $cur .= $d.'\\';
    if(!is_dir($cur) && !@mkdir($cur))fatalError("Failed to create dir \"{$cur}\".");
  }
}

/*
      ,  .
  
  IN $dir    - string,  .
  IN $ext    - array,   ,  .
  IN $quotes - bool,     .
  
  Return     - array,       $dir.
*/
function getDirList($dir, $ext, $quotes)
{
  $list = array();
  if(is_dir($dir) && ($dh = opendir($dir)) !== false)
  {
    while(($file = readdir($dh)) !== FALSE)if($file != '.' && $file != '..')
    {
      $ff = $dir.'\\'.$file;
      if(is_dir($ff))$list = array_merge($list, getDirList($ff, $ext, $quotes));
      else foreach($ext as $e)if(fnmatchEx($e, $file))$list[] = $quotes ? '"'.$ff.'"' : $ff;
    }
    closedir($dh);
  }
  return $list;
}

/*
   .
  
  IN $dir - string,   .
*/
function clearDir($dir)
{
  if(is_dir($dir) && ($dh = opendir($dir)) !== false)
  {
    while(($file = readdir($dh)) !== FALSE)if($file != '.' && $file != '..')
    {
      $s = $dir.'\\'.$file;
      if(is_dir($s))clearDir($s);
      else if(!@unlink($s))return FALSE;
    }
    closedir($dh);
  }
  if(!@rmdir($dir) && @file_exists($dir))writeWarning("Failed to remove \"{$dir}\".");
  return TRUE;
}

/*
    .
  
  IN $create - bool,      .
*/
function clearTemp($create)
{
  //temp
  writeLn("Clearing ".$GLOBALS['dir']['temp']);
  if(!clearDir($GLOBALS['dir']['temp']))fatalError("Failed to clear temp dir.");
  if($create)
  {
    writeLn("Creating ".$GLOBALS['dir']['temp']);
    createSubDir($GLOBALS['dir']['temp']);
  }

  //output
  writeLn("Clearing ".$GLOBALS['dir']['output'][0]);
  if(!clearDir($GLOBALS['dir']['output'][0]))fatalError("Failed to clear output dir.");
  if($create)
  {
    writeLn("Creating ".$GLOBALS['dir']['output'][0]);
    createSubDir($GLOBALS['dir']['output'][0]);
  }
}

/*
     .
*/
function loadGlobalConfig()
{
  $cfg = array();
  if(($cfg['global'] = @parse_ini_file(dirname(getcwd())/*$GLOBALS['dir']['project']*/.'\config.ini', true, INI_SCANNER_NORMAL)) === false)
  {
    fatalError("Failed to parse \"config.ini\".");
  }
  $GLOBALS['config'] = array_merge($GLOBALS['config'], $cfg);
}

/*
       C.
  
  IN $adv_data - string,      .
  
  Return       - string,   .
*/  
function generateDefaultConfigHeader($adv_data)
{
  $verClient = explode('.', $GLOBALS['config']['global']['versions']['client']);

  if(count($verClient) !== 4)fatalError("Bad format of \"versions\".");
  
  $s = headerOfHfile().
       "#define BO_CLIENT_VERSION 0x".bin2hex(pack("CCCC", $verClient[0], $verClient[1], $verClient[2], $verClient[3]))." //".$GLOBALS['config']['global']['versions']['client']."\r\n".
       "#define BO_NAME           L\"".BO_NAME."\"\r\n".
       "#define BO_BUILDTIME      L\"".BO_BUILDTIME."\"\r\n\r\n";

  $platforms_postfix_size = strlen('_platforms');
  foreach($GLOBALS['config'] as $k => $v)if(!is_array($v))
  {
    //     .
    if(strlen($k) > $platforms_postfix_size && strcmp(substr($k, -$platforms_postfix_size), '_platforms') == 0)
    {
      $platforms = @explode(',', $v);
      $v = (!is_array($platforms) || count($platforms) == 0 || (count($platforms) === 1 && empty($platforms[0]))) ? 0 : 1;
      $k = 'BO_'.strtoupper($k);
      
      $s .= "#define {$k} {$v}\r\n";
      foreach($platforms as $sk)if(!empty($sk))$s .= "#define {$k}_".strtoupper($sk)." 1\r\n";
    }
    else
    {
      $v = (is_numeric($v) ? $v : 'L"'.$v.'"');
      $s .= '#define BO_'.strtoupper($k).' '.$v."\r\n";
    }
  }

  return $s.(empty($adv_data) ? "" : $adv_data);
}

/*
    buildBin.
*/
function fileInList($array, $fileName)
{
  foreach($array as &$c)if(fnmatchEx($c, $fileName))return true;
  return false;
}

/*
  .
  
  IN $app     - string,    .
  IN $crypt   - int, /  ,    .
                0 -  .
                1 - .
                2 - .
  IN $clearMz - bool,  winX,  true -  MSDOS .
  IN $randBin - bool,  winX,    ,   obj-.
  
  Return      - array,  .
*/
function buildBinary($app, $crypt, $clearMz, $randBin)
{
  $platforms = @explode(',', $GLOBALS['config'][$app.'_platforms']);
  if(!is_array($platforms) || count($platforms) == 0)fatalError('Platforms not defined.');

  $r = 0;
  foreach($platforms as $pf)
  {
    $pf = strtolower(trim($pf));
    if(!isset($GLOBALS['commandLineOptions'][$app][$pf]))fatalError('Platform "'.$pf.'" not supported.');
    $opts = $GLOBALS['commandLineOptions'][$app][$pf];

    $dirTmp    = $GLOBALS['dir']['temp'].'\\'.$app.'\\'.$pf;
    $dirOutput = $GLOBALS['dir']['output'][$app];
    $dirSource = $GLOBALS['dir']['source'][$app];

    // PHP.
    if($pf == 'php')
    {
      createSubDir($dirTmp);

      $dirSource .= '[php]';
      $dirOutput .= '[php]';
      $list = getDirList($dirSource, $opts['include_files'], false);
      if(count($list) == 0)fatalError('Failed to find source files.');
      
      foreach($list as $f)if(!fileInList($opts['exclude_files'], ($f = substr($f, strlen($dirSource) + 1))))
      {
        $allowPP    = !fileInList($opts['exclude_pp_files'], $f);
        $fileSource = $dirSource.'\\'.$f;
        $fileOutput = $dirOutput.'\\'.$f;
        createSubDir(dirname($fileOutput));
        
        if(fileInList($opts['php_files'], $f))
        {
          $fileTmp = $dirTmp.'\\'.basename($f);
          evalPhpTemplate($fileSource, $fileTmp);
          
          if($allowPP && $crypt > 0 && filesize($fileTmp) > 0)
          {
            writeLn("......encrypting - {$fileTmp} => {$fileOutput}");
            passthru(replaceVarables(array('%OUTPUT%' => $fileOutput, '%SOURCE%' => $fileTmp), $GLOBALS['commandLine']['protect']['php']), $r);
            if($r != 0)die(1);          
          }
          else copyFile($fileTmp, $fileOutput);
        }
        /*else if($allowPP && fileInList($opts['js_files'], $f))
        {
          writeLn("......packing - {$fileSource} => {$fileOutput}");
          passthru(replaceVarables(array('%OUTPUT%' => $fileOutput, '%SOURCE%' => $fileSource), $GLOBALS['commandLine']['pack']['js']), $r);
          if($r != 0)die(1);
        }*/
        else copyFile($fileSource, $fileOutput);
      }
    }
    //
    else
    {
      createSubDir($dirTmp);
      createSubDir($dirOutput);

      $listCpp = array('*.cpp', '*.c');
      $listRc  = array('*.rc');
      $listObj = array('*.obj', '*.res', '*.o');      

      // objects.
      writeLn('['.$pf."] Compiling...\r\n");

      $list = getDirList($dirSource, $listCpp, true);
      if(count($list) == 0)fatalError('Failed to find source files.');

      if(!isset($opts['cpp_options']))fatalError('cpp_options no defined.');
      passthru(replaceVarables(array('%TEMP%'    => $dirTmp,
                                     '%OPTIONS%' => $opts['cpp_options'],
                                     '%SOURCE%'  => implode(' ', $list)),
                                     $GLOBALS['commandLine']['cpp'][$pf]), $r);
      if($r !=  0)die(1);
      writeLn('');

      // .
      $list = getDirList($dirSource, $listRc, false);
      if(count($list) != 0)foreach($list as $k => $v)
      {
        if(!isset($opts['res_options']))fatalError('res_options no defined.');
        passthru(replaceVarables(array('%OUTPUT%'  => $dirTmp.'\\'.$k.'.res',
                                       '%OPTIONS%' => $opts['res_options'],
                                       '%SOURCE%'  => $v),
                                       $GLOBALS['commandLine']['res'][$pf]), $r);
        if($r != 0)die(1);
        writeLn('');
      }
      
      // bin
      writeLn('['.$pf."] Linking...\r\n");
      $list = getDirList($dirTmp, $listObj, true);
      if(count($list) == 0)fatalError('Failed to find object files.');
      if($randBin)shuffle($list);
      
      
      if(!isset($opts['lnk_options']))fatalError('cpp_options no defined.');
      $fileBin = $dirOutput.'\\'.$opts['name'];
      $fileMap = $dirTmp.'\\'.$app.'.map';
      $fileDef = $dirSource.'\\'.$app.'.def';
      
      passthru(replaceVarables(array('%TEMP%'    => $dirTmp,
                                     '%OPTIONS%' => (file_exists($fileDef) ? "/DEF:\"{$fileDef}\" " : "").$opts['lnk_options'],
                                     '%MAPFILE%' => $fileMap,
                                     '%OUTPUT%'  => $fileBin,
                                     '%SUBSYS%'  => $opts['subsys'],
                                     '%SOURCE%'  => implode(' ', $list)),
                                     $GLOBALS['commandLine']['link'][$pf]), $r);

      if($r != 0)die(1);
      writeLn('');
      
      //   .
      if($crypt > 0)
      {
        $functionName = "{$app}BeforeCrypt";
        if(@function_exists($functionName))call_user_func($functionName, $fileBin, $app, $pf);
      }
      
      if($crypt == 1 && isset($GLOBALS['commandLine']['pack'][$pf]))
      {
        writeLn("\r\n[".$pf."] Compressing...\r\n");
        passthru(replaceVarables(array('%SOURCE%'  => $dirOutput.'\\'.$opts['name']), $GLOBALS['commandLine']['pack'][$pf]), $r);
        if($r != 0)die(1);
        writeLn('');
      }

      if($clearMz > 0)
      {
        writeLn('['.$pf."] Clearing DOS Header...\r\n");
        if(($fc = @file_get_contents($fileBin)) === false || strlen($fc) < 1024 || count(($v = unpack('L', substr($fc, 0x3C, 4)))) !== 1 || $v[1] === 0)fatalError('Failed to clear DOS Header in PE file "'.$fc.'".');  
        
        $v = $v[1];
        for($i = 2; $i < 0x3C; $i++)$fc[$i] = "\0";
        for($i = 0x40; $i < $v; $i++)$fc[$i] = "\0";
        
        writeFile($fileBin, $fc);
      }      
    }
  }
  
  return $platforms;
}

/*
      PHP ,   .
  
  IN $fileIn  - string,  .
  IN $fileOut - string,  .
*/
function evalPhpTemplate($fileIn, $fileOut)
{
  $data = getFileContext($fileIn);
  while(preg_match('|\/\*EVAL\_BEGIN\*\/(.*)(\/\*EVAL\_END\*\/)|msUu', $data, $m, PREG_OFFSET_CAPTURE) > 0)
  {
    // .
    $code_data = eval(substr($data, $m[1][1], $m[2][1] - $m[1][1]));

    // .
    $data = substr_replace($data, $code_data == NULL ? "" : $code_data, $m[0][1], $m[2][1] + 12 - $m[0][1]);
    $tr = substr($data, $m[0][1]);
    
    //   ,  .
    $delCount = 0;
    if(ord($tr[0]) == 0xD && ord($tr[1]) == 0xA)$delCount = 2;
    else if(ord($tr[0]) == 0xA)$delCount = 1;
    if($delCount > 0)$data = substr_replace($data, "", $m[0][1], $delCount);
  }  
  writeFile($fileOut, str_replace("\r\n", "\n", $data));
}

/*
     .
  
  IN $fn - string,  .
*/
function loadPackageConfig($fn)
{
  $fn   .= '\config';
  $list  = @file($fn);
  if(!is_array($list) || count($list) == 0)fatalError('Failed to parse config file "'.$fn.'".');

  foreach($list as $i => $k)
  {
    $k = trim($k);
    if(strlen($k) > 1 && $k[0] != '#')
    {
      $t = @explode('=', $k, 2);
      if(is_array($t) && count($t) == 2)
      {
        $t[0] = trim($t[0]);
        $t[1] = trim($t[1]);

        if(!isset($GLOBALS['config'][$t[0]]) || @is_array($GLOBALS['config'][$t[0]]))
        {
          fatalError("Unknown option \"{$t[0]}\" defined.");
        }

        $GLOBALS['config'][$t[0]] = $t[1];
      }
      else fatalError("Error parse config file \"{$fn}\" on line {$i}.");
    }
  }

  $nd = array();
  foreach($GLOBALS['config'] as $k => &$v)if($GLOBALS['config'][$k] === -1)$nd[] = $k;
  if(count($nd) > 0)fatalError('Not defined options: '.implode(', ', $nd).'.');
}

/*
    .
  
  IN $fileName - string,    .
*/
function generateCryptedStrings($fileName)
{
  $list = @file($fileName.'.txt', FILE_IGNORE_NEW_LINES);
  if(!is_array($list) || count($list) === 0)fatalError("Failed to open file \"{$fileName}.txt\".");

  $outputStruct  = '';
  $outputEnum    = '';
  $outputLenEnum = '';
  $structProlog  = '  ';
  $enumProlog    = '    id_';
  $lenEnumProlog = '    len_';
  $maxLen        = 0;

  foreach($list as $i => &$string)
  {
    $string = trim($string);
    
    //.
    if(strlen($string) === 0 || strpos($string, '//') === 0 || strpos($string, ';') === 0)continue;
    
    // .
    if(strpos($string, '#') === 0)
    {
      $outputStruct  .= "{$string}\r\n";
      $outputEnum    .= "{$string}\r\n";
      $outputLenEnum .= "{$string}\r\n";
      continue;
    }
    
    //.
    $e = explode(' ', $string, 2);
    if(!is_array($e) || count($e) !== 2 || strlen(($id = trim($e[0]))) === 0|| @eval("\$value = ".trim($e[1]).";") !== NULL)
    {
      fatalError("Failed to parse file \"{$fileName}.txt\" at line {$i}.");
    }

    $key      = mt_rand(1, 255);
    $valueLen = strlen($value);
    $valueHex = '';
    
    for($v = 0; $v < $valueLen; $v++)
    {
      $t = (ord($value[$v]) ^ $v) ^ $key;
      $valueHex .= sprintf("\x%02x", $t % 256);
    }
    
    $outputStruct  .= "{$structProlog}{".sprintf("% 3u", $key).", ".sprintf("% 3u", $valueLen).", \"{$valueHex}\"},\r\n";
    $outputEnum    .= "{$enumProlog}{$id},\r\n";
    $outputLenEnum .= "{$lenEnumProlog}{$id} = ({$valueLen} + 1),\r\n";
    
    if($valueLen > $maxLen)$maxLen = $valueLen;
  }
  unset($list);
  
  // .
  $outputStruct  .= "{$structProlog}{  0,   0, NULL}";
  $outputEnum    .= "{$enumProlog}count";
  $outputLenEnum .= "{$lenEnumProlog}max = ({$maxLen} + 1)";
  
  //.
  $output = getFileContext($fileName.'.h');
  $output = preg_replace('|(\/\*STRINGS\_ID\_BEGIN\*\/)(.*)(\/\*STRINGS\_ID\_END\*\/)|msU', "\$1\r\n".$outputEnum."\r\n$3", $output);
  $output = preg_replace('|(\/\*STRINGS\_LENGHT\_BEGIN\*\/)(.*)(\/\*STRINGS\_LENGHT\_END\*\/)|msU', "\$1\r\n".$outputLenEnum."\r\n$3", $output);
  writeFile($fileName.'.h', $output);
  
  $output = getFileContext($fileName.'.cpp');
  $output = preg_replace('|(\/\*STRINGS\_DATA\_BEGIN\*\/)(.*)(\/\*STRINGS\_DATA\_END\*\/)|msU', "\$1\r\n".$outputStruct."\r\n$3", $output);
  writeFile($fileName.'.cpp', $output);
}

/*
        .
*/
function goodEnd()
{
  writeLn("\r\nBUILD SUCCESSED!");
  die(0);
}

/*
    
  
  Return -  ,  0   .
*/
function waitAnyKey()
{
  $k = 0;
  if(($f = @fopen('php://stdin', 'rb')))
  {
    print "\nPress Return key for continue . . .";
    $k = @fread($f, 1);
    @fclose($f);
  }
  return $k;
}

/*
     .
  
  IN $str - string,   .
*/
function writeLn($str)
{
  echo $str."\r\n";
}

/*
    .
  
  IN $name - string,  .
*/
$___curstep = 0;
function writeStep($name)
{
  global $___curstep;
  $___curstep++;
  writeLn("\nSTEP #".$___curstep.": ".$name);
}

/*
      .
  
  IN $str - string,   .
*/
function fatalError($str)
{
  echo 'ERROR: '.$str."\r\n\r\n";
  die(1);
}

/*
   .
  
  IN $str - string,   .
*/
function writeWarning($str)
{
  echo 'WARNING: '.$str."\r\n";
}
?>