Files
scummvm/engines/qdengine/parser/xml_parser.cpp
T

350 lines
9.0 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _XML_ONLY_BINARY_SCRIPT_
#endif
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "common/file.h"
#include "common/textconsole.h"
#include "qdengine/qd_precomp.h"
#include "qdengine/parser/xml_parser.h"
namespace QDEngine {
namespace xml {
#ifndef _XML_ONLY_BINARY_SCRIPT_
static void start_element_handler(void *userData, const XML_Char *name, const XML_Char **atts);
static void end_element_handler(void *userData, const XML_Char *name);
static void character_data_handler(void *userData, const XML_Char *s, int len);
static int unknown_encoding_handler(void *encodingHandlerData, const XML_Char *name, XML_Encoding *info);
static const char *UTF8_convert(const char *input_string, int input_string_length = -1);
#endif
}; /* namespace xml */
/* --------------------------- DEFINITION SECTION --------------------------- */
namespace xml {
#ifndef _XML_ONLY_BINARY_SCRIPT_
static const char *UTF8_convert(const char *input_string, int input_string_length) {
static std::wstring wstr(1024, 0);
static std::string str(1024, 0);
unsigned int length = MultiByteToWideChar(CP_UTF8, 0, input_string, input_string_length, NULL, 0);
if (wstr.length() < length)
wstr.resize(length, 0);
MultiByteToWideChar(CP_UTF8, 0, input_string, input_string_length, &*wstr.begin(), length);
if (str.length() < length + 1)
str.resize(length + 1, 0);
str[length] = 0;
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), length, &*str.begin(), length, NULL, NULL);
return str.c_str();
}
static int unknown_encoding_handler(void *encodingHandlerData, const XML_Char *name, XML_Encoding *info) {
if (!scumm_stricmp(name, "WINDOWS-1251")) {
info->data = NULL;
info->convert = NULL;
info->release = NULL;
for (int i = 0; i < 256; i ++) {
char c = i;
wchar_t cc = 0;
MultiByteToWideChar(1251, 0, &c, 1, &cc, 1);
info->map[i] = cc;
}
return 1;
}
return 0;
}
static void start_element_handler(void *userData, const XML_Char *name, const XML_Char **atts) {
parser *p = static_cast<parser *>(userData);
p->start_element_handler(name, atts);
}
static void end_element_handler(void *userData, const XML_Char *name) {
parser *p = static_cast<parser *>(userData);
p->end_element_handler(name);
}
static void character_data_handler(void *userData, const XML_Char *s, int len) {
parser *p = static_cast<parser *>(userData);
p->character_data_handler(s, len);
}
#endif
parser::parser() : data_pool_position_(0), data_buffer_(1024, 0), cur_level_(0), skip_mode_(false), binary_script_(false) {
root_tag_.set_data(&data_pool_);
}
parser::~parser() {
}
void parser::clear() {
root_tag_.clear();
while (!tag_stack_.empty()) tag_stack_.pop();
cur_level_ = 0;
skip_mode_ = false;
data_pool_.clear();
std::vector<char>(data_pool_).swap(data_pool_);
}
#ifndef _XML_ONLY_BINARY_SCRIPT_
void parser::start_element_handler(const char *tag_name, const char **tag_attributes) {
if (!skip_mode_) {
const tag *fmt = get_tag_format(tag_name);
if (fmt) {
tag tg(*fmt);
tg.set_data(&data_pool_);
int sz = 0;
while (tag_attributes[sz]) sz++;
if (sz >= 2) {
for (int i = 0; i < sz; i += 2) {
const tag *afmt = get_tag_format(tag_attributes[i]);
if (afmt) {
tag att(*afmt);
att.set_data(&data_pool_);
read_tag_data(att, tag_attributes[i + 1], strlen(tag_attributes[i + 1]));
tg.add_subtag(att);
}
}
}
if (!tag_stack_.empty())
tag_stack_.push(&tag_stack_.top()->add_subtag(tg));
} else {
skip_mode_ = true;
cur_level_ = 0;
}
data_buffer_.clear();
} else
cur_level_ ++;
}
void parser::end_element_handler(const char *tag_name) {
if (!skip_mode_) {
if (!tag_stack_.empty())
read_tag_data(*tag_stack_.top(), data_buffer_.c_str(), strlen(data_buffer_.c_str()));
tag_stack_.pop();
} else {
if (!cur_level_--) skip_mode_ = false;
}
}
void parser::character_data_handler(const char *data, int data_length) {
data_buffer_.append(data, data_length);
}
#endif
bool parser::parse_file(const char *fname) {
if (is_script_binary(fname))
return read_binary_script(fname);
#ifndef _XML_ONLY_BINARY_SCRIPT_
binary_script_ = false;
warning("STUB: parser::parse_file()");
#if 0
XML_Parser p = XML_ParserCreate(NULL);
XML_SetUserData(p, this);
XML_SetElementHandler(p, xml::start_element_handler, xml::end_element_handler);
XML_SetCharacterDataHandler(p, xml::character_data_handler);
XML_SetUnknownEncodingHandler(p, unknown_encoding_handler, NULL);
if (!p) return false;
#endif
unsigned int fsize = ff.size();
#if 0
void *buf = XML_GetBuffer(p, fsize);
if (!buf) return false;
ff.read(static_cast<char *>(buf), fsize);
ff.close();
#endif
if (data_pool_.size() < fsize / 2)
data_pool_.resize(fsize / 2);
tag_stack_.push(&root_tag_);
#if 0
if (XML_ParseBuffer(p, fsize, 1) == XML_STATUS_OK) {
XML_ParserFree(p);
return true;
}
XML_Error err_code = XML_GetErrorCode(p);
XBuffer err_buf;
err_buf < XML_ErrorString(err_code) < "\nLine: " <= XML_GetCurrentLineNumber(p);
MessageBox(NULL, err_buf.c_str(), "XML Parser error", MB_OK);
XML_ParserFree(p);
#endif
#endif
return false;
}
#ifndef _XML_ONLY_BINARY_SCRIPT_
bool parser::read_tag_data(tag &tg, const char *data_ptr, int data_length) {
if (tg.data_size() && tg.data_format() != tag::TAG_DATA_VOID) {
if (tg.data_format() == tag::TAG_DATA_STRING) {
const char *str = UTF8_convert(data_ptr, data_length);
tg.set_data_size(strlen(str) + 1);
tg.set_data_offset(data_pool_position_);
unsigned int sz = tg.data_size() * tg.data_element_size();
if (data_pool_.size() < data_pool_position_ + sz)
data_pool_.resize(data_pool_position_ + sz);
char *p = &*(data_pool_.begin() + data_pool_position_);
size_t len = data_pool_.size() - data_pool_position_;
Common::strlcpy(p, str, len);
data_pool_position_ += sz;
return true;
}
tag_buffer buf(data_ptr, data_length);
if (tg.data_size() == -1) {
int sz;
buf >= sz;
tg.set_data_size(sz);
}
tg.set_data_offset(data_pool_position_);
unsigned int sz = tg.data_size() * tg.data_element_size();
if (data_pool_.size() < data_pool_position_ + sz)
data_pool_.resize(data_pool_position_ + sz);
switch (tg.data_format()) {
case tag::TAG_DATA_SHORT: {
short *p = reinterpret_cast<short *>(&*(data_pool_.begin() + data_pool_position_));
for (int j = 0; j < tg.data_size(); j ++) buf >= p[j];
}
break;
case tag::TAG_DATA_UNSIGNED_SHORT: {
unsigned short *p = reinterpret_cast<unsigned short *>(&*(data_pool_.begin() + data_pool_position_));
for (int j = 0; j < tg.data_size(); j ++) buf >= p[j];
}
break;
case tag::TAG_DATA_INT: {
int *p = reinterpret_cast<int *>(&*(data_pool_.begin() + data_pool_position_));
for (int j = 0; j < tg.data_size(); j ++) buf >= p[j];
}
break;
case tag::TAG_DATA_UNSIGNED_INT: {
unsigned int *p = reinterpret_cast<unsigned int *>(&*(data_pool_.begin() + data_pool_position_));
for (int j = 0; j < tg.data_size(); j ++) buf >= p[j];
}
break;
case tag::TAG_DATA_FLOAT: {
float *p = reinterpret_cast<float *>(&*(data_pool_.begin() + data_pool_position_));
for (int j = 0; j < tg.data_size(); j ++) buf >= p[j];
}
break;
}
data_pool_position_ += sz;
}
return true;
}
#endif
bool tag::readTag(Common::SeekableReadStream *ff, tag &tg) {
uint32 id = ff->readUint32LE();
uint32 data_format = ff->readUint32LE();
uint32 data_size = ff->readUint32LE();
uint32 data_offset = ff->readUint32LE();
tg = tag(tag(id, tag::tag_data_format(data_format), data_size, data_offset));
uint32 num_subtags = ff->readUint32LE();
for (int i = 0; i < num_subtags; i++) {
tag stg;
readTag(ff, stg);
tg.add_subtag(stg);
}
return true;
}
bool parser::read_binary_script(const char *fname) {
Common::File ff;
ff.open(fname);
binary_script_ = true;
/* uint32 v = */ff.readUint32LE();
uint32 size = ff.readUint32LE();
if (data_pool_.size() < size)
data_pool_.resize(size);
ff.read(&*data_pool_.begin(), size);
root_tag_.clear();
root_tag_.readTag(&ff, root_tag_);
root_tag_.set_data(&data_pool_);
ff.close();
return true;
}
bool parser::is_script_binary(const char *fname) const {
Common::File ff;
ff.open(fname);
uint32 v = ff.readUint32LE();
if (v == 8383) return true;
return false;
}
}; /* namespace xml */
} // namespace QDEngine