mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
XML validation. Based on patch by historic_bruno. Refs #245.
This was SVN commit r16733.
This commit is contained in:
parent
f260e75499
commit
a18fbd12ec
26 changed files with 250 additions and 85 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue