Merge branch 'feature/input-revamp' into medusa

This commit is contained in:
Vicki Pfau 2017-07-24 07:55:12 -07:00
commit 394a2e0bb8
78 changed files with 2200 additions and 695 deletions

View File

@ -3,15 +3,22 @@ if [ $TRAVIS_OS_NAME = "osx" ]; then
brew update
brew install qt5 ffmpeg imagemagick sdl2 libzip libpng
if [ "$CC" == "gcc" ]; then
brew install gcc@4.9
export CC=gcc-4.9
export CXX=g++-4.9
brew install gcc@5
export CC=gcc-5
export CXX=g++-5
fi
else
sudo apt-get clean
sudo add-apt-repository -y ppa:george-edison55/cmake-3.x
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install -y -q cmake libedit-dev libmagickwand-dev \
libpng-dev libsdl2-dev libzip-dev qtbase5-dev \
libqt5opengl5-dev qtmultimedia5-dev libavcodec-dev \
libavutil-dev libavformat-dev libavresample-dev libswscale-dev
if [ "$CC" == "gcc" ]; then
sudo apt-get install -y -q gcc-5 g++-5
export CC=gcc-5
export CXX=g++-5
fi
fi

View File

@ -2,9 +2,6 @@ language: c
sudo: required
matrix:
include:
- os: linux
dist: trusty
compiler: clang
- os: linux
dist: trusty
compiler: gcc
@ -16,4 +13,4 @@ matrix:
before_install:
- source ./.travis-deps.sh
script: mkdir build && cd build && cmake -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 .. && make
script: mkdir build && cd build && cmake -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 .. && make -j2

View File

@ -23,10 +23,17 @@ Features:
Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
- Python: Fix importing .gb or .gba before .core
<<<<<<< HEAD
=======
- GBA: Reset active region as needed when loading a ROM
- Qt: Fix command line debugger closing second game
>>>>>>> feature/input-revamp
Misc:
- GBA Timer: Use global cycles for timers
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
- All: Make FIXED_ROM_BUFFER an option instead of 3DS-only
- Qt: Don't rebuild library view if style hasn't changed
- Qt: Redo GameController into multiple classes
0.6.0: (2017-07-16)
Features:

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.11)
cmake_minimum_required(VERSION 3.1)
project(medusa)
set(BINARY_NAME medusa-emu CACHE INTERNAL "Name of output binaries")
if(NOT MSVC)
@ -262,7 +262,7 @@ if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII)
set(USE_SQLITE3 OFF)
endif()
if(DEFINED 3DS OR DEFINED WII)
if(DEFINED 3DS)
add_definitions(-DFIXED_ROM_BUFFER)
endif()
@ -625,7 +625,7 @@ endif()
if(USE_ELF)
list(APPEND FEATURES ELF)
include_directories(AFTER ${LIBELF_INCLUDE_DIRS})
find_file(ELF_REPL_H elf_repl.h PATHS ${LIBELF_INCLUDE_DIRS})
find_file(ELF_REPL_H elf_repl.h PATHS ${LIBELF_INCLUDE_DIRS} NO_CMAKE_FIND_ROOT_PATH)
if (ELF_REPL_H)
add_definitions(-DUSE_ELF_REPL)
endif()

View File

@ -103,6 +103,7 @@ void mCoreThreadWaitFromThread(struct mCoreThread* threadContext);
void mCoreThreadStopWaiting(struct mCoreThread* threadContext);
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool);
void mCoreThreadRewindParamsChanged(struct mCoreThread* threadContext);
struct mCoreThread* mCoreThreadGet(void);
struct mLogger* mCoreThreadLogger(void);

View File

@ -19,6 +19,9 @@ THREAD_ENTRY _rewindThread(void* context);
#endif
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) {
if (context->currentState) {
return;
}
mCoreRewindPatchesInit(&context->patchMemory, entries);
size_t e;
for (e = 0; e < entries; ++e) {
@ -42,6 +45,9 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries,
}
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
if (!context->currentState) {
return;
}
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexLock(&context->mutex);
@ -55,6 +61,8 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
#endif
context->previousState->close(context->previousState);
context->currentState->close(context->currentState);
context->previousState = NULL;
context->currentState = NULL;
size_t s;
for (s = 0; s < mCoreRewindPatchesSize(&context->patchMemory); ++s) {
deinitPatchFast(mCoreRewindPatchesGetPointer(&context->patchMemory, s));

View File

@ -167,10 +167,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
mLogFilterLoad(threadContext->logger.d.filter, &core->config);
}
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
}
mCoreThreadRewindParamsChanged(threadContext);
_changeState(threadContext->impl, THREAD_RUNNING, true);
@ -252,7 +249,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
}
bool mCoreThreadStart(struct mCoreThread* threadContext) {
threadContext->impl = malloc(sizeof(*threadContext->impl));
threadContext->impl = calloc(sizeof(*threadContext->impl), 1);
threadContext->impl->state = THREAD_INITIALIZED;
threadContext->logger.p = threadContext;
if (!threadContext->logger.d.log) {
@ -547,6 +544,16 @@ void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding)
MutexUnlock(&threadContext->impl->stateMutex);
}
void mCoreThreadRewindParamsChanged(struct mCoreThread* threadContext) {
struct mCore* core = threadContext->core;
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
} else {
mCoreRewindContextDeinit(&threadContext->impl->rewind);
}
}
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
MutexLock(&threadContext->impl->stateMutex);
if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING) {

View File

@ -208,7 +208,7 @@ void GBAReset(struct ARMCore* cpu) {
gba->debug = false;
memset(gba->debugString, 0, sizeof(gba->debugString));
if (!gba->romVf) {
if (!gba->romVf && gba->memory.rom) {
GBASkipBIOS(gba);
}
}
@ -312,6 +312,10 @@ bool GBALoadNull(struct GBA* gba) {
gba->memory.romMask = SIZE_CART0 - 1;
gba->memory.mirroring = false;
gba->romCrc32 = 0;
if (gba->cpu) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
}
return true;
}
@ -335,6 +339,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = 0;
gba->memory.romMask = 0;
gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize);
if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
}
return true;
}
@ -379,6 +386,9 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = SIZE_CART0;
gba->isPristine = false;
}
if (gba->cpu && gba->memory.activeRegion >= REGION_CART0) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
}
// TODO: error check
return true;
}

View File

@ -30,7 +30,6 @@ set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lib.c PROPERTIES GENERAT
file(GLOB PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
add_library(${BINARY_NAME}-pylib STATIC ${CMAKE_CURRENT_BINARY_DIR}/lib.c ${PYTHON_SRC})
add_dependencies(${BINARY_NAME}-pylib ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py)
set_target_properties(${BINARY_NAME}-pylib PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_BINARY_DIR};${INCLUDE_DIRECTORIES}")
set_target_properties(${BINARY_NAME}-pylib PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
set(PYTHON_LIBRARY ${BINARY_NAME}-pylib PARENT_SCOPE)

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AssetTile.h"
#include "CoreController.h"
#include "GBAApp.h"
#include <QFontDatabase>
@ -39,7 +40,7 @@ AssetTile::AssetTile(QWidget* parent)
m_ui.b->setFont(font);
}
void AssetTile::setController(GameController* controller) {
void AssetTile::setController(std::shared_ptr<CoreController> controller) {
m_tileCache = controller->tileCache();
switch (controller->platform()) {
#ifdef M_CORE_GBA
@ -83,7 +84,7 @@ void AssetTile::selectIndex(int index) {
m_index = index;
const uint16_t* data;
mTileCacheSetPalette(m_tileCache.get(), m_paletteSet);
mTileCacheSetPalette(m_tileCache, m_paletteSet);
unsigned bpp = 8 << m_tileCache->bpp;
int dispIndex = index;
int paletteId = m_paletteId;
@ -98,7 +99,7 @@ void AssetTile::selectIndex(int index) {
#endif
dispIndex -= m_boundary;
}
data = mTileCacheGetTile(m_tileCache.get(), index, paletteId);
data = mTileCacheGetTile(m_tileCache, index, paletteId);
m_ui.tileId->setText(QString::number(dispIndex * (1 + m_paletteSet)));
m_ui.address->setText(tr("%0%1%2")
.arg(m_addressWidth == 4 ? index >= m_boundary : 0)
@ -112,7 +113,7 @@ void AssetTile::selectIndex(int index) {
void AssetTile::selectColor(int index) {
const uint16_t* data;
mTileCacheSetPalette(m_tileCache.get(), m_paletteSet);
mTileCacheSetPalette(m_tileCache, m_paletteSet);
unsigned bpp = 8 << m_tileCache->bpp;
int paletteId = m_paletteId;
// XXX: Do this better
@ -121,7 +122,7 @@ void AssetTile::selectColor(int index) {
paletteId += m_tileCache->count / 2;
}
#endif
data = mTileCacheGetTile(m_tileCache.get(), m_index, m_paletteId);
data = mTileCacheGetTile(m_tileCache, m_index, m_paletteId);
uint16_t color = data[index];
m_ui.color->setColor(0, color);
m_ui.color->update();

View File

@ -6,20 +6,22 @@
#ifndef QGBA_ASSET_TILE
#define QGBA_ASSET_TILE
#include "GameController.h"
#include "ui_AssetTile.h"
#include <memory>
#include <mgba/core/tile-cache.h>
namespace QGBA {
class CoreController;
class AssetTile : public QGroupBox {
Q_OBJECT
public:
AssetTile(QWidget* parent = nullptr);
void setController(GameController*);
void setController(std::shared_ptr<CoreController>);
public slots:
void setPalette(int);
@ -30,7 +32,7 @@ public slots:
private:
Ui::AssetTile m_ui;
std::shared_ptr<mTileCache> m_tileCache;
mTileCache* m_tileCache;
int m_paletteId = 0;
int m_paletteSet = 0;
int m_index = 0;

View File

@ -5,32 +5,32 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AssetView.h"
#include <QTimer>
#include "CoreController.h"
#include <mgba/core/tile-cache.h>
#include <QTimer>
using namespace QGBA;
AssetView::AssetView(GameController* controller, QWidget* parent)
AssetView::AssetView(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_tileCache(controller->tileCache())
, m_controller(controller)
{
m_updateTimer.setSingleShot(true);
m_updateTimer.setInterval(1);
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTiles()));
connect(&m_updateTimer, &QTimer::timeout, this, static_cast<void(AssetView::*)()>(&AssetView::updateTiles));
connect(m_controller, &GameController::frameAvailable, &m_updateTimer,
connect(controller.get(), &CoreController::frameAvailable, &m_updateTimer,
static_cast<void(QTimer::*)()>(&QTimer::start));
connect(m_controller, &GameController::gameStopped, this, &AssetView::close);
connect(m_controller, &GameController::gameStopped, &m_updateTimer, &QTimer::stop);
connect(controller.get(), &CoreController::stopping, this, &AssetView::close);
connect(controller.get(), &CoreController::stopping, &m_updateTimer, &QTimer::stop);
}
void AssetView::updateTiles() {
updateTiles(false);
}
void AssetView::updateTiles(bool force) {
if (!m_controller->isLoaded()) {
return;
}
switch (m_controller->platform()) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
@ -56,7 +56,7 @@ void AssetView::showEvent(QShowEvent*) {
}
void AssetView::compositeTile(unsigned tileId, void* buffer, size_t stride, size_t x, size_t y, int depth) {
const uint8_t* tile = mTileCacheGetRawTile(m_tileCache.get(), tileId);
const uint8_t* tile = mTileCacheGetRawTile(m_tileCache, tileId);
uint8_t* pixels = static_cast<uint8_t*>(buffer);
size_t base = stride * y + x;
switch (depth) {

View File

@ -6,22 +6,28 @@
#ifndef QGBA_ASSET_VIEW
#define QGBA_ASSET_VIEW
#include <QTimer>
#include <QWidget>
#include "GameController.h"
#include <mgba/core/tile-cache.h>
#include <memory>
namespace QGBA {
class CoreController;
class AssetView : public QWidget {
Q_OBJECT
public:
AssetView(GameController* controller, QWidget* parent = nullptr);
AssetView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
void compositeTile(unsigned tileId, void* image, size_t stride, size_t x, size_t y, int depth = 8);
protected slots:
void updateTiles(bool force = false);
void updateTiles();
void updateTiles(bool force);
protected:
#ifdef M_CORE_GBA
@ -34,10 +40,10 @@ protected:
void resizeEvent(QResizeEvent*) override;
void showEvent(QShowEvent*) override;
const std::shared_ptr<mTileCache> m_tileCache;
mTileCache* const m_tileCache;
private:
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
QTimer m_updateTimer;
};

View File

@ -47,10 +47,18 @@ AudioProcessor::AudioProcessor(QObject* parent)
{
}
void AudioProcessor::setInput(mCoreThread* input) {
AudioProcessor::~AudioProcessor() {
stop();
}
void AudioProcessor::setInput(std::shared_ptr<CoreController> input) {
m_context = input;
}
void AudioProcessor::stop() {
m_context.reset();
}
void AudioProcessor::setBufferSamples(int samples) {
m_samples = samples;
}

View File

@ -7,6 +7,10 @@
#define QGBA_AUDIO_PROCESSOR
#include <QObject>
#include <memory>
#include "CoreController.h"
struct mCoreThread;
namespace QGBA {
@ -28,12 +32,14 @@ public:
static void setDriver(Driver driver) { s_driver = driver; }
AudioProcessor(QObject* parent = nullptr);
~AudioProcessor();
int getBufferSamples() const { return m_samples; }
virtual unsigned sampleRate() const = 0;
public slots:
virtual void setInput(mCoreThread* input);
virtual void setInput(std::shared_ptr<CoreController>);
virtual void stop();
virtual bool start() = 0;
virtual void pause() = 0;
@ -44,10 +50,10 @@ public slots:
virtual void requestSampleRate(unsigned) = 0;
protected:
mCoreThread* input() { return m_context; }
mCoreThread* input() { return m_context->thread(); }
private:
mCoreThread* m_context = nullptr;
std::shared_ptr<CoreController> m_context;
int m_samples = 2048;
static Driver s_driver;
};

View File

@ -20,16 +20,24 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
{
}
void AudioProcessorQt::setInput(mCoreThread* input) {
AudioProcessor::setInput(input);
void AudioProcessorQt::setInput(std::shared_ptr<CoreController> controller) {
AudioProcessor::setInput(controller);
if (m_device) {
m_device->setInput(input);
m_device->setInput(input());
if (m_audioOutput) {
m_device->setFormat(m_audioOutput->format());
}
}
}
void AudioProcessorQt::stop() {
if (m_device) {
m_device.reset();
}
pause();
AudioProcessor::stop();
}
bool AudioProcessorQt::start() {
if (!input()) {
LOG(QT, WARN) << tr("Can't start an audio processor without input");
@ -37,7 +45,7 @@ bool AudioProcessorQt::start() {
}
if (!m_device) {
m_device = new AudioDevice(this);
m_device = std::make_unique<AudioDevice>(this);
}
if (!m_audioOutput) {
@ -56,7 +64,7 @@ bool AudioProcessorQt::start() {
m_device->setInput(input());
m_device->setFormat(m_audioOutput->format());
m_audioOutput->start(m_device);
m_audioOutput->start(m_device.get());
return m_audioOutput->state() == QAudio::ActiveState;
}

View File

@ -22,7 +22,8 @@ public:
virtual unsigned sampleRate() const override;
public slots:
virtual void setInput(mCoreThread* input) override;
virtual void setInput(std::shared_ptr<CoreController> input) override;
virtual void stop() override;
virtual bool start() override;
virtual void pause() override;
@ -33,7 +34,7 @@ public slots:
private:
QAudioOutput* m_audioOutput = nullptr;
AudioDevice* m_device = nullptr;
std::unique_ptr<AudioDevice> m_device;
unsigned m_sampleRate = 44100;
};

View File

@ -16,16 +16,17 @@ AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
{
}
AudioProcessorSDL::~AudioProcessorSDL() {
void AudioProcessorSDL::setInput(std::shared_ptr<CoreController> controller) {
AudioProcessor::setInput(controller);
if (m_audio.core && input()->core != m_audio.core) {
mSDLDeinitAudio(&m_audio);
mSDLInitAudio(&m_audio, input());
}
}
void AudioProcessorSDL::setInput(mCoreThread* input) {
AudioProcessor::setInput(input);
if (m_audio.core && input->core != m_audio.core) {
void AudioProcessorSDL::stop() {
mSDLDeinitAudio(&m_audio);
mSDLInitAudio(&m_audio, input);
}
AudioProcessor::stop();
}
bool AudioProcessorSDL::start() {
@ -51,23 +52,27 @@ void AudioProcessorSDL::pause() {
void AudioProcessorSDL::setBufferSamples(int samples) {
AudioProcessor::setBufferSamples(samples);
if (m_audio.samples != samples) {
m_audio.samples = samples;
if (m_audio.core) {
mSDLDeinitAudio(&m_audio);
mSDLInitAudio(&m_audio, input());
}
}
}
void AudioProcessorSDL::inputParametersChanged() {
}
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
if (m_audio.sampleRate != rate) {
m_audio.sampleRate = rate;
if (m_audio.core) {
mSDLDeinitAudio(&m_audio);
mSDLInitAudio(&m_audio, input());
}
}
}
unsigned AudioProcessorSDL::sampleRate() const {
if (m_audio.core) {

View File

@ -18,12 +18,12 @@ Q_OBJECT
public:
AudioProcessorSDL(QObject* parent = nullptr);
~AudioProcessorSDL();
virtual unsigned sampleRate() const override;
public slots:
virtual void setInput(mCoreThread* input) override;
virtual void setInput(std::shared_ptr<CoreController> input) override;
virtual void stop() override;
virtual bool start() override;
virtual void pause() override;

View File

@ -1,6 +1,6 @@
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
@ -67,12 +67,13 @@ set(SOURCE_FILES
CheatsModel.cpp
CheatsView.cpp
ConfigController.cpp
CoreManager.cpp
CoreController.cpp
Display.cpp
DisplayGL.cpp
DisplayQt.cpp
GBAApp.cpp
GIFView.cpp
GameController.cpp
GamepadAxisEvent.cpp
GamepadButtonEvent.cpp
GamepadHatEvent.cpp
@ -282,7 +283,7 @@ if(APPLE)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
get_target_property(QTCOCOA Qt5::QCocoaIntegrationPlugin LOCATION)
get_target_property(COREAUDIO Qt5::CoreAudioPlugin LOCATION)
get_target_property(BUNDLE_PATH ${BINARY_NAME}-qt LOCATION)
set(BUNDLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app)
target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}")
set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)

View File

@ -6,7 +6,7 @@
#include "CheatsView.h"
#include "GBAApp.h"
#include "GameController.h"
#include "CoreController.h"
#include <QClipboard>
#include <QPushButton>
@ -21,7 +21,7 @@
using namespace QGBA;
CheatsView::CheatsView(GameController* controller, QWidget* parent)
CheatsView::CheatsView(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
, m_model(controller->cheatDevice())
@ -35,8 +35,8 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
connect(m_ui.save, &QPushButton::clicked, this, &CheatsView::save);
connect(m_ui.addSet, &QPushButton::clicked, this, &CheatsView::addSet);
connect(m_ui.remove, &QPushButton::clicked, this, &CheatsView::removeSet);
connect(controller, &GameController::gameStopped, this, &CheatsView::close);
connect(controller, &GameController::stateLoaded, &m_model, &CheatsModel::invalidated);
connect(controller.get(), &CoreController::stopping, this, &CheatsView::close);
connect(controller.get(), &CoreController::stateLoaded, &m_model, &CheatsModel::invalidated);
QPushButton* add;
switch (controller->platform()) {
@ -123,7 +123,7 @@ void CheatsView::save() {
}
void CheatsView::addSet() {
GameController::Interrupter interrupter(m_controller);
CoreController::Interrupter interrupter(m_controller);
mCheatSet* set = m_controller->cheatDevice()->createSet(m_controller->cheatDevice(), nullptr);
m_model.addSet(set);
}
@ -134,7 +134,7 @@ void CheatsView::removeSet() {
if (selection.count() < 1) {
return;
}
GameController::Interrupter interrupter(m_controller);
CoreController::Interrupter interrupter(m_controller);
for (const QModelIndex& index : selection) {
m_model.removeAt(selection[0]);
}
@ -154,7 +154,7 @@ void CheatsView::enterCheat(int codeType) {
if (!set) {
return;
}
m_controller->threadInterrupt();
CoreController::Interrupter interrupter(m_controller);
if (selection.count() == 0) {
m_model.addSet(set);
index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex());
@ -167,6 +167,5 @@ void CheatsView::enterCheat(int codeType) {
m_model.endAppendRow();
}
set->refresh(set, m_controller->cheatDevice());
m_controller->threadContinue();
m_ui.codeEntry->clear();
}

View File

@ -9,6 +9,7 @@
#include <QWidget>
#include <functional>
#include <memory>
#include "CheatsModel.h"
@ -18,13 +19,13 @@ struct mCheatDevice;
namespace QGBA {
class GameController;
class CoreController;
class CheatsView : public QWidget {
Q_OBJECT
public:
CheatsView(GameController* controller, QWidget* parent = nullptr);
CheatsView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
virtual bool eventFilter(QObject*, QEvent*) override;
@ -38,7 +39,7 @@ private:
void enterCheat(int codeType);
Ui::CheatsView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
CheatsModel m_model;
};

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ConfigController.h"
#include "GameController.h"
#include "CoreController.h"
#include <QAction>
#include <QDir>
@ -98,8 +98,8 @@ ConfigController::ConfigController(QObject* parent)
mCoreConfigInit(&m_config, PORT);
m_opts.audioSync = GameController::AUDIO_SYNC;
m_opts.videoSync = GameController::VIDEO_SYNC;
m_opts.audioSync = CoreController::AUDIO_SYNC;
m_opts.videoSync = CoreController::VIDEO_SYNC;
m_opts.fpsTarget = 60;
m_opts.audioBuffers = 1536;
m_opts.sampleRate = 44100;

View File

@ -0,0 +1,698 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CoreController.h"
#include "ConfigController.h"
#include "InputController.h"
#include "LogController.h"
#include "MultiplayerController.h"
#include "Override.h"
#include <QDateTime>
#include <QMutexLocker>
#include <mgba/core/serialize.h>
#include <mgba/feature/video-logger.h>
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/renderers/tile-cache.h>
#include <mgba/internal/gba/sharkport.h>
#endif
#ifdef M_CORE_GB
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/renderers/tile-cache.h>
#endif
#include <mgba-util/vfs.h>
using namespace QGBA;
CoreController::CoreController(mCore* core, QObject* parent)
: QObject(parent)
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
, m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
{
m_threadContext.core = core;
m_threadContext.userData = this;
QSize size = screenDimensions();
m_buffers[0].resize(size.width() * size.height() * sizeof(color_t));
m_buffers[1].resize(size.width() * size.height() * sizeof(color_t));
m_activeBuffer = &m_buffers[0];
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), size.width());
m_threadContext.startCallback = [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource());
context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble());
switch (context->core->platform(context->core)) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, controller->m_inputController->luminance());
break;
#endif
default:
break;
}
if (controller->m_override) {
controller->m_override->identify(context->core);
controller->m_override->apply(context->core);
}
if (mCoreLoadState(context->core, 0, controller->m_loadStateFlags)) {
mCoreDeleteState(context->core, 0);
}
if (controller->m_multiplayer) {
controller->m_multiplayer->attachGame(controller);
}
QMetaObject::invokeMethod(controller, "started");
};
m_threadContext.resetCallback = [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
for (auto action : controller->m_resetActions) {
action();
}
controller->m_resetActions.clear();
controller->m_activeBuffer->fill(0xFF);
controller->finishFrame();
};
m_threadContext.frameCallback = [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
controller->finishFrame();
};
m_threadContext.cleanCallback = [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
controller->clearMultiplayerController();
QMetaObject::invokeMethod(controller, "stopping");
};
m_threadContext.logger.d.log = [](mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
mThreadLogger* logContext = reinterpret_cast<mThreadLogger*>(logger);
mCoreThread* context = logContext->p;
static const char* savestateMessage = "State %i loaded";
static const char* savestateFailedMessage = "State %i failed to load";
static int biosCat = -1;
static int statusCat = -1;
if (!context) {
return;
}
CoreController* controller = static_cast<CoreController*>(context->userData);
QString message;
if (biosCat < 0) {
biosCat = mLogCategoryById("gba.bios");
}
if (statusCat < 0) {
statusCat = mLogCategoryById("core.status");
}
#ifdef M_CORE_GBA
if (level == mLOG_STUB && category == biosCat) {
va_list argc;
va_copy(argc, args);
int immediate = va_arg(argc, int);
va_end(argc);
QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
} else
#endif
if (category == statusCat) {
// Slot 0 is reserved for suspend points
if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
va_list argc;
va_copy(argc, args);
int slot = va_arg(argc, int);
va_end(argc);
if (slot == 0) {
format = "Loaded suspend state";
}
} else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) {
va_list argc;
va_copy(argc, args);
int slot = va_arg(argc, int);
va_end(argc);
if (slot == 0) {
return;
}
}
message = QString().vsprintf(format, args);
QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
}
if (level == mLOG_FATAL) {
mCoreThreadMarkCrashed(controller->thread());
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, QString().vsprintf(format, args)));
}
message = QString().vsprintf(format, args);
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
};
}
CoreController::~CoreController() {
endVideoLog();
stop();
disconnect();
if (m_tileCache) {
mTileCacheDeinit(m_tileCache.get());
m_tileCache.reset();
}
mCoreThreadJoin(&m_threadContext);
mCoreConfigDeinit(&m_threadContext.core->config);
m_threadContext.core->deinit(m_threadContext.core);
}
color_t* CoreController::drawContext() {
QMutexLocker locker(&m_mutex);
if (!m_completeBuffer) {
return nullptr;
}
return reinterpret_cast<color_t*>(m_completeBuffer->data());
}
bool CoreController::isPaused() {
return mCoreThreadIsPaused(&m_threadContext);
}
mPlatform CoreController::platform() const {
return m_threadContext.core->platform(m_threadContext.core);
}
QSize CoreController::screenDimensions() const {
unsigned width, height;
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
return QSize(width, height);
}
QPair<unsigned, unsigned> CoreController::frameRate() const {
return qMakePair(m_threadContext.core->frameCycles(m_threadContext.core), m_threadContext.core->frequency(m_threadContext.core));
}
void CoreController::loadConfig(ConfigController* config) {
Interrupter interrupter(this);
m_loadStateFlags = config->getOption("loadStateExtdata").toInt();
m_saveStateFlags = config->getOption("saveStateExtdata").toInt();
m_fastForwardRatio = config->getOption("fastForwardRatio").toFloat();
m_videoSync = config->getOption("videoSync").toInt();
m_audioSync = config->getOption("audioSync").toInt();
m_fpsTarget = config->getOption("fpsTarget").toFloat();
updateFastForward();
mCoreLoadForeignConfig(m_threadContext.core, config->config());
mCoreThreadRewindParamsChanged(&m_threadContext);
}
#ifdef USE_DEBUGGERS
void CoreController::setDebugger(mDebugger* debugger) {
Interrupter interrupter(this);
if (debugger) {
mDebuggerAttach(debugger, m_threadContext.core);
} else {
m_threadContext.core->detachDebugger(m_threadContext.core);
}
}
#endif
void CoreController::setMultiplayerController(MultiplayerController* controller) {
if (controller == m_multiplayer) {
return;
}
clearMultiplayerController();
m_multiplayer = controller;
if (!mCoreThreadHasStarted(&m_threadContext)) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* thread) {
CoreController* controller = static_cast<CoreController*>(thread->userData);
controller->m_multiplayer->attachGame(controller);
});
}
void CoreController::clearMultiplayerController() {
if (!m_multiplayer) {
return;
}
m_multiplayer->detachGame(this);
m_multiplayer = nullptr;
}
mTileCache* CoreController::tileCache() {
if (m_tileCache) {
return m_tileCache.get();
}
Interrupter interrupter(this);
switch (platform()) {
#ifdef M_CORE_GBA
case PLATFORM_GBA: {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
m_tileCache = std::make_unique<mTileCache>();
GBAVideoTileCacheInit(m_tileCache.get());
GBAVideoTileCacheAssociate(m_tileCache.get(), &gba->video);
mTileCacheSetPalette(m_tileCache.get(), 0);
break;
}
#endif
#ifdef M_CORE_GB
case PLATFORM_GB: {
GB* gb = static_cast<GB*>(m_threadContext.core->board);
m_tileCache = std::make_unique<mTileCache>();
GBVideoTileCacheInit(m_tileCache.get());
GBVideoTileCacheAssociate(m_tileCache.get(), &gb->video);
mTileCacheSetPalette(m_tileCache.get(), 0);
break;
}
#endif
default:
return nullptr;
}
return m_tileCache.get();
}
void CoreController::setOverride(std::unique_ptr<Override> override) {
Interrupter interrupter(this);
m_override = std::move(override);
m_override->identify(m_threadContext.core);
}
void CoreController::setInputController(InputController* inputController) {
m_inputController = inputController;
m_inputController->setPlatform(platform());
}
void CoreController::setLogger(LogController* logger) {
disconnect(m_log);
m_log = logger;
m_threadContext.logger.d.filter = logger->filter();
connect(this, &CoreController::logPosted, m_log, &LogController::postLog);
}
void CoreController::start() {
if (!m_patched) {
mCoreAutoloadPatch(m_threadContext.core);
}
if (!mCoreThreadStart(&m_threadContext)) {
emit failed();
emit stopping();
}
}
void CoreController::stop() {
#ifdef USE_DEBUGGERS
setDebugger(nullptr);
#endif
setPaused(false);
mCoreThreadEnd(&m_threadContext);
emit stopping();
}
void CoreController::reset() {
bool wasPaused = isPaused();
setPaused(false);
Interrupter interrupter(this);
mCoreThreadReset(&m_threadContext);
if (wasPaused) {
setPaused(true);
}
}
void CoreController::setPaused(bool paused) {
if (paused == isPaused()) {
return;
}
if (paused) {
QMutexLocker locker(&m_mutex);
m_frameActions.append([this]() {
mCoreThreadPauseFromThread(&m_threadContext);
QMetaObject::invokeMethod(this, "paused");
});
} else {
mCoreThreadUnpause(&m_threadContext);
emit unpaused();
}
}
void CoreController::frameAdvance() {
QMutexLocker locker(&m_mutex);
m_frameActions.append([this]() {
mCoreThreadPauseFromThread(&m_threadContext);
});
setPaused(false);
}
void CoreController::setSync(bool sync) {
if (sync) {
m_threadContext.impl->sync.audioWait = m_audioSync;
m_threadContext.impl->sync.videoFrameWait = m_videoSync;
} else {
m_threadContext.impl->sync.audioWait = false;
m_threadContext.impl->sync.videoFrameWait = false;
}
}
void CoreController::setRewinding(bool rewind) {
if (!m_threadContext.core->opts.rewindEnable) {
return;
}
if (rewind && m_multiplayer && m_multiplayer->attached() > 1) {
return;
}
if (rewind && isPaused()) {
setPaused(false);
// TODO: restore autopausing
}
mCoreThreadSetRewinding(&m_threadContext, rewind);
}
void CoreController::rewind(int states) {
{
Interrupter interrupter(this);
if (!states) {
states = INT_MAX;
}
for (int i = 0; i < states; ++i) {
if (!mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core)) {
break;
}
}
}
emit frameAvailable();
emit rewound();
}
void CoreController::setFastForward(bool enable) {
m_fastForward = enable;
updateFastForward();
}
void CoreController::forceFastForward(bool enable) {
m_fastForwardForced = enable;
updateFastForward();
}
void CoreController::loadState(int slot) {
if (slot > 0 && slot != m_stateSlot) {
m_stateSlot = slot;
m_backupSaveState.clear();
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
if (!controller->m_backupLoadState.isOpen()) {
controller->m_backupLoadState = VFileMemChunk(nullptr, 0);
}
mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_saveStateFlags);
if (mCoreLoadState(context->core, controller->m_stateSlot, controller->m_loadStateFlags)) {
emit controller->frameAvailable();
emit controller->stateLoaded();
}
});
}
void CoreController::saveState(int slot) {
if (slot > 0) {
m_stateSlot = slot;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, false);
if (vf) {
controller->m_backupSaveState.resize(vf->size(vf));
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
vf->close(vf);
}
mCoreSaveState(context->core, controller->m_stateSlot, controller->m_saveStateFlags);
});
}
void CoreController::loadBackupState() {
if (!m_backupLoadState.isOpen()) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
controller->m_backupLoadState.seek(0);
if (mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_loadStateFlags)) {
mLOG(STATUS, INFO, "Undid state load");
controller->frameAvailable();
controller->stateLoaded();
}
controller->m_backupLoadState.close();
});
}
void CoreController::saveBackupState() {
if (m_backupSaveState.isEmpty()) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, true);
if (vf) {
vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
vf->close(vf);
mLOG(STATUS, INFO, "Undid state save");
}
controller->m_backupSaveState.clear();
});
}
void CoreController::loadSave(const QString& path, bool temporary) {
m_resetActions.append([this, path, temporary]() {
VFile* vf = VFileDevice::open(path, temporary ? O_RDONLY : O_RDWR);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open save file: %1").arg(path);
return;
}
if (temporary) {
m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
} else {
m_threadContext.core->loadSave(m_threadContext.core, vf);
}
});
reset();
}
void CoreController::loadPatch(const QString& patchPath) {
Interrupter interrupter(this);
VFile* patch = VFileDevice::open(patchPath, O_RDONLY);
if (patch) {
m_threadContext.core->loadPatch(m_threadContext.core, patch);
m_patched = true;
}
patch->close(patch);
if (mCoreThreadHasStarted(&m_threadContext)) {
reset();
}
}
void CoreController::replaceGame(const QString& path) {
QFileInfo info(path);
if (!info.isReadable()) {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
return;
}
QString fname = info.canonicalFilePath();
Interrupter interrupter(this);
mDirectorySetDetachBase(&m_threadContext.core->dirs);
mCoreLoadFile(m_threadContext.core, fname.toLocal8Bit().constData());
}
void CoreController::yankPak() {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
Interrupter interrupter(this);
GBAYankROM(static_cast<GBA*>(m_threadContext.core->board));
#endif
}
#ifdef USE_PNG
void CoreController::screenshot() {
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
mCoreTakeScreenshot(context->core);
});
}
#endif
void CoreController::setRealTime() {
m_threadContext.core->rtc.override = RTC_NO_OVERRIDE;
}
void CoreController::setFixedTime(const QDateTime& time) {
m_threadContext.core->rtc.override = RTC_FIXED;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
}
void CoreController::setFakeEpoch(const QDateTime& time) {
m_threadContext.core->rtc.override = RTC_FAKE_EPOCH;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
}
void CoreController::importSharkport(const QString& path) {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
VFile* vf = VFileDevice::open(path, O_RDONLY);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open snapshot file for reading: %1").arg(path);
return;
}
Interrupter interrupter(this);
GBASavedataImportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf, false);
vf->close(vf);
#endif
}
void CoreController::exportSharkport(const QString& path) {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open snapshot file for writing: %1").arg(path);
return;
}
Interrupter interrupter(this);
GBASavedataExportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf);
vf->close(vf);
#endif
}
void CoreController::setAVStream(mAVStream* stream) {
Interrupter interrupter(this);
m_threadContext.core->setAVStream(m_threadContext.core, stream);
}
void CoreController::clearAVStream() {
Interrupter interrupter(this);
m_threadContext.core->setAVStream(m_threadContext.core, nullptr);
}
void CoreController::clearOverride() {
m_override.reset();
}
void CoreController::startVideoLog(const QString& path) {
if (m_vl) {
return;
}
Interrupter interrupter(this);
m_vl = mVideoLogContextCreate(m_threadContext.core);
m_vlVf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
mVideoLogContextSetOutput(m_vl, m_vlVf);
mVideoLogContextWriteHeader(m_vl, m_threadContext.core);
}
void CoreController::endVideoLog() {
if (!m_vl) {
return;
}
Interrupter interrupter(this);
mVideoLogContextDestroy(m_threadContext.core, m_vl);
if (m_vlVf) {
m_vlVf->close(m_vlVf);
m_vlVf = nullptr;
}
m_vl = nullptr;
}
void CoreController::updateKeys() {
int activeKeys = m_inputController->updateAutofire() | m_inputController->pollEvents();
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
}
void CoreController::finishFrame() {
QMutexLocker locker(&m_mutex);
m_completeBuffer = m_activeBuffer;
// TODO: Generalize this to triple buffering?
m_activeBuffer = &m_buffers[0];
if (m_activeBuffer == m_completeBuffer) {
m_activeBuffer = &m_buffers[1];
}
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width());
for (auto& action : m_frameActions) {
action();
}
m_frameActions.clear();
updateKeys();
QMetaObject::invokeMethod(this, "frameAvailable");
}
void CoreController::updateFastForward() {
if (m_fastForward || m_fastForwardForced) {
if (m_fastForwardRatio > 0) {
m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_fastForwardRatio;
} else {
setSync(false);
}
} else {
m_threadContext.impl->sync.fpsTarget = m_fpsTarget;
setSync(true);
}
}
CoreController::Interrupter::Interrupter(CoreController* parent, bool fromThread)
: m_parent(parent)
{
if (!m_parent->thread()->impl) {
return;
}
if (!fromThread) {
mCoreThreadInterrupt(m_parent->thread());
} else {
mCoreThreadInterruptFromThread(m_parent->thread());
}
}
CoreController::Interrupter::Interrupter(std::shared_ptr<CoreController> parent, bool fromThread)
: m_parent(parent.get())
{
if (!m_parent->thread()->impl) {
return;
}
if (!fromThread) {
mCoreThreadInterrupt(m_parent->thread());
} else {
mCoreThreadInterruptFromThread(m_parent->thread());
}
}
CoreController::Interrupter::Interrupter(const Interrupter& other)
: m_parent(other.m_parent)
{
if (!m_parent->thread()->impl) {
return;
}
mCoreThreadInterrupt(m_parent->thread());
}
CoreController::Interrupter::~Interrupter() {
if (!m_parent->thread()->impl) {
return;
}
mCoreThreadContinue(m_parent->thread());
}

View File

@ -0,0 +1,194 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_CORE_CONTROLLER
#define QGBA_CORE_CONTROLLER
#include <QByteArray>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QSize>
#include "VFileDevice.h"
#include <functional>
#include <memory>
#include <mgba/core/core.h>
#include <mgba/core/interface.h>
#include <mgba/core/thread.h>
#include <mgba/core/tile-cache.h>
struct mCore;
namespace QGBA {
class ConfigController;
class InputController;
class LogController;
class MultiplayerController;
class Override;
class CoreController : public QObject {
Q_OBJECT
public:
static const bool VIDEO_SYNC = false;
static const bool AUDIO_SYNC = true;
class Interrupter {
public:
Interrupter(CoreController*, bool fromThread = false);
Interrupter(std::shared_ptr<CoreController>, bool fromThread = false);
Interrupter(const Interrupter&);
~Interrupter();
private:
CoreController* m_parent;
};
CoreController(mCore* core, QObject* parent = nullptr);
~CoreController();
mCoreThread* thread() { return &m_threadContext; }
color_t* drawContext();
bool isPaused();
mPlatform platform() const;
QSize screenDimensions() const;
QPair<unsigned, unsigned> frameRate() const;
void loadConfig(ConfigController*);
mCheatDevice* cheatDevice() { return m_threadContext.core->cheatDevice(m_threadContext.core); }
#ifdef USE_DEBUGGERS
mDebugger* debugger() { return m_threadContext.core->debugger; }
void setDebugger(mDebugger*);
#endif
void setMultiplayerController(MultiplayerController*);
void clearMultiplayerController();
MultiplayerController* multiplayerController() { return m_multiplayer; }
mTileCache* tileCache();
int stateSlot() const { return m_stateSlot; }
void setOverride(std::unique_ptr<Override> override);
Override* override() { return m_override.get(); }
void setInputController(InputController*);
void setLogger(LogController*);
public slots:
void start();
void stop();
void reset();
void setPaused(bool paused);
void frameAdvance();
void setSync(bool enable);
void setRewinding(bool);
void rewind(int count = 0);
void setFastForward(bool);
void forceFastForward(bool);
void loadState(int slot = 0);
void saveState(int slot = 0);
void loadBackupState();
void saveBackupState();
void loadSave(const QString&, bool temporary);
void loadPatch(const QString&);
void replaceGame(const QString&);
void yankPak();
#ifdef USE_PNG
void screenshot();
#endif
void setRealTime();
void setFixedTime(const QDateTime& time);
void setFakeEpoch(const QDateTime& time);
void importSharkport(const QString& path);
void exportSharkport(const QString& path);
void setAVStream(mAVStream*);
void clearAVStream();
void clearOverride();
void startVideoLog(const QString& path);
void endVideoLog();
signals:
void started();
void paused();
void unpaused();
void stopping();
void crashed(const QString& errorMessage);
void failed();
void frameAvailable();
void stateLoaded();
void rewound();
void rewindChanged(bool);
void fastForwardChanged(bool);
void unimplementedBiosCall(int);
void statusPosted(const QString& message);
void logPosted(int level, int category, const QString& log);
private:
void updateKeys();
void finishFrame();
void updateFastForward();
mCoreThread m_threadContext{};
bool m_patched = false;
QByteArray m_buffers[2];
QByteArray* m_activeBuffer;
QByteArray* m_completeBuffer = nullptr;
std::unique_ptr<mTileCache> m_tileCache;
std::unique_ptr<Override> m_override;
QList<std::function<void()>> m_resetActions;
QList<std::function<void()>> m_frameActions;
QMutex m_mutex;
VFileDevice m_backupLoadState;
QByteArray m_backupSaveState{nullptr};
int m_stateSlot = 1;
int m_loadStateFlags;
int m_saveStateFlags;
bool m_audioSync = AUDIO_SYNC;
bool m_videoSync = VIDEO_SYNC;
int m_fastForward = false;
int m_fastForwardForced = false;
float m_fastForwardRatio = -1.f;
float m_fpsTarget;
InputController* m_inputController = nullptr;
LogController* m_log = nullptr;
MultiplayerController* m_multiplayer = nullptr;
mVideoLogContext* m_vl = nullptr;
VFile* m_vlVf = nullptr;
};
}
#endif

View File

@ -0,0 +1,165 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CoreManager.h"
#include "CoreController.h"
#include "LogController.h"
#include <QDir>
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
#endif
#include <mgba/core/core.h>
#include <mgba-util/vfs.h>
using namespace QGBA;
void CoreManager::setConfig(const mCoreConfig* config) {
m_config = config;
}
void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) {
m_multiplayer = multiplayer;
}
CoreController* CoreManager::loadGame(const QString& path) {
QFileInfo info(path);
if (!info.isReadable()) {
QString fname = info.fileName();
QString base = info.path();
if (base.endsWith("/") || base.endsWith(QDir::separator())) {
base.chop(1);
}
VDir* dir = VDirOpenArchive(base.toUtf8().constData());
if (dir) {
VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY);
if (vf) {
struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfclone->write(vfclone, buffer, read);
}
vf->close(vf);
vf = vfclone;
}
dir->close(dir);
loadGame(vf, fname, base);
} else {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
}
return nullptr;
}
VFile* vf = nullptr;
VDir* archive = VDirOpenArchive(path.toUtf8().constData());
if (archive) {
VFile* vfOriginal = VDirFindFirst(archive, [](VFile* vf) {
return mCoreIsCompatible(vf) != PLATFORM_NONE;
});
ssize_t size;
if (vfOriginal && (size = vfOriginal->size(vfOriginal)) > 0) {
void* mem = vfOriginal->map(vfOriginal, size, MAP_READ);
vf = VFileMemChunk(mem, size);
vfOriginal->unmap(vfOriginal, mem, (size_t) read);
vfOriginal->close(vfOriginal);
}
}
QDir dir(info.dir());
if (!vf) {
vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY);
}
return loadGame(vf, info.fileName(), dir.canonicalPath());
}
CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QString& base) {
if (!vf) {
return nullptr;
}
mCore* core = mCoreFindVF(vf);
if (!core) {
return nullptr;
}
core->init(core);
mCoreInitConfig(core, nullptr);
if (m_config) {
mCoreLoadForeignConfig(core, m_config);
}
if (m_preload) {
mCorePreloadVF(core, vf);
} else {
core->loadROM(core, vf);
}
QFileInfo info(base + "/" + path);
QByteArray bytes(info.baseName().toUtf8());
strncpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName));
bytes = info.dir().canonicalPath().toUtf8();
mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));
mCoreAutoloadSave(core);
CoreController* cc = new CoreController(core);
if (m_multiplayer) {
cc->setMultiplayerController(m_multiplayer);
}
emit coreLoaded(cc);
return cc;
}
CoreController* CoreManager::loadBIOS(int platform, const QString& path) {
QFileInfo info(path);
VFile* vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY);
if (!vf) {
return nullptr;
}
mCore* core = nullptr;
switch (platform) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
core = GBACoreCreate();
break;
#endif
default:
vf->close(vf);
return nullptr;
}
if (!core) {
vf->close(vf);
return nullptr;
}
core->init(core);
mCoreInitConfig(core, nullptr);
if (m_config) {
mCoreLoadForeignConfig(core, m_config);
}
core->loadBIOS(core, vf, 0);
mCoreConfigSetOverrideIntValue(&core->config, "useBios", 1);
mCoreConfigSetOverrideIntValue(&core->config, "skipBios", 0);
QByteArray bytes(info.baseName().toUtf8());
strncpy(core->dirs.baseName, bytes.constData(), sizeof(core->dirs.baseName));
bytes = info.dir().canonicalPath().toUtf8();
mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData()));
CoreController* cc = new CoreController(core);
if (m_multiplayer) {
cc->setMultiplayerController(m_multiplayer);
}
emit coreLoaded(cc);
return cc;
}

View File

@ -0,0 +1,45 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_CORE_MANAGER
#define QGBA_CORE_MANAGER
#include <QFileInfo>
#include <QObject>
#include <QString>
struct mCoreConfig;
struct VFile;
namespace QGBA {
class CoreController;
class MultiplayerController;
class CoreManager : public QObject {
Q_OBJECT
public:
void setConfig(const mCoreConfig*);
void setMultiplayerController(MultiplayerController*);
void setPreload(bool preload) { m_preload = preload; }
public slots:
CoreController* loadGame(const QString& path);
CoreController* loadGame(VFile* vf, const QString& path, const QString& base);
CoreController* loadBIOS(int platform, const QString& path);
signals:
void coreLoaded(CoreController*);
private:
const mCoreConfig* m_config = nullptr;
MultiplayerController* m_multiplayer = nullptr;
bool m_preload = false;
};
}
#endif

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DebuggerConsoleController.h"
#include "GameController.h"
#include "CoreController.h"
#include <QMutexLocker>
@ -13,8 +13,8 @@
using namespace QGBA;
DebuggerConsoleController::DebuggerConsoleController(GameController* controller, QObject* parent)
: DebuggerController(controller, &m_cliDebugger.d, parent)
DebuggerConsoleController::DebuggerConsoleController(QObject* parent)
: DebuggerController(&m_cliDebugger.d, parent)
{
m_backend.d.printf = printf;
m_backend.d.init = init;
@ -39,8 +39,10 @@ void DebuggerConsoleController::enterLine(const QString& line) {
}
void DebuggerConsoleController::detach() {
if (m_cliDebugger.d.state != DEBUGGER_SHUTDOWN) {
m_lines.append(QString());
m_cond.wakeOne();
}
DebuggerController::detach();
}
@ -68,14 +70,16 @@ void DebuggerConsoleController::init(struct CLIDebuggerBackend* be) {
void DebuggerConsoleController::deinit(struct CLIDebuggerBackend* be) {
Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self;
if (be->p->d.state != DEBUGGER_SHUTDOWN) {
self->m_lines.append(QString());
self->m_cond.wakeOne();
}
}
const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) {
Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self;
GameController::Interrupter interrupter(self->m_gameController, true);
CoreController::Interrupter interrupter(self->m_gameController, true);
QMutexLocker lock(&self->m_mutex);
while (self->m_lines.isEmpty()) {
self->m_cond.wait(&self->m_mutex);
@ -99,7 +103,7 @@ void DebuggerConsoleController::lineAppend(struct CLIDebuggerBackend* be, const
const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be, size_t* len) {
Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self;
GameController::Interrupter interrupter(self->m_gameController, true);
CoreController::Interrupter interrupter(self->m_gameController, true);
QMutexLocker lock(&self->m_mutex);
if (self->m_history.isEmpty()) {
return "i";
@ -111,7 +115,7 @@ const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be
void DebuggerConsoleController::historyAppend(struct CLIDebuggerBackend* be, const char* line) {
Backend* consoleBe = reinterpret_cast<Backend*>(be);
DebuggerConsoleController* self = consoleBe->self;
GameController::Interrupter interrupter(self->m_gameController, true);
CoreController::Interrupter interrupter(self->m_gameController, true);
QMutexLocker lock(&self->m_mutex);
self->m_history.append(QString::fromUtf8(line));
}

View File

@ -16,13 +16,13 @@
namespace QGBA {
class GameController;
class CoreController;
class DebuggerConsoleController : public DebuggerController {
Q_OBJECT
public:
DebuggerConsoleController(GameController* controller, QObject* parent = nullptr);
DebuggerConsoleController(QObject* parent = nullptr);
signals:
void log(const QString&);
@ -44,7 +44,7 @@ private:
static const char* historyLast(struct CLIDebuggerBackend* be, size_t* len);
static void historyAppend(struct CLIDebuggerBackend* be, const char* line);
CLIDebugger m_cliDebugger;
CLIDebugger m_cliDebugger{};
QMutex m_mutex;
QWaitCondition m_cond;

View File

@ -5,32 +5,44 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GDBController.h"
#include "GameController.h"
#include "CoreController.h"
using namespace QGBA;
DebuggerController::DebuggerController(GameController* controller, mDebugger* debugger, QObject* parent)
DebuggerController::DebuggerController(mDebugger* debugger, QObject* parent)
: QObject(parent)
, m_debugger(debugger)
, m_gameController(controller)
{
}
bool DebuggerController::isAttached() {
if (!m_gameController) {
return false;
}
return m_gameController->debugger() == m_debugger;
}
void DebuggerController::setController(std::shared_ptr<CoreController> controller) {
if (m_gameController && controller != m_gameController) {
m_gameController->disconnect(this);
detach();
}
m_gameController = controller;
if (controller) {
connect(m_gameController.get(), &CoreController::stopping, [this]() {
setController(nullptr);
});
}
}
void DebuggerController::attach() {
if (isAttached()) {
return;
}
if (m_gameController->isLoaded()) {
if (m_gameController) {
attachInternal();
m_gameController->setDebugger(m_debugger);
mDebuggerEnter(m_debugger, DEBUGGER_ENTER_ATTACHED, 0);
} else {
QObject::disconnect(m_autoattach);
m_autoattach = connect(m_gameController, &GameController::gameStarted, this, &DebuggerController::attach);
}
}
@ -39,16 +51,18 @@ void DebuggerController::detach() {
if (!isAttached()) {
return;
}
GameController::Interrupter interrupter(m_gameController);
if (m_gameController) {
CoreController::Interrupter interrupter(m_gameController);
shutdownInternal();
m_gameController->setDebugger(nullptr);
}
}
void DebuggerController::breakInto() {
if (!isAttached()) {
return;
}
GameController::Interrupter interrupter(m_gameController);
CoreController::Interrupter interrupter(m_gameController);
mDebuggerEnter(m_debugger, DEBUGGER_ENTER_MANUAL, 0);
}
@ -57,7 +71,7 @@ void DebuggerController::shutdown() {
if (!isAttached()) {
return;
}
GameController::Interrupter interrupter(m_gameController);
CoreController::Interrupter interrupter(m_gameController);
shutdownInternal();
}

View File

@ -8,20 +8,23 @@
#include <QObject>
#include <memory>
struct mDebugger;
namespace QGBA {
class GameController;
class CoreController;
class DebuggerController : public QObject {
Q_OBJECT
public:
DebuggerController(GameController* controller, mDebugger* debugger, QObject* parent = nullptr);
DebuggerController(mDebugger* debugger, QObject* parent = nullptr);
public:
bool isAttached();
void setController(std::shared_ptr<CoreController>);
public slots:
virtual void attach();
@ -34,7 +37,7 @@ protected:
virtual void shutdownInternal();
mDebugger* const m_debugger;
GameController* const m_gameController;
std::shared_ptr<CoreController> m_gameController;
private:
QMetaObject::Connection m_autoattach;

View File

@ -8,16 +8,19 @@
#include <mgba-util/common.h>
#include <memory>
#include <QWidget>
#include "MessagePainter.h"
struct mCoreThread;
struct VDir;
struct VideoShader;
namespace QGBA {
class CoreController;
class Display : public QWidget {
Q_OBJECT
@ -41,6 +44,7 @@ public:
bool isIntegerScalingLocked() const { return m_lockIntegerScaling; }
bool isFiltered() const { return m_filter; }
virtual void startDrawing(std::shared_ptr<CoreController>) = 0;
virtual bool isDrawing() const = 0;
virtual bool supportsShaders() const = 0;
virtual VideoShader* shaders() = 0;
@ -52,7 +56,6 @@ signals:
void hideCursor();
public slots:
virtual void startDrawing(mCoreThread* context) = 0;
virtual void stopDrawing() = 0;
virtual void pauseDrawing() = 0;
virtual void unpauseDrawing() = 0;
@ -60,7 +63,7 @@ public slots:
virtual void lockAspectRatio(bool lock);
virtual void lockIntegerScaling(bool lock);
virtual void filter(bool filter);
virtual void framePosted(const uint32_t*) = 0;
virtual void framePosted() = 0;
virtual void setShaders(struct VDir*) = 0;
virtual void clearShaders() = 0;

View File

@ -7,12 +7,13 @@
#if defined(BUILD_GL) || defined(BUILD_GLES)
#include "CoreController.h"
#include <QApplication>
#include <QResizeEvent>
#include <QTimer>
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#ifdef BUILD_GL
#include "platform/opengl/gl.h"
#endif
@ -52,14 +53,17 @@ VideoShader* DisplayGL::shaders() {
return shaders;
}
void DisplayGL::startDrawing(mCoreThread* thread) {
void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
if (m_drawThread) {
return;
}
QSize dims = controller->screenDimensions();
setSystemDimensions(dims.width(), dims.height());
m_isDrawing = true;
m_painter->setContext(thread);
m_painter->setContext(controller);
m_painter->setMessagePainter(messagePainter());
m_context = thread;
m_context = controller;
m_painter->resize(size());
m_gl->move(0, 0);
m_drawThread = new QThread(this);
@ -69,15 +73,10 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
m_painter->moveToThread(m_drawThread);
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
m_drawThread->start();
mCoreSyncSetVideoSync(&m_context->impl->sync, false);
lockAspectRatio(isAspectRatioLocked());
lockIntegerScaling(isIntegerScalingLocked());
unsigned width, height;
thread->core->desiredVideoDimensions(thread->core, &width, &height);
setSystemDimensions(width, height);
filter(isFiltered());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF());
@ -90,41 +89,27 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
void DisplayGL::stopDrawing() {
if (m_drawThread) {
m_isDrawing = false;
if (mCoreThreadIsActive(m_context)) {
mCoreThreadInterrupt(m_context);
}
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
m_drawThread->exit();
m_drawThread = nullptr;
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
}
m_context.reset();
}
void DisplayGL::pauseDrawing() {
if (m_drawThread) {
m_isDrawing = false;
if (mCoreThreadIsActive(m_context)) {
mCoreThreadInterrupt(m_context);
}
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
}
}
void DisplayGL::unpauseDrawing() {
if (m_drawThread) {
m_isDrawing = true;
if (mCoreThreadIsActive(m_context)) {
mCoreThreadInterrupt(m_context);
}
CoreController::Interrupter interrupter(m_context);
QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
}
}
@ -155,9 +140,9 @@ void DisplayGL::filter(bool filter) {
}
}
void DisplayGL::framePosted(const uint32_t* buffer) {
if (m_drawThread && buffer) {
m_painter->enqueue(buffer);
void DisplayGL::framePosted() {
if (m_drawThread) {
m_painter->enqueue(m_context->drawContext());
QMetaObject::invokeMethod(m_painter, "draw");
}
}
@ -188,12 +173,6 @@ void DisplayGL::resizePainter() {
PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
: m_gl(parent)
, m_active(false)
, m_started(false)
, m_context(nullptr)
, m_shader{}
, m_backend(nullptr)
, m_messagePainter(nullptr)
{
#ifdef BUILD_GL
mGLContext* glBackend;
@ -267,7 +246,7 @@ PainterGL::~PainterGL() {
m_backend = nullptr;
}
void PainterGL::setContext(mCoreThread* context) {
void PainterGL::setContext(std::shared_ptr<CoreController> context) {
m_context = context;
if (!context) {
@ -278,9 +257,8 @@ void PainterGL::setContext(mCoreThread* context) {
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
unsigned width, height;
context->core->desiredVideoDimensions(context->core, &width, &height);
m_backend->setDimensions(m_backend, width, height);
QSize size = m_context->screenDimensions();
m_backend->setDimensions(m_backend, size.width(), size.height());
m_gl->doneCurrent();
}
@ -334,13 +312,13 @@ void PainterGL::start() {
}
void PainterGL::draw() {
if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) {
if (m_queue.isEmpty()) {
return;
}
if (mCoreSyncWaitFrameStart(&m_context->impl->sync) || !m_queue.isEmpty()) {
if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) {
dequeue();
mCoreSyncWaitFrameEnd(&m_context->impl->sync);
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
m_painter.begin(m_gl->context()->device());
performDraw();
m_painter.end();
@ -354,7 +332,7 @@ void PainterGL::draw() {
m_delayTimer.restart();
}
} else {
mCoreSyncWaitFrameEnd(&m_context->impl->sync);
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
}
if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
@ -380,6 +358,7 @@ void PainterGL::stop() {
m_backend->swap(m_backend);
m_gl->doneCurrent();
m_gl->context()->moveToThread(m_gl->thread());
m_context.reset();
moveToThread(m_gl->thread());
}
@ -414,9 +393,8 @@ void PainterGL::enqueue(const uint32_t* backing) {
} else {
buffer = m_free.takeLast();
}
unsigned width, height;
m_context->core->desiredVideoDimensions(m_context->core, &width, &height);
memcpy(buffer, backing, width * height * BYTES_PER_PIXEL);
QSize size = m_context->screenDimensions();
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
m_queue.enqueue(buffer);
m_mutex.unlock();
}

View File

@ -46,12 +46,12 @@ public:
DisplayGL(const QGLFormat& format, QWidget* parent = nullptr);
~DisplayGL();
void startDrawing(std::shared_ptr<CoreController>) override;
bool isDrawing() const override { return m_isDrawing; }
bool supportsShaders() const override;
VideoShader* shaders() override;
public slots:
void startDrawing(mCoreThread* context) override;
void stopDrawing() override;
void pauseDrawing() override;
void unpauseDrawing() override;
@ -59,7 +59,7 @@ public slots:
void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override;
void filter(bool filter) override;
void framePosted(const uint32_t*) override;
void framePosted() override;
void setShaders(struct VDir*) override;
void clearShaders() override;
@ -74,7 +74,7 @@ private:
QGLWidget* m_gl;
PainterGL* m_painter;
QThread* m_drawThread = nullptr;
mCoreThread* m_context = nullptr;
std::shared_ptr<CoreController> m_context;
};
class PainterGL : public QObject {
@ -84,7 +84,7 @@ public:
PainterGL(int majorVersion, QGLWidget* parent);
~PainterGL();
void setContext(mCoreThread*);
void setContext(std::shared_ptr<CoreController>);
void setMessagePainter(MessagePainter*);
void enqueue(const uint32_t* backing);
@ -116,14 +116,14 @@ private:
QPainter m_painter;
QMutex m_mutex;
QGLWidget* m_gl;
bool m_active;
bool m_started;
mCoreThread* m_context;
bool m_active = false;
bool m_started = false;
std::shared_ptr<CoreController> m_context = nullptr;
bool m_supportsShaders;
VideoShader m_shader;
VideoBackend* m_backend;
VideoShader m_shader{};
VideoBackend* m_backend = nullptr;
QSize m_size;
MessagePainter* m_messagePainter;
MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer;
};

View File

@ -5,6 +5,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DisplayQt.h"
#include "CoreController.h"
#include <QPainter>
#include <mgba/core/core.h>
@ -17,11 +19,19 @@ DisplayQt::DisplayQt(QWidget* parent)
{
}
void DisplayQt::startDrawing(mCoreThread* context) {
context->core->desiredVideoDimensions(context->core, &m_width, &m_height);
void DisplayQt::startDrawing(std::shared_ptr<CoreController> controller) {
QSize size = controller->screenDimensions();
m_width = size.width();
m_height = size.height();
setSystemDimensions(m_width, m_height);
m_backing = std::move(QImage());
m_isDrawing = true;
m_context = controller;
}
void DisplayQt::stopDrawing() {
m_isDrawing = false;
m_context.reset();
}
void DisplayQt::lockAspectRatio(bool lock) {
@ -39,8 +49,9 @@ void DisplayQt::filter(bool filter) {
update();
}
void DisplayQt::framePosted(const uint32_t* buffer) {
void DisplayQt::framePosted() {
update();
color_t* buffer = m_context->drawContext();
if (const_cast<const QImage&>(m_backing).bits() == reinterpret_cast<const uchar*>(buffer)) {
return;
}

View File

@ -19,20 +19,20 @@ Q_OBJECT
public:
DisplayQt(QWidget* parent = nullptr);
void startDrawing(std::shared_ptr<CoreController>) override;
bool isDrawing() const override { return m_isDrawing; }
bool supportsShaders() const override { return false; }
VideoShader* shaders() override { return nullptr; }
public slots:
void startDrawing(mCoreThread* context) override;
void stopDrawing() override { m_isDrawing = false; }
void stopDrawing() override;
void pauseDrawing() override { m_isDrawing = false; }
void unpauseDrawing() override { m_isDrawing = true; }
void forceDraw() override { update(); }
void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override;
void filter(bool filter) override;
void framePosted(const uint32_t*) override;
void framePosted() override;
void setShaders(struct VDir*) override {}
void clearShaders() override {}
@ -44,6 +44,7 @@ private:
unsigned m_width;
unsigned m_height;
QImage m_backing{nullptr};
std::shared_ptr<CoreController> m_context = nullptr;
};
}

View File

@ -6,9 +6,10 @@
#include "GBAApp.h"
#include "AudioProcessor.h"
#include "CoreController.h"
#include "CoreManager.h"
#include "ConfigController.h"
#include "Display.h"
#include "GameController.h"
#include "Window.h"
#include "VFileDevice.h"
@ -57,6 +58,9 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
reloadGameDB();
m_manager.setConfig(m_configController->config());
m_manager.setMultiplayerController(&m_multiplayer);
if (!m_configController->getQtOption("audioDriver").isNull()) {
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt()));
}
@ -71,7 +75,8 @@ GBAApp::~GBAApp() {
bool GBAApp::event(QEvent* event) {
if (event->type() == QEvent::FileOpen) {
m_windows[0]->controller()->loadGame(static_cast<QFileOpenEvent*>(event)->file());
CoreController* core = m_manager.loadGame(static_cast<QFileOpenEvent*>(event)->file());
m_windows[0]->setController(core, static_cast<QFileOpenEvent*>(event)->file());
return true;
}
return QApplication::event(event);
@ -81,7 +86,7 @@ Window* GBAApp::newWindow() {
if (m_windows.count() >= MAX_GBAS) {
return nullptr;
}
Window* w = new Window(m_configController, m_multiplayer.attached());
Window* w = new Window(&m_manager, m_configController, m_multiplayer.attached());
int windowId = m_multiplayer.attached();
connect(w, &Window::destroyed, [this, w]() {
m_windows.removeAll(w);
@ -93,7 +98,6 @@ Window* GBAApp::newWindow() {
w->setAttribute(Qt::WA_DeleteOnClose);
w->loadConfig();
w->show();
w->controller()->setMultiplayerController(&m_multiplayer);
w->multiplayerChanged();
for (Window* w : m_windows) {
w->updateMultiplayerStatus(m_windows.count() < MAX_GBAS);
@ -107,7 +111,7 @@ GBAApp* GBAApp::app() {
void GBAApp::pauseAll(QList<Window*>* paused) {
for (auto& window : m_windows) {
if (!window->controller()->isLoaded() || window->controller()->isPaused()) {
if (!window->controller() || window->controller()->isPaused()) {
continue;
}
window->controller()->setPaused(true);
@ -117,9 +121,11 @@ void GBAApp::pauseAll(QList<Window*>* paused) {
void GBAApp::continueAll(const QList<Window*>& paused) {
for (auto& window : paused) {
if (window->controller()) {
window->controller()->setPaused(false);
}
}
}
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
QList<Window*> paused;

View File

@ -8,8 +8,12 @@
#include <QApplication>
#include <QFileDialog>
#include <QList>
#include <QObject>
#include <QString>
#include <QThread>
#include "CoreManager.h"
#include "MultiplayerController.h"
struct NoIntroDB;
@ -70,6 +74,7 @@ private:
ConfigController* m_configController;
QList<Window*> m_windows;
MultiplayerController m_multiplayer;
CoreManager m_manager;
NoIntroDB* m_db = nullptr;
#ifdef USE_SQLITE3

View File

@ -5,12 +5,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GDBController.h"
#include "GameController.h"
#include "CoreController.h"
using namespace QGBA;
GDBController::GDBController(GameController* controller, QObject* parent)
: DebuggerController(controller, &m_gdbStub.d, parent)
GDBController::GDBController(QObject* parent)
: DebuggerController(&m_gdbStub.d, parent)
, m_bindAddress({ IPV4, 0 })
{
GDBStubCreate(&m_gdbStub);
@ -21,7 +21,7 @@ ushort GDBController::port() {
}
bool GDBController::isAttached() {
return m_gameController->debugger() == &m_gdbStub.d;
return m_gameController && m_gameController->debugger() == &m_gdbStub.d;
}
void GDBController::setPort(ushort port) {
@ -34,7 +34,7 @@ void GDBController::setBindAddress(uint32_t bindAddress) {
}
void GDBController::listen() {
GameController::Interrupter interrupter(m_gameController);
CoreController::Interrupter interrupter(m_gameController);
if (!isAttached()) {
attach();
}

View File

@ -14,13 +14,13 @@
namespace QGBA {
class GameController;
class CoreController;
class GDBController : public DebuggerController {
Q_OBJECT
public:
GDBController(GameController* controller, QObject* parent = nullptr);
GDBController(QObject* parent = nullptr);
public:
ushort port();
@ -38,7 +38,7 @@ signals:
private:
virtual void shutdownInternal() override;
GDBStub m_gdbStub;
GDBStub m_gdbStub{};
ushort m_port = 2345;
Address m_bindAddress;

View File

@ -7,6 +7,7 @@
#ifdef USE_MAGICK
#include "CoreController.h"
#include "GBAApp.h"
#include "LogController.h"
@ -39,6 +40,12 @@ GIFView::~GIFView() {
stopRecording();
}
void GIFView::setController(std::shared_ptr<CoreController> controller) {
connect(controller.get(), &CoreController::stopping, this, &GIFView::stopRecording);
connect(this, &GIFView::recordingStarted, controller.get(), &CoreController::setAVStream);
connect(this, &GIFView::recordingStopped, controller.get(), &CoreController::clearAVStream, Qt::DirectConnection);
}
void GIFView::startRecording() {
int delayMs = m_ui.delayAuto->isChecked() ? -1 : m_ui.delayMs->value();
ImageMagickGIFEncoderSetParams(&m_encoder, m_ui.frameskip->value(), delayMs);

View File

@ -10,12 +10,16 @@
#include <QWidget>
#include <memory>
#include "ui_GIFView.h"
#include "feature/imagemagick/imagemagick-gif-encoder.h"
namespace QGBA {
class CoreController;
class GIFView : public QWidget {
Q_OBJECT
@ -26,6 +30,8 @@ public:
mAVStream* getStream() { return &m_encoder.d; }
public slots:
void setController(std::shared_ptr<CoreController>);
void startRecording();
void stopRecording();

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IOViewer.h"
#include "GameController.h"
#include "CoreController.h"
#include <QComboBox>
#include <QFontDatabase>
@ -1023,7 +1023,7 @@ const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions() {
return s_registers;
}
IOViewer::IOViewer(GameController* controller, QWidget* parent)
IOViewer::IOViewer(std::shared_ptr<CoreController> controller, QWidget* parent)
: QDialog(parent)
, m_controller(controller)
{
@ -1067,16 +1067,17 @@ IOViewer::IOViewer(GameController* controller, QWidget* parent)
}
selectRegister(0);
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
}
void IOViewer::updateRegister() {
m_value = 0;
uint16_t value = 0;
m_controller->threadInterrupt();
if (m_controller->isLoaded()) {
{
CoreController::Interrupter interrupter(m_controller);
value = GBAView16(static_cast<ARMCore*>(m_controller->thread()->core->cpu), BASE_IO | m_register);
}
m_controller->threadContinue();
for (int i = 0; i < 16; ++i) {
m_b[i]->setChecked(value & (1 << i) ? Qt::Checked : Qt::Unchecked);
@ -1095,11 +1096,10 @@ void IOViewer::bitFlipped() {
}
void IOViewer::writeback() {
m_controller->threadInterrupt();
if (m_controller->isLoaded()) {
{
CoreController::Interrupter interrupter(m_controller);
GBAIOWrite(static_cast<GBA*>(m_controller->thread()->core->board), m_register, m_value);
}
m_controller->threadContinue();
updateRegister();
}

View File

@ -9,11 +9,13 @@
#include <QDialog>
#include <QList>
#include <memory>
#include "ui_IOViewer.h"
namespace QGBA {
class GameController;
class CoreController;
class IOViewer : public QDialog {
Q_OBJECT
@ -39,7 +41,7 @@ public:
};
typedef QList<RegisterItem> RegisterDescription;
IOViewer(GameController* controller, QWidget* parent = nullptr);
IOViewer(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
static const QList<RegisterDescription>& registerDescriptions();
@ -65,7 +67,7 @@ private:
QCheckBox* m_b[16];
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
};
}

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LoadSaveState.h"
#include "GameController.h"
#include "CoreController.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "VFileDevice.h"
@ -20,7 +20,7 @@
using namespace QGBA;
LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
LoadSaveState::LoadSaveState(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
, m_mode(LoadSave::LOAD)
@ -61,6 +61,8 @@ LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
escape->setShortcut(QKeySequence("Esc"));
escape->setShortcutContext(Qt::WidgetWithChildrenShortcut);
addAction(escape);
connect(m_controller.get(), &CoreController::stopping, this, &QWidget::close);
}
void LoadSaveState::setMode(LoadSave mode) {

View File

@ -8,11 +8,13 @@
#include <QWidget>
#include <memory>
#include "ui_LoadSaveState.h"
namespace QGBA {
class GameController;
class CoreController;
class InputController;
class SavestateButton;
@ -27,7 +29,7 @@ Q_OBJECT
public:
const static int NUM_SLOTS = 9;
LoadSaveState(GameController* controller, QWidget* parent = nullptr);
LoadSaveState(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
void setInputController(InputController* controller);
void setMode(LoadSave mode);
@ -46,7 +48,7 @@ private:
void triggerState(int slot);
Ui::LoadSaveState m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
SavestateButton* m_slots[NUM_SLOTS];
LoadSave m_mode;

View File

@ -11,8 +11,11 @@ LogController LogController::s_global(mLOG_ALL);
LogController::LogController(int levels, QObject* parent)
: QObject(parent)
, m_logLevel(levels)
{
mLogFilterInit(&m_filter);
mLogFilterSet(&m_filter, "gba.bios", mLOG_STUB);
m_filter.defaultLevels = levels;
if (this != &s_global) {
connect(&s_global, &LogController::logPosted, this, &LogController::postLog);
connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels);
@ -26,24 +29,24 @@ LogController::Stream LogController::operator()(int category, int level) {
}
void LogController::postLog(int level, int category, const QString& string) {
if (!(m_logLevel & level)) {
if (!mLogFilterTest(&m_filter, category, static_cast<mLogLevel>(level))) {
return;
}
emit logPosted(level, category, string);
}
void LogController::setLevels(int levels) {
m_logLevel = levels;
m_filter.defaultLevels = levels;
emit levelsSet(levels);
}
void LogController::enableLevels(int levels) {
m_logLevel |= levels;
m_filter.defaultLevels |= levels;
emit levelsEnabled(levels);
}
void LogController::disableLevels(int levels) {
m_logLevel &= ~levels;
m_filter.defaultLevels &= ~levels;
emit levelsDisabled(levels);
}

View File

@ -8,6 +8,8 @@
#include "GBAApp.h"
#include <mgba/core/log.h>
#include <QObject>
#include <QStringList>
@ -35,7 +37,8 @@ private:
public:
LogController(int levels, QObject* parent = nullptr);
int levels() const { return m_logLevel; }
int levels() const { return m_filter.defaultLevels; }
mLogFilter* filter() { return &m_filter; }
Stream operator()(int category, int level);
@ -55,7 +58,7 @@ public slots:
void disableLevels(int levels);
private:
int m_logLevel;
mLogFilter m_filter;
static LogController s_global;
};

View File

@ -6,7 +6,7 @@
#include "MemoryModel.h"
#include "GBAApp.h"
#include "GameController.h"
#include "CoreController.h"
#include "LogController.h"
#include "VFileDevice.h"
@ -91,7 +91,7 @@ MemoryModel::MemoryModel(QWidget* parent)
setRegion(0, 0x10000000, tr("All"));
}
void MemoryModel::setController(GameController* controller) {
void MemoryModel::setController(std::shared_ptr<CoreController> controller) {
m_core = controller->thread()->core;
}

View File

@ -11,6 +11,7 @@
#include <QSize>
#include <QStaticText>
#include <QVector>
#include <memory>
#include <mgba-util/text-codec.h>
@ -19,7 +20,7 @@ struct mCore;
namespace QGBA {
class GameController;
class CoreController;
class MemoryModel : public QAbstractScrollArea {
Q_OBJECT
@ -27,7 +28,7 @@ Q_OBJECT
public:
MemoryModel(QWidget* parent = nullptr);
void setController(GameController* controller);
void setController(std::shared_ptr<CoreController> controller);
void setRegion(uint32_t base, uint32_t size, const QString& name = QString(), int segment = -1);
void setSegment(int segment);

View File

@ -8,12 +8,12 @@
#include <mgba/core/core.h>
#include "GameController.h"
#include "CoreController.h"
#include "MemoryView.h"
using namespace QGBA;
MemorySearch::MemorySearch(GameController* controller, QWidget* parent)
MemorySearch::MemorySearch(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
{
@ -26,6 +26,8 @@ MemorySearch::MemorySearch(GameController* controller, QWidget* parent)
connect(m_ui.numHex, &QPushButton::clicked, this, &MemorySearch::refresh);
connect(m_ui.numDec, &QPushButton::clicked, this, &MemorySearch::refresh);
connect(m_ui.viewMem, &QPushButton::clicked, this, &MemorySearch::openMemory);
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
}
MemorySearch::~MemorySearch() {
@ -109,10 +111,7 @@ void MemorySearch::search() {
mCoreMemorySearchParams params;
GameController::Interrupter interrupter(m_controller);
if (!m_controller->isLoaded()) {
return;
}
CoreController::Interrupter interrupter(m_controller);
mCore* core = m_controller->thread()->core;
if (createParams(&params)) {
@ -125,10 +124,7 @@ void MemorySearch::search() {
void MemorySearch::searchWithin() {
mCoreMemorySearchParams params;
GameController::Interrupter interrupter(m_controller);
if (!m_controller->isLoaded()) {
return;
}
CoreController::Interrupter interrupter(m_controller);
mCore* core = m_controller->thread()->core;
if (createParams(&params)) {
@ -139,10 +135,7 @@ void MemorySearch::searchWithin() {
}
void MemorySearch::refresh() {
GameController::Interrupter interrupter(m_controller);
if (!m_controller->isLoaded()) {
return;
}
CoreController::Interrupter interrupter(m_controller);
mCore* core = m_controller->thread()->core;
m_ui.results->clearContents();
@ -220,7 +213,6 @@ void MemorySearch::openMemory() {
MemoryView* memView = new MemoryView(m_controller);
memView->jumpToAddress(address);
connect(m_controller, &GameController::gameStopped, memView, &QWidget::close);
memView->setAttribute(Qt::WA_DeleteOnClose);
memView->show();
}

View File

@ -6,13 +6,15 @@
#ifndef QGBA_MEMORY_SEARCH
#define QGBA_MEMORY_SEARCH
#include <memory>
#include "ui_MemorySearch.h"
#include <mgba/core/mem-search.h>
namespace QGBA {
class GameController;
class CoreController;
class MemorySearch : public QWidget {
Q_OBJECT
@ -20,7 +22,7 @@ Q_OBJECT
public:
static constexpr size_t LIMIT = 10000;
MemorySearch(GameController* controller, QWidget* parent = nullptr);
MemorySearch(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
~MemorySearch();
public slots:
@ -36,7 +38,7 @@ private:
Ui::MemorySearch m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
mCoreMemorySearchResults m_results;
QByteArray m_string;

View File

@ -6,13 +6,13 @@
#include "MemoryView.h"
#include "GameController.h"
#include "CoreController.h"
#include <mgba/core/core.h>
using namespace QGBA;
MemoryView::MemoryView(GameController* controller, QWidget* parent)
MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
{
@ -45,12 +45,12 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
m_ui.hexfield, static_cast<void (MemoryModel::*)(uint32_t)>(&MemoryModel::jumpToAddress));
connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
connect(controller, &GameController::gameStopped, this, &QWidget::close);
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
connect(controller, &GameController::frameAvailable, this, &MemoryView::update);
connect(controller, &GameController::gamePaused, this, &MemoryView::update);
connect(controller, &GameController::stateLoaded, this, &MemoryView::update);
connect(controller, &GameController::rewound, this, &MemoryView::update);
connect(controller.get(), &CoreController::frameAvailable, this, &MemoryView::update);
connect(controller.get(), &CoreController::paused, this, &MemoryView::update);
connect(controller.get(), &CoreController::stateLoaded, this, &MemoryView::update);
connect(controller.get(), &CoreController::rewound, this, &MemoryView::update);
connect(m_ui.copy, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::copy);
connect(m_ui.save, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::save);
@ -94,9 +94,6 @@ void MemoryView::updateSelection(uint32_t start, uint32_t end) {
void MemoryView::updateStatus() {
int align = m_ui.hexfield->alignment();
if (!m_controller->isLoaded()) {
return;
}
mCore* core = m_controller->thread()->core;
QByteArray selection(m_ui.hexfield->serialize());
QString text(m_ui.hexfield->decodeText(selection));

View File

@ -12,13 +12,13 @@
namespace QGBA {
class GameController;
class CoreController;
class MemoryView : public QWidget {
Q_OBJECT
public:
MemoryView(GameController* controller, QWidget* parent = nullptr);
MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
public slots:
void update();
@ -33,7 +33,7 @@ private slots:
private:
Ui::MemoryView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
QPair<uint32_t, uint32_t> m_selection;
};

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MultiplayerController.h"
#include "GameController.h"
#include "CoreController.h"
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h>
@ -153,7 +153,7 @@ MultiplayerController::MultiplayerController() {
};
}
bool MultiplayerController::attachGame(GameController* controller) {
bool MultiplayerController::attachGame(CoreController* controller) {
if (m_lockstep.attached == MAX_GBAS) {
return false;
}
@ -232,13 +232,15 @@ bool MultiplayerController::attachGame(GameController* controller) {
return false;
}
void MultiplayerController::detachGame(GameController* controller) {
void MultiplayerController::detachGame(CoreController* controller) {
mCoreThread* thread = controller->thread();
if (!thread) {
return;
}
QList<CoreController::Interrupter> interrupters;
for (int i = 0; i < m_players.count(); ++i) {
m_players[i].controller->threadInterrupt();
interrupters.append(m_players[i].controller);
}
switch (controller->platform()) {
#ifdef M_CORE_GBA
@ -269,20 +271,16 @@ void MultiplayerController::detachGame(GameController* controller) {
break;
}
controller->threadContinue();
for (int i = 0; i < m_players.count(); ++i) {
if (m_players[i].controller == controller) {
m_players.removeAt(i);
break;
}
}
for (int i = 0; i < m_players.count(); ++i) {
m_players[i].controller->threadContinue();
}
emit gameDetached();
}
int MultiplayerController::playerId(GameController* controller) {
int MultiplayerController::playerId(CoreController* controller) {
for (int i = 0; i < m_players.count(); ++i) {
if (m_players[i].controller == controller) {
return i;

View File

@ -23,7 +23,7 @@ struct GBASIOLockstepNode;
namespace QGBA {
class GameController;
class CoreController;
class MultiplayerController : public QObject {
Q_OBJECT
@ -31,11 +31,11 @@ Q_OBJECT
public:
MultiplayerController();
bool attachGame(GameController*);
void detachGame(GameController*);
bool attachGame(CoreController*);
void detachGame(CoreController*);
int attached();
int playerId(GameController*);
int playerId(CoreController*);
signals:
void gameAttached();
@ -43,7 +43,7 @@ signals:
private:
struct Player {
GameController* controller;
CoreController* controller;
GBSIOLockstepNode* gbNode;
GBASIOLockstepNode* gbaNode;
int awake;

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ObjView.h"
#include "CoreController.h"
#include "GBAApp.h"
#include <QFontDatabase>
@ -24,7 +25,7 @@
using namespace QGBA;
ObjView::ObjView(GameController* controller, QWidget* parent)
ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
: AssetView(controller, parent)
, m_controller(controller)
{
@ -119,16 +120,16 @@ void ObjView::updateTilesGBA(bool force) {
};
m_objInfo = newInfo;
m_tileOffset = tile;
mTileCacheSetPalette(m_tileCache.get(), paletteSet);
mTileCacheSetPalette(m_tileCache, paletteSet);
int i = 0;
for (int y = 0; y < height / 8; ++y) {
for (int x = 0; x < width / 8; ++x, ++i, ++tile, ++tileBase) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * tileBase], tile, palette);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[32 * tileBase], tile, palette);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), tile, palette));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, tile, palette));
}
}
tile += newInfo.stride - width / 8;
@ -215,16 +216,16 @@ void ObjView::updateTilesGB(bool force) {
m_tileOffset = tile;
int i = 0;
mTileCacheSetPalette(m_tileCache.get(), 0);
mTileCacheSetPalette(m_tileCache, 0);
m_ui.tile->setPalette(palette);
m_ui.tile->setPaletteSet(0, 512, 1024);
for (int y = 0; y < height / 8; ++y, ++i) {
unsigned t = tile + i;
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[16 * t], t, palette);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, t, palette));
}
}
@ -247,7 +248,7 @@ void ObjView::updateTilesGB(bool force) {
#ifdef USE_PNG
void ObjView::exportObj() {
GameController::Interrupter interrupter(m_controller);
CoreController::Interrupter interrupter(m_controller);
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"),
tr("Portable Network Graphics (*.png)"));
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
@ -256,11 +257,11 @@ void ObjView::exportObj() {
return;
}
mTileCacheSetPalette(m_tileCache.get(), m_objInfo.paletteSet);
mTileCacheSetPalette(m_tileCache, m_objInfo.paletteSet);
png_structp png = PNGWriteOpen(vf);
png_infop info = PNGWriteHeader8(png, m_objInfo.width * 8, m_objInfo.height * 8);
const uint16_t* rawPalette = mTileCacheGetPalette(m_tileCache.get(), m_objInfo.paletteId);
const uint16_t* rawPalette = mTileCacheGetPalette(m_tileCache, m_objInfo.paletteId);
unsigned colors = 1 << m_objInfo.bits;
uint32_t palette[256];
for (unsigned c = 0; c < colors && c < 256; ++c) {

View File

@ -7,7 +7,6 @@
#define QGBA_OBJ_VIEW
#include "AssetView.h"
#include "GameController.h"
#include "ui_ObjView.h"
@ -15,11 +14,13 @@
namespace QGBA {
class CoreController;
class ObjView : public AssetView {
Q_OBJECT
public:
ObjView(GameController* controller, QWidget* parent = nullptr);
ObjView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
#ifdef USE_PNG
public slots:
@ -40,7 +41,7 @@ private:
Ui::ObjView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
mTileCacheEntry m_tileStatus[1024 * 32] = {}; // TODO: Correct size
int m_objId = 0;
struct ObjInfo {

View File

@ -9,7 +9,7 @@
#include <QPushButton>
#include "ConfigController.h"
#include "GameController.h"
#include "CoreController.h"
#ifdef M_CORE_GBA
#include "GBAOverride.h"
@ -28,9 +28,8 @@ QList<enum GBModel> OverrideView::s_gbModelList;
QList<enum GBMemoryBankControllerType> OverrideView::s_mbcList;
#endif
OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent)
OverrideView::OverrideView(ConfigController* config, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
, m_controller(controller)
, m_config(config)
{
#ifdef M_CORE_GB
@ -57,9 +56,6 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config,
#endif
m_ui.setupUi(this);
connect(controller, &GameController::gameStarted, this, &OverrideView::gameStarted);
connect(controller, &GameController::gameStopped, this, &OverrideView::gameStopped);
connect(m_ui.hwAutodetect, &QAbstractButton::toggled, [this] (bool enabled) {
m_ui.hwRTC->setEnabled(!enabled);
m_ui.hwGyro->setEnabled(!enabled);
@ -106,9 +102,16 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config,
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &OverrideView::saveOverride);
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
m_ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
}
if (controller->isLoaded()) {
gameStarted(controller->thread());
void OverrideView::setController(std::shared_ptr<CoreController> controller) {
m_controller = controller;
gameStarted();
connect(controller.get(), &CoreController::stopping, this, &OverrideView::gameStopped);
if (m_override) {
m_controller->setOverride(std::move(m_override));
} else {
m_controller->clearOverride();
}
}
@ -149,7 +152,7 @@ bool OverrideView::eventFilter(QObject* obj, QEvent* event) {
}
void OverrideView::saveOverride() {
if (!m_config) {
if (!m_config || !m_controller) {
return;
}
m_config->saveOverride(*m_controller->override());
@ -158,7 +161,7 @@ void OverrideView::saveOverride() {
void OverrideView::updateOverrides() {
#ifdef M_CORE_GBA
if (m_ui.tabWidget->currentWidget() == m_ui.tabGBA) {
GBAOverride* gba = new GBAOverride;
std::unique_ptr<GBAOverride> gba(new GBAOverride);
memset(gba->override.id, 0, 4);
gba->override.savetype = static_cast<SavedataType>(m_ui.savetype->currentIndex() - 1);
gba->override.hardware = HW_NO_OVERRIDE;
@ -193,18 +196,18 @@ void OverrideView::updateOverrides() {
gba->override.idleLoop = parsedIdleLoop;
}
if (gba->override.savetype != SAVEDATA_AUTODETECT || gba->override.hardware != HW_NO_OVERRIDE ||
gba->override.idleLoop != IDLE_LOOP_NONE) {
m_controller->setOverride(gba);
m_override = std::move(gba);
} else {
m_controller->clearOverride();
delete gba;
m_override.reset();
}
}
#endif
#ifdef M_CORE_GB
if (m_ui.tabWidget->currentWidget() == m_ui.tabGB) {
GBOverride* gb = new GBOverride;
std::unique_ptr<GBOverride> gb(new GBOverride);
gb->override.mbc = s_mbcList[m_ui.mbc->currentIndex()];
gb->override.model = s_gbModelList[m_ui.gbModel->currentIndex()];
gb->override.gbColors[0] = m_gbColors[0];
@ -214,20 +217,17 @@ void OverrideView::updateOverrides() {
bool hasOverride = gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT;
hasOverride = hasOverride || (m_gbColors[0] | m_gbColors[1] | m_gbColors[2] | m_gbColors[3]);
if (hasOverride) {
m_controller->setOverride(gb);
m_override = std::move(gb);
} else {
m_controller->clearOverride();
delete gb;
m_override.reset();
}
}
#endif
}
void OverrideView::gameStarted(mCoreThread* thread) {
if (!thread->core) {
gameStopped();
return;
}
void OverrideView::gameStarted() {
CoreController::Interrupter interrupter(m_controller);
mCoreThread* thread = m_controller->thread();
m_ui.tabWidget->setEnabled(false);
m_ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
@ -278,6 +278,7 @@ void OverrideView::gameStarted(mCoreThread* thread) {
}
void OverrideView::gameStopped() {
m_controller.reset();
m_ui.tabWidget->setEnabled(true);
m_ui.savetype->setCurrentIndex(0);
m_ui.idleLoop->clear();

View File

@ -8,10 +8,14 @@
#include <QDialog>
#include <memory>
#ifdef M_CORE_GB
#include <mgba/gb/interface.h>
#endif
#include "Override.h"
#include "ui_OverrideView.h"
struct mCoreThread;
@ -19,21 +23,23 @@ struct mCoreThread;
namespace QGBA {
class ConfigController;
class GameController;
class CoreController;
class Override;
class OverrideView : public QDialog {
Q_OBJECT
public:
OverrideView(GameController* controller, ConfigController* config, QWidget* parent = nullptr);
OverrideView(ConfigController* config, QWidget* parent = nullptr);
void setController(std::shared_ptr<CoreController> controller);
public slots:
void saveOverride();
private slots:
void updateOverrides();
void gameStarted(mCoreThread*);
void gameStarted();
void gameStopped();
protected:
@ -42,7 +48,8 @@ protected:
private:
Ui::OverrideView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
std::unique_ptr<Override> m_override;
ConfigController* m_config;
#ifdef M_CORE_GB

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "PaletteView.h"
#include "CoreController.h"
#include "GBAApp.h"
#include "LogController.h"
#include "VFileDevice.h"
@ -24,13 +25,13 @@
using namespace QGBA;
PaletteView::PaletteView(GameController* controller, QWidget* parent)
PaletteView::PaletteView(std::shared_ptr<CoreController> controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
{
m_ui.setupUi(this);
connect(m_controller, &GameController::frameAvailable, this, &PaletteView::updatePalette);
connect(controller.get(), &CoreController::frameAvailable, this, &PaletteView::updatePalette);
m_ui.bgGrid->setDimensions(QSize(16, 16));
m_ui.objGrid->setDimensions(QSize(16, 16));
int count = 256;
@ -61,7 +62,7 @@ PaletteView::PaletteView(GameController* controller, QWidget* parent)
connect(m_ui.exportBG, &QAbstractButton::clicked, [this, count] () { exportPalette(0, count); });
connect(m_ui.exportOBJ, &QAbstractButton::clicked, [this, count] () { exportPalette(count, count); });
connect(controller, &GameController::gameStopped, this, &QWidget::close);
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
}
void PaletteView::updatePalette() {
@ -133,7 +134,7 @@ void PaletteView::exportPalette(int start, int length) {
length = 512 - start;
}
GameController::Interrupter interrupter(m_controller);
CoreController::Interrupter interrupter(m_controller);
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export palette"),
tr("Windows PAL (*.pal);;Adobe Color Table (*.act)"));
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);

View File

@ -8,20 +8,22 @@
#include <QWidget>
#include "GameController.h"
#include <memory>
#include "Swatch.h"
#include "ui_PaletteView.h"
namespace QGBA {
class CoreController;
class Swatch;
class PaletteView : public QWidget {
Q_OBJECT
public:
PaletteView(GameController* controller, QWidget* parent = nullptr);
PaletteView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
public slots:
void updatePalette();
@ -34,7 +36,7 @@ private:
Ui::PaletteView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
};
}

View File

@ -6,7 +6,7 @@
#include "ROMInfo.h"
#include "GBAApp.h"
#include "GameController.h"
#include "CoreController.h"
#include <mgba/core/core.h>
#ifdef M_CORE_GB
@ -21,21 +21,17 @@
using namespace QGBA;
ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
ROMInfo::ROMInfo(std::shared_ptr<CoreController> controller, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
{
m_ui.setupUi(this);
if (!controller->isLoaded()) {
return;
}
#ifdef USE_SQLITE3
const NoIntroDB* db = GBAApp::app()->gameDB();
#endif
uint32_t crc32 = 0;
GameController::Interrupter interrupter(controller);
CoreController::Interrupter interrupter(controller);
mCore* core = controller->thread()->core;
char title[17] = {};
core->getGameTitle(core, title);

View File

@ -8,17 +8,19 @@
#include <QWidget>
#include <memory>
#include "ui_ROMInfo.h"
namespace QGBA {
class GameController;
class CoreController;
class ROMInfo : public QDialog {
Q_OBJECT
public:
ROMInfo(GameController* controller, QWidget* parent = nullptr);
ROMInfo(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
private:
Ui::ROMInfo m_ui;

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SensorView.h"
#include "GameController.h"
#include "CoreController.h"
#include "GamepadAxisEvent.h"
#include "InputController.h"
@ -14,9 +14,8 @@
using namespace QGBA;
SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent)
SensorView::SensorView(InputController* input, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
, m_controller(controller)
, m_input(input)
, m_rotation(input->rotationSource())
{
@ -26,22 +25,13 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg
this, &SensorView::setLuminanceValue);
connect(m_ui.lightSlide, &QAbstractSlider::valueChanged, this, &SensorView::setLuminanceValue);
connect(m_ui.timeNoOverride, &QAbstractButton::clicked, controller, &GameController::setRealTime);
connect(m_ui.timeFixed, &QRadioButton::clicked, [controller, this] () {
controller->setFixedTime(m_ui.time->dateTime());
});
connect(m_ui.timeFakeEpoch, &QRadioButton::clicked, [controller, this] () {
controller->setFakeEpoch(m_ui.time->dateTime());
});
connect(m_ui.time, &QDateTimeEdit::dateTimeChanged, [controller, this] (const QDateTime&) {
connect(m_ui.time, &QDateTimeEdit::dateTimeChanged, [this] (const QDateTime&) {
m_ui.timeButtons->checkedButton()->clicked();
});
connect(m_ui.timeNow, &QPushButton::clicked, [controller, this] () {
connect(m_ui.timeNow, &QPushButton::clicked, [this] () {
m_ui.time->setDateTime(QDateTime::currentDateTime());
});
connect(m_controller, &GameController::luminanceValueChanged, this, &SensorView::luminanceValueChanged);
m_timer.setInterval(2);
connect(&m_timer, &QTimer::timeout, this, &SensorView::updateSensors);
if (!m_rotation || !m_rotation->readTiltX || !m_rotation->readTiltY) {
@ -66,6 +56,22 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg
m_input->setGyroSensitivity(value * 1e8f);
});
m_input->stealFocus(this);
connect(m_input, &InputController::luminanceValueChanged, this, &SensorView::luminanceValueChanged);
}
void SensorView::setController(std::shared_ptr<CoreController> controller) {
m_controller = controller;
connect(m_ui.timeNoOverride, &QAbstractButton::clicked, controller.get(), &CoreController::setRealTime);
connect(m_ui.timeFixed, &QRadioButton::clicked, [controller, this] () {
controller->setFixedTime(m_ui.time->dateTime());
});
connect(m_ui.timeFakeEpoch, &QRadioButton::clicked, [controller, this] () {
controller->setFakeEpoch(m_ui.time->dateTime());
});
connect(controller.get(), &CoreController::stopping, [this]() {
m_controller.reset();
});
}
void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) {
@ -107,16 +113,7 @@ bool SensorView::eventFilter(QObject*, QEvent* event) {
}
void SensorView::updateSensors() {
GameController::Interrupter interrupter(m_controller);
if (m_rotation->sample &&
(!m_controller->isLoaded() || !(static_cast<GBA*>(m_controller->thread()->core->board)->memory.hw.devices & (HW_GYRO | HW_TILT)))) {
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
m_rotation->sample(m_rotation);
if (m_rotation->sample && (!m_controller || m_controller->isPaused())) {
m_rotation->sample(m_rotation);
}
if (m_rotation->readTiltX && m_rotation->readTiltY) {
@ -132,7 +129,9 @@ void SensorView::updateSensors() {
void SensorView::setLuminanceValue(int value) {
value = std::max(0, std::min(value, 255));
m_controller->setLuminanceValue(value);
if (m_input) {
m_input->setLuminanceValue(value);
}
}
void SensorView::luminanceValueChanged(int value) {

View File

@ -10,6 +10,7 @@
#include <QDialog>
#include <functional>
#include <memory>
#include "ui_SensorView.h"
@ -18,7 +19,7 @@ struct mRotationSource;
namespace QGBA {
class ConfigController;
class GameController;
class CoreController;
class GamepadAxisEvent;
class InputController;
@ -26,7 +27,9 @@ class SensorView : public QDialog {
Q_OBJECT
public:
SensorView(GameController* controller, InputController* input, QWidget* parent = nullptr);
SensorView(InputController* input, QWidget* parent = nullptr);
void setController(std::shared_ptr<CoreController>);
protected:
bool eventFilter(QObject*, QEvent* event) override;
@ -41,7 +44,7 @@ private:
Ui::SensorView m_ui;
std::function<void(int)> m_jiggered;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
InputController* m_input;
mRotationSource* m_rotation;
QTimer m_timer;

View File

@ -183,19 +183,26 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
SettingsView::~SettingsView() {
#if defined(BUILD_GL) || defined(BUILD_GLES)
if (m_shader) {
m_ui.stackedWidget->removeWidget(m_shader);
m_shader->setParent(nullptr);
}
setShaderSelector(nullptr);
#endif
}
void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) {
#if defined(BUILD_GL) || defined(BUILD_GLES)
if (m_shader) {
auto items = m_ui.tabs->findItems(tr("Shaders"), Qt::MatchFixedString);
for (const auto& item : items) {
m_ui.tabs->removeItemWidget(item);
}
m_ui.stackedWidget->removeWidget(m_shader);
m_shader->setParent(nullptr);
}
m_shader = shaderSelector;
if (shaderSelector) {
m_ui.stackedWidget->addWidget(m_shader);
m_ui.tabs->addItem(tr("Shaders"));
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved);
}
#endif
}
@ -281,6 +288,7 @@ void SettingsView::updateConfig() {
if (displayDriver != m_controller->getQtOption("displayDriver")) {
m_controller->setQtOption("displayDriver", displayDriver);
Display::setDriver(static_cast<Display::Driver>(displayDriver.toInt()));
setShaderSelector(nullptr);
emit displayDriverChanged();
}

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TileView.h"
#include "CoreController.h"
#include "GBAApp.h"
#include <QFontDatabase>
@ -16,7 +17,7 @@
using namespace QGBA;
TileView::TileView(GameController* controller, QWidget* parent)
TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
: AssetView(controller, parent)
, m_controller(controller)
{
@ -79,40 +80,40 @@ TileView::TileView(GameController* controller, QWidget* parent)
void TileView::updateTilesGBA(bool force) {
if (m_ui.palette256->isChecked()) {
m_ui.tiles->setTileCount(1536);
mTileCacheSetPalette(m_tileCache.get(), 1);
mTileCacheSetPalette(m_tileCache, 1);
for (int i = 0; i < 1024; ++i) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * i], i, 0);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[32 * i], i, 0);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), i, 0));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, i, 0));
}
}
for (int i = 1024; i < 1536; ++i) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * i], i, 1);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[32 * i], i, 1);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), i, 1));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, i, 1));
}
}
} else {
m_ui.tiles->setTileCount(3072);
mTileCacheSetPalette(m_tileCache.get(), 0);
mTileCacheSetPalette(m_tileCache, 0);
for (int i = 0; i < 2048; ++i) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * i], i, m_paletteId);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[32 * i], i, m_paletteId);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), i, m_paletteId));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, i, m_paletteId));
}
}
for (int i = 2048; i < 3072; ++i) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * i], i, m_paletteId + 16);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[32 * i], i, m_paletteId + 16);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), i, m_paletteId + 16));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, i, m_paletteId + 16));
}
}
}
@ -124,13 +125,13 @@ void TileView::updateTilesGB(bool force) {
const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
int count = gb->model >= GB_MODEL_CGB ? 1024 : 512;
m_ui.tiles->setTileCount(count);
mTileCacheSetPalette(m_tileCache.get(), 0);
mTileCacheSetPalette(m_tileCache, 0);
for (int i = 0; i < count; ++i) {
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * i], i, m_paletteId);
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache, &m_tileStatus[16 * i], i, m_paletteId);
if (data) {
m_ui.tiles->setTile(i, data);
} else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), i, m_paletteId));
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache, i, m_paletteId));
}
}
}

View File

@ -7,7 +7,6 @@
#define QGBA_TILE_VIEW
#include "AssetView.h"
#include "GameController.h"
#include "ui_TileView.h"
@ -15,11 +14,13 @@
namespace QGBA {
class CoreController;
class TileView : public AssetView {
Q_OBJECT
public:
TileView(GameController* controller, QWidget* parent = nullptr);
TileView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
public slots:
void updatePalette(int);
@ -34,7 +35,7 @@ private:
Ui::TileView m_ui;
GameController* m_controller;
std::shared_ptr<CoreController> m_controller;
mTileCacheEntry m_tileStatus[3072 * 32] = {}; // TODO: Correct size
int m_paletteId = 0;
};

View File

@ -13,7 +13,33 @@ VFileDevice::VFileDevice(VFile* vf, QObject* parent)
: QIODevice(parent)
, m_vf(vf)
{
// Nothing to do
// TODO: Correct mode
if (vf) {
setOpenMode(QIODevice::ReadWrite);
}
}
void VFileDevice::close() {
QIODevice::close();
m_vf->close(m_vf);
m_vf = nullptr;
}
bool VFileDevice::resize(qint64 sz) {
m_vf->truncate(m_vf, sz);
return true;
}
bool VFileDevice::seek(qint64 pos) {
QIODevice::seek(pos);
return m_vf->seek(m_vf, pos, SEEK_SET) == pos;
}
VFileDevice& VFileDevice::operator=(VFile* vf) {
close();
m_vf = vf;
setOpenMode(QIODevice::ReadWrite);
return *this;
}
qint64 VFileDevice::readData(char* data, qint64 maxSize) {

View File

@ -17,7 +17,16 @@ class VFileDevice : public QIODevice {
Q_OBJECT
public:
VFileDevice(VFile* vf, QObject* parent = nullptr);
VFileDevice(VFile* vf = nullptr, QObject* parent = nullptr);
virtual void close() override;
virtual bool seek(qint64 pos) override;
virtual qint64 size() const override;
bool resize(qint64 sz);
VFileDevice& operator=(VFile*);
operator VFile*() { return m_vf; }
static VFile* open(const QString& path, int mode);
static VDir* openDir(const QString& path);
@ -26,7 +35,6 @@ public:
protected:
virtual qint64 readData(char* data, qint64 maxSize) override;
virtual qint64 writeData(const char* data, qint64 maxSize) override;
virtual qint64 size() const override;
private:
VFile* m_vf;

View File

@ -195,6 +195,14 @@ VideoView::~VideoView() {
free(m_containerCstr);
}
void VideoView::setController(std::shared_ptr<CoreController> controller) {
connect(controller.get(), &CoreController::stopping, this, &VideoView::stopRecording);
connect(this, &VideoView::recordingStarted, controller.get(), &CoreController::setAVStream);
connect(this, &VideoView::recordingStopped, controller.get(), &CoreController::clearAVStream, Qt::DirectConnection);
setNativeResolution(controller->screenDimensions());
}
void VideoView::startRecording() {
if (!validateSettings()) {
return;

View File

@ -10,12 +10,18 @@
#include <QWidget>
#include <memory>
#include "CoreController.h"
#include "ui_VideoView.h"
#include "feature/ffmpeg/ffmpeg-encoder.h"
namespace QGBA {
class CoreController;
class VideoView : public QWidget {
Q_OBJECT
@ -26,6 +32,8 @@ public:
mAVStream* getStream() { return &m_encoder.d; }
public slots:
void setController(std::shared_ptr<CoreController>);
void startRecording();
void stopRecording();
void setNativeResolution(const QSize&);

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -12,6 +12,7 @@
#include <QTimer>
#include <functional>
#include <memory>
#include <mgba/core/thread.h>
@ -22,14 +23,17 @@ struct mArguments;
namespace QGBA {
class AudioProcessor;
class ConfigController;
class CoreController;
class CoreManager;
class DebuggerConsoleController;
class Display;
class GameController;
class GDBController;
class GIFView;
class LibraryController;
class LogView;
class OverrideView;
class ShaderSelector;
class VideoView;
class WindowBackground;
@ -38,10 +42,10 @@ class Window : public QMainWindow {
Q_OBJECT
public:
Window(ConfigController* config, int playerId = 0, QWidget* parent = nullptr);
Window(CoreManager* manager, ConfigController* config, int playerId = 0, QWidget* parent = nullptr);
virtual ~Window();
GameController* controller() { return m_controller; }
std::shared_ptr<CoreController> controller() { return m_controller; }
void setConfig(ConfigController*);
void argumentsPassed(mArguments*);
@ -51,13 +55,12 @@ public:
void updateMultiplayerStatus(bool canOpenAnother) { m_multiWindow->setEnabled(canOpenAnother); }
signals:
void startDrawing(mCoreThread*);
void startDrawing();
void shutdown();
void audioBufferSamplesChanged(int samples);
void sampleRateChanged(unsigned samples);
void fpsTargetChanged(float target);
void paused(bool);
public slots:
void setController(CoreController* controller, const QString& fname);
void selectROM();
#ifdef USE_SQLITE3
void selectROMInArchive();
@ -80,7 +83,6 @@ public slots:
void exportSharkport();
void openSettingsWindow();
void openAboutScreen();
void startVideoLog();
@ -113,12 +115,15 @@ protected:
virtual void mouseReleaseEvent(QMouseEvent*) override;
private slots:
void gameStarted(mCoreThread*, const QString&);
void gameStarted();
void gameStopped();
void gameCrashed(const QString&);
void gameFailed();
void unimplementedBiosCall(int);
void reloadAudioDriver();
void reloadDisplayDriver();
void tryMakePortable();
void mustRestart();
@ -141,8 +146,8 @@ private:
void openView(QWidget* widget);
template <typename T, typename A> std::function<void()> openTView(A arg);
template <typename T> std::function<void()> openTView();
template <typename T, typename... A> std::function<void()> openTView(A... arg);
template <typename T, typename... A> std::function<void()> openControllerTView(A... arg);
QAction* addControlledAction(QMenu* menu, QAction* action, const QString& name);
QAction* addHiddenAction(QMenu* menu, QAction* action, const QString& name);
@ -152,8 +157,11 @@ private:
QString getFilters() const;
QString getFiltersArchive() const;
GameController* m_controller;
Display* m_display;
CoreManager* m_manager;
std::shared_ptr<CoreController> m_controller;
std::unique_ptr<AudioProcessor> m_audioProcessor;
std::unique_ptr<Display> m_display;
int m_savedScale;
// TODO: Move these to a new class
QList<QAction*> m_gameActions;
@ -177,14 +185,17 @@ private:
QMenu* m_mruMenu = nullptr;
QMenu* m_videoLayers;
QMenu* m_audioChannels;
ShaderSelector* m_shaderView;
std::unique_ptr<ShaderSelector> m_shaderView;
bool m_fullscreenOnStart = false;
QTimer m_focusCheck;
bool m_autoresume = false;
bool m_wasOpened = false;
QString m_pendingPatch;
bool m_hitUnimplementedBiosCall;
OverrideView* m_overrideView = nullptr;
#ifdef USE_FFMPEG
VideoView* m_videoView = nullptr;
#endif

View File

@ -6,7 +6,7 @@
#include "InputController.h"
#include "ConfigController.h"
#include "GameController.h"
#include "CoreController.h"
#include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h"
#include "InputItem.h"
@ -94,18 +94,42 @@ void InputController::addKey(const QString& name) {
return;
}
m_keyIndex.addItem(qMakePair([this, name]() {
emit keyPressed(keyId(name));
m_activeKeys |= 1 << keyId(name);
}, [this, name]() {
emit keyReleased(keyId(name));
m_activeKeys &= ~(1 << keyId(name));
}), name, QString("key%0").arg(name), m_bindings.get());
m_keyIndex.addItem(qMakePair([this, name]() {
emit keyAutofire(keyId(name), true);
setAutofire(keyId(name), true);
}, [this, name]() {
emit keyAutofire(keyId(name), false);
setAutofire(keyId(name), false);
}), name, QString("autofire%1").arg(name), m_autofire.get());
}
void InputController::setAutofire(int key, bool enable) {
if (key >= 32 || key < 0) {
return;
}
m_autofireEnabled[key] = enable;
m_autofireStatus[key] = 0;
}
int InputController::updateAutofire() {
int active = 0;
for (int k = 0; k < 32; ++k) {
if (!m_autofireEnabled[k]) {
continue;
}
++m_autofireStatus[k];
if (m_autofireStatus[k]) {
m_autofireStatus[k] = 0;
active |= 1 << k;
}
}
return active;
}
void InputController::addPlatform(mPlatform platform, const mInputPlatformInfo* info) {
m_keyInfo[platform] = info;
for (size_t i = 0; i < info->nKeys; ++i) {
@ -130,6 +154,20 @@ void InputController::setPlatform(mPlatform platform) {
rebuildKeyIndex();
restoreModel();
#ifdef M_CORE_GBA
m_lux.p = this;
m_lux.sample = [](GBALuminanceSource* context) {
InputControllerLux* lux = static_cast<InputControllerLux*>(context);
lux->value = 0xFF - lux->p->m_luxValue;
};
m_lux.readLuminance = [](GBALuminanceSource* context) {
InputControllerLux* lux = static_cast<InputControllerLux*>(context);
return lux->value;
};
setLuminanceLevel(0);
#endif
}
InputController::~InputController() {
@ -167,7 +205,6 @@ void InputController::setConfiguration(ConfigController* config) {
m_config = config;
m_inputIndex.setConfigController(config);
m_keyIndex.setConfigController(config);
setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
loadConfiguration(KEYBOARD);
loadProfile(KEYBOARD, profileForType(KEYBOARD));
#ifdef BUILD_SDL
@ -382,7 +419,7 @@ const mInputMap* InputController::map() {
}
int InputController::pollEvents() {
int activeButtons = 0;
int activeButtons = m_activeKeys;
#ifdef BUILD_SDL
if (m_playerAttached && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
@ -853,3 +890,34 @@ void InputController::rebindKey(const QString& key) {
bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key);
#endif
}
void InputController::increaseLuminanceLevel() {
setLuminanceLevel(m_luxLevel + 1);
}
void InputController::decreaseLuminanceLevel() {
setLuminanceLevel(m_luxLevel - 1);
}
void InputController::setLuminanceLevel(int level) {
int value = 0x16;
level = std::max(0, std::min(10, level));
if (level > 0) {
value += GBA_LUX_LEVELS[level - 1];
}
setLuminanceValue(value);
}
void InputController::setLuminanceValue(uint8_t value) {
m_luxValue = value;
value = std::max<int>(value - 0x16, 0);
m_luxLevel = 10;
for (int i = 0; i < 10; ++i) {
if (value < GBA_LUX_LEVELS[i]) {
m_luxLevel = i;
break;
}
}
emit luminanceValueChanged(m_luxValue);
}

View File

@ -21,6 +21,8 @@
#include <mgba/core/core.h>
#include <mgba/core/input.h>
#include <mgba/gba/interface.h>
#ifdef BUILD_SDL
#include "platform/sdl/sdl-events.h"
#endif
@ -62,8 +64,9 @@ public:
void saveProfile(uint32_t type, const QString& profile);
const char* profileForType(uint32_t type);
bool allowOpposing() const { return m_allowOpposing; }
void setAllowOpposing(bool allowOpposing) { m_allowOpposing = allowOpposing; }
GBAKey mapKeyboard(int key) const;
void bindKey(uint32_t type, int key, GBAKey);
const mInputMap* map();
@ -97,22 +100,29 @@ public:
mRumble* rumble();
mRotationSource* rotationSource();
GBALuminanceSource* luminance() { return &m_lux; }
signals:
void profileLoaded(const QString& profile);
void keyPressed(int);
void keyReleased(int);
void keyAutofire(int, bool enabled);
void luminanceValueChanged(int value);
public slots:
void testGamepad(int type);
void updateJoysticks();
int updateAutofire();
void setAutofire(int key, bool enable);
// TODO: Move these to somewhere that makes sense
void suspendScreensaver();
void resumeScreensaver();
void setScreensaverSuspendable(bool);
void increaseLuminanceLevel();
void decreaseLuminanceLevel();
void setLuminanceLevel(int level);
void setLuminanceValue(uint8_t value);
protected:
bool eventFilter(QObject*, QEvent*) override;
@ -129,10 +139,21 @@ private:
InputIndex m_inputIndex;
InputIndex m_keyIndex;
struct InputControllerLux : GBALuminanceSource {
InputController* p;
uint8_t value;
} m_lux;
uint8_t m_luxValue;
int m_luxLevel;
mInputMap m_inputMap;
int m_activeKeys;
bool m_autofireEnabled[32] = {};
int m_autofireStatus[32] = {};
ConfigController* m_config = nullptr;
int m_playerId;
bool m_allowOpposing = false;
QWidget* m_topLevel;
QWidget* m_focusParent;
QMap<mPlatform, const mInputPlatformInfo*> m_keyInfo;

View File

@ -83,6 +83,9 @@ LibraryController::~LibraryController() {
}
void LibraryController::setViewStyle(LibraryStyle newStyle) {
if (m_currentStyle == newStyle) {
return;
}
m_currentStyle = newStyle;
AbstractGameList* newCurrentList = nullptr;