1
0
forked from 0ad/0ad
This was SVN commit r665.
This commit is contained in:
Ykkrosh 2004-07-08 15:22:09 +00:00
parent 92f14cd90a
commit a8f48ff7e0
13 changed files with 1224 additions and 290 deletions

View File

@ -1,5 +1,4 @@
#include "precompiled.h"
#undef new // if it was redefined for leak detection, since xerces doesn't like it
#include "ObjectEntry.h"
#include "ObjectManager.h"
@ -9,12 +8,7 @@
#include "UnitManager.h"
#include "XML.h"
// automatically use namespace ..
XERCES_CPP_NAMESPACE_USE
#include "ps/Xeromyces.h"
CObjectEntry::CObjectEntry(int type) : m_Model(0), m_Type(type)
{
@ -36,6 +30,7 @@ CObjectEntry::~CObjectEntry()
bool CObjectEntry::BuildModel()
{
// check we've enough data to consider building the object
if (m_ModelName.Length()==0 || m_TextureName.Length()==0) {
return false;
@ -149,138 +144,98 @@ CSkeletonAnim* CObjectEntry::GetNamedAnimation( CStr animationName )
bool CObjectEntry::Load(const char* filename)
{
bool parseOK = false;
// Initialize XML library
CXeromyces XeroFile;
try
{
XMLCh* attachpointtext=XMLString::transcode("attachpoint");
XMLCh* modeltext=XMLString::transcode("model");
XMLCh* nametext=XMLString::transcode("name");
XMLCh* filetext=XMLString::transcode("file");
XMLCh* speedtext=XMLString::transcode("speed");
XeroFile.Load(filename);
}
catch (...)
{
return false;
}
// Create parser instance
XercesDOMParser *parser = new XercesDOMParser();
// Define all the elements and attributes used in the XML file
#define EL(x) int el_##x = XeroFile.getElementID(L#x)
#define AT(x) int at_##x = XeroFile.getAttributeID(L#x)
EL(name);
EL(modelname);
EL(texturename);
EL(animations);
EL(props);
AT(attachpoint);
AT(model);
AT(name);
AT(file);
AT(speed);
#undef AT
#undef EL
// Setup parser
parser->setValidationScheme(XercesDOMParser::Val_Auto);
parser->setDoNamespaces(false);
parser->setDoSchema(false);
parser->setCreateEntityReferenceNodes(false);
XMBElement root = XeroFile.getRoot();
// Set customized error handler
CXercesErrorHandler *errorHandler = new CXercesErrorHandler();
parser->setErrorHandler(errorHandler);
XMBElementList children = root.getChildNodes();
CVFSEntityResolver *entityResolver = new CVFSEntityResolver(filename);
parser->setEntityResolver(entityResolver);
for (int i = 0; i < children.Count; ++i) {
// Get node
XMBElement child = children.item(i);
// Push the CLogger to mark it's reading this file.
int element_name = child.getNodeName();
CStr element_value=tocstr(child.getText());
// Get main node
CVFSInputSource source;
parseOK = source.OpenFile(filename) == 0;
if (parseOK)
if (element_name == el_name)
m_Name=element_value;
else if (element_name == el_modelname)
m_ModelName=element_value;
else if (element_name == el_texturename)
m_TextureName=element_value;
else if (element_name == el_animations)
{
// Parse file
parser->parse(source);
XMBElementList animations=child.getChildNodes();
// Check how many errors
parseOK = parser->getErrorCount() == 0;
}
if (parseOK) {
// parsed successfully - grab our data
DOMDocument *doc = parser->getDocument();
DOMElement *element = doc->getDocumentElement();
for (int j = 0; j < animations.Count; ++j) {
XMBElement anim_element = animations.item(j);
XMBAttributeList attributes=anim_element.getAttributes();
if (attributes.Count) {
Anim anim;
// root_name should be Object
CStr root_name = XMLTranscode( element->getNodeName() );
anim.m_AnimName=tocstr(attributes.getNamedItem(at_name));
anim.m_FileName=tocstr(attributes.getNamedItem(at_file));
CStr16 speedstr=attributes.getNamedItem(at_speed);
// should have at least 3 children - Name, ModelName and TextureName
DOMNodeList *children = element->getChildNodes();
int numChildren=children->getLength();
for (int i=0; i<numChildren; ++i) {
// Get node
DOMNode *child = children->item(i);
anim.m_Speed=float(speedstr.ToInt())/100.0f;
if (anim.m_Speed<=0.0) anim.m_Speed=1.0f;
// A child element
if (child->getNodeType() == DOMNode::ELEMENT_NODE)
{
// First get element and not node
DOMElement *child_element = (DOMElement*)child;
CStr element_name = XMLTranscode( child_element->getNodeName() );
DOMNode *value_node= child_element->getChildNodes()->item(0);
CStr element_value=value_node ? XMLTranscode(value_node->getNodeValue()) : "";
if (element_name==CStr("Name")) {
m_Name=element_value;
} else if (element_name==CStr("ModelName")) {
m_ModelName=element_value;
} else if (element_name==CStr("TextureName")) {
m_TextureName=element_value;
} else if (element_name==CStr("Animations")) {
DOMNodeList* animations=(DOMNodeList*) child_element->getChildNodes();
for (uint j=0; j<animations->getLength(); ++j) {
DOMElement *anim_element = (DOMElement*) animations->item(j);
CStr element_name = XMLTranscode( anim_element->getNodeName() );
DOMNamedNodeMap* attributes=anim_element->getAttributes();
if (attributes) {
Anim anim;
DOMNode *nameattr=attributes->getNamedItem(nametext);
anim.m_AnimName=XMLTranscode(nameattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *fileattr=attributes->getNamedItem(filetext);
anim.m_FileName=XMLTranscode(fileattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *speedattr=attributes->getNamedItem(speedtext);
CStr speedstr=XMLTranscode(speedattr->getChildNodes()->item(0)->getNodeValue());
anim.m_Speed=float(atoi((const char*) speedstr))/100.0f;
if (anim.m_Speed<=0) anim.m_Speed=1.0f;
m_Animations.push_back(anim);
}
}
} else if (element_name==CStr("Props")) {
DOMNodeList* props=(DOMNodeList*) child_element->getChildNodes();
for (uint j=0; j<props->getLength(); ++j) {
DOMElement *prop_element = (DOMElement*) props->item(j);
CStr element_name = XMLTranscode( prop_element->getNodeName() );
DOMNamedNodeMap* attributes=prop_element->getAttributes();
if (attributes) {
Prop prop;
DOMNode *nameattr=attributes->getNamedItem(attachpointtext);
prop.m_PropPointName=XMLTranscode(nameattr->getChildNodes()->item(0)->getNodeValue());
DOMNode *modelattr=attributes->getNamedItem(modeltext);
prop.m_ModelName=XMLTranscode(modelattr->getChildNodes()->item(0)->getNodeValue());
m_Props.push_back(prop);
}
}
}
m_Animations.push_back(anim);
}
}
}
else if (element_name == el_props)
{
XMBElementList props=child.getChildNodes();
XMLString::release(&attachpointtext);
XMLString::release(&modeltext);
XMLString::release(&nametext);
XMLString::release(&filetext);
XMLString::release(&speedtext);
for (int j = 0; j < props.Count; ++j) {
XMBElement prop_element = props.item(j);
XMBAttributeList attributes=prop_element.getAttributes();
if (attributes.Count) {
Prop prop;
delete parser;
delete errorHandler;
delete entityResolver;
prop.m_PropPointName=tocstr(attributes.getNamedItem(at_attachpoint));
prop.m_ModelName=tocstr(attributes.getNamedItem(at_model));
m_Props.push_back(prop);
}
}
}
}
return parseOK;
return true;
}
bool CObjectEntry::Save(const char* filename)
{
FILE* fp=fopen(filename,"w");

49
source/lib/crc32.cpp Executable file
View File

@ -0,0 +1,49 @@
// $Id: crc32.cpp,v 1.1 2004/07/08 15:21:21 philip Exp $
#include "precompiled.h"
// CRC32, based on code copied from anywhere on the internet.
// Find a thousand possible sources at
// http://www.google.com/search?q=0xEDB88320L
unsigned long crc_table[256];
int generate_table()
{
unsigned long crc;
int i, j;
const unsigned long poly = 0xEDB88320L; // magic
for (i = 0; i < 256; ++i)
{
crc = i;
for (j = 0; j < 8; ++j)
{
if (crc & 1)
crc = (crc >> 1) ^ poly;
else
crc >>= 1;
}
crc_table[i] = crc;
}
return 0;
}
unsigned long crc32_calculate(char* data, int len)
{
// Only calculate the table once
static int _temp = generate_table();
unsigned long crc;
crc = ~0;
while (len)
{
crc = (crc>>8) ^ crc_table[ (crc^*data) & 0xff ];
--len; ++data;
}
return ~crc;
}

4
source/lib/crc32.h Executable file
View File

@ -0,0 +1,4 @@
// $Id: crc32.h,v 1.1 2004/07/08 15:21:21 philip Exp $
// I don't think this really needs any documentation
unsigned long crc32_calculate(char* data, int len);

View File

@ -37,22 +37,6 @@
#include <algorithm>
#include <numeric>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/sax/InputSource.hpp>
#include <xercesc/sax/EntityResolver.hpp>
#include <xercesc/util/BinMemInputStream.hpp>
#include <xercesc/sax/SAXParseException.hpp>
#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/sax/ErrorHandler.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
// Nicer memory leak reporting in MSVC:
// (You've got to include all STL headers first to avoid lots of errors,
// so make sure they're in the list above and you compile with PCH)

View File

@ -38,17 +38,16 @@
#include "EntityHandles.h"
#include "EntityManager.h"
#include "PathfindEngine.h"
#include "XML.h"
#include "scripting/JSInterface_Entity.h"
#include "scripting/JSInterface_BaseEntity.h"
#include "scripting/JSInterface_Vector3D.h"
#include "gui/scripting/JSInterface_IGUIObject.h"
#include "gui/scripting/JSInterface_GUITypes.h"
#include "ConfigDB.h"
#include "CLogger.h"
#ifndef NO_GUI
#include "gui/GUI.h"
#endif
@ -548,14 +547,8 @@ void ParseArgs(int argc, char* argv[])
static void psInit()
{
// start up Xerces - only needs to be done once (unless locale changes mid-game, for
// some reason), not on every XML file load; multiple initialization calls are ok, though,
// provided there are a matching number of XMLPlatformUtils::Terminate calls
XMLPlatformUtils::Initialize();
g_Font_Console = unifont_load("fonts/console");
g_Font_Misc = unifont_load("fonts/verdana16");
@ -566,9 +559,11 @@ static void psInit()
#ifndef NO_GUI
// GUI uses VFS, so this must come after VFS init.
g_GUI.Initialize();
g_GUI.LoadXMLFile("gui/styles.xml");
g_GUI.LoadXMLFile("gui/hello.xml");
g_GUI.LoadXMLFile("gui/sprite1.xml");
g_GUI.LoadXMLFile("gui/test/styles.xml");
g_GUI.LoadXMLFile("gui/test/hello.xml");
g_GUI.LoadXMLFile("gui/test/sprite1.xml");
#endif
}
@ -582,15 +577,14 @@ static void psShutdown()
delete g_Console;
// close down Xerces
XMLPlatformUtils::Terminate();
// close down Xerces if it was loaded
CXeromyces::Terminate();
}
extern u64 PREVTSC;
int main(int argc, char* argv[])
{
#ifdef _MSC_VER
u64 TSC=rdtsc();
debug_out(
@ -600,6 +594,7 @@ debug_out(
PREVTSC=TSC;
#endif
const int ERR_MSG_SIZE = 1000;
wchar_t err_msg[ERR_MSG_SIZE];
@ -613,6 +608,13 @@ PREVTSC=TSC;
// Create the scripting host. This needs to be done before the GUI is created.
new ScriptingHost;
// Register the JavaScript interfaces with the runtime
JSI_Entity::init();
JSI_BaseEntity::init();
JSI_IGUIObject::init();
JSI_GUITypes::init();
JSI_Vector3D::init();
detect();
// init SDL
@ -729,11 +731,6 @@ PREVTSC=CURTSC;
g_EntityTemplateCollection.loadTemplates();
// Register the JavaScript interfaces with the runtime
JSI_Entity::init();
// JSI_BaseEntity::init(); // janwas: commented this out to avoid crash in JS_DestroyContext
JSI_Vector3D::init();
// if no map name specified, load test01.pmp (for convenience during
// development. that means loading no map at all is currently impossible.
// is that a problem?
@ -833,7 +830,6 @@ PREVTSC=CURTSC;
// ugly, but necessary. these are one-shot events, have to be reset.
mouseButtons[SDL_BUTTON_WHEELUP] = false;
mouseButtons[SDL_BUTTON_WHEELDOWN] = false;
in_get_events();
float TimeSinceLastFrame = (float)(time1-time0);
@ -862,6 +858,8 @@ PREVTSC=CURTSC;
if (g_FixedFrameTiming && frameCount==100) quit=true;
} // main loop, while(!quit)
psShutdown(); // Must delete g_GUI before g_ScriptingHost
delete &g_ScriptingHost;
delete &g_Pathfinder;
delete &g_EntityManager;
@ -880,8 +878,6 @@ PREVTSC=CURTSC;
delete &g_ConfigDB;
psShutdown();
exit(0);
return 0;
}

View File

@ -22,8 +22,11 @@
#ifndef _XercesVFS_H
#define _XercesVFS_H
#include <xercesc/dom/DOM.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
// Temporarily undefine new, because the Xerces headers don't like it
#ifdef HAVE_DEBUGALLOC
# undef new
#endif
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
@ -32,12 +35,13 @@
#include <xercesc/util/BinMemInputStream.hpp>
#include <xercesc/sax/SAXParseException.hpp>
#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/sax/ErrorHandler.hpp>
#include <xercesc/framework/LocalFileInputSource.hpp>
#ifdef HAVE_DEBUGALLOC
# define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
#include "lib/crc32.h"
#include "res/h_mgr.h"
#include "lib.h"
@ -73,6 +77,9 @@ public:
// returns 0 if successful, -1 on failure
int OpenFile(const char *path);
// Calculate the CRC32 checksum of the file's contents
unsigned long CRC32() { return crc32_calculate((char*)m_pBuffer, (int)m_BufferSize); }
virtual BinInputStream *makeStream() const;
};

View File

@ -1,7 +1,9 @@
#include "precompiled.h"
#undef new // if it was redefined for leak detection, since xerces doesn't like it
#include "XML.h"
#undef new // if it was redefined for leak detection, since xerces doesn't like it
#include "CStr.h"
#include "CLogger.h"
#include "posix.h" // ptrdiff_t

View File

@ -22,10 +22,6 @@ gee@pyro.nu
#include "XML.h"
//#include <xercesc/util/XercesDefs.hpp>
//#include <xercesc/sax/ErrorHandler.hpp>
#include <iostream>
/**

250
source/ps/XeroXMB.cpp Executable file
View File

@ -0,0 +1,250 @@
// $Id: XeroXMB.cpp,v 1.1 2004/07/08 15:21:42 philip Exp $
#include "precompiled.h"
#include "Xeromyces.h"
#include <assert.h>
const int HeaderMagic = 0x30424D58; // = "XMB0" (little-endian)
const char* HeaderMagicStr = "XMB0";
// Warning: May contain traces of pointer abuse
void XMBFile::Initialise(char* FileData)
{
m_Pointer = FileData;
int Header = *(int*)m_Pointer; m_Pointer += 4;
assert(Header == HeaderMagic && "Invalid XMB header!");
int Checksum = *(int*)m_Pointer; m_Pointer += 4;
int i;
#ifdef XERO_USEMAP
// Build a std::map of all the names->ids
int ElementNameCount = *(int*)m_Pointer; m_Pointer += 4;
for (i = 0; i < ElementNameCount; ++i)
m_ElementNames[ReadZStr()] = i;
int AttributeNameCount = *(int*)m_Pointer; m_Pointer += 4;
for (i = 0; i < AttributeNameCount; ++i)
m_AttributeNames[ReadZStr()] = i;
#else
// Ignore all the names for now, and skip over them
// (remembering the position of the first)
m_ElementNameCount = *(int*)m_Pointer; m_Pointer += 4;
m_ElementPointer = m_Pointer;
for (i = 0; i < m_ElementNameCount; ++i)
m_Pointer += 4 + *(int*)m_Pointer; // skip over the string
m_AttributeNameCount = *(int*)m_Pointer; m_Pointer += 4;
m_AttributePointer = m_Pointer;
for (i = 0; i < m_AttributeNameCount; ++i)
m_Pointer += 4 + *(int*)m_Pointer; // skip over the string
#endif
}
std::wstring XMBFile::ReadZStr()
{
int Length = *(int*)m_Pointer;
m_Pointer += 4;
std::wstring String ((wchar_t*)m_Pointer);
m_Pointer += Length;
return String;
}
XMBElement XMBFile::getRoot()
{
return XMBElement(m_Pointer);
}
#ifdef XERO_USEMAP
int XMBFile::getElementID(const wchar_t* Name)
{
return m_ElementNames[Name];
}
int XMBFile::getAttributeID(const wchar_t* Name)
{
return m_AttributeNames[Name];
}
#else // #ifdef XERO_USEMAP
int XMBFile::getElementID(const wchar_t* Name)
{
char* Pos = m_ElementPointer;
int len = ((int)wcslen(Name)+1)<<1; // count bytes, including null terminator
// Loop through each string to find a match
for (int i = 0; i < m_ElementNameCount; ++i)
{
// See if this could be the right string, checking its
// length and then its contents
if (*(int*)Pos == len && memcmp((wchar_t*)(Pos+4), Name, len) == 0)
return i;
// If not, jump to the next string
Pos += 4 + *(int*)Pos;
}
// Failed
return -1;
}
int XMBFile::getAttributeID(const wchar_t* Name)
{
char* Pos = m_AttributePointer;
int len = ((int)wcslen(Name)+1)<<1; // count bytes, including null terminator
// Loop through each string to find a match
for (int i = 0; i < m_AttributeNameCount; ++i)
{
// See if this could be the right string, checking its
// length and then its contents
if (*(int*)Pos == len && memcmp((wchar_t*)(Pos+4), Name, len) == 0)
return i;
// If not, jump to the next string
Pos += 4 + *(int*)Pos;
}
// Failed
return -1;
}
#endif // #ifdef XERO_USEMAP / #else
// Relatively inefficient, so only use when
// laziness overcomes the need for speed
std::wstring XMBFile::getElementString(const int ID)
{
char* Pos = m_ElementPointer;
for (int i = 0; i < ID; ++i)
Pos += 4 + *(int*)Pos;
return std::wstring((wchar_t*)(Pos+4));
}
std::wstring XMBFile::getAttributeString(const int ID)
{
char* Pos = m_AttributePointer;
for (int i = 0; i < ID; ++i)
Pos += 4 + *(int*)Pos;
return std::wstring((wchar_t*)(Pos+4));
}
int XMBElement::getNodeName()
{
return *(int*)(m_Pointer + 4); // == ElementName
}
XMBElementList XMBElement::getChildNodes()
{
return XMBElementList(
m_Pointer + 20 + *(int*)(m_Pointer + 16), // == Children[]
*(int*)(m_Pointer + 12) // == ChildCount
);
}
XMBAttributeList XMBElement::getAttributes()
{
return XMBAttributeList(
m_Pointer + 24 + *(int*)(m_Pointer + 20), // == Attributes[]
*(int*)(m_Pointer + 8) // == AttributeCount
);
}
std::wstring XMBElement::getText()
{
return std::wstring((wchar_t*)(m_Pointer + 24)); // == Text
}
XMBElement XMBElementList::item(const int id)
{
assert(id >= 0 && id < Count && "Element ID out of range");
char* Pos;
// If access is sequential, don't bother scanning
// through all the nodes to find the next one
if (id == m_LastItemID+1)
{
Pos = m_LastPointer;
Pos += *(int*)Pos; // skip over the last node
}
else
{
Pos = m_Pointer;
// Skip over each preceding node
for (int i=0; i<id; ++i)
Pos += *(int*)Pos;
}
// Cache information about this node
m_LastItemID = id;
m_LastPointer = Pos;
return XMBElement(Pos);
}
std::wstring XMBAttributeList::getNamedItem(const int AttributeName)
{
char* Pos = m_Pointer;
// Maybe not the cleverest algorithm, but it should be
// fast enough with half a dozen attributes:
for (int i = 0; i < Count; ++i)
{
if (*(int*)Pos == AttributeName)
return std::wstring((wchar_t*)(Pos+8));
Pos += 8 + *(int*)(Pos+4); // Skip over the string
}
// Can't find attribute
return std::wstring(L"");
}
XMBAttribute XMBAttributeList::item(const int id)
{
assert(id >= 0 && id < Count && "Attribute ID out of range");
char* Pos;
// If access is sequential, don't bother scanning through
// all the nodes to find the right one
if (id == m_LastItemID+1)
{
Pos = m_LastPointer;
// Skip over the last attribute
Pos += 8 + *(int*)(Pos+4);
}
else
{
Pos = m_Pointer;
// Skip over each preceding attribute
for (int i=0; i<id; ++i)
Pos += 8 + *(int*)(Pos+4); // skip ID, length, and string data
}
// Cache information about this attribute
m_LastItemID = id;
m_LastPointer = Pos;
return XMBAttribute(*(int*)Pos, std::wstring((wchar_t*)(Pos+8)));
}
// Temporary hackiness. (Could CStr do automatic conversions?)
CStr tocstr(std::wstring s)
{
size_t len = s.size();
char* s2 = new char[len+1];
const wchar_t* s1 = s.c_str();
for (size_t i=0; i<=len; ++i)
s2[i] = (char)s1[i];
CStr r(s2);
delete s2;
return r;
}

213
source/ps/XeroXMB.h Executable file
View File

@ -0,0 +1,213 @@
/* $Id: XeroXMB.h,v 1.1 2004/07/08 15:21:42 philip Exp $
Xeromyces - XMB reading library
Philip Taylor (philip@zaynar.demon.co.uk / @wildfiregames.com)
*/
/*
Brief outline:
XMB is a binary representation of XML, with some limitations
but much more efficiency (particularly for loading simple data
classes that don't need much initialisation).
Main limitations:
* Only handles UTF16 internally. (It's meant to be a feature, but
can be detrimental if it's always being converted back to
ASCII.)
* Can't correctly handle mixed text/elements inside elements -
"<div> <b> Text </b> </div>" and "<div> Te<b/>xt </div>" are
considered identical.
* Tries to avoid using strings - you usually have to load the
numeric IDs and use them instead.
* Case-sensitive (but converts all element/attribute names in
the XML file to lowercase, so you only have to be careful in
the code)
Theoretical file structure:
XMB_File {
char Header[4]; // because everyone has one; currently "XMB0"
int Checksum; // CRC32 of original XML file, to detect changes
int ElementNameCount;
XMB_ZStr ElementNames[];
int AttributeNameCount;
ZStr AttributeNames[];
XMB_Node Root;
}
XMB_Node {
0) int Length; // of entire struct, so it can be skipped over
4) int ElementName;
8) int AttributeCount;
12) int ChildCount;
16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes)
20) ZStr Text;
XMB_Attribute Attributes[];
XMB_Node Children[];
}
XMB_Attribute {
int Name;
XMB_ZStr Value;
}
XMB_ZStr {
int Length; // in bytes (always a multiple of 2)
wchar_t* Text; // null-terminated, UTF16-LE
}
*/
#ifndef _XEROXMB_H_
#define _XEROXMB_H_
// Define to use a std::map for name lookups rather than a linear search.
// (The map is usually slower.)
//#define XERO_USEMAP
#include <string>
#ifdef XERO_USEMAP
# include <map>
#endif
// File headers, to make sure it doesn't try loading anything other than an XMB
extern const int HeaderMagic;
extern const char* HeaderMagicStr;
class XMBElement;
class XMBElementList;
class XMBAttributeList;
class XMBFile
{
public:
XMBFile() : m_Pointer(NULL) {};
// Initialise from the contents of an XMB file.
// FileData must remain allocated and unchanged while
// the XMBFile is being used.
void Initialise(char* FileData);
// Returns the root element
XMBElement getRoot();
// Returns internal ID for a given element/attribute string.
// Use getElementID(L"name") to get a wchar_t*
int getElementID(const wchar_t* Name);
int getAttributeID(const wchar_t* Name);
// For lazy people (e.g. me) when speed isn't vital:
// Returns element/attribute string for a given internal ID
std::wstring getElementString(const int ID);
std::wstring getAttributeString(const int ID);
private:
char* m_Pointer;
#ifdef XERO_USEMAP
std::map<std::wstring, int> m_ElementNames;
std::map<std::wstring, int> m_AttributeNames;
#else
int m_ElementNameCount;
int m_AttributeNameCount;
char* m_ElementPointer;
char* m_AttributePointer;
#endif
std::wstring ReadZStr();
};
class XMBElement
{
public:
XMBElement(char* offset)
: m_Pointer(offset) {}
int getNodeName(); // == ElementName
XMBElementList getChildNodes();
XMBAttributeList getAttributes();
std::wstring getText();
private:
// Pointer to the start of the node
char* m_Pointer;
};
class XMBElementList
{
public:
XMBElementList(char* offset, int count)
: Count(count),
m_Pointer(offset),
m_LastItemID(-2) {} // use -2 because it isn't x-1 where x is a non-negative integer
XMBElement item(const int id); // returns Children[id]
int Count;
private:
char* m_Pointer;
// For optimised sequential access:
int m_LastItemID;
char* m_LastPointer;
};
struct XMBAttribute
{
XMBAttribute(int name, std::wstring value)
: Name(name), Value(value) {};
int Name;
std::wstring Value;
};
class XMBAttributeList
{
public:
XMBAttributeList(char* offset, int count)
: Count(count), m_Pointer(offset) {};
// Get the attribute value directly (unlike Xerces)
std::wstring getNamedItem(const int AttributeName);
// Returns an attribute by position in the list
XMBAttribute item(const int id);
int Count;
private:
// Pointer to start of attribute list
char* m_Pointer;
// For optimised sequential access:
int m_LastItemID;
char* m_LastPointer;
};
#include "ps/CStr.h"
CStr tocstr(std::wstring s);
#endif // _XEROXMB_H_

463
source/ps/Xeromyces.cpp Executable file
View File

@ -0,0 +1,463 @@
// $Id: Xeromyces.cpp,v 1.1 2004/07/08 15:21:42 philip Exp $
#include "precompiled.h"
#include <vector>
#include <set>
#include <map>
#include <stack>
#include "ps/Xeromyces.h"
#include "ps/CLogger.h"
#include "lib/res/file.h"
// Because I (and Xerces) don't like these being redefined by wposix.h:
#ifdef HAVE_PCH
# undef read
# undef write
#endif
#include "XML.h"
// For Xerces headers:
#ifdef HAVE_DEBUGALLOC
# undef new
#endif
// The converter uses SAX2, so it should [theoretically]
// be fairly easy to swap Xerces for something else (if desired)
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
// Reenable better memory-leak messages
#ifdef HAVE_DEBUGALLOC
# define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int CXeromyces::XercesLoaded = 0; // for once-only initialisation
// Slightly nasty fwrite/fseek/ftell style thing
class membuffer
{
public:
membuffer()
{
buffer = (char*)malloc(bufferinc);
assert(buffer);
allocated = bufferinc;
length = 0;
}
~membuffer()
{
free(buffer);
}
void write(void* data, int size)
{
while (length + size >= allocated) grow();
memcpy(&buffer[length], data, size);
length += size;
}
void write(void* data, int size, int offset)
{
assert(offset >= 0 && offset+size <= length);
memcpy(&buffer[offset], data, size);
}
int tell()
{
return length;
}
char* steal_buffer()
{
char* ret = buffer;
buffer = NULL;
return ret;
}
char* buffer;
int length;
private:
int allocated;
static const int bufferinc = 1024;
void grow()
{
allocated += bufferinc;
buffer = (char*)realloc(buffer, allocated);
assert(buffer);
}
};
// Convenient storage for the internal tree
typedef struct {
std::wstring name;
std::wstring value;
} XMLAttribute;
typedef struct XMLElement {
std::wstring name;
std::wstring text;
std::vector<XMLElement*> childs;
std::vector<XMLAttribute*> attrs;
} XMLElement;
class XeroHandler : public DefaultHandler
{
public:
// SAX2 event handlers:
virtual void startDocument();
virtual void endDocument();
virtual void startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const Attributes& attrs);
virtual void endElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname);
virtual void characters(const XMLCh* const chars, const unsigned int length);
// Non-SAX2 stuff, used for storing the
// parsed data and constructing the XMB:
void CreateXMB(unsigned long crc);
membuffer buffer;
private:
std::set<std::wstring> ElementNames;
std::set<std::wstring> AttributeNames;
XMLElement* Root;
XMLElement* CurrentElement;
std::stack<XMLElement*> ElementStack;
std::map<std::wstring, int> ElementID;
std::map<std::wstring, int> AttributeID;
void OutputElement(XMLElement* el);
};
CXeromyces::CXeromyces()
: XMBFileHandle(0), XMBBuffer(NULL)
{
}
CXeromyces::~CXeromyces() {
if (XMBFileHandle)
{
// If it was read from a file, close it
vfs_unmap(XMBFileHandle);
vfs_close(XMBFileHandle);
}
else
{
// If it was converted from a XML directly into memory,
// free that memory buffer
free(XMBBuffer);
}
}
void CXeromyces::Terminate()
{
if (XercesLoaded)
{
XMLPlatformUtils::Terminate();
XercesLoaded = 0;
}
}
void CXeromyces::Load(const char* filename)
{
// HACK: This is only done so early because CVFSInputSource
// requires XMLTranscode. It would preferably not be done until
// we actually need Xerces.
if (! XercesLoaded)
{
XMLPlatformUtils::Initialize();
XercesLoaded = 1;
}
CVFSInputSource source;
if (source.OpenFile(filename))
{
LOG(ERROR, "CXeromyces: Failed to load XML file '%s'", filename);
throw "Failed to load XML file";
}
unsigned long XMLChecksum = source.CRC32();
// Check whether the XMB file needs to be regenerated:
// Generate the XMB's filename
CStr filenameXMB = filename;
filenameXMB[(int)filenameXMB.Length()-1] = 'b';
// HACK: Check whether the XMB exists (in a rather unpleasant way)
char path[VFS_MAX_PATH];
vfs_realpath(filename, path); // can't get the XMB's VFS path because it doesn't exist
path[strlen(path)-1] = 'b';
bool fileExists = false;
{
FILE* f = fopen(path, "r");
if (f)
{
fileExists = true;
fclose(f);
}
}
// Load the entire file, with the assumption that usually it's
// going to be valid and will then be passed to XMBFile().
if (fileExists && ReadXMBFile(filenameXMB.c_str(), true, XMLChecksum))
return;
// XMB isn't up to date with the XML, so rebuild it:
SAX2XMLReader* Parser = XMLReaderFactory::createXMLReader();
// Enable validation
Parser->setFeature(L"http://xml.org/sax/features/validation", true);
Parser->setFeature(L"http://apache.org/xml/features/validation/dynamic", true);
XeroHandler handler;
Parser->setContentHandler(&handler);
CXercesErrorHandler errorHandler;
Parser->setErrorHandler(&errorHandler);
CVFSEntityResolver entityResolver(filename);
Parser->setEntityResolver(&entityResolver);
// Build a tree inside handler
Parser->parse(source);
// (It's horribly inefficient doing SAX2->tree then tree->XMB,
// but the XML->XMB conversion should be done very rarely
// anyway. If it's ever needed, the XMB writing can be done
// directly from inside the SAX2 event handlers, although that's
// a little more complex)
delete Parser;
// Convert the data structures into the XMB format
handler.CreateXMB(XMLChecksum);
// Save the file to disk, so it can be loaded quickly next time
vfs_uncached_store(filenameXMB, handler.buffer.buffer, handler.buffer.length);
XMBBuffer = handler.buffer.steal_buffer();
Initialise(XMBBuffer);
}
bool CXeromyces::ReadXMBFile(const char* filename, bool CheckCRC, unsigned long CRC)
{
Handle file = vfs_open(filename);
if (file <= 0)
{
LOG(ERROR, "CXeromyces: file '%s' couldn't be opened (vfs_open: %lld)\n", filename, file);
return false;
}
void* buffer;
size_t bufferSize;
int err;
if ( (err=vfs_map(file, 0, buffer, bufferSize)) )
{
LOG(ERROR, "CXeromyces: file '%s' couldn't be read (vfs_map: %d)\n", filename, err);
vfs_close(file);
return false;
}
assert(bufferSize >= 42 && "Invalid XMB file"); // 42 bytes is the smallest possible XMB. (Well, okay, 50 bytes is the smallest, but it was 42 the first time I counted, and 42 is a much nicer number.)
assert(*(int*)buffer == HeaderMagic && "Invalid XMB file header");
if (CheckCRC)
{
unsigned long fileCRC = *((unsigned long*)buffer + 1); // read the second four-byte number
if (CRC != fileCRC)
{
// Checksums don't match; have to regenerate from the XML
vfs_unmap(file);
vfs_close(file);
return false;
}
}
// Store the Handle so it can be closed later
XMBFileHandle = file;
// Set up the XMBFile
Initialise((char*)buffer);
return true;
}
void XeroHandler::startDocument()
{
Root = new XMLElement;
ElementStack.push(Root);
}
void XeroHandler::endDocument()
{
}
std::wstring lowercase(std::wstring a)
{
std::wstring b;
b.resize(a.length());
for (size_t i = 0; i < a.length(); ++i)
b[i] = towlower(a[i]);
return b;
}
void XeroHandler::startElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const Attributes& attrs)
{
std::wstring elementName = lowercase(localname);
ElementNames.insert(elementName);
// Create a new element
XMLElement* e = new XMLElement;
e->name = elementName;
// Store all the attributes in the new element
for (unsigned int i = 0; i < attrs.getLength(); ++i)
{
std::wstring attrName = lowercase(attrs.getLocalName(i));
AttributeNames.insert(attrName);
XMLAttribute* a = new XMLAttribute;
a->name = attrName;
a->value = attrs.getValue(i);
e->attrs.push_back(a);
}
// Add the element to its parent
ElementStack.top()->childs.push_back(e);
// Set as parent of following elements
ElementStack.push(e);
}
void XeroHandler::endElement(const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname)
{
ElementStack.pop();
}
void XeroHandler::characters(const XMLCh* const chars, const unsigned int length)
{
ElementStack.top()->text += chars;
}
void XeroHandler::CreateXMB(unsigned long crc)
{
// Header
buffer.write((void*)HeaderMagicStr, 4);
// Checksum
buffer.write(&crc, 4);
std::set<std::wstring>::iterator it;
int i;
// Element names
i = 0;
int ElementCount = (int)ElementNames.size();
buffer.write(&ElementCount, 4);
for (it = ElementNames.begin(); it != ElementNames.end(); ++it)
{
int TextLen = 2*((int)it->length()+1);
buffer.write(&TextLen, 4);
buffer.write((void*)it->c_str(), TextLen);
ElementID[*it] = i++;
}
// Attribute names
i = 0;
int AttributeCount = (int)AttributeNames.size();
buffer.write(&AttributeCount, 4);
for (it = AttributeNames.begin(); it != AttributeNames.end(); ++it)
{
int TextLen = 2*((int)it->length()+1);
buffer.write(&TextLen, 4);
buffer.write((void*)it->c_str(), TextLen);
AttributeID[*it] = i++;
}
// All the XML contents must be surrounded by a single element
assert(Root->childs.size() == 1);
OutputElement(Root->childs[0]);
delete Root;
}
// Writes a whole element (recursively if it has children) into the buffer,
// and also frees all the memory that has been allocated for that element.
void XeroHandler::OutputElement(XMLElement* el)
{
// Filled in later with the length of the element
int Pos_Length = buffer.tell();
buffer.write("????", 4);
int NameID = ElementID[el->name];
buffer.write(&NameID, 4);
int AttrCount = (int)el->attrs.size();
buffer.write(&AttrCount, 4);
int ChildCount = (int)el->childs.size();
buffer.write(&ChildCount, 4);
// Filled in later with the offset to the list of child elements
int Pos_ChildrenOffset = buffer.tell();
buffer.write("????", 4);
// Trim excess whitespace
std::wstring whitespace = L" \t\r\n";
size_t first = el->text.find_first_not_of(whitespace);
if (first == -1) // entirely whitespace
el->text = L"";
else
{
size_t last = el->text.find_last_not_of(whitespace);
el->text = el->text.substr(first, 1+last-first);
}
// Output text, prefixed by length in bytes
int TextLen = 2*((int)el->text.length()+1);
buffer.write(&TextLen, 4);
buffer.write((void*)el->text.c_str(), TextLen);
for (int i = 0; i < AttrCount; ++i)
{
int AttrName = AttributeID[el->attrs[i]->name];
buffer.write(&AttrName, 4);
int AttrLen = 2*((int)el->attrs[i]->value.length()+1);
buffer.write(&AttrLen, 4);
buffer.write((void*)el->attrs[i]->value.c_str(), AttrLen);
// Free each attribute as soon as it's been dealt with
delete el->attrs[i];
}
// Go back and fill in the child-element offset
int ChildrenOffset = buffer.tell() - (Pos_ChildrenOffset+4);
buffer.write(&ChildrenOffset, 4, Pos_ChildrenOffset);
for (int i = 0; i < ChildCount; ++i)
OutputElement(el->childs[i]);
// Go back and fill in the length
int Length = buffer.tell() - Pos_Length;
buffer.write(&Length, 4, Pos_Length);
// Tidy up the parser's mess
delete el;
}

46
source/ps/Xeromyces.h Executable file
View File

@ -0,0 +1,46 @@
/* $Id: Xeromyces.h,v 1.1 2004/07/08 15:21:42 philip Exp $
Xeromyces file-loading interface.
Automatically creates and caches relatively
efficient binary representations of XML files.
- Philip Taylor (philip@zaynar.demon.co.uk / @wildfiregames.com)
*/
#ifndef _XEROMYCES_H_
#define _XEROMYCES_H_
#include "ps/XeroXMB.h"
#include "lib/res/vfs.h"
class CXeromyces : public XMBFile
{
public:
CXeromyces();
~CXeromyces();
// Load from an XML file (with invisible XMB caching).
// Throws a const char* if stuff breaks.
void Load(const char* filename);
// Call once when shutting down the program.
static void Terminate();
// Get the XMBFile after having called Load
// XMBFile* GetXMB() { assert(XMB); return XMB; }
private:
bool ReadXMBFile(const char* filename, bool CheckCRC, unsigned long CRC);
XMBFile* XMB;
Handle XMBFileHandle; // if it's being read from disk
char* XMBBuffer; // if it's being read from RAM
static int XercesLoaded; // for once-only initialisation
};
#endif // _XEROMYCES_H_

View File

@ -1,14 +1,10 @@
#include "precompiled.h"
#undef new // if it was redefined for leak detection, since xerces doesn't like it
#include "BaseEntity.h"
#include "ObjectManager.h"
#include "CStr.h"
#include "XML.h"
// automatically use namespace ..
XERCES_CPP_NAMESPACE_USE
#include "ps/Xeromyces.h"
CBaseEntity::CBaseEntity()
{
@ -32,125 +28,98 @@ CBaseEntity::~CBaseEntity()
bool CBaseEntity::loadXML( CStr filename )
{
bool parseOK = false;
// Initialize XML library
XMLPlatformUtils::Initialize();
CXeromyces XeroFile;
try
{
// Create parser instance
XercesDOMParser *parser = new XercesDOMParser();
// Setup parser
parser->setValidationScheme(XercesDOMParser::Val_Auto);
parser->setDoNamespaces(false);
parser->setDoSchema(false);
parser->setCreateEntityReferenceNodes(false);
// Set customized error handler
CXercesErrorHandler *errorHandler = new CXercesErrorHandler();
parser->setErrorHandler(errorHandler);
CVFSEntityResolver *entityResolver = new CVFSEntityResolver(filename);
parser->setEntityResolver(entityResolver);
// Get main node
CVFSInputSource source;
parseOK=source.OpenFile(filename)==0;
if (parseOK)
{
// Parse file
parser->parse(source);
// Check how many errors
parseOK = parser->getErrorCount() == 0;
}
if (parseOK) {
// parsed successfully - grab our data
DOMDocument *doc = parser->getDocument();
DOMElement *element = doc->getDocumentElement();
// root_name should be Object
CStr root_name = XMLTranscode( element->getNodeName() );
// should have at least 3 children - Name, ModelName and TextureName
DOMNodeList *children = element->getChildNodes();
int numChildren=children->getLength();
for (int i=0; i<numChildren; ++i) {
// Get node
DOMNode *child = children->item(i);
// A child element
if (child->getNodeType() == DOMNode::ELEMENT_NODE)
{
// First get element and not node
DOMElement *child_element = (DOMElement*)child;
CStr element_name = XMLTranscode( child_element->getNodeName() );
DOMNode *value_node= child_element->getChildNodes()->item(0);
CStr element_value=value_node ? XMLTranscode(value_node->getNodeValue()) : "";
if( element_name == CStr( "Name" ) )
{
m_name = element_value;
}
else if( element_name == CStr( "Actor" ) )
{
m_actorObject = g_ObjMan.FindObject( element_value );
}
else if( element_name == CStr( "Speed" ) )
{
m_speed = element_value.ToFloat();
}
else if( element_name == CStr( "TurningRadius" ) )
{
m_turningRadius = element_value.ToFloat();
}
else if( element_name == CStr( "Size" ) )
{
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
CStr radius = XMLTranscode( child_element->getAttribute( (XMLCh*)L"Radius" ) );
m_bound_circle->setRadius( radius.ToFloat() );
m_bound_type = CBoundingObject::BOUND_CIRCLE;
}
else if( element_name == CStr( "Footprint" ) )
{
if( !m_bound_box )
m_bound_box = new CBoundingBox();
CStr width = XMLTranscode( child_element->getAttribute( (XMLCh*)L"Width" ) );
CStr height = XMLTranscode( child_element->getAttribute( (XMLCh*)L"Height" ) );
m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_OABB;
}
else if( element_name == CStr( "BoundsOffset" ) )
{
CStr x = XMLTranscode( child_element->getAttribute( (XMLCh*)L"x" ) );
CStr y = XMLTranscode( child_element->getAttribute( (XMLCh*)L"y" ) );
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
if( !m_bound_box )
m_bound_box = new CBoundingBox();
m_bound_circle->m_offset.x = x.ToFloat();
m_bound_circle->m_offset.y = y.ToFloat();
m_bound_box->m_offset.x = x.ToFloat();
m_bound_box->m_offset.y = y.ToFloat();
}
}
}
}
delete parser;
delete errorHandler;
delete entityResolver;
XeroFile.Load(filename);
}
catch (...)
{
return false;
}
XMLPlatformUtils::Terminate();
return parseOK;
// Define all the elements and attributes used in the XML file
#define EL(x) int el_##x = XeroFile.getElementID(L#x)
#define AT(x) int at_##x = XeroFile.getAttributeID(L#x)
EL(entity);
EL(name);
EL(actor);
EL(speed);
EL(turningradius);
EL(size);
EL(footprint);
EL(boundsoffset);
AT(radius);
AT(width);
AT(height);
AT(x);
AT(y);
#undef AT
#undef EL
XMBElement Root = XeroFile.getRoot();
assert(Root.getNodeName() == el_entity);
XMBElementList RootChildren = Root.getChildNodes();
for (int i = 0; i < RootChildren.Count; ++i)
{
XMBElement Child = RootChildren.item(i);
int ChildName = Child.getNodeName();
if (ChildName == el_name)
{
m_name = tocstr(Child.getText());
}
else if (ChildName == el_actor)
{
m_actorObject = g_ObjMan.FindObject( tocstr(Child.getText()) );
}
else if (ChildName == el_speed)
{
m_speed = CStr16(Child.getText()).ToFloat();
}
else if (ChildName == el_turningradius)
{
m_turningRadius = CStr16(Child.getText()).ToFloat();
}
else if (ChildName == el_size)
{
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
CStr16 radius = Child.getAttributes().getNamedItem(at_radius);
m_bound_circle->setRadius( radius.ToFloat() );
m_bound_type = CBoundingObject::BOUND_CIRCLE;
}
else if (ChildName == el_footprint)
{
if( !m_bound_box )
m_bound_box = new CBoundingBox();
CStr16 width = Child.getAttributes().getNamedItem(at_width);
CStr16 height = Child.getAttributes().getNamedItem(at_height);
m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() );
m_bound_type = CBoundingObject::BOUND_OABB;
}
else if (ChildName == el_boundsoffset)
{
CStr16 x = Child.getAttributes().getNamedItem(at_x);
CStr16 y = Child.getAttributes().getNamedItem(at_y);
if( !m_bound_circle )
m_bound_circle = new CBoundingCircle();
if( !m_bound_box )
m_bound_box = new CBoundingBox();
m_bound_circle->m_offset.x = x.ToFloat();
m_bound_circle->m_offset.y = y.ToFloat();
m_bound_box->m_offset.x = x.ToFloat();
m_bound_box->m_offset.y = y.ToFloat();
}
}
return true;
}