Ticket #245: xml_validation.patch

File xml_validation.patch, 29.3 KB (added by historic_bruno, 10 years ago)
  • source/graphics/MapReader.cpp

     
    358358    VfsPath filename_xml = pathname.ChangeExtension(L".xml");
    359359
    360360    CXeromyces xmb_file;
    361     if (xmb_file.Load(g_VFS, filename_xml) != PSRETURN_OK)
     361    if (xmb_file.Load(g_VFS, filename_xml, "scenario") != PSRETURN_OK)
    362362        return PSRETURN_File_ReadFailed;
    363363
    364364    // Define all the relevant elements used in the XML file
     
    463463    // must only assign once, so do it here
    464464    node_idx = entity_idx = 0;
    465465
    466     if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK)
     466    if (xmb_file.Load(g_VFS, xml_filename, "scenario") != PSRETURN_OK)
    467467        throw PSERROR_File_ReadFailed();
    468468
    469469    // define the elements and attributes that are frequently used in the XML file,
  • source/graphics/MaterialManager.cpp

     
    2222#include "lib/ogl.h"
    2323#include "maths/MathUtil.h"
    2424#include "maths/Vector4D.h"
     25#include "ps/CLogger.h"
    2526#include "ps/ConfigDB.h"
    2627#include "ps/Filesystem.h"
    2728#include "ps/PreprocessorWrapper.h"
     
    3536    qualityLevel = 5.0;
    3637    CFG_GET_VAL("materialmgr.quality", Float, qualityLevel);
    3738    qualityLevel = clamp(qualityLevel, 0.0f, 10.0f);
     39
     40    if (!CXeromyces::AddValidator(g_VFS, "material", "art/materials/material.rng"))
     41        LOGERROR(L"CMaterialManager: failed to load grammar file 'art/materials/material.rng'");
    3842}
    3943
    4044CMaterial CMaterialManager::LoadMaterial(const VfsPath& pathname)
     
    4751        return iter->second;
    4852
    4953    CXeromyces xeroFile;
    50     if (xeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
     54    if (xeroFile.Load(g_VFS, pathname, "material") != PSRETURN_OK)
    5155        return CMaterial();
    5256
    5357    #define EL(x) int el_##x = xeroFile.GetElementID(#x)
  • source/graphics/ObjectBase.cpp

     
    4444    m_UsedFiles.insert(pathname);
    4545
    4646    CXeromyces XeroFile;
    47     if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
     47    if (XeroFile.Load(g_VFS, pathname, "actor") != PSRETURN_OK)
    4848        return false;
    4949
    5050    // Define all the elements used in the XML file
  • source/graphics/ObjectManager.cpp

     
    2525#include "ps/Game.h"
    2626#include "ps/Profile.h"
    2727#include "ps/Filesystem.h"
     28#include "ps/XML/Xeromyces.h"
    2829#include "simulation2/Simulation2.h"
    2930#include "simulation2/components/ICmpTerrain.h"
    3031#include "simulation2/components/ICmpVisual.h"
     
    6263: m_MeshManager(meshManager), m_SkeletonAnimManager(skeletonAnimManager), m_Simulation(simulation)
    6364{
    6465    RegisterFileReloadFunc(ReloadChangedFileCB, this);
     66
     67    if (!CXeromyces::AddValidator(g_VFS, "actor", "art/actors/actor.rng"))
     68        LOGERROR(L"CObjectManager: failed to load actor grammar file 'art/actors/actor.rng'");
    6569}
    6670
    6771CObjectManager::~CObjectManager()
  • source/graphics/ParticleEmitterType.cpp

     
    361361    m_Texture = g_Renderer.GetTextureManager().GetErrorTexture();
    362362
    363363    CXeromyces XeroFile;
    364     PSRETURN ret = XeroFile.Load(g_VFS, path);
     364    PSRETURN ret = XeroFile.Load(g_VFS, path, "particle");
    365365    if (ret != PSRETURN_OK)
    366366        return false;
    367367
    368     // TODO: should do some RNG schema validation
    369 
    370368    // Define all the elements and attributes used in the XML file
    371369#define EL(x) int el_##x = XeroFile.GetElementID(#x)
    372370#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
  • source/graphics/ShaderManager.cpp

     
    2828#include "ps/Filesystem.h"
    2929#include "ps/PreprocessorWrapper.h"
    3030#include "ps/Profile.h"
     31#if USE_SHADER_XML_VALIDATION
     32# include "ps/XML/RelaxNG.h"
     33#endif
    3134#include "ps/XML/Xeromyces.h"
    3235#include "ps/XML/XMLWriter.h"
    3336#include "renderer/Renderer.h"
     
    4750#if USE_SHADER_XML_VALIDATION
    4851    {
    4952        TIMER_ACCRUE(tc_ShaderValidation);
    50         CVFSFile grammar;
    51         if (grammar.Load(g_VFS, L"shaders/program.rng") != PSRETURN_OK)
    52             LOGERROR(L"Failed to read grammar shaders/program.rng");
    53         else
    54         {
    55             if (!m_Validator.LoadGrammar(grammar.GetAsString()))
    56                 LOGERROR(L"Failed to load grammar shaders/program.rng");
    57         }
     53       
     54        if (!CXeromyces::AddValidator(g_VFS, "shader", "shaders/program.rng"))
     55            LOGERROR(L"CShaderManager: failed to load grammar shaders/program.rng");
    5856    }
    5957#endif
    6058
     
    141139        XML_Start();
    142140        XML_SetPrettyPrint(false);
    143141        XML_WriteXMB(XeroFile);
    144         bool ok = m_Validator.ValidateEncoded(wstring_from_utf8(name), XML_GetOutput());
     142        bool ok = CXeromyces::GetValidator("shader").ValidateEncoded(wstring_from_utf8(name), XML_GetOutput());
    145143        if (!ok)
    146144            return false;
    147145    }
  • source/graphics/ShaderManager.h

     
    2727#include "graphics/ShaderProgram.h"
    2828#include "graphics/ShaderTechnique.h"
    2929
    30 #if USE_SHADER_XML_VALIDATION
    31 # include "ps/XML/RelaxNG.h"
    32 #endif
    33 
    3430#include <set>
    3531
    3632/**
     
    121117    typedef boost::unordered_map<VfsPath, std::set<boost::weak_ptr<CShaderProgram> > > HotloadFilesMap;
    122118    HotloadFilesMap m_HotloadFiles;
    123119
    124 #if USE_SHADER_XML_VALIDATION
    125     RelaxNGValidator m_Validator;
    126 #endif
    127 
    128120    bool NewProgram(const char* name, const CShaderDefines& defines, CShaderProgramPtr& program);
    129121    bool NewEffect(const char* name, const CShaderDefines& defines, CShaderTechniquePtr& tech);
    130122
  • source/graphics/TerrainProperties.cpp

     
    4848CTerrainPropertiesPtr CTerrainProperties::FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname)
    4949{
    5050    CXeromyces XeroFile;
    51     if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
     51    if (XeroFile.Load(g_VFS, pathname, "terrain") != PSRETURN_OK)
    5252        return CTerrainPropertiesPtr();
    5353
    5454    XMBElement root = XeroFile.GetRoot();
  • source/graphics/TerrainTextureEntry.cpp

     
    4545    ENSURE(properties);
    4646   
    4747    CXeromyces XeroFile;
    48     if (XeroFile.Load(g_VFS, path) != PSRETURN_OK)
     48    if (XeroFile.Load(g_VFS, path, "terrain_texture") != PSRETURN_OK)
    4949    {
    5050        LOGERROR(L"Terrain xml not found (%hs)", path.string().c_str());
    5151        return;
  • source/graphics/TerrainTextureManager.cpp

     
    3030
    3131#include "ps/CLogger.h"
    3232#include "ps/Filesystem.h"
     33#include "ps/XML/Xeromyces.h"
    3334
    3435// Disable "'boost::algorithm::detail::is_classifiedF' : assignment operator could not be generated"
    3536// and "find_format_store.hpp(74) : warning C4100: 'Input' : unreferenced formal parameter"
     
    4344
    4445CTerrainTextureManager::CTerrainTextureManager():
    4546    m_LastGroupIndex(0)
    46 {}
     47{
     48    if (!CXeromyces::AddValidator(g_VFS, "terrain", "art/terrains/terrain.rng"))
     49        LOGERROR(L"CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain.rng'");
     50    if (!CXeromyces::AddValidator(g_VFS, "terrain_texture", "art/terrains/terrain_texture.rng"))
     51        LOGERROR(L"CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain_texture.rng'");
     52}
    4753
    4854CTerrainTextureManager::~CTerrainTextureManager()
    4955{
  • source/graphics/TextureConverter.cpp

     
    9696CTextureConverter::SettingsFile* CTextureConverter::LoadSettings(const VfsPath& path) const
    9797{
    9898    CXeromyces XeroFile;
    99     if (XeroFile.Load(m_VFS, path) != PSRETURN_OK)
     99    if (XeroFile.Load(m_VFS, path, "texture") != PSRETURN_OK)
    100100        return NULL;
    101101
    102102    // Define all the elements used in the XML file
  • source/gui/CGUI.cpp

     
    923923    Paths.insert(Filename);
    924924
    925925    CXeromyces XeroFile;
    926     if (XeroFile.Load(g_VFS, Filename) != PSRETURN_OK)
     926    if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK)
    927927        // Fail silently
    928928        return;
    929929
     
    13471347                if (!directory.empty())
    13481348                    LOGWARNING(L"GUI: Include element found with file name (%ls) and directory name (%ls). Only the file will be processed.", filename.c_str(), directory.c_str());
    13491349
    1350                 Paths.insert(filename);
    1351 
    13521350                CXeromyces XeroIncluded;
    1353                 if (XeroIncluded.Load(g_VFS, filename) != PSRETURN_OK)
     1351                if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK)
    13541352                {
    13551353                    LOGERROR(L"GUI: Error reading included XML: '%ls'", filename.c_str());
    13561354                    continue;
     
    13741372                    // one might use the same parts of the GUI in different situations
    13751373                    Paths.insert(*it);
    13761374                    CXeromyces XeroIncluded;
    1377                     if (XeroIncluded.Load(g_VFS, *it) != PSRETURN_OK)
     1375                    if (XeroIncluded.Load(g_VFS, *it, "gui") != PSRETURN_OK)
    13781376                    {
    13791377                        LOGERROR(L"GUI: Error reading included XML: '%ls'", (*it).string().c_str());
    13801378                        continue;
  • source/gui/GUIManager.cpp

     
    5858    m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIManager", m_ScriptRuntime));
    5959    m_ScriptInterface->SetCallbackData(this);
    6060    m_ScriptInterface->LoadGlobalScripts();
     61
     62    if (!CXeromyces::AddValidator(g_VFS, "gui_page", "gui/gui_page.rng"))
     63        LOGERROR(L"CGUIManager: failed to load GUI page grammar file 'gui/gui_page.rng'");
     64    if (!CXeromyces::AddValidator(g_VFS, "gui", "gui/gui.rng"))
     65        LOGERROR(L"CGUIManager: failed to load GUI XML grammar file 'gui/gui.rng'");
     66
    6167    RegisterFileReloadFunc(ReloadChangedFileCB, this);
    6268}
    6369
     
    183189    page.inputs.insert(path);
    184190
    185191    CXeromyces xero;
    186     if (xero.Load(g_VFS, path) != PSRETURN_OK)
     192    if (xero.Load(g_VFS, path, "gui_page") != PSRETURN_OK)
    187193        // Fail silently (Xeromyces reported the error)
    188194        return;
    189195
  • source/gui/MiniMap.cpp

     
    3838#include "ps/Game.h"
    3939#include "ps/Profile.h"
    4040#include "ps/World.h"
     41#include "ps/XML/Xeromyces.h"
    4142#include "renderer/Renderer.h"
    4243#include "renderer/WaterManager.h"
    4344#include "scriptinterface/ScriptInterface.h"
     
    7071    m_Clicking = false;
    7172    m_MouseHovering = false;
    7273   
     74    // Register Relax NG validator
     75    CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
     76
    7377    // Get the maximum height for unit passage in water.
    7478    CParamNode externalParamNode;
    75     CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
     79    CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
    7680    const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses");
    7781    if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk())
    7882        m_ShallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat();
  • source/ps/GameSetup/GameSetup.cpp

     
    10141014
    10151015    ogl_WarnIfError();
    10161016
     1017    // TODO: where should this go?
     1018    CXeromyces::AddValidator(g_VFS, "map", "maps/scenario.rng");
     1019
    10171020    try
    10181021    {
    10191022        if (!Autostart(args))
  • source/ps/XML/RelaxNG.cpp

     
    2222#include "lib/timer.h"
    2323#include "lib/utf8.h"
    2424#include "ps/CLogger.h"
     25#include "ps/Filesystem.h"
    2526
    2627#include <libxml/relaxng.h>
    2728#include <map>
     
    2829
    2930TIMER_ADD_CLIENT(xml_validation);
    3031
     32static void relaxNGErrorHandler(void* UNUSED(userData), xmlErrorPtr error)
     33{
     34    // Strip a trailing newline
     35    std::string message = error->message;
     36    if (message.length() > 0 && message[message.length()-1] == '\n')
     37        message.erase(message.length()-1);
     38
     39    LOGERROR(L"RelaxNGValidator: Validation %ls: %hs:%d: %hs",
     40        error->level == XML_ERR_WARNING ? L"warning" : L"error",
     41        error->file, error->line, message.c_str());
     42}
     43
    3144/*
    3245 * libxml2 leaks memory when parsing schemas: https://bugzilla.gnome.org/show_bug.cgi?id=615767
    3346 * To minimise that problem, keep a global cache of parsed schemas, so we don't
     
    7083
    7184bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
    7285{
    73     TIMER_ACCRUE(xml_validation);
    74 
    7586    shared_ptr<RelaxNGSchema> schema;
    7687
    7788    {
     
    89100    }
    90101
    91102    m_Schema = schema->m_Schema;
    92 
    93103    if (!m_Schema)
    94104        return false;
     105
     106    MD5 hash;
     107    hash.Update((const u8*)grammar.c_str(), grammar.length());
     108    m_Hash = hash;
     109
    95110    return true;
    96111}
    97112
    98 bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document)
     113bool RelaxNGValidator::LoadGrammarFile(const PIVFS& vfs, const VfsPath& grammarPath)
    99114{
     115    CVFSFile file;
     116    if (file.Load(vfs, grammarPath) != PSRETURN_OK)
     117        return false;
     118
     119    return LoadGrammar(file.DecodeUTF8());
     120}
     121
     122bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document) const
     123{
    100124    std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
    101125
    102126    return ValidateEncoded(filename, docutf8);
    103127}
    104128
    105 bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document)
     129bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document) const
    106130{
    107131    TIMER_ACCRUE(xml_validation);
    108132
     
    115139    xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
    116140    if (doc == NULL)
    117141    {
    118         LOGERROR(L"RelaxNGValidator: Failed to parse document");
     142        LOGERROR(L"RelaxNGValidator: Failed to parse document '%ls'", filename.c_str());
    119143        return false;
    120144    }
    121145
     146    bool ret = ValidateEncoded(doc);
     147    xmlFreeDoc(doc);
     148    return ret;
     149}
     150
     151bool RelaxNGValidator::ValidateEncoded(xmlDocPtr doc) const
     152{
    122153    xmlRelaxNGValidCtxtPtr ctxt = xmlRelaxNGNewValidCtxt(m_Schema);
     154    xmlRelaxNGSetValidStructuredErrors(ctxt, &relaxNGErrorHandler, NULL);
    123155    int ret = xmlRelaxNGValidateDoc(ctxt, doc);
    124156    xmlRelaxNGFreeValidCtxt(ctxt);
    125     xmlFreeDoc(doc);
    126 
     157   
    127158    if (ret == 0)
    128159    {
    129160        return true;
     
    130161    }
    131162    else if (ret > 0)
    132163    {
    133         LOGERROR(L"RelaxNGValidator: Validation failed");
     164        LOGERROR(L"RelaxNGValidator: Validation failed for '%hs'", doc->name);
    134165        return false;
    135166    }
    136167    else
     
    139170        return false;
    140171    }
    141172}
     173
     174bool RelaxNGValidator::CanValidate() const
     175{
     176    return m_Schema != NULL;
     177}
  • source/ps/XML/RelaxNG.h

     
    1818#ifndef INCLUDED_RELAXNG
    1919#define INCLUDED_RELAXNG
    2020
     21#include "lib/file/vfs/vfs.h"
     22#include "maths/MD5.h"
     23
    2124typedef struct _xmlRelaxNG xmlRelaxNG;
    2225typedef xmlRelaxNG *xmlRelaxNGPtr;
     26typedef struct _xmlDoc xmlDoc;
     27typedef xmlDoc *xmlDocPtr;
    2328
     29class IRelaxNGGrammar;
     30
    2431class RelaxNGValidator
    2532{
    2633public:
     
    2936
    3037    bool LoadGrammar(const std::string& grammar);
    3138
    32     bool Validate(const std::wstring& filename, const std::wstring& document);
     39    bool LoadGrammarFile(const PIVFS& vfs, const VfsPath& grammarPath);
    3340
    34     bool ValidateEncoded(const std::wstring& filename, const std::string& document);
     41    MD5 GetGrammarHash() const { return m_Hash; }
    3542
     43    bool Validate(const std::wstring& filename, const std::wstring& document) const;
     44
     45    bool ValidateEncoded(const std::wstring& filename, const std::string& document) const;
     46
     47    bool ValidateEncoded(xmlDocPtr doc) const;
     48
     49    bool CanValidate() const;
     50
    3651private:
     52    MD5 m_Hash;
    3753    xmlRelaxNGPtr m_Schema;
    3854};
    3955
  • source/ps/XML/tests/test_RelaxNG.h

     
    4444        {
    4545            TestLogger logger;
    4646            TS_ASSERT(!v.Validate(L"doc", L"<bogus/>"));
    47             TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"Parse error: doc:1: Expecting element test, got bogus");
     47            TS_ASSERT_WSTR_CONTAINS(logger.GetOutput(), L"Validation error: doc:1: Expecting element test, got bogus");
    4848        }
    4949
    5050        {
  • source/ps/XML/Xeromyces.cpp

     
    2727#include "ps/CacheLoader.h"
    2828#include "ps/CLogger.h"
    2929#include "ps/Filesystem.h"
     30#include "RelaxNG.h"
    3031#include "Xeromyces.h"
    3132
    3233#include <libxml/parser.h>
     
    4546    // so the caching is less transparent than it should be
    4647}
    4748
     49static CMutex g_ValidatorCacheLock;
     50static std::map<const std::string, RelaxNGValidator> g_ValidatorCache;
    4851static bool g_XeromycesStarted = false;
    4952void CXeromyces::Startup()
    5053{
     
    5255    xmlInitParser();
    5356    xmlSetStructuredErrorFunc(NULL, &errorHandler);
    5457    g_XeromycesStarted = true;
     58
     59    g_ValidatorCache.insert(std::make_pair("", RelaxNGValidator()));
    5560}
    5661
    5762void CXeromyces::Terminate()
     
    5964    ENSURE(g_XeromycesStarted);
    6065    xmlCleanupParser();
    6166    xmlSetStructuredErrorFunc(NULL, NULL);
     67    g_ValidatorCache.clear();
    6268    g_XeromycesStarted = false;
    6369}
    6470
     71bool CXeromyces::AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath)
     72{
     73    ENSURE(g_XeromycesStarted);
     74   
     75    RelaxNGValidator validator;
     76    if (!validator.LoadGrammarFile(vfs, grammarPath))
     77    {
     78        LOGERROR(L"CXeromyces: failed adding validator for '%ls'", grammarPath.string().c_str());
     79        return false;
     80    }
     81    {
     82        CScopeLock lock(g_ValidatorCacheLock);
     83        std::map<const std::string, RelaxNGValidator>::iterator it = g_ValidatorCache.find(name);
     84        if (it != g_ValidatorCache.end())
     85            g_ValidatorCache.erase(it);
     86        g_ValidatorCache.insert(std::make_pair(name, validator));
     87    }
     88    return true;
     89}
     90
     91RelaxNGValidator& CXeromyces::GetValidator(const std::string &name)
     92{
     93    CScopeLock lock(g_ValidatorCacheLock);
     94    if (g_ValidatorCache.find(name) == g_ValidatorCache.end())
     95        return g_ValidatorCache.find("")->second;
     96    return g_ValidatorCache.find(name)->second;
     97}
     98
    6599PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename)
    66100{
     101    return Load(vfs, filename, "");
     102}
     103
     104PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName)
     105{
    67106    ENSURE(g_XeromycesStarted);
    68107
    69108    CCacheLoader cacheLoader(vfs, L".xmb");
    70109
     110    RelaxNGValidator& validator = GetValidator(validatorName);
    71111    VfsPath xmbPath;
    72     Status ret = cacheLoader.TryLoadingCached(filename, MD5(), XMBVersion, xmbPath);
     112    Status ret = cacheLoader.TryLoadingCached(filename, validator.GetGrammarHash(), XMBVersion, xmbPath);
    73113
    74114    if (ret == INFO::OK)
    75115    {
     
    94134    }
    95135
    96136    // XMB isn't up to date with the XML, so rebuild it
    97     return ConvertFile(vfs, filename, xmbPath);
     137    return ConvertFile(vfs, filename, xmbPath, validatorName);
    98138}
    99139
    100140bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath)
    101141{
     142    return GenerateCachedXMB(vfs, sourcePath, archiveCachePath, "");
     143}
     144
     145bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName)
     146{
    102147    CCacheLoader cacheLoader(vfs, L".xmb");
    103148
    104149    archiveCachePath = cacheLoader.ArchiveCachePath(sourcePath);
    105150
    106     return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath) == PSRETURN_OK);
     151    return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath, validatorName) == PSRETURN_OK);
    107152}
    108153
    109 PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath)
     154PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName)
    110155{
    111156    CVFSFile input;
    112157    if (input.Load(vfs, filename))
     
    115160        return PSRETURN_Xeromyces_XMLOpenFailed;
    116161    }
    117162
    118     CStr8 filename8(CStrW(filename.string()).ToUTF8());
    119     xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), (int)input.GetBufferSize(),
    120         filename8.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
    121     if (! doc)
     163    xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), input.GetBufferSize(), CStrW(filename.string()).ToUTF8().c_str(), NULL,
     164        XML_PARSE_NONET|XML_PARSE_NOCDATA);
     165    if (!doc)
    122166    {
    123167        LOGERROR(L"CXeromyces: Failed to parse XML file %ls", filename.string().c_str());
    124168        return PSRETURN_Xeromyces_XMLParseError;
    125169    }
    126170
     171    RelaxNGValidator& validator = GetValidator(validatorName);
     172    if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     173    {
     174        // For now, log the error and continue, in the future we might fail
     175        LOGERROR(L"CXeromyces: failed to validate XML file %ls", filename.string().c_str());
     176    }
     177
    127178    WriteBuffer writeBuffer;
    128179    CreateXMB(doc, writeBuffer);
    129180
     
    162213
    163214PSRETURN CXeromyces::LoadString(const char* xml)
    164215{
     216    return LoadString(xml, "");
     217}
     218
     219PSRETURN CXeromyces::LoadString(const char* xml, const std::string& validatorName)
     220{
    165221    ENSURE(g_XeromycesStarted);
    166222
    167     xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
    168     if (! doc)
     223    xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "(no file)", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
     224    if (!doc)
    169225    {
    170226        LOGERROR(L"CXeromyces: Failed to parse XML string");
    171227        return PSRETURN_Xeromyces_XMLParseError;
    172228    }
    173229
     230    RelaxNGValidator& validator = GetValidator(validatorName);
     231    if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     232    {
     233        // For now, log the error and continue, in the future we might fail
     234        LOGERROR(L"CXeromyces: failed to validate XML string");
     235    }
     236
    174237    WriteBuffer writeBuffer;
    175238    CreateXMB(doc, writeBuffer);
    176239
  • source/ps/XML/Xeromyces.h

     
    3333
    3434#include "lib/file/vfs/vfs.h"
    3535
     36class RelaxNGValidator;
    3637class WriteBuffer;
    37 class MD5;
    3838
    3939typedef struct _xmlDoc xmlDoc;
    4040typedef xmlDoc* xmlDocPtr;
     
    4747     * Load from an XML file (with invisible XMB caching).
    4848     */
    4949    PSRETURN Load(const PIVFS& vfs, const VfsPath& filename);
     50    PSRETURN Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName);
    5051
    5152    /**
    5253     * Load from an in-memory XML string (with no caching).
    5354     */
    5455    PSRETURN LoadString(const char* xml);
     56    PSRETURN LoadString(const char* xml, const std::string& validatorName);
    5557
    5658    /**
    5759     * Convert the given XML file into an XMB in the archive cache.
     
    5961     * Returns false on error.
    6062     */
    6163    bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath);
     64    bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName);
    6265
    6366    /**
    6467     * Call once when initialising the program, to load libxml2.
     
    7174     */
    7275    static void Terminate();
    7376
     77    static bool AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath);
     78
     79    static RelaxNGValidator& GetValidator(const std::string& name);
     80
    7481private:
    75     PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath);
     82    PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName);
    7683
    7784    bool ReadXMBFile(const PIVFS& vfs, const VfsPath& filename);
    7885
  • source/simulation2/components/CCmpPathfinder.cpp

     
    2727#include "ps/CLogger.h"
    2828#include "ps/CStr.h"
    2929#include "ps/Profile.h"
     30#include "ps/XML/Xeromyces.h"
    3031#include "renderer/Scene.h"
    3132#include "simulation2/MessageTypes.h"
    3233#include "simulation2/components/ICmpObstruction.h"
     
    5657
    5758    m_SameTurnMovesCount = 0;
    5859
     60    // Register Relax NG validator
     61    CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
     62
    5963    // Since this is used as a system component (not loaded from an entity template),
    6064    // we can't use the real paramNode (it won't get handled properly when deserializing),
    6165    // so load the data from a special XML file.
    6266    CParamNode externalParamNode;
    63     CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml");
     67    CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
    6468
    6569    // Previously all move commands during a turn were
    6670    // queued up and processed asynchronously at the start
  • source/simulation2/components/CCmpTerritoryManager.cpp

     
    2727#include "maths/MathUtil.h"
    2828#include "maths/Vector2D.h"
    2929#include "ps/Overlay.h"
     30#include "ps/XML/Xeromyces.h"
    3031#include "renderer/Renderer.h"
    3132#include "renderer/Scene.h"
    3233#include "renderer/TerrainOverlay.h"
     
    123124
    124125        m_AnimTime = 0.0;
    125126
     127        // Register Relax NG validator
     128        CXeromyces::AddValidator(g_VFS, "territorymanager", "simulation/data/territorymanager.rng");
     129
    126130        CParamNode externalParamNode;
    127         CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
     131        CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml", "territorymanager");
    128132
    129133        int impassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
    130134        ENSURE(0 <= impassableCost && impassableCost <= 255);
  • source/simulation2/system/ParamNode.cpp

     
    4747    ret.ApplyLayer(xmb, xmb.GetRoot(), sourceIdentifier);
    4848}
    4949
    50 void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path)
     50void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName)
    5151{
    5252    CXeromyces xero;
    53     PSRETURN ok = xero.Load(g_VFS, path);
     53    PSRETURN ok = xero.Load(g_VFS, path, validatorName);
    5454    if (ok != PSRETURN_OK)
    5555        return; // (Xeromyces already logged an error)
    5656
  • source/simulation2/system/ParamNode.h

     
    134134     * Any existing data in @a ret will be overwritten or else kept, so this
    135135     * can be called multiple times to build up a node from multiple inputs.
    136136     */
    137     static void LoadXML(CParamNode& ret, const VfsPath& path);
     137    static void LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName);
    138138
    139139    /**
    140140     * See LoadXML, but parses the XML string @a xml.
  • source/soundmanager/scripting/SoundGroup.cpp

     
    283283bool CSoundGroup::LoadSoundGroup(const VfsPath& pathnameXML)
    284284{
    285285    CXeromyces XeroFile;
    286     if (XeroFile.Load(g_VFS, pathnameXML) != PSRETURN_OK)
     286    if (XeroFile.Load(g_VFS, pathnameXML, "sound_group") != PSRETURN_OK)
    287287    {
    288288        HandleError(L"error loading file", pathnameXML, ERR::FAIL);
    289289        return false;
  • source/soundmanager/SoundManager.cpp

     
    2929#include "ps/CStr.h"
    3030#include "ps/Filesystem.h"
    3131#include "ps/Profiler2.h"
     32#include "ps/XML/Xeromyces.h"
    3233
    3334ISoundManager* g_SoundManager = NULL;
    3435
     
    266267        m_PlayListItems = new PlayList;
    267268    }
    268269
     270    if (!CXeromyces::AddValidator(g_VFS, "sound_group", "audio/sound_group.rng"))
     271        LOGERROR(L"CSoundManager: failed to load grammar file 'audio/sound_group.rng'");
     272
    269273    RegisterFileReloadFunc(ReloadChangedFileCB, this);
    270274}
    271275