271 lines
8.4 KiB
C++
271 lines
8.4 KiB
C++
#include "RuntimeConfigStore.h"
|
|
|
|
#include "RuntimeJson.h"
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <windows.h>
|
|
|
|
namespace
|
|
{
|
|
double Clamp01(double value)
|
|
{
|
|
return (std::max)(0.0, (std::min)(1.0, value));
|
|
}
|
|
|
|
bool LooksLikePackagedRuntimeRoot(const std::filesystem::path& candidate)
|
|
{
|
|
return std::filesystem::exists(candidate / "config" / "runtime-host.json") &&
|
|
std::filesystem::exists(candidate / "runtime" / "templates" / "shader_wrapper.slang.in") &&
|
|
std::filesystem::exists(candidate / "shaders");
|
|
}
|
|
|
|
bool LooksLikeRepoRoot(const std::filesystem::path& candidate)
|
|
{
|
|
return std::filesystem::exists(candidate / "CMakeLists.txt") &&
|
|
std::filesystem::exists(candidate / "apps" / "LoopThroughWithOpenGLCompositing");
|
|
}
|
|
|
|
std::filesystem::path FindRepoRootCandidate()
|
|
{
|
|
std::vector<std::filesystem::path> rootsToTry;
|
|
|
|
char currentDirectory[MAX_PATH] = {};
|
|
if (GetCurrentDirectoryA(MAX_PATH, currentDirectory) > 0)
|
|
rootsToTry.push_back(std::filesystem::path(currentDirectory));
|
|
|
|
char modulePath[MAX_PATH] = {};
|
|
DWORD moduleLength = GetModuleFileNameA(NULL, modulePath, MAX_PATH);
|
|
if (moduleLength > 0 && moduleLength < MAX_PATH)
|
|
rootsToTry.push_back(std::filesystem::path(modulePath).parent_path());
|
|
|
|
for (const std::filesystem::path& startPath : rootsToTry)
|
|
{
|
|
std::filesystem::path candidate = startPath;
|
|
for (int depth = 0; depth < 10 && !candidate.empty(); ++depth)
|
|
{
|
|
if (LooksLikePackagedRuntimeRoot(candidate) || LooksLikeRepoRoot(candidate))
|
|
return candidate;
|
|
|
|
candidate = candidate.parent_path();
|
|
}
|
|
}
|
|
|
|
return std::filesystem::path();
|
|
}
|
|
}
|
|
|
|
bool RuntimeConfigStore::Initialize(std::string& error)
|
|
{
|
|
if (!ResolvePaths(error))
|
|
return false;
|
|
if (!LoadConfig(error))
|
|
return false;
|
|
RefreshConfigDependentPaths();
|
|
return true;
|
|
}
|
|
|
|
const RuntimeConfigStore::AppConfig& RuntimeConfigStore::GetConfig() const
|
|
{
|
|
return mConfig;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetRepoRoot() const
|
|
{
|
|
return mRepoRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetUiRoot() const
|
|
{
|
|
return mUiRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetDocsRoot() const
|
|
{
|
|
return mDocsRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetShaderRoot() const
|
|
{
|
|
return mShaderRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetRuntimeRoot() const
|
|
{
|
|
return mRuntimeRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetPresetRoot() const
|
|
{
|
|
return mPresetRoot;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetRuntimeStatePath() const
|
|
{
|
|
return mRuntimeStatePath;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetWrapperPath() const
|
|
{
|
|
return mWrapperPath;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetGeneratedGlslPath() const
|
|
{
|
|
return mGeneratedGlslPath;
|
|
}
|
|
|
|
const std::filesystem::path& RuntimeConfigStore::GetPatchedGlslPath() const
|
|
{
|
|
return mPatchedGlslPath;
|
|
}
|
|
|
|
void RuntimeConfigStore::SetBoundControlServerPort(unsigned short port)
|
|
{
|
|
mConfig.serverPort = port;
|
|
}
|
|
|
|
bool RuntimeConfigStore::ResolvePaths(std::string& error)
|
|
{
|
|
mRepoRoot = FindRepoRootCandidate();
|
|
if (mRepoRoot.empty())
|
|
{
|
|
error = "Could not locate the repository root from the current runtime path.";
|
|
return false;
|
|
}
|
|
|
|
const std::filesystem::path builtUiRoot = mRepoRoot / "ui" / "dist";
|
|
mUiRoot = std::filesystem::exists(builtUiRoot) ? builtUiRoot : (mRepoRoot / "ui");
|
|
mDocsRoot = mRepoRoot / "docs";
|
|
mConfigPath = mRepoRoot / "config" / "runtime-host.json";
|
|
mRuntimeRoot = mRepoRoot / "runtime";
|
|
mPresetRoot = mRuntimeRoot / "stack_presets";
|
|
mRuntimeStatePath = mRuntimeRoot / "runtime_state.json";
|
|
RefreshConfigDependentPaths();
|
|
|
|
std::error_code fsError;
|
|
std::filesystem::create_directories(mRuntimeRoot / "shader_cache", fsError);
|
|
std::filesystem::create_directories(mPresetRoot, fsError);
|
|
return true;
|
|
}
|
|
|
|
bool RuntimeConfigStore::LoadConfig(std::string& error)
|
|
{
|
|
if (!std::filesystem::exists(mConfigPath))
|
|
return true;
|
|
|
|
std::string configText = ReadTextFile(mConfigPath, error);
|
|
if (configText.empty())
|
|
return false;
|
|
|
|
JsonValue configJson;
|
|
if (!ParseJson(configText, configJson, error))
|
|
return false;
|
|
|
|
if (const JsonValue* shaderLibraryValue = configJson.find("shaderLibrary"))
|
|
mConfig.shaderLibrary = shaderLibraryValue->asString();
|
|
if (const JsonValue* serverPortValue = configJson.find("serverPort"))
|
|
mConfig.serverPort = static_cast<unsigned short>(serverPortValue->asNumber(mConfig.serverPort));
|
|
if (const JsonValue* oscPortValue = configJson.find("oscPort"))
|
|
mConfig.oscPort = static_cast<unsigned short>(oscPortValue->asNumber(mConfig.oscPort));
|
|
if (const JsonValue* oscBindAddressValue = configJson.find("oscBindAddress"))
|
|
mConfig.oscBindAddress = oscBindAddressValue->asString();
|
|
if (const JsonValue* oscSmoothingValue = configJson.find("oscSmoothing"))
|
|
mConfig.oscSmoothing = Clamp01(oscSmoothingValue->asNumber(mConfig.oscSmoothing));
|
|
if (const JsonValue* autoReloadValue = configJson.find("autoReload"))
|
|
mConfig.autoReload = autoReloadValue->asBoolean(mConfig.autoReload);
|
|
if (const JsonValue* maxTemporalHistoryFramesValue = configJson.find("maxTemporalHistoryFrames"))
|
|
{
|
|
const double configuredValue = maxTemporalHistoryFramesValue->asNumber(static_cast<double>(mConfig.maxTemporalHistoryFrames));
|
|
mConfig.maxTemporalHistoryFrames = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue);
|
|
}
|
|
if (const JsonValue* previewFpsValue = configJson.find("previewFps"))
|
|
{
|
|
const double configuredValue = previewFpsValue->asNumber(static_cast<double>(mConfig.previewFps));
|
|
mConfig.previewFps = configuredValue <= 0.0 ? 0u : static_cast<unsigned>(configuredValue);
|
|
}
|
|
if (const JsonValue* enableExternalKeyingValue = configJson.find("enableExternalKeying"))
|
|
mConfig.enableExternalKeying = enableExternalKeyingValue->asBoolean(mConfig.enableExternalKeying);
|
|
if (const JsonValue* videoFormatValue = configJson.find("videoFormat"))
|
|
{
|
|
if (videoFormatValue->isString() && !videoFormatValue->asString().empty())
|
|
{
|
|
mConfig.inputVideoFormat = videoFormatValue->asString();
|
|
mConfig.outputVideoFormat = videoFormatValue->asString();
|
|
}
|
|
}
|
|
if (const JsonValue* frameRateValue = configJson.find("frameRate"))
|
|
{
|
|
if (frameRateValue->isString() && !frameRateValue->asString().empty())
|
|
{
|
|
mConfig.inputFrameRate = frameRateValue->asString();
|
|
mConfig.outputFrameRate = frameRateValue->asString();
|
|
}
|
|
else if (frameRateValue->isNumber())
|
|
{
|
|
std::ostringstream stream;
|
|
stream << frameRateValue->asNumber();
|
|
mConfig.inputFrameRate = stream.str();
|
|
mConfig.outputFrameRate = stream.str();
|
|
}
|
|
}
|
|
if (const JsonValue* inputVideoFormatValue = configJson.find("inputVideoFormat"))
|
|
{
|
|
if (inputVideoFormatValue->isString() && !inputVideoFormatValue->asString().empty())
|
|
mConfig.inputVideoFormat = inputVideoFormatValue->asString();
|
|
}
|
|
if (const JsonValue* inputFrameRateValue = configJson.find("inputFrameRate"))
|
|
{
|
|
if (inputFrameRateValue->isString() && !inputFrameRateValue->asString().empty())
|
|
mConfig.inputFrameRate = inputFrameRateValue->asString();
|
|
else if (inputFrameRateValue->isNumber())
|
|
{
|
|
std::ostringstream stream;
|
|
stream << inputFrameRateValue->asNumber();
|
|
mConfig.inputFrameRate = stream.str();
|
|
}
|
|
}
|
|
if (const JsonValue* outputVideoFormatValue = configJson.find("outputVideoFormat"))
|
|
{
|
|
if (outputVideoFormatValue->isString() && !outputVideoFormatValue->asString().empty())
|
|
mConfig.outputVideoFormat = outputVideoFormatValue->asString();
|
|
}
|
|
if (const JsonValue* outputFrameRateValue = configJson.find("outputFrameRate"))
|
|
{
|
|
if (outputFrameRateValue->isString() && !outputFrameRateValue->asString().empty())
|
|
mConfig.outputFrameRate = outputFrameRateValue->asString();
|
|
else if (outputFrameRateValue->isNumber())
|
|
{
|
|
std::ostringstream stream;
|
|
stream << outputFrameRateValue->asNumber();
|
|
mConfig.outputFrameRate = stream.str();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string RuntimeConfigStore::ReadTextFile(const std::filesystem::path& path, std::string& error) const
|
|
{
|
|
std::ifstream input(path, std::ios::binary);
|
|
if (!input)
|
|
{
|
|
error = "Could not open file: " + path.string();
|
|
return std::string();
|
|
}
|
|
|
|
std::ostringstream buffer;
|
|
buffer << input.rdbuf();
|
|
return buffer.str();
|
|
}
|
|
|
|
void RuntimeConfigStore::RefreshConfigDependentPaths()
|
|
{
|
|
mShaderRoot = mRepoRoot / mConfig.shaderLibrary;
|
|
mWrapperPath = mRuntimeRoot / "shader_cache" / "active_shader_wrapper.slang";
|
|
mGeneratedGlslPath = mRuntimeRoot / "shader_cache" / "active_shader.raw.frag";
|
|
mPatchedGlslPath = mRuntimeRoot / "shader_cache" / "active_shader.frag";
|
|
}
|