Files
video-shader-toys/apps/LoopThroughWithOpenGLCompositing/runtime/RuntimeConfigStore.cpp
Aiden 5cbdbd6813
Some checks failed
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m48s
CI / Windows Release Package (push) Has been cancelled
Pass 3
2026-05-11 02:06:17 +10:00

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";
}