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

View File

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

11
CHANGES
View File

@ -1,12 +1,19 @@
0.7.0: (Future)
Features:
- ELF support
Bugfixes:
- 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:
- GBA Timer: Use global cycles for timers
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
- All: Make FIXED_ROM_BUFFER an option instead of 3DS-only
- Qt: Don't rebuild library view if style hasn't changed
- Qt: Redo GameController into multiple classes
0.6.0: (Future)
0.6.0: (2017-07-16)
Features:
- Library view
- Sprite viewer
@ -181,6 +188,8 @@ Bugfixes:
- Core: Fix rewinding getting out of sync (fixes mgba.io/i/791)
- Qt: Fix GL-less build
- Qt: Fix Software renderer not handling alpha bits properly
- Qt: Fix screen background improperly stretching
- SDL: Fix cheats not loading
Misc:
- GB Serialize: Add MBC state serialization
- 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)
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
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_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_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_GB ON CACHE BOOL "Build Game Boy core")
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)
endif()
if(DEFINED 3DS OR DEFINED WII)
if(DEFINED 3DS)
add_definitions(-DFIXED_ROM_BUFFER)
endif()
@ -397,6 +398,7 @@ find_feature(USE_MAGICK "MagickWand")
find_feature(USE_EPOXY "epoxy")
find_feature(USE_CMOCKA "cmocka")
find_feature(USE_SQLITE3 "sqlite3")
find_feature(USE_ELF "libelf")
find_feature(ENABLE_PYTHON "PythonLibs")
# Features
@ -602,6 +604,17 @@ if(USE_SQLITE3)
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c")
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)
list(APPEND ENABLES SCRIPTING)
@ -935,6 +948,7 @@ if(NOT QUIET)
message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " SQLite3 game database: ${USE_SQLITE3}")
message(STATUS " ELF loading support: ${USE_ELF}")
message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:")
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.
- ImageMagick: for GIF recording.
- 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.

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* 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
#endif

View File

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

View File

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

View File

@ -91,6 +91,7 @@ struct mDebugger {
struct mCPUComponent d;
struct mDebuggerPlatform* platform;
enum mDebuggerState state;
enum mDebuggerType type;
struct mCore* core;
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);
bool GBALoadMB(struct GBA* gba, struct VFile* vf);
bool GBALoadNull(struct GBA* gba);
bool GBAIsROM(struct VFile* vf);
bool GBAIsMB(struct VFile* vf);

View File

@ -8,6 +8,11 @@
#include <mgba/core/log.h>
#include <mgba/core/serialize.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
#include <mgba/gb/core.h>
@ -273,3 +278,67 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
core->rtc.custom = rtc;
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
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) {
if (context->currentState) {
return;
}
mCoreRewindPatchesInit(&context->patchMemory, entries);
size_t e;
for (e = 0; e < entries; ++e) {
@ -42,6 +45,9 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries,
}
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
if (!context->currentState) {
return;
}
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexLock(&context->mutex);
@ -55,6 +61,8 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
#endif
context->previousState->close(context->previousState);
context->currentState->close(context->currentState);
context->previousState = NULL;
context->currentState = NULL;
size_t s;
for (s = 0; s < mCoreRewindPatchesSize(&context->patchMemory); ++s) {
deinitPatchFast(mCoreRewindPatchesGetPointer(&context->patchMemory, s));

View File

@ -19,6 +19,12 @@ struct mScriptInfo {
bool success;
};
struct mScriptSymbol {
const char* name;
int32_t* out;
bool success;
};
static void _seDeinit(void* value) {
struct mScriptEngine* se = value;
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) {
UNUSED(key);
UNUSED(user);
@ -111,3 +126,13 @@ bool mScriptBridgeLoadScript(struct mScriptBridge* sb, const char* name) {
vf->close(vf);
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);
}
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
}
mCoreThreadRewindParamsChanged(threadContext);
_changeState(threadContext->impl, THREAD_RUNNING, true);
@ -252,7 +249,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
}
bool mCoreThreadStart(struct mCoreThread* threadContext) {
threadContext->impl = malloc(sizeof(*threadContext->impl));
threadContext->impl = calloc(sizeof(*threadContext->impl), 1);
threadContext->impl->state = THREAD_INITIALIZED;
threadContext->logger.p = threadContext;
if (!threadContext->logger.d.log) {
@ -547,6 +544,16 @@ void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding)
MutexUnlock(&threadContext->impl->stateMutex);
}
void mCoreThreadRewindParamsChanged(struct mCoreThread* threadContext) {
struct mCore* core = threadContext->core;
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->impl->rewind, core->opts.rewindBufferCapacity, true);
threadContext->impl->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
} else {
mCoreRewindContextDeinit(&threadContext->impl->rewind);
}
}
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
MutexLock(&threadContext->impl->stateMutex);
if (threadContext->impl->interruptDepth && threadContext->impl->savedState == THREAD_RUNNING) {

View File

@ -551,6 +551,11 @@ static void _lookupIdentifier(struct mDebugger* debugger, const char* name, stru
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
if (cliDebugger->system) {
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)) {
return;
}
@ -832,6 +837,7 @@ void CLIDebuggerCreate(struct CLIDebugger* debugger) {
debugger->d.custom = _cliDebuggerCustom;
debugger->d.paused = _commandLine;
debugger->d.entered = _reportEntry;
debugger->d.type = DEBUGGER_CLI;
debugger->system = NULL;
debugger->backend = NULL;

View File

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

View File

@ -8,6 +8,7 @@
#include <mgba/core/core.h>
#include <mgba/core/log.h>
#include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
@ -20,6 +21,9 @@
#include <mgba/internal/gba/renderers/video-software.h>
#include <mgba/internal/gba/savedata.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/patch.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) {
#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)) {
return GBALoadMB(core->board, vf);
}
@ -704,7 +717,27 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
}
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

View File

@ -21,6 +21,10 @@
#include <mgba-util/memory.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_DEBUG, "GBA Debug", "gba.debug");
@ -203,6 +207,10 @@ void GBAReset(struct ARMCore* cpu) {
gba->debug = false;
memset(gba->debugString, 0, sizeof(gba->debugString));
if (!gba->romVf && gba->memory.rom) {
GBASkipBIOS(gba);
}
}
void GBASkipBIOS(struct GBA* gba) {
@ -288,6 +296,29 @@ void GBADetachDebugger(struct GBA* gba) {
}
#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) {
GBAUnloadROM(gba);
gba->romVf = vf;
@ -308,6 +339,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = 0;
gba->memory.romMask = 0;
gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize);
if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
}
return true;
}
@ -352,6 +386,9 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
gba->memory.romSize = SIZE_CART0;
gba->isPristine = false;
}
if (gba->cpu && gba->memory.activeRegion >= REGION_CART0) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
}
// TODO: error check
return true;
}
@ -479,6 +516,17 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
}
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) {
return false;
}
@ -496,6 +544,14 @@ bool GBAIsMB(struct VFile* vf) {
if (!GBAIsROM(vf)) {
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) {
return false;
}

View File

@ -80,6 +80,8 @@ void GBAMemoryInit(struct GBA* gba) {
gba->memory.biosPrefetch = 0;
gba->memory.mirroring = false;
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
GBADMAInit(gba);
GBAVFameInit(&gba->memory.vfame);
}
@ -107,9 +109,8 @@ void GBAMemoryReset(struct GBA* gba) {
}
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));

View File

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

View File

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

View File

@ -28,6 +28,7 @@ ffi.set_source("mgba._pylib", """
#include <mgba/core/version.h>
#include <mgba/debugger/debugger.h>
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/debugger/cli-debugger.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/input.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_init_code("""
from mgba._pylib import ffi
debugger = None
from mgba._pylib import ffi, lib
symbols = {}
globalSyms = {
'symbols': symbols
}
pendingCode = []
@ffi.def_extern()
def mPythonSetDebugger(_debugger):
from mgba.debugger import NativeDebugger
global debugger
if debugger and debugger._native == _debugger:
def mPythonSetDebugger(debugger):
from mgba.debugger import NativeDebugger, CLIDebugger
oldDebugger = globalSyms.get('debugger')
if oldDebugger and oldDebugger._native == debugger:
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()
def mPythonLoadScript(name, vf):
@ -99,18 +110,40 @@ ffi.embedding_init_code("""
def mPythonRunPending():
global pendingCode
for code in pendingCode:
exec(code)
exec(code, globalSyms, {})
pendingCode = []
@ffi.def_extern()
def mPythonDebuggerEntered(reason, info):
global debugger
debugger = globalSyms['debugger']
if not debugger:
return
if info == ffi.NULL:
info = None
for cb in debugger._cbs:
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__":

View File

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

View File

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

View File

@ -91,6 +91,12 @@ class CoreCallbacks(object):
cb()
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):
self._core = native
self._wasReset = False
@ -117,8 +123,10 @@ class Core(object):
@classmethod
def _detect(cls, core):
if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA:
from .gba import GBA
return GBA(core)
if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB:
from .gb import GB
return GB(core)
return Core(core)
@ -253,12 +261,3 @@ class IRunner(object):
def isPaused(self):
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/.
from ._pylib import ffi, lib
from .core import IRunner, ICoreOwner, Core
import io
import sys
class DebuggerCoreOwner(ICoreOwner):
def __init__(self, debugger):
@ -78,3 +80,22 @@ class NativeDebugger(IRunner):
def addCallback(self, 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/. */
#include "AssetTile.h"
#include "CoreController.h"
#include "GBAApp.h"
#include <QFontDatabase>
@ -39,7 +40,7 @@ AssetTile::AssetTile(QWidget* parent)
m_ui.b->setFont(font);
}
void AssetTile::setController(GameController* controller) {
void AssetTile::setController(std::shared_ptr<CoreController> controller) {
m_tileCache = controller->tileCache();
switch (controller->platform()) {
#ifdef M_CORE_GBA
@ -83,7 +84,7 @@ void AssetTile::selectIndex(int index) {
m_index = index;
const uint16_t* data;
mTileCacheSetPalette(m_tileCache.get(), m_paletteSet);
mTileCacheSetPalette(m_tileCache, m_paletteSet);
unsigned bpp = 8 << m_tileCache->bpp;
int dispIndex = index;
int paletteId = m_paletteId;
@ -98,7 +99,7 @@ void AssetTile::selectIndex(int index) {
#endif
dispIndex -= m_boundary;
}
data = mTileCacheGetTile(m_tileCache.get(), index, paletteId);
data = mTileCacheGetTile(m_tileCache, index, paletteId);
m_ui.tileId->setText(QString::number(dispIndex * (1 + m_paletteSet)));
m_ui.address->setText(tr("%0%1%2")
.arg(m_addressWidth == 4 ? index >= m_boundary : 0)
@ -112,7 +113,7 @@ void AssetTile::selectIndex(int index) {
void AssetTile::selectColor(int index) {
const uint16_t* data;
mTileCacheSetPalette(m_tileCache.get(), m_paletteSet);
mTileCacheSetPalette(m_tileCache, m_paletteSet);
unsigned bpp = 8 << m_tileCache->bpp;
int paletteId = m_paletteId;
// XXX: Do this better
@ -121,7 +122,7 @@ void AssetTile::selectColor(int index) {
paletteId += m_tileCache->count / 2;
}
#endif
data = mTileCacheGetTile(m_tileCache.get(), m_index, m_paletteId);
data = mTileCacheGetTile(m_tileCache, m_index, m_paletteId);
uint16_t color = data[index];
m_ui.color->setColor(0, color);
m_ui.color->update();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#endif
#endif
#include <mgba/core/cheats.h>
#include <mgba/core/core.h>
#include <mgba/core/config.h>
#include <mgba/core/input.h>
@ -104,6 +105,16 @@ int main(int argc, char** argv) {
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);
mCoreInitConfig(renderer.core, PORT);
applyArguments(&args, &subparser, &renderer.core->config);
@ -148,6 +159,10 @@ int main(int argc, char** argv) {
mSDLDetachPlayer(&renderer.events, &renderer.player);
mInputMapDeinit(&renderer.core->inputMap);
if (device) {
mCheatDeviceDestroy(device);
}
mSDLDeinit(&renderer);
freeArguments(&args);
@ -184,10 +199,10 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
#endif
mDebuggerAttach(debugger, renderer->core);
mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL);
}
#ifdef ENABLE_SCRIPTING
mScriptBridgeSetDebugger(bridge, debugger);
#ifdef ENABLE_SCRIPTING
mScriptBridgeSetDebugger(bridge, debugger);
#endif
}
#endif
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