mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/input-revamp' into medusa
This commit is contained in:
commit
394a2e0bb8
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
7
CHANGES
7
CHANGES
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -16,16 +16,17 @@ AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
|
|||
{
|
||||
}
|
||||
|
||||
AudioProcessorSDL::~AudioProcessorSDL() {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
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) {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
mSDLInitAudio(&m_audio, input);
|
||||
}
|
||||
void AudioProcessorSDL::stop() {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
AudioProcessor::stop();
|
||||
}
|
||||
|
||||
bool AudioProcessorSDL::start() {
|
||||
|
@ -51,10 +52,12 @@ void AudioProcessorSDL::pause() {
|
|||
|
||||
void AudioProcessorSDL::setBufferSamples(int samples) {
|
||||
AudioProcessor::setBufferSamples(samples);
|
||||
m_audio.samples = samples;
|
||||
if (m_audio.core) {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
mSDLInitAudio(&m_audio, input());
|
||||
if (m_audio.samples != samples) {
|
||||
m_audio.samples = samples;
|
||||
if (m_audio.core) {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
mSDLInitAudio(&m_audio, input());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,10 +65,12 @@ void AudioProcessorSDL::inputParametersChanged() {
|
|||
}
|
||||
|
||||
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
|
||||
m_audio.sampleRate = rate;
|
||||
if (m_audio.core) {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
mSDLInitAudio(&m_audio, input());
|
||||
if (m_audio.sampleRate != rate) {
|
||||
m_audio.sampleRate = rate;
|
||||
if (m_audio.core) {
|
||||
mSDLDeinitAudio(&m_audio);
|
||||
mSDLInitAudio(&m_audio, input());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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() {
|
||||
m_lines.append(QString());
|
||||
m_cond.wakeOne();
|
||||
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;
|
||||
self->m_lines.append(QString());
|
||||
self->m_cond.wakeOne();
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
shutdownInternal();
|
||||
m_gameController->setDebugger(nullptr);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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,7 +121,9 @@ void GBAApp::pauseAll(QList<Window*>* paused) {
|
|||
|
||||
void GBAApp::continueAll(const QList<Window*>& paused) {
|
||||
for (auto& window : paused) {
|
||||
window->controller()->setPaused(false);
|
||||
if (window->controller()) {
|
||||
window->controller()->setPaused(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(¶ms)) {
|
||||
|
@ -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(¶ms)) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
m_ui.stackedWidget->addWidget(m_shader);
|
||||
m_ui.tabs->addItem(tr("Shaders"));
|
||||
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved);
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -83,6 +83,9 @@ LibraryController::~LibraryController() {
|
|||
}
|
||||
|
||||
void LibraryController::setViewStyle(LibraryStyle newStyle) {
|
||||
if (m_currentStyle == newStyle) {
|
||||
return;
|
||||
}
|
||||
m_currentStyle = newStyle;
|
||||
|
||||
AbstractGameList* newCurrentList = nullptr;
|
||||
|
|
Loading…
Reference in New Issue