Ticket #245: xml_validation_r16661.patch

File xml_validation_r16661.patch, 34.0 KB (added by leper, 9 years ago)
  • source/graphics/MapReader.cpp

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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
     
    468468    // must only assign once, so do it here
    469469    node_idx = entity_idx = 0;
    470470
    471     if (xmb_file.Load(g_VFS, xml_filename) != PSRETURN_OK)
     471    if (xmb_file.Load(g_VFS, xml_filename, "scenario") != PSRETURN_OK)
    472472        throw PSERROR_File_ReadFailed();
    473473
    474474    // define the elements and attributes that are frequently used in the XML file,
  • source/graphics/MaterialManager.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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", qualityLevel);
    3738    qualityLevel = clamp(qualityLevel, 0.0f, 10.0f);
     39
     40    if (!CXeromyces::AddValidator(g_VFS, "material", "art/materials/material.rng"))
     41        LOGERROR("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

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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("CObjectManager: failed to load actor grammar file 'art/actors/actor.rng'");
    6569}
    6670
    6771CObjectManager::~CObjectManager()
  • source/graphics/ParticleEmitterType.cpp

     
    1 /* Copyright (C) 2011 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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("Failed to read grammar shaders/program.rng");
    53         else
    54         {
    55             if (!m_Validator.LoadGrammar(grammar.GetAsString()))
    56                 LOGERROR("Failed to load grammar shaders/program.rng");
    57         }
     53
     54        if (!CXeromyces::AddValidator(g_VFS, "shader", "shaders/program.rng"))
     55            LOGERROR("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::ValidateEncoded("shader", wstring_from_utf8(name), XML_GetOutput());
    145143        if (!ok)
    146144            return false;
    147145    }
  • source/graphics/ShaderManager.h

     
    121121    typedef boost::unordered_map<VfsPath, std::set<std::weak_ptr<CShaderProgram>, std::owner_less<std::weak_ptr<CShaderProgram>>>> HotloadFilesMap;
    122122    HotloadFilesMap m_HotloadFiles;
    123123
    124 #if USE_SHADER_XML_VALIDATION
    125     RelaxNGValidator m_Validator;
    126 #endif
    127 
    128124    bool NewProgram(const char* name, const CShaderDefines& defines, CShaderProgramPtr& program);
    129125    bool NewEffect(const char* name, const CShaderDefines& defines, CShaderTechniquePtr& tech);
    130126
  • source/graphics/TerrainProperties.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    4646CTerrainPropertiesPtr CTerrainProperties::FromXML(const CTerrainPropertiesPtr& parent, const VfsPath& pathname)
    4747{
    4848    CXeromyces XeroFile;
    49     if (XeroFile.Load(g_VFS, pathname) != PSRETURN_OK)
     49    if (XeroFile.Load(g_VFS, pathname, "terrain") != PSRETURN_OK)
    5050        return CTerrainPropertiesPtr();
    5151
    5252    XMBElement root = XeroFile.GetRoot();
  • source/graphics/TerrainTextureEntry.cpp

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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("Terrain xml not found (%s)", path.string8());
    5151        return;
  • source/graphics/TerrainTextureManager.cpp

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    3030
    3131#include "ps/CLogger.h"
    3232#include "ps/Filesystem.h"
     33#include "ps/XML/Xeromyces.h"
    3334
    3435#include <boost/algorithm/string.hpp>
    3536
    3637
    3738CTerrainTextureManager::CTerrainTextureManager():
    3839    m_LastGroupIndex(0)
    39 {}
     40{
     41    if (!CXeromyces::AddValidator(g_VFS, "terrain", "art/terrains/terrain.rng"))
     42        LOGERROR("CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain.rng'");
     43    if (!CXeromyces::AddValidator(g_VFS, "terrain_texture", "art/terrains/terrain_texture.rng"))
     44        LOGERROR("CTerrainTextureManager: failed to load grammar file 'art/terrains/terrain_texture.rng'");
     45}
    4046
    4147CTerrainTextureManager::~CTerrainTextureManager()
    4248{
  • 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

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    929929    Paths.insert(Filename);
    930930
    931931    CXeromyces XeroFile;
    932     if (XeroFile.Load(g_VFS, Filename) != PSRETURN_OK)
     932    if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK)
    933933        // Fail silently
    934934        return;
    935935
     
    13531353                if (!directory.empty())
    13541354                    LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory));
    13551355
    1356                 Paths.insert(filename);
    1357 
    13581356                CXeromyces XeroIncluded;
    1359                 if (XeroIncluded.Load(g_VFS, filename) != PSRETURN_OK)
     1357                if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK)
    13601358                {
    13611359                    LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename));
    13621360                    continue;
     
    13801378                    // one might use the same parts of the GUI in different situations
    13811379                    Paths.insert(*it);
    13821380                    CXeromyces XeroIncluded;
    1383                     if (XeroIncluded.Load(g_VFS, *it) != PSRETURN_OK)
     1381                    if (XeroIncluded.Load(g_VFS, *it, "gui") != PSRETURN_OK)
    13841382                    {
    13851383                        LOGERROR("GUI: Error reading included XML: '%s'", (*it).string8());
    13861384                        continue;
  • source/gui/GUIManager.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    5959    m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIManager", m_ScriptRuntime));
    6060    m_ScriptInterface->SetCallbackData(this);
    6161    m_ScriptInterface->LoadGlobalScripts();
     62
     63    if (!CXeromyces::AddValidator(g_VFS, "gui_page", "gui/gui_page.rng"))
     64        LOGERROR("CGUIManager: failed to load GUI page grammar file 'gui/gui_page.rng'");
     65    if (!CXeromyces::AddValidator(g_VFS, "gui", "gui/gui.rng"))
     66        LOGERROR("CGUIManager: failed to load GUI XML grammar file 'gui/gui.rng'");
     67
    6268    RegisterFileReloadFunc(ReloadChangedFileCB, this);
    6369}
    6470
     
    192198    page.inputs.insert(path);
    193199
    194200    CXeromyces xero;
    195     if (xero.Load(g_VFS, path) != PSRETURN_OK)
     201    if (xero.Load(g_VFS, path, "gui_page") != PSRETURN_OK)
    196202        // Fail silently (Xeromyces reported the error)
    197203        return;
    198204
  • source/gui/MiniMap.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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

     
    10711071
    10721072    ogl_WarnIfError();
    10731073
     1074    // TODO: where should this go?
     1075    CXeromyces::AddValidator(g_VFS, "map", "maps/scenario.rng");
     1076
    10741077    try
    10751078    {
    10761079        if (!Autostart(args))
  • source/ps/XML/RelaxNG.cpp

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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>
     
    3334 * To minimise that problem, keep a global cache of parsed schemas, so we don't
    3435 * leak an indefinitely large amount of memory when repeatedly restarting the simulation.
    3536 */
     37class RelaxNGSchema;
     38static std::map<std::string, shared_ptr<RelaxNGSchema> > g_SchemaCache;
     39static CMutex g_SchemaCacheLock;
     40
     41void ClearSchemaCache()
     42{
     43    CScopeLock lock(g_SchemaCacheLock);
     44    g_SchemaCache.clear();
     45}
     46
     47static void relaxNGErrorHandler(void* UNUSED(userData), xmlErrorPtr error)
     48{
     49    // Strip a trailing newline
     50    std::string message = error->message;
     51    if (message.length() > 0 && message[message.length()-1] == '\n')
     52        message.erase(message.length()-1);
     53
     54    LOGERROR("RelaxNGValidator: Validation %s: %s:%d: %s",
     55        error->level == XML_ERR_WARNING ? "warning" : "error",
     56        error->file, error->line, message.c_str());
     57}
    3658
    3759class RelaxNGSchema
    3860{
     
    5678    }
    5779};
    5880
    59 static std::map<std::string, shared_ptr<RelaxNGSchema> > g_SchemaCache;
    60 static CMutex g_SchemaCacheLock;
    61 
    6281RelaxNGValidator::RelaxNGValidator() :
    6382    m_Schema(NULL)
    6483{
     
    7089
    7190bool RelaxNGValidator::LoadGrammar(const std::string& grammar)
    7291{
    73     TIMER_ACCRUE(xml_validation);
    74 
    7592    shared_ptr<RelaxNGSchema> schema;
    7693
    7794    {
     
    89106    }
    90107
    91108    m_Schema = schema->m_Schema;
    92 
    93109    if (!m_Schema)
    94110        return false;
     111
     112    MD5 hash;
     113    hash.Update((const u8*)grammar.c_str(), grammar.length());
     114    m_Hash = hash;
     115
    95116    return true;
    96117}
    97118
    98 bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document)
     119bool RelaxNGValidator::LoadGrammarFile(const PIVFS& vfs, const VfsPath& grammarPath)
     120{
     121    CVFSFile file;
     122    if (file.Load(vfs, grammarPath) != PSRETURN_OK)
     123        return false;
     124
     125    return LoadGrammar(file.DecodeUTF8());
     126}
     127
     128bool RelaxNGValidator::Validate(const std::wstring& filename, const std::wstring& document) const
    99129{
    100130    std::string docutf8 = "<?xml version='1.0' encoding='utf-8'?>" + utf8_from_wstring(document);
    101131
    102132    return ValidateEncoded(filename, docutf8);
    103133}
    104134
    105 bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document)
     135bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document) const
    106136{
    107137    TIMER_ACCRUE(xml_validation);
    108138
     
    115145    xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
    116146    if (doc == NULL)
    117147    {
    118         LOGERROR("RelaxNGValidator: Failed to parse document");
     148        LOGERROR("RelaxNGValidator: Failed to parse document '%s'", utf8_from_wstring(filename).c_str());
    119149        return false;
    120150    }
    121151
     152    bool ret = ValidateEncoded(doc);
     153    xmlFreeDoc(doc);
     154    return ret;
     155}
     156
     157bool RelaxNGValidator::ValidateEncoded(xmlDocPtr doc) const
     158{
    122159    xmlRelaxNGValidCtxtPtr ctxt = xmlRelaxNGNewValidCtxt(m_Schema);
     160    xmlRelaxNGSetValidStructuredErrors(ctxt, &relaxNGErrorHandler, NULL);
    123161    int ret = xmlRelaxNGValidateDoc(ctxt, doc);
    124162    xmlRelaxNGFreeValidCtxt(ctxt);
    125     xmlFreeDoc(doc);
    126163
    127164    if (ret == 0)
    128165    {
     
    130167    }
    131168    else if (ret > 0)
    132169    {
    133         LOGERROR("RelaxNGValidator: Validation failed");
     170        LOGERROR("RelaxNGValidator: Validation failed for '%s'", doc->name);
    134171        return false;
    135172    }
    136173    else
     
    139176        return false;
    140177    }
    141178}
     179
     180bool RelaxNGValidator::CanValidate() const
     181{
     182    return m_Schema != NULL;
     183}
  • source/ps/XML/RelaxNG.h

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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;
     28
     29class IRelaxNGGrammar;
    2330
    2431class RelaxNGValidator
    2532{
     
    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);
     40
     41    MD5 GetGrammarHash() const { return m_Hash; }
     42
     43    bool Validate(const std::wstring& filename, const std::wstring& document) const;
    3344
    34     bool ValidateEncoded(const std::wstring& filename, const std::string& document);
     45    bool ValidateEncoded(const std::wstring& filename, const std::string& document) const;
     46
     47    bool ValidateEncoded(xmlDocPtr doc) const;
     48
     49    bool CanValidate() const;
    3550
    3651private:
     52    MD5 m_Hash;
    3753    xmlRelaxNGPtr m_Schema;
    3854};
    3955
     56/**
     57 * There should be no references to validators or schemas outside of the cache anymore when calling this.
     58 */
     59void ClearSchemaCache();
     60
    4061#endif // INCLUDED_RELAXNG
  • source/ps/XML/Xeromyces.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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>
    3334
     35static CMutex g_ValidatorCacheLock;
     36static std::map<const std::string, RelaxNGValidator> g_ValidatorCache;
     37static bool g_XeromycesStarted = false;
     38
    3439static void errorHandler(void* UNUSED(userData), xmlErrorPtr error)
    3540{
    3641    // Strip a trailing newline
     
    4550    // so the caching is less transparent than it should be
    4651}
    4752
    48 static bool g_XeromycesStarted = false;
    4953void CXeromyces::Startup()
    5054{
    5155    ENSURE(!g_XeromycesStarted);
    5256    xmlInitParser();
    5357    xmlSetStructuredErrorFunc(NULL, &errorHandler);
     58    CScopeLock lock(g_ValidatorCacheLock);
     59    g_ValidatorCache.insert(std::make_pair("", RelaxNGValidator()));
    5460    g_XeromycesStarted = true;
    5561}
    5662
     
    5965    ENSURE(g_XeromycesStarted);
    6066    xmlCleanupParser();
    6167    xmlSetStructuredErrorFunc(NULL, NULL);
     68    CScopeLock lock(g_ValidatorCacheLock);
     69    g_ValidatorCache.clear();
     70    ClearSchemaCache();
    6271    g_XeromycesStarted = false;
    6372}
    6473
    65 PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename)
     74bool CXeromyces::AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath)
     75{
     76    ENSURE(g_XeromycesStarted);
     77   
     78    RelaxNGValidator validator;
     79    if (!validator.LoadGrammarFile(vfs, grammarPath))
     80    {
     81        LOGERROR("CXeromyces: failed adding validator for '%s'", grammarPath.string8());
     82        return false;
     83    }
     84    {
     85        CScopeLock lock(g_ValidatorCacheLock);
     86        std::map<const std::string, RelaxNGValidator>::iterator it = g_ValidatorCache.find(name);
     87        if (it != g_ValidatorCache.end())
     88            g_ValidatorCache.erase(it);
     89        g_ValidatorCache.insert(std::make_pair(name, validator));
     90    }
     91    return true;
     92}
     93
     94bool CXeromyces::ValidateEncoded(const std::string& name, const std::wstring& filename, const std::string& document)
     95{
     96    CScopeLock lock(g_ValidatorCacheLock);
     97    return GetValidator(name).ValidateEncoded(filename, document);
     98}
     99
     100/**
     101 * NOTE: Callers MUST acquire the g_ValidatorCacheLock before calling this.
     102 */
     103RelaxNGValidator& CXeromyces::GetValidator(const std::string& name)
     104{
     105    if (g_ValidatorCache.find(name) == g_ValidatorCache.end())
     106        return g_ValidatorCache.find("")->second;
     107    return g_ValidatorCache.find(name)->second;
     108}
     109
     110PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName /* = "" */)
    66111{
    67112    ENSURE(g_XeromycesStarted);
    68113
    69114    CCacheLoader cacheLoader(vfs, L".xmb");
    70115
     116    MD5 validatorGrammarHash;
     117    {
     118        CScopeLock lock(g_ValidatorCacheLock);
     119        validatorGrammarHash = GetValidator(validatorName).GetGrammarHash();
     120    }
    71121    VfsPath xmbPath;
    72     Status ret = cacheLoader.TryLoadingCached(filename, MD5(), XMBVersion, xmbPath);
     122    Status ret = cacheLoader.TryLoadingCached(filename, validatorGrammarHash, XMBVersion, xmbPath);
    73123
    74124    if (ret == INFO::OK)
    75125    {
     
    94144    }
    95145
    96146    // XMB isn't up to date with the XML, so rebuild it
    97     return ConvertFile(vfs, filename, xmbPath);
     147    return ConvertFile(vfs, filename, xmbPath, validatorName);
    98148}
    99149
    100 bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath)
     150bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName /* = "" */)
    101151{
    102152    CCacheLoader cacheLoader(vfs, L".xmb");
    103153
    104154    archiveCachePath = cacheLoader.ArchiveCachePath(sourcePath);
    105155
    106     return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath) == PSRETURN_OK);
     156    return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath, validatorName) == PSRETURN_OK);
    107157}
    108158
    109 PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath)
     159PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName)
    110160{
    111161    CVFSFile input;
    112162    if (input.Load(vfs, filename))
     
    115165        return PSRETURN_Xeromyces_XMLOpenFailed;
    116166    }
    117167
    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)
     168    xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), input.GetBufferSize(), CStrW(filename.string()).ToUTF8().c_str(), NULL,
     169        XML_PARSE_NONET|XML_PARSE_NOCDATA);
     170    if (!doc)
    122171    {
    123172        LOGERROR("CXeromyces: Failed to parse XML file %s", filename.string8());
    124173        return PSRETURN_Xeromyces_XMLParseError;
    125174    }
    126175
     176    {
     177        CScopeLock lock(g_ValidatorCacheLock);
     178        RelaxNGValidator& validator = GetValidator(validatorName);
     179        if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     180        {
     181            // For now, log the error and continue, in the future we might fail
     182            LOGERROR("CXeromyces: failed to validate XML file %s", filename.string8());
     183        }
     184    }
     185
    127186    WriteBuffer writeBuffer;
    128187    CreateXMB(doc, writeBuffer);
    129188
     
    160219    return true;
    161220}
    162221
    163 PSRETURN CXeromyces::LoadString(const char* xml)
     222PSRETURN CXeromyces::LoadString(const char* xml, const std::string& validatorName /* = "" */)
    164223{
    165224    ENSURE(g_XeromycesStarted);
    166225
    167     xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
    168     if (! doc)
     226    xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "(no file)", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
     227    if (!doc)
    169228    {
    170229        LOGERROR("CXeromyces: Failed to parse XML string");
    171230        return PSRETURN_Xeromyces_XMLParseError;
    172231    }
    173232
     233    {
     234        CScopeLock lock(g_ValidatorCacheLock);
     235        RelaxNGValidator& validator = GetValidator(validatorName);
     236        if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     237        {
     238            // For now, log the error and continue, in the future we might fail
     239            LOGERROR("CXeromyces: failed to validate XML string");
     240        }
     241    }
     242
    174243    WriteBuffer writeBuffer;
    175244    CreateXMB(doc, writeBuffer);
    176245
  • source/ps/XML/Xeromyces.h

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    3333
    3434#include "lib/file/vfs/vfs.h"
    3535
     36class RelaxNGValidator;
    3637class WriteBuffer;
    37 class MD5;
    3838
    3939typedef struct _xmlDoc xmlDoc;
    4040typedef xmlDoc* xmlDocPtr;
     
    4646    /**
    4747     * Load from an XML file (with invisible XMB caching).
    4848     */
    49     PSRETURN Load(const PIVFS& vfs, const VfsPath& filename);
     49    PSRETURN Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName = "");
    5050
    5151    /**
    5252     * Load from an in-memory XML string (with no caching).
    5353     */
    54     PSRETURN LoadString(const char* xml);
     54    PSRETURN LoadString(const char* xml, const std::string& validatorName = "");
    5555
    5656    /**
    5757     * Convert the given XML file into an XMB in the archive cache.
    5858     * Returns the XMB path in @p archiveCachePath.
    5959     * Returns false on error.
    6060     */
    61     bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath);
     61    bool GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName = "");
    6262
    6363    /**
    6464     * Call once when initialising the program, to load libxml2.
     
    7171     */
    7272    static void Terminate();
    7373
     74    static bool AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath);
     75
     76    static bool ValidateEncoded(const std::string& name, const std::wstring& filename, const std::string& document);
     77
    7478private:
    75     PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath);
     79    static RelaxNGValidator& GetValidator(const std::string& name);
     80
     81    PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName);
    7682
    7783    bool ReadXMBFile(const PIVFS& vfs, const VfsPath& filename);
    7884
  • source/ps/XML/tests/test_RelaxNG.h

     
    1 /* Copyright (C) 2010 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    4444        {
    4545            TestLogger logger;
    4646            TS_ASSERT(!v.Validate(L"doc", L"<bogus/>"));
    47             TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Parse error: doc:1: Expecting element test, got bogus");
     47            TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Validation error: doc:1: Expecting element test, got bogus");
    4848        }
    4949
    5050        {
  • source/simulation2/components/CCmpPathfinder.cpp

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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

     
    1 /* Copyright (C) 2012 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    2626#include "graphics/TerritoryBoundary.h"
    2727#include "maths/MathUtil.h"
    2828#include "maths/Vector2D.h"
     29#include "ps/XML/Xeromyces.h"
    2930#include "renderer/Renderer.h"
    3031#include "renderer/Scene.h"
    3132#include "renderer/TerrainOverlay.h"
     
    122123
    123124        m_AnimTime = 0.0;
    124125
     126        // Register Relax NG validator
     127        CXeromyces::AddValidator(g_VFS, "territorymanager", "simulation/data/territorymanager.rng");
     128
    125129        CParamNode externalParamNode;
    126         CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
     130        CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml", "territorymanager");
    127131
    128132        int impassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
    129133        ENSURE(0 <= impassableCost && impassableCost <= 255);
  • source/simulation2/system/ParamNode.cpp

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    4242    ret.ApplyLayer(xmb, xmb.GetRoot(), sourceIdentifier);
    4343}
    4444
    45 void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path)
     45void CParamNode::LoadXML(CParamNode& ret, const VfsPath& path, const std::string& validatorName)
    4646{
    4747    CXeromyces xero;
    48     PSRETURN ok = xero.Load(g_VFS, path);
     48    PSRETURN ok = xero.Load(g_VFS, path, validatorName);
    4949    if (ok != PSRETURN_OK)
    5050        return; // (Xeromyces already logged an error)
    5151
  • source/simulation2/system/ParamNode.h

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    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/SoundManager.cpp

     
    3030#include "ps/ConfigDB.h"
    3131#include "ps/Filesystem.h"
    3232#include "ps/Profiler2.h"
     33#include "ps/XML/Xeromyces.h"
    3334
    3435ISoundManager* g_SoundManager = NULL;
    3536
     
    253254        m_PlayListItems = new PlayList;
    254255    }
    255256
     257    if (!CXeromyces::AddValidator(g_VFS, "sound_group", "audio/sound_group.rng"))
     258        LOGERROR("CSoundManager: failed to load grammar file 'audio/sound_group.rng'");
     259
    256260    RegisterFileReloadFunc(ReloadChangedFileCB, this);
    257261}
    258262
  • 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;