2004-10-13 13:37:06 +00:00

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