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() {
59 XFileNode::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();
75 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
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 */
177 XFileTemplate *XFile::
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 */
206 XFileTemplate *XFile::
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 */
236 XFileTemplate *XFile::
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 */
246 XFileTemplate *XFile::
247 find_standard_template(const WindowsGuid &guid) {
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 */
256 XFileDataNodeTemplate *XFile::
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 */
271 XFileDataNodeTemplate *XFile::
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 }
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Filename::set_binary
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
Filename::set_text
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
Filename::open_write
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
Filename::get_extension
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:866
VirtualFileSystem::open_read_file
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,...
Definition: virtualFileSystem.cxx:846
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
WindowsGuid
This is an implementation of the Windows GUID object, used everywhere as a world-unique identifier fo...
Definition: windowsGuid.h:26
XFileDataNodeTemplate
This is a node which contains all of the data elements defined by a template.
Definition: xFileDataNodeTemplate.h:30
XFileNode
A single node of an X file.
Definition: xFileNode.h:40
XFileNode::find_descendent
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
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
XFileNode::clear
virtual void clear()
Removes all children from the node, and otherwise resets it to its initial state.
Definition: xFileNode.cxx:209
XFileNode::get_child
XFileNode * get_child(int n) const
Returns the nth child of this node.
Definition: xFileNode.I:46
XFileTemplate
A template definition in the X file.
Definition: xFileTemplate.h:27
XFileTemplate::matches
virtual bool matches(const XFileNode *other) const
Returns true if the node, particularly a template node, is structurally equivalent to the other node ...
Definition: xFileTemplate.cxx:121
XFile
This represents the complete contents of an X file (file.x) in memory.
Definition: xFile.h:32
XFile::read
bool read(Filename filename)
Opens and reads the indicated .x file by name.
Definition: xFile.cxx:73
XFile::find_standard_template
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
XFile::find_template
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
XFile::clear
virtual void clear()
Removes all of the classes defined within the XFile and prepares it for reading a new file.
Definition: xFile.cxx:58
XFile::write_text
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
XFile::find_data_object
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
XFile::write
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
config_xfile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
standard_templates.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xFileDataNodeTemplate.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xFileTemplate.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xLexerDefs.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
xParserDefs.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
zStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.