summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp139
1 files changed, 137 insertions, 2 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 0beffa5..0593ed6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,10 @@
#include <iostream>
+#include <memory>
#include "CLI11.hpp"
#include "Companion.h"
+#ifdef PM64_SUPPORT
+#include "factories/pm64/AudioPreview.h"
+#endif
#ifdef BK64_SUPPORT
#include "factories/bk64/ConfigFactory.h"
#endif
@@ -9,6 +13,36 @@ Companion* Companion::Instance;
#if defined(STANDALONE) && !defined(__EMSCRIPTEN__)
+#ifdef BUILD_UI
+#include "imgui.h"
+#include "ui/BaseBackend.h"
+#include "ui/View.h"
+#include "ui/list/Main.h"
+#include "ui/backends/LusBackend.h"
+#include "ui/audio/SequenceDriver.h"
+#ifdef PM64_SUPPORT
+#include "factories/pm64/ShapeFactory.h"
+#endif
+
+static void LaunchUI() {
+ if (Companion::Instance == nullptr) {
+ std::cout << "No ROM loaded; nothing to display." << std::endl;
+ return;
+ }
+
+ auto backend = UI::CreateLusBackend();
+ UI::SetBackend(backend.get());
+
+ auto views = std::make_shared<ViewManager>();
+ views->SetView(std::make_shared<MainView>());
+
+ // The backend owns the window + frame loop; returns when the user quits.
+ backend->RunViewer(views);
+
+ UI::SetBackend(nullptr);
+}
+#endif
+
int main(int argc, char* argv[]) {
CLI::App app{ "Torch - [T]orch is [O]ur [R]esource [C]onversion [H]elper\n\
* It extracts from a baserom and generates code or an otr.\n\
@@ -146,8 +180,8 @@ int main(int argc, char* argv[]) {
#ifdef BK64_SUPPORT
/* Emit per-slot SHA-1s of the BK64 asset table */
std::string hashesOut;
- const auto hashes = app.add_subcommand(
- "hashes", "Hashes - Emit per-slot SHA-1 of BK64 asset table for v1.0 baseline\n");
+ const auto hashes =
+ app.add_subcommand("hashes", "Hashes - Emit per-slot SHA-1 of BK64 asset table for v1.0 baseline\n");
hashes->add_option("<baserom.z64>", filename, "")->required()->check(CLI::ExistingFile);
hashes->add_option("-o,--output", hashesOut, "Output yaml path (default: hashes.yaml in cwd)");
@@ -216,6 +250,107 @@ int main(int argc, char* argv[]) {
}
});
+#ifdef BUILD_UI
+ /* Launch the interactive viewer */
+ const auto ui = app.add_subcommand("ui", "UI - Parses a baserom and opens the interactive asset viewer\n");
+
+ ui->add_option("<baserom.z64>", filename, "")->required()->check(CLI::ExistingFile);
+ ui->add_flag("-v,--verbose", debug, "Verbose Debug Mode");
+ ui->add_option("-s,--srcdir", srcdir, "Set source directory to locate config.yml and asset metadata for processing")
+ ->check(CLI::ExistingDirectory);
+
+ ui->parse_complete_callback([&] {
+ const auto instance = Companion::Instance = new Companion(filename, ArchiveType::None, debug, srcdir, destdir);
+#ifdef PM64_SUPPORT
+ PM64Audio::SetPreviewAssets(true);
+#endif
+ // Parse-only: populate the asset results for the viewer without exporting
+ // anything (shouldProcess = false skips all writes). Export type is
+ // irrelevant since nothing is exported.
+ std::atomic<size_t> assetCount{ 0 };
+ instance->Init(ExportType::Binary, assetCount, false);
+ if (std::getenv("TORCH_PM64_TEST") != nullptr) { // headless shape walk, no window
+#ifdef PM64_SUPPORT
+ for (const auto& [file, results] : instance->GetParseResults()) {
+ for (const auto& item : results) {
+ if (item.type != "PM64:SHAPE") {
+ continue;
+ }
+ if (const char* filter = std::getenv("TORCH_SEQ_FILTER");
+ filter != nullptr && item.name.find(filter) == std::string::npos) {
+ continue;
+ }
+ PM64ShapeDebugBuild(item);
+ }
+ }
+#endif
+ return;
+ }
+ if (std::getenv("TORCH_SEQ_TEST") != nullptr) { // headless driver check, no window
+ for (const auto& [file, results] : instance->GetParseResults()) {
+ for (const auto& item : results) {
+ if (UI::GetSequenceDriver(item.type) == nullptr || !item.data.has_value()) {
+ continue;
+ }
+ if (const char* filter = std::getenv("TORCH_SEQ_FILTER");
+ filter != nullptr && item.name.find(filter) == std::string::npos) {
+ continue;
+ }
+ auto* driver = UI::GetSequenceDriver(item.type);
+ if (driver == nullptr) {
+ continue;
+ }
+ UI::RenderedAudio out;
+ const bool ok = driver->Render(item, 0, out);
+ float peak = 0.0f;
+ double sumSq = 0.0;
+ size_t loud = 0;
+ for (const auto v : out.pcm) {
+ const float f = std::fabs((float)v) / 32768.0f;
+ peak = std::max(peak, f);
+ sumSq += (double)f * f;
+ loud += f > 0.01f ? 1 : 0;
+ }
+ const double rms = out.pcm.empty() ? 0.0 : std::sqrt(sumSq / (double)out.pcm.size());
+ printf("[seqtest] %s ok=%d notes=%zu frames=%zu peak=%.3f rms=%.4f loud=%.1f%%\n",
+ item.name.c_str(), ok, out.noteCount, out.pcm.size() / 2, peak, rms,
+ out.pcm.empty() ? 0.0 : 100.0 * (double)loud / (double)out.pcm.size());
+ if (const char* wavDir = std::getenv("TORCH_SEQ_WAV")) {
+ std::string base = item.name;
+ std::replace(base.begin(), base.end(), '/', '_');
+ const std::string wavPath = std::string(wavDir) + "/" + base + ".wav";
+ FILE* f = fopen(wavPath.c_str(), "wb");
+ if (f != nullptr) {
+ const uint32_t dataSize = (uint32_t)(out.pcm.size() * 2);
+ const uint32_t riffSize = 36 + dataSize;
+ const uint32_t rate = 32000, byteRate = rate * 4;
+ const uint16_t fmt = 1, ch = 2, align = 4, bits = 16;
+ const uint32_t fmtSize = 16;
+ fwrite("RIFF", 1, 4, f);
+ fwrite(&riffSize, 4, 1, f);
+ fwrite("WAVE", 1, 4, f);
+ fwrite("fmt ", 1, 4, f);
+ fwrite(&fmtSize, 4, 1, f);
+ fwrite(&fmt, 2, 1, f);
+ fwrite(&ch, 2, 1, f);
+ fwrite(&rate, 4, 1, f);
+ fwrite(&byteRate, 4, 1, f);
+ fwrite(&align, 2, 1, f);
+ fwrite(&bits, 2, 1, f);
+ fwrite("data", 1, 4, f);
+ fwrite(&dataSize, 4, 1, f);
+ fwrite(out.pcm.data(), 2, out.pcm.size(), f);
+ fclose(f);
+ }
+ }
+ }
+ }
+ return;
+ }
+ LaunchUI();
+ });
+#endif
+
try {
app.parse(argc, argv);
} catch (const CLI::ParseError& e) {