Merge branch 'master' into feature/input-revamp

This commit is contained in:
Vicki Pfau 2017-07-23 16:06:48 -07:00
commit 5122c399f5
100 changed files with 2685 additions and 2255 deletions

View File

@ -3,15 +3,22 @@ if [ $TRAVIS_OS_NAME = "osx" ]; then
brew update brew update
brew install qt5 ffmpeg imagemagick sdl2 libzip libpng brew install qt5 ffmpeg imagemagick sdl2 libzip libpng
if [ "$CC" == "gcc" ]; then if [ "$CC" == "gcc" ]; then
brew install gcc@4.9 brew install gcc@5
export CC=gcc-4.9 export CC=gcc-5
export CXX=g++-4.9 export CXX=g++-5
fi fi
else else
sudo apt-get clean 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 update
sudo apt-get install -y -q cmake libedit-dev libmagickwand-dev \ sudo apt-get install -y -q cmake libedit-dev libmagickwand-dev \
libpng-dev libsdl2-dev libzip-dev qtbase5-dev \ libpng-dev libsdl2-dev libzip-dev qtbase5-dev \
libqt5opengl5-dev qtmultimedia5-dev libavcodec-dev \ libqt5opengl5-dev qtmultimedia5-dev libavcodec-dev \
libavutil-dev libavformat-dev libavresample-dev libswscale-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 fi

View File

@ -2,9 +2,6 @@ language: c
sudo: required sudo: required
matrix: matrix:
include: include:
- os: linux
dist: trusty
compiler: clang
- os: linux - os: linux
dist: trusty dist: trusty
compiler: gcc compiler: gcc
@ -16,4 +13,4 @@ matrix:
before_install: before_install:
- source ./.travis-deps.sh - 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

11
CHANGES
View File

@ -1,12 +1,19 @@
0.7.0: (Future) 0.7.0: (Future)
Features:
- ELF support
Bugfixes: Bugfixes:
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
- Python: Fix importing .gb or .gba before .core
- GBA: Reset active region as needed when loading a ROM
- Qt: Fix command line debugger closing second game
Misc: Misc:
- GBA Timer: Use global cycles for timers - GBA Timer: Use global cycles for timers
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) - 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 - 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: (Future) 0.6.0: (2017-07-16)
Features: Features:
- Library view - Library view
- Sprite viewer - Sprite viewer
@ -181,6 +188,8 @@ Bugfixes:
- Core: Fix rewinding getting out of sync (fixes mgba.io/i/791) - Core: Fix rewinding getting out of sync (fixes mgba.io/i/791)
- Qt: Fix GL-less build - Qt: Fix GL-less build
- Qt: Fix Software renderer not handling alpha bits properly - Qt: Fix Software renderer not handling alpha bits properly
- Qt: Fix screen background improperly stretching
- SDL: Fix cheats not loading
Misc: Misc:
- GB Serialize: Add MBC state serialization - GB Serialize: Add MBC state serialization
- GBA Memory: Call crash callbacks regardless of if hard crash is enabled - GBA Memory: Call crash callbacks regardless of if hard crash is enabled

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8.11) cmake_minimum_required(VERSION 3.1)
project(mGBA) project(mGBA)
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries") set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
if(NOT MSVC) if(NOT MSVC)
@ -16,6 +16,7 @@ set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support")
set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support") set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support")
set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support") set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support")
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support") set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core") set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core") set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support") set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
@ -256,7 +257,7 @@ if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII)
set(USE_SQLITE3 OFF) set(USE_SQLITE3 OFF)
endif() endif()
if(DEFINED 3DS OR DEFINED WII) if(DEFINED 3DS)
add_definitions(-DFIXED_ROM_BUFFER) add_definitions(-DFIXED_ROM_BUFFER)
endif() endif()
@ -397,6 +398,7 @@ find_feature(USE_MAGICK "MagickWand")
find_feature(USE_EPOXY "epoxy") find_feature(USE_EPOXY "epoxy")
find_feature(USE_CMOCKA "cmocka") find_feature(USE_CMOCKA "cmocka")
find_feature(USE_SQLITE3 "sqlite3") find_feature(USE_SQLITE3 "sqlite3")
find_feature(USE_ELF "libelf")
find_feature(ENABLE_PYTHON "PythonLibs") find_feature(ENABLE_PYTHON "PythonLibs")
# Features # Features
@ -602,6 +604,17 @@ if(USE_SQLITE3)
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c") list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c")
endif() 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} NO_CMAKE_FIND_ROOT_PATH)
if (ELF_REPL_H)
add_definitions(-DUSE_ELF_REPL)
endif()
list(APPEND DEPENDENCY_LIB ${LIBELF_LIBRARIES})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libelfg0")
endif()
if(ENABLE_SCRIPTING) if(ENABLE_SCRIPTING)
list(APPEND ENABLES SCRIPTING) list(APPEND ENABLES SCRIPTING)
@ -935,6 +948,7 @@ if(NOT QUIET)
message(STATUS " ZIP support: ${SUMMARY_ZIP}") message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}") message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " SQLite3 game database: ${USE_SQLITE3}") message(STATUS " SQLite3 game database: ${USE_SQLITE3}")
message(STATUS " ELF loading support: ${USE_ELF}")
message(STATUS " OpenGL support: ${SUMMARY_GL}") message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:") message(STATUS "Frontends:")
message(STATUS " Qt: ${BUILD_QT}") message(STATUS " Qt: ${BUILD_QT}")

View File

@ -142,6 +142,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are
- libzip or zlib: for loading ROMs stored in zip files. - libzip or zlib: for loading ROMs stored in zip files.
- ImageMagick: for GIF recording. - ImageMagick: for GIF recording.
- SQLite3: for game databases. - SQLite3: for game databases.
- libelf: for ELF loading.
SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first. SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.

View File

@ -0,0 +1,51 @@
/* 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 ELF_READ_H
#define ELF_READ_H
#include <mgba-util/common.h>
CXX_GUARD_START
#ifdef USE_ELF
#include <libelf.h>
#if USE_ELF_REPL
#include <elf_repl.h>
#else
#include <elf.h>
#endif
#include <mgba-util/vector.h>
struct ELF;
struct VFile;
DECLARE_VECTOR(ELFProgramHeaders, Elf32_Phdr);
DECLARE_VECTOR(ELFSectionHeaders, Elf32_Shdr);
struct ELF* ELFOpen(struct VFile*);
void ELFClose(struct ELF*);
void* ELFBytes(struct ELF*, size_t* size);
uint16_t ELFMachine(struct ELF*);
uint32_t ELFEntry(struct ELF*);
void ELFGetProgramHeaders(struct ELF*, struct ELFProgramHeaders*);
size_t ELFFindSection(struct ELF*, const char* name);
void ELFGetSectionHeaders(struct ELF*, struct ELFSectionHeaders*);
Elf32_Shdr* ELFGetSectionHeader(struct ELF*, size_t index);
const char* ELFGetString(struct ELF*, size_t section, size_t string);
#endif
CXX_GUARD_END
#endif

View File

@ -190,6 +190,14 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size);
#ifdef USE_ELF
struct ELF;
bool mCoreLoadELF(struct mCore* core, struct ELF* elf);
void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF*);
#endif
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -24,6 +24,7 @@ struct mScriptEngine {
bool (*isScript)(struct mScriptEngine*, const char* name, struct VFile* vf); bool (*isScript)(struct mScriptEngine*, const char* name, struct VFile* vf);
bool (*loadScript)(struct mScriptEngine*, const char* name, struct VFile* vf); bool (*loadScript)(struct mScriptEngine*, const char* name, struct VFile* vf);
void (*run)(struct mScriptEngine*); void (*run)(struct mScriptEngine*);
bool (*lookupSymbol)(struct mScriptEngine*, const char* name, int32_t* out);
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
void (*debuggerEntered)(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); void (*debuggerEntered)(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
@ -44,6 +45,8 @@ void mScriptBridgeDebuggerEntered(struct mScriptBridge*, enum mDebuggerEntryReas
void mScriptBridgeRun(struct mScriptBridge*); void mScriptBridgeRun(struct mScriptBridge*);
bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name); bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name);
bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out);
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

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

View File

@ -91,6 +91,7 @@ struct mDebugger {
struct mCPUComponent d; struct mCPUComponent d;
struct mDebuggerPlatform* platform; struct mDebuggerPlatform* platform;
enum mDebuggerState state; enum mDebuggerState state;
enum mDebuggerType type;
struct mCore* core; struct mCore* core;
struct mScriptBridge* bridge; struct mScriptBridge* bridge;

View File

@ -168,6 +168,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf);
void GBAApplyPatch(struct GBA* gba, struct Patch* patch); void GBAApplyPatch(struct GBA* gba, struct Patch* patch);
bool GBALoadMB(struct GBA* gba, struct VFile* vf); bool GBALoadMB(struct GBA* gba, struct VFile* vf);
bool GBALoadNull(struct GBA* gba);
bool GBAIsROM(struct VFile* vf); bool GBAIsROM(struct VFile* vf);
bool GBAIsMB(struct VFile* vf); bool GBAIsMB(struct VFile* vf);

View File

@ -8,6 +8,11 @@
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/core/serialize.h> #include <mgba/core/serialize.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
#include <mgba/internal/debugger/symbols.h>
#ifdef USE_ELF
#include <mgba-util/elf-read.h>
#endif
#ifdef M_CORE_GB #ifdef M_CORE_GB
#include <mgba/gb/core.h> #include <mgba/gb/core.h>
@ -273,3 +278,67 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
core->rtc.custom = rtc; core->rtc.custom = rtc;
core->rtc.override = RTC_CUSTOM_START; core->rtc.override = RTC_CUSTOM_START;
} }
void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) {
const struct mCoreMemoryBlock* blocks;
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
size_t i;
for (i = 0; i < nBlocks; ++i) {
if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
continue;
}
if (start < blocks[i].start) {
continue;
}
if (start >= blocks[i].start + blocks[i].size) {
continue;
}
uint8_t* out = core->getMemoryBlock(core, blocks[i].id, size);
out += start - blocks[i].start;
*size -= start - blocks[i].start;
return out;
}
return NULL;
}
#ifdef USE_ELF
bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
struct ELFProgramHeaders ph;
ELFProgramHeadersInit(&ph, 0);
ELFGetProgramHeaders(elf, &ph);
size_t i;
for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) {
size_t bsize, esize;
Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i);
void* block = mCoreGetMemoryBlock(core, phdr->p_paddr, &bsize);
char* bytes = ELFBytes(elf, &esize);
if (block && bsize >= phdr->p_filesz && esize >= phdr->p_filesz + phdr->p_offset) {
memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz);
} else {
return false;
}
}
return true;
}
void mCoreLoadELFSymbols(struct mDebuggerSymbols* symbols, struct ELF* elf) {
size_t symIndex = ELFFindSection(elf, ".symtab");
size_t names = ELFFindSection(elf, ".strtab");
Elf32_Shdr* symHeader = ELFGetSectionHeader(elf, symIndex);
char* bytes = ELFBytes(elf, NULL);
Elf32_Sym* syms = (Elf32_Sym*) &bytes[symHeader->sh_offset];
size_t i;
for (i = 0; i * sizeof(*syms) < symHeader->sh_size; ++i) {
if (!syms[i].st_name || ELF32_ST_TYPE(syms[i].st_info) == STT_FILE) {
continue;
}
const char* name = ELFGetString(elf, names, syms[i].st_name);
if (name[0] == '$') {
continue;
}
mDebuggerSymbolAdd(symbols, name, syms[i].st_value, -1);
}
}
#endif

View File

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

View File

@ -19,6 +19,12 @@ struct mScriptInfo {
bool success; bool success;
}; };
struct mScriptSymbol {
const char* name;
int32_t* out;
bool success;
};
static void _seDeinit(void* value) { static void _seDeinit(void* value) {
struct mScriptEngine* se = value; struct mScriptEngine* se = value;
se->deinit(se); se->deinit(se);
@ -33,6 +39,15 @@ static void _seTryLoad(const char* key, void* value, void* user) {
} }
} }
static void _seLookupSymbol(const char* key, void* value, void* user) {
UNUSED(key);
struct mScriptEngine* se = value;
struct mScriptSymbol* si = user;
if (!si->success) {
si->success = se->lookupSymbol(se, si->name, si->out);
}
}
static void _seRun(const char* key, void* value, void* user) { static void _seRun(const char* key, void* value, void* user) {
UNUSED(key); UNUSED(key);
UNUSED(user); UNUSED(user);
@ -111,3 +126,13 @@ bool mScriptBridgeLoadScript(struct mScriptBridge* sb, const char* name) {
vf->close(vf); vf->close(vf);
return info.success; return info.success;
} }
bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32_t* out) {
struct mScriptSymbol info = {
.name = name,
.out = out,
.success = false
};
HashTableEnumerate(&sb->engines, _seLookupSymbol, &info);
return info.success;
}

View File

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

View File

@ -551,6 +551,11 @@ static void _lookupIdentifier(struct mDebugger* debugger, const char* name, stru
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
if (cliDebugger->system) { if (cliDebugger->system) {
uint32_t value; uint32_t value;
#ifdef ENABLE_SCRIPTING
if (debugger->bridge && mScriptBridgeLookupSymbol(debugger->bridge, name, &dv->intValue)) {
return;
}
#endif
if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) { if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) {
return; return;
} }
@ -832,6 +837,7 @@ void CLIDebuggerCreate(struct CLIDebugger* debugger) {
debugger->d.custom = _cliDebuggerCustom; debugger->d.custom = _cliDebuggerCustom;
debugger->d.paused = _commandLine; debugger->d.paused = _commandLine;
debugger->d.entered = _reportEntry; debugger->d.entered = _reportEntry;
debugger->d.type = DEBUGGER_CLI;
debugger->system = NULL; debugger->system = NULL;
debugger->backend = NULL; debugger->backend = NULL;

View File

@ -658,6 +658,7 @@ void GDBStubCreate(struct GDBStub* stub) {
stub->d.paused = _gdbStubWait; stub->d.paused = _gdbStubWait;
stub->d.entered = _gdbStubEntered; stub->d.entered = _gdbStubEntered;
stub->d.custom = _gdbStubPoll; stub->d.custom = _gdbStubPoll;
stub->d.type = DEBUGGER_GDB;
stub->untilPoll = GDB_STUB_INTERVAL; stub->untilPoll = GDB_STUB_INTERVAL;
stub->lineAck = GDB_ACK_PENDING; stub->lineAck = GDB_ACK_PENDING;
stub->shouldBlock = false; stub->shouldBlock = false;

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/internal/arm/debugger/debugger.h> #include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
@ -20,6 +21,9 @@
#include <mgba/internal/gba/renderers/video-software.h> #include <mgba/internal/gba/renderers/video-software.h>
#include <mgba/internal/gba/savedata.h> #include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/serialize.h> #include <mgba/internal/gba/serialize.h>
#ifdef USE_ELF
#include <mgba-util/elf-read.h>
#endif
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -315,6 +319,15 @@ static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
} }
static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
#ifdef USE_ELF
struct ELF* elf = ELFOpen(vf);
if (elf) {
GBALoadNull(core->board);
bool success = mCoreLoadELF(core, elf);
ELFClose(elf);
return success;
}
#endif
if (GBAIsMB(vf)) { if (GBAIsMB(vf)) {
return GBALoadMB(core->board, vf); return GBALoadMB(core->board, vf);
} }
@ -704,7 +717,27 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
} }
static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
// TODO #ifdef USE_ELF
bool closeAfter = false;
core->symbolTable = mDebuggerSymbolTableCreate();
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
if (!vf) {
closeAfter = true;
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".elf", O_RDONLY);
}
#endif
if (!vf) {
return;
}
struct ELF* elf = ELFOpen(vf);
if (elf) {
mCoreLoadELFSymbols(core->symbolTable, elf);
ELFClose(elf);
}
if (closeAfter) {
vf->close(vf);
}
#endif
} }
#endif #endif

View File

@ -21,6 +21,10 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
#ifdef USE_ELF
#include <mgba-util/elf-read.h>
#endif
mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba"); mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba");
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug"); mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
@ -203,6 +207,10 @@ void GBAReset(struct ARMCore* cpu) {
gba->debug = false; gba->debug = false;
memset(gba->debugString, 0, sizeof(gba->debugString)); memset(gba->debugString, 0, sizeof(gba->debugString));
if (!gba->romVf && gba->memory.rom) {
GBASkipBIOS(gba);
}
} }
void GBASkipBIOS(struct GBA* gba) { void GBASkipBIOS(struct GBA* gba) {
@ -288,6 +296,29 @@ void GBADetachDebugger(struct GBA* gba) {
} }
#endif #endif
bool GBALoadNull(struct GBA* gba) {
GBAUnloadROM(gba);
gba->romVf = NULL;
gba->pristineRomSize = 0;
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
#ifndef FIXED_ROM_BUFFER
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
#else
gba->memory.rom = romBuffer;
#endif
gba->isPristine = false;
gba->yankedRomSize = 0;
gba->memory.romSize = SIZE_CART0;
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;
}
bool GBALoadMB(struct GBA* gba, struct VFile* vf) { bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
GBAUnloadROM(gba); GBAUnloadROM(gba);
gba->romVf = vf; gba->romVf = vf;
@ -308,6 +339,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = 0; gba->memory.romSize = 0;
gba->memory.romMask = 0; gba->memory.romMask = 0;
gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize); 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; return true;
} }
@ -352,6 +386,9 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = SIZE_CART0; gba->memory.romSize = SIZE_CART0;
gba->isPristine = false; 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 // TODO: error check
return true; return true;
} }
@ -479,6 +516,17 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
} }
bool GBAIsROM(struct VFile* vf) { bool GBAIsROM(struct VFile* vf) {
#ifdef USE_ELF
struct ELF* elf = ELFOpen(vf);
if (elf) {
uint32_t entry = ELFEntry(elf);
bool isGBA = true;
isGBA = isGBA && ELFMachine(elf) == EM_ARM;
isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM);
ELFClose(elf);
return isGBA;
}
#endif
if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
return false; return false;
} }
@ -496,6 +544,14 @@ bool GBAIsMB(struct VFile* vf) {
if (!GBAIsROM(vf)) { if (!GBAIsROM(vf)) {
return false; return false;
} }
#ifdef USE_ELF
struct ELF* elf = ELFOpen(vf);
if (elf) {
bool isMB = ELFEntry(elf) == BASE_WORKING_RAM;
ELFClose(elf);
return isMB;
}
#endif
if (vf->size(vf) > SIZE_WORKING_RAM) { if (vf->size(vf) > SIZE_WORKING_RAM) {
return false; return false;
} }

View File

@ -80,6 +80,8 @@ void GBAMemoryInit(struct GBA* gba) {
gba->memory.biosPrefetch = 0; gba->memory.biosPrefetch = 0;
gba->memory.mirroring = false; gba->memory.mirroring = false;
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
GBADMAInit(gba); GBADMAInit(gba);
GBAVFameInit(&gba->memory.vfame); GBAVFameInit(&gba->memory.vfame);
} }
@ -107,9 +109,8 @@ void GBAMemoryReset(struct GBA* gba) {
} }
if (gba->memory.iwram) { if (gba->memory.iwram) {
mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM); memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM);
} }
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
memset(gba->memory.io, 0, sizeof(gba->memory.io)); memset(gba->memory.io, 0, sizeof(gba->memory.io));

View File

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

View File

@ -58,4 +58,5 @@ void free(void*);
#endif #endif
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
#include <mgba/debugger/debugger.h> #include <mgba/debugger/debugger.h>
#include <mgba/internal/debugger/cli-debugger.h>
#endif #endif

View File

@ -28,6 +28,7 @@ ffi.set_source("mgba._pylib", """
#include <mgba/core/version.h> #include <mgba/core/version.h>
#include <mgba/debugger/debugger.h> #include <mgba/debugger/debugger.h>
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/debugger/cli-debugger.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#include <mgba/internal/gba/renderers/tile-cache.h> #include <mgba/internal/gba/renderers/tile-cache.h>
@ -70,17 +71,27 @@ for line in preprocessed.splitlines():
ffi.embedding_api('\n'.join(lines)) ffi.embedding_api('\n'.join(lines))
ffi.embedding_init_code(""" ffi.embedding_init_code("""
from mgba._pylib import ffi from mgba._pylib import ffi, lib
debugger = None symbols = {}
globalSyms = {
'symbols': symbols
}
pendingCode = [] pendingCode = []
@ffi.def_extern() @ffi.def_extern()
def mPythonSetDebugger(_debugger): def mPythonSetDebugger(debugger):
from mgba.debugger import NativeDebugger from mgba.debugger import NativeDebugger, CLIDebugger
global debugger oldDebugger = globalSyms.get('debugger')
if debugger and debugger._native == _debugger: if oldDebugger and oldDebugger._native == debugger:
return return
debugger = _debugger and NativeDebugger(_debugger) if oldDebugger and not debugger:
del globalSyms['debugger']
return
if debugger.type == lib.DEBUGGER_CLI:
debugger = CLIDebugger(debugger)
else:
debugger = NativeDebugger(debugger)
globalSyms['debugger'] = debugger
@ffi.def_extern() @ffi.def_extern()
def mPythonLoadScript(name, vf): def mPythonLoadScript(name, vf):
@ -99,18 +110,40 @@ ffi.embedding_init_code("""
def mPythonRunPending(): def mPythonRunPending():
global pendingCode global pendingCode
for code in pendingCode: for code in pendingCode:
exec(code) exec(code, globalSyms, {})
pendingCode = [] pendingCode = []
@ffi.def_extern() @ffi.def_extern()
def mPythonDebuggerEntered(reason, info): def mPythonDebuggerEntered(reason, info):
global debugger debugger = globalSyms['debugger']
if not debugger: if not debugger:
return return
if info == ffi.NULL: if info == ffi.NULL:
info = None info = None
for cb in debugger._cbs: for cb in debugger._cbs:
cb(reason, info) cb(reason, info)
@ffi.def_extern()
def mPythonLookupSymbol(name, outptr):
name = ffi.string(name).decode('utf-8')
if name not in symbols:
return False
sym = symbols[name]
val = None
try:
val = int(sym)
except:
try:
val = sym()
except:
pass
if val is None:
return False
try:
outptr[0] = ffi.cast('int32_t', val)
return True
except:
return False
""") """)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -21,6 +21,7 @@ static void mPythonScriptEngineDeinit(struct mScriptEngine*);
static bool mPythonScriptEngineIsScript(struct mScriptEngine*, const char* name, struct VFile* vf); static bool mPythonScriptEngineIsScript(struct mScriptEngine*, const char* name, struct VFile* vf);
static bool mPythonScriptEngineLoadScript(struct mScriptEngine*, const char* name, struct VFile* vf); static bool mPythonScriptEngineLoadScript(struct mScriptEngine*, const char* name, struct VFile* vf);
static void mPythonScriptEngineRun(struct mScriptEngine*); static void mPythonScriptEngineRun(struct mScriptEngine*);
static bool mPythonScriptEngineLookupSymbol(struct mScriptEngine*, const char* name, int32_t* out);
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
static void mPythonScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); static void mPythonScriptDebuggerEntered(struct mScriptEngine*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
@ -39,6 +40,7 @@ struct mPythonScriptEngine* mPythonCreateScriptEngine(void) {
engine->d.isScript = mPythonScriptEngineIsScript; engine->d.isScript = mPythonScriptEngineIsScript;
engine->d.loadScript = mPythonScriptEngineLoadScript; engine->d.loadScript = mPythonScriptEngineLoadScript;
engine->d.run = mPythonScriptEngineRun; engine->d.run = mPythonScriptEngineRun;
engine->d.lookupSymbol = mPythonScriptEngineLookupSymbol;
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
engine->d.debuggerEntered = mPythonScriptDebuggerEntered; engine->d.debuggerEntered = mPythonScriptDebuggerEntered;
#endif #endif
@ -89,6 +91,11 @@ void mPythonScriptEngineRun(struct mScriptEngine* se) {
mPythonRunPending(); mPythonRunPending();
} }
bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) {
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
return mPythonLookupSymbol(name, out);
}
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
void mPythonScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { void mPythonScriptDebuggerEntered(struct mScriptEngine* se, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se; struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;

View File

@ -4,6 +4,7 @@ struct VFile;
extern bool mPythonLoadScript(const char*, struct VFile*); extern bool mPythonLoadScript(const char*, struct VFile*);
extern void mPythonRunPending(); extern void mPythonRunPending();
extern bool mPythonLookupSymbol(const char* name, int32_t* out);
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
extern void mPythonSetDebugger(struct mDebugger*); extern void mPythonSetDebugger(struct mDebugger*);

View File

@ -91,6 +91,12 @@ class CoreCallbacks(object):
cb() cb()
class Core(object): class Core(object):
if hasattr(lib, 'PLATFORM_GBA'):
PLATFORM_GBA = lib.PLATFORM_GBA
if hasattr(lib, 'PLATFORM_GB'):
PLATFORM_GB = lib.PLATFORM_GB
def __init__(self, native): def __init__(self, native):
self._core = native self._core = native
self._wasReset = False self._wasReset = False
@ -117,8 +123,10 @@ class Core(object):
@classmethod @classmethod
def _detect(cls, core): def _detect(cls, core):
if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA: if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA:
from .gba import GBA
return GBA(core) return GBA(core)
if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB: if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB:
from .gb import GB
return GB(core) return GB(core)
return Core(core) return Core(core)
@ -253,12 +261,3 @@ class IRunner(object):
def isPaused(self): def isPaused(self):
raise NotImplementedError raise NotImplementedError
if hasattr(lib, 'PLATFORM_GBA'):
from .gba import GBA
Core.PLATFORM_GBA = lib.PLATFORM_GBA
if hasattr(lib, 'PLATFORM_GB'):
from .gb import GB
from .lr35902 import LR35902Core
Core.PLATFORM_GB = lib.PLATFORM_GB

View File

@ -5,6 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib from ._pylib import ffi, lib
from .core import IRunner, ICoreOwner, Core from .core import IRunner, ICoreOwner, Core
import io
import sys
class DebuggerCoreOwner(ICoreOwner): class DebuggerCoreOwner(ICoreOwner):
def __init__(self, debugger): def __init__(self, debugger):
@ -78,3 +80,22 @@ class NativeDebugger(IRunner):
def addCallback(self, cb): def addCallback(self, cb):
self._cbs.append(cb) self._cbs.append(cb)
class CLIBackend(object):
def __init__(self, backend):
self.backend = backend
def write(self, string):
self.backend.printf(string)
class CLIDebugger(NativeDebugger):
def __init__(self, native):
super(CLIDebugger, self).__init__(native)
self._cli = ffi.cast("struct CLIDebugger*", native)
def printf(self, message, *args, **kwargs):
message = message.format(*args, **kwargs)
self._cli.backend.printf(ffi.new("char []", b"%s"), ffi.new("char []", message.encode('utf-8')))
def installPrint(self):
sys.stdout = CLIBackend(self)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,8 @@ public:
virtual unsigned sampleRate() const override; virtual unsigned sampleRate() const override;
public slots: 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 bool start() override;
virtual void pause() override; virtual void pause() override;
@ -33,7 +34,7 @@ public slots:
private: private:
QAudioOutput* m_audioOutput = nullptr; QAudioOutput* m_audioOutput = nullptr;
AudioDevice* m_device = nullptr; std::unique_ptr<AudioDevice> m_device;
unsigned m_sampleRate = 44100; unsigned m_sampleRate = 44100;
}; };

View File

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

View File

@ -18,12 +18,12 @@ Q_OBJECT
public: public:
AudioProcessorSDL(QObject* parent = nullptr); AudioProcessorSDL(QObject* parent = nullptr);
~AudioProcessorSDL();
virtual unsigned sampleRate() const override; virtual unsigned sampleRate() const override;
public slots: 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 bool start() override;
virtual void pause() override; virtual void pause() override;

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,694 @@
/* 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);
}
void CoreController::loadConfig(ConfigController* config) {
Interrupter interrupter(this);
m_loadStateFlags = config->getOption("loadStateExtdata").toInt();
m_saveStateFlags = config->getOption("saveStateExtdata").toInt();
m_fastForwardRatio = config->getOption("fastForwardRatio").toFloat();
m_videoSync = config->getOption("videoSync").toInt();
m_audioSync = config->getOption("audioSync").toInt();
m_fpsTarget = config->getOption("fpsTarget").toFloat();
updateFastForward();
mCoreLoadForeignConfig(m_threadContext.core, config->config());
mCoreThreadRewindParamsChanged(&m_threadContext);
}
#ifdef USE_DEBUGGERS
void CoreController::setDebugger(mDebugger* debugger) {
Interrupter interrupter(this);
if (debugger) {
mDebuggerAttach(debugger, m_threadContext.core);
} else {
m_threadContext.core->detachDebugger(m_threadContext.core);
}
}
#endif
void CoreController::setMultiplayerController(MultiplayerController* controller) {
if (controller == m_multiplayer) {
return;
}
clearMultiplayerController();
m_multiplayer = controller;
if (!mCoreThreadHasStarted(&m_threadContext)) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* thread) {
CoreController* controller = static_cast<CoreController*>(thread->userData);
controller->m_multiplayer->attachGame(controller);
});
}
void CoreController::clearMultiplayerController() {
if (!m_multiplayer) {
return;
}
m_multiplayer->detachGame(this);
m_multiplayer = nullptr;
}
mTileCache* CoreController::tileCache() {
if (m_tileCache) {
return m_tileCache.get();
}
Interrupter interrupter(this);
switch (platform()) {
#ifdef M_CORE_GBA
case PLATFORM_GBA: {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
m_tileCache = std::make_unique<mTileCache>();
GBAVideoTileCacheInit(m_tileCache.get());
GBAVideoTileCacheAssociate(m_tileCache.get(), &gba->video);
mTileCacheSetPalette(m_tileCache.get(), 0);
break;
}
#endif
#ifdef M_CORE_GB
case PLATFORM_GB: {
GB* gb = static_cast<GB*>(m_threadContext.core->board);
m_tileCache = std::make_unique<mTileCache>();
GBVideoTileCacheInit(m_tileCache.get());
GBVideoTileCacheAssociate(m_tileCache.get(), &gb->video);
mTileCacheSetPalette(m_tileCache.get(), 0);
break;
}
#endif
default:
return nullptr;
}
return m_tileCache.get();
}
void CoreController::setOverride(std::unique_ptr<Override> override) {
Interrupter interrupter(this);
m_override = std::move(override);
m_override->identify(m_threadContext.core);
}
void CoreController::setInputController(InputController* inputController) {
m_inputController = inputController;
m_inputController->setPlatform(platform());
}
void CoreController::setLogger(LogController* logger) {
disconnect(m_log);
m_log = logger;
m_threadContext.logger.d.filter = logger->filter();
connect(this, &CoreController::logPosted, m_log, &LogController::postLog);
}
void CoreController::start() {
if (!m_patched) {
mCoreAutoloadPatch(m_threadContext.core);
}
if (!mCoreThreadStart(&m_threadContext)) {
emit failed();
emit stopping();
}
}
void CoreController::stop() {
#ifdef USE_DEBUGGERS
setDebugger(nullptr);
#endif
setPaused(false);
mCoreThreadEnd(&m_threadContext);
emit stopping();
}
void CoreController::reset() {
bool wasPaused = isPaused();
setPaused(false);
Interrupter interrupter(this);
mCoreThreadReset(&m_threadContext);
if (wasPaused) {
setPaused(true);
}
}
void CoreController::setPaused(bool paused) {
if (paused == isPaused()) {
return;
}
if (paused) {
QMutexLocker locker(&m_mutex);
m_frameActions.append([this]() {
mCoreThreadPauseFromThread(&m_threadContext);
QMetaObject::invokeMethod(this, "paused");
});
} else {
mCoreThreadUnpause(&m_threadContext);
emit unpaused();
}
}
void CoreController::frameAdvance() {
QMutexLocker locker(&m_mutex);
m_frameActions.append([this]() {
mCoreThreadPauseFromThread(&m_threadContext);
});
setPaused(false);
}
void CoreController::setSync(bool sync) {
if (sync) {
m_threadContext.impl->sync.audioWait = m_audioSync;
m_threadContext.impl->sync.videoFrameWait = m_videoSync;
} else {
m_threadContext.impl->sync.audioWait = false;
m_threadContext.impl->sync.videoFrameWait = false;
}
}
void CoreController::setRewinding(bool rewind) {
if (!m_threadContext.core->opts.rewindEnable) {
return;
}
if (rewind && m_multiplayer && m_multiplayer->attached() > 1) {
return;
}
if (rewind && isPaused()) {
setPaused(false);
// TODO: restore autopausing
}
mCoreThreadSetRewinding(&m_threadContext, rewind);
}
void CoreController::rewind(int states) {
{
Interrupter interrupter(this);
if (!states) {
states = INT_MAX;
}
for (int i = 0; i < states; ++i) {
if (!mCoreRewindRestore(&m_threadContext.impl->rewind, m_threadContext.core)) {
break;
}
}
}
emit frameAvailable();
emit rewound();
}
void CoreController::setFastForward(bool enable) {
m_fastForward = enable;
updateFastForward();
}
void CoreController::forceFastForward(bool enable) {
m_fastForwardForced = enable;
updateFastForward();
}
void CoreController::loadState(int slot) {
if (slot > 0 && slot != m_stateSlot) {
m_stateSlot = slot;
m_backupSaveState.clear();
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
if (!controller->m_backupLoadState.isOpen()) {
controller->m_backupLoadState = VFileMemChunk(nullptr, 0);
}
mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_saveStateFlags);
if (mCoreLoadState(context->core, controller->m_stateSlot, controller->m_loadStateFlags)) {
emit controller->frameAvailable();
emit controller->stateLoaded();
}
});
}
void CoreController::saveState(int slot) {
if (slot > 0) {
m_stateSlot = slot;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, false);
if (vf) {
controller->m_backupSaveState.resize(vf->size(vf));
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
vf->close(vf);
}
mCoreSaveState(context->core, controller->m_stateSlot, controller->m_saveStateFlags);
});
}
void CoreController::loadBackupState() {
if (!m_backupLoadState.isOpen()) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
controller->m_backupLoadState.seek(0);
if (mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_loadStateFlags)) {
mLOG(STATUS, INFO, "Undid state load");
controller->frameAvailable();
controller->stateLoaded();
}
controller->m_backupLoadState.close();
});
}
void CoreController::saveBackupState() {
if (m_backupSaveState.isEmpty()) {
return;
}
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
CoreController* controller = static_cast<CoreController*>(context->userData);
VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, true);
if (vf) {
vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
vf->close(vf);
mLOG(STATUS, INFO, "Undid state save");
}
controller->m_backupSaveState.clear();
});
}
void CoreController::loadSave(const QString& path, bool temporary) {
m_resetActions.append([this, path, temporary]() {
VFile* vf = VFileDevice::open(path, temporary ? O_RDONLY : O_RDWR);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open save file: %1").arg(path);
return;
}
if (temporary) {
m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
} else {
m_threadContext.core->loadSave(m_threadContext.core, vf);
}
});
reset();
}
void CoreController::loadPatch(const QString& patchPath) {
Interrupter interrupter(this);
VFile* patch = VFileDevice::open(patchPath, O_RDONLY);
if (patch) {
m_threadContext.core->loadPatch(m_threadContext.core, patch);
m_patched = true;
}
patch->close(patch);
if (mCoreThreadHasStarted(&m_threadContext)) {
reset();
}
}
void CoreController::replaceGame(const QString& path) {
QFileInfo info(path);
if (!info.isReadable()) {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
return;
}
QString fname = info.canonicalFilePath();
Interrupter interrupter(this);
mDirectorySetDetachBase(&m_threadContext.core->dirs);
mCoreLoadFile(m_threadContext.core, fname.toLocal8Bit().constData());
}
void CoreController::yankPak() {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
Interrupter interrupter(this);
GBAYankROM(static_cast<GBA*>(m_threadContext.core->board));
#endif
}
#ifdef USE_PNG
void CoreController::screenshot() {
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
mCoreTakeScreenshot(context->core);
});
}
#endif
void CoreController::setRealTime() {
m_threadContext.core->rtc.override = RTC_NO_OVERRIDE;
}
void CoreController::setFixedTime(const QDateTime& time) {
m_threadContext.core->rtc.override = RTC_FIXED;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
}
void CoreController::setFakeEpoch(const QDateTime& time) {
m_threadContext.core->rtc.override = RTC_FAKE_EPOCH;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
}
void CoreController::importSharkport(const QString& path) {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
VFile* vf = VFileDevice::open(path, O_RDONLY);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open snapshot file for reading: %1").arg(path);
return;
}
Interrupter interrupter(this);
GBASavedataImportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf, false);
vf->close(vf);
#endif
}
void CoreController::exportSharkport(const QString& path) {
#ifdef M_CORE_GBA
if (platform() != PLATFORM_GBA) {
return;
}
VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open snapshot file for writing: %1").arg(path);
return;
}
Interrupter interrupter(this);
GBASavedataExportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf);
vf->close(vf);
#endif
}
void CoreController::setAVStream(mAVStream* stream) {
Interrupter interrupter(this);
m_threadContext.core->setAVStream(m_threadContext.core, stream);
}
void CoreController::clearAVStream() {
Interrupter interrupter(this);
m_threadContext.core->setAVStream(m_threadContext.core, nullptr);
}
void CoreController::clearOverride() {
m_override.reset();
}
void CoreController::startVideoLog(const QString& path) {
if (m_vl) {
return;
}
Interrupter interrupter(this);
m_vl = mVideoLogContextCreate(m_threadContext.core);
m_vlVf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
mVideoLogContextSetOutput(m_vl, m_vlVf);
mVideoLogContextWriteHeader(m_vl, m_threadContext.core);
}
void CoreController::endVideoLog() {
if (!m_vl) {
return;
}
Interrupter interrupter(this);
mVideoLogContextDestroy(m_threadContext.core, m_vl);
if (m_vlVf) {
m_vlVf->close(m_vlVf);
m_vlVf = nullptr;
}
m_vl = nullptr;
}
void CoreController::updateKeys() {
int activeKeys = m_inputController->updateAutofire() | m_inputController->pollEvents();
m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
}
void CoreController::finishFrame() {
QMutexLocker locker(&m_mutex);
m_completeBuffer = m_activeBuffer;
// TODO: Generalize this to triple buffering?
m_activeBuffer = &m_buffers[0];
if (m_activeBuffer == m_completeBuffer) {
m_activeBuffer = &m_buffers[1];
}
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer->data()), screenDimensions().width());
for (auto& action : m_frameActions) {
action();
}
m_frameActions.clear();
updateKeys();
QMetaObject::invokeMethod(this, "frameAvailable");
}
void CoreController::updateFastForward() {
if (m_fastForward || m_fastForwardForced) {
if (m_fastForwardRatio > 0) {
m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_fastForwardRatio;
} else {
setSync(false);
}
} else {
m_threadContext.impl->sync.fpsTarget = m_fpsTarget;
setSync(true);
}
}
CoreController::Interrupter::Interrupter(CoreController* parent, bool fromThread)
: m_parent(parent)
{
if (!m_parent->thread()->impl) {
return;
}
if (!fromThread) {
mCoreThreadInterrupt(m_parent->thread());
} else {
mCoreThreadInterruptFromThread(m_parent->thread());
}
}
CoreController::Interrupter::Interrupter(std::shared_ptr<CoreController> parent, bool fromThread)
: m_parent(parent.get())
{
if (!m_parent->thread()->impl) {
return;
}
if (!fromThread) {
mCoreThreadInterrupt(m_parent->thread());
} else {
mCoreThreadInterruptFromThread(m_parent->thread());
}
}
CoreController::Interrupter::Interrupter(const Interrupter& other)
: m_parent(other.m_parent)
{
if (!m_parent->thread()->impl) {
return;
}
mCoreThreadInterrupt(m_parent->thread());
}
CoreController::Interrupter::~Interrupter() {
if (!m_parent->thread()->impl) {
return;
}
mCoreThreadContinue(m_parent->thread());
}

View File

@ -0,0 +1,193 @@
/* 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;
void loadConfig(ConfigController*);
mCheatDevice* cheatDevice() { return m_threadContext.core->cheatDevice(m_threadContext.core); }
#ifdef USE_DEBUGGERS
mDebugger* debugger() { return m_threadContext.core->debugger; }
void setDebugger(mDebugger*);
#endif
void setMultiplayerController(MultiplayerController*);
void clearMultiplayerController();
MultiplayerController* multiplayerController() { return m_multiplayer; }
mTileCache* tileCache();
int stateSlot() const { return m_stateSlot; }
void setOverride(std::unique_ptr<Override> override);
Override* override() { return m_override.get(); }
void setInputController(InputController*);
void setLogger(LogController*);
public slots:
void start();
void stop();
void reset();
void setPaused(bool paused);
void frameAdvance();
void setSync(bool enable);
void setRewinding(bool);
void rewind(int count = 0);
void setFastForward(bool);
void forceFastForward(bool);
void loadState(int slot = 0);
void saveState(int slot = 0);
void loadBackupState();
void saveBackupState();
void loadSave(const QString&, bool temporary);
void loadPatch(const QString&);
void replaceGame(const QString&);
void yankPak();
#ifdef USE_PNG
void screenshot();
#endif
void setRealTime();
void setFixedTime(const QDateTime& time);
void setFakeEpoch(const QDateTime& time);
void importSharkport(const QString& path);
void exportSharkport(const QString& path);
void setAVStream(mAVStream*);
void clearAVStream();
void clearOverride();
void startVideoLog(const QString& path);
void endVideoLog();
signals:
void started();
void paused();
void unpaused();
void stopping();
void crashed(const QString& errorMessage);
void failed();
void frameAvailable();
void stateLoaded();
void rewound();
void rewindChanged(bool);
void fastForwardChanged(bool);
void unimplementedBiosCall(int);
void statusPosted(const QString& message);
void logPosted(int level, int category, const QString& log);
private:
void updateKeys();
void finishFrame();
void updateFastForward();
mCoreThread m_threadContext{};
bool m_patched = false;
QByteArray m_buffers[2];
QByteArray* m_activeBuffer;
QByteArray* m_completeBuffer = nullptr;
std::unique_ptr<mTileCache> m_tileCache;
std::unique_ptr<Override> m_override;
QList<std::function<void()>> m_resetActions;
QList<std::function<void()>> m_frameActions;
QMutex m_mutex;
VFileDevice m_backupLoadState;
QByteArray m_backupSaveState{nullptr};
int m_stateSlot = 1;
int m_loadStateFlags;
int m_saveStateFlags;
bool m_audioSync = AUDIO_SYNC;
bool m_videoSync = VIDEO_SYNC;
int m_fastForward = false;
int m_fastForwardForced = false;
float m_fastForwardRatio = -1.f;
float m_fpsTarget;
InputController* m_inputController = nullptr;
LogController* m_log = nullptr;
MultiplayerController* m_multiplayer = nullptr;
mVideoLogContext* m_vl = nullptr;
VFile* m_vlVf = nullptr;
};
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -5,32 +5,44 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GDBController.h" #include "GDBController.h"
#include "GameController.h" #include "CoreController.h"
using namespace QGBA; using namespace QGBA;
DebuggerController::DebuggerController(GameController* controller, mDebugger* debugger, QObject* parent) DebuggerController::DebuggerController(mDebugger* debugger, QObject* parent)
: QObject(parent) : QObject(parent)
, m_debugger(debugger) , m_debugger(debugger)
, m_gameController(controller)
{ {
} }
bool DebuggerController::isAttached() { bool DebuggerController::isAttached() {
if (!m_gameController) {
return false;
}
return m_gameController->debugger() == m_debugger; 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() { void DebuggerController::attach() {
if (isAttached()) { if (isAttached()) {
return; return;
} }
if (m_gameController->isLoaded()) { if (m_gameController) {
attachInternal(); attachInternal();
m_gameController->setDebugger(m_debugger); m_gameController->setDebugger(m_debugger);
mDebuggerEnter(m_debugger, DEBUGGER_ENTER_ATTACHED, 0); 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()) { if (!isAttached()) {
return; return;
} }
GameController::Interrupter interrupter(m_gameController); if (m_gameController) {
shutdownInternal(); CoreController::Interrupter interrupter(m_gameController);
m_gameController->setDebugger(nullptr); shutdownInternal();
m_gameController->setDebugger(nullptr);
}
} }
void DebuggerController::breakInto() { void DebuggerController::breakInto() {
if (!isAttached()) { if (!isAttached()) {
return; return;
} }
GameController::Interrupter interrupter(m_gameController); CoreController::Interrupter interrupter(m_gameController);
mDebuggerEnter(m_debugger, DEBUGGER_ENTER_MANUAL, 0); mDebuggerEnter(m_debugger, DEBUGGER_ENTER_MANUAL, 0);
} }
@ -57,7 +71,7 @@ void DebuggerController::shutdown() {
if (!isAttached()) { if (!isAttached()) {
return; return;
} }
GameController::Interrupter interrupter(m_gameController); CoreController::Interrupter interrupter(m_gameController);
shutdownInternal(); shutdownInternal();
} }

View File

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

View File

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

View File

@ -7,12 +7,13 @@
#if defined(BUILD_GL) || defined(BUILD_GLES) #if defined(BUILD_GL) || defined(BUILD_GLES)
#include "CoreController.h"
#include <QApplication> #include <QApplication>
#include <QResizeEvent> #include <QResizeEvent>
#include <QTimer> #include <QTimer>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h>
#ifdef BUILD_GL #ifdef BUILD_GL
#include "platform/opengl/gl.h" #include "platform/opengl/gl.h"
#endif #endif
@ -52,14 +53,14 @@ VideoShader* DisplayGL::shaders() {
return shaders; return shaders;
} }
void DisplayGL::startDrawing(mCoreThread* thread) { void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
if (m_drawThread) { if (m_drawThread) {
return; return;
} }
m_isDrawing = true; m_isDrawing = true;
m_painter->setContext(thread); m_painter->setContext(controller);
m_painter->setMessagePainter(messagePainter()); m_painter->setMessagePainter(messagePainter());
m_context = thread; m_context = controller;
m_painter->resize(size()); m_painter->resize(size());
m_gl->move(0, 0); m_gl->move(0, 0);
m_drawThread = new QThread(this); m_drawThread = new QThread(this);
@ -69,7 +70,6 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
m_painter->moveToThread(m_drawThread); m_painter->moveToThread(m_drawThread);
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start); connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
m_drawThread->start(); m_drawThread->start();
mCoreSyncSetVideoSync(&m_context->impl->sync, false);
lockAspectRatio(isAspectRatioLocked()); lockAspectRatio(isAspectRatioLocked());
lockIntegerScaling(isIntegerScalingLocked()); lockIntegerScaling(isIntegerScalingLocked());
@ -85,41 +85,27 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
void DisplayGL::stopDrawing() { void DisplayGL::stopDrawing() {
if (m_drawThread) { if (m_drawThread) {
m_isDrawing = false; m_isDrawing = false;
if (mCoreThreadIsActive(m_context)) { CoreController::Interrupter interrupter(m_context);
mCoreThreadInterrupt(m_context);
}
QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
m_drawThread->exit(); m_drawThread->exit();
m_drawThread = nullptr; m_drawThread = nullptr;
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
} }
m_context.reset();
} }
void DisplayGL::pauseDrawing() { void DisplayGL::pauseDrawing() {
if (m_drawThread) { if (m_drawThread) {
m_isDrawing = false; m_isDrawing = false;
if (mCoreThreadIsActive(m_context)) { CoreController::Interrupter interrupter(m_context);
mCoreThreadInterrupt(m_context);
}
QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
} }
} }
void DisplayGL::unpauseDrawing() { void DisplayGL::unpauseDrawing() {
if (m_drawThread) { if (m_drawThread) {
m_isDrawing = true; m_isDrawing = true;
if (mCoreThreadIsActive(m_context)) { CoreController::Interrupter interrupter(m_context);
mCoreThreadInterrupt(m_context);
}
QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection); QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
if (mCoreThreadIsActive(m_context)) {
mCoreThreadContinue(m_context);
}
} }
} }
@ -150,9 +136,9 @@ void DisplayGL::filter(bool filter) {
} }
} }
void DisplayGL::framePosted(const uint32_t* buffer) { void DisplayGL::framePosted() {
if (m_drawThread && buffer) { if (m_drawThread) {
m_painter->enqueue(buffer); m_painter->enqueue(m_context->drawContext());
QMetaObject::invokeMethod(m_painter, "draw"); QMetaObject::invokeMethod(m_painter, "draw");
} }
} }
@ -183,12 +169,6 @@ void DisplayGL::resizePainter() {
PainterGL::PainterGL(int majorVersion, QGLWidget* parent) PainterGL::PainterGL(int majorVersion, QGLWidget* parent)
: m_gl(parent) : m_gl(parent)
, m_active(false)
, m_started(false)
, m_context(nullptr)
, m_shader{}
, m_backend(nullptr)
, m_messagePainter(nullptr)
{ {
#ifdef BUILD_GL #ifdef BUILD_GL
mGLContext* glBackend; mGLContext* glBackend;
@ -262,7 +242,7 @@ PainterGL::~PainterGL() {
m_backend = nullptr; m_backend = nullptr;
} }
void PainterGL::setContext(mCoreThread* context) { void PainterGL::setContext(std::shared_ptr<CoreController> context) {
m_context = context; m_context = context;
if (!context) { if (!context) {
@ -273,9 +253,8 @@ void PainterGL::setContext(mCoreThread* context) {
#if defined(_WIN32) && defined(USE_EPOXY) #if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent(); epoxy_handle_external_wglMakeCurrent();
#endif #endif
unsigned width, height; QSize size = m_context->screenDimensions();
context->core->desiredVideoDimensions(context->core, &width, &height); m_backend->setDimensions(m_backend, size.width(), size.height());
m_backend->setDimensions(m_backend, width, height);
m_gl->doneCurrent(); m_gl->doneCurrent();
} }
@ -329,13 +308,13 @@ void PainterGL::start() {
} }
void PainterGL::draw() { void PainterGL::draw() {
if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) { if (m_queue.isEmpty()) {
return; return;
} }
if (mCoreSyncWaitFrameStart(&m_context->impl->sync) || !m_queue.isEmpty()) { if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) {
dequeue(); dequeue();
mCoreSyncWaitFrameEnd(&m_context->impl->sync); mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
m_painter.begin(m_gl->context()->device()); m_painter.begin(m_gl->context()->device());
performDraw(); performDraw();
m_painter.end(); m_painter.end();
@ -349,7 +328,7 @@ void PainterGL::draw() {
m_delayTimer.restart(); m_delayTimer.restart();
} }
} else { } else {
mCoreSyncWaitFrameEnd(&m_context->impl->sync); mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
} }
if (!m_queue.isEmpty()) { if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
@ -375,6 +354,7 @@ void PainterGL::stop() {
m_backend->swap(m_backend); m_backend->swap(m_backend);
m_gl->doneCurrent(); m_gl->doneCurrent();
m_gl->context()->moveToThread(m_gl->thread()); m_gl->context()->moveToThread(m_gl->thread());
m_context.reset();
moveToThread(m_gl->thread()); moveToThread(m_gl->thread());
} }
@ -409,9 +389,8 @@ void PainterGL::enqueue(const uint32_t* backing) {
} else { } else {
buffer = m_free.takeLast(); buffer = m_free.takeLast();
} }
unsigned width, height; QSize size = m_context->screenDimensions();
m_context->core->desiredVideoDimensions(m_context->core, &width, &height); memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
memcpy(buffer, backing, width * height * BYTES_PER_PIXEL);
m_queue.enqueue(buffer); m_queue.enqueue(buffer);
m_mutex.unlock(); m_mutex.unlock();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,7 @@
#ifdef USE_MAGICK #ifdef USE_MAGICK
#include "CoreController.h"
#include "GBAApp.h" #include "GBAApp.h"
#include "LogController.h" #include "LogController.h"
@ -39,6 +40,12 @@ GIFView::~GIFView() {
stopRecording(); 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() { void GIFView::startRecording() {
int delayMs = m_ui.delayAuto->isChecked() ? -1 : m_ui.delayMs->value(); int delayMs = m_ui.delayAuto->isChecked() ? -1 : m_ui.delayMs->value();
ImageMagickGIFEncoderSetParams(&m_encoder, m_ui.frameskip->value(), delayMs); ImageMagickGIFEncoderSetParams(&m_encoder, m_ui.frameskip->value(), delayMs);

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,262 +0,0 @@
/* Copyright (c) 2013-2015 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_GAME_CONTROLLER
#define QGBA_GAME_CONTROLLER
#include <QAtomicInt>
#include <QFile>
#include <QImage>
#include <QObject>
#include <QString>
#include <QTimer>
#include <memory>
#include <functional>
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/input.h>
#ifdef BUILD_SDL
#include "platform/sdl/sdl-events.h"
#endif
struct Configuration;
struct GBAAudio;
struct mCoreConfig;
struct mDebugger;
struct mTileCache;
struct mVideoLogContext;
namespace QGBA {
class AudioProcessor;
class InputController;
class MultiplayerController;
class Override;
class GameController : public QObject {
Q_OBJECT
public:
static const bool VIDEO_SYNC = false;
static const bool AUDIO_SYNC = true;
class Interrupter {
public:
Interrupter(GameController*, bool fromThread = false);
~Interrupter();
private:
GameController* m_parent;
bool m_fromThread;
};
GameController(QObject* parent = nullptr);
~GameController();
const uint32_t* drawContext() const { return m_drawContext; }
mCoreThread* thread() { return &m_threadContext; }
mCheatDevice* cheatDevice() { return m_threadContext.core ? m_threadContext.core->cheatDevice(m_threadContext.core) : nullptr; }
void threadInterrupt();
void threadContinue();
bool isPaused();
bool isLoaded() { return m_gameOpen && mCoreThreadIsActive(&m_threadContext); }
mPlatform platform() const;
bool audioSync() const { return m_audioSync; }
bool videoSync() const { return m_videoSync; }
QSize screenDimensions() const;
void setInputController(InputController* controller) { m_inputController = controller; }
void setMultiplayerController(MultiplayerController* controller);
MultiplayerController* multiplayerController() { return m_multiplayer; }
void clearMultiplayerController();
void setOverride(Override* override);
Override* override() { return m_override; }
void clearOverride();
void setConfig(const mCoreConfig*);
int stateSlot() const { return m_stateSlot; }
#ifdef USE_DEBUGGERS
mDebugger* debugger();
void setDebugger(mDebugger*);
#endif
std::shared_ptr<mTileCache> tileCache();
signals:
void frameAvailable(const uint32_t*);
void gameStarted(mCoreThread*, const QString& fname);
void gameStopped(mCoreThread*);
void gamePaused(mCoreThread*);
void gameUnpaused(mCoreThread*);
void gameCrashed(const QString& errorMessage);
void gameFailed();
void stateLoaded(mCoreThread*);
void rewound(mCoreThread*);
void unimplementedBiosCall(int);
void luminanceValueChanged(int);
void statusPosted(const QString& message);
void postLog(int level, int category, const QString& log);
public slots:
void loadGame(const QString& path);
void loadGame(VFile* vf, const QString& path, const QString& base);
void loadBIOS(int platform, const QString& path);
void loadSave(const QString& path, bool temporary = true);
void yankPak();
void replaceGame(const QString& path);
void setUseBIOS(bool);
void loadPatch(const QString& path);
void importSharkport(const QString& path);
void exportSharkport(const QString& path);
void bootBIOS();
void closeGame();
void setPaused(bool paused);
void reset();
void frameAdvance();
void setRewind(bool enable, int capacity, bool rewindSave);
void rewind(int states = 0);
void startRewinding();
void stopRewinding();
void keyPressed(int key);
void keyReleased(int key);
void clearKeys();
void setAutofire(int key, bool enable);
void setAudioBufferSamples(int samples);
void setAudioSampleRate(unsigned rate);
void setAudioChannelEnabled(int channel, bool enable = true);
void startAudio();
void setVideoLayerEnabled(int layer, bool enable = true);
void setFPSTarget(float fps);
void loadState(int slot = 0);
void saveState(int slot = 0);
void loadBackupState();
void saveBackupState();
void setTurbo(bool, bool forced = true);
void setTurboSpeed(float ratio);
void setSync(bool);
void setAudioSync(bool);
void setVideoSync(bool);
void setAVStream(mAVStream*);
void clearAVStream();
void reloadAudioDriver();
void setSaveStateExtdata(int flags);
void setLoadStateExtdata(int flags);
void setPreload(bool);
#ifdef USE_PNG
void screenshot();
#endif
void setLuminanceValue(uint8_t value);
uint8_t luminanceValue() const { return m_luxValue; }
void setLuminanceLevel(int level);
void increaseLuminanceLevel() { setLuminanceLevel(m_luxLevel + 1); }
void decreaseLuminanceLevel() { setLuminanceLevel(m_luxLevel - 1); }
void setRealTime();
void setFixedTime(const QDateTime& time);
void setFakeEpoch(const QDateTime& time);
void setLogLevel(int);
void enableLogLevel(int);
void disableLogLevel(int);
void startVideoLog(const QString& path);
void endVideoLog();
private slots:
void openGame(bool bios = false);
void crashGame(const QString& crashMessage);
void cleanGame();
void pollEvents();
void updateAutofire();
private:
void updateKeys();
void redoSamples(int samples);
void enableTurbo();
uint32_t* m_drawContext = nullptr;
uint32_t* m_frontBuffer = nullptr;
mCoreThread m_threadContext{};
const mCoreConfig* m_config;
mCheatDevice* m_cheatDevice;
int m_activeKeys = 0;
int m_activeButtons = 0;
int m_inactiveKeys = 0;
int m_logLevels = 0;
bool m_gameOpen = false;
QString m_fname;
QString m_fsub;
VFile* m_vf = nullptr;
QString m_bios;
bool m_useBios = false;
QString m_patch;
Override* m_override = nullptr;
AudioProcessor* m_audioProcessor;
QAtomicInt m_pauseAfterFrame{false};
QList<std::function<void ()>> m_resetActions;
bool m_sync = true;
bool m_videoSync = VIDEO_SYNC;
bool m_audioSync = AUDIO_SYNC;
float m_fpsTarget = -1;
bool m_turbo = false;
bool m_turboForced = false;
float m_turboSpeed = -1;
bool m_wasPaused = false;
std::shared_ptr<mTileCache> m_tileCache;
QList<bool> m_audioChannels;
QList<bool> m_videoLayers;
bool m_autofire[GBA_KEY_MAX] = {};
int m_autofireStatus[GBA_KEY_MAX] = {};
int m_stateSlot = 1;
struct VFile* m_backupLoadState = nullptr;
QByteArray m_backupSaveState{nullptr};
int m_saveStateFlags;
int m_loadStateFlags;
bool m_preload = false;
InputController* m_inputController = nullptr;
MultiplayerController* m_multiplayer = nullptr;
mAVStream* m_stream = nullptr;
mVideoLogContext* m_vl = nullptr;
VFile* m_vlVf = nullptr;
struct GameControllerLux : GBALuminanceSource {
GameController* p;
uint8_t value;
} m_lux;
uint8_t m_luxValue;
int m_luxLevel;
};
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

@ -11,8 +11,11 @@ LogController LogController::s_global(mLOG_ALL);
LogController::LogController(int levels, QObject* parent) LogController::LogController(int levels, QObject* parent)
: 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) { if (this != &s_global) {
connect(&s_global, &LogController::logPosted, this, &LogController::postLog); connect(&s_global, &LogController::logPosted, this, &LogController::postLog);
connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels); 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) { void LogController::postLog(int level, int category, const QString& string) {
if (!(m_logLevel & level)) { if (!mLogFilterTest(&m_filter, category, static_cast<mLogLevel>(level))) {
return; return;
} }
emit logPosted(level, category, string); emit logPosted(level, category, string);
} }
void LogController::setLevels(int levels) { void LogController::setLevels(int levels) {
m_logLevel = levels; m_filter.defaultLevels = levels;
emit levelsSet(levels); emit levelsSet(levels);
} }
void LogController::enableLevels(int levels) { void LogController::enableLevels(int levels) {
m_logLevel |= levels; m_filter.defaultLevels |= levels;
emit levelsEnabled(levels); emit levelsEnabled(levels);
} }
void LogController::disableLevels(int levels) { void LogController::disableLevels(int levels) {
m_logLevel &= ~levels; m_filter.defaultLevels &= ~levels;
emit levelsDisabled(levels); emit levelsDisabled(levels);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -174,19 +174,26 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
SettingsView::~SettingsView() { SettingsView::~SettingsView() {
#if defined(BUILD_GL) || defined(BUILD_GLES) #if defined(BUILD_GL) || defined(BUILD_GLES)
if (m_shader) { setShaderSelector(nullptr);
m_ui.stackedWidget->removeWidget(m_shader);
m_shader->setParent(nullptr);
}
#endif #endif
} }
void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) {
#if defined(BUILD_GL) || defined(BUILD_GLES) #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_shader = shaderSelector;
m_ui.stackedWidget->addWidget(m_shader); if (shaderSelector) {
m_ui.tabs->addItem(tr("Shaders")); m_ui.stackedWidget->addWidget(m_shader);
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved); m_ui.tabs->addItem(tr("Shaders"));
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved);
}
#endif #endif
} }
@ -269,6 +276,7 @@ void SettingsView::updateConfig() {
if (displayDriver != m_controller->getQtOption("displayDriver")) { if (displayDriver != m_controller->getQtOption("displayDriver")) {
m_controller->setQtOption("displayDriver", displayDriver); m_controller->setQtOption("displayDriver", displayDriver);
Display::setDriver(static_cast<Display::Driver>(displayDriver.toInt())); Display::setDriver(static_cast<Display::Driver>(displayDriver.toInt()));
setShaderSelector(nullptr);
emit displayDriverChanged(); emit displayDriverChanged();
} }

View File

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

View File

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

View File

@ -13,7 +13,33 @@ VFileDevice::VFileDevice(VFile* vf, QObject* parent)
: QIODevice(parent) : QIODevice(parent)
, m_vf(vf) , 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) { qint64 VFileDevice::readData(char* data, qint64 maxSize) {

View File

@ -17,7 +17,16 @@ class VFileDevice : public QIODevice {
Q_OBJECT Q_OBJECT
public: 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 VFile* open(const QString& path, int mode);
static VDir* openDir(const QString& path); static VDir* openDir(const QString& path);
@ -26,7 +35,6 @@ public:
protected: protected:
virtual qint64 readData(char* data, qint64 maxSize) override; virtual qint64 readData(char* data, qint64 maxSize) override;
virtual qint64 writeData(const char* data, qint64 maxSize) override; virtual qint64 writeData(const char* data, qint64 maxSize) override;
virtual qint64 size() const override;
private: private:
VFile* m_vf; VFile* m_vf;

View File

@ -195,6 +195,14 @@ VideoView::~VideoView() {
free(m_containerCstr); 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() { void VideoView::startRecording() {
if (!validateSettings()) { if (!validateSettings()) {
return; return;

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -6,7 +6,7 @@
#include "InputController.h" #include "InputController.h"
#include "ConfigController.h" #include "ConfigController.h"
#include "GameController.h" #include "CoreController.h"
#include "GamepadAxisEvent.h" #include "GamepadAxisEvent.h"
#include "GamepadButtonEvent.h" #include "GamepadButtonEvent.h"
#include "InputItem.h" #include "InputItem.h"
@ -85,18 +85,42 @@ void InputController::addKey(const QString& name) {
return; return;
} }
m_keyIndex.addItem(qMakePair([this, name]() { m_keyIndex.addItem(qMakePair([this, name]() {
emit keyPressed(keyId(name)); m_activeKeys |= 1 << keyId(name);
}, [this, name]() { }, [this, name]() {
emit keyReleased(keyId(name)); m_activeKeys &= ~(1 << keyId(name));
}), name, QString("key%0").arg(name), m_bindings.get()); }), name, QString("key%0").arg(name), m_bindings.get());
m_keyIndex.addItem(qMakePair([this, name]() { m_keyIndex.addItem(qMakePair([this, name]() {
emit keyAutofire(keyId(name), true); setAutofire(keyId(name), true);
}, [this, name]() { }, [this, name]() {
emit keyAutofire(keyId(name), false); setAutofire(keyId(name), false);
}), name, QString("autofire%1").arg(name), m_autofire.get()); }), 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) { void InputController::addPlatform(mPlatform platform, const mInputPlatformInfo* info) {
m_keyInfo[platform] = info; m_keyInfo[platform] = info;
for (size_t i = 0; i < info->nKeys; ++i) { for (size_t i = 0; i < info->nKeys; ++i) {
@ -121,6 +145,20 @@ void InputController::setPlatform(mPlatform platform) {
rebuildKeyIndex(); rebuildKeyIndex();
restoreModel(); 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() { InputController::~InputController() {
@ -158,7 +196,6 @@ void InputController::setConfiguration(ConfigController* config) {
m_config = config; m_config = config;
m_inputIndex.setConfigController(config); m_inputIndex.setConfigController(config);
m_keyIndex.setConfigController(config); m_keyIndex.setConfigController(config);
setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
loadConfiguration(KEYBOARD); loadConfiguration(KEYBOARD);
loadProfile(KEYBOARD, profileForType(KEYBOARD)); loadProfile(KEYBOARD, profileForType(KEYBOARD));
#ifdef BUILD_SDL #ifdef BUILD_SDL
@ -373,7 +410,7 @@ const mInputMap* InputController::map() {
} }
int InputController::pollEvents() { int InputController::pollEvents() {
int activeButtons = 0; int activeButtons = m_activeKeys;
#ifdef BUILD_SDL #ifdef BUILD_SDL
if (m_playerAttached && m_sdlPlayer.joystick) { if (m_playerAttached && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
@ -844,3 +881,34 @@ void InputController::rebindKey(const QString& key) {
bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key); bindAxis(SDL_BINDING_BUTTON, item->axis(), item->direction(), key);
#endif #endif
} }
void InputController::increaseLuminanceLevel() {
setLuminanceLevel(m_luxLevel + 1);
}
void InputController::decreaseLuminanceLevel() {
setLuminanceLevel(m_luxLevel - 1);
}
void InputController::setLuminanceLevel(int level) {
int value = 0x16;
level = std::max(0, std::min(10, level));
if (level > 0) {
value += GBA_LUX_LEVELS[level - 1];
}
setLuminanceValue(value);
}
void InputController::setLuminanceValue(uint8_t value) {
m_luxValue = value;
value = std::max<int>(value - 0x16, 0);
m_luxLevel = 10;
for (int i = 0; i < 10; ++i) {
if (value < GBA_LUX_LEVELS[i]) {
m_luxLevel = i;
break;
}
}
emit luminanceValueChanged(m_luxValue);
}

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#endif #endif
#endif #endif
#include <mgba/core/cheats.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/config.h> #include <mgba/core/config.h>
#include <mgba/core/input.h> #include <mgba/core/input.h>
@ -104,6 +105,16 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
struct mCheatDevice* device = NULL;
if (args.cheatsFile && (device = renderer.core->cheatDevice(renderer.core))) {
struct VFile* vf = VFileOpen(args.cheatsFile, O_RDONLY);
if (vf) {
mCheatDeviceClear(device);
mCheatParseFile(device, vf);
vf->close(vf);
}
}
mInputMapInit(&renderer.core->inputMap, renderer.core->inputInfo); mInputMapInit(&renderer.core->inputMap, renderer.core->inputInfo);
mCoreInitConfig(renderer.core, PORT); mCoreInitConfig(renderer.core, PORT);
applyArguments(&args, &subparser, &renderer.core->config); applyArguments(&args, &subparser, &renderer.core->config);
@ -148,6 +159,10 @@ int main(int argc, char** argv) {
mSDLDetachPlayer(&renderer.events, &renderer.player); mSDLDetachPlayer(&renderer.events, &renderer.player);
mInputMapDeinit(&renderer.core->inputMap); mInputMapDeinit(&renderer.core->inputMap);
if (device) {
mCheatDeviceDestroy(device);
}
mSDLDeinit(&renderer); mSDLDeinit(&renderer);
freeArguments(&args); freeArguments(&args);
@ -184,10 +199,10 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
#endif #endif
mDebuggerAttach(debugger, renderer->core); mDebuggerAttach(debugger, renderer->core);
mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL);
} #ifdef ENABLE_SCRIPTING
#ifdef ENABLE_SCRIPTING mScriptBridgeSetDebugger(bridge, debugger);
mScriptBridgeSetDebugger(bridge, debugger);
#endif #endif
}
#endif #endif
if (args->patch) { if (args->patch) {

125
src/util/elf-read.c Normal file
View File

@ -0,0 +1,125 @@
/* 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 <mgba-util/elf-read.h>
#ifdef USE_ELF
#include <mgba-util/vfs.h>
DEFINE_VECTOR(ELFProgramHeaders, Elf32_Phdr);
DEFINE_VECTOR(ELFSectionHeaders, Elf32_Shdr);
static bool _elfInit = false;
struct ELF {
Elf* e;
struct VFile* vf;
size_t size;
char* memory;
};
struct ELF* ELFOpen(struct VFile* vf) {
if (!_elfInit) {
_elfInit = elf_version(EV_CURRENT) != EV_NONE;
if (!_elfInit) {
return NULL;
}
}
if (!vf) {
return NULL;
}
size_t size = vf->size(vf);
char* memory = vf->map(vf, size, MAP_READ);
if (!memory) {
return NULL;
}
Elf* e = elf_memory(memory, size);
if (!e || elf_kind(e) != ELF_K_ELF) {
elf_end(e);
vf->unmap(vf, memory, size);
return false;
}
struct ELF* elf = malloc(sizeof(*elf));
elf->e = e;
elf->vf = vf;
elf->size = size;
elf->memory = memory;
return elf;
}
void ELFClose(struct ELF* elf) {
elf_end(elf->e);
elf->vf->unmap(elf->vf, elf->memory, elf->size);
free(elf);
}
void* ELFBytes(struct ELF* elf, size_t* size) {
if (size) {
*size = elf->size;
}
return elf->memory;
}
uint16_t ELFMachine(struct ELF* elf) {
Elf32_Ehdr* hdr = elf32_getehdr(elf->e);
if (!hdr) {
return 0;
}
return hdr->e_machine;
}
uint32_t ELFEntry(struct ELF* elf) {
Elf32_Ehdr* hdr = elf32_getehdr(elf->e);
if (!hdr) {
return 0;
}
return hdr->e_entry;
}
void ELFGetProgramHeaders(struct ELF* elf, struct ELFProgramHeaders* ph) {
ELFProgramHeadersClear(ph);
Elf32_Ehdr* hdr = elf32_getehdr(elf->e);
Elf32_Phdr* phdr = elf32_getphdr(elf->e);
ELFProgramHeadersResize(ph, hdr->e_phnum);
memcpy(ELFProgramHeadersGetPointer(ph, 0), phdr, sizeof(*phdr) * hdr->e_phnum);
}
void ELFGetSectionHeaders(struct ELF* elf, struct ELFSectionHeaders* sh) {
ELFSectionHeadersClear(sh);
Elf_Scn* section = elf_getscn(elf->e, 0);
do {
*ELFSectionHeadersAppend(sh) = *elf32_getshdr(section);
} while ((section = elf_nextscn(elf->e, section)));
}
Elf32_Shdr* ELFGetSectionHeader(struct ELF* elf, size_t index) {
Elf_Scn* section = elf_getscn(elf->e, index);
return elf32_getshdr(section);
}
size_t ELFFindSection(struct ELF* elf, const char* name) {
Elf32_Ehdr* hdr = elf32_getehdr(elf->e);
size_t shstrtab = hdr->e_shstrndx;
if (strcmp(name, ".shstrtab") == 0) {
return shstrtab;
}
Elf_Scn* section = NULL;
while ((section = elf_nextscn(elf->e, section))) {
Elf32_Shdr* shdr = elf32_getshdr(section);
const char* sname = elf_strptr(elf->e, shstrtab, shdr->sh_name);
if (strcmp(sname, name) == 0) {
return elf_ndxscn(section);
}
}
return 0;
}
const char* ELFGetString(struct ELF* elf, size_t section, size_t string) {
return elf_strptr(elf->e, section, string);
}
#endif