Skip to content

Commit

Permalink
Implement the shader file path validation logic in CShader, plus the …
Browse files Browse the repository at this point in the history
…necessary refactoring in Doom3ShaderSystem.
  • Loading branch information
codereader committed Apr 18, 2021
1 parent e14b83d commit 39bdf9a
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 25 deletions.
2 changes: 2 additions & 0 deletions include/ishaders.h
Expand Up @@ -210,6 +210,8 @@ class Material
virtual const char* getShaderFileName() const = 0;

// Set the mtr file name to define where this material should be saved to
// This will throw an exception if the given path (absolute or relative)
// is not located within the current mod file tree (VFS)
virtual void setShaderFileName(const std::string& fullPath) = 0;

// Returns the VFS info structure of the file this shader is defined in
Expand Down
31 changes: 31 additions & 0 deletions libs/materials/ParseLib.h
@@ -1,12 +1,43 @@
#pragma once

#include <map>
#include <stdexcept>
#include "ishaders.h"
#include "ishaderlayer.h"
#include "gamelib.h"
#include "os/path.h"
#include "xmlutil/MissingXMLNodeException.h"

namespace shaders
{

constexpr const char* MISSING_BASEPATH_NODE = "Failed to find \"/game/filesystem/shaders/basepath\" node in game descriptor";
constexpr const char* MISSING_EXTENSION_NODE = "Failed to find \"/game/filesystem/shaders/extension\" node in game descriptor";

inline std::string getMaterialsFolderName()
{
auto nodes = game::current::getNodes("/filesystem/shaders/basepath");

if (nodes.empty())
{
throw xml::MissingXMLNodeException(MISSING_BASEPATH_NODE);
}

return os::standardPathWithSlash(nodes[0].getContent());
}

inline std::string getMaterialFileExtension()
{
auto nodes = game::current::getNodes("/filesystem/shaders/extension");

if (nodes.empty())
{
throw xml::MissingXMLNodeException(MISSING_EXTENSION_NODE);
}

return nodes[0].getContent();
}

// A regex pattern that can be used to detect a material name in a single line of a material file
// which is optionally detecting an opening curly brace in the same line
inline std::string getDeclNamePatternForMaterialName(const std::string& name)
Expand Down
36 changes: 34 additions & 2 deletions radiantcore/shaders/CShader.cpp
Expand Up @@ -6,6 +6,7 @@
#include "ishaders.h"
#include "texturelib.h"
#include "gamelib.h"
#include "materials/ParseLib.h"
#include "parser/DefTokeniser.h"

/* CONSTANTS */
Expand Down Expand Up @@ -316,8 +317,39 @@ const char* CShader::getShaderFileName() const

void CShader::setShaderFileName(const std::string& fullPath)
{
_fileInfo.topDir = os::getDirectory(GlobalFileSystem().findRoot(fullPath));
_fileInfo.name = os::getRelativePath(fullPath, _fileInfo.topDir);
std::string path = fullPath;

if (path_is_absolute(path.c_str()))
{
auto rootPath = GlobalFileSystem().findRoot(path);

if (rootPath.empty())
{
throw std::invalid_argument("The path " + path + " is not located in the current mod file structure");
}

path = os::getRelativePath(path, rootPath);
}

auto materialsFolder = getMaterialsFolderName();
auto pathRelativeToMaterialsFolder = os::getRelativePath(path, materialsFolder);

// Check if the path starts with a "materials/" folder
// getRelativePath will return the unchanged path if this is not the case
if (pathRelativeToMaterialsFolder == path)
{
throw std::invalid_argument("The path " + path + " does not point to a " + materialsFolder + " folder");
}

auto extension = getMaterialFileExtension();
if (os::getExtension(pathRelativeToMaterialsFolder) != extension)
{
throw std::invalid_argument("The file extension must be " + extension);
}

_fileInfo.topDir = materialsFolder;
_fileInfo.name = pathRelativeToMaterialsFolder;
_fileInfo.visibility = vfs::Visibility::NORMAL;
}

const vfs::FileInfo& CShader::getShaderFileInfo() const
Expand Down
26 changes: 3 additions & 23 deletions radiantcore/shaders/Doom3ShaderSystem.cpp
Expand Up @@ -36,13 +36,6 @@
namespace
{
const char* TEXTURE_PREFIX = "textures/";
const char* MISSING_BASEPATH_NODE =
"Failed to find \"/game/filesystem/shaders/basepath\" node \
in game descriptor";

const char* MISSING_EXTENSION_NODE =
"Failed to find \"/game/filesystem/shaders/extension\" node \
in game descriptor";

// Default image maps for optional material stages
const std::string IMAGE_FLAT = "_flat.bmp";
Expand Down Expand Up @@ -92,30 +85,17 @@ void Doom3ShaderSystem::destroy()
ShaderLibraryPtr Doom3ShaderSystem::loadMaterialFiles()
{
// Get the shaders path and extension from the XML game file
xml::NodeList nlShaderPath =
GlobalGameManager().currentGame()->getLocalXPath("/filesystem/shaders/basepath");
if (nlShaderPath.empty())
throw xml::MissingXMLNodeException(MISSING_BASEPATH_NODE);

xml::NodeList nlShaderExt =
GlobalGameManager().currentGame()->getLocalXPath("/filesystem/shaders/extension");
if (nlShaderExt.empty())
throw xml::MissingXMLNodeException(MISSING_EXTENSION_NODE);
auto materialsFolder = getMaterialsFolderName();
auto extension = getMaterialFileExtension();

// Load the shader files from the VFS
std::string sPath = nlShaderPath[0].getContent();
if (!string::ends_with(sPath, "/"))
sPath += "/";

std::string extension = nlShaderExt[0].getContent();

ShaderLibraryPtr library = std::make_shared<ShaderLibrary>();

// Load each file from the global filesystem
{
ScopedDebugTimer timer("ShaderFiles parsed: ");
ShaderFileLoader<ShaderLibrary> loader(GlobalFileSystem(), *library,
sPath, extension);
materialsFolder, extension);
loader.parseFiles();
}

Expand Down
20 changes: 20 additions & 0 deletions test/MaterialExport.cpp
Expand Up @@ -1229,6 +1229,26 @@ TEST_F(MaterialExportTest, WritingMaterialFiles)
<< "New definition not found in file";
}

TEST_F(MaterialExportTest, SetShaderFilePath)
{
auto newMaterial = GlobalMaterialManager().createEmptyMaterial("textures/exporttest/somePath");
newMaterial->setDescription("--");

auto projectPath = _context.getTestProjectPath();

newMaterial->setShaderFileName(projectPath + "materials/exporttest.mtr");
EXPECT_EQ(newMaterial->getShaderFileInfo().topDir, "materials/");
EXPECT_EQ(newMaterial->getShaderFileInfo().name, "exporttest.mtr");

newMaterial->setShaderFileName(projectPath + "materials/_test.mtr");
EXPECT_EQ(newMaterial->getShaderFileInfo().topDir, "materials/");
EXPECT_EQ(newMaterial->getShaderFileInfo().name, "_test.mtr");

newMaterial->setShaderFileName("materials/blah.mtr");
EXPECT_EQ(newMaterial->getShaderFileInfo().topDir, "materials/");
EXPECT_EQ(newMaterial->getShaderFileInfo().name, "blah.mtr");
}

// Not all shader file paths are valid, they must be within the current mod's VFS structure, and in the materials/ folder

TEST_F(MaterialExportTest, ShaderFilePathValidation)
Expand Down

0 comments on commit 39bdf9a

Please sign in to comment.