function GetCVARList($version, $listtype)
{
    global $datapath;
    // Load settings if available
    $settings = array();
    $settingfile = "{$datapath}/cvarlist/{$version}/{$listtype}.txt";
    $cachefile = "cvarlist/{$version}/{$listtype}.json";
    $data = GetCacheFile($cachefile);
    if ($data) {
        return $data;
    }
    if (file_exists($settingfile)) {
        $lines = file($settingfile);
        foreach ($lines as $line) {
            $bits = explode(" ", trim($line), 2);
            $settings[$bits[0]] = $bits[1];
        }
    }
    // Load CVAR list
    $listfile = "{$datapath}/cvarlist/{$version}/{$listtype}.csv";
    $f = fopen($listfile, "r");
    // Load the file into fields
    while (($line = fgetcsv($f)) !== false) {
        // First line is field names
        if (!isset($fields)) {
            $fields = $line;
            continue;
        }
        //$row = array_filter(array_combine($fields,array_map('trim',$line)));
        $row = array_combine($fields, array_map('trim', $line));
        if (isset($settings[$row['Name']])) {
            if ($row['Value'] != $settings[$row['Name']]) {
                $row['Default'] = $row['Value'];
                $row['Value'] = $settings[$row['Name']];
                //$row['Value'].=" ({$settings[$row['Name']]})";
                //var_dump($row['Name'].": ".$row['Value']);
            }
        }
        $data[] = $row;
    }
    fclose($f);
    PutCacheFile($cachefile, $data);
    return $data;
}
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'];
}