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