Skip to content
This repository has been archived by the owner on Sep 16, 2024. It is now read-only.

Commit

Permalink
Add downloader window
Browse files Browse the repository at this point in the history
  • Loading branch information
OFFTKP committed Nov 25, 2023
1 parent 1cf01d2 commit 5a7ead4
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 14 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<!-- [![hydra](https://img.shields.io/aur/version/hydra?color=1793d1&label=yay&logo=arch-linux&style=for-the-badge)](https://aur.archlinux.org/packages/hydra) -->
<!-- [![GitHub license](https://img.shields.io/github/license/offtkp/hydra?color=333333&style=for-the-badge)](https://github.com/offtkp/hydra/blob/master/LICENSE)
![Commit activity](https://img.shields.io/github/commit-activity/m/OFFTKP/hydra?style=for-the-badge)
![Stars](https://img.shields.io/github/stars/OFFTKP/hydra?style=for-the-badge)
![Size](https://img.shields.io/github/repo-size/OFFTKP/hydra?style=for-the-badge) -->
![Stars](https://img.shields.io/github/stars/OFFTKP/hydra)

<p align="center"> <img src="./data/images/hydra.png"><img src="./data/images/logo.png"></p>

----

<p align="center">A multi-system emulator</p>
<p align="center"><img src="./data/images/screen.png"><img src="./data/images/android.png" height=520></p>
<p align="center"><img src="./data/images/screen.png"><img src="./data/images/android_screenshot.png" height=520></p>

<p align="center">⚠️ <b>This project is very early in development and not ready for use yet!</b> ⚠️</p>

Expand Down
Binary file added data/images/android_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions include/download.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace hydra
else
{
printf("Failed to parse URL: %s\n", url.c_str());
return {};
}
return std::make_pair(host, query);
}
Expand All @@ -40,6 +41,11 @@ namespace hydra
try
{
auto [host, query] = split_url(url);
if (host.empty() || query.empty())
{
return {};
}

httplib::Client client(host);
client.set_follow_location(true);
httplib::Result response =
Expand Down
6 changes: 3 additions & 3 deletions include/hsystem.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ inline std::string hydra_os()
#endif

#if defined(HYDRA_X86_64)
ret += " x86_64";
ret += " x64";
#elif defined(HYDRA_X86)
ret += " x86";
#elif defined(HYDRA_ARM64)
ret += " ARM64";
ret += " arm64";
#elif defined(HYDRA_ARM)
ret += " ARM";
ret += " arm32";
#else
ret += " Unknown";
#endif
Expand Down
55 changes: 55 additions & 0 deletions include/observer.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Helper structs for observer pattern

#pragma once

#include <algorithm>
#include <vector>

namespace hydra
{
struct Subject;

struct Observer
{
explicit Observer(Subject* subject);

virtual ~Observer() = default;
virtual void update() = 0;

Observer(const Observer&) = delete;
Observer& operator=(const Observer&) = delete;

protected:
Subject* subject_;
};

struct Subject
{
void notify()
{
for (auto& observer : observers_)
{
observer->update();
}
}

void attach(Observer* observer)
{
observers_.push_back(observer);
}

void detach(Observer* observer)
{
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
observers_.end());
}

private:
std::vector<Observer*> observers_;
};

inline Observer::Observer(Subject* subject) : subject_(subject)
{
subject_->attach(this);
}
} // namespace hydra
31 changes: 31 additions & 0 deletions include/update.hxx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "compatibility.hxx"
#include "corewrapper.hxx"
#include "json.hpp"
#include "settings.hxx"
#include <download.hxx>
Expand Down Expand Up @@ -185,6 +186,36 @@ namespace hydra
return database;
}

static void InstallCore(const std::vector<uint8_t>& zipped_core)
{
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));

if (!mz_zip_reader_init_mem(&zip_archive, zipped_core.data(), zipped_core.size(), 0))
log_fatal("Failed to read database zip");

if (mz_zip_reader_get_num_files(&zip_archive) != 1)
log_fatal("Invalid core zip");

mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat))
log_fatal("Failed to stat file in zip");

std::filesystem::path path = file_stat.m_filename;
if (path.extension() == hydra::dynlib_get_extension())
{
std::string data;
data.resize(file_stat.m_uncomp_size);
if (!mz_zip_reader_extract_to_mem(&zip_archive, 0, data.data(), data.size(), 0))
log_fatal("Failed to extract file from zip");

std::ofstream file(std::filesystem::path(Settings::Get("core_path")) /
path.filename(),
std::ios::binary);
file << data;
}
}

private:
static std::string get_database_time()
{
Expand Down
1 change: 0 additions & 1 deletion qt/cheatswindow.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "log.h"
#include "settings.hxx"

#include <qcheckbox.h>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
Expand Down
2 changes: 1 addition & 1 deletion qt/cheatswindow.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <corewrapper.hxx>
#include <filesystem>
#include <memory>
#include <qaction.h>
#include <QAction>
#include <QWidget>

class QListWidget;
Expand Down
130 changes: 126 additions & 4 deletions qt/downloaderwindow.cxx
Original file line number Diff line number Diff line change
@@ -1,17 +1,129 @@
#include "downloaderwindow.hxx"
#include "download.hxx"
#include "hsystem.hxx"
#include "observer.hxx"
#include "update.hxx"
#include <cstdint>
#include <fmt/format.h>
#include <future>
#include <QFuture>
#include <QHBoxLayout>
#include <QLabel>
#include <QPixmap>
#include <QProgressBar>
#include <QPushButton>
#include <QtConcurrent/QtConcurrent>
#include <QTextEdit>
#include <QTreeWidget>
#include <QVBoxLayout>

class DownloadProgressBar : public QProgressBar, public hydra::Subject
{
public:
explicit DownloadProgressBar(QWidget* parent = nullptr) : QProgressBar(parent)
{
setMinimum(0);
setMaximum(100);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
setTextVisible(false);
}

void Download(const std::string& url)
{
if (watcher_)
{
printf("Watcher already exists...?\n");
exit(1);
}

setMinimum(0);
setMaximum(0);

auto func = [this](uint64_t current, uint64_t total) {
return update_callback(current, total);
};
QFuture<hydra::HydraBufferWrapper> future =
QtConcurrent::run(hydra::Downloader::DownloadProgress, url, func);
watcher_ = new QFutureWatcher<hydra::HydraBufferWrapper>;
watcher_->setFuture(future);
connect(watcher_, &QFutureWatcher<hydra::HydraBufferWrapper>::finished, this,
&DownloadProgressBar::download_finished);
notify();
}

bool IsDownloading() const
{
return watcher_ != nullptr;
}

private:
void download_finished()
{
hydra::Updater::InstallCore(watcher_->result());
watcher_->deleteLater();
watcher_ = nullptr;
setMinimum(0);
setMaximum(100);
setValue(100);
notify();
}

bool update_callback(uint64_t current, uint64_t total)
{
setMinimum(0);
if (current == 0 && total == 0)
{
setMaximum(0);
}
else
{
setMaximum(100);
setValue((current * 100) / total);
}
return true;
}

QFutureWatcher<hydra::HydraBufferWrapper>* watcher_ = nullptr;
};

class DownloadButton : public QPushButton, public hydra::Observer
{
public:
explicit DownloadButton(DownloadProgressBar* progress_bar, const std::string& url,
QWidget* parent = nullptr)
: QPushButton("Download", parent), hydra::Observer(progress_bar), url_(url),
progress_bar_(progress_bar)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setMaximumWidth(100);
setMaximumHeight(20);
connect(this, &QPushButton::clicked, this, &DownloadButton::download_clicked);
}

private:
void download_clicked()
{
progress_bar_->Download(url_);
}

void update() override
{
if (progress_bar_->IsDownloading())
{
setEnabled(false);
setText("Downloading...");
}
else
{
setEnabled(true);
setText("Download");
}
}

DownloadProgressBar* progress_bar_ = nullptr;
std::string url_;
};

DownloaderWindow::DownloaderWindow(QWidget* parent) : QWidget(parent, Qt::Window)
{
QTreeWidget* tree = new QTreeWidget;
Expand All @@ -22,7 +134,9 @@ DownloaderWindow::DownloaderWindow(QWidget* parent) : QWidget(parent, Qt::Window
tree->setIndentation(20);
tree->setColumnCount(1);

uint32_t minimum_size = 0;
auto database = hydra::Updater::GetDatabase();
DownloadProgressBar* bar = new DownloadProgressBar;
for (auto& [key, entries] : database)
{
QTreeWidgetItem* item = new QTreeWidgetItem;
Expand All @@ -45,29 +159,37 @@ DownloaderWindow::DownloaderWindow(QWidget* parent) : QWidget(parent, Qt::Window
layout->setContentsMargins(0, 0, 0, 0);
layout->addStretch();

uint32_t icons_size = 0;
for (auto& [key, _] : entry.Downloads)
{
QLabel* label = new QLabel;
std::string os = key.substr(0, key.find_first_of(' '));
QString path = ":/images/" + QString::fromStdString(os) + ".png";
label->setPixmap(QPixmap(path).scaled(16, 16, Qt::KeepAspectRatio));
layout->addWidget(label);
icons_size += 16;
}

QPushButton* button = new QPushButton("Download");
button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
button->setMaximumWidth(100);
button->setMaximumHeight(20);
DownloadButton* button = new DownloadButton(bar, entry.Downloads[hydra_os()]);
layout->addWidget(button);

item->addChild(child);
tree->setItemWidget(child, 0, widget);

uint32_t new_size = label->sizeHint().width() + button->sizeHint().width() + icons_size;
if (new_size > minimum_size)
minimum_size = new_size + 20;
}
}

QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(tree);
layout->addWidget(bar);
setLayout(layout);

setMinimumSize(minimum_size + 100, 0);
setWindowTitle("Core Downloader");

show();
}

Expand Down
3 changes: 3 additions & 0 deletions qt/downloaderwindow.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ class DownloaderWindow : public QWidget
public:
DownloaderWindow(QWidget* parent = nullptr);
~DownloaderWindow();

private:
void download_core(const std::string& url);
};
4 changes: 2 additions & 2 deletions qt/mainwindow.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,7 @@ void MainWindow::create_actions()
scripts_act_->setIcon(QIcon(":/images/scripts.png"));
scripts_act_->setCheckable(true);
connect(scripts_act_, &QAction::triggered, this, &MainWindow::action_scripts);
#ifndef HYDRA_USE_LUA
scripts_act_->setEnabled(false);
#endif

terminal_act_ = new QAction(tr("&Terminal"), this);
terminal_act_->setShortcut(Qt::Key_F9);
Expand Down Expand Up @@ -380,7 +378,9 @@ void MainWindow::create_menus()
tools_menu_ = menuBar()->addMenu(tr("&Tools"));
tools_menu_->addAction(cheats_act_);
tools_menu_->addAction(terminal_act_);
#ifdef HYDRA_USE_LUA
tools_menu_->addAction(scripts_act_);
#endif
help_menu_ = menuBar()->addMenu(tr("&Help"));
help_menu_->addAction(about_act_);
}
Expand Down
2 changes: 1 addition & 1 deletion qt/scripteditor.hxx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <qaction.h>
#include <QAction>
#include <QCheckBox>
#include <QList>
#include <QRegularExpression>
Expand Down

0 comments on commit 5a7ead4

Please sign in to comment.