This commit is contained in:
watrabi
2025-10-28 14:05:46 -04:00
parent 977f1ff4b8
commit c93494f795
452 changed files with 47860 additions and 152 deletions
+48
View File
@@ -0,0 +1,48 @@
#ifndef _568E368F53F1431aB7D4923F6D45021A
#define _568E368F53F1431aB7D4923F6D45021A
#include "rbx/Debug.h"
#include "Util/Object.h"
#include "Util/Handle.h"
#include <list>
#include <map>
#include <set>
#include <string>
class XmlNameValuePair;
namespace RBX {
namespace Reflection
{
class DescribedBase;
}
// Used for streaming
class RBXInterface IIDREF
{
private:
friend class IReferenceBinder;
virtual void assignIDREF(Reflection::DescribedBase* propertyOwner, const InstanceHandle& handle) const = 0;
};
// Used for streaming
class RBXBaseClass IReferenceBinder
{
public:
virtual void announceID(const XmlNameValuePair* valueID, Reflection::DescribedBase* source) = 0;
virtual void announceIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) = 0;
virtual bool resolveRefs() = 0;
virtual ~IReferenceBinder(){}
protected:
void assign(const IIDREF* idref, Reflection::DescribedBase* propertyOwner, const InstanceHandle& handle) {
idref->assignIDREF(propertyOwner, handle);
}
};
}
#endif
+72
View File
@@ -0,0 +1,72 @@
#ifndef V8XML_SERIALIZER_H
#define V8XML_SERIALIZER_H
#pragma once
#include "SerializerV2.h"
#include "util/SoundService.h"
#include "v8datamodel/Workspace.h"
#include "v8datamodel/Lighting.h"
#include "v8datamodel/ServerStorage.h"
#include "v8datamodel/ReplicatedStorage.h"
#include "v8datamodel/ReplicatedFirst.h"
#include "v8datamodel/PlayerGui.h"
#include "v8datamodel/Hopper.h"
#include "v8datamodel/StarterPlayerService.h"
#include "v8datamodel/ServerScriptService.h"
#include "v8datamodel/CSGDictionaryService.h"
class Serializer : public SerializerV2
{
public:
static bool canWriteChild(const shared_ptr<RBX::Instance> instance, RBX::Instance::SaveFilter saveFilter)
{
if(!instance->getIsArchivable())
return false;
switch(saveFilter)
{
case RBX::Instance::SAVE_ALL:
return true;
case RBX::Instance::SAVE_WORLD:
if ( RBX::Instance::fastDynamicCast<RBX::Workspace>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::Lighting>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::Soundscape::SoundService>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::ServerStorage>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::ReplicatedStorage>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::CSGDictionaryService>(instance.get()) )
return true;
return false;
case RBX::Instance::SAVE_GAME:
if ( RBX::Instance::fastDynamicCast<RBX::StarterGuiService>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::StarterPackService>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::StarterPlayerService>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::ServerScriptService>(instance.get()) )
return true;
if ( RBX::Instance::fastDynamicCast<RBX::ReplicatedFirst>(instance.get()) )
return true;
return false;
default:
return true;
}
}
};
#endif
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <istream>
#include <ostream>
#include <v8tree/Instance.h>
namespace RBX
{
namespace SerializerBinary
{
enum SerializeFlags
{
sfHighCompression = 1 << 0,
sfNoCompression = 1 << 1,
sfInexactCFrame = 1 << 2
};
static const char kMagicHeader[] = "<roblox!";
void serialize(std::ostream& out, const Instance* root, unsigned int flags = 0, const Instance::SaveFilter saveFilter = Instance::SAVE_ALL);
void serialize(std::ostream& out, const Instances& instances, unsigned int flags = 0);
void deserialize(std::istream& in, Instance* root);
void deserialize(std::istream& in, Instances& result);
};
}
+121
View File
@@ -0,0 +1,121 @@
#pragma once
#include <vector>
#include <list>
#include <string>
#include "V8Xml/XmlElement.h"
#include "V8tree/instance.h"
#include "V8Xml/Reference.h"
#include "Util/Object.h"
//typedef std::vector<class PVItem*> ModelList;
namespace RBX {
class DataModel;
}
#if defined(G3D_WIN32)
#pragma warning(push)
#pragma warning(disable:4290)
#endif
// TODO: Refactor: Call this RBX::DOM or something
class SerializerV2 {
protected:
int schemaVersionLoading;
public:
static const int CURRENT_SCHEMA_VERSION = 4;
// writing:
static XmlElement* newRootElement();
static XmlElement* newRootElement(const std::string& type);
// reading:
void loadInstancesFromText(const XmlElement* root, RBX::Instances& result);
// Until DataModel becomes an Instance and it can handle "globals" like Workspace, we need to treat
// it specially during reads:
void load(std::istream& stream, RBX::DataModel* dataModel);
void loadInstances(std::istream& stream, RBX::Instances& result);
private:
void loadXML(std::istream& stream, RBX::DataModel* dataModel);
void loadInstancesXML(const XmlElement* root, RBX::Instances& result, RBX::IReferenceBinder& binder, RBX::CreatorRole creatorRole);
shared_ptr<RBX::Instance> loadInstanceXML(const XmlElement* itemElement, RBX::IReferenceBinder& binder, RBX::CreatorRole creatorRole);
};
#if defined(G3D_WIN32)
#pragma warning(pop)
#endif
namespace RBX
{
// MergeBinder is used to merge an XML stream into an existing world (for undo/redo operations)
class MergeBinder : public IReferenceBinder
{
struct IDREFItem {
const IIDREF* idref;
Reflection::DescribedBase* propertyOwner;
RBX::InstanceHandle value;
};
// TODO: vector or list???
std::vector<IDREFItem> deferredIDREFItems;
public:
virtual void announceID(const XmlNameValuePair* valueID, Reflection::DescribedBase* target) {
processID(valueID, target);
}
virtual void announceIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) {
bool processedIDREF = processIDREF(valueIDREF, propertyOwner, idref);
RBXASSERT(processedIDREF);
}
virtual bool resolveRefs() {
for (std::vector<IDREFItem>::iterator iter = deferredIDREFItems.begin(); iter!=deferredIDREFItems.end(); ++iter)
{
assign(iter->idref, iter->propertyOwner, iter->value);
}
deferredIDREFItems.clear();
return true;
}
protected:
virtual bool processID(const XmlNameValuePair* valueID, Reflection::DescribedBase* source) {
RBX::InstanceHandle h;
if (valueID->getValue(h)) {
h.linkTo(shared_from(source));
return true;
} else if (valueID->isValueEqual(&value_IDREF_nil))
return true; // "nil" means "skip this value", like xsi:nil does for other types
else
return false;
}
virtual bool processIDREF(const XmlNameValuePair* valueIDREF, Reflection::DescribedBase* propertyOwner, const IIDREF* idref) {
RBX::InstanceHandle value;
if (valueIDREF->getValue(value)) {
if (!value.empty())
assign(idref, propertyOwner, value);
else {
IDREFItem item = {idref, propertyOwner, value};
deferredIDREFItems.push_back(item);
}
return true;
} else if (valueIDREF->isValueEqual(&value_IDREF_nil))
return true; // "nil" means "skip this value", like xsi:nil does for other types
else
return false;
}
};
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "Reflection/Type.h"
class XmlElement;
namespace RBX
{
class WebParser
{
private:
static boost::mutex JSONmutex;
public:
typedef enum
{
FailOnNonJSON,
SkipNonJSON
} NonJSONBehavior;
static bool parseWebGenericResponse(std::istream& stream, RBX::Reflection::Variant& result);
static bool parseWebGenericResponse(const XmlElement* root, RBX::Reflection::Variant& result);
static bool parseWebListResponse(std::istream& stream, RBX::Reflection::ValueArray& result);
static bool legacyParseWebJSONResponse(std::stringstream& rawWebResponse, shared_ptr<const Reflection::ValueTable>& valueTable);
static bool ptreeParseWebJSONResponse(std::stringstream& rawWebResponse, shared_ptr<const Reflection::ValueTable>& valueTable);
static bool parseJSONTable(const std::string& rawWebResponse, shared_ptr<const Reflection::ValueTable>& valueTable);
static bool parseJSONArray(const std::string& rawWebResponse, shared_ptr<const Reflection::ValueArray>& valueArray);
static bool parseJSONObject(const std::string& rawWebResponse, Reflection::Variant& result);
static bool writeJSON(const Reflection::Variant& value, std::string& result, NonJSONBehavior skip = SkipNonJSON);
protected:
static bool loadTable(const XmlElement* tableElement, RBX::Reflection::ValueMap& result);
static bool loadList(const XmlElement* listElement, RBX::Reflection::ValueArray& result);
static bool loadEntry(const XmlElement* entryElement, std::string& key, RBX::Reflection::Variant& value);
static bool loadValue(const XmlElement* valueElement, RBX::Reflection::Variant& value);
};
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "Reflection/Type.h"
class XmlElement;
namespace RBX
{
class WebSerializer {
public:
static XmlElement* writeTable(const RBX::Reflection::ValueMap& result);
static XmlElement* writeList(const RBX::Reflection::ValueArray& result);
static XmlElement* writeEntry(const std::string& key, const RBX::Reflection::Variant& value);
static XmlElement* writeValue(const RBX::Reflection::Variant& value);
};
}
+417
View File
@@ -0,0 +1,417 @@
#ifndef V8XML_XMLELEMENT_H
#define V8XML_XMLELEMENT_H
#include "G3D/Vector3.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/Color3.h"
#include "rbx/Debug.h"
#include "Util/Name.h"
#include "Util/Memory.h"
#include "util/Utilities.h"
#include "util/ContentId.h"
#include <string>
#include <map>
#include <list>
#include <vector>
#include"Util/Utilities.h"
#include"Util/Handle.h"
//typedef std::string XmlTag;
typedef RBX::Name XmlTag;
// w3 XSD schema specification states that IDREF can't have empty values and can't have the xsi:nil attribute.
// We use xsi:nil to signify "don't change your value when reading me"
// As a result, we define 2 special IDREF strings "null" and "nil"
//
// "nil" means "don't change your current value
//
// for an IDREF "null" means "set your value to NULL"
extern const RBX::Name& value_IDREF_null;
extern const RBX::Name& value_IDREF_nil;
// TODO: Put these in a file that knows about the Roblox schema
extern const XmlTag& name_root;
extern const XmlTag& tag_roblox;
extern const XmlTag& name_xsinil;
extern const XmlTag& name_xsitype;
extern const XmlTag& tag_xmlnsxsi;
extern const XmlTag& tag_xsinoNamespaceSchemaLocation;
extern const XmlTag& tag_version;
extern const XmlTag& tag_assettype;
extern const XmlTag& name_referent;
extern const XmlTag& tag_External;
extern const XmlTag& name_Ref;
extern const XmlTag& name_token;
extern const XmlTag& name_name;
extern const XmlTag& tag_bool;
extern const XmlTag& tag_Refs;
extern const XmlTag& tag_X;
extern const XmlTag& tag_Y;
extern const XmlTag& tag_Z;
extern const XmlTag& tag_R00;
extern const XmlTag& tag_R01;
extern const XmlTag& tag_R02;
extern const XmlTag& tag_R10;
extern const XmlTag& tag_R11;
extern const XmlTag& tag_R12;
extern const XmlTag& tag_R20;
extern const XmlTag& tag_R21;
extern const XmlTag& tag_R22;
extern const XmlTag& tag_R;
extern const XmlTag& tag_G;
extern const XmlTag& tag_B;
extern const XmlTag& tag_class;
extern const XmlTag& tag_Item;
extern const XmlTag& tag_Properties;
extern const XmlTag& tag_Feature;
extern const XmlTag& tag_hash;
extern const XmlTag& tag_null;
extern const XmlTag& tag_mimeType;
extern const XmlTag& tag_S;
extern const XmlTag& tag_O;
extern const XmlTag& tag_XS;
extern const XmlTag& tag_XO;
extern const XmlTag& tag_YS;
extern const XmlTag& tag_YO;
extern const XmlTag& tag_faces;
extern const XmlTag& tag_axes;
extern const XmlTag& tag_Origin;
extern const XmlTag& tag_Direction;
extern const XmlTag& tag_Min;
extern const XmlTag& tag_Max;
extern const XmlTag& tag_WebTable;
extern const XmlTag& tag_WebList;
extern const XmlTag& tag_WebEntry;
extern const XmlTag& tag_WebKey;
extern const XmlTag& tag_WebValue;
extern const XmlTag& tag_WebType;
extern const XmlTag& tag_customPhysProp;
extern const XmlTag& tag_customDensity;
extern const XmlTag& tag_customFriction;
extern const XmlTag& tag_customElasticity;
extern const XmlTag& tag_customFrictionWeight;
extern const XmlTag& tag_customElasticityWeight;
// TODO: Optimization: Create a base class that has not children or attributes
class XmlElement;
class XmlWriter;
class XmlNameValuePair
{
public:
#ifndef _WIN32
#ifdef UINT
#undef UINT
#endif
#endif
typedef enum { NONE, NAME, STRING, CONTENTID, BOOL, INT, UINT, FLOAT, HANDLE, DOUBLE } ValueType;
private:
const XmlTag& tag;
mutable ValueType valueType;
union {
mutable std::string* stringValue;
mutable RBX::ContentId* contentIdValue;
mutable bool boolValue;
mutable int intValue;
mutable unsigned int uintValue;
mutable float floatValue;
mutable double doubleValue;
mutable const RBX::Name* nameValue;
mutable RBX::InstanceHandle* handleValue; // TODO: with in-place constructor/destructor, could we avoid a "new"
};
void clearValue() const;
public:
XmlNameValuePair(const XmlTag& tag)
:tag(tag),valueType(NONE) {}
XmlNameValuePair(const XmlTag& tag, const std::string& text)
:tag(tag),stringValue(new std::string(text)),valueType(STRING) {}
XmlNameValuePair(const XmlTag& tag, const char* text)
:tag(tag),stringValue(new std::string(text)),valueType(STRING) {}
XmlNameValuePair(const XmlTag& tag, RBX::ContentId contentId)
:tag(tag),contentIdValue(new RBX::ContentId(contentId)),valueType(CONTENTID) {}
XmlNameValuePair(const XmlTag& tag, const int _number)
:tag(tag),valueType(INT),intValue(_number) {}
XmlNameValuePair(const XmlTag& tag, const unsigned int _number)
:tag(tag),valueType(UINT),uintValue(_number) {}
XmlNameValuePair(const XmlTag& tag, const RBX::Name* value)
:tag(tag),valueType(NAME),nameValue(value) {}
XmlNameValuePair(const XmlTag& tag, bool value)
:tag(tag),valueType(BOOL),boolValue(value) {}
XmlNameValuePair(const XmlTag& tag, float value)
:tag(tag),valueType(FLOAT),floatValue(value) {}
XmlNameValuePair(const XmlTag& tag, double value)
:tag(tag),valueType(DOUBLE),doubleValue(value) {}
XmlNameValuePair(const XmlTag& tag, RBX::InstanceHandle value)
:tag(tag),valueType(HANDLE),handleValue(new RBX::InstanceHandle(value)) {}
~XmlNameValuePair() {
clearValue();
}
const XmlTag& getTag() const {return tag;}
bool isValueEmpty() const { return valueType==NONE; }
// equality tests (does not change valueType
bool isValueEqual(const std::string& value) const;
bool isValueEqual(RBX::ContentId contentId) const;
bool isValueEqual(int value) const;
bool isValueEqual(float value) const;
bool isValueEqual(double value) const;
bool isValueEqual(bool value) const;
bool isValueEqual(const RBX::Name* value) const;
bool isValueEqual(RBX::InstanceHandle value) const;
// Templated version that can be implemented by clients
template<class T>
bool isValueEqual(T value) const;
std::string toString(XmlWriter* writer) const;
// returns true if the value is of the given type
template<class T>
bool isValueType() const;
ValueType getValueType() const {return valueType;}
// get requests. If possible, these functions will convert valueType to the desired type
// TODO: refactor to "toValue"?
bool getValue(std::string &value) const;
bool getValue(RBX::ContentId& contentId) const;
bool getValue(int &value) const;
bool getValue(unsigned int &value) const;
bool getValue(float &value) const;
bool getValue(double &value) const;
bool getValue(bool &value) const;
bool getValue(const RBX::Name* &value) const;
bool getValue(RBX::InstanceHandle &value) const;
// Templated version that can be implemented by clients
template<class T>
bool getValue(T& value) const;
void setValue(std::string value) {clearValue(); stringValue = new std::string(value); valueType=STRING; }
void setValue(RBX::ContentId contentId) {clearValue(); contentIdValue = new RBX::ContentId(contentId); valueType=CONTENTID; }
void setValue(const char* value) {clearValue(); stringValue = new std::string(value); valueType=STRING; }
void setValue(int value) {clearValue(); intValue = value; valueType=INT; }
void setValue(unsigned int value) {clearValue(); uintValue = value; valueType=UINT; }
void setValue(bool value) {clearValue(); boolValue = value; valueType=BOOL; };
void setValue(float value) {clearValue(); floatValue = value; valueType=FLOAT; };
void setValue(double value) {clearValue(); doubleValue = value; valueType=DOUBLE; };
void setValue(const RBX::Name* value) {clearValue(); nameValue = value; valueType=NAME; };
void setValue(RBX::InstanceHandle handle) {clearValue(); handleValue = new RBX::InstanceHandle(handle); valueType=HANDLE; };
// Templated version that can be implemented by clients
template<class T>
void setValue(T value);
};
namespace RBX
{
// Parent and Child are a variation on the left-child/right-sibling tree, where each node also contains a reference
// the its right-most child. This makes it fast to push a child at the right-most end
template <class ChildClass>
class Parent {
private:
ChildClass* first; // "left child"
ChildClass* last; // "right child"
public:
Parent()
:first(NULL)
,last(NULL)
{}
void pushBackChild(ChildClass* child) {
if (last==NULL)
first = child;
else
last->setNextSibling(child);
last = child;
}
void pushFrontChild(ChildClass* child) {
if (first==NULL)
last = child;
else
child->setNextSibling(first);
first = child;
}
void addChild(ChildClass* child) {
pushBackChild(child);
}
void removeChild(ChildClass* child) {
if (first==child)
first = child->nextSibling();
else {
for (ChildClass* sibling = first; sibling!=NULL; sibling = sibling->nextSibling()) {
if (sibling->nextSibling()==child) {
sibling->setNextSibling(child->nextSibling());
if (child==last)
last = sibling;
break;
}
}
}
child->setNextSibling(NULL);
}
ChildClass* firstChild() { return first; }
ChildClass* nextChild(ChildClass* child) { return child->nextSibling(); }
const ChildClass* firstChild() const { return first; }
const ChildClass* nextChild(const ChildClass* child) const { return child->nextSibling(); }
};
template <class SiblingClass>
class Sibling {
private:
SiblingClass* next; // "right sibling"
protected:
Sibling():next(NULL) {}
Sibling(SiblingClass* nextSibling):next(nextSibling) {}
public:
const SiblingClass* nextSibling() const { return next; }
SiblingClass* nextSibling() { return next; }
private:
friend class Parent<SiblingClass>; // Only give "ParentClass" access to the setting of the sibling
void setNextSibling(SiblingClass* nextSibling) { this->next = nextSibling; }
};
}
class XmlAttribute
: public RBX::Sibling<XmlAttribute>
, public XmlNameValuePair
, public RBX::Allocator<XmlAttribute>
{
public:
XmlAttribute(const XmlTag& tag)
:XmlNameValuePair(tag) {}
template <typename T>
XmlAttribute(const XmlTag& tag, T value)
:XmlNameValuePair(tag, value) {}
};
class XmlElement
: public RBX::Sibling<XmlElement>
, public RBX::Parent<XmlElement>
, public XmlNameValuePair
, public RBX::Allocator<XmlElement>
{
#ifdef _DEBUG
char leak[15];
void recordLeak()
{
strcpy(leak,"XmlElement");
}
#endif
private:
RBX::Parent<XmlAttribute> attributes;
public:
XmlElement(const XmlTag& tag)
:XmlNameValuePair(tag)
{
#ifdef _DEBUG
recordLeak();
#endif
}
template <typename T>
XmlElement(const XmlTag& tag, T value)
:XmlNameValuePair(tag, value)
{
#ifdef _DEBUG
recordLeak();
#endif
}
~XmlElement() {
XmlAttribute* attr = attributes.firstChild();
while (attr)
{
XmlAttribute* temp = attr;
attr = attr->nextSibling();
delete temp;
}
XmlElement* child = firstChild();
while (child)
{
XmlElement* temp = child;
child = child->nextSibling();
delete temp;
}
}
/////////////////////////////////////////////////////////////////////
// attributes
//
// returns true if this element has an xsi:nil attribute with value "true"
bool isXsiNil() const;
template <typename T>
inline void addAttribute(const XmlTag& _tag, T value) {
XmlAttribute* a = new XmlAttribute(_tag, value);
addAttribute(a);
}
inline const XmlAttribute* getFirstAttribute() const {return attributes.firstChild();}
inline const XmlAttribute* getNextAttribute(const XmlAttribute* attribute) const {return attributes.nextChild(attribute);}
inline XmlAttribute* getFirstAttribute() {return attributes.firstChild();}
inline XmlAttribute* getNextAttribute(XmlAttribute* attribute) {return attributes.nextChild(attribute);}
const XmlAttribute* findAttribute(const XmlTag& _tag) const;
XmlAttribute* findAttribute(const XmlTag& _tag);
inline bool findAttributeValue(const XmlTag& _tag, const RBX::Name*& value) const {
const XmlAttribute* attribute = findAttribute(_tag);
if (attribute==NULL)
return false;
return attribute->getValue(value);
}
inline bool findAttributeValue(const XmlTag& _tag, std::string& value) const {
const XmlAttribute* attribute = findAttribute(_tag);
if (attribute==NULL)
return false;
return attribute->getValue(value);
}
/////////////////////////////////////////////////////////////////////
// child elements
//
inline XmlElement* addChild(XmlElement* element) { pushBackChild(element); return element; }
XmlElement* addChild(const XmlTag& _tag) {
XmlElement* n = new XmlElement(_tag);
return addChild(n);
}
const XmlElement* findFirstChildByTag(const XmlTag& _tag) const;
const XmlElement* findNextChildWithSameTag(const XmlElement* node) const;
protected:
inline void addAttribute(XmlAttribute* attribute) {
attributes.pushBackChild(attribute);
}
};
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef V8XML_XMLSERIALIZER_H
#define V8XML_XMLSERIALIZER_H
#include <vector>
#include <stack>
#include <ostream>
#include <boost/unordered_map.hpp>
#include "V8Xml/XmlElement.h"
namespace RBX
{
class ContentProvider;
}
class RBXBaseClass XmlWriter : public boost::noncopyable {
protected:
std::map<RBX::InstanceHandle, int> handles;
typedef boost::unordered_map<std::string, RBX::InstanceHandle> IdValidationMap;
IdValidationMap idValidationMap;
std::ostream& stream;
XmlWriter(std::ostream& stream);
public:
virtual void serialize(const XmlElement* xmlNode) = 0;
int getHandleIndex(RBX::InstanceHandle h) {
int i;
std::map<RBX::InstanceHandle, int>::iterator iter = handles.find(h);
if (iter==handles.end()) {
i = static_cast<int>(handles.size()); // TODO: Should this be size_t getHandleIndex?
handles[h] = i;
return i;
} else
i = iter->second;
return i;
}
bool isValidId(const std::string& id, const RBX::InstanceHandle& h) const
{
IdValidationMap::const_iterator itr = idValidationMap.find(id);
return itr == idValidationMap.end() || (itr->second.getTarget() == h.getTarget());
}
void recordId(const std::string& id, const RBX::InstanceHandle& h)
{
RBXASSERT(isValidId(id, h));
idValidationMap[id] = h;
}
};
class TextXmlWriter : public XmlWriter
{
public:
TextXmlWriter(std::ostream& stream)
:XmlWriter(stream)
{}
void serialize(const XmlElement* xmlNode);
protected:
void serialize(const XmlElement* xmlNode, int depth);
void writeOpenTag(const XmlElement* element, int depth);
void writeCloseTag(const XmlElement* element, int depth);
public:
static void xmlEncodedWrite(std::ostream& stream, const std::string& text);
static void xmlOrCDataEncodedWrite(std::ostream& stream, const std::string& text);
protected:
void serializeNode(const XmlElement* xmlNode, int depth);
};
class XmlParser : public boost::noncopyable {
protected:
std::streambuf* buffer;
XmlParser(std::streambuf* buffer);
std::stack<XmlElement*> elements; // stack
public:
virtual std::auto_ptr<XmlElement> parse() = 0;
};
class TextXmlParser : public XmlParser {
std::map<std::string, std::string> legacyHashes; // workaround for a bug in the MD5 hasher
public:
TextXmlParser(std::streambuf* buffer)
:XmlParser(buffer)
{}
std::auto_ptr<XmlElement> parse();
private:
void skipWhitespace();
std::string readTag();
std::string readFirstTag();
std::string readText(bool decode);
// attribute handling
std::string removeTag(const std::string& contents, int &index);
std::string findNextToken(const std::string& contents, int &index);
std::string findText(const std::string& attribute);
XmlElement* parseAttributes(const std::string& currentTag);
};
#endif