96 lines
3.1 KiB
C++
96 lines
3.1 KiB
C++
#include "fgc/CameraMode.h"
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
extern "C" {
|
|
#include "ini.h"
|
|
}
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace fgc {
|
|
|
|
namespace {
|
|
|
|
// inih callback. Unlike Config.cpp's handler this APPENDS rather than writing a
|
|
// map: mode files depend on file order and on repeated keys (see CameraMode).
|
|
int modeHandler(void* user, const char* section, const char* name, const char* value) {
|
|
auto* m = static_cast<CameraMode*>(user);
|
|
const std::string sec = section ? section : "";
|
|
const std::string key = name ? name : "";
|
|
const std::string val = value ? value : "";
|
|
|
|
if (sec == "Camera") {
|
|
m->features.emplace_back(key, val);
|
|
return 1;
|
|
}
|
|
if (sec != "Mode") return 0; // unknown section -> parse error
|
|
|
|
if (key == "description") {
|
|
m->description = val;
|
|
} else if (key == "settle_wait_ms" || key == "frame_timeout_ms") {
|
|
int parsed = 0;
|
|
try {
|
|
size_t consumed = 0;
|
|
parsed = std::stoi(val, &consumed);
|
|
if (consumed != val.size() || parsed < 0) return 0;
|
|
} catch (const std::exception&) {
|
|
return 0;
|
|
}
|
|
(key == "settle_wait_ms" ? m->settle_wait_ms : m->frame_timeout_ms) = parsed;
|
|
} else {
|
|
return 0; // unknown [Mode] key -> parse error, not a silent typo
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CameraMode loadCameraMode(const std::string& path) {
|
|
CameraMode mode;
|
|
mode.name = fs::path(path).stem().string();
|
|
|
|
const int rc = ini_parse(path.c_str(), modeHandler, &mode);
|
|
if (rc == -1)
|
|
throw std::runtime_error("Cannot open camera mode file: " + path);
|
|
if (rc == -2)
|
|
throw std::runtime_error("Out of memory reading camera mode file: " + path);
|
|
if (rc > 0)
|
|
throw std::runtime_error("Camera mode file '" + path + "' line " + std::to_string(rc) +
|
|
": unknown section, unknown [Mode] key, or invalid value "
|
|
"(expected [Mode] description/settle_wait_ms/frame_timeout_ms, "
|
|
"or a [Camera] feature)");
|
|
if (mode.features.empty())
|
|
throw std::runtime_error("Camera mode file '" + path + "' sets no [Camera] features");
|
|
|
|
return mode;
|
|
}
|
|
|
|
std::map<std::string, CameraMode> loadCameraModes(const std::string& dir) {
|
|
std::error_code ec;
|
|
if (!fs::is_directory(dir, ec))
|
|
throw std::runtime_error("Camera mode directory not found: " + dir);
|
|
|
|
// Sorted so startup logs and the UI list modes in a stable order.
|
|
std::vector<fs::path> files;
|
|
for (const auto& entry : fs::directory_iterator(dir, ec))
|
|
if (entry.is_regular_file() && entry.path().extension() == ".ini")
|
|
files.push_back(entry.path());
|
|
std::sort(files.begin(), files.end());
|
|
|
|
std::map<std::string, CameraMode> modes;
|
|
for (const auto& f : files) {
|
|
CameraMode m = loadCameraMode(f.string());
|
|
modes.emplace(m.name, std::move(m));
|
|
}
|
|
if (modes.empty())
|
|
throw std::runtime_error("No camera modes (*.ini) in " + dir);
|
|
|
|
return modes;
|
|
}
|
|
|
|
} // namespace fgc
|