/* Copyright (C) 2005-2007 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 #include #include #include #include #include #include #include using namespace std; #include #include #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 /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 2048*4 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 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::iterator i = data.begin(); i != data.end(); i++) { if (!*i) { return(false); } } return(true); } private: vector data; }; set defines, dependancies; nstack ifs; typedef vector 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 }, { "char", "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_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 = "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 \n" << "#include \n" << "#include \n" << "#include \n"; if (defines.find("PSR_COMPRESSED") != defines.end()) { c_stream << "#include \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::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 (!*str && len)\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(" <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::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 "); } 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(size_t argc, const char *const *const argv) { const char *cheader_file = 0; bool compile = false; size_t 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] \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 the\n" << " 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); }