mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
531 lines
16 KiB
C++
531 lines
16 KiB
C++
// Filename: xFile.cxx
|
|
// Created by: drose (03Oct04)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "xFile.h"
|
|
#include "xParserDefs.h"
|
|
#include "xLexerDefs.h"
|
|
#include "xFileTemplate.h"
|
|
#include "xFileDataNodeTemplate.h"
|
|
#include "config_xfile.h"
|
|
#include "standard_templates.h"
|
|
#include "zStream.h"
|
|
#include "config_express.h"
|
|
#include "virtualFileSystem.h"
|
|
#include "dcast.h"
|
|
|
|
TypeHandle XFile::_type_handle;
|
|
PT(XFile) XFile::_standard_templates;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
XFile::
|
|
XFile() : XFileNode(this, "") {
|
|
_major_version = 3;
|
|
_minor_version = 2;
|
|
_format_type = FT_text;
|
|
_float_size = FS_64;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::Destructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
XFile::
|
|
~XFile() {
|
|
clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::clear
|
|
// Access: Public, Virtual
|
|
// Description: Removes all of the classes defined within the XFile
|
|
// and prepares it for reading a new file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void XFile::
|
|
clear() {
|
|
XFileNode::clear();
|
|
|
|
_nodes_by_guid.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::read
|
|
// Access: Public
|
|
// Description: Opens and reads the indicated .x file by name. The
|
|
// nodes and templates defined in the file will be
|
|
// appended to the set of nodes already recorded, if
|
|
// any.
|
|
//
|
|
// Returns true if the file is successfully read, false
|
|
// if there was an error (in which case the file might
|
|
// have been partially read).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
read(Filename filename) {
|
|
ifstream in;
|
|
|
|
filename.set_text();
|
|
if (use_vfs) {
|
|
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
istream *in = vfs->open_read_file(filename);
|
|
if (in == (istream *)NULL) {
|
|
xfile_cat.error()
|
|
<< "Cannot open " << filename << " for reading.\n";
|
|
return false;
|
|
}
|
|
bool okflag = read(*in, filename);
|
|
delete in;
|
|
return okflag;
|
|
|
|
} else {
|
|
filename.open_read(in);
|
|
|
|
if (!in) {
|
|
xfile_cat.error()
|
|
<< "Cannot open " << filename << " for reading.\n";
|
|
return false;
|
|
}
|
|
|
|
return read(in, filename);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::read
|
|
// Access: Public
|
|
// Description: Parses the already-opened input stream for
|
|
// distributed class descriptions. The filename
|
|
// parameter is optional and is only used when reporting
|
|
// errors.
|
|
//
|
|
// The distributed classes defined in the file will be
|
|
// appended to the set of distributed classes already
|
|
// recorded, if any.
|
|
//
|
|
// Returns true if the file is successfully read, false
|
|
// if there was an error (in which case the file might
|
|
// have been partially read).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
read(istream &in, const string &filename) {
|
|
if (!read_header(in)) {
|
|
return false;
|
|
}
|
|
|
|
if (_format_type != FT_text) {
|
|
// Does anyone actually use the binary format? It wouldn't be too
|
|
// hard to support it if there were any reason at all to do so.
|
|
xfile_cat.error()
|
|
<< "Cannot read binary .x files at this time.\n";
|
|
return false;
|
|
}
|
|
|
|
// We must call this first so the standard templates file will be
|
|
// parsed and available by the time we need it--it's tricky to
|
|
// invoke the parser from within another parser instance.
|
|
get_standard_templates();
|
|
|
|
x_init_parser(in, filename, *this);
|
|
xyyparse();
|
|
x_cleanup_parser();
|
|
|
|
return (x_error_count() == 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::write
|
|
// Access: Public
|
|
// Description: Opens the indicated filename for output and writes a
|
|
// parseable description of all the known distributed
|
|
// classes to the file.
|
|
//
|
|
// Returns true if the description is successfully
|
|
// written, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
write(Filename filename) const {
|
|
ofstream out;
|
|
|
|
// We actually open the file to write in binary mode, to avoid the
|
|
// MS-DOS newline characters (since Windows seems to do this too).
|
|
filename.set_binary();
|
|
filename.open_write(out);
|
|
|
|
if (!out) {
|
|
xfile_cat.error()
|
|
<< "Can't open " << filename << " for output.\n";
|
|
return false;
|
|
}
|
|
|
|
return write(out);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::write
|
|
// Access: Public
|
|
// Description: Writes a parseable description of all the known
|
|
// nodes and templates to the stream.
|
|
//
|
|
// Returns true if the description is successfully
|
|
// written, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
write(ostream &out) const {
|
|
if (!write_header(out)) {
|
|
return false;
|
|
}
|
|
|
|
write_text(out, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_template
|
|
// Access: Public
|
|
// Description: Returns the template associated with the indicated
|
|
// name, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileTemplate *XFile::
|
|
find_template(const string &name) const {
|
|
XFileTemplate *standard = (XFileTemplate *)NULL;
|
|
const XFile *standard_templates = get_standard_templates();
|
|
if (standard_templates != this) {
|
|
standard = standard_templates->find_template(name);
|
|
}
|
|
|
|
XFileNode *child = find_child(name);
|
|
if (child != (XFileNode *)NULL &&
|
|
child->is_of_type(XFileTemplate::get_class_type())) {
|
|
XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
|
|
if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
|
|
// If the template matches a standard template, return the
|
|
// standard instead. The assumption is that code may expect a
|
|
// certain naming scheme for the data elements of the standard
|
|
// template, so we want to be sure to provide it.
|
|
return standard;
|
|
}
|
|
return xtemplate;
|
|
}
|
|
|
|
return standard;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_template
|
|
// Access: Public
|
|
// Description: Returns the template associated with the indicated
|
|
// GUID, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileTemplate *XFile::
|
|
find_template(const WindowsGuid &guid) const {
|
|
XFileTemplate *standard = (XFileTemplate *)NULL;
|
|
const XFile *standard_templates = get_standard_templates();
|
|
if (standard_templates != this) {
|
|
standard = standard_templates->find_template(guid);
|
|
}
|
|
|
|
NodesByGuid::const_iterator gi;
|
|
gi = _nodes_by_guid.find(guid);
|
|
if (gi != _nodes_by_guid.end() &&
|
|
(*gi).second->is_of_type(XFileTemplate::get_class_type())) {
|
|
XFileTemplate *xtemplate = DCAST(XFileTemplate, (*gi).second);
|
|
if (standard != (XFileTemplate *)NULL && xtemplate->matches(standard)) {
|
|
// If the template matches a standard template, return the
|
|
// standard instead. The assumption is that code may expect a
|
|
// certain naming scheme for the data elements of the standard
|
|
// template, so we want to be sure to provide it.
|
|
return standard;
|
|
}
|
|
return xtemplate;
|
|
}
|
|
|
|
return standard;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_standard_template
|
|
// Access: Public, Static
|
|
// Description: Returns the standard template associated with the
|
|
// indicated name, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileTemplate *XFile::
|
|
find_standard_template(const string &name) {
|
|
const XFile *standard_templates = get_standard_templates();
|
|
return standard_templates->find_template(name);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_standard_template
|
|
// Access: Public, Static
|
|
// Description: Returns the template associated with the indicated
|
|
// GUID, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileTemplate *XFile::
|
|
find_standard_template(const WindowsGuid &guid) {
|
|
const XFile *standard_templates = get_standard_templates();
|
|
return standard_templates->find_template(guid);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_data_object
|
|
// Access: Public
|
|
// Description: Returns the data object associated with the indicated
|
|
// name, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileDataNodeTemplate *XFile::
|
|
find_data_object(const string &name) const {
|
|
XFileNode *child = find_descendent(name);
|
|
if (child != (XFileNode *)NULL &&
|
|
child->is_of_type(XFileDataNodeTemplate::get_class_type())) {
|
|
return DCAST(XFileDataNodeTemplate, child);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::find_data_object
|
|
// Access: Public
|
|
// Description: Returns the data object associated with the indicated
|
|
// GUID, if any, or NULL if none.
|
|
////////////////////////////////////////////////////////////////////
|
|
XFileDataNodeTemplate *XFile::
|
|
find_data_object(const WindowsGuid &guid) const {
|
|
NodesByGuid::const_iterator gi;
|
|
gi = _nodes_by_guid.find(guid);
|
|
if (gi != _nodes_by_guid.end() &&
|
|
(*gi).second->is_of_type(XFileDataNodeTemplate::get_class_type())) {
|
|
return DCAST(XFileDataNodeTemplate, (*gi).second);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::write_text
|
|
// Access: Public, Virtual
|
|
// Description: Writes a suitable representation of this node to an
|
|
// .x file in text mode.
|
|
////////////////////////////////////////////////////////////////////
|
|
void XFile::
|
|
write_text(ostream &out, int indent_level) const {
|
|
Children::const_iterator ci;
|
|
for (ci = _children.begin(); ci != _children.end(); ++ci) {
|
|
(*ci)->write_text(out, indent_level);
|
|
out << "\n";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::read_header
|
|
// Access: Private
|
|
// Description: Reads the header and magic number associated with the
|
|
// file. Returns true on success, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
read_header(istream &in) {
|
|
char magic[4];
|
|
if (!in.read(magic, 4)) {
|
|
xfile_cat.error()
|
|
<< "Empty file.\n";
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(magic, "xof ", 4) != 0) {
|
|
xfile_cat.error()
|
|
<< "Not a DirectX file.\n";
|
|
return false;
|
|
}
|
|
|
|
char version[4];
|
|
if (!in.read(version, 4)) {
|
|
xfile_cat.error()
|
|
<< "Truncated file.\n";
|
|
return false;
|
|
}
|
|
_major_version = (version[0] - '0') * 10 + (version[1] - '0');
|
|
_minor_version = (version[2] - '0') * 10 + (version[3] - '0');
|
|
|
|
char format[4];
|
|
if (!in.read(format, 4)) {
|
|
xfile_cat.error()
|
|
<< "Truncated file.\n";
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(format, "txt ", 4) == 0) {
|
|
_format_type = FT_text;
|
|
|
|
} else if (memcmp(format, "bin ", 4) == 0) {
|
|
_format_type = FT_binary;
|
|
|
|
} else if (memcmp(format, "com ", 4) == 0) {
|
|
_format_type = FT_compressed;
|
|
|
|
} else {
|
|
xfile_cat.error()
|
|
<< "Unknown format type: " << string(format, 4) << "\n";
|
|
return false;
|
|
}
|
|
|
|
if (_format_type == FT_compressed) {
|
|
// Read and ignore the compression type, since we don't support
|
|
// compression anyway.
|
|
char compression_type[4];
|
|
in.read(compression_type, 4);
|
|
}
|
|
|
|
char float_size[4];
|
|
if (!in.read(float_size, 4)) {
|
|
xfile_cat.error()
|
|
<< "Truncated file.\n";
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(float_size, "0032", 4) == 0) {
|
|
_float_size = FS_32;
|
|
|
|
} else if (memcmp(float_size, "0064", 4) == 0) {
|
|
_float_size = FS_64;
|
|
|
|
} else {
|
|
xfile_cat.error()
|
|
<< "Unknown float size: " << string(float_size, 4) << "\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::write_header
|
|
// Access: Private
|
|
// Description: Writes the header and magic number associated with the
|
|
// file. Returns true on success, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool XFile::
|
|
write_header(ostream &out) const {
|
|
out.write("xof ", 4);
|
|
|
|
char buffer[128];
|
|
sprintf(buffer, "%02d%02d", _major_version, _minor_version);
|
|
if (strlen(buffer) != 4) {
|
|
xfile_cat.error()
|
|
<< "Invalid version: " << _major_version << "." << _minor_version
|
|
<< "\n";
|
|
return false;
|
|
}
|
|
|
|
out.write(buffer, 4);
|
|
|
|
switch (_format_type) {
|
|
case FT_text:
|
|
out.write("txt ", 4);
|
|
break;
|
|
|
|
case FT_binary:
|
|
out.write("bin ", 4);
|
|
break;
|
|
|
|
case FT_compressed:
|
|
out.write("cmp ", 4);
|
|
break;
|
|
|
|
default:
|
|
xfile_cat.error()
|
|
<< "Invalid format type: " << _format_type << "\n";
|
|
return false;
|
|
}
|
|
|
|
if (_format_type == FT_compressed) {
|
|
// Write a bogus compression type, just so we have a valid header.
|
|
out.write("xxx ", 4);
|
|
}
|
|
|
|
switch (_float_size) {
|
|
case FS_32:
|
|
out.write("0032", 4);
|
|
break;
|
|
|
|
case FS_64:
|
|
out.write("0064", 4);
|
|
break;
|
|
|
|
default:
|
|
xfile_cat.error()
|
|
<< "Invalid float size: " << _float_size << "\n";
|
|
return false;
|
|
}
|
|
|
|
if (_format_type == FT_text) {
|
|
// If it's a text format, we can now write a newline.
|
|
out << "\n";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: XFile::get_standard_templates
|
|
// Access: Private, Static
|
|
// Description: Returns a global XFile object that contains the
|
|
// standard list of Direct3D template definitions that
|
|
// may be assumed to be at the head of every file.
|
|
////////////////////////////////////////////////////////////////////
|
|
const XFile *XFile::
|
|
get_standard_templates() {
|
|
if (_standard_templates == (XFile *)NULL) {
|
|
// The standardTemplates.x file has been compiled into this
|
|
// binary. Extract it out.
|
|
|
|
string data((const char *)standard_templates_data, standard_templates_data_len);
|
|
|
|
#ifdef HAVE_ZLIB
|
|
// The data is stored compressed; decompress it on-the-fly.
|
|
istringstream inz(data);
|
|
IDecompressStream in(&inz, false);
|
|
|
|
#else
|
|
// The data is stored uncompressed, so just load it.
|
|
istringstream in(data);
|
|
#endif // HAVE_ZLIB
|
|
|
|
_standard_templates = new XFile;
|
|
if (!_standard_templates->read(in, "standardTemplates.x")) {
|
|
xfile_cat.error()
|
|
<< "Internal error: Unable to parse built-in standardTemplates.x!\n";
|
|
}
|
|
|
|
// Now flag all of these templates as "standard".
|
|
for (int i = 0; i < _standard_templates->get_num_children(); i++) {
|
|
XFileNode *child = _standard_templates->get_child(i);
|
|
if (child->is_of_type(XFileTemplate::get_class_type())) {
|
|
XFileTemplate *xtemplate = DCAST(XFileTemplate, child);
|
|
xtemplate->_is_standard = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return _standard_templates;
|
|
}
|