Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ Misc:
- Qt: Prevent both gyro and RTC sensors from being enabled at the same time
- Qt: Put fullscreen information in report view
- Qt: Add "clear console" menu item for the scripting view
- Qt: Replace save warning with a copy dialog (closes mgba.io/i/3637)
- Res: Port hq2x and OmniScale shaders from SameBoy
- Res: Port NSO-gba-colors shader (closes mgba.io/i/2834)
- Res: Update gba-colors shader (closes mgba.io/i/2976)
Expand Down
110 changes: 102 additions & 8 deletions src/platform/qt/CoreManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CoreManager.h"

#include <QMessageBox>

#include "GBAApp.h"
#include "CoreController.h"
#include "LogController.h"
#include "VFileDevice.h"
#include "utils.h"

#include <QDir>

Expand Down Expand Up @@ -71,16 +75,41 @@ CoreController* CoreManager::loadGame(const QString& path) {
}
archive->close(archive);
}
QDir dir(info.dir());
QDir tmpdir(QDir::tempPath());
if (info.canonicalFilePath().startsWith(tmpdir.canonicalPath())) {
LOG(QT, ERROR) << tr("The ROM appears to be loaded from a temporary directory. This will likely lead to data loss (e.g. saves, screenshots, etc.) if you continue. "
"Please put the ROM in a more suitable location and then re-open it. If you are loading the ROM from an archive, please extract the archive first.");
}
if (!vf) {
// Open bare file
vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY);
}

if (!vf) {
return nullptr;
}

QDir dir(info.dir());
QDir tmpdir(QDir::tempPath());
if (info.canonicalFilePath().startsWith(tmpdir.canonicalPath())) {
bool bad = false;
if (m_config) {
mCoreOptions opts;
mCoreConfigMap(m_config, &opts);
bad = bad || !opts.savegamePath;
bad = bad || !opts.savestatePath;
bad = bad || !opts.screenshotPath;
bad = bad || !opts.cheatsPath;
mCoreConfigFreeOpts(&opts);
} else {
bad = true;
}
if (bad) {
QString newPath = saveFailed(vf, tr("Temporary file loaded"),
tr("The ROM appears to be loaded from a temporary directory, perhaps automatically extracted from an archive (e.g. a zip file)."),
"*." + info.suffix());
if (!newPath.isEmpty()) {
vf->close(vf);
return loadGame(newPath);
}
}
}

return loadGame(vf, info.fileName(), dir.canonicalPath());
}

Expand Down Expand Up @@ -119,7 +148,15 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr
bytes = info.dir().canonicalPath().toUtf8();
mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));
if (!mCoreAutoloadSave(core)) {
LOG(QT, ERROR) << tr("Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows).");
QString filter = romFilters(false, core->platform(core), true);
QString newPath = saveFailed(vf, tr("Could not open save file"),
tr("Failed to open save file; in-game saves cannot be updated."),
filter);
if (!newPath.isEmpty()) {
mCoreConfigDeinit(&core->config);
core->deinit(core);
return loadGame(newPath);
}
}
mCoreAutoloadCheats(core);

Expand Down Expand Up @@ -178,7 +215,64 @@ CoreController* CoreManager::loadBIOS(int platform, const QString& path) {
if (m_multiplayer) {
cc->setMultiplayerController(m_multiplayer);
}
cc->setPath(path, info.dir().canonicalPath());
emit coreLoaded(cc);
return cc;
}

QString CoreManager::saveFailed(VFile* vf, const QString& title, const QString& summary, const QString& filter) {
int result = QMessageBox::critical(nullptr, title,
summary + "\n\n" + tr("Would you like to copy the ROM to a different location? If you don't, this will likely lead to data loss (e.g. saves, screenshots, etc.)."),
QMessageBox::Ok | QMessageBox::Ignore,QMessageBox::Ok);
if (result == QMessageBox::Ignore) {
return QString();
}

auto retry = [this]() {
int result = QMessageBox::critical(nullptr, tr("Copy failed"), tr("Failed to copy ROM. Do you want to try again?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
return result == QMessageBox::Yes;
};

bool ok = true;
while (ok) {
QString newPath = GBAApp::app()->getSaveFileName(nullptr, tr("New ROM location"), filter);
if (newPath.isEmpty()) {
return QString();
}

QFile newFile(newPath);
if (!newFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Truncate)) {
if (!retry()) {
ok = false;
}
continue;
}

vf->seek(vf, 0, SEEK_SET);
char buffer[4096];
while (ok) {
ssize_t read = vf->read(vf, buffer, sizeof(buffer));
if (read < 0) {
ok = false;
}
if (read <= 0) {
break;
}

qint64 written = newFile.write(buffer, read);
if (written < read) {
newFile.remove();
if (!retry()) {
ok = false;
}
break;
}
}

if (ok) {
return newPath;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps attempt to delete the destination file if it was created but not fully populated? I can imagine an edge case where you run into a disk-full condition and the partial ROM is eating up the last megabyte of space on a tiny SD card.


return QString();
}
2 changes: 2 additions & 0 deletions src/platform/qt/CoreManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public slots:
void coreLoaded(CoreController*);

private:
QString saveFailed(VFile* vf, const QString& title, const QString& summary, const QString& filter);

const mCoreConfig* m_config = nullptr;
MultiplayerController* m_multiplayer = nullptr;
bool m_preload = true;
Expand Down
4 changes: 2 additions & 2 deletions src/platform/qt/GBAApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void GBAApp::cleanup() {
bool GBAApp::event(QEvent* event) {
if (event->type() == QEvent::FileOpen) {
CoreController* core = m_manager.loadGame(static_cast<QFileOpenEvent*>(event)->file());
m_windows[0]->setController(core, static_cast<QFileOpenEvent*>(event)->file());
m_windows[0]->setController(core);
return true;
}
return QApplication::event(event);
Expand Down Expand Up @@ -416,7 +416,7 @@ void GBAApp::initMultiplayer() {
break;
}
CoreController* core = m_manager.loadGame(fname);
w->setController(core, fname);
w->setController(core);
w = nullptr;
}
}
Expand Down
37 changes: 24 additions & 13 deletions src/platform/qt/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
VFile* output = m_libraryView->selectedVFile();
if (output) {
QPair<QString, QString> path = m_libraryView->selectedPath();
setController(m_manager->loadGame(output, path.second, path.first), path.first + "/" + path.second);
setController(m_manager->loadGame(output, path.second, path.first));
}
});
#endif
Expand Down Expand Up @@ -250,7 +250,7 @@ void Window::argumentsPassed() {
}

if (args->fname) {
setController(m_manager->loadGame(args->fname), args->fname);
setController(m_manager->loadGame(args->fname));
}

if (m_config->graphicsOpts()->fullscreen) {
Expand Down Expand Up @@ -355,7 +355,7 @@ QString Window::getFiltersArchive() const {
void Window::selectROM() {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), romFilters(true));
if (!filename.isEmpty()) {
setController(m_manager->loadGame(filename), filename);
setController(m_manager->loadGame(filename));
}
}

Expand All @@ -364,7 +364,7 @@ void Window::bootBIOS() {
if (bios.isEmpty()) {
bios = m_config->getOption("bios");
}
setController(m_manager->loadBIOS(mPLATFORM_GBA, bios), QString());
setController(m_manager->loadBIOS(mPLATFORM_GBA, bios));
}

#ifdef USE_SQLITE3
Expand All @@ -378,7 +378,7 @@ void Window::selectROMInArchive() {
VFile* output = archiveInspector->selectedVFile();
QPair<QString, QString> path = archiveInspector->selectedPath();
if (output) {
setController(m_manager->loadGame(output, path.second, path.first), path.first + "/" + path.second);
setController(m_manager->loadGame(output, path.second, path.first));
}
archiveInspector->close();
});
Expand Down Expand Up @@ -839,7 +839,7 @@ void Window::dropEvent(QDropEvent* event) {
return;
}
event->accept();
setController(m_manager->loadGame(url.toLocalFile()), url.toLocalFile());
setController(m_manager->loadGame(url.toLocalFile()));
}

#ifndef Q_OS_MAC
Expand Down Expand Up @@ -2076,7 +2076,7 @@ void Window::updateMRU() {
for (const QString& file : m_mruFiles) {
QString displayName(QDir::toNativeSeparators(file).replace("&", "&&"));
m_actions.addAction(displayName, QString("mru.%1").arg(QString::number(i)), [this, file]() {
setController(m_manager->loadGame(file), file);
setController(m_manager->loadGame(file));
}, "mru", QString("Ctrl+%1").arg(i));
++i;
}
Expand Down Expand Up @@ -2175,7 +2175,7 @@ void Window::updateFrame() {
m_screenWidget->setPixmap(pixmap);
}

void Window::setController(CoreController* controller, const QString& fname) {
void Window::setController(CoreController* controller) {
if (!controller) {
return;
}
Expand All @@ -2185,14 +2185,25 @@ void Window::setController(CoreController* controller, const QString& fname) {

if (m_controller) {
m_controller->stop();
QTimer::singleShot(0, this, [this, controller, fname]() {
setController(controller, fname);
QTimer::singleShot(0, this, [this, controller]() {
setController(controller);
});
return;
}
if (!fname.isEmpty()) {
setWindowFilePath(fname);
appendMRU(fname);

QString baseDirectory = controller->baseDirectory();
QString path = controller->path();
if (!path.isEmpty()) {
QString fname;
if (baseDirectory.isEmpty()) {
fname = path;
} else {
fname = QFileInfo(QDir(baseDirectory), path).filePath();
}
if (!fname.isEmpty()) {
setWindowFilePath(fname);
appendMRU(fname);
}
}

if (!m_display) {
Expand Down
2 changes: 1 addition & 1 deletion src/platform/qt/Window.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Q_OBJECT
void shaderSelectorAdded(ShaderSelector*);

public slots:
void setController(CoreController* controller, const QString& fname);
void setController(CoreController* controller);
void selectROM();
void bootBIOS();
#ifdef USE_SQLITE3
Expand Down