/*
Copyright (C) 2005-2008 Nach ( http://nsrt.edgeemu.com )

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
Config file handler creator by Nach (C) 2005-2007
*/

#if !defined(__GNUC__) && !defined(_MSC_VER)
#error You are using an unsupported compiler
#endif

#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
using namespace std;

#include <errno.h>
#include <zlib.h>

#ifdef _MSC_VER // MSVC
typedef int ssize_t;
#define strcasecmp stricmp
#define strncasecmp strnicmp
#define __WIN32__
#endif

string gcc = "gcc";
string cflags;

#ifdef _MSC_VER // MSVC
static inline string COMPILE_OBJ(const string& obj, const string& c)
{
    return (string(string("cl /nologo /c /Fo") + obj + string(" ") + c));
}
#else
static inline string COMPILE_OBJ(const string& obj, const string& c)
{
    return (string(gcc + (" ") + cflags + (" -o ") + obj + string(" -c ") + c));
}
#endif

#define LINE_LENGTH 262144
char line[LINE_LENGTH];

string family_name = "cfg";

/*

Line Tracking and Error Control

*/

static struct
{
    size_t line_number;
    size_t column_number;

    void error(const char* str)
    {
        cerr << "Error: parse problem occured at " << line_number << ":" << column_number << ". " << str << "." << endl;
    }
} current_location;

/*

String Functions for various parsing

*/

// Find next matching character which is not escaped
char* find_next_match(char* str, char match_char)
{
    char* pos = 0;

    while (*str) {
        if (*str == match_char) {
            pos = str;
            break;
        }
        if (*str == '\\') {
            if (str[1]) {
                str++;
            } else {
                break;
            }
        }
        str++;
    }
    return (pos);
}

// This is like strtok(), except this understands quoted characters and updates error locations
char* get_token(char* str, const char* delim)
{
    static char* pos = 0;
    char* token = 0;

    if (str) // Start a new string?
    {
        pos = str;
    }

    if (pos) {
        // Skip delimiters
        while (*pos && strchr(delim, *pos)) {
            pos++;
        }
        if (*pos) {
            token = pos;

            // Skip non-delimiters
            while (*pos && !strchr(delim, *pos)) {
                // Skip quoted characters
                if ((*pos == '\"') || (*pos == '\'')) {
                    char* match_pos = 0;
                    if ((match_pos = find_next_match(pos + 1, *pos))) {
                        pos = match_pos;
                    }
                }
                pos++;
            }
            if (*pos) {
                *pos++ = '\0';
            }
        }
    }

    if (token) {
        current_location.column_number = token - line;
    }
    return (token);
}

// Like strchr() but understands quoted characters
char* find_chr(char* str, char match_char)
{
    char* pos = 0;

    while (*str) {
        if (*str == match_char) {
            pos = str;
            break;
        }
        // Skip quoted characters
        if ((*str == '\"') || (*str == '\'')) {
            char* match_pos = 0;
            if ((match_pos = find_next_match(str + 1, *str))) {
                str = match_pos;
            }
        }
        str++;
    }
    return (pos);
}

// Convert $AB12 and 0AB12h style hex to 0xAB12 hex
string asm2c_hex_convert(string str)
{
    size_t dollar_pos;
    int start = -1;
    while ((dollar_pos = str.find("$", start + 1)) != string::npos) {
        if ((str.length() - dollar_pos > 1) && isxdigit(str[dollar_pos + 1])) {
            str.replace(dollar_pos, 1, "0x");
            dollar_pos++;
        }
        start = dollar_pos;
    }

    start = -1;
    int h_pos;
    while ((string::size_type)(h_pos = str.find_first_of("hH", start + 1)) != string::npos) {
        int h_len = 1;
        while ((h_pos - h_len > start) && isxdigit(str[h_pos - h_len])) {
            h_len++;
        }
        h_len--;

        if (isdigit(str[h_pos - h_len])) {
            str.erase(h_pos, 1);
            str.insert(h_pos - h_len, "0x");
            h_pos++;
        }
        start = h_pos;
    }

    return (str);
}

// Convert 2EF to 751
string c_hex_convert(string str)
{
    size_t hex_pos;
    while ((hex_pos = str.find("0x")) != string::npos) {
        size_t len = 2, total = 0;
        while (isxdigit(str[hex_pos + len]) && !(str[hex_pos + len] == '0' && str[hex_pos + len + 1] == 'x')) {
            total *= 16;
            total += isdigit(str[hex_pos + len]) ? str[hex_pos + len] - '0' : (toupper(str[hex_pos + len]) - 'A') + 10;
            len++;
        }

        ostringstream converter;
        converter << total;
        str.replace(hex_pos, len, converter.str());
    }
    return (str);
}

// Ascii numbers to integer, with support for mathematics in the string
ssize_t enhanced_atoi(char*& s, int level = 0)
{
    const int max_level = 6;
    if (level == max_level) {
        if (*s == '(') {
            ssize_t res = enhanced_atoi(++s);
            if (*s != ')') {
                current_location.error("Missing ) in expression");
            }
            s++;
            return (res);
        } else if (isdigit(*s)) {
            int numc, t;
            sscanf(s, "%d%n", &t, &numc);
            s += numc;
            return ((ssize_t)t);
        } else if (*s == '-') {
            return (-enhanced_atoi(++s, max_level));
        } else if (*s == '~') {
            return (~enhanced_atoi(++s, max_level));
        }
    }
    ssize_t val = enhanced_atoi(s, level + 1);
    while (*s) {
        const char* org = s;
        switch (level) {
        case 0:
            if (*s != '|') {
                return (val);
            }
            break;
        case 1:
            if (*s != '^') {
                return (val);
            }
            break;
        case 2:
            if (*s != '&') {
                return (val);
            }
            break;
        case 3:
            if (!((s[0] == '<' && s[1] == '<') || (s[0] == '>' && s[1] == '>'))) {
                return (val);
            } else {
                ++s;
            }
            break;
        case 4:
            if (!(*s == '+' || *s == '-')) {
                return (val);
            }
            break;
        case 5:
            if (!(*s == '*' || *s == '/' || *s == '%')) {
                return (val);
            }
            break;
        }
        ssize_t res = enhanced_atoi(++s, level + 1);
        switch (*org) {
        case '|':
            val |= res;
            break;
        case '^':
            val ^= res;
            break;
        case '&':
            val &= res;
            break;
        case '<':
            val <<= res;
            break;
        case '>':
            val >>= res;
            break;
        case '+':
            val += res;
            break;
        case '-':
            val -= res;
            break;
        case '*':
            val *= res;
            break;
        case '/':
            val /= res;
            break;
        case '%':
            val %= res;
            break;
        }
    }
    return (val);
}

// Standard atoi(), but shows error if string isn't all a number
ssize_t safe_atoi(string str)
{
    if (!str.length()) {
        str = "X";
    } // Force error

    const char* p = str.c_str();
    for (p = ((*p == '-') ? p + 1 : p); *p; p++) {
        if (!isdigit(*p)) {
            current_location.error("Not a number");
        }
    }

    return (atoi(str.c_str()));
}

bool all_spaces(const char* str)
{
    while (*str) {
        if (!isspace(*str)) {
            return (false);
        }
        str++;
    }
    return (true);
}

string encode_string(const string& str, bool quotes = true)
{
    string newstr("");
    if (quotes) {
        newstr += '\"';
    }

    for (size_t i = 0; i < str.length(); i++) {
        if ((str[i] == '\\') || (str[i] == '\"') || (str[i] == '\'') || (str[i] == '\n') || (str[i] == '\t')) {
            newstr += '\\';
        }
        newstr += str[i];
    }

    if (quotes) {
        newstr += '\"';
    }
    return (newstr);
}

/*

Structures used to store config data

*/

template <typename T>
class nstack {
public:
    nstack() { }
    ~nstack() { }

    void push(T data) { this->data.push_back(data); }
    bool empty() { return (data.empty()); }
    T top() { return (data.back()); }
    void pop() { data.pop_back(); }
    size_t size() { return (data.size()); }

    bool all_true()
    {
        for (typename vector<T>::iterator i = data.begin(); i != data.end(); i++) {
            if (!*i) {
                return (false);
            }
        }
        return (true);
    }

private:
    vector<T> data;
};

set<string> defines, dependancies;
nstack<bool> ifs;

typedef vector<string> str_array;

str_array memsets;

namespace variable {
enum ctype { NT,
    UC,
    US,
    UD,
    SC,
    SS,
    SD,
    LT };

static struct
{
    const char* CTypeSpace;
    const char* CTypeUnderscore;
    char FormatChar;
    bool Signed;
} info[] = {
    { "", "", 0, false },
    { "unsigned char", "unsigned_char", 'u', false },
    { "unsigned short", "unsigned_short", 'u', false },
    { "unsigned int", "unsigned_int", 'u', false },
    { "signed char", "signed_char", 'd', true },
    { "short", "short", 'd', true },
    { "int", "int", 'd', true }
};

ctype GetCType(const char* str)
{
    int i = NT;
    for (; i < LT; i++) {
        if (!strcmp(info[i].CTypeSpace, str)) {
            break;
        }
    }
    i %= LT;

    if (i == NT) {
        cerr << "Invalid C type \"" << str << "\" when parsing line " << current_location.line_number << "." << endl;
    }

    return ((ctype)i);
}

enum storage_format { none,
    single,
    quoted,
    mult,
    mult_packed,
    ptr };

struct config_data_element {
    string name;
    storage_format format;
    ctype type;
    size_t length;
    string dependancy;
    string comment;

    bool operator==(const string& name) const { return (this->name == name); }
};

typedef vector<config_data_element> config_data_array;

static class
{
private:
    config_data_array data_array;

    bool duplicate_name(const string& name)
    {
        if (find(data_array.begin(), data_array.end(), name) != data_array.end()) {
            cerr << "Duplicate definition of \"" << name << "\" found on line " << current_location.line_number << "." << endl;
            return (true);
        }
        return (false);
    }

public:
    void add_comment(const string& comment)
    {
        config_data_element new_element = { "", none, NT, 0, "", comment };
        data_array.push_back(new_element);
    }

    void add_var_single(const string& name, ctype type, const string& dependancy, const string& comment = "")
    {
        if (!duplicate_name(name)) {
            config_data_element new_element = { name, single, type, 0, dependancy, comment };
            data_array.push_back(new_element);
        }
    }
    void add_var_single(const string& name, const char* type, const string& dependancy, const string& comment = "")
    {
        add_var_single(name, GetCType(type), dependancy, comment);
    }

    void add_var_quoted(const string& name, const string& dependancy, const string& comment = "")
    {
        if (!duplicate_name(name)) {
            config_data_element new_element = { name, quoted, NT, 0, dependancy, comment };
            data_array.push_back(new_element);
        }
    }

    void add_var_mult(const string& name, ctype type, size_t length, const string& dependancy, const string& comment = "")
    {
        if (!duplicate_name(name)) {
            config_data_element new_element = { name, mult, type, length, dependancy, comment };
            data_array.push_back(new_element);
        }
    }
    void add_var_mult(const string& name, const char* type, size_t length, const string& dependancy, const string& comment = "")
    {
        add_var_mult(name, GetCType(type), length, dependancy, comment);
    }

    void add_var_packed(const string& name, size_t length, const string& dependancy, const string& comment = "")
    {
        if (!duplicate_name(name)) {
            config_data_element new_element = { name, mult_packed, NT, length, dependancy, comment };
            data_array.push_back(new_element);
        }
    }

    void add_var_ptr(const string& name, ctype type, size_t length, const string& dependancy, const string& comment = "")
    {
        if (!duplicate_name(name)) {
            config_data_element new_element = { name, ptr, type, length, dependancy, comment };
            data_array.push_back(new_element);
        }
    }
    void add_var_ptr(const string& name, const char* type, size_t length, const string& dependancy, const string& comment = "")
    {
        add_var_ptr(name, GetCType(type), length, dependancy, comment);
    }

    bool ctype_mult_used(ctype type)
    {
        for (config_data_array::iterator i = data_array.begin(); i != data_array.end(); i++) {
            if ((i->format == mult) && (i->type == type)) {
                return (true);
            }
        }
        return (false);
    }

    bool ctype_ptr_used(ctype type)
    {
        for (config_data_array::iterator i = data_array.begin(); i != data_array.end(); i++) {
            if ((i->format == ptr) && (i->type == type)) {
                return (true);
            }
        }
        return (false);
    }

    bool packed_used()
    {
        for (config_data_array::iterator i = data_array.begin(); i != data_array.end(); i++) {
            if (i->format == mult_packed) {
                return (true);
            }
        }
        return (false);
    }

    bool quoted_used()
    {
        for (config_data_array::iterator i = data_array.begin(); i != data_array.end(); i++) {
            if (i->format == quoted) {
                return (true);
            }
        }
        return (false);
    }

    bool unsigned_used()
    {
        for (config_data_array::iterator i = data_array.begin(); i != data_array.end(); i++) {
            if (!info[i->type].Signed) {
                return (true);
            }
        }
        return (false);
    }

    config_data_array::iterator begin() { return (data_array.begin()); }
    config_data_array::iterator end() { return (data_array.end()); }
} config_data;
}

/*

Compiler with it's helper functions

*/

#define var_type_is_char(var_type) !strcmp(var_type + strlen(var_type) - strlen("char"), "char")
#define var_type_is_short(var_type) !strcmp(var_type + strlen(var_type) - strlen("short"), "short")
#define var_type_is_int(var_type) !strcmp(var_type + strlen(var_type) - strlen("int"), "int")

#define short_scale "*sizeof(short)"
#define int_scale "*sizeof(int)"

// Convert asm types to C types
const char* convert_asm_type(const char* str, bool unsigned_var = true)
{
    const char* var_type = 0;
    if (!strcasecmp(str, "dd")) {
        var_type = "unsigned int";
    } else if (!strcasecmp(str, "dw")) {
        var_type = "unsigned short";
    } else if (!strcasecmp(str, "db")) {
        var_type = "unsigned char";
    } else if (!strcasecmp(str, "sd")) {
        var_type = "int";
    } else if (!strcasecmp(str, "sw")) {
        var_type = "short";
    } else if (!strcasecmp(str, "sb")) {
        var_type = "signed char";
    } else {
        current_location.error("Not a valid type");
    }

    if (var_type && !strncmp(var_type, "unsigned ", strlen("unsigned ")) && !unsigned_var) {
        var_type += strlen("unsigned ");
    }

    return (var_type);
}

void output_parser_start(ostream& c_stream, const string& cheader_file)
{
    c_stream << "/*\n"
             << "Config file handler generated by Nach's Config file handler creator.\n"
             << "*/\n"
             << "\n"
             << "#include <stdio.h>\n"
             << "#include <stdlib.h>\n"
             << "#include <ctype.h>\n"
             << "#include <string.h>\n";
    if (defines.find("PSR_COMPRESSED") != defines.end()) {
        c_stream << "#include <zlib.h>\n";
    }
    if (cheader_file.length()) {
        c_stream << "#include \"" << cheader_file << "\"\n";
    }
    c_stream << "\n"
             << "\n"
             << "#define LINE_LENGTH " << LINE_LENGTH << "\n"
             << "static char line[LINE_LENGTH];\n"
             << "\n";
    if (variable::config_data.quoted_used() || variable::config_data.packed_used()) {
        c_stream << "\n"
                 << "static char *encode_string(const char *str)\n"
                 << "{\n"
                 << "  size_t i = 0;\n"
                 << "  line[i++] = '\\\"';\n"
                 << "  while (*str)\n"
                 << "  {\n"
                 << "    if ((*str == '\\\\') ||\n"
                 << "        (*str == '\\\"') ||\n"
                 << "        (*str == '\\\'') ||\n"
                 << "        (*str == '\\n') ||\n"
                 << "        (*str == '\\t'))\n"
                 << "    {\n"
                 << "      line[i++] = '\\\\';\n"
                 << "    }\n"
                 << "    line[i++] = *str++;\n"
                 << "  }\n"
                 << "  line[i++] = '\\\"';\n"
                 << "  line[i] = 0;\n"
                 << "  return(line);\n"
                 << "}\n"
                 << "\n"
                 << "static char *decode_string(char *str)\n"
                 << "{\n"
                 << "  size_t str_len = strlen(str), i = 0;\n"
                 << "  char *dest = str;\n"
                 << "\n"
                 << "  if ((str_len > 1) && (*str == '\\\"') && (str[str_len-1] == '\\\"'))\n"
                 << "  {\n"
                 << "    memmove(str, str+1, str_len-2);\n"
                 << "    str[str_len-2] = 0;\n"
                 << "\n"
                 << "    while (*str)\n"
                 << "    {\n"
                 << "      if (*str == '\\\\')\n"
                 << "      {\n"
                 << "        str++;\n"
                 << "      }\n"
                 << "      dest[i++] = *str++;\n"
                 << "    }\n"
                 << "  }\n"
                 << "  dest[i] = 0;\n"
                 << "  return(dest);\n"
                 << "}\n";
    }
    c_stream << "\n"
             << "static char *find_next_match(char *str, char match_char)\n"
             << "{\n"
             << "  char *pos = 0;\n"
             << "\n"
             << "  while (*str)\n"
             << "  {\n"
             << "    if (*str == match_char)\n"
             << "    {\n"
             << "      pos = str;\n"
             << "      break;\n"
             << "    }\n"
             << "    if (*str == '\\\\')\n"
             << "    {\n"
             << "      if (str[1])\n"
             << "      {\n"
             << "        str++;\n"
             << "      }\n"
             << "      else\n"
             << "      {\n"
             << "        break;\n"
             << "      }\n"
             << "    }\n"
             << "    str++;\n"
             << "  }\n"
             << "  return(pos);\n"
             << "}\n"
             << "\n"
             << "static char *find_str(char *str, char *match_str)\n"
             << "{\n"
             << "  char *pos = 0;\n"
             << "\n"
             << "  while (*str)\n"
             << "  {\n"
             << "    if (strchr(match_str, *str))\n"
             << "    {\n"
             << "      pos = str;\n"
             << "      break;\n"
             << "    }\n"
             << "    if ((*str == '\\\"') || (*str == '\\\''))\n"
             << "    {\n"
             << "      char *match_pos = 0;\n"
             << "      if ((match_pos = find_next_match(str+1, *str)))\n"
             << "      {\n"
             << "        str = match_pos;\n"
             << "      }\n"
             << "    }\n"
             << "    str++;\n"
             << "  }\n"
             << "  return(pos);\n"
             << "}\n"
             << "\n";
    if (variable::config_data.unsigned_used()) {
        c_stream << "\n"
                 << "static int atoui(const char *nptr)\n"
                 << "{\n"
                 << "  return(strtoul(nptr, 0, 10));\n"
                 << "}\n";
    }
    c_stream << "\n";
}

void output_cheader_start(ostream& cheader_stream)
{
    cheader_stream << "/*\n"
                   << "Config file handler header generated by Nach's Config file handler creator.\n"
                   << "*/\n"
                   << "\n"
                   << "#ifdef __cplusplus\n"
                   << "  extern \"C\" {\n"
                   << "#endif\n"
                   << "\n"
                   << "unsigned char read_" << family_name << "_vars(const char *);\n"
                   << "unsigned char write_" << family_name << "_vars(const char *);\n";
    if (defines.find("PSR_COMPRESSED") != defines.end()) {
        cheader_stream << "unsigned char read_" << family_name << "_vars_compressed(const char *);\n"
                       << "unsigned char write_" << family_name << "_vars_compressed(const char *);\n";
    }
    if (defines.find("PSR_MEMCPY") != defines.end()) {
        cheader_stream << "void read_" << family_name << "_vars_memory(unsigned char *);\n"
                       << "void write_" << family_name << "_vars_memory(unsigned char *);\n"
                       << "unsigned int size_" << family_name << "_vars_memory();\n";
    }
    cheader_stream << "\n";
}

void output_cheader_end(ostream& cheader_stream)
{
    cheader_stream << "\n"
                   << "#ifdef __cplusplus\n"
                   << "             }\n"
                   << "#endif\n"
                   << "\n";
}

void output_extsym_dependancies(ostream& c_stream)
{
    c_stream << "\n";
    for (set<string>::iterator i = dependancies.begin(); i != dependancies.end(); i++) {
        c_stream << "extern unsigned char " << *i << ";\n";
    }
}

void output_init_var(ostream& c_stream)
{
    c_stream << "\n"
             << "static unsigned char psr_init_done = 0;\n"
             << "static void init_" << family_name << "_vars()\n"
             << "{\n"
             << "  if (!psr_init_done)\n"
             << "  {\n"
             << "    psr_init_done = 1;\n"
             << "\n";
    for (str_array::iterator i = memsets.begin(); i != memsets.end(); i++) {
        c_stream << "    " << *i << "\n";
    }
    c_stream << "  }\n"
             << "}\n";
}

void output_packed_write(ostream& c_stream)
{
    if (variable::config_data.packed_used()) {
        c_stream << "\n"
                 << "static char *base94_encode(size_t size)\n"
                 << "{\n"
                 << "  unsigned int i;\n"
                 << "  static char buffer[] = { 0, 0, 0, 0, 0, 0};\n"
                 << "  for (i = 0; i < 5; i++)\n"
                 << "  {\n"
                 << "    buffer[i] = ' ' + (char)(size % 94);\n"
                 << "    size /= 94;\n"
                 << "  }\n"
                 << "  return(buffer);\n"
                 << "}\n"
                 << "\n"
                 << "static char *char_array_pack(const char *str, size_t len)\n"
                 << "{\n"
                 << "  char packed[LINE_LENGTH];\n"
                 << "  char *p = packed;\n"
                 << "  while (len)\n"
                 << "  {\n"
                 << "    if (*str)\n"
                 << "    {\n"
                 << "      size_t length = strlen(str);\n"
                 << "      strcpy(p, encode_string(str));\n"
                 << "      str += length;\n"
                 << "      len -= length;\n"
                 << "      p += strlen(p);\n"
                 << "    }\n"
                 << "    else\n"
                 << "    {\n"
                 << "      size_t i = 0;\n"
                 << "      while (len && !*str)\n"
                 << "      {\n"
                 << "        i++;\n"
                 << "        str++;\n"
                 << "        len--;\n"
                 << "      }\n"
                 << "\n"
                 << "      sprintf(p, \"0%s\", encode_string(base94_encode(i)));\n"
                 << "      p += strlen(p);\n"
                 << "    }\n"
                 << "    *p++ = '\\\\';\n"
                 << "  }\n"
                 << "  p[-1] = 0;\n"
                 << "  strcpy(line, packed);"
                 << "  return(line);\n"
                 << "}\n";
    }
}

void output_array_write(ostream& c_stream, variable::ctype type)
{
    if (variable::config_data.ctype_mult_used(type) || variable::config_data.ctype_ptr_used(type)) {
        c_stream << "\n"
                 << "static void write_" << variable::info[type].CTypeUnderscore << "_array(int (*outf)(void *, const char *, ...), void *fp, const char *var_name, " << variable::info[type].CTypeSpace << " *var, size_t size, const char *comment)\n"
                 << "{\n"
                 << "  size_t i;\n"
                 << "  outf(fp, \"%s=%" << variable::info[type].FormatChar << "\", var_name, (int)*var);\n"
                 << "  for (i = 1; i < size; i++)\n"
                 << "  {\n"
                 << "    outf(fp, \",%" << variable::info[type].FormatChar << "\", (int)(var[i]));\n"
                 << "  }\n"
                 << "  if (comment)\n"
                 << "  {\n"
                 << "    outf(fp, \" ;%s\", comment);\n"
                 << "  }\n"
                 << "  outf(fp, \"\\n\");\n"
                 << "}\n";
    }
}

void output_write_var(ostream& c_stream)
{
    output_packed_write(c_stream);
    output_array_write(c_stream, variable::UC);
    output_array_write(c_stream, variable::US);
    output_array_write(c_stream, variable::UD);
    output_array_write(c_stream, variable::SC);
    output_array_write(c_stream, variable::SS);
    output_array_write(c_stream, variable::SD);

    c_stream << "\n"
             << "static void write_" << family_name << "_vars_internal(void *fp, int (*outf)(void *, const char *, ...))\n"
             << "{\n";
    for (variable::config_data_array::iterator i = variable::config_data.begin(); i != variable::config_data.end(); i++) {
        string dependancy_prefix, dependancy_suffix;
        if (i->dependancy != "") {
            dependancy_prefix = string("if (") + string(i->dependancy, 0, i->dependancy.length() - 1) + string(") { ");
            dependancy_suffix = " }";
        }

        if (i->format == variable::none) {
            if (i->comment != "") {
                c_stream << "  outf(fp, \";%s\\n\", " << encode_string(i->comment) << ");\n";
            } else {
                c_stream << "  outf(fp, \"\\n\");\n";
            }
        } else if ((i->format == variable::mult) || (i->format == variable::ptr)) {
            c_stream << "  " << dependancy_prefix << "write_" << variable::info[i->type].CTypeUnderscore
                     << "_array(outf, fp, \"" << i->dependancy << i->name << "\", " << i->name << ", " << i->length << ", " << ((i->comment != "") ? encode_string(i->comment) : "0") << ");" << dependancy_suffix << "\n";
        } else {
            string config_comment = (i->comment != "") ? (string(" ;") + encode_string(i->comment, false)) : "";
            c_stream << "  " << dependancy_prefix << "outf(fp, \"" << i->dependancy << i->name << "=";
            if (i->format == variable::single) {
                c_stream << "%" << variable::info[i->type].FormatChar << config_comment << "\\n\", " << i->name;
            } else if (i->format == variable::quoted) {
                c_stream << "%s" << config_comment << "\\n\", encode_string(" << i->name << ")";
            } else if (i->format == variable::mult_packed) {
                c_stream << "%s" << config_comment << "\\n\", char_array_pack((char *)" << i->name << ", " << i->length << ")";
            }
            c_stream << ");" << dependancy_suffix << "\n";
        }
    }
    if (defines.find("PSR_HASH") != defines.end()) {
        c_stream << "  outf(fp, \"\\n\\n\\n;Do not modify the following, for internal use only.\\n\");\n"
                 << "  outf(fp, \"PSR_HASH"
                 << "=%u\\n\", PSR_HASH);\n";
    }
    c_stream << "}\n"
             << "\n"
             << "unsigned char write_" << family_name << "_vars(const char *file)\n"
             << "{\n"
             << "  FILE *fp = 0;\n"
             << "\n";
    if (defines.find("PSR_EXTERN") == defines.end()) {
        c_stream << "  init_" << family_name << "_vars();\n"
                 << "\n";
    }
    c_stream << "  if ((fp = fopen(file, \"w\")))\n"
             << "  {\n"
             << "    write_" << family_name << "_vars_internal(fp, (int (*)(void *, const char *, ...))fprintf);\n"
             << "    fclose(fp);\n"
             << "\n"
             << "    return(1);\n"
             << "  }\n"
             << "  return(0);\n"
             << "}\n";

    if (defines.find("PSR_COMPRESSED") != defines.end()) {
        c_stream << "\n"
                 << "unsigned char write_" << family_name << "_vars_compressed(const char *file)\n"
                 << "{\n"
                 << "  gzFile gzfp;\n"
                 << "\n";
        if (defines.find("PSR_EXTERN") == defines.end()) {
            c_stream << "  init_" << family_name << "_vars();\n"
                     << "\n";
        }
        c_stream << "  if ((gzfp = gzopen(file, \"wb9\")))\n"
                 << "  {\n"
                 << "    write_" << family_name << "_vars_internal(gzfp, gzprintf);\n"
                 << "    gzclose(gzfp);\n"
                 << "\n"
                 << "    return(1);\n"
                 << "  }\n"
                 << "\n"
                 << "  return(0);\n"
                 << "}\n";
    }

    if (defines.find("PSR_MEMCPY") != defines.end()) {
        c_stream << "\n"
                 << "static unsigned int " << family_name << "_vars_memory(unsigned char *buffer, void *(*cpy)(void *, void *, size_t))\n"
                 << "{\n"
                 << "  unsigned char *p = buffer;\n";
        for (variable::config_data_array::iterator i = variable::config_data.begin(); i != variable::config_data.end(); i++) {
            string dependancy_prefix, dependancy_suffix;
            if (i->dependancy != "") {
                dependancy_prefix = string("if (") + string(i->dependancy, 0, i->dependancy.length() - 1) + string(") { ");
                dependancy_suffix = " }";
            }

            if (i->format == variable::ptr) {
                c_stream << "  " << dependancy_prefix << "cpy(p, " << i->name << ", sizeof(" << variable::info[i->type].CTypeSpace << ")*" << i->length << "); p += sizeof(" << variable::info[i->type].CTypeSpace << ")*" << i->length << ";" << dependancy_suffix << "\n";
            } else if (i->format != variable::none) {
                c_stream << "  " << dependancy_prefix << "cpy(p, " << ((i->format == variable::single) ? "&" : "") << i->name << ", sizeof(" << i->name << ")); p += sizeof(" << i->name << ");" << dependancy_suffix << "\n";
            }
        }
        c_stream << "  return(p-buffer);\n"
                 << "}\n"
                 << "\n"
                 << "static void *cpynull(void *l, void *r, size_t len){ return(0); }\n"
                 << "\n"
                 << "unsigned int size_" << family_name << "_vars_memory()\n"
                 << "{\n"
                 << "  return(" << family_name << "_vars_memory(0, cpynull));\n"
                 << "}\n"
                 << "\n"
                 << "void write_" << family_name << "_vars_memory(unsigned char *buffer)\n"
                 << "{\n"
                 << "  " << family_name << "_vars_memory(buffer, (void *(*)(void *, void *, size_t))memcpy);\n"
                 << "}\n";
    }
}

void output_packed_read(ostream& c_stream)
{
    if (variable::config_data.packed_used()) {
        c_stream << "\n"
                 << "static size_t base94_decode(const char *buffer)\n"
                 << "{\n"
                 << "  size_t size = 0;\n"
                 << "  int i;\n"
                 << "  for (i = 4; i >= 0; i--)\n"
                 << "  {\n"
                 << "    size *= 94;\n"
                 << "    size += (size_t)(buffer[i]-' ');\n"
                 << "  }\n"
                 << "  return(size);\n"
                 << "}\n"
                 << "\n"
                 << "static char *get_token(char *str, char *delim)\n"
                 << "{\n"
                 << "  static char *pos = 0;\n"
                 << "  char *token = 0;\n"
                 << "\n"
                 << "  if (str) //Start a new string?\n"
                 << "  {\n"
                 << "    pos = str;\n"
                 << "  }\n"
                 << "\n"
                 << "  if (pos)\n"
                 << "  {\n"
                 << "    //Skip delimiters\n"
                 << "    while (*pos && strchr(delim, *pos))\n"
                 << "    {\n"
                 << "      pos++;\n"
                 << "    }\n"
                 << "    if (*pos)\n"
                 << "    {\n"
                 << "      token = pos;\n"
                 << "\n"
                 << "      //Skip non-delimiters\n"
                 << "      while (*pos && !strchr(delim, *pos))\n"
                 << "      {\n"
                 << "        //Skip quoted characters\n"
                 << "        if ((*pos == '\\\"') || (*pos == '\\''))\n"
                 << "        {\n"
                 << "          char *match_pos = 0;\n"
                 << "          if ((match_pos = find_next_match(pos+1, *pos)))\n"
                 << "          {\n"
                 << "            pos = match_pos;\n"
                 << "          }\n"
                 << "        }\n"
                 << "        pos++;\n"
                 << "      }\n"
                 << "      if (*pos)\n"
                 << "      {\n"
                 << "        *pos++ = '\\0';\n"
                 << "      }\n"
                 << "    }\n"
                 << "  }\n"
                 << "  return(token);\n"
                 << "}\n"
                 << "\n"
                 << "static char *char_array_unpack(char *str)\n"
                 << "{\n"
                 << "  char packed[LINE_LENGTH];\n"
                 << "  char *p = packed, *token;\n"
                 << "  size_t len = 0;\n"
                 << "  memset(packed, 0, sizeof(packed));\n"
                 << "  for (token = get_token(str, \"\\\\\"); token; token = get_token(0, \"\\\\\"))\n"
                 << "  {\n"
                 << "    if (*token == '0')\n"
                 << "    {\n"
                 << "      size_t i = base94_decode(decode_string(token+1));\n"
                 << "      len += i;\n"
                 << "      if (len > sizeof(packed)) { break; }\n"
                 << "      memset(p, 0, i);\n"
                 << "      p += i;\n"
                 << "    }\n"
                 << "    else\n"
                 << "    {\n"
                 << "      char *decoded = decode_string(token);\n"
                 << "      size_t decoded_length = strlen(decoded);\n"
                 << "      len += decoded_length;\n"
                 << "      if (len > sizeof(packed))\n"
                 << "      {\n"
                 << "        memcpy(p, decoded, sizeof(packed)-(len-decoded_length));\n"
                 << "        break;\n"
                 << "      }\n"
                 << "      memcpy(p, decoded, decoded_length);\n"
                 << "      p += decoded_length;\n"
                 << "    }\n"
                 << "  }\n"
                 << "  memcpy(line, packed, sizeof(packed));"
                 << "  return(line);\n"
                 << "}\n";
    }
}

void output_array_read(ostream& c_stream, variable::ctype type)
{
    if (variable::config_data.ctype_mult_used(type) || variable::config_data.ctype_ptr_used(type)) {
        c_stream << "\n"
                 << "static void read_" << variable::info[type].CTypeUnderscore << "_array(char *line, " << variable::info[type].CTypeSpace << " *var, size_t size)\n"
                 << "{\n"
                 << "  size_t i;\n"
                 << "  char *token;\n"
                 << "  *var = (" << variable::info[type].CTypeSpace << ")" << (variable::info[type].Signed ? "atoi" : "atoui") << "(strtok(line, \", \\t\\r\\n\"));\n"
                 << "  for (i = 1; (i < size) && (token = strtok(0, \", \\t\\r\\n\")); i++)\n"
                 << "  {\n"
                 << "    var[i] = (" << variable::info[type].CTypeSpace << ")" << (variable::info[type].Signed ? "atoi" : "atoui") << "(token);\n"
                 << "  }\n"
                 << "}\n";
    }
}

void output_read_var(ostream& c_stream)
{
    output_packed_read(c_stream);
    output_array_read(c_stream, variable::UC);
    output_array_read(c_stream, variable::US);
    output_array_read(c_stream, variable::UD);
    output_array_read(c_stream, variable::SC);
    output_array_read(c_stream, variable::SS);
    output_array_read(c_stream, variable::SD);

    c_stream << "\n"
             << "static void read_" << family_name << "_vars_internal(void *fp, char *(*fin)(char *, int, void *), int (*fend)(void *))\n"
             << "{\n"
             << "  while (!fend(fp))\n"
             << "  {\n"
             << "    char *p, *var, *value;\n"
             << "\n"
             << "    fin(line, LINE_LENGTH, fp);\n"
             << "    if ((p = find_str(line, \";\"))) { *p = 0; }\n"
             << "    if ((p = strchr(line, '=')))\n"
             << "    {\n"
             << "      *p = 0;\n"
             << "      var = line;\n"
             << "      value = p+1;\n"
             << "      while (isspace(*var)) { var++; }\n"
             << "      while (isspace(*value)) { value++; }\n"
             << "      if ((p = find_str(var, \" \\t\\r\\n\"))) { *p = 0; }\n"
             << "      if ((p = find_str(value, \" \\t\\r\\n\"))) { *p = 0; }\n"
             << "      if (!*var || !*value) { continue; }\n";
    if (dependancies.size()) {
        c_stream << "      if ((p = strchr(var, ':')))\n"
                 << "      {\n"
                 << "        if (!strlen(p+1)) { continue; }\n";
        set<string>::iterator i = dependancies.begin();
        c_stream << "        if (!strncmp(var, \"" << *i << ":\", (p-var)+1)) { if (!" << *i << ") { continue; } }\n";
        for (i++; i != dependancies.end(); i++) {
            c_stream << "        else if (!strncmp(var, \"" << *i << ":\", (p-var)+1)) { if (!" << *i << ") { continue; } }\n";
        }
        c_stream << "        else { continue; }\n"
                 << "      }\n";
    }
    c_stream << "    }\n"
             << "    else\n"
             << "    {\n"
             << "      continue;\n"
             << "    }\n"
             << "\n";
    for (variable::config_data_array::iterator i = variable::config_data.begin(); i != variable::config_data.end(); i++) {
        if (i->format != variable::none) {
            c_stream << "    if (!strcmp(var, \"" << i->dependancy << i->name << "\")) { ";
            if (i->format == variable::single) {
                c_stream << i->name << " = (" << variable::info[i->type].CTypeSpace << ")" << (variable::info[i->type].Signed ? "atoi" : "atoui") << "(value);";
            } else if ((i->format == variable::mult) || (i->format == variable::ptr)) {
                c_stream << "read_" << variable::info[i->type].CTypeUnderscore
                         << "_array(value, " << i->name << ", " << i->length << ");";
            } else if (i->format == variable::quoted) {
                c_stream << "*" << i->name << " = 0; "
                         << "strncat(" << i->name << ", decode_string(value), sizeof(" << i->name << ")-1);";
            } else if (i->format == variable::mult_packed) {
                c_stream << "memcpy(" << i->name << ", char_array_unpack(value), " << i->length << ");";
            }
            c_stream << " continue; }\n";
        }
    }
    if (defines.find("PSR_HASH") != defines.end()) {
        c_stream << "    if (!strcmp(var, \"PSR_HASH\"))\n"
                 << "    {\n"
                 << "       if ((unsigned int)atoui(value) == PSR_HASH)\n"
                 << "       {\n"
                 << "         psr_init_done = 2;\n"
                 << "         continue;\n"
                 << "       }\n"
                 << "       break;\n"
                 << "    }\n";
    }
    c_stream << "  }\n";
    if (defines.find("PSR_HASH") != defines.end()) {
        c_stream << "  if (psr_init_done == 2)\n"
                 << "  {\n"
                 << "    psr_init_done = 1;\n"
                 << "  }\n"
                 << "  else\n"
                 << "  {\n"
                 << "    psr_init_done = 0;\n"
                 << "    init_" << family_name << "_vars();\n"
                 << "  }\n";
    }
    c_stream << "}\n"
             << "\n"
             << "unsigned char read_" << family_name << "_vars(const char *file)\n"
             << "{\n"
             << "  FILE *fp = 0;\n"
             << "\n";
    if (defines.find("PSR_EXTERN") == defines.end()) {
        c_stream << "  init_" << family_name << "_vars();\n"
                 << "\n";
    }
    c_stream << "  if ((fp = fopen(file, \"r\")))\n"
             << "  {\n"
             << "    read_" << family_name << "_vars_internal(fp, (char *(*)(char *, int, void *))fgets, (int (*)(void *))feof);\n"
             << "    fclose(fp);\n";
    if (defines.find("PSR_NOUPDATE") == defines.end()) {
        c_stream << "    write_" << family_name << "_vars(file);\n";
    }
    c_stream << "    return(1);\n"
             << "  }\n"
             << "\n";
    if (defines.find("PSR_NOUPDATE") == defines.end()) {
        c_stream << "  write_" << family_name << "_vars(file);\n";
    }
    c_stream << "  return(0);\n"
             << "}\n";

    if (defines.find("PSR_COMPRESSED") != defines.end()) {
        c_stream << "\n"
                 << "static char *gzgets_fix(char *buf, int len, void *file)\n"
                 << "{\n"
                 << "  return(gzgets(file, buf, len));\n"
                 << "}\n"
                 << "\n"
                 << "unsigned char read_" << family_name << "_vars_compressed(const char *file)\n"
                 << "{\n"
                 << "  gzFile gzfp;\n"
                 << "\n";
        if (defines.find("PSR_EXTERN") == defines.end()) {
            c_stream << "  init_" << family_name << "_vars();\n"
                     << "\n";
        }
        c_stream << "  if ((gzfp = gzopen(file, \"rb\")))\n"
                 << "  {\n"
                 << "    read_" << family_name << "_vars_internal(gzfp, gzgets_fix, gzeof);\n"
                 << "    gzclose(gzfp);\n";
        if (defines.find("PSR_NOUPDATE") == defines.end()) {
            c_stream << "    write_" << family_name << "_vars_compressed(file);\n";
        }
        c_stream << "    return(1);\n"
                 << "  }\n"
                 << "\n";
        if (defines.find("PSR_NOUPDATE") == defines.end()) {
            c_stream << "  write_" << family_name << "_vars_compressed(file);\n";
        }
        c_stream << "  return(0);\n"
                 << "}\n";
    }

    if (defines.find("PSR_MEMCPY") != defines.end()) {
        c_stream << "\n"
                 << "static void *cpyright(void *src, void *dest, size_t len)\n"
                 << "{\n"
                 << "  memcpy(dest, src, len);\n"
                 << "  return(0);\n"
                 << "}\n"
                 << "\n"
                 << "void read_" << family_name << "_vars_memory(unsigned char *buffer)\n"
                 << "{\n"
                 << "  " << family_name << "_vars_memory(buffer, cpyright);\n"
                 << "}\n";
    }
}

void handle_directive(const char* instruction, const char* label)
{
    if (!strcasecmp(instruction, "define")) {
        if (label) {
            defines.insert(label);
        } else {
            current_location.error("Could not get define label");
        }
    } else if (!strcasecmp(instruction, "undef")) {
        if (label) {
            defines.erase(label);
        } else {
            current_location.error("Could not get undefine label");
        }
    } else if (!strcasecmp(instruction, "ifdef")) {
        if (label) {
            if (defines.find(label) != defines.end()) {
                ifs.push(true);
            } else {
                ifs.push(false);
            }
        } else {
            current_location.error("Could not get ifdef label");
        }
    } else if (!strcasecmp(instruction, "ifndef")) {
        if (label) {
            if (defines.find(label) == defines.end()) {
                ifs.push(true);
            } else {
                ifs.push(false);
            }
        } else {
            current_location.error("Could not get ifndef label");
        }
    } else if (!strcasecmp(instruction, "else")) {
        if (label) {
            current_location.error("Processor directive else does not accept labels");
        } else {
            if (ifs.empty()) {
                current_location.error("Processor directive else without ifdef");
            } else {
                bool process = !ifs.top();
                ifs.pop();
                ifs.push(process);
            }
        }
    } else if (!strcasecmp(instruction, "elifdef") || !strcasecmp(instruction, "elseifdef")) {
        if (label) {
            if (ifs.top()) {
                ifs.pop();
                ifs.push(false);
            } else if (defines.find(label) != defines.end()) {
                ifs.pop();
                ifs.push(true);
            }
        } else {
            current_location.error("Could not get elseifdef label");
        }

    } else if (!strcasecmp(instruction, "endif")) {
        if (label) {
            current_location.error("Processor directive endif does not accept labels");
        } else {
            if (ifs.empty()) {
                current_location.error("Processor directive endif without ifdef");
            } else {
                ifs.pop();
            }
        }
    } else {
        current_location.error("Unknown processor directive");
    }
}

// Return the comment from global line variable
char* get_comment(char comment_seperator)
{
    char* comment = find_chr(line, comment_seperator);
    if (comment) {
        *comment = 0;
        comment++;
        if (isspace(comment[strlen(comment) - 1])) {
            comment[strlen(comment) - 1] = 0;
        }
    }
    return (comment);
}

void output_parser_comment(ostream& c_stream, const char* comment)
{
    if (comment) {
        c_stream << " //" << comment;
    }
    c_stream << "\n";
}

void output_header_conditional(ostream& cheader_stream, const char* instruction, const char* label)
{
    if ((!strcasecmp(instruction, "elifdef") || !strcasecmp(instruction, "elseifdef")) && label) {
        cheader_stream << "#elif defined(" << label << ")\n";
    } else {
        cheader_stream << "#" << instruction;
        if (label) {
            cheader_stream << " " << label;
        }
        cheader_stream << "\n";
    }
}

#define CONFIG_COMMENT (config_comment ? config_comment : "")

void parser_generate(istream& psr_stream, ostream& c_stream, ostream& cheader_stream, const string& cheader_file = "")
{
    current_location.line_number = current_location.column_number = 0;
    ostringstream cvars(""), hvars("");
    uLong psr_file_hash = crc32(0L, Z_NULL, 0);

    while (!psr_stream.eof()) {
        char* token;
        const char* parser_comment;
        const char* config_comment;

        psr_stream.getline(line, LINE_LENGTH);
        current_location.line_number++;
        psr_file_hash = crc32(psr_file_hash, (const Bytef*)line, strlen(line));

        parser_comment = get_comment(';');

        if (all_spaces(line)) {
            if (ifs.all_true()) {
                output_parser_comment(cvars, parser_comment);
            }
            continue;
        }

        config_comment = get_comment('@');

        if (all_spaces(line) && config_comment) {
            if (ifs.all_true()) {
                variable::config_data.add_comment(config_comment);
            }
            continue;
        }

        if ((token = get_token(line, " ")) && (strcasecmp(token, "NEWSYM") || (token = get_token(0, " ,")))) {
            if ((*token == '#') || (*token == '%')) {
                const char* next_token = get_token(0, " ");
                handle_directive(token + 1, next_token);

                if (cheader_stream && (!next_token || strncasecmp(next_token, "PSR_", strlen("PSR_")))) {
                    output_header_conditional(hvars, token + 1, next_token);
                }
                continue;
            }

            string varname;
            string dependancy;
            const char* d;

            if ((d = strchr(token, ':'))) {
                varname = d + 1;
                dependancy.assign(token, d - token);
                dependancies.insert(dependancy);
                dependancy += ':';
            } else {
                varname = token;
            }

            if ((token = get_token(0, " ,"))) {
                size_t array = 0;
                bool is_array = !strcasecmp(token, "times");
                bool is_packed = !strcasecmp(token, "packed");
                bool is_ptr = !strcasecmp(token, "ptr");
                if ((!is_array && !is_packed && !is_ptr) || ((token = get_token(0, " ")) && (array = enhanced_atoi(token)) && (token = get_token(0, " ")))) {
                    const char* asm_type = token;
                    const char* var_type = convert_asm_type(asm_type);

                    if (var_type) {
                        string initial_value = get_token(0, " ,\n");
                        ostringstream var_init("");

                        if (((initial_value[0] == '\"') && (initial_value[initial_value.length() - 1] == '\"')) || ((initial_value[0] == '\'') && (initial_value[initial_value.length() - 1] == '\''))) {
                            // Make sure it's double quoted
                            initial_value[0] = '\"';
                            initial_value[initial_value.length() - 1] = '\"';

                            if (!array) {
                                array = initial_value.length() - 1; // Size minus quotes plus null
                            }

                            var_init << "char " << varname << "[" << array << "];";

                            if (ifs.all_true()) {
                                ostringstream memset_line;

                                if (initial_value.length() - 2 < array) {
                                    memset_line << "strcpy(" << varname << ", " << initial_value << ");";
                                } else {
                                    memset_line << "strncpy(" << varname << ", " << initial_value << ", " << (array - 1) << "); "
                                                << varname << "[" << array << "] = 0;";
                                }
                                memsets.push_back(memset_line.str());
                                variable::config_data.add_var_quoted(varname, dependancy, CONFIG_COMMENT);
                            }
                        } else if (is_ptr) {
                            var_init << var_type << " *" << varname << ";";
                            if (ifs.all_true()) {
                                variable::config_data.add_var_ptr(varname, var_type, array, dependancy, CONFIG_COMMENT);
                            }
                        } else {
                            ssize_t init_value_num = safe_atoi(c_hex_convert(asm2c_hex_convert(initial_value)));

                            if ((init_value_num < 0) && !strncmp(var_type, "unsigned ", strlen("unsigned "))) {
                                var_type += strlen("unsigned ");
                                if (strcmp(var_type, "char") == 0)
                                    var_type -= sizeof("signed ") - 1;
                            }

                            var_init << var_type << " " << varname;
                            if (array) {
                                if (var_type_is_char(var_type) || !init_value_num) {
                                    var_init << "[" << array << "]";

                                    if (ifs.all_true()) {
                                        ostringstream memset_line;
                                        memset_line << "memset(" << varname << ", " << init_value_num << ", " << array;

                                        if (var_type_is_short(var_type)) {
                                            memset_line << short_scale;
                                        } else if (var_type_is_int(var_type)) {
                                            memset_line << int_scale;
                                        }

                                        memset_line << ");";
                                        memsets.push_back(memset_line.str());
                                    }
                                } else {
                                    var_init << "[" << array << "] = {";
                                    for (size_t i = array; i > 1; i--) {
                                        var_init << init_value_num << ",";
                                    }
                                    var_init << init_value_num << "}";
                                }

                                if (ifs.all_true()) {
                                    if (is_array) {
                                        variable::config_data.add_var_mult(varname, var_type, array, dependancy, CONFIG_COMMENT);
                                    } else if (is_packed) {
                                        variable::config_data.add_var_packed(varname, array, dependancy, CONFIG_COMMENT);
                                    }
                                }
                            } else {
                                if ((token = get_token(0, " ,\n"))) {
                                    array = 1;
                                    var_init << "[] = {" << init_value_num;
                                    do {
                                        var_init << "," << atoi(token);
                                        array++;
                                    } while ((token = get_token(0, " ,\n")));
                                    var_init << "}";

                                    if (ifs.all_true()) {
                                        variable::config_data.add_var_mult(varname, var_type, array, dependancy, CONFIG_COMMENT);
                                    }
                                } else {
                                    var_init << " = " << init_value_num;

                                    if (ifs.all_true()) {
                                        variable::config_data.add_var_single(varname, var_type, dependancy, CONFIG_COMMENT);
                                    }
                                }
                            }
                            var_init << ";";
                        }

                        if (ifs.all_true()) {
                            cvars << var_init.str();
                        }

                        if (cheader_stream) {
                            string header_data = var_init.str();
                            size_t equal_pos;
                            if ((equal_pos = header_data.find("=")) != string::npos) {
                                header_data.erase(equal_pos - 1);
                                header_data.append(";");
                            }
                            hvars << "extern " << header_data << "\n";
                        }
                    }
                    // Else already handled
                } else {
                    current_location.error("Could not get array size");
                }
            } else {
                current_location.error("Could not get type");
            }
        } else {
            current_location.error("Could not get variable name");
        }

        if (ifs.all_true()) {
            output_parser_comment(cvars, parser_comment);
        }
    }

    output_parser_start(c_stream, cheader_file);
    output_extsym_dependancies(c_stream);
    if (defines.find("PSR_EXTERN") == defines.end()) {
        c_stream << cvars.str();
        output_init_var(c_stream);
    } else if (!cheader_file.length()) {
        cerr << "Error: Requested PSR_EXTERN yet no header file specified." << endl;
    }

    if (defines.find("PSR_HASH") != defines.end()) {
        c_stream << "static unsigned int PSR_HASH = 0x" << hex << psr_file_hash << dec << ";\n";
    }

    output_write_var(c_stream);
    output_read_var(c_stream);
    c_stream << "\n";

    if (cheader_stream) {
        output_cheader_start(cheader_stream);
        cheader_stream << hvars.str();
        output_cheader_end(cheader_stream);
    }

    if (!ifs.empty()) {
        cerr << "Error: " << ifs.size() << " ifdef segments have no endif." << endl;
    }
}

int main(int argc, const char* const* const argv)
{
    const char* cheader_file = 0;
    bool compile = false;

    int param_pos = 1;
    for (; param_pos < argc; param_pos++) {
        if (!strncmp(argv[param_pos], "-D", 2)) {
            defines.insert(argv[param_pos] + 2);
        } else if (!strcmp(argv[param_pos], "-cheader")) {
            param_pos++;
            cheader_file = argv[param_pos];
        } else if (!strcmp(argv[param_pos], "-compile")) {
            compile = true;
        } else if (!strcmp(argv[param_pos], "-flags")) {
            param_pos++;
            cflags = argv[param_pos];
        } else if (!strcmp(argv[param_pos], "-fname")) {
            param_pos++;
            family_name = argv[param_pos];
        } else if (!strcmp(argv[param_pos], "-gcc")) {
            param_pos++;
            gcc = argv[param_pos];
        } else {
            break;
        }
    }

    if ((argc - param_pos) != 2) {
        cout << "Config file handler creator by Nach (C) 2005-2007\n"
             << "\n"
             << "Usage:\n"
             << "parsegen [options] <output> <input>\n"
             << "\n"
             << "\n"
             << "Options:\n"
             << "\n"
             << "  -Ddefine   Define a processor director. Example: -D__WIN32__\n"
             << "             Can specify multiple defines.\n"
             << "\n"
             << "  -cheader   Create a C/C++ header with the following name.\n"
             << "             Example: -cheader cfgvars.h\n"
             << "\n"
             << "  -fname     Use the following name for the main functions.\n"
             << "             Example: -fname math\n"
             << "             Would make init_cfg_vars become init_math_vars this\n"
             << "             also happens to write_cfg_vars and read_cfg_vars.\n"
             << "\n"
             << "  -compile   Compiles output instead of outputting C file.\n"
             << "\n"
             << "  -gcc       Use with -compile. Parameter passed in the name of\n"
             << "             the C compiler to use, it should be GCC based.\n"
             << "             It will not work with MSVC based compilers.\n"
             << "\n"
             << "  -flags     Use with -compile. Flags passed as next parameter\n"
             << "             are passed to the C compiler.\n"
             << "             Example: -flags \"-O3 -march=pentium3 -ggdb3\"\n"
             << "\n"
             << endl;

        return (1);
    }

    string cname = family_name + string(".c");
    const char *psr_file = argv[param_pos + 1], *c_file = compile ? cname.c_str() : argv[param_pos];
    const char* obj_file = compile ? argv[param_pos] : 0;
    int ret_val = 0;

    ifstream psr_stream(psr_file);
    if (psr_stream) {
        ofstream c_stream(c_file);
        if (c_stream) {
            ofstream cheader_stream;
            if (cheader_file) {
                cheader_stream.open(cheader_file);
                if (cheader_stream) {
                    parser_generate(psr_stream, c_stream, cheader_stream, cheader_file);
                } else {
                    cerr << "Error opening " << cheader_file << " for writing." << endl;
                    ret_val |= 8;
                }

                cheader_stream.close();
            } else {
                parser_generate(psr_stream, c_stream, cheader_stream);
            }
            c_stream.close();
        } else {
            cerr << "Error opening " << c_file << " for writing." << endl;
            ret_val |= 2;
        }

        psr_stream.close();
    } else {
        cerr << "Error opening " << psr_file << " for reading." << endl;
        ret_val |= 4;
    }

    if (!ret_val && compile) {
        string command = COMPILE_OBJ(obj_file, cname);
        cout << "parsegen: " << command << "\n";
        system(command.c_str());
        remove(cname.c_str());
    }

    return (ret_val);
}
