Files
source/Tactical/LogicalBodyTypes/AbstractXMLLoader.cpp
T
AsdowandGitHub f48fadb802 Improve AbstractXMLParser error reporting (#330)
The more detailed error message that used to go only to Livelog.txt is now also displayed in the ingame error screen, providing more helpful error message to players when making bug reports/asking for help
2024-11-08 23:50:52 +02:00

217 lines
6.9 KiB
C++

#include "AbstractXMLLoader.h"
namespace LogicalBodyTypes {
AbstractXMLLoader::AbstractXMLLoader(XML_StartElementHandler startHandler, XML_EndElementHandler endHandler, XML_CharacterDataHandler charHandler, ParseDataFactoryFunc parseDataFactF) {
startElementHandler = startHandler;
endElementHandler = endHandler;
characterDataHandler = charHandler;
parseDataFactFuncPntr = parseDataFactF;
}
AbstractXMLLoader::~AbstractXMLLoader(void) {
}
AbstractXMLLoader::ParseData::ParseData(XML_Parser* parser) {
pParser = parser;
state = E_NONE;
level = 0;
szCharData[0] = '\0';
szErrorTxt[0] = '\0';
}
AbstractXMLLoader::ParseData* AbstractXMLLoader::MakeParseData(XML_Parser* parser) {
return new ParseData(parser);
}
bool AbstractXMLLoader::LoadFromFile(const char* directoryName, const char* fileName, CHAR8* errorBuf) {
HWFILE hFile;
UINT32 uiBytesRead;
UINT32 uiFSize;
CHAR8* lpcBuffer;
char fileNameFull[MAX_PATH + 1];
if (strlen(fileName) + strlen(directoryName) >= MAX_PATH) {
sprintf(errorBuf, "Can't load file %s%s, Concatenated filename too long for buffer!", directoryName, fileName);
LiveMessage(errorBuf);
return false;
}
SetDirectoryName(directoryName);
SetFileName(fileName);
strcpy(fileNameFull, directoryName);
strcat(fileNameFull, fileName);
XML_Parser parser = XML_ParserCreate(NULL);
AbstractXMLLoader::ParseData* data = parseDataFactFuncPntr(&parser);
std::string msg = "Loading ";
msg += fileName;
DebugMsg(TOPIC_JA2, DBG_LEVEL_3, msg.c_str());
hFile = FileOpen(fileNameFull, FILE_ACCESS_READ, FALSE);
if (!hFile) {
sprintf(errorBuf, "Can't open %s", fileNameFull);
delete data;
return false;
}
uiFSize = FileGetSize(hFile);
lpcBuffer = (CHAR8*)MemAlloc(uiFSize + 1);
if (!FileRead(hFile, lpcBuffer, uiFSize, &uiBytesRead)) {
sprintf(errorBuf, "Error reading %s to buffer", fileNameFull);
MemFree(lpcBuffer);
delete data;
return false;
}
lpcBuffer[uiFSize] = 0;
FileClose(hFile);
ExternalEntityArgs eeArgs;
eeArgs.directoryName = directoryName;
eeArgs.fileName = fileName;
eeArgs.pParser = &parser;
XML_SetElementHandler(parser, startElementHandler, endElementHandler);
XML_SetCharacterDataHandler(parser, characterDataHandler);
XML_SetUserData(parser, data);
XML_SetExternalEntityRefHandler(parser, ExternalEntityHandler);
XML_SetExternalEntityRefHandlerArg(parser, (void*)&eeArgs);
try {
if (!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) {
sprintf(errorBuf, "XML Parser Error in %s[%d]: %s", fileNameFull, XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser)));
LiveMessage(errorBuf);
MemFree(lpcBuffer);
delete data;
return false;
}
} catch (XMLParseException e) {
sprintf(errorBuf, "XML Parser Exception in %s[%d]: %s", fileNameFull, e._LINE, e.what());
LiveMessage(errorBuf);
MemFree(lpcBuffer);
XML_ParserFree(parser);
delete data;
return false;
}
MemFree(lpcBuffer);
XML_ParserFree(parser);
delete data;
return true;
}
int XMLCALL AbstractXMLLoader::ExternalEntityHandler(XML_Parser args, const XML_Char* context, const XML_Char* base, const XML_Char* systemId, const XML_Char* publicId) {
ExternalEntityArgs* eArgs = (ExternalEntityArgs*)args;
char fileNameFull[MAX_PATH + 1];
UINT32 uiBytesRead;
UINT32 uiFSize;
CHAR8* lpcBuffer;
HWFILE hFile;
if (strlen(eArgs->directoryName) + strlen(systemId) >= MAX_PATH) {
LiveMessage("Can't load file specified in external entity. Concatinated filename too long for buffer!");
return XML_STATUS_ERROR;
}
strcpy(fileNameFull, eArgs->directoryName);
strcat(fileNameFull, systemId);
hFile = FileOpen(fileNameFull, FILE_ACCESS_READ, FALSE);
if (!hFile) {
LiveMessage("File specified in external entity can't be opened!");
return XML_STATUS_ERROR;
}
uiFSize = FileGetSize(hFile);
lpcBuffer = (CHAR8*)MemAlloc(uiFSize + 1);
if (!FileRead(hFile, lpcBuffer, uiFSize, &uiBytesRead)) {
LiveMessage("File specified in external entity couldn't be read!");
MemFree(lpcBuffer);
return XML_STATUS_ERROR;
}
lpcBuffer[uiFSize] = 0;
FileClose(hFile);
XML_Parser extParser = XML_ExternalEntityParserCreate(*eArgs->pParser, context, NULL);
ParseData* data = (ParseData*)XML_GetUserData(*eArgs->pParser);
data->pParser = &extParser;
try {
if (!XML_Parse(extParser, lpcBuffer, uiFSize, TRUE)) {
CHAR8 errorBuf[512];
sprintf(errorBuf, "XML Parser Error in external entity %s[%d]: %s", systemId, XML_GetCurrentLineNumber(extParser), XML_ErrorString(XML_GetErrorCode(extParser)));
LiveMessage(errorBuf);
MemFree(lpcBuffer);
return XML_STATUS_ERROR;
}
} catch (XMLParseException e) {
CHAR8 errorBuf[512];
sprintf(errorBuf, "XML Parser Exception in external entity %s[%d]: %s", systemId, e._LINE, e.what());
LiveMessage(errorBuf);
XML_ParserFree(extParser);
MemFree(lpcBuffer);
return XML_STATUS_ERROR;
}
data->pParser = eArgs->pParser;
XML_ParserFree(extParser);
MemFree(lpcBuffer);
return XML_STATUS_OK;
};
const char* AbstractXMLLoader::GetFileName() {
return fileName;
}
const char* AbstractXMLLoader::GetDirectoryName() {
return directoryName;
}
void AbstractXMLLoader::SetFileName(const char* fileName) {
strcpy(this->fileName, fileName);
}
void AbstractXMLLoader::SetDirectoryName(const char* directoryName) {
strcpy(this->directoryName, directoryName);
}
const XML_Char* AbstractXMLLoader::GetAttribute(const XML_Char* name, const XML_Char** atts) {
for (const XML_Char** pIter = atts; *pIter != NULL; pIter++) {
const XML_Char* key = *pIter++;
if (strcmp(key, name) == 0) return *pIter;
}
return NULL;
}
bool AbstractXMLLoader::ConvertStringToINT8(const XML_Char* num, INT8* int8) {
long l = strtol(num, NULL, 10);
if (l > 127 || l < -128) return false;
if (l == 0 && strcmp(num, "0") != 0) return false;
*int8 = (INT8)l;
return true;
}
bool AbstractXMLLoader::ConvertStringToUINT8(const XML_Char* num, UINT8* uInt8) {
unsigned long ul = strtoul(num, NULL, 10);
if (ul > 255) return false;
if (ul == 0 && strcmp(num, "0") != 0) return false;
*uInt8 = (UINT8)ul;
return true;
}
bool AbstractXMLLoader::ConvertStringToINT32(const XML_Char* num, INT32* int32) {
long l = strtol(num, NULL, 10);
// TODO: add range checks also for long types (and use the defined macros)
// TODO: string should be trimmed (other convert functions also)
if (l == 0 && strcmp(num, "0") != 0) return false;
*int32 = (INT32)l;
return true;
}
bool AbstractXMLLoader::ConvertStringToUINT32(const XML_Char* num, UINT32* uInt32) {
unsigned long ul = strtoul(num, NULL, 10);
if (ul == 0 && strcmp(num, "0") != 0) return false;
*uInt32 = ul;
return true;
}
bool AbstractXMLLoader::ConvertStringToBOOLEAN(const XML_Char* num, BOOLEAN* fBoolean) {
unsigned long ul = strtoul(num, NULL, 10);
if (ul == 0 && strcmp(num, "0") != 0) return false;
*fBoolean = ul != 0;
return true;
}
}