Panda3D

xFile.cxx

00001 // Filename: xFile.cxx
00002 // Created by:  drose (03Oct04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "xFile.h"
00016 #include "xParserDefs.h"
00017 #include "xLexerDefs.h"
00018 #include "xFileTemplate.h"
00019 #include "xFileDataNodeTemplate.h"
00020 #include "config_xfile.h"
00021 #include "standard_templates.h"
00022 #include "zStream.h"
00023 #include "virtualFileSystem.h"
00024 #include "dcast.h"
00025 
00026 TypeHandle XFile::_type_handle;
00027 PT(XFile) XFile::_standard_templates;
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: XFile::Constructor
00031 //       Access: Public
00032 //  Description:
00033 ////////////////////////////////////////////////////////////////////
00034 XFile::
00035 XFile(bool keep_names) : XFileNode(this, "") {
00036   _major_version = 3;
00037   _minor_version = 2;
00038   _format_type = FT_text;
00039   _float_size = FS_64;
00040   _keep_names = keep_names;
00041 }
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: XFile::Destructor
00045 //       Access: Public
00046 //  Description:
00047 ////////////////////////////////////////////////////////////////////
00048 XFile::
00049 ~XFile() {
00050   clear();
00051 }
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: XFile::clear
00055 //       Access: Public, Virtual
00056 //  Description: Removes all of the classes defined within the XFile
00057 //               and prepares it for reading a new file.
00058 ////////////////////////////////////////////////////////////////////
00059 void XFile::
00060 clear() {
00061   XFileNode::clear();
00062 
00063   _nodes_by_guid.clear();
00064 }
00065 
00066 ////////////////////////////////////////////////////////////////////
00067 //     Function: XFile::read
00068 //       Access: Public
00069 //  Description: Opens and reads the indicated .x file by name.  The
00070 //               nodes and templates defined in the file will be
00071 //               appended to the set of nodes already recorded, if
00072 //               any.
00073 //
00074 //               Returns true if the file is successfully read, false
00075 //               if there was an error (in which case the file might
00076 //               have been partially read).
00077 ////////////////////////////////////////////////////////////////////
00078 bool XFile::
00079 read(Filename filename) {
00080   filename.set_text();
00081   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00082   istream *in = vfs->open_read_file(filename, true);
00083   if (in == (istream *)NULL) {
00084     xfile_cat.error()
00085       << "Cannot open " << filename << " for reading.\n";
00086     return false;
00087   }
00088   bool okflag = read(*in, filename);
00089   vfs->close_read_file(in);
00090   return okflag;
00091 }
00092 
00093 ////////////////////////////////////////////////////////////////////
00094 //     Function: XFile::read
00095 //       Access: Public
00096 //  Description: Parses the already-opened input stream for
00097 //               distributed class descriptions.  The filename
00098 //               parameter is optional and is only used when reporting
00099 //               errors.
00100 //
00101 //               The distributed classes defined in the file will be
00102 //               appended to the set of distributed classes already
00103 //               recorded, if any.
00104 //
00105 //               Returns true if the file is successfully read, false
00106 //               if there was an error (in which case the file might
00107 //               have been partially read).
00108 ////////////////////////////////////////////////////////////////////
00109 bool XFile::
00110 read(istream &in, const string &filename) {
00111   if (!read_header(in)) {
00112     return false;
00113   }
00114 
00115   if (_format_type != FT_text) {
00116     // Does anyone actually use the binary format?  It wouldn't be too
00117     // hard to support it if there were any reason at all to do so.
00118     xfile_cat.error()
00119       << "Cannot read binary .x files at this time.\n";
00120     return false;
00121   }
00122 
00123   // We must call this first so the standard templates file will be
00124   // parsed and available by the time we need it--it's tricky to
00125   // invoke the parser from within another parser instance.
00126   get_standard_templates();
00127 
00128   x_init_parser(in, filename, *this);
00129   xyyparse();
00130   x_cleanup_parser();
00131 
00132   return (x_error_count() == 0);
00133 }
00134 
00135 ////////////////////////////////////////////////////////////////////
00136 //     Function: XFile::write
00137 //       Access: Public
00138 //  Description: Opens the indicated filename for output and writes a
00139 //               parseable description of all the known distributed
00140 //               classes to the file.
00141 //
00142 //               Returns true if the description is successfully
00143 //               written, false otherwise.
00144 ////////////////////////////////////////////////////////////////////
00145 bool XFile::
00146 write(Filename filename) const {
00147   ofstream out;
00148 
00149   // We actually open the file to write in binary mode, to avoid the
00150   // MS-DOS newline characters (since Windows seems to do this too).
00151   filename.set_binary();
00152   filename.open_write(out);
00153 
00154   if (!out) {
00155     xfile_cat.error()
00156       << "Can't open " << filename << " for output.\n";
00157     return false;
00158   }
00159 
00160 #ifdef HAVE_ZLIB
00161   if (filename.get_extension() == "pz") {
00162     // The filename ends in .pz, which means to automatically compress
00163     // the X file that we write.
00164     OCompressStream compressor(&out, false);
00165     return write(compressor);
00166   }
00167 #endif  // HAVE_ZLIB
00168 
00169   return write(out);
00170 }
00171 
00172 ////////////////////////////////////////////////////////////////////
00173 //     Function: XFile::write
00174 //       Access: Public
00175 //  Description: Writes a parseable description of all the known
00176 //               nodes and templates to the stream.
00177 //
00178 //               Returns true if the description is successfully
00179 //               written, false otherwise.
00180 ////////////////////////////////////////////////////////////////////
00181 bool XFile::
00182 write(ostream &out) const {
00183   if (!write_header(out)) {
00184     return false;
00185   }
00186 
00187   write_text(out, 0);
00188 
00189   return true;
00190 }
00191 
00192 ////////////////////////////////////////////////////////////////////
00193 //     Function: XFile::find_template
00194 //       Access: Public
00195 //  Description: Returns the template associated with the indicated
00196 //               name, if any, or NULL if none.
00197 ////////////////////////////////////////////////////////////////////
00198 XFileTemplate *XFile::
00199 find_template(const string &name) const {
00200   XFileTemplate *standard = (XFileTemplate *)NULL;
00201   const XFile *standard_templates = get_standard_templates();
00202   if (standard_templates != this) {
00203     standard = standard_templates->find_template(name);
00204   }
00205 
00206   XFileNode *child = find_child(name);
00207   if (child != (XFileNode *)NULL &&
00208       child->is_of_type(XFileTemplate::get_class_type())) {
00209     XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
00210     if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
00211       // If the template matches a standard template, return the
00212       // standard instead.  The assumption is that code may expect a
00213       // certain naming scheme for the data elements of the standard
00214       // template, so we want to be sure to provide it.
00215       return standard;
00216     }
00217     return xtemplate;
00218   }
00219 
00220   return standard;
00221 }
00222 
00223 ////////////////////////////////////////////////////////////////////
00224 //     Function: XFile::find_template
00225 //       Access: Public
00226 //  Description: Returns the template associated with the indicated
00227 //               GUID, if any, or NULL if none.
00228 ////////////////////////////////////////////////////////////////////
00229 XFileTemplate *XFile::
00230 find_template(const WindowsGuid &guid) const {
00231   XFileTemplate *standard = (XFileTemplate *)NULL;
00232   const XFile *standard_templates = get_standard_templates();
00233   if (standard_templates != this) {
00234     standard = standard_templates->find_template(guid);
00235   }
00236 
00237   NodesByGuid::const_iterator gi;
00238   gi = _nodes_by_guid.find(guid);
00239   if (gi != _nodes_by_guid.end() && 
00240       (*gi).second->is_of_type(XFileTemplate::get_class_type())) {
00241     XFileTemplate *xtemplate = DCAST(XFileTemplate, (*gi).second);
00242     if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
00243       // If the template matches a standard template, return the
00244       // standard instead.  The assumption is that code may expect a
00245       // certain naming scheme for the data elements of the standard
00246       // template, so we want to be sure to provide it.
00247       return standard;
00248     }
00249     return xtemplate;
00250   }
00251 
00252   return standard;
00253 }
00254 
00255 ////////////////////////////////////////////////////////////////////
00256 //     Function: XFile::find_standard_template
00257 //       Access: Public, Static
00258 //  Description: Returns the standard template associated with the
00259 //               indicated name, if any, or NULL if none.
00260 ////////////////////////////////////////////////////////////////////
00261 XFileTemplate *XFile::
00262 find_standard_template(const string &name) {
00263   const XFile *standard_templates = get_standard_templates();
00264   return standard_templates->find_template(name);
00265 }
00266 
00267 ////////////////////////////////////////////////////////////////////
00268 //     Function: XFile::find_standard_template
00269 //       Access: Public, Static
00270 //  Description: Returns the template associated with the indicated
00271 //               GUID, if any, or NULL if none.
00272 ////////////////////////////////////////////////////////////////////
00273 XFileTemplate *XFile::
00274 find_standard_template(const WindowsGuid &guid) {
00275   const XFile *standard_templates = get_standard_templates();
00276   return standard_templates->find_template(guid);
00277 }
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: XFile::find_data_object
00281 //       Access: Public
00282 //  Description: Returns the data object associated with the indicated
00283 //               name, if any, or NULL if none.
00284 ////////////////////////////////////////////////////////////////////
00285 XFileDataNodeTemplate *XFile::
00286 find_data_object(const string &name) const {
00287   XFileNode *child = find_descendent(name);
00288   if (child != (XFileNode *)NULL &&
00289       child->is_of_type(XFileDataNodeTemplate::get_class_type())) {
00290     return DCAST(XFileDataNodeTemplate, child);
00291   }
00292 
00293   return NULL;
00294 }
00295 
00296 ////////////////////////////////////////////////////////////////////
00297 //     Function: XFile::find_data_object
00298 //       Access: Public
00299 //  Description: Returns the data object associated with the indicated
00300 //               GUID, if any, or NULL if none.
00301 ////////////////////////////////////////////////////////////////////
00302 XFileDataNodeTemplate *XFile::
00303 find_data_object(const WindowsGuid &guid) const {
00304   NodesByGuid::const_iterator gi;
00305   gi = _nodes_by_guid.find(guid);
00306   if (gi != _nodes_by_guid.end() && 
00307       (*gi).second->is_of_type(XFileDataNodeTemplate::get_class_type())) {
00308     return DCAST(XFileDataNodeTemplate, (*gi).second);
00309   }
00310 
00311   return NULL;
00312 }
00313 
00314 ////////////////////////////////////////////////////////////////////
00315 //     Function: XFile::write_text
00316 //       Access: Public, Virtual
00317 //  Description: Writes a suitable representation of this node to an
00318 //               .x file in text mode.
00319 ////////////////////////////////////////////////////////////////////
00320 void XFile::
00321 write_text(ostream &out, int indent_level) const {
00322   Children::const_iterator ci;
00323   for (ci = _children.begin(); ci != _children.end(); ++ci) {
00324     (*ci)->write_text(out, indent_level);
00325     out << "\n";
00326   }
00327 }
00328 
00329 ////////////////////////////////////////////////////////////////////
00330 //     Function: XFile::read_header
00331 //       Access: Private
00332 //  Description: Reads the header and magic number associated with the
00333 //               file.  Returns true on success, false otherwise.
00334 ////////////////////////////////////////////////////////////////////
00335 bool XFile::
00336 read_header(istream &in) {
00337   char magic[4];
00338   if (!in.read(magic, 4)) {
00339     xfile_cat.error()
00340       << "Empty file.\n";
00341     return false;
00342   }
00343 
00344   if (memcmp(magic, "xof ", 4) != 0) {
00345     xfile_cat.error()
00346       << "Not a DirectX file.\n";
00347     return false;
00348   }
00349 
00350   char version[4];
00351   if (!in.read(version, 4)) {
00352     xfile_cat.error()
00353       << "Truncated file.\n";
00354     return false;
00355   }
00356   _major_version = (version[0] - '0') * 10 + (version[1] - '0');
00357   _minor_version = (version[2] - '0') * 10 + (version[3] - '0');
00358 
00359   char format[4];
00360   if (!in.read(format, 4)) {
00361     xfile_cat.error()
00362       << "Truncated file.\n";
00363     return false;
00364   }
00365 
00366   if (memcmp(format, "txt ", 4) == 0) {
00367     _format_type = FT_text;
00368 
00369   } else if (memcmp(format, "bin ", 4) == 0) {
00370     _format_type = FT_binary;
00371 
00372   } else if (memcmp(format, "com ", 4) == 0) {
00373     _format_type = FT_compressed;
00374 
00375   } else {
00376     xfile_cat.error()
00377       << "Unknown format type: " << string(format, 4) << "\n";
00378     return false;
00379   }
00380 
00381   if (_format_type == FT_compressed) {
00382     // Read and ignore the compression type, since we don't support
00383     // compression anyway.
00384     char compression_type[4];
00385     in.read(compression_type, 4);
00386   }
00387 
00388   char float_size[4];
00389   if (!in.read(float_size, 4)) {
00390     xfile_cat.error()
00391       << "Truncated file.\n";
00392     return false;
00393   }
00394 
00395   if (memcmp(float_size, "0032", 4) == 0) {
00396     _float_size = FS_32;
00397 
00398   } else if (memcmp(float_size, "0064", 4) == 0) {
00399     _float_size = FS_64;
00400 
00401   } else {
00402     xfile_cat.error()
00403       << "Unknown float size: " << string(float_size, 4) << "\n";
00404     return false;
00405   }
00406 
00407   return true;
00408 }
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: XFile::write_header
00412 //       Access: Private
00413 //  Description: Writes the header and magic number associated with the
00414 //               file.  Returns true on success, false otherwise.
00415 ////////////////////////////////////////////////////////////////////
00416 bool XFile::
00417 write_header(ostream &out) const {
00418   out.write("xof ", 4);
00419 
00420   char buffer[128];
00421   sprintf(buffer, "%02d%02d", _major_version, _minor_version);
00422   if (strlen(buffer) != 4) {
00423     xfile_cat.error()
00424       << "Invalid version: " << _major_version << "." << _minor_version
00425       << "\n";
00426     return false;
00427   }
00428 
00429   out.write(buffer, 4);
00430 
00431   switch (_format_type) {
00432   case FT_text:
00433     out.write("txt ", 4);
00434     break;
00435  
00436   case FT_binary:
00437     out.write("bin ", 4);
00438     break;
00439   
00440   case FT_compressed:
00441     out.write("cmp ", 4);
00442     break;
00443 
00444   default:
00445     xfile_cat.error()
00446       << "Invalid format type: " << _format_type << "\n";
00447     return false;
00448   }
00449 
00450   if (_format_type == FT_compressed) {
00451     // Write a bogus compression type, just so we have a valid header.
00452     out.write("xxx ", 4);
00453   }
00454 
00455   switch (_float_size) {
00456   case FS_32:
00457     out.write("0032", 4);
00458     break;
00459  
00460   case FS_64:
00461     out.write("0064", 4);
00462     break;
00463 
00464   default:
00465     xfile_cat.error()
00466       << "Invalid float size: " << _float_size << "\n";
00467     return false;
00468   }
00469 
00470   if (_format_type == FT_text) {
00471     // If it's a text format, we can now write a newline.
00472     out << "\n";
00473   }
00474 
00475   return true;
00476 }
00477 
00478 ////////////////////////////////////////////////////////////////////
00479 //     Function: XFile::get_standard_templates
00480 //       Access: Private, Static
00481 //  Description: Returns a global XFile object that contains the
00482 //               standard list of Direct3D template definitions that
00483 //               may be assumed to be at the head of every file.
00484 ////////////////////////////////////////////////////////////////////
00485 const XFile *XFile::
00486 get_standard_templates() {
00487   if (_standard_templates == (XFile *)NULL) {
00488     // The standardTemplates.x file has been compiled into this
00489     // binary.  Extract it out.
00490 
00491     string data((const char *)standard_templates_data, standard_templates_data_len);
00492 
00493 #ifdef HAVE_ZLIB
00494     // The data is stored compressed; decompress it on-the-fly.
00495     istringstream inz(data);
00496     IDecompressStream in(&inz, false);
00497     
00498 #else
00499     // The data is stored uncompressed, so just load it.
00500     istringstream in(data);
00501 #endif  // HAVE_ZLIB
00502     
00503     _standard_templates = new XFile;
00504     if (!_standard_templates->read(in, "standardTemplates.x")) {
00505       xfile_cat.error()
00506         << "Internal error: Unable to parse built-in standardTemplates.x!\n";
00507     }
00508 
00509     // Now flag all of these templates as "standard".
00510     for (int i = 0; i < _standard_templates->get_num_children(); i++) {
00511       XFileNode *child = _standard_templates->get_child(i);
00512       if (child->is_of_type(XFileTemplate::get_class_type())) {
00513         XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
00514         xtemplate->_is_standard = true;
00515       }
00516     }
00517   }
00518   
00519   return _standard_templates;
00520 }
 All Classes Functions Variables Enumerations