//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Nestopia is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include "NstStream.hpp" #include "NstVector.hpp" #include "NstXml.hpp" namespace Nes { namespace Core { #ifdef NST_MSVC_OPTIMIZE #pragma optimize("s", on) #endif Xml::Xml() : root(NULL) {} Xml::~Xml() { Destroy(); } void Xml::Destroy() { delete root; root = NULL; } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif Xml::Format::Format() : tab (" "), newline ("\r\n"), valueDelimiter (""), xmlHeader (true), byteOrderMark (true) { } inline int Xml::ToChar(idword ch) { return CHAR_MAX < 0xFF && ch > CHAR_MAX ? ch - (CHAR_MAX-CHAR_MIN+1) : ch; } inline wchar_t Xml::ToWideChar(idword ch) { return WCHAR_MAX < 0xFFFF && ch > WCHAR_MAX ? ch - (WCHAR_MAX-WCHAR_MIN+1) : ch; } byte* Xml::Input::Init(std::istream& stdStream,dword& size) { byte* data = NULL; try { Stream::In stream( &stdStream ); size = stream.Length(); data = new byte [size + 4]; stream.Read( data, size ); std::memset( data + size, 0, 4 ); } catch (...) { if (data) delete [] data; throw; } return data; } Xml::Input::Input(std::istream& s,dword t) : stream(Init(s,t)), size(t), pos(0) {} Xml::Input::~Input() { delete [] stream; } inline dword Xml::Input::Size() const { return size; } inline uint Xml::Input::ToByte(dword i) const { NST_ASSERT( i < size+4 ); return stream[i]; } inline int Xml::Input::ToChar(dword i) const { NST_ASSERT( i < size+4 ); return Xml::ToChar(stream[i]); } inline uint Xml::Input::FromUTF16BE(dword i) const { return ToByte(i+1) | ToByte(i+0) << 8; } inline uint Xml::Input::FromUTF16LE(dword i) const { return ToByte(i+0) | ToByte(i+1) << 8; } inline void Xml::Input::SetReadPointer(dword p) { NST_ASSERT( p <= size ); pos = p; } uint Xml::Input::ReadUTF8() { NST_ASSERT( pos <= size ); if (uint v = ToByte(pos)) { pos++; if (v & 0x80) { const uint w = ToByte(pos++); if ((v & 0xE0) == 0xC0) { if ((w & 0xC0) != 0x80) throw 1; v = (v << 6 & 0x7C0) | (w & 0x03F); } else if ((v & 0xF0) == 0xE0) { const uint z = ToByte(pos++); if ((w & 0xC0) == 0x80) { if ((z & 0xC0) != 0x80) throw 1; v = (v << 12 & 0xF000) | (w << 6 & 0x0FC0) | (z & 0x03F); } } else { throw 1; } } return v; } return 0; } Xml::Output::Output(std::ostream& s,const Format& f) : stream(s), format(f) {} inline Xml::Output::Type::Type(wcstring s) : string(s) {} inline Xml::Output::Value::Value(wcstring s) : string(s) {} void Xml::Output::Write(cstring s,uint n) const { stream.write( s, n ); } const Xml::Output& Xml::Output::operator << (char c) const { stream.put( c ); return *this; } const Xml::Output& Xml::Output::operator << (byte c) const { return *this << char(Xml::ToChar(c)); } const Xml::Output& Xml::Output::operator << (wchar_t c) const { const unsigned long int v = ((long)WCHAR_MIN < 0 ? (c + ((long)WCHAR_MAX-(long)WCHAR_MIN+1)) & 0xFFFFU : c); if (v < 0x80) { *this << byte( v ); } else if (v < 0x800) { *this << byte( 0xC0 | (v >> 6 & 0x1F) ); *this << byte( 0x80 | (v >> 0 & 0x3F) ); } else { *this << byte( 0xE0 | (v >> 12 & 0x0F) ); *this << byte( 0x80 | (v >> 6 & 0x3F) ); *this << byte( 0x80 | (v >> 0 & 0x3F) ); } return *this; } const Xml::Output& Xml::Output::operator << (Type type) const { while (const wchar_t c = *type.string++) (*this) << c; return *this; } const Xml::Output& Xml::Output::operator << (Value value) const { while (const wchar_t c = *value.string++) { switch (c) { case L'&': (*this) << "&"; break; case L'<': (*this) << "<"; break; case L'>': (*this) << ">"; break; case L'\'': (*this) << "'"; break; case L'\"': (*this) << """; break; default: (*this) << c; break; } } return *this; } inline const Xml::Output& Xml::Output::operator << (cstring s) const { while (const char c = *s++) (*this) << c; return *this; } template inline const Xml::Output& Xml::Output::operator << (const char (&s)[N]) const { Write( s, N-1 ); return *this; } Xml::Node Xml::Read(std::istream& stream) { Destroy(); Vector buffer; try { Input input( stream ); if (input.ToByte(0) == 0xFE && input.ToByte(1) == 0xFF) { buffer.Resize( input.Size() / 2 ); for (dword i=0, n=buffer.Size(); i < n; ++i) buffer[i] = input.FromUTF16BE( 2 + i * 2 ); } else if (input.ToByte(0) == 0xFF && input.ToByte(1) == 0xFE) { buffer.Resize( input.Size() / 2 ); for (dword i=0, n=buffer.Size(); i < n; ++i) buffer[i] = input.FromUTF16LE( 2 + i * 2 ); } else { bool utf8 = (input.ToByte(0) == 0xEF && input.ToByte(1) == 0xBB && input.ToByte(2) == 0xBF); if (utf8) { input.SetReadPointer(3); } else if (input.ToChar(0) == '<' && input.ToChar(1) == '?') { for (uint i=2; i < 128 && input.ToChar(i) && input.ToChar(i) != '>'; ++i) { if ( (input.ToChar( i+0 ) == 'U' || input.ToChar( i+0 ) == 'u') && (input.ToChar( i+1 ) == 'T' || input.ToChar( i+1 ) == 't') && (input.ToChar( i+2 ) == 'F' || input.ToChar( i+2 ) == 'f') && (input.ToChar( i+3 ) == '-' && input.ToChar( i+4 ) == '8') ) { utf8 = true; break; } } } if (utf8) { buffer.Reserve( input.Size() ); uint v; do { v = input.ReadUTF8(); buffer.Append( v ); } while (v); } else { buffer.Resize( input.Size() + 1 ); for (dword i=0, n=buffer.Size(); i < n; ++i) buffer[i] = input.ToByte( i ); } } } catch (...) { Destroy(); return NULL; } return Read( buffer.Begin() ); } Xml::Node Xml::Create(wcstring type) { Destroy(); if (type) { try { root = new BaseNode( type, type + std::wcslen(type), BaseNode::OUT ); } catch (...) { Destroy(); } } return root; } Xml::Node Xml::Read(utfstring file) { Destroy(); if (file) { try { for (utfstring stream = SkipVoid( file ); *stream; ) { switch (const Tag tag = CheckTag( stream )) { case TAG_XML: if (stream != file) throw 1; case TAG_COMMENT: case TAG_INSTRUCTION: stream = ReadTag( stream, root ); break; case TAG_OPEN: case TAG_OPEN_CLOSE: if (!root) { stream = ReadNode( stream, tag, root ); break; } default: throw 1; } } } catch (...) { Destroy(); } } return root; } void Xml::Write(const Node node,std::ostream& stream,const Format& format) const { if (node) { const Output output( stream, format ); if (format.byteOrderMark) output << byte(0xEF) << byte(0xBB) << byte(0xBF); if (format.xmlHeader) output << "" << format.newline; WriteNode( node, output, 0 ); } } void Xml::WriteNode(const Node node,const Output& output,const uint level) { for (uint i=level; i; --i) output << output.format.tab; output << '<' << Output::Type(node.GetType()); for (Attribute attribute(node.GetFirstAttribute()); attribute; attribute = attribute.GetNext()) { output << ' ' << Output::Type(attribute.GetType()) << "=\"" << Output::Value(attribute.GetValue()) << '\"'; } if (node.HasChildren() || *node.GetValue()) { output << '>'; if (*node.GetValue()) { output << output.format.valueDelimiter << Output::Value(node.GetValue()) << output.format.valueDelimiter; } if (node.HasChildren()) { output << output.format.newline; for (Node child(node.GetFirstChild()); child; child=child.GetNextSibling()) WriteNode( child, output, level+1 ); for (uint i=level; i; --i) output << output.format.tab; } output << "'; } else { output << " />"; } output << output.format.newline; } Xml::utfstring Xml::ReadNode(utfstring stream,Tag tag,BaseNode*& node) { NST_ASSERT( node == NULL && tag != TAG_CLOSE ); stream = ReadTag( stream, node ); if (tag == TAG_OPEN) { for (BaseNode** next = &node->child;;) { if (*stream == '<') { tag = CheckTag( stream ); if (tag == TAG_CLOSE) break; stream = ReadNode( stream, tag, *next ); if (*next) next = &(*next)->sibling; } else { stream = ReadValue( stream, *node ); } } stream = ReadTag( stream, node ); } return stream; } Xml::utfstring Xml::ReadTag(utfstring stream,BaseNode*& node) { NST_ASSERT( *stream == '<' ); ++stream; if (*stream == '!') { if (stream[1] == '-' && stream[2] == '-') { for (stream += 3; *stream; ++stream) { if (stream[0] == '-' && stream[1] == '-' && stream[2] == '>') { stream += 2; break; } } } } else if (*stream == '?') { while (*++stream) { if (stream[0] == '?' && stream[1] == '>') { ++stream; break; } } } else if (node) { if (*stream++ != '/') throw 1; for (wcstring type=node->type; *stream; ++stream, ++type) { if (ToWideChar(*stream) != *type) { if (*type) throw 1; stream = SkipVoid( stream ); break; } } } else { for (utfstring const t=stream; *stream; ++stream) { if (*stream == '>' || *stream == '/' || IsVoid( *stream )) { node = new BaseNode( t, stream, BaseNode::IN ); break; } } for (;;++stream) { if (*stream == '>') { break; } else if (*stream == '/') { ++stream; break; } else if (!IsVoid( *stream )) { utfstring const t = stream; while (*stream && *stream != '=' && !IsVoid( *stream )) ++stream; utfstring const tn = stream; stream = SkipVoid( stream ); if (*stream++ != '=') throw 1; stream = SkipVoid( stream ); const utfchar enclosing = *stream++; if (enclosing != '\"' && enclosing != '\'') throw 1; stream = SkipVoid( stream ); utfstring const v = stream; while (*stream && *stream != enclosing) ++stream; if (*stream != enclosing) throw 1; node->AddAttribute( t, tn, v, RewindVoid(stream,v) ); } } } if (*stream++ != '>') throw 1; return SkipVoid( stream ); } Xml::utfstring Xml::ReadValue(utfstring stream,BaseNode& node) { NST_ASSERT( *stream != '<' && !IsVoid( *stream ) ); for (utfstring const value = stream; *stream; ++stream) { if (*stream == '<') { node.SetValue( value, RewindVoid(stream), BaseNode::IN ); break; } } return stream; } bool Xml::IsEqual(wcstring a,wcstring b) { do { if (*a != *b) return false; } while (++b, *a++); return true; } bool Xml::IsEqualNonCase(wcstring a,wcstring b) { do { if ( (*a >= L'A' && *a <= L'Z' ? L'a' + (*a - L'A') : *a) != (*b >= L'A' && *b <= L'Z' ? L'a' + (*b - L'A') : *b) ) return false; } while (++b, *a++); return true; } long Xml::ToSigned(wcstring string,uint base,wcstring* end) { NST_ASSERT( string ); long value = 0; if (*string) { wchar_t* endptr = NULL; value = std::wcstol( string, end ? &endptr : NULL, base ); if (end) *end = (endptr ? endptr : string); if (errno == ERANGE) value = 0; } return value; } ulong Xml::ToUnsigned(wcstring string,uint base,wcstring* end) { NST_ASSERT( string ); ulong value = 0; if (*string) { wchar_t* endptr = NULL; value = std::wcstoul( string, end ? &endptr : NULL, base ); if (end) *end = (endptr ? endptr : string); if (errno == ERANGE) value = 0; } return value; } bool Xml::IsVoid(utfchar ch) { switch (ch) { case ' ': case '\r': case '\n': case '\t': return true; } return false; } bool Xml::IsCtrl(utfchar ch) { switch (ch) { case '\0': case '\a': case '\b': case '\t': case '\v': case '\n': case '\r': case '\f': return true; } return false; } Xml::utfstring Xml::SkipVoid(utfstring stream) { while (IsVoid( *stream )) ++stream; return stream; } Xml::utfstring Xml::RewindVoid(utfstring stream,utfstring stop) { while (stream != stop && IsVoid( stream[-1] )) --stream; return stream; } Xml::Tag Xml::CheckTag(utfstring stream) { if (stream[0] == '<') { if (stream[1] == '/') return TAG_CLOSE; if ( stream[1] == '!' && stream[2] == '-' && stream[3] == '-' ) return TAG_COMMENT; if (stream[1] == '?') { if ( stream[2] == 'x' && stream[3] == 'm' && stream[4] == 'l' && IsVoid( stream[5] ) ) return TAG_XML; else return TAG_INSTRUCTION; } while (*++stream) { if (*stream == '\"' || *stream == '\'') { for (const utfchar enclosing = *stream; stream[1]; ) { if (*++stream == enclosing) break; } } else if (*stream == '>') { if (stream[-1] == '/') return TAG_OPEN_CLOSE; else return TAG_OPEN; } } } throw 1; } template Xml::BaseNode::BaseNode(T t,T n,U u) : type (SetType(new wchar_t [n-t+1],t,n,u)), value (L""), attribute (NULL), child (NULL), sibling (NULL) { if (!type) throw 1; } template Xml::BaseNode::Attribute::Attribute(T t,T tn,T v,T vn,U u) : type (SetType(new wchar_t [(tn-t)+1+(vn-v)+1],t,tn,u)), value (SetValue(const_cast(type+(tn-t)+1),v,vn,u)), next (NULL) { } Xml::BaseNode::Attribute::~Attribute() { delete [] type; delete next; } Xml::BaseNode::~BaseNode() { delete [] type; if (*value) delete [] value; delete attribute; delete child; if (BaseNode* node = sibling) { do { BaseNode* tmp = node->sibling; node->sibling = NULL; delete node; node = tmp; } while (node); } } template void Xml::BaseNode::SetValue(T v,T vn,U u) { if (vn-v) { if (!*value) value = SetValue( new wchar_t [vn-v+1], v, vn, u ); else throw 1; } } void Xml::BaseNode::AddAttribute(utfstring t,utfstring tn,utfstring v,utfstring vn) { if (tn-t) { Attribute** a = &attribute; while (*a) a = &(*a)->next; (*a) = new Attribute( t, tn, v, vn, BaseNode::IN ); } else if (vn-t) { throw 1; } } wchar_t* Xml::BaseNode::SetType(wchar_t* NST_RESTRICT dst,utfstring src,utfstring const end,In) { NST_ASSERT( dst && src && end ); wchar_t* const ptr = dst; while (src != end) { const utfchar ch = *src++; if (!IsCtrl( ch )) { *dst++ = ToWideChar( ch ); } else { delete [] ptr; return NULL; } } *dst = L'\0'; return ptr; } wchar_t* Xml::BaseNode::SetType(wchar_t* NST_RESTRICT dst,wcstring src,wcstring const end,Out) { NST_ASSERT( dst && src && end ); wchar_t* const ptr = dst; while (src != end) *dst++ = *src++; *dst = L'\0'; return ptr; } wchar_t* Xml::BaseNode::SetValue(wchar_t* NST_RESTRICT dst,utfstring src,utfstring const end,In) { NST_ASSERT( dst && src && end ); wchar_t* const ptr = dst; while (src != end) { utfchar ch = *src++; if (ch == '&') ch = ParseReference( src, end ); if (!IsCtrl( ch ) || IsVoid( ch )) { *dst++ = ToWideChar( ch ); } else { delete [] ptr; return NULL; } } *dst = L'\0'; return ptr; } wchar_t* Xml::BaseNode::SetValue(wchar_t* NST_RESTRICT dst,wcstring src,wcstring const end,Out) { NST_ASSERT( dst && src && end ); wchar_t* const ptr = dst; while (src != end) *dst++ = *src++; *dst = L'\0'; return ptr; } Xml::utfchar Xml::BaseNode::ParseReference(utfstring& string,utfstring const end) { utfstring src = string; if (end-src >= 3) { switch (*src++) { case '#': for (utfstring const offset = src++; src != end; ++src) { if (*src == ';') { string = src + 1; if (*offset == 'x') { for (dword ch=0, n=0; ; n += (n < 16 ? 4 : 0)) { const utfchar v = *--src; if (v >= '0' && v <= '9') { ch |= dword(v - '0') << n; } else if (v >= 'a' && v <= 'f') { ch |= dword(v - 'a' + 10) << n; } else if (v >= 'A' && v <= 'F') { ch |= dword(v - 'A' + 10) << n; } else { return src == offset && ch <= 0xFFFF ? ch : '\0'; } } } else { for (dword ch=0, n=1; ; n *= (n < 100000 ? 10 : 1)) { const utfchar v = *--src; if (v >= '0' && v <= '9') { ch += (v - '0') * n; } else { return src < offset && ch <= 0xFFFF ? ch : '\0'; } } } } } break; case 'a': if (*src == 'm') { if ( end-src >= 3 && src[1] == 'p' && src[2] == ';' ) { string = src + 3; return '&'; } } else if (*src == 'p') { if ( end-src >= 4 && src[1] == 'o' && src[2] == 's' && src[3] == ';' ) { string = src + 4; return '\''; } } break; case 'l': if ( src[0] == 't' && src[1] == ';' ) { string = src + 2; return '<'; } break; case 'g': if ( src[0] == 't' && src[1] == ';' ) { string = src + 2; return '>'; } break; case 'q': if ( end-src >= 4 && src[0] == 'u' && src[1] == 'o' && src[2] == 't' && src[3] == ';' ) { string = src + 4; return '\"'; } break; } } return '\0'; } dword Xml::Node::NumAttributes() const { dword n = 0; if (node) { for (const BaseNode::Attribute* attribute = node->attribute; attribute; attribute = attribute->next) ++n; } return n; } dword Xml::Node::NumChildren(wcstring type) const { dword n = 0; if (node) { for (const BaseNode* next = node->child; next; next = next->sibling) n += (!type || !*type || IsEqual( next->type, type )); } return n; } Xml::Attribute Xml::Node::GetAttribute(dword i) const { BaseNode::Attribute* next = NULL; if (node) { for (next = node->attribute; i && next; --i) next = next->next; } return next; } Xml::Attribute Xml::Node::GetAttribute(wcstring type) const { if (node) { if (!type) type = L""; for (BaseNode::Attribute* next = node->attribute; next; next = next->next) { if (IsEqual( next->type, type )) return next; } } return NULL; } Xml::Node Xml::Node::GetChild(dword i) const { BaseNode* next = node; if (next) { for (next = node->child; i && next; --i) next = next->sibling; } return next; } Xml::Node Xml::Node::GetChild(wcstring type) const { if (node) { if (!type) type = L""; for (BaseNode* next = node->child; next; next = next->sibling) { if (IsEqual( next->type, type )) return next; } } return NULL; } long Xml::Node::GetSignedValue(uint base) const { return ToSigned( GetValue(), base, NULL ); } long Xml::Node::GetSignedValue(wcstring& end,uint base) const { return ToSigned( GetValue(), base, &end ); } ulong Xml::Node::GetUnsignedValue(uint base) const { return ToUnsigned( GetValue(), base, NULL ); } ulong Xml::Node::GetUnsignedValue(wcstring& end,uint base) const { return ToUnsigned( GetValue(), base, &end ); } Xml::BaseNode* Xml::Node::Add(wcstring type,wcstring value,BaseNode** next) const { while (*next) next = &(*next)->sibling; *next = new BaseNode( type, type + std::wcslen(type), BaseNode::OUT ); if (value && *value) (*next)->SetValue( value, value + std::wcslen(value), BaseNode::OUT ); return *next; } Xml::Attribute Xml::Node::AddAttribute(wcstring type,wcstring value) { if (type && *type && node) { BaseNode::Attribute** next = &node->attribute; while (*next) next = &(*next)->next; *next = new BaseNode::Attribute ( type, type + std::wcslen(type), value ? value : L"", value ? value + std::wcslen(value) : NULL, BaseNode::OUT ); return *next; } return NULL; } Xml::Node Xml::Node::AddChild(wcstring type,wcstring value) { return (type && *type && node) ? Add( type, value, &node->child ) : NULL; } Xml::Node Xml::Node::AddSibling(wcstring type,wcstring value) { return (type && *type && node) ? Add( type, value, &node->sibling ) : NULL; } long Xml::Attribute::GetSignedValue(uint base) const { return ToSigned( GetValue(), base, NULL ); } long Xml::Attribute::GetSignedValue(wcstring& end,uint base) const { return ToSigned( GetValue(), base, &end ); } ulong Xml::Attribute::GetUnsignedValue(uint base) const { return ToUnsigned( GetValue(), base, NULL ); } ulong Xml::Attribute::GetUnsignedValue(wcstring& end,uint base) const { return ToUnsigned( GetValue(), base, &end ); } } }