mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
b5d7a37aa6
4
CHANGES
4
CHANGES
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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*);
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue