// Filename: xFileDataDef.cxx // Created by: drose (03Oct04) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "xFileDataDef.h" #include "indent.h" #include "xLexerDefs.h" #include "xFileParseData.h" #include "xFileDataObjectInteger.h" #include "xFileDataObjectDouble.h" #include "xFileDataObjectString.h" #include "xFileDataNodeTemplate.h" #include "xFileDataObjectArray.h" #include "string_utils.h" TypeHandle XFileDataDef::_type_handle; //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// XFileDataDef:: ~XFileDataDef() { clear(); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::clear // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// void XFileDataDef:: clear() { XFileNode::clear(); _array_def.clear(); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::add_array_def // Access: Public // Description: Adds an additional array dimension to the data // description. //////////////////////////////////////////////////////////////////// void XFileDataDef:: add_array_def(const XFileArrayDef &array_def) { _array_def.push_back(array_def); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::write_text // Access: Public, Virtual // Description: Writes a suitable representation of this node to an // .x file in text mode. //////////////////////////////////////////////////////////////////// void XFileDataDef:: write_text(ostream &out, int indent_level) const { indent(out, indent_level); if (!_array_def.empty()) { out << "array "; } switch (_type) { case T_word: out << "WORD"; break; case T_dword: out << "DWORD"; break; case T_float: out << "FLOAT"; break; case T_double: out << "DOUBLE"; break; case T_char: out << "CHAR"; break; case T_uchar: out << "UCHAR"; break; case T_sword: out << "SWORD"; break; case T_sdword: out << "SDWORD"; break; case T_string: out << "STRING"; break; case T_cstring: out << "CSTRING"; break; case T_unicode: out << "UNICODE"; break; case T_template: out << _template->get_name(); break; } if (has_name()) { out << " " << get_name(); } ArrayDef::const_iterator ai; for (ai = _array_def.begin(); ai != _array_def.end(); ++ai) { (*ai).output(out); } out << ";\n"; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::repack_data // Access: Public, Virtual // Description: This is called on the template that defines an // object, once the data for the object has been parsed. // It is responsible for identifying which component of // the template owns each data element, and packing the // data elements appropriately back into the object. // // It returns true on success, or false on an error // (e.g. too many semicolons, not enough data elements, // mismatched data type). //////////////////////////////////////////////////////////////////// bool XFileDataDef:: repack_data(XFileDataObject *object, const XFileParseDataList &parse_data_list, XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index) const { if (index >= parse_data_list._list.size()) { xyyerror("Not enough data elements in structure."); return false; } // We'll fill this in with the data value we pack, if any. PT(XFileDataObject) data_value; // What kind of data element are we expecting? switch (_type) { case T_word: case T_dword: case T_char: case T_uchar: case T_sword: case T_sdword: // Expected integer data. data_value = unpack_value(parse_data_list, 0, prev_data, index, sub_index, &XFileDataDef::unpack_integer_value); break; case T_float: case T_double: data_value = unpack_value(parse_data_list, 0, prev_data, index, sub_index, &XFileDataDef::unpack_double_value); break; case T_string: case T_cstring: case T_unicode: data_value = unpack_value(parse_data_list, 0, prev_data, index, sub_index, &XFileDataDef::unpack_string_value); break; case T_template: data_value = unpack_value(parse_data_list, 0, prev_data, index, sub_index, &XFileDataDef::unpack_template_value); break; } if (data_value != (XFileDataObject *)NULL) { object->add_element(data_value); prev_data[this] = data_value; } return XFileNode::repack_data(object, parse_data_list, prev_data, index, sub_index); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::fill_zero_data // Access: Public, Virtual // Description: This is similar to repack_data(), except it is used // to fill the initial values for a newly-created // template object to zero. //////////////////////////////////////////////////////////////////// bool XFileDataDef:: fill_zero_data(XFileDataObject *object) const { PT(XFileDataObject) data_value; // What kind of data element are we expecting? switch (_type) { case T_word: case T_dword: case T_char: case T_uchar: case T_sword: case T_sdword: data_value = zero_fill_value(0, &XFileDataDef::zero_fill_integer_value); break; case T_float: case T_double: data_value = zero_fill_value(0, &XFileDataDef::zero_fill_double_value); break; case T_string: case T_cstring: case T_unicode: data_value = zero_fill_value(0, &XFileDataDef::zero_fill_string_value); break; case T_template: data_value = zero_fill_value(0, &XFileDataDef::zero_fill_template_value); break; } if (data_value != (XFileDataObject *)NULL) { object->add_element(data_value); } return XFileNode::fill_zero_data(object); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::matches // Access: Public, Virtual // Description: Returns true if the node, particularly a template // node, is structurally equivalent to the other node // (which must be of the same type). This checks data // element types, but does not compare data element // names. //////////////////////////////////////////////////////////////////// bool XFileDataDef:: matches(const XFileNode *other) const { if (!XFileNode::matches(other)) { return false; } const XFileDataDef *data_def = DCAST(XFileDataDef, other); if (data_def->get_data_type() != get_data_type()) { return false; } if (get_data_type() == T_template && !get_template()->matches(data_def->get_template())) { return false; } if (data_def->get_num_array_defs() != get_num_array_defs()) { return false; } for (int i = 0; i < get_num_array_defs(); i++) { if (!get_array_def(i).matches(data_def->get_array_def(i), this, data_def)) { return false; } } return true; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::unpack_integer_value // Access: Private // Description: Unpacks and returns the next sequential integer value // from the parse_data_list. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: unpack_integer_value(const XFileParseDataList &parse_data_list, const XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index) const { nassertr(index < parse_data_list._list.size(), NULL); const XFileParseData &parse_data = parse_data_list._list[index]; PT(XFileDataObject) data_value; if ((parse_data._parse_flags & XFileParseData::PF_int) != 0) { nassertr(sub_index < parse_data._int_list.size(), false); int value = parse_data._int_list[sub_index]; data_value = new XFileDataObjectInteger(this, value); sub_index++; if (sub_index >= parse_data._int_list.size()) { index++; sub_index = 0; } } else { parse_data.yyerror("Expected integer data for " + get_name()); } return data_value; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::unpack_double_value // Access: Private // Description: Unpacks and returns the next sequential double value // from the parse_data_list. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: unpack_double_value(const XFileParseDataList &parse_data_list, const XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index) const { nassertr(index < parse_data_list._list.size(), NULL); const XFileParseData &parse_data = parse_data_list._list[index]; PT(XFileDataObject) data_value; if ((parse_data._parse_flags & XFileParseData::PF_double) != 0) { nassertr(sub_index < parse_data._double_list.size(), false); double value = parse_data._double_list[sub_index]; data_value = new XFileDataObjectDouble(this, value); sub_index++; if (sub_index >= parse_data._double_list.size()) { index++; sub_index = 0; } } else if ((parse_data._parse_flags & XFileParseData::PF_int) != 0) { nassertr(sub_index < parse_data._int_list.size(), false); int value = parse_data._int_list[sub_index]; data_value = new XFileDataObjectDouble(this, value); sub_index++; if (sub_index >= parse_data._int_list.size()) { index++; sub_index = 0; } } else { parse_data.yyerror("Expected floating-point data for " + get_name()); } return data_value; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::unpack_string_value // Access: Private // Description: Unpacks and returns the next sequential string value // from the parse_data_list. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: unpack_string_value(const XFileParseDataList &parse_data_list, const XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index) const { nassertr(index < parse_data_list._list.size(), NULL); const XFileParseData &parse_data = parse_data_list._list[index]; PT(XFileDataObject) data_value; if ((parse_data._parse_flags & XFileParseData::PF_string) != 0) { data_value = new XFileDataObjectString(this, parse_data._string); index++; sub_index = 0; } else { parse_data.yyerror("Expected string data for " + get_name()); } return data_value; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::unpack_template_value // Access: Private // Description: Unpacks a nested template object's data. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: unpack_template_value(const XFileParseDataList &parse_data_list, const XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index) const { PT(XFileDataNodeTemplate) data_value = new XFileDataNodeTemplate(get_x_file(), get_name(), _template); PrevData nested_prev_data(prev_data); if (!_template->repack_data(data_value, parse_data_list, nested_prev_data, index, sub_index)) { return NULL; } return data_value.p(); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::unpack_value // Access: Private // Description: Unpacks and returns the next sequential value, of the // type supported by the unpack_method. If the value // is an array type, unpacks all the elements of the // array. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: unpack_value(const XFileParseDataList &parse_data_list, int array_index, const XFileDataDef::PrevData &prev_data, size_t &index, size_t &sub_index, XFileDataDef::UnpackMethod unpack_method) const { PT(XFileDataObject) data_value; if (array_index == (int)_array_def.size()) { data_value = (this->*unpack_method)(parse_data_list, prev_data, index, sub_index); } else { data_value = new XFileDataObjectArray(this); int array_size = _array_def[array_index].get_size(prev_data); for (int i = 0; i < array_size; i++) { if (index >= parse_data_list._list.size()) { xyyerror(string("Expected ") + format_string(array_size) + " array elements, found " + format_string(i)); return data_value; } PT(XFileDataObject) array_element = unpack_value(parse_data_list, array_index + 1, prev_data, index, sub_index, unpack_method); if (array_element == (XFileDataObject *)NULL) { return NULL; } data_value->add_element(array_element); } } return data_value; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::zero_fill_integer_value // Access: Private // Description: Returns a newly-allocated zero integer value. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: zero_fill_integer_value() const { return new XFileDataObjectInteger(this, 0); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::zero_fill_double_value // Access: Private // Description: Returns a newly-allocated zero floating-point value. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: zero_fill_double_value() const { return new XFileDataObjectDouble(this, 0.0); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::zero_fill_string_value // Access: Private // Description: Returns a newly-allocated empty string value. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: zero_fill_string_value() const { return new XFileDataObjectString(this, ""); } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::zero_fill_template_value // Access: Private // Description: Returns a newly-allocated zero-filled nested template // value. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: zero_fill_template_value() const { PT(XFileDataObject) data_value = new XFileDataNodeTemplate(get_x_file(), get_name(), _template); if (!_template->fill_zero_data(data_value)) { return NULL; } return data_value; } //////////////////////////////////////////////////////////////////// // Function: XFileDataDef::zero_fill_value // Access: Private // Description: Creates a zero-valued element for the next sequential // value, of the type returned by the zero_fill_method. // If the value is a fixed-size array type, zero-fills // all the elements of the array. //////////////////////////////////////////////////////////////////// PT(XFileDataObject) XFileDataDef:: zero_fill_value(int array_index, XFileDataDef::ZeroFillMethod zero_fill_method) const { PT(XFileDataObject) data_value; if (array_index == (int)_array_def.size()) { data_value = (this->*zero_fill_method)(); } else { data_value = new XFileDataObjectArray(this); int array_size = 0; if (_array_def[array_index].is_fixed_size()) { array_size = _array_def[array_index].get_fixed_size(); } for (int i = 0; i < array_size; i++) { PT(XFileDataObject) array_element = zero_fill_value(array_index + 1, zero_fill_method); if (array_element == (XFileDataObject *)NULL) { return NULL; } data_value->add_element(array_element); } } return data_value; }