From 11c28357a13a4b424682339606003508d6db2d90 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 2 Jun 2020 23:35:01 -0400 Subject: [PATCH] Implements a basic ROM installation loop. Albeit that I need to figure out how layouts work to keep that request view at least centred. --- OSBindings/Qt/mainwindow.cpp | 207 +++++++++++++++++++++++++++-------- OSBindings/Qt/mainwindow.h | 17 +++ OSBindings/Qt/mainwindow.ui | 44 ++++++-- 3 files changed, 214 insertions(+), 54 deletions(-) diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 9fc18df53..7ec363529 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -3,11 +3,9 @@ #include #include "mainwindow.h" -#include "ui_mainwindow.h" #include "timer.h" -#include "../../Analyser/Static/StaticAnalyser.hpp" -#include "../../Machines/Utility/MachineForTarget.hpp" +#include "../../Numeric/CRC.hpp" /* General Qt implementation notes: @@ -41,6 +39,11 @@ MainWindow::MainWindow(QWidget *parent) // TODO: not until there's actually something to display. timerThread->start(); qTimer->start(); + + // Hide the missing ROMs box unless or until it's needed; grab the text it + // began with as a prefix for future mutation. + ui->missingROMsBox->setVisible(false); + romRequestBaseText = ui->missingROMsBox->toPlainText(); } void MainWindow::createActions() { @@ -65,51 +68,12 @@ void MainWindow::createActions() { void MainWindow::open() { QString fileName = QFileDialog::getOpenFileName(this); if(!fileName.isEmpty()) { - const auto targets = Analyser::Static::GetTargets(fileName.toStdString()); + targets = Analyser::Static::GetTargets(fileName.toStdString()); if(targets.empty()) { qDebug() << "Not media:" << fileName; } else { qDebug() << "Got media:" << fileName; - - const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations] - (const std::vector &roms) -> std::vector>> { - std::vector>> results; - - for(const auto &rom: roms) { - FILE *file = nullptr; - for(const auto &path: appDataLocations) { - const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name; - const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString(); - - qDebug() << "Taking a run at " << nativeSource.c_str(); - - file = fopen(nativeSource.c_str(), "rb"); - if(file) break; - } - - if(file) { - auto data = std::make_unique>(); - - fseek(file, 0, SEEK_END); - data->resize(std::ftell(file)); - fseek(file, 0, SEEK_SET); - size_t read = fread(data->data(), 1, data->size(), file); - fclose(file); - - if(read == data->size()) { - results.push_back(std::move(data)); - } else { - results.push_back(nullptr); - } - } else { - results.push_back(nullptr); - } - } - return results; - }; - Machine::Error error; - std::unique_ptr machine(Machine::MachineForTargets(targets, rom_fetcher, error)); + launchMachine(); } } } @@ -121,3 +85,158 @@ MainWindow::~MainWindow() { while(timerThread->isRunning()); } +// MARK: Machine launch. + +namespace { + +std::unique_ptr> fileContentsAndClose(FILE *file) { + auto data = std::make_unique>(); + + fseek(file, 0, SEEK_END); + data->resize(std::ftell(file)); + fseek(file, 0, SEEK_SET); + size_t read = fread(data->data(), 1, data->size(), file); + fclose(file); + + if(read == data->size()) { + return data; + } + + return nullptr; +} + +} + +void MainWindow::launchMachine() { + const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + missingRoms.clear(); + + ROMMachine::ROMFetcher rom_fetcher = [&appDataLocations, this] + (const std::vector &roms) -> std::vector>> { + std::vector>> results; + + for(const auto &rom: roms) { + FILE *file = nullptr; + for(const auto &path: appDataLocations) { + const std::string source = path.toStdString() + "/ROMImages/" + rom.machine_name + "/" + rom.file_name; + const std::string nativeSource = QDir::toNativeSeparators(QString::fromStdString(source)).toStdString(); + + qDebug() << "Taking a run at " << nativeSource.c_str(); + + file = fopen(nativeSource.c_str(), "rb"); + if(file) break; + } + + if(file) { + auto data = fileContentsAndClose(file); + if(data) { + results.push_back(std::move(data)); + continue; + } + } + + results.push_back(nullptr); + missingRoms.push_back(rom); + } + return results; + }; + Machine::Error error; + std::unique_ptr machine(Machine::MachineForTargets(targets, rom_fetcher, error)); + + switch(error) { + default: + ui->missingROMsBox->setVisible(false); + uiPhase = UIPhase::RunningMachine; + + // TODO: launch machine. + break; + + case Machine::Error::MissingROM: { + ui->missingROMsBox->setVisible(true); + uiPhase = UIPhase::RequestingROMs; + + // Populate request text. + QString requestText = romRequestBaseText; + size_t index = 0; + for(const auto rom: missingRoms) { + requestText += "• "; + requestText += rom.descriptive_name.c_str(); + + ++index; + if(index == missingRoms.size()) { + requestText += ".\n"; + continue; + } + if(index == missingRoms.size() - 1) { + requestText += "; and\n"; + continue; + } + requestText += ";\n"; + } + ui->missingROMsBox->setPlainText(requestText); + } break; + } +} + +void MainWindow::dragEnterEvent(QDragEnterEvent* event) { + // Always accept dragged files. + if(event->mimeData()->hasUrls()) + event->accept(); +} + +void MainWindow::dropEvent(QDropEvent* event) { + if(!event->mimeData()->hasUrls()) { + return; + } + event->accept(); + + switch(uiPhase) { + case UIPhase::NoFileSelected: + // Treat exactly as a File -> Open... . + break; + + case UIPhase::RequestingROMs: { + // Attempt to match up the dragged files to the requested ROM list; + // if and when they match, copy to a writeable QStandardPaths::AppDataLocation + // and try launchMachine() again. + + bool foundROM = false; + const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString(); + + for(const auto &url: event->mimeData()->urls()) { + const char *const name = url.toLocalFile().toUtf8(); + FILE *const file = fopen(name, "rb"); + const auto contents = fileContentsAndClose(file); + if(!contents) continue; + + CRC::CRC32 generator; + const uint32_t crc = generator.compute_crc(*contents); + + for(const auto &rom: missingRoms) { + if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) { + foundROM = true; + + // Ensure the destination folder exists. + const std::string path = appDataLocation + "/ROMImages/" + rom.machine_name; + QDir dir(QString::fromStdString(path)); + if (!dir.exists()) + dir.mkpath("."); + + // Write into place. + const std::string destination = QDir::toNativeSeparators(QString::fromStdString(path+ "/" + rom.file_name)).toStdString(); + FILE *const target = fopen(destination.c_str(), "wb"); + fwrite(contents->data(), 1, contents->size(), target); + fclose(target); + } + } + } + + if(foundROM) launchMachine(); + } break; + + case UIPhase::RunningMachine: + // Attempt to insert into the running machine. + qDebug() << "Should start machine"; + break; + } +} diff --git a/OSBindings/Qt/mainwindow.h b/OSBindings/Qt/mainwindow.h index 4e21a1055..819e56323 100644 --- a/OSBindings/Qt/mainwindow.h +++ b/OSBindings/Qt/mainwindow.h @@ -4,6 +4,10 @@ #include #include #include "timer.h" +#include "ui_mainwindow.h" + +#include "../../Analyser/Static/StaticAnalyser.hpp" +#include "../../Machines/Utility/MachineForTarget.hpp" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -24,6 +28,19 @@ class MainWindow : public QMainWindow { std::unique_ptr timerThread; std::unique_ptr timer; + // Initial setup stuff. + Analyser::Static::TargetList targets; + enum class UIPhase { + NoFileSelected, RequestingROMs, RunningMachine + } uiPhase = UIPhase::NoFileSelected; + void launchMachine(); + + QString romRequestBaseText; + std::vector missingRoms; + + // File drag and drop is supported. + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; private slots: void open(); diff --git a/OSBindings/Qt/mainwindow.ui b/OSBindings/Qt/mainwindow.ui index 6537e2903..daf8834a0 100644 --- a/OSBindings/Qt/mainwindow.ui +++ b/OSBindings/Qt/mainwindow.ui @@ -23,18 +23,42 @@ MainWindow - - - - - 0 - 0 - 800 - 22 - - + + + + 170 + 170 + 421 + 231 + + + + + 0 + 0 + + + + true + + + true + + + Clock Signal requires you to provide images of the system ROMs for this machine. They will be stored permanently; you need do this only once. + +Please drag and drop the following over this window: + + + + + + + Do this + +