function ProcessGameDataDir($root, $path = '')
{
    global $gamedata_files, $gamedata_data;
    $entries = glob(MergePath($root, $path, "*"));
    foreach ($entries as $entry) {
        $entry_path = MergePath($path, basename($entry));
        //		var_dump("ep: {$entry_path}, pgd: {$entry}");
        if (is_dir($entry)) {
            ProcessGameDataDir($root, $entry_path);
        }
        if (substr(basename($entry), -4) == ".txt") {
            $gamedata_files[$entry_path] = parseKeyValues(file_get_contents(MergePath($root, $entry_path)));
            //			var_dump($gamedata_files[$entry_path]);
            $gamedata_data = theater_array_replace_recursive($gamedata_data, $gamedata_files[$entry_path]);
        }
    }
}
function ParseIt($data, $name = 'theater', $path = '', $level = 0)
{
    global $ordered_fields, $object_fields;
    $out = array();
    // Figure out if we are dealing with named objects, or section names
    // If this is an ordered field, parse the sub-items
    $path = $path ? "{$path}/{$name}" : $name;
    $objects = matchTheaterPath($path, $object_fields);
    $ordered = matchTheaterPath($path, $ordered_fields);
    //	echo "path: {$path} ordered: {$ordered}\n";
    if ($ordered) {
        $items = array();
        foreach ($data as $idx => $item) {
            foreach ($item as $key => $val) {
                $items[$key] = $val;
            }
        }
    } else {
        $items = $data;
    }
    foreach ($items as $key => $val) {
        $title = $objects ? "@name" : $key;
        if (is_array($val)) {
            $pdata = ParseIt($val, $title, $path, $level + 1);
            if (isset($out[$title])) {
                $pdata = theater_array_replace_recursive($out[$title], $pdata);
            }
            $out[$title] = $pdata;
            uksort($out[$title], "strnatcasecmp");
        } else {
            $out[$title] = vartype($val);
        }
    }
    uksort($out, "strnatcasecmp");
    return $out;
}
function ParseTheaterFile($filename, $mod = '', $version = '', $path = '', &$base_theaters = array(), $depth = 0)
{
    //var_dump("ParseTheaterFile",$filename,$mod,$version,$path,$base_theaters,$depth);
    global $custom_theater_paths, $newest_version, $latest_version, $theaterpath, $datapath, $steam_ver, $mods;
    if ($version == '') {
        $version = $newest_version;
    }
    $basename = basename($filename);
    if (file_exists($filename)) {
        $filepath = $filename;
    } else {
        if (file_exists("{$path}/{$filename}")) {
            $filepath = "{$path}/{$filename}";
        } else {
            $filepath = GetDataFile("scripts/theaters/{$basename}", $mod, $version);
        }
    }
    $base_theaters[$basename] = md5($filepath);
    $sniproot = "{$GLOBALS['rootpath']}/theaters/snippets/";
    $snipfile = str_replace($sniproot, "", $filepath);
    if ($snipfile != $filepath) {
        $cachefile = "theaters/snippets/" . str_replace("/", "_", "{$snipfile}");
        //var_dump($sniproot,$snipfile,$snippath,$cachefile);
    } else {
        $cachefile = "theaters/{$mod}/{$version}/{$basename}";
    }
    // Attempt to load file from cache
    $cachedata = GetCacheFile($cachefile);
    if (isset($cachedata['base'])) {
        // Check all files for MD5
        foreach ($cachedata['base'] as $file => $md5) {
            if ($file == $basename) {
                $filemd5 = $base_theaters[$basename];
            } else {
                $bfpath = GetDataFile("scripts/theaters/{$file}", $mod, $version);
                $filemd5 = md5($bfpath);
            }
            // If a component file is modified, do not use the cache.
            if ($filemd5 != $md5) {
                //var_dump("md5 no match for {$file} - {$filemd5} != {$md5}");
                $cachedata['theater'] = '';
                break;
            }
        }
    }
    if (!is_array($cachedata['theater'])) {
        //var_dump("processing {$filename}");
        // Load raw theater file
        $data = file_get_contents($filepath);
        // Parse KeyValues data
        $thisfile = parseKeyValues($data);
        //var_dump($thisfile);
        // Get theater array
        // If the theater sources another theater, process them in order using a merge which blends sub-array values from bottom to top, recursively replacing.
        // This appears to be the way the game processes these files it appears.
        if (isset($thisfile["#base"])) {
            $basedata = array();
            // Create an array of base files
            if (is_array($thisfile["#base"])) {
                $bases = $thisfile["#base"];
            } else {
                $bases = array($thisfile["#base"]);
            }
            // Merge all base files into basedata array
            foreach ($bases as $base) {
                //var_dump("base {$base}");
                $base_file = GetDataURL("scripts/theaters/{$base}", $mod, $version);
                $cachedata['base'][$base] = md5($base_file);
                if (in_array($base, array_keys($base_theaters)) === true) {
                    continue;
                }
                $base_theaters[$base] = $cachedata['base'][$base];
                //var_dump("processing base {$base}");
                $basedata = array_merge_recursive(ParseTheaterFile($base, $mod, $version, $path, $base_theaters, $depth + 1), $basedata);
            }
            // Merge this theater on top of combined base
            $cachedata['theater'] = theater_array_replace_recursive($basedata, $thisfile['theater']);
        } else {
            $cachedata['theater'] = $thisfile["theater"];
        }
        /*
        		// Include parts that might be conditional in their parents, basically put everything in flat arrays
        		// This isn't congruent with how the game handles them, I believe this ougght to be a selector in the UI that can handle this better
        		foreach ($cachedata['theater'] as $sec => $data) {
        			foreach ($data as $key => $val) {
        				if (($key[0] == '?') && (is_array($val))) {
        					unset($cachedata['theater'][$sec][$key]);
        					$cachedata['theater'][$sec] = $val;// theater_array_replace_recursive($cachedata['theater'][$sec],$val);
        				}
        			}
        		}
        */
        // Save cache data
        //var_dump($cachedata);
        PutCacheFile($cachefile, $cachedata);
    }
    // Send back theater object
    return $cachedata['theater'];
}
function GenerateTheater()
{
    global $theater, $version, $theaterfile, $snippet_path, $snippets, $mod, $mods;
    $data = array();
    $hdr = array();
    //"// Theater generated");
    $ib = $_REQUEST['include_all_theaters'] == 'on';
    //$basedata = array_merge_recursive(ParseTheaterFile($base,$mod,$version,$path,$base_theaters),$basedata);
    //$theater = theater_array_replace_recursive($basedata,$theater);
    if ($ib) {
        $hdr[] = "//\"#base\"\t\t\"mods/{$mod}/{$version}/scripts/theaters/{$theaterfile}.theater\"";
        $data = $theater;
    } else {
        array_unshift($hdr, "\"#base\" \"{$theaterfile}.theater\"");
        $hdr[] = "//\"#base\"\t\t\"mods/{$mod}/{$version}/scripts/theaters/{$theaterfile}.theater\"";
    }
    foreach ($_REQUEST['section'] as $section => $snippet) {
        //var_dump($section,$snippet);
        if (is_array($snippet)) {
        } else {
            if (!strlen($snippet)) {
                echo "not parsing {$section}\n";
                continue;
            }
            $snippet = array($snippet => "on");
        }
        foreach ($snippet as $sname => $sval) {
            $hdr[] = "//\"#base\"\t\t\"snippets/{$section}/{$sname}.theater\"";
            $data = theater_array_replace_recursive(ParseTheaterFile("{$sname}.theater", $mod, $version, "{$snippet_path}/{$section}"), $data);
        }
    }
    foreach ($_REQUEST['mutator'] as $mname => $mdata) {
        if (!strlen($mdata)) {
            continue;
        }
        foreach ($_REQUEST['setting'][$mname] as $section => $settings) {
            foreach ($settings as $key => $val) {
                $hdr[] = "//mutator: {$section}.{$key}: {$val}";
                foreach ($theater[$section] as $iname => $idata) {
                    $data[$section][$iname][$key] = $val;
                }
            }
        }
    }
    $onlythese = array();
    $group_keys = array('weapons' => 'weapon', 'weapon_upgrades' => 'weapon_upgrade', 'player_gear' => 'gear');
    foreach ($_REQUEST['item_groups'] as $gname => $gstatus) {
        if ($gstatus == 'Ignore') {
            continue;
        }
        $hdr[] = "// Change weapon group {$gname} to {$gstatus}";
        $gdata = $snippets['item_groups']['settings'][$gname];
        //		$weapons = (isset($gdata['weapons'])) ? $gdata['weapons'] : array();
        //		$weapon_upgrades = (isset($gdata['weapon_upgrades'])) ? $gdata['weapon_upgrades'] : array();
        //		$player_gear = (isset($gdata['player_gear'])) ? $gdata['player_gear'] : array();
        //		$filters = (isset($gdata['filters'])) ? $gdata['filters'] : array();
        foreach ($theater['player_templates'] as $cname => $cdata) {
            $allowed_items = $gstatus == 'OnlyThese' ? $onlythese : $cdata['allowed_items'];
            foreach ($gdata as $field => $items) {
                if (!isset($theater[$field]) && !isset($data[$field])) {
                    continue;
                }
                foreach ($items as $item) {
                    $match = -1;
                    foreach ($allowed_items as $idx => $pair) {
                        foreach ($pair as $type => $name) {
                            //var_dump($type,$field,$name,$item);
                            if (($type == $field || $type == $group_keys[$field]) && $name == $item) {
                                $match = $idx;
                                break;
                            }
                        }
                    }
                    switch ($gstatus) {
                        case 'Disable':
                            if ($match > -1) {
                                unset($allowed_items[$match]);
                            }
                            break;
                        case 'AllClasses':
                        case 'OnlyThese':
                            if ($match == -1) {
                                $allowed_items[] = array($group_keys[$field] => $item);
                            }
                            break;
                    }
                }
            }
            //var_dump($allowed_items);
            if ($allowed_items != $cdata['allowed_items']) {
                if ($gstatus == 'OnlyThese') {
                    $onlythese = $allowed_items;
                }
                $data['player_templates'][$cname]['allowed_items'] = $allowed_items;
            }
        }
    }
    $kvdata = kvwrite(array('theater' => $data));
    //var_dump($kvdata);
    return implode("\n", $hdr) . "\n" . $kvdata;
}