Panda3D
 All Classes Functions Variables Enumerations
xFile.cxx
1 // Filename: xFile.cxx
2 // Created by: drose (03Oct04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "xFile.h"
16 #include "xParserDefs.h"
17 #include "xLexerDefs.h"
18 #include "xFileTemplate.h"
19 #include "xFileDataNodeTemplate.h"
20 #include "config_xfile.h"
21 #include "standard_templates.h"
22 #include "zStream.h"
23 #include "virtualFileSystem.h"
24 #include "dcast.h"
25 
26 TypeHandle XFile::_type_handle;
27 PT(XFile) XFile::_standard_templates;
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: XFile::Constructor
31 // Access: Public
32 // Description:
33 ////////////////////////////////////////////////////////////////////
34 XFile::
35 XFile(bool keep_names) : XFileNode(this, "") {
36  _major_version = 3;
37  _minor_version = 2;
38  _format_type = FT_text;
39  _float_size = FS_64;
40  _keep_names = keep_names;
41 }
42 
43 ////////////////////////////////////////////////////////////////////
44 // Function: XFile::Destructor
45 // Access: Public
46 // Description:
47 ////////////////////////////////////////////////////////////////////
48 XFile::
49 ~XFile() {
50  clear();
51 }
52 
53 ////////////////////////////////////////////////////////////////////
54 // Function: XFile::clear
55 // Access: Public, Virtual
56 // Description: Removes all of the classes defined within the XFile
57 // and prepares it for reading a new file.
58 ////////////////////////////////////////////////////////////////////
59 void XFile::
60 clear() {
62 
63  _nodes_by_guid.clear();
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: XFile::read
68 // Access: Public
69 // Description: Opens and reads the indicated .x file by name. The
70 // nodes and templates defined in the file will be
71 // appended to the set of nodes already recorded, if
72 // any.
73 //
74 // Returns true if the file is successfully read, false
75 // if there was an error (in which case the file might
76 // have been partially read).
77 ////////////////////////////////////////////////////////////////////
78 bool XFile::
79 read(Filename filename) {
80  filename.set_text();
82  istream *in = vfs->open_read_file(filename, true);
83  if (in == (istream *)NULL) {
84  xfile_cat.error()
85  << "Cannot open " << filename << " for reading.\n";
86  return false;
87  }
88  bool okflag = read(*in, filename);
89  vfs->close_read_file(in);
90  return okflag;
91 }
92 
93 ////////////////////////////////////////////////////////////////////
94 // Function: XFile::read
95 // Access: Public
96 // Description: Parses the already-opened input stream for
97 // distributed class descriptions. The filename
98 // parameter is optional and is only used when reporting
99 // errors.
100 //
101 // The distributed classes defined in the file will be
102 // appended to the set of distributed classes already
103 // recorded, if any.
104 //
105 // Returns true if the file is successfully read, false
106 // if there was an error (in which case the file might
107 // have been partially read).
108 ////////////////////////////////////////////////////////////////////
109 bool XFile::
110 read(istream &in, const string &filename) {
111  if (!read_header(in)) {
112  return false;
113  }
114 
115  if (_format_type != FT_text) {
116  // Does anyone actually use the binary format? It wouldn't be too
117  // hard to support it if there were any reason at all to do so.
118  xfile_cat.error()
119  << "Cannot read binary .x files at this time.\n";
120  return false;
121  }
122 
123  // We must call this first so the standard templates file will be
124  // parsed and available by the time we need it--it's tricky to
125  // invoke the parser from within another parser instance.
126  get_standard_templates();
127 
128  x_init_parser(in, filename, *this);
129  xyyparse();
130  x_cleanup_parser();
131 
132  return (x_error_count() == 0);
133 }
134 
135 ////////////////////////////////////////////////////////////////////
136 // Function: XFile::write
137 // Access: Public
138 // Description: Opens the indicated filename for output and writes a
139 // parseable description of all the known distributed
140 // classes to the file.
141 //
142 // Returns true if the description is successfully
143 // written, false otherwise.
144 ////////////////////////////////////////////////////////////////////
145 bool XFile::
146 write(Filename filename) const {
147  ofstream out;
148 
149  // We actually open the file to write in binary mode, to avoid the
150  // MS-DOS newline characters (since Windows seems to do this too).
151  filename.set_binary();
152  filename.open_write(out);
153 
154  if (!out) {
155  xfile_cat.error()
156  << "Can't open " << filename << " for output.\n";
157  return false;
158  }
159 
160 #ifdef HAVE_ZLIB
161  if (filename.get_extension() == "pz") {
162  // The filename ends in .pz, which means to automatically compress
163  // the X file that we write.
164  OCompressStream compressor(&out, false);
165  return write(compressor);
166  }
167 #endif // HAVE_ZLIB
168 
169  return write(out);
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: XFile::write
174 // Access: Public
175 // Description: Writes a parseable description of all the known
176 // nodes and templates to the stream.
177 //
178 // Returns true if the description is successfully
179 // written, false otherwise.
180 ////////////////////////////////////////////////////////////////////
181 bool XFile::
182 write(ostream &out) const {
183  if (!write_header(out)) {
184  return false;
185  }
186 
187  write_text(out, 0);
188 
189  return true;
190 }
191 
192 ////////////////////////////////////////////////////////////////////
193 // Function: XFile::find_template
194 // Access: Public
195 // Description: Returns the template associated with the indicated
196 // name, if any, or NULL if none.
197 ////////////////////////////////////////////////////////////////////
199 find_template(const string &name) const {
200  XFileTemplate *standard = (XFileTemplate *)NULL;
201  const XFile *standard_templates = get_standard_templates();
202  if (standard_templates != this) {
203  standard = standard_templates->find_template(name);
204  }
205 
206  XFileNode *child = find_child(name);
207  if (child != (XFileNode *)NULL &&
208  child->is_of_type(XFileTemplate::get_class_type())) {
209  XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
210  if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
211  // If the template matches a standard template, return the
212  // standard instead. The assumption is that code may expect a
213  // certain naming scheme for the data elements of the standard
214  // template, so we want to be sure to provide it.
215  return standard;
216  }
217  return xtemplate;
218  }
219 
220  return standard;
221 }
222 
223 ////////////////////////////////////////////////////////////////////
224 // Function: XFile::find_template
225 // Access: Public
226 // Description: Returns the template associated with the indicated
227 // GUID, if any, or NULL if none.
228 ////////////////////////////////////////////////////////////////////
230 find_template(const WindowsGuid &guid) const {
231  XFileTemplate *standard = (XFileTemplate *)NULL;
232  const XFile *standard_templates = get_standard_templates();
233  if (standard_templates != this) {
234  standard = standard_templates->find_template(guid);
235  }
236 
237  NodesByGuid::const_iterator gi;
238  gi = _nodes_by_guid.find(guid);
239  if (gi != _nodes_by_guid.end() &&
240  (*gi).second->is_of_type(XFileTemplate::get_class_type())) {
241  XFileTemplate *xtemplate = DCAST(XFileTemplate, (*gi).second);
242  if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
243  // If the template matches a standard template, return the
244  // standard instead. The assumption is that code may expect a
245  // certain naming scheme for the data elements of the standard
246  // template, so we want to be sure to provide it.
247  return standard;
248  }
249  return xtemplate;
250  }
251 
252  return standard;
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: XFile::find_standard_template
257 // Access: Public, Static
258 // Description: Returns the standard template associated with the
259 // indicated name, if any, or NULL if none.
260 ////////////////////////////////////////////////////////////////////
262 find_standard_template(const string &name) {
263  const XFile *standard_templates = get_standard_templates();
264  return standard_templates->find_template(name);
265 }
266 
267 ////////////////////////////////////////////////////////////////////
268 // Function: XFile::find_standard_template
269 // Access: Public, Static
270 // Description: Returns the template associated with the indicated
271 // GUID, if any, or NULL if none.
272 ////////////////////////////////////////////////////////////////////
275  const XFile *standard_templates = get_standard_templates();
276  return standard_templates->find_template(guid);
277 }
278 
279 ////////////////////////////////////////////////////////////////////
280 // Function: XFile::find_data_object
281 // Access: Public
282 // Description: Returns the data object associated with the indicated
283 // name, if any, or NULL if none.
284 ////////////////////////////////////////////////////////////////////
286 find_data_object(const string &name) const {
287  XFileNode *child = find_descendent(name);
288  if (child != (XFileNode *)NULL &&
289  child->is_of_type(XFileDataNodeTemplate::get_class_type())) {
290  return DCAST(XFileDataNodeTemplate, child);
291  }
292 
293  return NULL;
294 }
295 
296 ////////////////////////////////////////////////////////////////////
297 // Function: XFile::find_data_object
298 // Access: Public
299 // Description: Returns the data object associated with the indicated
300 // GUID, if any, or NULL if none.
301 ////////////////////////////////////////////////////////////////////
303 find_data_object(const WindowsGuid &guid) const {
304  NodesByGuid::const_iterator gi;
305  gi = _nodes_by_guid.find(guid);
306  if (gi != _nodes_by_guid.end() &&
307  (*gi).second->is_of_type(XFileDataNodeTemplate::get_class_type())) {
308  return DCAST(XFileDataNodeTemplate, (*gi).second);
309  }
310 
311  return NULL;
312 }
313 
314 ////////////////////////////////////////////////////////////////////
315 // Function: XFile::write_text
316 // Access: Public, Virtual
317 // Description: Writes a suitable representation of this node to an
318 // .x file in text mode.
319 ////////////////////////////////////////////////////////////////////
320 void XFile::
321 write_text(ostream &out, int indent_level) const {
322  Children::const_iterator ci;
323  for (ci = _children.begin(); ci != _children.end(); ++ci) {
324  (*ci)->write_text(out, indent_level);
325  out << "\n";
326  }
327 }
328 
329 ////////////////////////////////////////////////////////////////////
330 // Function: XFile::read_header
331 // Access: Private
332 // Description: Reads the header and magic number associated with the
333 // file. Returns true on success, false otherwise.
334 ////////////////////////////////////////////////////////////////////
335 bool XFile::
336 read_header(istream &in) {
337  char magic[4];
338  if (!in.read(magic, 4)) {
339  xfile_cat.error()
340  << "Empty file.\n";
341  return false;
342  }
343 
344  if (memcmp(magic, "xof ", 4) != 0) {
345  xfile_cat.error()
346  << "Not a DirectX file.\n";
347  return false;
348  }
349 
350  char version[4];
351  if (!in.read(version, 4)) {
352  xfile_cat.error()
353  << "Truncated file.\n";
354  return false;
355  }
356  _major_version = (version[0] - '0') * 10 + (version[1] - '0');
357  _minor_version = (version[2] - '0') * 10 + (version[3] - '0');
358 
359  char format[4];
360  if (!in.read(format, 4)) {
361  xfile_cat.error()
362  << "Truncated file.\n";
363  return false;
364  }
365 
366  if (memcmp(format, "txt ", 4) == 0) {
367  _format_type = FT_text;
368 
369  } else if (memcmp(format, "bin ", 4) == 0) {
370  _format_type = FT_binary;
371 
372  } else if (memcmp(format, "com ", 4) == 0) {
373  _format_type = FT_compressed;
374 
375  } else {
376  xfile_cat.error()
377  << "Unknown format type: " << string(format, 4) << "\n";
378  return false;
379  }
380 
381  if (_format_type == FT_compressed) {
382  // Read and ignore the compression type, since we don't support
383  // compression anyway.
384  char compression_type[4];
385  in.read(compression_type, 4);
386  }
387 
388  char float_size[4];
389  if (!in.read(float_size, 4)) {
390  xfile_cat.error()
391  << "Truncated file.\n";
392  return false;
393  }
394 
395  if (memcmp(float_size, "0032", 4) == 0) {
396  _float_size = FS_32;
397 
398  } else if (memcmp(float_size, "0064", 4) == 0) {
399  _float_size = FS_64;
400 
401  } else {
402  xfile_cat.error()
403  << "Unknown float size: " << string(float_size, 4) << "\n";
404  return false;
405  }
406 
407  return true;
408 }
409 
410 ////////////////////////////////////////////////////////////////////
411 // Function: XFile::write_header
412 // Access: Private
413 // Description: Writes the header and magic number associated with the
414 // file. Returns true on success, false otherwise.
415 ////////////////////////////////////////////////////////////////////
416 bool XFile::
417 write_header(ostream &out) const {
418  out.write("xof ", 4);
419 
420  char buffer[128];
421  sprintf(buffer, "%02d%02d", _major_version, _minor_version);
422  if (strlen(buffer) != 4) {
423  xfile_cat.error()
424  << "Invalid version: " << _major_version << "." << _minor_version
425  << "\n";
426  return false;
427  }
428 
429  out.write(buffer, 4);
430 
431  switch (_format_type) {
432  case FT_text:
433  out.write("txt ", 4);
434  break;
435 
436  case FT_binary:
437  out.write("bin ", 4);
438  break;
439 
440  case FT_compressed:
441  out.write("cmp ", 4);
442  break;
443 
444  default:
445  xfile_cat.error()
446  << "Invalid format type: " << _format_type << "\n";
447  return false;
448  }
449 
450  if (_format_type == FT_compressed) {
451  // Write a bogus compression type, just so we have a valid header.
452  out.write("xxx ", 4);
453  }
454 
455  switch (_float_size) {
456  case FS_32:
457  out.write("0032", 4);
458  break;
459 
460  case FS_64:
461  out.write("0064", 4);
462  break;
463 
464  default:
465  xfile_cat.error()
466  << "Invalid float size: " << _float_size << "\n";
467  return false;
468  }
469 
470  if (_format_type == FT_text) {
471  // If it's a text format, we can now write a newline.
472  out << "\n";
473  }
474 
475  return true;
476 }
477 
478 ////////////////////////////////////////////////////////////////////
479 // Function: XFile::get_standard_templates
480 // Access: Private, Static
481 // Description: Returns a global XFile object that contains the
482 // standard list of Direct3D template definitions that
483 // may be assumed to be at the head of every file.
484 ////////////////////////////////////////////////////////////////////
485 const XFile *XFile::
486 get_standard_templates() {
487  if (_standard_templates == (XFile *)NULL) {
488  // The standardTemplates.x file has been compiled into this
489  // binary. Extract it out.
490 
491  string data((const char *)standard_templates_data, standard_templates_data_len);
492 
493 #ifdef HAVE_ZLIB
494  // The data is stored compressed; decompress it on-the-fly.
495  istringstream inz(data);
496  IDecompressStream in(&inz, false);
497 
498 #else
499  // The data is stored uncompressed, so just load it.
500  istringstream in(data);
501 #endif // HAVE_ZLIB
502 
503  _standard_templates = new XFile;
504  if (!_standard_templates->read(in, "standardTemplates.x")) {
505  xfile_cat.error()
506  << "Internal error: Unable to parse built-in standardTemplates.x!\n";
507  }
508 
509  // Now flag all of these templates as "standard".
510  for (int i = 0; i < _standard_templates->get_num_children(); i++) {
511  XFileNode *child = _standard_templates->get_child(i);
512  if (child->is_of_type(XFileTemplate::get_class_type())) {
513  XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
514  xtemplate->_is_standard = true;
515  }
516  }
517  }
518 
519  return _standard_templates;
520 }
virtual void write_text(ostream &out, int indent_level) const
Writes a suitable representation of this node to an .x file in text mode.
Definition: xFile.cxx:321
virtual void clear()
Removes all of the classes defined within the XFile and prepares it for reading a new file...
Definition: xFile.cxx:60
bool write(Filename filename) const
Opens the indicated filename for output and writes a parseable description of all the known distribut...
Definition: xFile.cxx:146
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
virtual bool matches(const XFileNode *other) const
Returns true if the node, particularly a template node, is structurally equivalent to the other node ...
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:507
This is an implementation of the Windows GUID object, used everywhere as a world-unique identifier fo...
Definition: windowsGuid.h:29
XFileTemplate * find_template(const string &name) const
Returns the template associated with the indicated name, if any, or NULL if none. ...
Definition: xFile.cxx:199
static XFileTemplate * find_standard_template(const string &name)
Returns the standard template associated with the indicated name, if any, or NULL if none...
Definition: xFile.cxx:262
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
This is a node which contains all of the data elements defined by a template.
A single node of an X file.
Definition: xFileNode.h:42
XFileDataNodeTemplate * find_data_object(const string &name) const
Returns the data object associated with the indicated name, if any, or NULL if none.
Definition: xFile.cxx:286
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
bool read(Filename filename)
Opens and reads the indicated .x file by name.
Definition: xFile.cxx:79
istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read...
XFileNode * find_descendent(const string &name) const
Returns the first child or descendent found with the indicated name after a depth-first search...
Definition: xFileNode.cxx:113
virtual void clear()
Removes all children from the node, and otherwise resets it to its initial state. ...
Definition: xFileNode.cxx:250
This represents the complete contents of an X file (file.x) in memory.
Definition: xFile.h:35
XFileNode * find_child(const string &name) const
Returns the child with the indicated name, if any, or NULL if none.
Definition: xFileNode.cxx:61
A template definition in the X file.
Definition: xFileTemplate.h:29
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
string get_extension() const
Returns the file extension.
Definition: filename.I:477
XFileNode * get_child(int n) const
Returns the nth child of this node.
Definition: xFileNode.I:44