diff --git a/pandatool/src/xfile/xFileDataDef.cxx b/pandatool/src/xfile/xFileDataDef.cxx index c3444de188..fa7b309aa1 100644 --- a/pandatool/src/xfile/xFileDataDef.cxx +++ b/pandatool/src/xfile/xFileDataDef.cxx @@ -92,8 +92,12 @@ write_text(ostream &out, int indent_level) const { out << "UCHAR"; break; - case T_byte: - out << "BYTE"; + case T_sword: + out << "SWORD"; + break; + + case T_sdword: + out << "SDWORD"; break; case T_string: diff --git a/pandatool/src/xfile/xFileDataDef.h b/pandatool/src/xfile/xFileDataDef.h index 09ce025ccf..d08e15d791 100644 --- a/pandatool/src/xfile/xFileDataDef.h +++ b/pandatool/src/xfile/xFileDataDef.h @@ -41,7 +41,8 @@ public: T_double, T_char, T_uchar, - T_byte, + T_sword, + T_sdword, T_string, T_cstring, T_unicode, diff --git a/pandatool/src/xfile/xFileTemplate.I b/pandatool/src/xfile/xFileTemplate.I index e80bc56fca..72626b0623 100644 --- a/pandatool/src/xfile/xFileTemplate.I +++ b/pandatool/src/xfile/xFileTemplate.I @@ -32,7 +32,7 @@ get_guid() const { // Access: Public // Description: Sets whether the template is considered "open" or // not. If it is open (this flag is true), the set of -// restrictions is ignored and the instances of this +// options is ignored and the instances of this // template may include any types of children. If it is // closed (false), only the named types may be added. //////////////////////////////////////////////////////////////////// @@ -46,7 +46,7 @@ set_open(bool open) { // Access: Public // Description: Returns whether the template is considered "open" or // not. If it is open (this flag is true), the set of -// restrictions is ignored and the instances of this +// options is ignored and the instances of this // template may include any types of children. If it is // closed (false), only the named types may be added. //////////////////////////////////////////////////////////////////// @@ -56,36 +56,34 @@ get_open() const { } //////////////////////////////////////////////////////////////////// -// Function: XFileTemplate::add_restriction +// Function: XFileTemplate::add_option // Access: Public // Description: Adds a new type to the list of allowable types of -// child nodes for an instance of this template. The -// method is a little misnamed; we are actually removing -// a restriction by adding this type to the list. +// child nodes for an instance of this template. //////////////////////////////////////////////////////////////////// INLINE void XFileTemplate:: -add_restriction(XFileTemplate *restriction) { - _restrictions.push_back(restriction); +add_option(XFileTemplate *option) { + _options.push_back(option); } //////////////////////////////////////////////////////////////////// -// Function: XFileTemplate::get_num_restrictions +// Function: XFileTemplate::get_num_options // Access: Public -// Description: Returns the number of templates on the restrictions +// Description: Returns the number of templates on the options // list. //////////////////////////////////////////////////////////////////// INLINE int XFileTemplate:: -get_num_restrictions() const { - return _restrictions.size(); +get_num_options() const { + return _options.size(); } //////////////////////////////////////////////////////////////////// -// Function: XFileTemplate::get_restriction +// Function: XFileTemplate::get_option // Access: Public -// Description: Returns the nth template on the restrictions list. +// Description: Returns the nth template on the options list. //////////////////////////////////////////////////////////////////// INLINE XFileTemplate *XFileTemplate:: -get_restriction(int n) const { - nassertr(n >= 0 && n < (int)_restrictions.size(), NULL); - return _restrictions[n]; +get_option(int n) const { + nassertr(n >= 0 && n < (int)_options.size(), NULL); + return _options[n]; } diff --git a/pandatool/src/xfile/xFileTemplate.cxx b/pandatool/src/xfile/xFileTemplate.cxx index 1b5717efdf..79cdca4b58 100644 --- a/pandatool/src/xfile/xFileTemplate.cxx +++ b/pandatool/src/xfile/xFileTemplate.cxx @@ -53,7 +53,7 @@ XFileTemplate:: void XFileTemplate:: clear() { XFileNode::clear(); - _restrictions.clear(); + _options.clear(); } //////////////////////////////////////////////////////////////////// @@ -76,16 +76,16 @@ write_text(ostream &out, int indent_level) const { indent(out, indent_level + 2) << "[ ... ]\n"; - } else if (!_restrictions.empty()) { + } else if (!_options.empty()) { // A restricted template indent(out, indent_level + 2); char delimiter = '['; - Restrictions::const_iterator ri; - for (ri = _restrictions.begin(); ri != _restrictions.end(); ++ri) { - XFileTemplate *restriction = (*ri); + Options::const_iterator ri; + for (ri = _options.begin(); ri != _options.end(); ++ri) { + XFileTemplate *option = (*ri); out << delimiter << " " - << restriction->get_name() << " <" << restriction->get_guid() + << option->get_name() << " <" << option->get_guid() << ">"; delimiter = ','; } diff --git a/pandatool/src/xfile/xFileTemplate.h b/pandatool/src/xfile/xFileTemplate.h index ea854fc180..11236f4127 100644 --- a/pandatool/src/xfile/xFileTemplate.h +++ b/pandatool/src/xfile/xFileTemplate.h @@ -43,16 +43,16 @@ public: INLINE void set_open(bool open); INLINE bool get_open() const; - INLINE void add_restriction(XFileTemplate *restriction); - INLINE int get_num_restrictions() const; - INLINE XFileTemplate *get_restriction(int n) const; + INLINE void add_option(XFileTemplate *option); + INLINE int get_num_options() const; + INLINE XFileTemplate *get_option(int n) const; private: WindowsGuid _guid; bool _open; - typedef pvector< PT(XFileTemplate) > Restrictions; - Restrictions _restrictions; + typedef pvector< PT(XFileTemplate) > Options; + Options _options; public: static TypeHandle get_class_type() { diff --git a/pandatool/src/xfile/xLexer.lxx b/pandatool/src/xfile/xLexer.lxx index 6270171096..d5187927bd 100644 --- a/pandatool/src/xfile/xLexer.lxx +++ b/pandatool/src/xfile/xLexer.lxx @@ -10,6 +10,7 @@ #include "xParserDefs.h" #include "xParser.h" #include "indent.h" +#include "string_utils.h" #include "notify.h" static int yyinput(void); // declared by flex. @@ -340,6 +341,42 @@ scan_guid_string() { return result; } +// Parses the text into a list of integers and returns them. +static PTA_int +parse_int_list(const string &text, const string &delimiter) { + PTA_int result; + + vector_string words; + tokenize(text, words, delimiter); + + vector_string::const_iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + int number = 0; + string_to_int(*wi, number); + result.push_back(number); + } + + return result; +} + +// Parses the text into a list of doubles and returns them. +static PTA_double +parse_double_list(const string &text, const string &delimiter) { + PTA_double result; + + vector_string words; + tokenize(text, words, delimiter); + + vector_string::const_iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + double number = 0; + string_to_double(*wi, number); + result.push_back(number); + } + + return result; +} + // accept() is called below as each piece is pulled off and @@ -351,7 +388,6 @@ inline void accept() { %} INTEGERNUM ([+-]?([0-9]+)) -UNSIGNED_HEXNUM (0x[0-9a-fA-F]*) REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) %% @@ -388,128 +424,161 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) accept(); } +"{" { + accept(); + return TOKEN_OBRACE; +} + +"}" { + accept(); + return TOKEN_CBRACE; +} + +"[" { + accept(); + return TOKEN_OBRACKET; +} + +"]" { + accept(); + return TOKEN_CBRACKET; +} + +"." { + accept(); + return TOKEN_DOT; +} + +"," { + accept(); + return TOKEN_COMMA; +} + +";" { + accept(); + return TOKEN_SEMICOLON; +} "array" { accept(); - return KW_ARRAY; -} - -"binary" { - accept(); - return KW_BINARY; + return TOKEN_ARRAY; } "byte" { accept(); - return KW_BYTE; + return TOKEN_UCHAR; } "char" { accept(); - return KW_CHAR; + return TOKEN_CHAR; } "cstring" { accept(); - return KW_CSTRING; + return TOKEN_CSTRING; } "double" { accept(); - return KW_DOUBLE; + return TOKEN_DOUBLE; } "dword" { accept(); - return KW_DWORD; + return TOKEN_DWORD; +} + +"sdword" { + accept(); + return TOKEN_SDWORD; } "float" { accept(); - return KW_FLOAT; + return TOKEN_FLOAT; } "string" { accept(); - return KW_STRING; + return TOKEN_LPSTR; } "template" { accept(); - return KW_TEMPLATE; + return TOKEN_TEMPLATE; } "uchar" { accept(); - return KW_UCHAR; + return TOKEN_UCHAR; } "unicode" { accept(); - return KW_UNICODE; + return TOKEN_UNICODE; +} + +"sword" { + accept(); + return TOKEN_SWORD; } "word" { accept(); - return KW_WORD; + return TOKEN_WORD; } {INTEGERNUM} { // A signed or unsigned integer number. accept(); - xyylval.u.s_int = atol(xyytext); + xyylval.u.number = atol(xyytext); xyylval.str = yytext; - return INTEGER; + return TOKEN_INTEGER; } -{UNSIGNED_HEXNUM} { - // A hexadecimal integer number. - accept(); +({INTEGERNUM};)+ { + // An integer as part of a semicolon-delimited list. + accept(); + xyylval.int_list = parse_int_list(xyytext, ";"); + xyylval.u.separator_token = TOKEN_SEMICOLON; - // As above, we'll decode the hex string by hand. - xyylval.str = xyytext; - xyylval.u.s_int = 0; - const char *p = xyytext + 2; - while (*p != '\0') { - int next_value = xyylval.u.s_int * 16; - if (next_value < xyylval.u.s_int) { - xyyerror("Number out of range."); - xyylval.u.s_int = 1; - return INTEGER; - } - - if (isalpha(*p)) { - xyylval.u.s_int = next_value + (tolower(*p) - 'a' + 10); - } else { - xyylval.u.s_int = next_value + (*p - '0'); - } - ++p; - } - - return INTEGER; + return TOKEN_INTEGER_LIST; } -{REALNUM} { - // A floating-point number. +({REALNUM};)+ { + // A floating-point number as part of a semicolon-delimited list. accept(); - xyylval.u.real = atof(xyytext); - xyylval.str = xyytext; - return REAL; + xyylval.double_list = parse_double_list(xyytext, ";"); + xyylval.u.separator_token = TOKEN_SEMICOLON; + + return TOKEN_REALNUM_LIST; +} + +({INTEGERNUM},)+ { + // An integer as part of a semicolon-delimited list. + accept(); + xyylval.int_list = parse_int_list(xyytext, ","); + xyylval.u.separator_token = TOKEN_COMMA; + + return TOKEN_INTEGER_LIST; +} + +({REALNUM},)+ { + // A floating-point number as part of a semicolon-delimited list. + accept(); + xyylval.double_list = parse_double_list(xyytext, ","); + xyylval.u.separator_token = TOKEN_COMMA; + + return TOKEN_REALNUM_LIST; } ["] { // Quoted string. accept(); xyylval.str = scan_quoted_string('"'); - return STRING; -} - -['] { - // Single-quoted string. - accept(); - xyylval.str = scan_quoted_string('\''); - return STRING; + return TOKEN_STRING; } [<] { @@ -521,20 +590,14 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) xyyerror("Malformed GUID."); } - return WINDOWS_GUID; + return TOKEN_GUID; } [A-Za-z_-][A-Za-z_0-9-]* { // Identifier. accept(); xyylval.str = xyytext; - return IDENTIFIER; + return TOKEN_NAME; } - -. { - // Send any other printable character as itself. - accept(); - return xyytext[0]; -} diff --git a/pandatool/src/xfile/xParser.yxx b/pandatool/src/xfile/xParser.yxx index ab0babac02..c04ee3a580 100644 --- a/pandatool/src/xfile/xParser.yxx +++ b/pandatool/src/xfile/xParser.yxx @@ -3,6 +3,12 @@ // //////////////////////////////////////////////////////////////////// +// The grammar defined in this file is taken more-or-less from the +// Microsoft DirectX File Format Specification document, version 1.13. +// The document actually describes a slightly incomplete and incorrect +// grammar, so small changes had to be made, but an attempt was made +// to be as faithful as possible to the intention of the spec. + %{ #include "xLexerDefs.h" #include "xParserDefs.h" @@ -45,114 +51,119 @@ x_cleanup_parser() { %} -%token INTEGER -%token REAL -%token STRING IDENTIFIER -%token WINDOWS_GUID +// These token values are taken from the DirectX spec; the particular +// numeric values are useful when reading an .x file in binary mode +// (which basically just streams the tokens retrieved by the lexer). -%token KW_ARRAY -%token KW_BINARY -%token KW_BYTE -%token KW_CHAR -%token KW_CSTRING -%token KW_DOUBLE -%token KW_DWORD -%token KW_FLOAT -%token KW_STRING -%token KW_TEMPLATE -%token KW_UCHAR -%token KW_UNICODE -%token KW_WORD +%token TOKEN_NAME 1 +%token TOKEN_STRING 2 +%token TOKEN_INTEGER 3 +%token TOKEN_GUID 5 +%token TOKEN_INTEGER_LIST 6 +%token TOKEN_REALNUM_LIST 7 -%type xtemplate -%type data_object -%type optional_identifier +%token TOKEN_OBRACE 10 +%token TOKEN_CBRACE 11 +%token TOKEN_OPAREN 12 +%token TOKEN_CPAREN 13 +%token TOKEN_OBRACKET 14 +%token TOKEN_CBRACKET 15 +%token TOKEN_OANGLE 16 +%token TOKEN_CANGLE 17 +%token TOKEN_DOT 18 +%token TOKEN_COMMA 19 +%token TOKEN_SEMICOLON 20 +%token TOKEN_TEMPLATE 31 +%token TOKEN_WORD 40 +%token TOKEN_DWORD 41 +%token TOKEN_FLOAT 42 +%token TOKEN_DOUBLE 43 +%token TOKEN_CHAR 44 +%token TOKEN_UCHAR 45 +%token TOKEN_SWORD 46 +%token TOKEN_SDWORD 47 +%token TOKEN_VOID 48 +%token TOKEN_LPSTR 49 +%token TOKEN_UNICODE 50 +%token TOKEN_CSTRING 51 +%token TOKEN_ARRAY 52 + +%type template +%type object +%type primitive_type +%type integer_list +%type realnum_list +%type name +%type optional_name +%type class_id +%type optional_class_id %% xfile: empty - | xfile xtemplate - | xfile data_object + | xfile template + | xfile object ; -xtemplate: - KW_TEMPLATE IDENTIFIER '{' WINDOWS_GUID +template: + TOKEN_TEMPLATE name TOKEN_OBRACE class_id { $$ = current_node; XFileTemplate *templ = new XFileTemplate($2, $4); current_node->add_child(templ); current_node = templ; } - template_members template_restrictions '}' + template_parts TOKEN_CBRACE { $$ = current_node; current_node = $5; } - ; + ; -template_members: - empty - | template_members template_member ';' - ; +template_parts: + template_members_part TOKEN_OBRACKET template_option_info TOKEN_CBRACKET + | template_members_list + ; -template_member: - data_def - | KW_ARRAY data_def array_dimensions - ; +template_members_part: + empty + | template_members_list + ; -data_def: - KW_WORD optional_identifier +template_option_info: + ellipsis { - current_data_def = new XFileDataDef(XFileDataDef::T_word, $2); + DCAST(XFileTemplate, current_node)->set_open(true); +} + | template_option_list + ; + +template_members_list: + template_members + | template_members_list template_members + ; + +template_members: + primitive + | array + | template_reference + ; + +primitive: + primitive_type optional_name TOKEN_SEMICOLON +{ + current_data_def = new XFileDataDef($1, $2); current_node->add_child(current_data_def); } - | KW_DWORD optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_dword, $2); - current_node->add_child(current_data_def); -} - | KW_FLOAT optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_float, $2); - current_node->add_child(current_data_def); -} - | KW_DOUBLE optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_double, $2); - current_node->add_child(current_data_def); -} - | KW_CHAR optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_char, $2); - current_node->add_child(current_data_def); -} - | KW_UCHAR optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_uchar, $2); - current_node->add_child(current_data_def); -} - | KW_BYTE optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_byte, $2); - current_node->add_child(current_data_def); -} - | KW_STRING optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_string, $2); - current_node->add_child(current_data_def); -} - | KW_CSTRING optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_cstring, $2); - current_node->add_child(current_data_def); -} - | KW_UNICODE optional_identifier -{ - current_data_def = new XFileDataDef(XFileDataDef::T_unicode, $2); - current_node->add_child(current_data_def); -} - | IDENTIFIER optional_identifier + ; + +array: + TOKEN_ARRAY array_data_type dimension_list TOKEN_SEMICOLON + ; + +template_reference: + name optional_name TOKEN_SEMICOLON { XFileTemplate *xtemplate = x_file->find_template($1); if (xtemplate == (XFileTemplate *)NULL) { @@ -162,58 +173,118 @@ data_def: current_node->add_child(current_data_def); } } - ; + ; -array_dimensions: - array_level - | array_dimensions array_level - ; +primitive_type: + TOKEN_WORD +{ + $$ = XFileDataDef::T_word; +} + | TOKEN_DWORD +{ + $$ = XFileDataDef::T_dword; +} + | TOKEN_FLOAT +{ + $$ = XFileDataDef::T_float; +} + | TOKEN_DOUBLE +{ + $$ = XFileDataDef::T_double; +} + | TOKEN_CHAR +{ + $$ = XFileDataDef::T_char; +} + | TOKEN_UCHAR +{ + $$ = XFileDataDef::T_uchar; +} + | TOKEN_SWORD +{ + $$ = XFileDataDef::T_sword; +} + | TOKEN_SDWORD +{ + $$ = XFileDataDef::T_sdword; +} + | TOKEN_LPSTR +{ + $$ = XFileDataDef::T_string; +} + | TOKEN_UNICODE +{ + $$ = XFileDataDef::T_unicode; +} + | TOKEN_CSTRING +{ + $$ = XFileDataDef::T_cstring; +} + ; -array_level: - '[' INTEGER ']' +array_data_type: + primitive_type name { - current_data_def->add_array_def(XFileArrayDef($2)); + current_data_def = new XFileDataDef($1, $2); + current_node->add_child(current_data_def); } - | '[' IDENTIFIER ']' -{ - XFileNode *data_def = current_node->find_child($2); - if (data_def == (XFileNode *)NULL) { - yyerror("Unknown identifier: " + $2); - } else { - current_data_def->add_array_def(XFileArrayDef(DCAST(XFileDataDef, data_def))); - } -} - ; - -template_restrictions: - empty - | '[' '.' '.' '.' ']' -{ - DCAST(XFileTemplate, current_node)->set_open(true); -} - | '[' template_restriction_list ']' - ; - -template_restriction_list: - template_restriction_element -{ -} - | template_restriction_list ',' template_restriction_element -{ -} - ; - -template_restriction_element: - IDENTIFIER + | name name { XFileTemplate *xtemplate = x_file->find_template($1); if (xtemplate == (XFileTemplate *)NULL) { yyerror("Unknown template: " + $1); } else { - DCAST(XFileTemplate, current_node)->add_restriction(xtemplate); + current_data_def = new XFileDataDef(XFileDataDef::T_template, $2, xtemplate); + current_node->add_child(current_data_def); } } - | IDENTIFIER WINDOWS_GUID + ; + +dimension_list: + dimension + | dimension_list dimension + ; + +dimension: + TOKEN_OBRACKET dimension_size TOKEN_CBRACKET + ; + +dimension_size: + TOKEN_INTEGER +{ + current_data_def->add_array_def(XFileArrayDef($1)); +} + | name +{ + XFileNode *data_def = current_node->find_child($1); + if (data_def == (XFileNode *)NULL) { + yyerror("Unknown identifier: " + $1); + } else { + current_data_def->add_array_def(XFileArrayDef(DCAST(XFileDataDef, data_def))); + } +} + ; + +template_option_list: + template_option_part +{ +} + | template_option_list template_option_part +{ +} + ; + +template_option_part: + name +{ + XFileTemplate *xtemplate = x_file->find_template($1); + if (xtemplate == (XFileTemplate *)NULL) { + yyerror("Unknown template: " + $1); + } else { + DCAST(XFileTemplate, current_node)->add_option(xtemplate); + } +} + | name TOKEN_GUID { XFileTemplate *xtemplate = x_file->find_template($2); if (xtemplate == (XFileTemplate *)NULL) { @@ -223,13 +294,41 @@ template_restriction_element: xyywarning("GUID identifies template " + xtemplate->get_name() + ", not " + $1); } - DCAST(XFileTemplate, current_node)->add_restriction(xtemplate); + DCAST(XFileTemplate, current_node)->add_option(xtemplate); } } - ; + ; -data_object: - IDENTIFIER optional_identifier '{' +name: + TOKEN_NAME + ; + +optional_name: + empty +{ + $$ = string(); +} + | name + ; + +class_id: + TOKEN_GUID + ; + +optional_class_id: + empty +{ + $$ = WindowsGuid(); +} + | class_id + ; + +ellipsis: + TOKEN_DOT TOKEN_DOT TOKEN_DOT + ; + +object: + name optional_name TOKEN_OBRACE { XFileTemplate *xtemplate = x_file->find_template($1); $$ = current_node; @@ -243,55 +342,59 @@ data_object: current_node = templ; } } - data_object_members '}' + optional_class_id data_parts_list TOKEN_CBRACE { $$ = current_node; current_node = $4; } ; +data_parts_list: + data_part + | data_parts_list data_part + ; -data_object_members: - empty - | data_object_members data_object_member - ; +data_part: + data_reference +{ +} + | object +{ +} + | integer_list +{ +} + | realnum_list +{ +} + | string list_separator +{ +} + | list_separator +{ +} + ; + +integer_list: + TOKEN_INTEGER_LIST + ; -data_object_member: - data_object -{ -} - | data_reference -{ -} - | INTEGER -{ -} - | REAL -{ -} - | STRING -{ -} - | ';' -{ -} - | ',' -{ -} +realnum_list: + TOKEN_REALNUM_LIST + ; + +string: + TOKEN_STRING + ; + +list_separator: + TOKEN_SEMICOLON + | TOKEN_COMMA ; data_reference: - '{' IDENTIFIER '}' - ; - -optional_identifier: - empty -{ - $$ = string(); -} - | IDENTIFIER - ; + TOKEN_OBRACE name optional_class_id TOKEN_CBRACE + ; empty: ; - diff --git a/pandatool/src/xfile/xParserDefs.h b/pandatool/src/xfile/xParserDefs.h index 3969b7a767..207a2f1dc2 100644 --- a/pandatool/src/xfile/xParserDefs.h +++ b/pandatool/src/xfile/xParserDefs.h @@ -20,8 +20,10 @@ #define XPARSERDEFS_H #include "pandatoolbase.h" - #include "windowsGuid.h" +#include "xFileDataDef.h" +#include "pta_int.h" +#include "pta_double.h" class XFile; class XFileNode; @@ -40,12 +42,15 @@ int xyyparse(); class XTokenType { public: union U { - int s_int; - double real; + int number; XFileNode *node; + XFileDataDef::Type primitive_type; + int separator_token; // This is filled in for double_list and int_list. } u; string str; WindowsGuid guid; + PTA_double double_list; + PTA_int int_list; }; // The yacc-generated code expects to use the symbol 'YYSTYPE' to