Ticket #245: xml_validation_r16600.patch

File xml_validation_r16600.patch, 33.2 KB (added by leper, 9 years ago)

Working patch (including tests) agains r16600.

  • 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::GetValidator("shader").ValidateEncoded(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
     56void ClearSchemaCache();
     57
    4058#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);
    5458    g_XeromycesStarted = true;
     59
     60    g_ValidatorCache.insert(std::make_pair("", RelaxNGValidator()));
    5561}
    5662
    5763void CXeromyces::Terminate()
     
    5965    ENSURE(g_XeromycesStarted);
    6066    xmlCleanupParser();
    6167    xmlSetStructuredErrorFunc(NULL, NULL);
     68    g_ValidatorCache.clear();
     69    ClearSchemaCache();
    6270    g_XeromycesStarted = false;
    6371}
    6472
    65 PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename)
     73bool CXeromyces::AddValidator(const PIVFS& vfs, const std::string& name, const VfsPath& grammarPath)
     74{
     75    ENSURE(g_XeromycesStarted);
     76   
     77    RelaxNGValidator validator;
     78    if (!validator.LoadGrammarFile(vfs, grammarPath))
     79    {
     80        LOGERROR("CXeromyces: failed adding validator for '%s'", grammarPath.string8());
     81        return false;
     82    }
     83    {
     84        CScopeLock lock(g_ValidatorCacheLock);
     85        std::map<const std::string, RelaxNGValidator>::iterator it = g_ValidatorCache.find(name);
     86        if (it != g_ValidatorCache.end())
     87            g_ValidatorCache.erase(it);
     88        g_ValidatorCache.insert(std::make_pair(name, validator));
     89    }
     90    return true;
     91}
     92
     93RelaxNGValidator& CXeromyces::GetValidator(const std::string &name)
     94{
     95    CScopeLock lock(g_ValidatorCacheLock);
     96    if (g_ValidatorCache.find(name) == g_ValidatorCache.end())
     97        return g_ValidatorCache.find("")->second;
     98    return g_ValidatorCache.find(name)->second;
     99}
     100
     101PSRETURN CXeromyces::Load(const PIVFS& vfs, const VfsPath& filename, const std::string& validatorName /* = "" */)
    66102{
    67103    ENSURE(g_XeromycesStarted);
    68104
    69105    CCacheLoader cacheLoader(vfs, L".xmb");
    70106
     107    RelaxNGValidator& validator = GetValidator(validatorName);
    71108    VfsPath xmbPath;
    72     Status ret = cacheLoader.TryLoadingCached(filename, MD5(), XMBVersion, xmbPath);
     109    Status ret = cacheLoader.TryLoadingCached(filename, validator.GetGrammarHash(), XMBVersion, xmbPath);
    73110
    74111    if (ret == INFO::OK)
    75112    {
     
    94131    }
    95132
    96133    // XMB isn't up to date with the XML, so rebuild it
    97     return ConvertFile(vfs, filename, xmbPath);
     134    return ConvertFile(vfs, filename, xmbPath, validatorName);
    98135}
    99136
    100 bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath)
     137bool CXeromyces::GenerateCachedXMB(const PIVFS& vfs, const VfsPath& sourcePath, VfsPath& archiveCachePath, const std::string& validatorName /* = "" */)
    101138{
    102139    CCacheLoader cacheLoader(vfs, L".xmb");
    103140
    104141    archiveCachePath = cacheLoader.ArchiveCachePath(sourcePath);
    105142
    106     return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath) == PSRETURN_OK);
     143    return (ConvertFile(vfs, sourcePath, VfsPath("cache") / archiveCachePath, validatorName) == PSRETURN_OK);
    107144}
    108145
    109 PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath)
     146PSRETURN CXeromyces::ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName)
    110147{
    111148    CVFSFile input;
    112149    if (input.Load(vfs, filename))
     
    115152        return PSRETURN_Xeromyces_XMLOpenFailed;
    116153    }
    117154
    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)
     155    xmlDocPtr doc = xmlReadMemory((const char*)input.GetBuffer(), input.GetBufferSize(), CStrW(filename.string()).ToUTF8().c_str(), NULL,
     156        XML_PARSE_NONET|XML_PARSE_NOCDATA);
     157    if (!doc)
    122158    {
    123159        LOGERROR("CXeromyces: Failed to parse XML file %s", filename.string8());
    124160        return PSRETURN_Xeromyces_XMLParseError;
    125161    }
    126162
     163    RelaxNGValidator& validator = GetValidator(validatorName);
     164    if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     165    {
     166        // For now, log the error and continue, in the future we might fail
     167        LOGERROR("CXeromyces: failed to validate XML file %s", filename.string8());
     168    }
     169
    127170    WriteBuffer writeBuffer;
    128171    CreateXMB(doc, writeBuffer);
    129172
     
    160203    return true;
    161204}
    162205
    163 PSRETURN CXeromyces::LoadString(const char* xml)
     206PSRETURN CXeromyces::LoadString(const char* xml, const std::string& validatorName /* = "" */)
    164207{
    165208    ENSURE(g_XeromycesStarted);
    166209
    167     xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
    168     if (! doc)
     210    xmlDocPtr doc = xmlReadMemory(xml, (int)strlen(xml), "(no file)", NULL, XML_PARSE_NONET|XML_PARSE_NOCDATA);
     211    if (!doc)
    169212    {
    170213        LOGERROR("CXeromyces: Failed to parse XML string");
    171214        return PSRETURN_Xeromyces_XMLParseError;
    172215    }
    173216
     217    RelaxNGValidator& validator = GetValidator(validatorName);
     218    if (validator.CanValidate() && !validator.ValidateEncoded(doc))
     219    {
     220        // For now, log the error and continue, in the future we might fail
     221        LOGERROR("CXeromyces: failed to validate XML string");
     222    }
     223
    174224    WriteBuffer writeBuffer;
    175225    CreateXMB(doc, writeBuffer);
    176226
  • 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 RelaxNGValidator& GetValidator(const std::string& name);
     77
    7478private:
    75     PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath);
     79    PSRETURN ConvertFile(const PIVFS& vfs, const VfsPath& filename, const VfsPath& xmbPath, const std::string& validatorName);
    7680
    7781    bool ReadXMBFile(const PIVFS& vfs, const VfsPath& filename);
    7882
  • 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;