Game Development Community

dev|Pro Game Development Curriculum

XML Console Parser

by Matthew "Ashteth" Kee · 10/24/2005 (8:00 am) · 22 comments

Download Code File

//------------------------------------------------------------------------------
// Matthew Kee
// Console Based XML parser for Torque Game Engine.
//
// This resource is based upon the TinyXML library and extends the resource:
// XML Persistence for Torque Objects
// By J. Donavan Stanley
// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=4238
//------------------------------------------------------------------------------

This resource extends the XML persistence "pickler" resource by adding an
XML parser console object. The main advantage of parsing XML from within
script is that it allows for easier conditional execution and object creation
(such as level loading). There are advantages to both pickling and script
execution which is why I chose to build this resource on top of the excellent
work of J. Donavan Stanley.

To install this resource, simply unzip the included source files into
your Torque / T2D base directory. If you already have the persistence
resource installed, there is no need to overwrite any files -- I haven't
changed the persistence resource in any way, I simply chose to include it
for added convenience.

At this point, you will need to alter your makefile "SDK\engine\targets.torque.mk"
by adding these lines:

SOURCE.PERSISTANCE=\
PERSISTANCE/tinystr.cc \
PERSISTANCE/tinyxmlerror.cc \
PERSISTANCE/tinyxmlparser.cc \
PERSISTANCE/tinyxml.cc \
PERSISTANCE/pickle.cc \
PERSISTANCE/xmlDoc.cc


And to the preexisting "SOURCE.ENGINE =\" designator, add:
$(SOURCE.PERSISTANCE) \

And to the preexisting "SOURCE.TESTAPP =\" designator, add:
$(SOURCE.PERSISTANCE) \

You may now execute the included demo. First, rename your current "client.cs"
file to something like "client_bak.cs". Then copy the included
file "client_xml.cs" and rename it "client.cs". You will also need to copy
the included file "level.xml" into the same directory as your Torque executable.

Note: This XML parser is stack based, so you will predominantly want to
move up and down the XML hierarchy logically rather than skipping
around indiscriminately.

Known issues: This resource loads files based upon the executable's active
directory and *not* the active mod directory. Furthermore, traditional Torque
filename designators like "~client/" do not work at present.

Presently this resource only implements the bare minimum functions needed to
parse XML files in TorqueScript. If you require any additional functions,
feel free to contact me (e-mail in my profile) or implement them yourself.
If you have any useful additions to this resource, feel free to contact me
and I'll see about adding the functionality.

Acknowledgment: The input file for this resource is based upon source
from the article: "Creating Moddable Games with XML and Scripting Part I"
by Oli Wilkinson
found at: "http://www.gamedev.net/reference/programming/features/modxml1/"

It is possible that in the future I will demonstrate a T2D space invaders
clone using XML for level loading. I don't promise anything though ;)

Update: Now contains XML write example.
Page «Previous 1 2
#1
12/30/2005 (7:04 am)
I'd like to give this a try but I think Con::getTypeName() isn't used anymore in TGE 1.4. I get this error when compiling:
Error 1 error C2039: 'getTypeName' : is not a member of 'Con' c:\Torque\engine\persistance\pickle.cc 251

Any ideas?

Nick
#2
01/06/2006 (6:54 am)
I just grepped the code, the thing has issues w/o T2D installed but...

Just add this to your console.cc (bottom of it is fine)
static const char *typeNames[MaxDataTypes] = { NULL, };
const char *getTypeName(S32 type)
{
	return typeNames[type];
}

Then add this to console.h on line 555
const char *getTypeName(S32 type);
#3
01/18/2006 (8:17 am)
Hey Dreamer, thanks for your help! I tried it but I can't get it to save nested SimGroups. I'll see if I can find another way.

Nick
#4
01/18/2006 (10:00 am)
Thats odd, but thanks for the heads up.
#5
05/30/2006 (9:50 pm)
Use the following function instead of the one in the resource for 1.4 TGE

bool TiXmlDocument::LoadStream( FileStream& stream )
{
   // Delete the existing data:
	Clear();
   U32 length = stream.getStreamSize();
   char* data = new char[length + 1];
   stream.read( length, data);
   
   data[length] = 0;

   Parse( data );
   delete[] data;
	if( Error() )
      return false;
   else
		return true;
}
#6
05/30/2006 (11:07 pm)
If you want to be able to get the TEXT of an element, i.e. 1.0 in this case it would be the "1.0"

Add this around line 230 in xmlDoc.cc

// -----------------------------------------------------------------------------
// Get text of element on stack if it exists. Must be first child of type TEXT
// -----------------------------------------------------------------------------
const char* xmlDoc::getText(void)
{
   TiXmlNode* pElement;
   if(!m_paNode.empty())
   {
      const int iLastElement = m_paNode.size() - 1;
      TiXmlNode* pNode = m_paNode[iLastElement];
      if(!pNode)
      {
         return false;
      }
      pElement = pNode->FirstChild();
   }
   else
   {
      if(!m_qDocument)
      {
         return false;
      }
      pElement = m_qDocument->FirstChild();
   }
  
   if(pElement)
   {
      if (pElement->Type() == TiXmlNode::TEXT)
      {
        return pElement->Value();
      }
   }
   return "";
}
ConsoleMethod(xmlDoc, GetText, const char*, 2, 2,
   "Get text of element on stack.")
{
   return object->getText();
}

and add this around line 58 in the xmlDoc.h

const char * getText();
#7
07/27/2006 (1:39 pm)
is there a way to read in the data without knowing the node names beforehand?
#8
08/01/2006 (3:26 pm)
To read strings, use the attributeExists function.

if(%pXml.attributeExists("color"))
      {
         // Get the x position attribute as a floating point number.
         %fVal = %pXml.attribute("color");
         echo("Color:" SPC %fVal);
      }
#9
08/15/2006 (12:05 pm)
is there a way to setText() of an xml node? i guess similar to getText() above, I m hoping someone has already implemented this...
#10
08/16/2006 (4:32 pm)
I added a simple line to free the file after loading it in tinyxml.ccc
bool TiXmlDocument::LoadStream( FileStream& stream )
{
   // Delete the existing data:
	Clear();
   U32 length = stream.getStreamSize();
   char* data = new char[length + 1];
   stream.read( length, data);
   
   data[length] = 0;

   Parse( data );
   delete[] data;

   stream.close(); //addon
	if( Error() )
      return false;
   else
		return true;
}
#11
08/22/2006 (1:35 pm)
I just wrote and testing this... works for the settext

// -----------------------------------------------------------------------------
// Set text of element on stack.
// -----------------------------------------------------------------------------
bool xmlDoc::setText(const char* rText)
{
   TiXmlNode* pElement = NULL;
   TiXmlNode* pNode = NULL;
   
   if(!m_paNode.empty())
   {
      const int iLastElement = m_paNode.size() - 1;
      pNode = m_paNode[iLastElement];
      if(pNode)
        pElement = pNode->FirstChild();
   }
   else
   {
      if(!m_qDocument)
         return false;
      
      pElement = m_qDocument->FirstChild();
   }
  
   if(pElement)
   {
      if (pElement->Type() == TiXmlNode::TEXT)
      {
          pElement->SetValue(rText);
          return true;
      }
      else if(pNode)
      {
          TiXmlNode* pNew = new TiXmlText(rText);
          pNode->InsertBeforeChild(pElement,*pNew);
          return true;
      }
      return false;
   }
   else
   {
        TiXmlNode* pNew = new TiXmlText(rText);
        pNode->InsertEndChild(*pNew);
        return true;
   }
   return false;
}

ConsoleMethod(xmlDoc, setText, bool, 1, 0,
   "Set text of element on stack.")
{
   return object->setText(argv[2]);
}
#12
10/22/2006 (6:31 pm)
Found a bug...

in the function
bool xmlDoc::nextSiblingElement(const char* rName)

around line 245 in xmlDoc.cc

pElement = pElement->NextSiblingElement(rName);
return true;

should be

pElement = pElement->NextSiblingElement(rName);
if(!pElement)
return false;

return true;

without this fix, whenever doing a

while(doc.nextSiblingElement("nodeName"))
{...}

You'll get one extra one at the end that is actually NULL;

:)
Sumner
#13
10/26/2006 (9:55 pm)
#14
11/07/2006 (3:12 pm)
Can't get this to compile with the latest TGB. Is it just me? Or...?
#15
11/11/2006 (1:36 pm)
Resource has been updated with the above suggestions and a few bug fixes. Sorry for the long delay :)
#16
11/19/2006 (6:17 pm)
[EDIT: POST REMOVED]

Posted some code but it turned out to be buggy... If I can't fix it myself I'll repost it with bugs included and perhaps one of you can help.
#17
11/19/2006 (7:12 pm)
Heh... Nevermind... I don't think that this resource is geared towards what I was looking to do. :)
#18
11/21/2006 (6:04 pm)
I just downloaded and imported the files. There's a bug in tinyxml.cc:

#include "core[b]\[/b]resManager.h"
should be
#include "core[b]/[/b]resManager.h"
The build process would halt claiming that it couldn't find resManager.h.

I'm using XCode 2.4.1 on MacOSX 10.4.8 PPC, TGB 1.1.2.
#19
02/16/2007 (11:13 am)
what is pickle.cc for? I added this previously, but it's no longer in this xml resource.
And also I am getting the following compilation error:
persistance\pickle.cc(253): error C2039: 'setValue' : is not a member of 'TiXmlElement'
#20
03/30/2007 (1:46 am)
So this resource isn't UTF-8 compatible.

How would one modify the code such that it is?

It's funny because when you do a %xml.addHeader(), the header says it's UTF8. My project actually requires UTF8 and that would be really nice.

Thoughts?
Page «Previous 1 2