diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 139 |
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) { |
