XML validation. Based on patch by historic_bruno. Refs #245.

This was SVN commit r16733.
This commit is contained in:
leper 2015-06-07 21:56:52 +00:00
parent f260e75499
commit a18fbd12ec
26 changed files with 250 additions and 85 deletions

View file

@ -358,7 +358,7 @@ PSRETURN CMapSummaryReader::LoadMap(const VfsPath& pathname)
VfsPath filename_xml = pathname.ChangeExtension(L".xml");
CXeromyces xmb_file;
if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK)
if (xmb_file.Load(g_VFS, filename_xml, "scenario") != PSRETURN_OK)
return PSRETURN_File_ReadFailed;
// Define all the relevant elements used in the XML file
@ -467,7 +467,7 @@ void CXMLReader::Init(const VfsPath& xml_filename)
// must only assign once, so do it here
node_idx = entity_idx = 0;
if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK)
if (xmb_file.Load(g_VFS, xml_filename, "scenario") != PSRETURN_OK)
throw PSERROR_File_ReadFailed();
// define the elements and attributes that are frequently used in the XML file,

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,6 +22,7 @@
#include "lib/ogl.h"
#include "maths/MathUtil.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
#include "ps/PreprocessorWrapper.h"
@ -35,6 +36,9 @@ CMaterialManager::CMaterialManager()
qualityLevel = 5.0;
CFG_GET_VAL("materialmgr.quality", qualityLevel);
qualityLevel = clamp(qualityLevel, 0.0f, 10.0f);
if (!CXeromyces::AddValidator(g_VFS, "material", "art/materials/material.rng"))
LOGERROR("CMaterialManager: failed to load grammar file 'art/materials/material.rng'");
}
CMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname)
@ -47,7 +51,7 @@ CMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname)
return iter->second;
CXeromyces xeroFile;
if (xeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
if (xeroFile.Load(g_VFS, pathname, "material") != PSRETURN_OK)
return CMaterial();
#define EL(x) int el_##x = xeroFile.GetElementID(#x)

View file

@ -44,7 +44,7 @@ bool CObjectBase::Load(const VfsPath& pathname)
m_UsedFiles.insert(pathname);
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
if (XeroFile.Load(g_VFS, pathname, "actor") != PSRETURN_OK)
return false;
// Define all the elements used in the XML file

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -25,6 +25,7 @@
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpVisual.h"
@ -62,6 +63,9 @@ CObjectManager::CObjectManager(CMeshManager& meshManager, CSkeletonAnimManager&
: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager), m_Simulation(simulation)
{
RegisterFileReloadFunc(ReloadChangedFileCB, this);
if (!CXeromyces::AddValidator(g_VFS, "actor", "art/actors/actor.rng"))
LOGERROR("CObjectManager: failed to load actor grammar file 'art/actors/actor.rng'");
}
CObjectManager::~CObjectManager()

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2011 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -361,12 +361,10 @@ bool CParticleEmitterType::LoadXML(const VfsPath& path)
m_Texture = g_Renderer.GetTextureManager().GetErrorTexture();
CXeromyces XeroFile;
PSRETURN ret = XeroFile.Load(g_VFS, path);
PSRETURN ret = XeroFile.Load(g_VFS, path, "particle");
if (ret != PSRETURN_OK)
return false;
// TODO: should do some RNG schema validation
// Define all the elements and attributes used in the XML file
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)

View file

@ -28,6 +28,9 @@
#include "ps/Filesystem.h"
#include "ps/PreprocessorWrapper.h"
#include "ps/Profile.h"
#if USE_SHADER_XML_VALIDATION
# include "ps/XML/RelaxNG.h"
#endif
#include "ps/XML/Xeromyces.h"
#include "ps/XML/XMLWriter.h"
#include "renderer/Renderer.h"
@ -47,14 +50,9 @@ CShaderManager::CShaderManager()
#if USE_SHADER_XML_VALIDATION
{
TIMER_ACCRUE(tc_ShaderValidation);
CVFSFile grammar;
if (grammar.Load(g_VFS, L"shaders/program.rng") != PSRETURN_OK)
LOGERROR("Failed to read grammar shaders/program.rng");
else
{
if (!m_Validator.LoadGrammar(grammar.GetAsString()))
LOGERROR("Failed to load grammar shaders/program.rng");
}
if (!CXeromyces::AddValidator(g_VFS, "shader", "shaders/program.rng"))
LOGERROR("CShaderManager: failed to load grammar shaders/program.rng");
}
#endif
@ -141,7 +139,7 @@ bool CShaderManager::NewProgram(const char* name, const CShaderDefines& baseDefi
XML_Start();
XML_SetPrettyPrint(false);
XML_WriteXMB(XeroFile);
bool ok = m_Validator.ValidateEncoded(wstring_from_utf8(name), XML_GetOutput());
bool ok = CXeromyces::ValidateEncoded("shader", wstring_from_utf8(name), XML_GetOutput());
if (!ok)
return false;
}

View file

@ -121,10 +121,6 @@ private:
typedef boost::unordered_map<VfsPath, std::set<std::weak_ptr<CShaderProgram>, std::owner_less<std::weak_ptr<CShaderProgram>>>> HotloadFilesMap;
HotloadFilesMap m_HotloadFiles;
#if USE_SHADER_XML_VALIDATION
RelaxNGValidator m_Validator;
#endif
bool NewProgram(const char* name, const CShaderDefines& defines, CShaderProgramPtr& program);
bool NewEffect(const char* name, const CShaderDefines& defines, CShaderTechniquePtr& tech);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -46,7 +46,7 @@ CTerrainProperties::CTerrainProperties(CTerrainPropertiesPtr parent):
CTerrainPropertiesPtr CTerrainProperties::FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname)
{
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
if (XeroFile.Load(g_VFS, pathname, "terrain") != PSRETURN_OK)
return CTerrainPropertiesPtr();
XMBElement root = XeroFile.GetRoot();

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -45,7 +45,7 @@ CTerrainTextureEntry::CTerrainTextureEntry(CTerrainPropertiesPtr properties, con
ENSURE(properties);
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, path) != PSRETURN_OK)
if (XeroFile.Load(g_VFS, path, "terrain_texture") != PSRETURN_OK)
{
LOGERROR("Terrain xml not found (%s)", path.string8());
return;

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -30,13 +30,19 @@
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/XML/Xeromyces.h"
#include <boost/algorithm/string.hpp>
CTerrainTextureManager::CTerrainTextureManager():
m_LastGroupIndex(0)
{}
{
if (!CXeromyces::AddValidator(g_VFS, "terrain", "art/terrains/terrain.rng"))
LOGERROR("CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain.rng'");
if (!CXeromyces::AddValidator(g_VFS, "terrain_texture", "art/terrains/terrain_texture.rng"))
LOGERROR("CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain_texture.rng'");
}
CTerrainTextureManager::~CTerrainTextureManager()
{

View file

@ -96,7 +96,7 @@ void CTextureConverter::Settings::Hash(MD5& hash)
CTextureConverter::SettingsFile* CTextureConverter::LoadSettings(const VfsPath& path) const
{
CXeromyces XeroFile;
if (XeroFile.Load(m_VFS, path) != PSRETURN_OK)
if (XeroFile.Load(m_VFS, path, "texture") != PSRETURN_OK)
return NULL;
// Define all the elements used in the XML file

View file

@ -929,7 +929,7 @@ void CGUI::LoadXmlFile(const VfsPath& Filename, boost::unordered_set<VfsPath>& P
Paths.insert(Filename);
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, Filename) != PSRETURN_OK)
if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK)
// Fail silently
return;
@ -1315,7 +1315,7 @@ void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObjec
Paths.insert(filename);
CXeromyces XeroIncluded;
if (XeroIncluded.Load(g_VFS, filename) != PSRETURN_OK)
if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK)
{
LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename));
continue;
@ -1352,7 +1352,7 @@ void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObjec
// one might use the same parts of the GUI in different situations
Paths.insert(path);
CXeromyces XeroIncluded;
if (XeroIncluded.Load(g_VFS, path) != PSRETURN_OK)
if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK)
{
LOGERROR("GUI: Error reading included XML: '%s'", path.string8());
continue;

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -59,6 +59,12 @@ CGUIManager::CGUIManager()
m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIManager", m_ScriptRuntime));
m_ScriptInterface->SetCallbackData(this);
m_ScriptInterface->LoadGlobalScripts();
if (!CXeromyces::AddValidator(g_VFS, "gui_page", "gui/gui_page.rng"))
LOGERROR("CGUIManager: failed to load GUI page grammar file 'gui/gui_page.rng'");
if (!CXeromyces::AddValidator(g_VFS, "gui", "gui/gui.rng"))
LOGERROR("CGUIManager: failed to load GUI XML grammar file 'gui/gui.rng'");
RegisterFileReloadFunc(ReloadChangedFileCB, this);
}
@ -192,7 +198,7 @@ void CGUIManager::LoadPage(SGUIPage& page)
page.inputs.insert(path);
CXeromyces xero;
if (xero.Load(g_VFS, path) != PSRETURN_OK)
if (xero.Load(g_VFS, path, "gui_page") != PSRETURN_OK)
// Fail silently (Xeromyces reported the error)
return;

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,6 +38,7 @@
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/ScriptInterface.h"
@ -70,9 +71,12 @@ CMiniMap::CMiniMap() :
m_Clicking = false;
m_MouseHovering = false;
// Register Relax NG validator
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
// Get the maximum height for unit passage in water.
CParamNode externalParamNode;
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses");
if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk())
m_ShallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat();

View file

@ -1074,6 +1074,9 @@ void InitGraphics(const CmdLineArgs& args, int flags)
ogl_WarnIfError();
// TODO: Is this the best place for this?
CXeromyces::AddValidator(g_VFS, "map", "maps/scenario.rng");
try
{
if (!VisualReplay(args.Get("replay-visual")) && !Autostart(args))

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -22,6 +22,7 @@
#include "lib/timer.h"
#include "lib/utf8.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include <libxml/relaxng.h>
#include <map>
@ -33,6 +34,27 @@ TIMER_ADD_CLIENT(xml_validation);
* To minimise that problem, keep a global cache of parsed schemas, so we don't
* leak an indefinitely large amount of memory when repeatedly restarting the simulation.
*/
class RelaxNGSchema;
static std::map<std::string, shared_ptr<RelaxNGSchema> > g_SchemaCache;
static CMutex g_SchemaCacheLock;
void ClearSchemaCache()
{
CScopeLock lock(g_SchemaCacheLock);
g_SchemaCache.clear();
}
static void relaxNGErrorHandler(void* UNUSED(userData), xmlErrorPtr error)
{
// Strip a trailing newline
std::string message = error->message;
if (message.length() > 0 && message[message.length()-1] == '\n')
message.erase(message.length()-1);
LOGERROR("RelaxNGValidator: Validation %s: %s:%d: %s",
error->level == XML_ERR_WARNING ? "warning" : "error",
error->file, error->line, message.c_str());
}
class RelaxNGSchema
{
@ -56,9 +78,6 @@ public:
}
};
static std::map<std::string, shared_ptr<RelaxNGSchema> > g_SchemaCache;
static CMutex g_SchemaCacheLock;
RelaxNGValidator::RelaxNGValidator() :
m_Schema(NULL)
{
@ -70,8 +89,6 @@ RelaxNGValidator::~RelaxNGValidator()
bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
{
TIMER_ACCRUE(xml_validation);
shared_ptr<RelaxNGSchema> schema;
{
@ -89,20 +106,33 @@ bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
}
m_Schema = schema->m_Schema;
if (!m_Schema)
return false;
MD5 hash;
hash.Update((const u8*)grammar.c_str(), grammar.length());
m_Hash = hash;
return true;
}
bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document)
bool RelaxNGValidator::LoadGrammarFile(const PIVFS& vfs, const VfsPath& grammarPath)
{
CVFSFile file;
if (file.Load(vfs, grammarPath) != PSRETURN_OK)
return false;
return LoadGrammar(file.DecodeUTF8());
}
bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document) const
{
std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
return ValidateEncoded(filename, docutf8);
}
bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document)
bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document) const
{
TIMER_ACCRUE(xml_validation);
@ -115,14 +145,21 @@ bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::
xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
if (doc == NULL)
{
LOGERROR("RelaxNGValidator: Failed to parse document");
LOGERROR("RelaxNGValidator: Failed to parse document '%s'", utf8_from_wstring(filename).c_str());
return false;
}
bool ret = ValidateEncoded(doc);
xmlFreeDoc(doc);
return ret;
}
bool RelaxNGValidator::ValidateEncoded(xmlDocPtr doc) const
{
xmlRelaxNGValidCtxtPtr ctxt = xmlRelaxNGNewValidCtxt(m_Schema);
xmlRelaxNGSetValidStructuredErrors(ctxt, &relaxNGErrorHandler, NULL);
int ret = xmlRelaxNGValidateDoc(ctxt, doc);
xmlRelaxNGFreeValidCtxt(ctxt);
xmlFreeDoc(doc);
if (ret == 0)
{
@ -130,7 +167,7 @@ bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::
}
else if (ret > 0)
{
LOGERROR("RelaxNGValidator: Validation failed");
LOGERROR("RelaxNGValidator: Validation failed for '%s'", doc->name);
return false;
}
else
@ -139,3 +176,8 @@ bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::
return false;
}
}
bool RelaxNGValidator::CanValidate() const
{
return m_Schema != NULL;
}

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,8 +18,15 @@
#ifndef INCLUDED_RELAXNG
#define INCLUDED_RELAXNG
#include "lib/file/vfs/vfs.h"
#include "maths/MD5.h"
typedef struct _xmlRelaxNG xmlRelaxNG;
typedef xmlRelaxNG *xmlRelaxNGPtr;
typedef struct _xmlDoc xmlDoc;
typedef xmlDoc *xmlDocPtr;
class IRelaxNGGrammar;
class RelaxNGValidator
{
@ -29,12 +36,26 @@ public:
bool LoadGrammar(const std::string& grammar);
bool Validate(const std::wstring& filename, const std::wstring& document);
bool LoadGrammarFile(const PIVFS& vfs, const VfsPath& grammarPath);
bool ValidateEncoded(const std::wstring& filename, const std::string& document);
MD5 GetGrammarHash() const { return m_Hash; }
bool Validate(const std::wstring& filename, const std::wstring& document) const;
bool ValidateEncoded(const std::wstring& filename, const std::string& document) const;
bool ValidateEncoded(xmlDocPtr doc) const;
bool CanValidate() const;
private:
MD5 m_Hash;
xmlRelaxNGPtr m_Schema;
};
/**
* There should be no references to validators or schemas outside of the cache anymore when calling this.
*/
void ClearSchemaCache();
#endif // INCLUDED_RELAXNG

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,10 +27,15 @@
#include "ps/CacheLoader.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "RelaxNG.h"
#include "Xeromyces.h"
#include <libxml/parser.h>
static CMutex g_ValidatorCacheLock;
static std::map<const std::string, RelaxNGValidator> g_ValidatorCache;
static bool g_XeromycesStarted = false;
static void errorHandler(void* UNUSED(userData), xmlErrorPtr error)
{
// Strip a trailing newline
@ -45,31 +50,76 @@ static void errorHandler(void* UNUSED(userData), xmlErrorPtr error)
// so the caching is less transparent than it should be
}
static bool g_XeromycesStarted = false;
void CXeromyces::Startup()
{
ENSURE(!g_XeromycesStarted);
xmlInitParser();
xmlSetStructuredErrorFunc(NULL, &errorHandler);
CScopeLock lock(g_ValidatorCacheLock);
g_ValidatorCache.insert(std::make_pair("", RelaxNGValidator()));
g_XeromycesStarted = true;
}
void CXeromyces::Terminate()
{
ENSURE(g_XeromycesStarted);
xmlCleanupParser();
xmlSetStructuredErrorFunc(NULL, NULL);
g_XeromycesStarted = false;
ClearSchemaCache();
CScopeLock lock(g_ValidatorCacheLock);
g_ValidatorCache.clear();
xmlSetStructuredErrorFunc(NULL, NULL);
xmlCleanupParser();
}
PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename)
bool CXeromyces::AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath)
{
ENSURE(g_XeromycesStarted);
RelaxNGValidator validator;
if (!validator.LoadGrammarFile(vfs, grammarPath))
{
LOGERROR("CXeromyces: failed adding validator for '%s'", grammarPath.string8());
return false;
}
{
CScopeLock lock(g_ValidatorCacheLock);
std::map<const std::string, RelaxNGValidator>::iterator it = g_ValidatorCache.find(name);
if (it != g_ValidatorCache.end())
g_ValidatorCache.erase(it);
g_ValidatorCache.insert(std::make_pair(name, validator));
}
return true;
}
bool CXeromyces::ValidateEncoded(const std::string& name, const std::wstring& filename, const std::string& document)
{
CScopeLock lock(g_ValidatorCacheLock);
return GetValidator(name).ValidateEncoded(filename, document);
}
/**
* NOTE: Callers MUST acquire the g_ValidatorCacheLock before calling this.
*/
RelaxNGValidator& CXeromyces::GetValidator(const std::string& name)
{
if (g_ValidatorCache.find(name) == g_ValidatorCache.end())
return g_ValidatorCache.find("")->second;
return g_ValidatorCache.find(name)->second;
}
PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName /* = "" */)
{
ENSURE(g_XeromycesStarted);
CCacheLoader cacheLoader(vfs, L".xmb");
MD5 validatorGrammarHash;
{
CScopeLock lock(g_ValidatorCacheLock);
validatorGrammarHash = GetValidator(validatorName).GetGrammarHash();
}
VfsPath xmbPath;
Status ret = cacheLoader.TryLoadingCached(filename, MD5(), XMBVersion, xmbPath);
Status ret = cacheLoader.TryLoadingCached(filename, validatorGrammarHash, XMBVersion, xmbPath);
if (ret == INFO::OK)
{
@ -94,19 +144,19 @@ PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename)
}
// XMB isn't up to date with the XML, so rebuild it
return ConvertFile(vfs, filename, xmbPath);
return ConvertFile(vfs, filename, xmbPath, validatorName);
}
bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath)
bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName /* = "" */)
{
CCacheLoader cacheLoader(vfs, L".xmb");
archiveCachePath = cacheLoader.ArchiveCachePath(sourcePath);
return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath) == PSRETURN_OK);
return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath, validatorName) == PSRETURN_OK);
}
PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath)
PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName)
{
CVFSFile input;
if (input.Load(vfs, filename))
@ -115,15 +165,22 @@ PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, cons
return PSRETURN_Xeromyces_XMLOpenFailed;
}
CStr8 filename8(CStrW(filename.string()).ToUTF8());
xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), (int)input.GetBufferSize(),
filename8.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
if (! doc)
xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), input.GetBufferSize(), CStrW(filename.string()).ToUTF8().c_str(), NULL,
XML_PARSE_NONET|XML_PARSE_NOCDATA);
if (!doc)
{
LOGERROR("CXeromyces: Failed to parse XML file %s", filename.string8());
return PSRETURN_Xeromyces_XMLParseError;
}
{
CScopeLock lock(g_ValidatorCacheLock);
RelaxNGValidator& validator = GetValidator(validatorName);
if (validator.CanValidate() && !validator.ValidateEncoded(doc))
// For now, log the error and continue, in the future we might fail
LOGERROR("CXeromyces: failed to validate XML file %s", filename.string8());
}
WriteBuffer writeBuffer;
CreateXMB(doc, writeBuffer);
@ -160,17 +217,25 @@ bool CXeromyces::ReadXMBFile(const PIVFS& vfs, const VfsPath& filename)
return true;
}
PSRETURN CXeromyces::LoadString(const char* xml)
PSRETURN CXeromyces::LoadString(const char* xml, const std::string& validatorName /* = "" */)
{
ENSURE(g_XeromycesStarted);
xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
if (! doc)
xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "(no file)", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
if (!doc)
{
LOGERROR("CXeromyces: Failed to parse XML string");
return PSRETURN_Xeromyces_XMLParseError;
}
{
CScopeLock lock(g_ValidatorCacheLock);
RelaxNGValidator& validator = GetValidator(validatorName);
if (validator.CanValidate() && !validator.ValidateEncoded(doc))
// For now, log the error and continue, in the future we might fail
LOGERROR("CXeromyces: failed to validate XML string");
}
WriteBuffer writeBuffer;
CreateXMB(doc, writeBuffer);

View file

@ -33,8 +33,8 @@ ERROR_TYPE(Xeromyces, XMLParseError);
#include "lib/file/vfs/vfs.h"
class RelaxNGValidator;
class WriteBuffer;
class MD5;
typedef struct _xmlDoc xmlDoc;
typedef xmlDoc* xmlDocPtr;
@ -46,19 +46,19 @@ public:
/**
* Load from an XML file (with invisible XMB caching).
*/
PSRETURN Load(const PIVFS& vfs, const VfsPath& filename);
PSRETURN Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName = "");
/**
* Load from an in-memory XML string (with no caching).
*/
PSRETURN LoadString(const char* xml);
PSRETURN LoadString(const char* xml, const std::string& validatorName = "");
/**
* Convert the given XML file into an XMB in the archive cache.
* Returns the XMB path in @p archiveCachePath.
* Returns false on error.
*/
bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath);
bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName = "");
/**
* Call once when initialising the program, to load libxml2.
@ -71,8 +71,14 @@ public:
*/
static void Terminate();
static bool AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath);
static bool ValidateEncoded(const std::string& name, const std::wstring& filename, const std::string& document);
private:
PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath);
static RelaxNGValidator& GetValidator(const std::string& name);
PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName);
bool ReadXMBFile(const PIVFS& vfs, const VfsPath& filename);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2010 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -44,7 +44,7 @@ public:
{
TestLogger logger;
TS_ASSERT(!v.Validate(L"doc", L"<bogus/>"));
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Parse error: doc:1: Expecting element test, got bogus");
TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Validation error: doc:1: Expecting element test, got bogus");
}
{

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2013 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -27,6 +27,7 @@
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "ps/Profile.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/Scene.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/components/ICmpObstruction.h"
@ -56,11 +57,14 @@ void CCmpPathfinder::Init(const CParamNode& UNUSED(paramNode))
m_SameTurnMovesCount = 0;
// Register Relax NG validator
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
// Since this is used as a system component (not loaded from an entity template),
// we can't use the real paramNode (it won't get handled properly when deserializing),
// so load the data from a special XML file.
CParamNode externalParamNode;
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
// Previously all move commands during a turn were
// queued up and processed asynchronously at the start

View file

@ -25,6 +25,7 @@
#include "graphics/TextureManager.h"
#include "graphics/TerritoryBoundary.h"
#include "maths/MathUtil.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/Renderer.h"
#include "renderer/Scene.h"
#include "renderer/TerrainOverlay.h"
@ -120,8 +121,11 @@ public:
m_AnimTime = 0.0;
// Register Relax NG validator
CXeromyces::AddValidator(g_VFS, "territorymanager", "simulation/data/territorymanager.rng");
CParamNode externalParamNode;
CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml", "territorymanager");
int impassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
ENSURE(0 <= impassableCost && impassableCost <= 255);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -42,10 +42,10 @@ void CParamNode::LoadXML(CParamNode& ret, const XMBFile& xmb, const wchar_t* sou
ret.ApplyLayer(xmb, xmb.GetRoot(), sourceIdentifier);
}
void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path)
void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName)
{
CXeromyces xero;
PSRETURN ok = xero.Load(g_VFS, path);
PSRETURN ok = xero.Load(g_VFS, path, validatorName);
if (ok != PSRETURN_OK)
return; // (Xeromyces already logged an error)

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Wildfire Games.
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -134,7 +134,7 @@ public:
* Any existing data in @a ret will be overwritten or else kept, so this
* can be called multiple times to build up a node from multiple inputs.
*/
static void LoadXML(CParamNode& ret, const VfsPath& path);
static void LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName);
/**
* See LoadXML, but parses the XML string @a xml.

View file

@ -30,6 +30,7 @@
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
#include "ps/Profiler2.h"
#include "ps/XML/Xeromyces.h"
ISoundManager* g_SoundManager = NULL;
@ -253,6 +254,9 @@ CSoundManager::CSoundManager()
m_PlayListItems = new PlayList;
}
if (!CXeromyces::AddValidator(g_VFS, "sound_group", "audio/sound_group.rng"))
LOGERROR("CSoundManager: failed to load grammar file 'audio/sound_group.rng'");
RegisterFileReloadFunc(ReloadChangedFileCB, this);
}

View file

@ -283,7 +283,7 @@ void CSoundGroup::Update(float UNUSED(TimeSinceLastFrame))
bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
{
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK)
if (XeroFile.Load(g_VFS, pathnameXML, "sound_group") != PSRETURN_OK)
{
HandleError(L"error loading file", pathnameXML, ERR::FAIL);
return false;