Panda3D
|
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 }