Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2021-06-11 19:44:02 -07:00
commit b5d7a37aa6
30 changed files with 3124 additions and 2887 deletions

View File

@ -48,6 +48,7 @@ Features:
- New unlicensed GB mappers: Pokémon Jade/Diamond, BBD, and Hitek - New unlicensed GB mappers: Pokémon Jade/Diamond, BBD, and Hitek
- Stack tracing tools in ARM debugger (by ahigerd) - Stack tracing tools in ARM debugger (by ahigerd)
- Command scripts for CLI debugger (by ahigerd) - Command scripts for CLI debugger (by ahigerd)
- Scheduled event dumping in CLI debugger
- ARM disassembler now resolves addresses to symbol names - ARM disassembler now resolves addresses to symbol names
- Add Game Boy Player feature support to ports - Add Game Boy Player feature support to ports
- Individual window types can now be toggled in debugging views - Individual window types can now be toggled in debugging views
@ -130,6 +131,7 @@ Misc:
- Core: Improve support for ROM patch cheats, supporting disabling overlapping patches - Core: Improve support for ROM patch cheats, supporting disabling overlapping patches
- GB: Allow pausing event loop while CPU is blocked - GB: Allow pausing event loop while CPU is blocked
- GB: Add support for sleep and shutdown callbacks - GB: Add support for sleep and shutdown callbacks
- GB Core: Return the current number of banks for ROM/SRAM, not theoretical max
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468) - GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
- GBA: Allow pausing event loop while CPU is blocked - GBA: Allow pausing event loop while CPU is blocked
- GBA BIOS: Division by zero should emit a FATAL error - GBA BIOS: Division by zero should emit a FATAL error
@ -152,6 +154,8 @@ Misc:
- Qt: Redo OpenGL context thread handling (fixes mgba.io/i/1724) - Qt: Redo OpenGL context thread handling (fixes mgba.io/i/1724)
- Qt: Discard additional frame draws if waiting fails - Qt: Discard additional frame draws if waiting fails
- Qt: Unify monospace font usage - Qt: Unify monospace font usage
- Qt: Add button to jump to log settings
- Qt: Use relative paths in portable mode when applicable (fixes mgba.io/i/838)
- SDL: Fall back to sw blit if OpenGL init fails - SDL: Fall back to sw blit if OpenGL init fails
- Util: Reset vector size on deinit - Util: Reset vector size on deinit
- VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730) - VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)

View File

@ -203,6 +203,7 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size); void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size);
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask); void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask);
const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint32_t address);
#ifdef USE_ELF #ifdef USE_ELF
struct ELF; struct ELF;

View File

@ -107,14 +107,17 @@ static void _patchROM(struct mCheatDevice* device, struct mCheatSet* cheats) {
struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i); struct mCheatPatch* patch = mCheatPatchListGetPointer(&cheats->romPatches, i);
int segment = -1; int segment = -1;
if (patch->check && patch->segment < 0) { if (patch->check && patch->segment < 0) {
int maxSegment = 0; const struct mCoreMemoryBlock* block = mCoreGetMemoryBlockInfo(device->p, patch->address);
for (segment = 0; segment < maxSegment; ++segment) { if (!block) {
continue;
}
for (segment = 0; segment < block->maxSegment; ++segment) {
uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width); uint32_t value = _readMemSegment(device->p, patch->address, segment, patch->width);
if (value == patch->checkValue) { if (value == patch->checkValue) {
break; break;
} }
} }
if (segment == maxSegment) { if (segment == block->maxSegment) {
continue; continue;
} }
} }

View File

@ -373,6 +373,17 @@ void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
} }
void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) { void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) {
const struct mCoreMemoryBlock* block = mCoreGetMemoryBlockInfo(core, start);
if (!block || !(block->flags & mask)) {
return NULL;
}
uint8_t* out = core->getMemoryBlock(core, block->id, size);
out += start - block->start;
*size -= start - block->start;
return out;
}
const struct mCoreMemoryBlock* mCoreGetMemoryBlockInfo(struct mCore* core, uint32_t address) {
const struct mCoreMemoryBlock* blocks; const struct mCoreMemoryBlock* blocks;
size_t nBlocks = core->listMemoryBlocks(core, &blocks); size_t nBlocks = core->listMemoryBlocks(core, &blocks);
size_t i; size_t i;
@ -380,19 +391,13 @@ void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size
if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) { if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
continue; continue;
} }
if (!(blocks[i].flags & mask)) { if (address < blocks[i].start) {
continue; continue;
} }
if (start < blocks[i].start) { if (address >= blocks[i].start + blocks[i].size) {
continue; continue;
} }
if (start >= blocks[i].start + blocks[i].size) { return &blocks[i];
continue;
}
uint8_t* out = core->getMemoryBlock(core, blocks[i].id, size);
out += start - blocks[i].start;
*size -= start - blocks[i].start;
return out;
} }
return NULL; return NULL;
} }

View File

@ -8,6 +8,7 @@
#include <mgba/internal/debugger/symbols.h> #include <mgba/internal/debugger/symbols.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/timing.h>
#include <mgba/core/version.h> #include <mgba/core/version.h>
#include <mgba/internal/debugger/parser.h> #include <mgba/internal/debugger/parser.h>
#include <mgba-util/string.h> #include <mgba-util/string.h>
@ -66,6 +67,7 @@ static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*);
static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
static void _events(struct CLIDebugger*, struct CLIDebugVector*);
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
static void _source(struct CLIDebugger*, struct CLIDebugVector*); static void _source(struct CLIDebugger*, struct CLIDebugVector*);
#endif #endif
@ -81,6 +83,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "continue", _continue, "", "Continue execution" }, { "continue", _continue, "", "Continue execution" },
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" }, { "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" }, { "disassemble", _disassemble, "Ii", "Disassemble instructions" },
{ "events", _events, "", "Print list of scheduled events" },
{ "finish", _finish, "", "Execute until current stack frame returns" }, { "finish", _finish, "", "Execute until current stack frame returns" },
{ "help", _printHelp, "S", "Print help" }, { "help", _printHelp, "S", "Print help" },
{ "listb", _listBreakpoints, "", "List breakpoints" }, { "listb", _listBreakpoints, "", "List breakpoints" },
@ -768,6 +771,15 @@ static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv
debugger->system->printStatus(debugger->system); debugger->system->printStatus(debugger->system);
} }
static void _events(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
UNUSED(dv);
struct mTiming* timing = debugger->d.core->timing;
struct mTimingEvent* next = timing->root;
for (; next; next = next->next) {
debugger->backend->printf(debugger->backend, "%s in %i cycles\n", next->name, mTimingUntil(timing, next));
}
}
struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) { struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) {
if (!string || length < 1) { if (!string || length < 1) {
return 0; return 0;

View File

@ -80,6 +80,7 @@ struct GBCore {
const struct Configuration* overrides; const struct Configuration* overrides;
struct mDebuggerPlatform* debuggerPlatform; struct mDebuggerPlatform* debuggerPlatform;
struct mCheatDevice* cheatDevice; struct mCheatDevice* cheatDevice;
struct mCoreMemoryBlock memoryBlocks[8];
}; };
static bool _GBCoreInit(struct mCore* core) { static bool _GBCoreInit(struct mCore* core) {
@ -101,6 +102,7 @@ static bool _GBCoreInit(struct mCore* core) {
#ifndef MINIMAL_CORE #ifndef MINIMAL_CORE
gbcore->logContext = NULL; gbcore->logContext = NULL;
#endif #endif
memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
GBCreate(gb); GBCreate(gb);
memset(gbcore->components, 0, sizeof(gbcore->components)); memset(gbcore->components, 0, sizeof(gbcore->components));
@ -571,6 +573,26 @@ static void _GBCoreReset(struct mCore* core) {
} }
#endif #endif
if (gb->model < GB_MODEL_CGB) {
memcpy(gbcore->memoryBlocks, _GBMemoryBlocks, sizeof(_GBMemoryBlocks));
} else {
memcpy(gbcore->memoryBlocks, _GBCMemoryBlocks, sizeof(_GBCMemoryBlocks));
}
size_t i;
for (i = 0; i < sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks); ++i) {
if (gbcore->memoryBlocks[i].id == GB_REGION_CART_BANK0) {
gbcore->memoryBlocks[i].maxSegment = gb->memory.romSize / GB_SIZE_CART_BANK0;
} else if (gbcore->memoryBlocks[i].id == GB_REGION_EXTERNAL_RAM) {
gbcore->memoryBlocks[i].maxSegment = gb->sramSize / GB_SIZE_EXTERNAL_RAM;
} else {
continue;
}
if (gbcore->memoryBlocks[i].maxSegment) {
--gbcore->memoryBlocks[i].maxSegment;
}
}
SM83Reset(core->cpu); SM83Reset(core->cpu);
if (core->opts.skipBios) { if (core->opts.skipBios) {
@ -755,20 +777,9 @@ static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment,
} }
size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) { size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
const struct GB* gb = core->board; struct GBCore* gbcore = (struct GBCore*) core;
switch (gb->model) { *blocks = gbcore->memoryBlocks;
case GB_MODEL_DMG: return sizeof(gbcore->memoryBlocks) / sizeof(*gbcore->memoryBlocks);
case GB_MODEL_MGB:
case GB_MODEL_SGB:
case GB_MODEL_SGB2:
default:
*blocks = _GBMemoryBlocks;
return sizeof(_GBMemoryBlocks) / sizeof(*_GBMemoryBlocks);
case GB_MODEL_CGB:
case GB_MODEL_AGB:
*blocks = _GBCMemoryBlocks;
return sizeof(_GBCMemoryBlocks) / sizeof(*_GBCMemoryBlocks);
}
} }
void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) { void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {

View File

@ -707,15 +707,16 @@ void GBProcessEvents(struct SM83Core* cpu) {
int32_t nextEvent; int32_t nextEvent;
cpu->cycles = 0; cpu->cycles = 0;
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += cycles;
#endif
cpu->nextEvent = INT_MAX; cpu->nextEvent = INT_MAX;
nextEvent = cycles; nextEvent = cycles;
do { do {
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += nextEvent;
#endif
nextEvent = mTimingTick(&gb->timing, nextEvent); nextEvent = mTimingTick(&gb->timing, nextEvent);
} while (gb->cpuBlocked && !gb->earlyExit); } while (gb->cpuBlocked);
// This loop cannot early exit until the SM83 run loop properly handles mid-M-cycle-exits
cpu->nextEvent = nextEvent; cpu->nextEvent = nextEvent;
if (cpu->halted) { if (cpu->halted) {

View File

@ -595,10 +595,10 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
memset(&softwareRenderer->row[startX], 0, (endX - startX) * sizeof(softwareRenderer->row[0])); memset(&softwareRenderer->row[startX], 0, (endX - startX) * sizeof(softwareRenderer->row[0]));
} }
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
if (startX == 0) { if (startX == 0) {
_cleanOAM(softwareRenderer, y); _cleanOAM(softwareRenderer, y);
} }
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
int i; int i;
for (i = 0; i < softwareRenderer->objMax; ++i) { for (i = 0; i < softwareRenderer->objMax; ++i) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, &softwareRenderer->obj[i], startX, endX, y); GBVideoSoftwareRendererDrawObj(softwareRenderer, &softwareRenderer->obj[i], startX, endX, y);

View File

@ -288,7 +288,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
int32_t cycles = cpu->cycles; int32_t cycles = cpu->cycles;
cpu->cycles = 0; cpu->cycles = 0;
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
gba->timing.globalCycles += cycles; gba->timing.globalCycles += cycles < nextEvent ? nextEvent : cycles;
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
if (cycles < 0) { if (cycles < 0) {

View File

@ -6,13 +6,14 @@
#include "LogView.h" #include "LogView.h"
#include "LogController.h" #include "LogController.h"
#include "Window.h"
#include <QTextBlock> #include <QTextBlock>
#include <QTextCursor> #include <QTextCursor>
using namespace QGBA; using namespace QGBA;
LogView::LogView(LogController* log, QWidget* parent) LogView::LogView(LogController* log, Window* window, QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
@ -38,6 +39,9 @@ LogView::LogView(LogController* log, QWidget* parent)
setLevel(mLOG_GAME_ERROR, set); setLevel(mLOG_GAME_ERROR, set);
}); });
connect(m_ui.clear, &QAbstractButton::clicked, this, &LogView::clear); connect(m_ui.clear, &QAbstractButton::clicked, this, &LogView::clear);
connect(m_ui.advanced, &QAbstractButton::clicked, this, [window]() {
window->openSettingsWindow(SettingsView::Page::LOGGING);
});
connect(m_ui.maxLines, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), connect(m_ui.maxLines, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
this, &LogView::setMaxLines); this, &LogView::setMaxLines);
m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT); m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT);

View File

@ -13,12 +13,13 @@
namespace QGBA { namespace QGBA {
class LogController; class LogController;
class Window;
class LogView : public QWidget { class LogView : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
LogView(LogController* log, QWidget* parent = nullptr); LogView(LogController* log, Window* window, QWidget* parent = nullptr);
signals: signals:
void levelsEnabled(int levels); void levelsEnabled(int levels);

View File

@ -102,6 +102,13 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="advanced">
<property name="text">
<string>Advanced settings</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer_2">
<property name="orientation"> <property name="orientation">

View File

@ -24,7 +24,7 @@
using namespace QGBA; using namespace QGBA;
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent) SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
, m_controller(controller) , m_controller(controller)
, m_input(inputController) , m_input(inputController)
@ -32,7 +32,17 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
m_pageIndex[Page::AV] = 0;
m_pageIndex[Page::INTERFACE] = 1;
m_pageIndex[Page::EMULATION] = 2;
m_pageIndex[Page::ENHANCEMENTS] = 3;
m_pageIndex[Page::BIOS] = 4;
m_pageIndex[Page::PATHS] = 5;
m_pageIndex[Page::LOGGING] = 6;
#ifdef M_CORE_GB #ifdef M_CORE_GB
m_pageIndex[Page::GB] = 7;
for (auto model : GameBoy::modelList()) { for (auto model : GameBoy::modelList()) {
m_ui.gbModel->addItem(GameBoy::modelName(model), model); m_ui.gbModel->addItem(GameBoy::modelName(model), model);
m_ui.sgbModel->addItem(GameBoy::modelName(model), model); m_ui.sgbModel->addItem(GameBoy::modelName(model), model);
@ -75,11 +85,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.savegameBrowse, &QAbstractButton::pressed, [this] () { connect(m_ui.savegameBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); selectPath(m_ui.savegamePath, m_ui.savegameSameDir);
if (!path.isNull()) {
m_ui.savegameSameDir->setChecked(false);
m_ui.savegamePath->setText(path);
}
}); });
if (m_ui.savestatePath->text().isEmpty()) { if (m_ui.savestatePath->text().isEmpty()) {
@ -91,11 +97,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.savestateBrowse, &QAbstractButton::pressed, [this] () { connect(m_ui.savestateBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); selectPath(m_ui.savestatePath, m_ui.savestateSameDir);
if (!path.isNull()) {
m_ui.savestateSameDir->setChecked(false);
m_ui.savestatePath->setText(path);
}
}); });
if (m_ui.screenshotPath->text().isEmpty()) { if (m_ui.screenshotPath->text().isEmpty()) {
@ -107,11 +109,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.screenshotBrowse, &QAbstractButton::pressed, [this] () { connect(m_ui.screenshotBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); selectPath(m_ui.screenshotPath, m_ui.screenshotSameDir);
if (!path.isNull()) {
m_ui.screenshotSameDir->setChecked(false);
m_ui.screenshotPath->setText(path);
}
}); });
if (m_ui.patchPath->text().isEmpty()) { if (m_ui.patchPath->text().isEmpty()) {
@ -123,11 +121,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.patchBrowse, &QAbstractButton::pressed, [this] () { connect(m_ui.patchBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); selectPath(m_ui.patchPath, m_ui.patchSameDir);
if (!path.isNull()) {
m_ui.patchSameDir->setChecked(false);
m_ui.patchPath->setText(path);
}
}); });
if (m_ui.cheatsPath->text().isEmpty()) { if (m_ui.cheatsPath->text().isEmpty()) {
@ -139,11 +133,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () { connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () {
QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); selectPath(m_ui.cheatsPath, m_ui.cheatsSameDir);
if (!path.isNull()) {
m_ui.cheatsSameDir->setChecked(false);
m_ui.cheatsPath->setText(path);
}
}); });
connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared); connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared);
@ -325,6 +315,11 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
m_ui.logFile->setText(path); m_ui.logFile->setText(path);
} }
}); });
ShortcutView* shortcutView = new ShortcutView();
shortcutView->setController(shortcutController);
shortcutView->setInputController(inputController);
addPage(tr("Shortcuts"), shortcutView, Page::SHORTCUTS);
} }
SettingsView::~SettingsView() { SettingsView::~SettingsView() {
@ -352,10 +347,33 @@ void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) {
#endif #endif
} }
void SettingsView::selectPage(SettingsView::Page page) {
m_ui.tabs->setCurrentRow(m_pageIndex[page]);
}
QString SettingsView::makePortablePath(const QString& path) {
if (m_controller->isPortable()) {
QDir configDir(m_controller->configDir());
QFileInfo pathInfo(path);
if (pathInfo.canonicalPath() == configDir.canonicalPath()) {
return configDir.relativeFilePath(pathInfo.canonicalFilePath());
}
}
return path;
}
void SettingsView::selectBios(QLineEdit* bios) { void SettingsView::selectBios(QLineEdit* bios) {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS")); QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS"));
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
bios->setText(filename); bios->setText(makePortablePath(filename));
}
}
void SettingsView::selectPath(QLineEdit* field, QCheckBox* sameDir) {
QString path = GBAApp::app()->getOpenDirectoryName(this, tr("Select directory"));
if (!path.isNull()) {
sameDir->setChecked(false);
field->setText(makePortablePath(path));
} }
} }
@ -705,6 +723,12 @@ void SettingsView::reloadConfig() {
loadSetting("videoScale", m_ui.videoScale, 1); loadSetting("videoScale", m_ui.videoScale, 1);
} }
void SettingsView::addPage(const QString& name, QWidget* view, Page index) {
m_pageIndex[index] = m_ui.tabs->count();
m_ui.tabs->addItem(name);
m_ui.stackedWidget->addWidget(view);
}
void SettingsView::saveSetting(const char* key, const QAbstractButton* field) { void SettingsView::saveSetting(const char* key, const QAbstractButton* field) {
m_controller->setOption(key, field->isChecked()); m_controller->setOption(key, field->isChecked());
m_controller->updateOption(key); m_controller->updateOption(key);

View File

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <QDialog> #include <QDialog>
#include <QMap>
#include "ColorPicker.h" #include "ColorPicker.h"
#include "LogConfigModel.h" #include "LogConfigModel.h"
@ -24,12 +25,28 @@ class ConfigController;
class InputController; class InputController;
class InputIndex; class InputIndex;
class ShaderSelector; class ShaderSelector;
class ShortcutController;
class SettingsView : public QDialog { class SettingsView : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent = nullptr); enum class Page {
AV,
INTERFACE,
EMULATION,
ENHANCEMENTS,
BIOS,
PATHS,
LOGGING,
GB,
KEYBOARD,
CONTROLLERS,
SHORTCUTS,
SHADERS,
};
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent = nullptr);
~SettingsView(); ~SettingsView();
void setShaderSelector(ShaderSelector* shaderSelector); void setShaderSelector(ShaderSelector* shaderSelector);
@ -45,8 +62,12 @@ signals:
void languageChanged(); void languageChanged();
void libraryCleared(); void libraryCleared();
public slots:
void selectPage(Page);
private slots: private slots:
void selectBios(QLineEdit*); void selectBios(QLineEdit*);
void selectPath(QLineEdit*, QCheckBox*);
void updateConfig(); void updateConfig();
void reloadConfig(); void reloadConfig();
@ -63,6 +84,12 @@ private:
ColorPicker m_colorPickers[12]; ColorPicker m_colorPickers[12];
#endif #endif
QMap<Page, int> m_pageIndex;
QString makePortablePath(const QString& path);
void addPage(const QString& name, QWidget* view, Page index);
void saveSetting(const char* key, const QAbstractButton*); void saveSetting(const char* key, const QAbstractButton*);
void saveSetting(const char* key, const QComboBox*); void saveSetting(const char* key, const QComboBox*);
void saveSetting(const char* key, const QDoubleSpinBox*); void saveSetting(const char* key, const QDoubleSpinBox*);

View File

@ -50,7 +50,6 @@
#include "ReportView.h" #include "ReportView.h"
#include "ROMInfo.h" #include "ROMInfo.h"
#include "SensorView.h" #include "SensorView.h"
#include "SettingsView.h"
#include "ShaderSelector.h" #include "ShaderSelector.h"
#include "ShortcutController.h" #include "ShortcutController.h"
#include "TileView.h" #include "TileView.h"
@ -104,7 +103,7 @@ using namespace QGBA;
Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWidget* parent) Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWidget* parent)
: QMainWindow(parent) : QMainWindow(parent)
, m_manager(manager) , m_manager(manager)
, m_logView(new LogView(&m_log)) , m_logView(new LogView(&m_log, this))
, m_screenWidget(new WindowBackground()) , m_screenWidget(new WindowBackground())
, m_config(config) , m_config(config)
, m_inputController(playerId, this) , m_inputController(playerId, this)
@ -501,7 +500,11 @@ void Window::exportSharkport() {
} }
void Window::openSettingsWindow() { void Window::openSettingsWindow() {
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, &m_log); openSettingsWindow(SettingsView::Page::AV);
}
void Window::openSettingsWindow(SettingsView::Page page) {
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController, &m_log);
#if defined(BUILD_GL) || defined(BUILD_GLES2) #if defined(BUILD_GL) || defined(BUILD_GLES2)
if (m_display->supportsShaders()) { if (m_display->supportsShaders()) {
settingsWindow->setShaderSelector(m_shaderView.get()); settingsWindow->setShaderSelector(m_shaderView.get());
@ -518,6 +521,7 @@ void Window::openSettingsWindow() {
connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear); connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear);
#endif #endif
openView(settingsWindow); openView(settingsWindow);
settingsWindow->selectPage(page);
} }
void Window::startVideoLog() { void Window::startVideoLog() {

View File

@ -22,6 +22,8 @@
#include "InputController.h" #include "InputController.h"
#include "LoadSaveState.h" #include "LoadSaveState.h"
#include "LogController.h" #include "LogController.h"
#include "SettingsView.h"
struct mArguments; struct mArguments;
namespace QGBA { namespace QGBA {
@ -95,6 +97,7 @@ public slots:
void exportSharkport(); void exportSharkport();
void openSettingsWindow(); void openSettingsWindow();
void openSettingsWindow(SettingsView::Page);
void startVideoLog(); void startVideoLog();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -915,7 +915,7 @@ void _gameUnloaded(struct mGUIRunner* runner) {
void _gameLoaded(struct mGUIRunner* runner) { void _gameLoaded(struct mGUIRunner* runner) {
reconfigureScreen(runner); reconfigureScreen(runner);
if (runner->core->platform(runner->core) == PLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) { if (runner->core->platform(runner->core) == mPLATFORM_GBA && ((struct GBA*) runner->core->board)->memory.hw.devices & HW_GYRO) {
int i; int i;
for (i = 0; i < 6; ++i) { for (i = 0; i < 6; ++i) {
u32 result = WPAD_SetMotionPlus(0, 1); u32 result = WPAD_SetMotionPlus(0, 1);