Merge branch 'master' into translations
3
CHANGES
|
@ -50,6 +50,8 @@ Emulation fixes:
|
|||
- GBA Video: Fix sprite layer priority updating in GL
|
||||
Other fixes:
|
||||
- ARM: Disassemble Thumb mov pseudo-instruction properly
|
||||
- ARM: Disassemble ARM asr/lsr #32 properly
|
||||
- ARM: Disassemble ARM movs properly
|
||||
- Core: Don't attempt to restore rewind diffs past start of rewind
|
||||
- Core: Fix the runloop resuming after a game has crashed (fixes mgba.io/i/2451)
|
||||
- Core: Fix crash if library can't be opened
|
||||
|
@ -64,6 +66,7 @@ Other fixes:
|
|||
- Qt: Fix crash when clicking past last tile in viewer
|
||||
- Qt: Fix preloading for ROM replacing
|
||||
- Qt: Fix screen not displaying on Wayland (fixes mgba.io/i/2190)
|
||||
- Qt: Fix crash when selecting 256-color sprite in sprite view
|
||||
- VFS: Failed file mapping should return NULL on POSIX
|
||||
Misc:
|
||||
- Core: Suspend runloop when a core crashes
|
||||
|
|
|
@ -754,6 +754,7 @@ if(ENABLE_SCRIPTING)
|
|||
include_directories(AFTER ${LUA_INCLUDE_DIR})
|
||||
list(APPEND FEATURE_DEFINES LUA_VERSION_ONLY=\"${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}\")
|
||||
list(APPEND DEPENDENCY_LIB ${LUA_LIBRARY})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},liblua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}-0")
|
||||
endif()
|
||||
|
||||
if(BUILD_PYTHON)
|
||||
|
|
10
README.md
|
@ -122,13 +122,15 @@ Compiling requires using CMake 3.1 or newer. GCC and Clang are both known to wor
|
|||
|
||||
#### Docker building
|
||||
|
||||
The recommended way to build for most platforms is to use Docker. Several Docker images are provided that contain the requisite toolchain and dependencies for building mGBA across several platforms.
|
||||
The recommended way to build for most platforms is to use Docker. Several Docker images are provided that contain the requisite toolchain and dependencies for building mGBA across several platforms.
|
||||
|
||||
Note: If you are on an older Windows system before Windows 10, you may need to configure your Docker to use VirtualBox shared folders to correctly map your current `mgba` checkout directory to the Docker image's working directory. (See issue [#1985](https://mgba.io/i/1985) for details.)
|
||||
|
||||
To use a Docker image to build mGBA, simply run the following command while in the root of an mGBA checkout:
|
||||
|
||||
docker run --rm -t -v ${PWD}:/home/mgba/src mgba/windows:w32
|
||||
docker run --rm -it -v ${PWD}:/home/mgba/src mgba/windows:w32
|
||||
|
||||
This will produce a `build-win32` directory with the build products. Replace `mgba/windows:w32` with another Docker image for other platforms, which will produce a corresponding other directory. The following Docker images available on Docker Hub:
|
||||
After starting the Docker container, it will produce a `build-win32` directory with the build products. Replace `mgba/windows:w32` with another Docker image for other platforms, which will produce a corresponding other directory. The following Docker images available on Docker Hub:
|
||||
|
||||
- mgba/3ds
|
||||
- mgba/switch
|
||||
|
@ -141,6 +143,8 @@ This will produce a `build-win32` directory with the build products. Replace `mg
|
|||
- mgba/windows:w32
|
||||
- mgba/windows:w64
|
||||
|
||||
If you want to speed up the build process, consider adding the flag `-e MAKEFLAGS=-jN` to do a parallel build for mGBA with `N` number of CPU cores.
|
||||
|
||||
#### *nix building
|
||||
|
||||
To use CMake to build on a Unix-based system, the recommended commands are as follows:
|
||||
|
|
|
@ -86,6 +86,7 @@ struct mCore {
|
|||
bool (*loadSave)(struct mCore*, struct VFile* vf);
|
||||
bool (*loadTemporarySave)(struct mCore*, struct VFile* vf);
|
||||
void (*unloadROM)(struct mCore*);
|
||||
size_t (*romSize)(const struct mCore*);
|
||||
void (*checksum)(const struct mCore*, void* data, enum mCoreChecksumType type);
|
||||
|
||||
bool (*loadBIOS)(struct mCore*, struct VFile* vf, int biosID);
|
||||
|
|
|
@ -36,6 +36,12 @@ struct mLogger {
|
|||
struct mLogFilter* filter;
|
||||
};
|
||||
|
||||
struct mStandardLogger {
|
||||
struct mLogger d;
|
||||
bool logToStdout;
|
||||
struct VFile* logFile;
|
||||
};
|
||||
|
||||
struct mLogger* mLogGetContext(void);
|
||||
void mLogSetDefaultLogger(struct mLogger*);
|
||||
int mLogGenerateCategory(const char*, const char*);
|
||||
|
@ -44,6 +50,10 @@ const char* mLogCategoryId(int);
|
|||
int mLogCategoryById(const char*);
|
||||
|
||||
struct mCoreConfig;
|
||||
void mStandardLoggerInit(struct mStandardLogger*);
|
||||
void mStandardLoggerDeinit(struct mStandardLogger*);
|
||||
void mStandardLoggerConfig(struct mStandardLogger*, struct mCoreConfig* config);
|
||||
|
||||
void mLogFilterInit(struct mLogFilter*);
|
||||
void mLogFilterDeinit(struct mLogFilter*);
|
||||
void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*);
|
||||
|
|
|
@ -21,6 +21,7 @@ struct mCoreThread;
|
|||
struct mThreadLogger {
|
||||
struct mLogger d;
|
||||
struct mCoreThread* p;
|
||||
struct mLogger* logger;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
|
|
@ -67,10 +67,11 @@ struct ParseTree {
|
|||
};
|
||||
|
||||
size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol);
|
||||
void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv);
|
||||
|
||||
void lexFree(struct LexVector* lv);
|
||||
|
||||
struct ParseTree* parseTreeCreate(void);
|
||||
void parseFree(struct ParseTree* tree);
|
||||
bool parseLexedExpression(struct ParseTree* tree, struct LexVector* lv);
|
||||
|
||||
struct mDebugger;
|
||||
bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tree, int32_t* value, int* segment);
|
||||
|
|
|
@ -155,6 +155,7 @@ struct GBAVideoGLRenderer {
|
|||
GLuint vbo;
|
||||
|
||||
GLuint outputTex;
|
||||
bool outputTexDirty;
|
||||
|
||||
GLuint paletteTex;
|
||||
uint16_t shadowPalette[GBA_VIDEO_VERTICAL_PIXELS][512];
|
||||
|
|
After Width: | Height: | Size: 443 B |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 509 B |
After Width: | Height: | Size: 782 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 548 B |
After Width: | Height: | Size: 677 B |
|
@ -19,6 +19,9 @@
|
|||
info->operandFormat |= ARM_OPERAND_SHIFT_REGISTER_3; \
|
||||
} else { \
|
||||
info->op3.shifterImm = (opcode >> 7) & 0x1F; \
|
||||
if (!info->op3.shifterImm && (ARM_SHIFT_ ## OP == ARM_SHIFT_LSR || ARM_SHIFT_ ## OP == ARM_SHIFT_ASR)) { \
|
||||
info->op3.shifterImm = 32; \
|
||||
} \
|
||||
info->operandFormat |= ARM_OPERAND_SHIFT_IMMEDIATE_3; \
|
||||
}
|
||||
|
||||
|
|
|
@ -416,6 +416,7 @@ int ARMDisassemble(const struct ARMInstructionInfo* info, struct ARMCore* cpu, c
|
|||
case ARM_MN_LSR:
|
||||
case ARM_MN_MLA:
|
||||
case ARM_MN_MUL:
|
||||
case ARM_MN_MOV:
|
||||
case ARM_MN_MVN:
|
||||
case ARM_MN_ORR:
|
||||
case ARM_MN_ROR:
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#define MAX_CATEGORY 64
|
||||
#define MAX_LOG_BUF 1024
|
||||
|
||||
static struct mLogger* _defaultLogger = NULL;
|
||||
|
||||
|
@ -183,4 +185,63 @@ int mLogFilterLevels(const struct mLogFilter* filter , int category) {
|
|||
return value;
|
||||
}
|
||||
|
||||
void _mCoreStandardLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
struct mStandardLogger* stdlog = (struct mStandardLogger*) logger;
|
||||
|
||||
if (!mLogFilterTest(logger->filter, category, level)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[MAX_LOG_BUF];
|
||||
|
||||
// Prepare the string
|
||||
size_t length = snprintf(buffer, sizeof(buffer), "%s: ", mLogCategoryName(category));
|
||||
if (length < sizeof(buffer)) {
|
||||
length += vsnprintf(buffer + length, sizeof(buffer) - length, format, args);
|
||||
}
|
||||
if (length < sizeof(buffer)) {
|
||||
length += snprintf(buffer + length, sizeof(buffer) - length, "\n");
|
||||
}
|
||||
|
||||
// Make sure the length doesn't exceed the size of the buffer when actually writing
|
||||
if (length > sizeof(buffer)) {
|
||||
length = sizeof(buffer);
|
||||
}
|
||||
|
||||
if (stdlog->logToStdout) {
|
||||
printf("%s", buffer);
|
||||
}
|
||||
|
||||
if (stdlog->logFile) {
|
||||
stdlog->logFile->write(stdlog->logFile, buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
void mStandardLoggerInit(struct mStandardLogger* logger) {
|
||||
logger->d.log = _mCoreStandardLog;
|
||||
logger->d.filter = malloc(sizeof(struct mLogFilter));
|
||||
mLogFilterInit(logger->d.filter);
|
||||
}
|
||||
|
||||
void mStandardLoggerDeinit(struct mStandardLogger* logger) {
|
||||
if (logger->d.filter) {
|
||||
mLogFilterDeinit(logger->d.filter);
|
||||
free(logger->d.filter);
|
||||
logger->d.filter = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void mStandardLoggerConfig(struct mStandardLogger* logger, struct mCoreConfig* config) {
|
||||
bool logToFile = false;
|
||||
const char* logFile = mCoreConfigGetValue(config, "logFile");
|
||||
mCoreConfigGetBoolValue(config, "logToStdout", &logger->logToStdout);
|
||||
mCoreConfigGetBoolValue(config, "logToFile", &logToFile);
|
||||
|
||||
if (logToFile && logFile) {
|
||||
logger->logFile = VFileOpen(logFile, O_WRONLY | O_CREAT | O_APPEND);
|
||||
}
|
||||
|
||||
mLogFilterLoad(logger->d.filter, config);
|
||||
}
|
||||
|
||||
mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
|
||||
|
|
|
@ -215,19 +215,19 @@ static struct mScriptValue* mScriptMemoryDomainReadRange(struct mScriptMemoryDom
|
|||
static void mScriptMemoryDomainWrite8(struct mScriptMemoryDomain* adapter, uint32_t address, uint8_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite8(adapter->core, address, segmentAddress, value);
|
||||
adapter->core->rawWrite8(adapter->core, segmentAddress, segment, value);
|
||||
}
|
||||
|
||||
static void mScriptMemoryDomainWrite16(struct mScriptMemoryDomain* adapter, uint32_t address, uint16_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite16(adapter->core, address, segmentAddress, value);
|
||||
adapter->core->rawWrite16(adapter->core, segmentAddress, segment, value);
|
||||
}
|
||||
|
||||
static void mScriptMemoryDomainWrite32(struct mScriptMemoryDomain* adapter, uint32_t address, uint32_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite32(adapter->core, address, segmentAddress, value);
|
||||
adapter->core->rawWrite32(adapter->core, segmentAddress, segment, value);
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainBase(struct mScriptMemoryDomain* adapter) {
|
||||
|
@ -424,6 +424,7 @@ mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0);
|
|||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameTitle, _mScriptCoreGetGameTitle, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameCode, _mScriptCoreGetGameCode, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S64, romSize, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WSTR, checksum, _mScriptCoreChecksum, 1, S32, type);
|
||||
|
||||
// Run functions
|
||||
|
@ -483,6 +484,8 @@ mSCRIPT_DEFINE_STRUCT(mCore)
|
|||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the size of the loaded ROM")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, romSize)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the checksum of the loaded ROM")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, checksum)
|
||||
|
||||
|
|
|
@ -253,10 +253,13 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
core->setSync(core, &threadContext->impl->sync);
|
||||
|
||||
struct mLogFilter filter;
|
||||
if (!threadContext->logger.d.filter) {
|
||||
threadContext->logger.d.filter = &filter;
|
||||
mLogFilterInit(threadContext->logger.d.filter);
|
||||
mLogFilterLoad(threadContext->logger.d.filter, &core->config);
|
||||
struct mLogger* logger = &threadContext->logger.d;
|
||||
if (threadContext->logger.logger) {
|
||||
logger->filter = threadContext->logger.logger->filter;
|
||||
} else {
|
||||
logger->filter = &filter;
|
||||
mLogFilterInit(logger->filter);
|
||||
mLogFilterLoad(logger->filter, &core->config);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
|
@ -431,10 +434,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
#endif
|
||||
core->clearCoreCallbacks(core);
|
||||
|
||||
if (threadContext->logger.d.filter == &filter) {
|
||||
if (logger->filter == &filter) {
|
||||
mLogFilterDeinit(&filter);
|
||||
}
|
||||
threadContext->logger.d.filter = NULL;
|
||||
logger->filter = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -444,10 +447,8 @@ bool mCoreThreadStart(struct mCoreThread* threadContext) {
|
|||
threadContext->impl->state = mTHREAD_INITIALIZED;
|
||||
threadContext->impl->requested = 0;
|
||||
threadContext->logger.p = threadContext;
|
||||
if (!threadContext->logger.d.log) {
|
||||
threadContext->logger.d.log = _mCoreLog;
|
||||
threadContext->logger.d.filter = NULL;
|
||||
}
|
||||
threadContext->logger.d.log = _mCoreLog;
|
||||
threadContext->logger.d.filter = NULL;
|
||||
|
||||
if (!threadContext->impl->sync.fpsTarget) {
|
||||
threadContext->impl->sync.fpsTarget = _defaultFPSTarget;
|
||||
|
@ -718,14 +719,17 @@ struct mCoreThread* mCoreThreadGet(void) {
|
|||
}
|
||||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(logger);
|
||||
UNUSED(level);
|
||||
printf("%s: ", mLogCategoryName(category));
|
||||
vprintf(format, args);
|
||||
printf("\n");
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
if (thread && level == mLOG_FATAL) {
|
||||
mCoreThreadMarkCrashed(thread);
|
||||
struct mThreadLogger* threadLogger = (struct mThreadLogger*) logger;
|
||||
if (level == mLOG_FATAL) {
|
||||
mCoreThreadMarkCrashed(threadLogger->p);
|
||||
}
|
||||
if (!threadLogger->p->logger.logger) {
|
||||
printf("%s: ", mLogCategoryName(category));
|
||||
vprintf(format, args);
|
||||
printf("\n");
|
||||
} else {
|
||||
logger = threadLogger->p->logger.logger;
|
||||
logger->log(logger, category, level, format, args);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -588,7 +588,7 @@ static struct ParseTree* _parseTree(const char** string) {
|
|||
}
|
||||
struct ParseTree* tree = NULL;
|
||||
if (!error) {
|
||||
tree = malloc(sizeof(*tree));
|
||||
tree = parseTreeCreate();
|
||||
parseLexedExpression(tree, &lv);
|
||||
}
|
||||
lexFree(&lv);
|
||||
|
@ -796,17 +796,16 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
|
|||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
|
||||
struct ParseTree tree;
|
||||
parseLexedExpression(&tree, &lv);
|
||||
if (tree.token.type == TOKEN_ERROR_TYPE) {
|
||||
struct ParseTree* tree = parseTreeCreate();
|
||||
if (!parseLexedExpression(tree, &lv)) {
|
||||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
} else {
|
||||
if (!mDebuggerEvaluateParseTree(&debugger->d, &tree, &dvTemp.intValue, &dvTemp.segmentValue)) {
|
||||
if (!mDebuggerEvaluateParseTree(&debugger->d, tree, &dvTemp.intValue, &dvTemp.segmentValue)) {
|
||||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
parseFree(&tree);
|
||||
parseFree(tree);
|
||||
|
||||
lexFree(&lv);
|
||||
LexVectorDeinit(&lv);
|
||||
|
|
|
@ -495,7 +495,7 @@ static const int _operatorPrecedence[] = {
|
|||
[OP_DEREFERENCE] = 2,
|
||||
};
|
||||
|
||||
static struct ParseTree* _parseTreeCreate(void) {
|
||||
struct ParseTree* parseTreeCreate(void) {
|
||||
struct ParseTree* tree = malloc(sizeof(struct ParseTree));
|
||||
tree->token.type = TOKEN_ERROR_TYPE;
|
||||
tree->p = NULL;
|
||||
|
@ -529,12 +529,12 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int
|
|||
}
|
||||
break;
|
||||
case TOKEN_SEGMENT_TYPE:
|
||||
tree->lhs = _parseTreeCreate();
|
||||
tree->lhs = parseTreeCreate();
|
||||
tree->lhs->token.type = TOKEN_UINT_TYPE;
|
||||
tree->lhs->token.uintValue = token->uintValue;
|
||||
tree->lhs->p = tree;
|
||||
tree->lhs->precedence = precedence;
|
||||
tree->rhs = _parseTreeCreate();
|
||||
tree->rhs = parseTreeCreate();
|
||||
tree->rhs->p = tree;
|
||||
tree->rhs->precedence = precedence;
|
||||
tree->token.type = TOKEN_SEGMENT_TYPE;
|
||||
|
@ -572,7 +572,7 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int
|
|||
}
|
||||
newPrecedence = _operatorPrecedence[token->operatorValue];
|
||||
if (newPrecedence < precedence) {
|
||||
newTree = _parseTreeCreate();
|
||||
newTree = parseTreeCreate();
|
||||
memcpy(newTree, tree, sizeof(*tree));
|
||||
if (newTree->lhs) {
|
||||
newTree->lhs->p = newTree;
|
||||
|
@ -582,7 +582,7 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int
|
|||
}
|
||||
newTree->p = tree;
|
||||
tree->lhs = newTree;
|
||||
tree->rhs = _parseTreeCreate();
|
||||
tree->rhs = parseTreeCreate();
|
||||
tree->rhs->p = tree;
|
||||
tree->rhs->precedence = newPrecedence;
|
||||
precedence = newPrecedence;
|
||||
|
@ -617,9 +617,9 @@ static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, int
|
|||
return i;
|
||||
}
|
||||
|
||||
void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) {
|
||||
bool parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) {
|
||||
if (!tree) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
tree->token.type = TOKEN_ERROR_TYPE;
|
||||
|
@ -636,6 +636,7 @@ void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) {
|
|||
}
|
||||
tree->token.type = TOKEN_ERROR_TYPE;
|
||||
}
|
||||
return tree->token.type != TOKEN_ERROR_TYPE;
|
||||
}
|
||||
|
||||
void lexFree(struct LexVector* lv) {
|
||||
|
|
|
@ -260,8 +260,16 @@ bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
|
|||
|
||||
free((void*) buffer.name);
|
||||
free((void*) buffer.romName);
|
||||
free((void*) dbType);
|
||||
free((void*) dbVersion);
|
||||
|
||||
if (dbType) {
|
||||
free(dbType);
|
||||
}
|
||||
if (dbVersion) {
|
||||
free(dbVersion);
|
||||
}
|
||||
if (fieldName) {
|
||||
free(fieldName);
|
||||
}
|
||||
|
||||
sqlite3_finalize(gamedbTable);
|
||||
sqlite3_finalize(gamedbDrop);
|
||||
|
|
|
@ -92,7 +92,7 @@ bool mUpdaterInit(struct mUpdaterContext* context, const char* manifest) {
|
|||
ConfigurationInit(&context->manifest);
|
||||
|
||||
struct VFile* vf = VFileFromConstMemory(manifest, strlen(manifest) + 1);
|
||||
bool success = vf && ConfigurationReadVFile(&context->manifest, vf);
|
||||
bool success = ConfigurationReadVFile(&context->manifest, vf);
|
||||
vf->close(vf);
|
||||
if (!success) {
|
||||
ConfigurationDeinit(&context->manifest);
|
||||
|
|
|
@ -473,8 +473,16 @@ static void _GBCoreUnloadROM(struct mCore* core) {
|
|||
GBUnloadROM(core->board);
|
||||
}
|
||||
|
||||
static size_t _GBCoreROMSize(const struct mCore* core) {
|
||||
const struct GB* gb = (const struct GB*) core->board;
|
||||
if (gb->romVf) {
|
||||
return gb->romVf->size(gb->romVf);
|
||||
}
|
||||
return gb->pristineRomSize;
|
||||
}
|
||||
|
||||
static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
|
||||
struct GB* gb = (struct GB*) core->board;
|
||||
const struct GB* gb = (const struct GB*) core->board;
|
||||
switch (type) {
|
||||
case mCHECKSUM_CRC32:
|
||||
memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
|
||||
|
@ -865,59 +873,59 @@ static bool _GBCoreReadRegister(const struct mCore* core, const char* name, void
|
|||
uint16_t* value16 = out;
|
||||
uint8_t* value8 = out;
|
||||
|
||||
if (strcmp(name, "b") == 0) {
|
||||
if (strcasecmp(name, "b") == 0) {
|
||||
*value8 = cpu->b;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "c") == 0) {
|
||||
if (strcasecmp(name, "c") == 0) {
|
||||
*value8 = cpu->c;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "d") == 0) {
|
||||
if (strcasecmp(name, "d") == 0) {
|
||||
*value8 = cpu->d;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "e") == 0) {
|
||||
if (strcasecmp(name, "e") == 0) {
|
||||
*value8 = cpu->e;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "a") == 0) {
|
||||
if (strcasecmp(name, "a") == 0) {
|
||||
*value8 = cpu->a;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "f") == 0) {
|
||||
if (strcasecmp(name, "f") == 0) {
|
||||
*value8 = cpu->f.packed;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "h") == 0) {
|
||||
if (strcasecmp(name, "h") == 0) {
|
||||
*value8 = cpu->h;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "l") == 0) {
|
||||
if (strcasecmp(name, "l") == 0) {
|
||||
*value8 = cpu->l;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "bc") == 0) {
|
||||
if (strcasecmp(name, "bc") == 0) {
|
||||
*value16 = cpu->bc;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "de") == 0) {
|
||||
if (strcasecmp(name, "de") == 0) {
|
||||
*value16 = cpu->de;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "hl") == 0) {
|
||||
if (strcasecmp(name, "hl") == 0) {
|
||||
*value16 = cpu->hl;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "af") == 0) {
|
||||
if (strcasecmp(name, "af") == 0) {
|
||||
*value16 = cpu->af;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "pc") == 0) {
|
||||
if (strcasecmp(name, "pc") == 0) {
|
||||
*value16 = cpu->pc;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "sp") == 0) {
|
||||
if (strcasecmp(name, "sp") == 0) {
|
||||
*value16 = cpu->sp;
|
||||
return true;
|
||||
}
|
||||
|
@ -1237,6 +1245,7 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->loadTemporarySave = _GBCoreLoadTemporarySave;
|
||||
core->loadPatch = _GBCoreLoadPatch;
|
||||
core->unloadROM = _GBCoreUnloadROM;
|
||||
core->romSize = _GBCoreROMSize;
|
||||
core->checksum = _GBCoreChecksum;
|
||||
core->reset = _GBCoreReset;
|
||||
core->runFrame = _GBCoreRunFrame;
|
||||
|
|
|
@ -446,6 +446,7 @@ static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
|
|||
#ifdef BUILD_GLES3
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->glRenderer.outputTex = texid;
|
||||
gbacore->glRenderer.outputTexDirty = true;
|
||||
#else
|
||||
UNUSED(core);
|
||||
UNUSED(texid);
|
||||
|
@ -568,8 +569,16 @@ static void _GBACoreUnloadROM(struct mCore* core) {
|
|||
GBAUnloadROM(core->board);
|
||||
}
|
||||
|
||||
static size_t _GBACoreROMSize(const struct mCore* core) {
|
||||
const struct GBA* gba = (const struct GBA*) core->board;
|
||||
if (gba->romVf) {
|
||||
return gba->romVf->size(gba->romVf);
|
||||
}
|
||||
return gba->pristineRomSize;
|
||||
}
|
||||
|
||||
static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
|
||||
struct GBA* gba = (struct GBA*) core->board;
|
||||
const struct GBA* gba = (const struct GBA*) core->board;
|
||||
switch (type) {
|
||||
case mCHECKSUM_CRC32:
|
||||
memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
|
||||
|
@ -1362,6 +1371,7 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->loadTemporarySave = _GBACoreLoadTemporarySave;
|
||||
core->loadPatch = _GBACoreLoadPatch;
|
||||
core->unloadROM = _GBACoreUnloadROM;
|
||||
core->romSize = _GBACoreROMSize;
|
||||
core->checksum = _GBACoreChecksum;
|
||||
core->reset = _GBACoreReset;
|
||||
core->runFrame = _GBACoreRunFrame;
|
||||
|
|
|
@ -257,7 +257,7 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||
|
||||
|
||||
if (gba->romVf && gba->pristineRomSize > SIZE_CART0) {
|
||||
if (gba->romVf && gba->romVf->size(gba->romVf) > SIZE_CART0) {
|
||||
char ident;
|
||||
gba->romVf->seek(gba->romVf, 0xAC, SEEK_SET);
|
||||
gba->romVf->read(gba->romVf, &ident, 1);
|
||||
|
|
|
@ -779,6 +779,7 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) {
|
|||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
|
||||
_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
|
||||
glRenderer->outputTexDirty = false;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
|
@ -1678,6 +1679,10 @@ static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, u
|
|||
void _finalizeLayers(struct GBAVideoGLRenderer* renderer) {
|
||||
const GLuint* uniforms = renderer->finalizeShader.uniforms;
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OUTPUT]);
|
||||
if (renderer->outputTexDirty) {
|
||||
_initFramebufferTexture(renderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, renderer->scale);
|
||||
renderer->outputTexDirty = false;
|
||||
}
|
||||
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
|
||||
glScissor(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
|
||||
if (GBARegisterDISPCNTIsForcedBlank(renderer->dispcnt)) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutScreen</class>
|
||||
<widget class="QWidget" name="AboutScreen">
|
||||
<class>QGBA::AboutScreen</class>
|
||||
<widget class="QWidget" name="QGBA::AboutScreen">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ApplicationUpdatePrompt</class>
|
||||
<widget class="QDialog" name="ApplicationUpdatePrompt">
|
||||
<class>QGBA::ApplicationUpdatePrompt</class>
|
||||
<widget class="QDialog" name="QGBA::ApplicationUpdatePrompt">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -58,7 +58,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ApplicationUpdatePrompt</receiver>
|
||||
<receiver>QGBA::ApplicationUpdatePrompt</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ArchiveInspector</class>
|
||||
<widget class="QDialog" name="ArchiveInspector">
|
||||
<class>QGBA::ArchiveInspector</class>
|
||||
<widget class="QDialog" name="QGBA::ArchiveInspector">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -46,7 +46,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ArchiveInspector</receiver>
|
||||
<receiver>QGBA::ArchiveInspector</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -62,7 +62,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ArchiveInspector</receiver>
|
||||
<receiver>QGBA::ArchiveInspector</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -82,6 +82,9 @@ void AssetTile::setBoundary(int boundary, int set0, int set1) {
|
|||
}
|
||||
|
||||
void AssetTile::selectIndex(int index) {
|
||||
if (index > m_maxTile) {
|
||||
return;
|
||||
}
|
||||
m_index = index;
|
||||
const color_t* data;
|
||||
mTileCache* tileCache = m_tileCaches[index >= m_boundary];
|
||||
|
@ -141,3 +144,7 @@ void AssetTile::selectColor(int index) {
|
|||
m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0')));
|
||||
m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0')));
|
||||
}
|
||||
|
||||
void AssetTile::setMaxTile(int tile) {
|
||||
m_maxTile = tile;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public slots:
|
|||
void selectIndex(int);
|
||||
void setFlip(bool h, bool v);
|
||||
void selectColor(int);
|
||||
void setMaxTile(int);
|
||||
|
||||
protected:
|
||||
int customLocation(const QString& id = {}) override;
|
||||
|
@ -45,6 +46,7 @@ private:
|
|||
int m_addressBase;
|
||||
int m_boundary;
|
||||
int m_boundaryBase;
|
||||
int m_maxTile;
|
||||
bool m_flipH = false;
|
||||
bool m_flipV = false;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AssetTile</class>
|
||||
<widget class="QGBA::AssetInfo" name="AssetTile">
|
||||
<class>QGBA::AssetTile</class>
|
||||
<widget class="QGBA::AssetInfo" name="QGBA::AssetTile">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BattleChipView</class>
|
||||
<widget class="QDialog" name="BattleChipView">
|
||||
<class>QGBA::BattleChipView</class>
|
||||
<widget class="QDialog" name="QGBA::BattleChipView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -238,7 +238,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>BattleChipView</receiver>
|
||||
<receiver>QGBA::BattleChipView</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -330,17 +330,17 @@ if(${QT}LinguistTools_FOUND)
|
|||
file(GLOB TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/ts/${BINARY_NAME}-*.ts")
|
||||
if(UPDATE_TRANSLATIONS)
|
||||
if(TARGET Qt6::Core)
|
||||
qt_create_translation(TRANSLATION_FILES ${SOURCE_FILES} ${UI_FILES} ${TS_FILES} OPTIONS -locations absolute -no-obsolete)
|
||||
qt_create_translation(TRANSLATION_FILES ${SOURCE_FILES} ${UI_FILES} ${TS_FILES} ${CMAKE_CURRENT_SOURCE_DIR} OPTIONS -locations absolute -no-obsolete)
|
||||
else()
|
||||
qt5_create_translation(TRANSLATION_FILES ${SOURCE_FILES} ${UI_FILES} ${TS_FILES} OPTIONS -locations absolute -no-obsolete)
|
||||
qt5_create_translation(TRANSLATION_FILES ${SOURCE_FILES} ${UI_FILES} ${TS_FILES} ${CMAKE_CURRENT_SOURCE_DIR} OPTIONS -locations absolute -no-obsolete)
|
||||
endif()
|
||||
list(REMOVE_ITEM TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/ts/${BINARY_NAME}-template.ts")
|
||||
else()
|
||||
list(REMOVE_ITEM TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/ts/${BINARY_NAME}-template.ts")
|
||||
if(TARGET Qt6::Core)
|
||||
qt_add_translation(TRANSLATION_FILES ${TS_FILES})
|
||||
qt_add_translation(TRANSLATION_FILES ${TS_FILES} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
else()
|
||||
qt5_add_translation(TRANSLATION_FILES ${TS_FILES})
|
||||
qt5_add_translation(TRANSLATION_FILES ${TS_FILES} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
set(QT_QM_FILES)
|
||||
|
@ -358,9 +358,14 @@ if(${QT}LinguistTools_FOUND)
|
|||
endforeach()
|
||||
list(APPEND TRANSLATION_FILES ${QT_QM_FILES})
|
||||
endif()
|
||||
add_custom_command(OUTPUT ${TRANSLATION_QRC}
|
||||
COMMAND ${CMAKE_COMMAND} -DTRANSLATION_QRC:FILEPATH="${TRANSLATION_QRC}" -DQM_BASE="${CMAKE_CURRENT_BINARY_DIR}" "-DTRANSLATION_FILES='${TRANSLATION_FILES}'" -P "${CMAKE_CURRENT_SOURCE_DIR}/ts.cmake"
|
||||
DEPENDS ${TRANSLATION_FILES})
|
||||
|
||||
file(WRITE ${TRANSLATION_QRC} "<RCC>\n\t<qresource prefix=\"/translations/\">\n")
|
||||
foreach(TS ${TRANSLATION_FILES})
|
||||
get_filename_component(TS_BASE "${TS}" NAME)
|
||||
file(APPEND ${TRANSLATION_QRC} "\t\t<file alias=\"${TS_BASE}\">${TS}</file>\n")
|
||||
endforeach()
|
||||
file(APPEND ${TRANSLATION_QRC} "\t</qresource>\n</RCC>")
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
qt_add_resources(TRANSLATION_RESOURCES ${TRANSLATION_QRC})
|
||||
else()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CheatsView</class>
|
||||
<widget class="QWidget" name="CheatsView">
|
||||
<class>QGBA::CheatsView</class>
|
||||
<widget class="QWidget" name="QGBA::CheatsView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -144,19 +144,19 @@ CoreController::CoreController(mCore* core, QObject* parent)
|
|||
QMetaObject::invokeMethod(controller, "unpaused");
|
||||
};
|
||||
|
||||
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;
|
||||
m_logger.self = this;
|
||||
m_logger.log = [](mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
CoreLogger* logContext = static_cast<CoreLogger*>(logger);
|
||||
|
||||
static const char* savestateMessage = "State %i saved";
|
||||
static const char* loadstateMessage = "State %i loaded";
|
||||
static const char* savestateFailedMessage = "State %i failed to load";
|
||||
static int biosCat = -1;
|
||||
static int statusCat = -1;
|
||||
if (!context) {
|
||||
if (!logContext) {
|
||||
return;
|
||||
}
|
||||
CoreController* controller = static_cast<CoreController*>(context->userData);
|
||||
CoreController* controller = logContext->self;
|
||||
QString message;
|
||||
if (biosCat < 0) {
|
||||
biosCat = mLogCategoryById("gba.bios");
|
||||
|
@ -201,10 +201,10 @@ CoreController::CoreController(mCore* core, QObject* parent)
|
|||
message = QString::vasprintf(format, args);
|
||||
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
|
||||
if (level == mLOG_FATAL) {
|
||||
mCoreThreadMarkCrashed(controller->thread());
|
||||
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, message));
|
||||
}
|
||||
};
|
||||
m_threadContext.logger.logger = &m_logger;
|
||||
}
|
||||
|
||||
CoreController::~CoreController() {
|
||||
|
@ -424,7 +424,7 @@ void CoreController::setInputController(InputController* inputController) {
|
|||
void CoreController::setLogger(LogController* logger) {
|
||||
disconnect(m_log);
|
||||
m_log = logger;
|
||||
m_threadContext.logger.d.filter = logger->filter();
|
||||
m_logger.filter = logger->filter();
|
||||
connect(this, &CoreController::logPosted, m_log, &LogController::postLog);
|
||||
}
|
||||
|
||||
|
@ -1238,24 +1238,21 @@ void CoreController::updateFastForward() {
|
|||
if (m_fastForwardMute >= 0) {
|
||||
m_threadContext.core->opts.mute = m_fastForwardMute || m_mute;
|
||||
}
|
||||
setSync(false);
|
||||
|
||||
// If we aren't holding the fast forward button
|
||||
// then use the non "(held)" ratio
|
||||
if(!m_fastForward) {
|
||||
if (m_fastForwardRatio > 0) {
|
||||
m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_fastForwardRatio;
|
||||
setSync(true);
|
||||
} else {
|
||||
setSync(false);
|
||||
m_threadContext.impl->sync.audioWait = true;
|
||||
}
|
||||
} else {
|
||||
// If we are holding the fast forward button,
|
||||
// then use the held ratio
|
||||
if (m_fastForwardHeldRatio > 0) {
|
||||
m_threadContext.impl->sync.fpsTarget = m_fpsTarget * m_fastForwardHeldRatio;
|
||||
setSync(true);
|
||||
} else {
|
||||
setSync(false);
|
||||
m_threadContext.impl->sync.audioWait = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1358,3 +1355,7 @@ void CoreController::Interrupter::resume(CoreController* controller) {
|
|||
|
||||
mCoreThreadContinue(controller->thread());
|
||||
}
|
||||
|
||||
bool CoreController::Interrupter::held() const {
|
||||
return m_parent && m_parent->thread()->impl;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
void interrupt(std::shared_ptr<CoreController>);
|
||||
void resume();
|
||||
|
||||
bool held() const;
|
||||
|
||||
private:
|
||||
void interrupt();
|
||||
void resume(CoreController*);
|
||||
|
@ -239,6 +241,9 @@ private:
|
|||
void updateROMInfo();
|
||||
|
||||
mCoreThread m_threadContext{};
|
||||
struct CoreLogger : public mLogger {
|
||||
CoreController* self;
|
||||
} m_logger{};
|
||||
|
||||
bool m_patched = false;
|
||||
bool m_preload = false;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DebuggerConsole</class>
|
||||
<widget class="QWidget" name="DebuggerConsole">
|
||||
<class>QGBA::DebuggerConsole</class>
|
||||
<widget class="QWidget" name="QGBA::DebuggerConsole">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -285,7 +285,7 @@ void DisplayGL::stopDrawing() {
|
|||
m_isDrawing = false;
|
||||
m_hasStarted = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "stop", Qt::BlockingQueuedConnection);
|
||||
m_painter->stop();
|
||||
if (m_gl) {
|
||||
hide();
|
||||
}
|
||||
|
@ -457,8 +457,7 @@ void PainterGL::create() {
|
|||
m_paintDev = std::make_unique<QOpenGLPaintDevice>();
|
||||
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
auto version = m_format.version();
|
||||
if (version >= qMakePair(2, 0)) {
|
||||
if (m_supportsShaders) {
|
||||
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
|
||||
mGLES2ContextCreate(gl2Backend);
|
||||
m_backend = &gl2Backend->d;
|
||||
|
@ -626,6 +625,15 @@ void PainterGL::draw() {
|
|||
if (!m_started || m_queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_interrupter.held()) {
|
||||
// A resize event is pending; that needs to happen first
|
||||
if (!m_drawTimer.isActive()) {
|
||||
m_drawTimer.start(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mCoreSync* sync = &m_context->thread()->impl->sync;
|
||||
if (!mCoreSyncWaitFrameStart(sync)) {
|
||||
mCoreSyncWaitFrameEnd(sync);
|
||||
|
@ -676,6 +684,11 @@ void PainterGL::forceDraw() {
|
|||
}
|
||||
|
||||
void PainterGL::stop() {
|
||||
m_started = false;
|
||||
QMetaObject::invokeMethod(this, "doStop", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
void PainterGL::doStop() {
|
||||
m_drawTimer.stop();
|
||||
m_active = false;
|
||||
m_started = false;
|
||||
|
|
|
@ -135,6 +135,8 @@ public:
|
|||
void setMessagePainter(MessagePainter*);
|
||||
void enqueue(const uint32_t* backing);
|
||||
|
||||
void stop();
|
||||
|
||||
bool supportsShaders() const { return m_supportsShaders; }
|
||||
int glTex();
|
||||
|
||||
|
@ -148,7 +150,6 @@ public slots:
|
|||
void forceDraw();
|
||||
void draw();
|
||||
void start();
|
||||
void stop();
|
||||
void pause();
|
||||
void unpause();
|
||||
void resize(const QSize& size);
|
||||
|
@ -167,6 +168,9 @@ public slots:
|
|||
signals:
|
||||
void started();
|
||||
|
||||
private slots:
|
||||
void doStop();
|
||||
|
||||
private:
|
||||
void makeCurrent();
|
||||
void performDraw();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DolphinConnector</class>
|
||||
<widget class="QDialog" name="DolphinConnector">
|
||||
<class>QGBA::DolphinConnector</class>
|
||||
<widget class="QDialog" name="QGBA::DolphinConnector">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -98,7 +98,7 @@
|
|||
<connection>
|
||||
<sender>close</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>DolphinConnector</receiver>
|
||||
<receiver>QGBA::DolphinConnector</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FrameView</class>
|
||||
<widget class="QWidget" name="FrameView">
|
||||
<class>QGBA::FrameView</class>
|
||||
<widget class="QWidget" name="QGBA::FrameView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GIFView</class>
|
||||
<widget class="QWidget" name="GIFView">
|
||||
<class>QGBA::GIFView</class>
|
||||
<widget class="QWidget" name="QGBA::GIFView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -180,7 +180,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>GIFView</receiver>
|
||||
<receiver>QGBA::GIFView</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -44,9 +44,7 @@ static const QList<GBMemoryBankControllerType> s_mbcList{
|
|||
static QMap<GBModel, QString> s_gbModelNames;
|
||||
static QMap<GBMemoryBankControllerType, QString> s_mbcNames;
|
||||
|
||||
static QString tr(const char* str) {
|
||||
return QCoreApplication::translate("Game Boy", str);
|
||||
}
|
||||
#define tr(STR) QCoreApplication::translate("QGBA::GameBoy", STR)
|
||||
|
||||
QList<GBModel> GameBoy::modelList() {
|
||||
return s_gbModelList;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>IOViewer</class>
|
||||
<widget class="QWidget" name="IOViewer">
|
||||
<class>QGBA::IOViewer</class>
|
||||
<widget class="QWidget" name="QGBA::IOViewer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoadSaveState</class>
|
||||
<widget class="QWidget" name="LoadSaveState">
|
||||
<class>QGBA::LoadSaveState</class>
|
||||
<widget class="QWidget" name="QGBA::LoadSaveState">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LogView</class>
|
||||
<widget class="QWidget" name="LogView">
|
||||
<class>QGBA::LogView</class>
|
||||
<widget class="QWidget" name="QGBA::LogView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -42,6 +42,7 @@ MapView::MapView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
m_boundary = 2048;
|
||||
m_ui.tile->setMaxTile(3096);
|
||||
m_addressBase = BASE_VRAM;
|
||||
m_addressWidth = 8;
|
||||
m_ui.bgInfo->addCustomProperty("priority", tr("Priority"));
|
||||
|
@ -55,6 +56,7 @@ MapView::MapView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
#ifdef M_CORE_GB
|
||||
case mPLATFORM_GB:
|
||||
m_boundary = 1024;
|
||||
m_ui.tile->setMaxTile(512);
|
||||
m_addressBase = GB_BASE_VRAM;
|
||||
m_addressWidth = 4;
|
||||
m_ui.bgInfo->addCustomProperty("screenBase", tr("Map base"));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapView</class>
|
||||
<widget class="QWidget" name="MapView">
|
||||
<class>QGBA::MapView</class>
|
||||
<widget class="QWidget" name="QGBA::MapView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MemoryDump</class>
|
||||
<widget class="QDialog" name="MemoryDump">
|
||||
<class>QGBA::MemoryDump</class>
|
||||
<widget class="QDialog" name="QGBA::MemoryDump">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -126,7 +126,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>MemoryDump</receiver>
|
||||
<receiver>QGBA::MemoryDump</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -142,7 +142,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MemoryDump</receiver>
|
||||
<receiver>QGBA::MemoryDump</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MemorySearch</class>
|
||||
<widget class="QWidget" name="MemorySearch">
|
||||
<class>QGBA::MemorySearch</class>
|
||||
<widget class="QWidget" name="QGBA::MemorySearch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -367,7 +367,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MemorySearch</receiver>
|
||||
<receiver>QGBA::MemorySearch</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MemoryView</class>
|
||||
<widget class="QWidget" name="MemoryView">
|
||||
<class>QGBA::MemoryView</class>
|
||||
<widget class="QWidget" name="QGBA::MemoryView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -116,14 +116,16 @@ void ObjView::updateTilesGBA(bool force) {
|
|||
if (GBAObjAttributesAIs256Color(obj->a)) {
|
||||
m_ui.palette->setText("256-color");
|
||||
m_ui.tile->setBoundary(1024, 1, 3);
|
||||
m_ui.tile->setPalette(0);
|
||||
m_boundary = 1024;
|
||||
tileBase *= 2;
|
||||
m_ui.tile->setMaxTile(1536);
|
||||
m_ui.tile->setPalette(0);
|
||||
} else {
|
||||
m_ui.palette->setText(QString::number(newInfo.paletteId));
|
||||
m_ui.tile->setBoundary(2048, 0, 2);
|
||||
m_ui.tile->setPalette(newInfo.paletteId);
|
||||
m_boundary = 2048;
|
||||
m_ui.tile->setMaxTile(3072);
|
||||
m_ui.tile->setPalette(newInfo.paletteId);
|
||||
}
|
||||
if (newInfo != m_objInfo) {
|
||||
force = true;
|
||||
|
@ -225,6 +227,7 @@ void ObjView::updateTilesGB(bool force) {
|
|||
m_objInfo = newInfo;
|
||||
m_tileOffset = tile;
|
||||
m_boundary = 1024;
|
||||
m_ui.tile->setMaxTile(512);
|
||||
|
||||
int i = 0;
|
||||
m_ui.tile->setPalette(newInfo.paletteId);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ObjView</class>
|
||||
<widget class="QWidget" name="ObjView">
|
||||
<class>QGBA::ObjView</class>
|
||||
<widget class="QWidget" name="QGBA::ObjView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OverrideView</class>
|
||||
<widget class="QWidget" name="OverrideView">
|
||||
<class>QGBA::OverrideView</class>
|
||||
<widget class="QWidget" name="QGBA::OverrideView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PaletteView</class>
|
||||
<widget class="QWidget" name="PaletteView">
|
||||
<class>QGBA::PaletteView</class>
|
||||
<widget class="QWidget" name="QGBA::PaletteView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlacementControl</class>
|
||||
<widget class="QDialog" name="PlacementControl">
|
||||
<class>QGBA::PlacementControl</class>
|
||||
<widget class="QDialog" name="QGBA::PlacementControl">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PrinterView</class>
|
||||
<widget class="QWidget" name="PrinterView">
|
||||
<class>QGBA::PrinterView</class>
|
||||
<widget class="QWidget" name="QGBA::PrinterView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -250,7 +250,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PrinterView</receiver>
|
||||
<receiver>QGBA::PrinterView</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -9,12 +9,6 @@
|
|||
#include "CoreController.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#endif
|
||||
#ifdef USE_SQLITE3
|
||||
#include "feature/sqlite3/no-intro.h"
|
||||
#endif
|
||||
|
@ -46,25 +40,8 @@ ROMInfo::ROMInfo(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
|
||||
core->checksum(core, &crc32, mCHECKSUM_CRC32);
|
||||
|
||||
switch (controller->thread()->core->platform(controller->thread()->core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA: {
|
||||
GBA* gba = static_cast<GBA*>(core->board);
|
||||
m_ui.size->setText(QString::number(gba->pristineRomSize) + tr(" bytes"));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case mPLATFORM_GB: {
|
||||
GB* gb = static_cast<GB*>(core->board);
|
||||
m_ui.size->setText(QString::number(gb->pristineRomSize) + tr(" bytes"));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
m_ui.size->setText(tr("(unknown)"));
|
||||
break;
|
||||
}
|
||||
m_ui.size->setText(QString::number(core->romSize(core)) + tr(" bytes"));
|
||||
|
||||
if (crc32) {
|
||||
m_ui.crc->setText(QString::number(crc32, 16));
|
||||
#ifdef USE_SQLITE3
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ROMInfo</class>
|
||||
<widget class="QDialog" name="ROMInfo">
|
||||
<class>QGBA::ROMInfo</class>
|
||||
<widget class="QDialog" name="QGBA::ROMInfo">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ReportView</class>
|
||||
<widget class="QDialog" name="ReportView">
|
||||
<class>QGBA::ReportView</class>
|
||||
<widget class="QDialog" name="QGBA::ReportView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -163,7 +163,7 @@
|
|||
<connection>
|
||||
<sender>openList</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ReportView</receiver>
|
||||
<receiver>QGBA::ReportView</receiver>
|
||||
<slot>openBugReportPage()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -179,7 +179,7 @@
|
|||
<connection>
|
||||
<sender>generate</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ReportView</receiver>
|
||||
<receiver>QGBA::ReportView</receiver>
|
||||
<slot>generateReport()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -195,7 +195,7 @@
|
|||
<connection>
|
||||
<sender>save</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ReportView</receiver>
|
||||
<receiver>QGBA::ReportView</receiver>
|
||||
<slot>save()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -447,14 +447,14 @@ SaveConverter::AnnotatedSave::operator QString() const {
|
|||
QString typeFormat("%1");
|
||||
QString endianStr;
|
||||
QString saveType;
|
||||
QString format = QCoreApplication::translate("SaveConverter", "%1 %2 save game");
|
||||
QString format = QCoreApplication::translate("QGBA::SaveConverter", "%1 %2 save game");
|
||||
|
||||
switch (endianness) {
|
||||
case Endian::LITTLE:
|
||||
endianStr = QCoreApplication::translate("SaveConverter", "little endian");
|
||||
endianStr = QCoreApplication::translate("QGBA::SaveConverter", "little endian");
|
||||
break;
|
||||
case Endian::BIG:
|
||||
endianStr = QCoreApplication::translate("SaveConverter", "big endian");
|
||||
endianStr = QCoreApplication::translate("QGBA::SaveConverter", "big endian");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -465,15 +465,15 @@ SaveConverter::AnnotatedSave::operator QString() const {
|
|||
case mPLATFORM_GBA:
|
||||
switch (gba.type) {
|
||||
case SAVEDATA_SRAM:
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "SRAM");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "SRAM");
|
||||
break;
|
||||
case SAVEDATA_FLASH512:
|
||||
case SAVEDATA_FLASH1M:
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "%1 flash");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 flash");
|
||||
break;
|
||||
case SAVEDATA_EEPROM:
|
||||
case SAVEDATA_EEPROM512:
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "%1 EEPROM");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 EEPROM");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -485,29 +485,29 @@ SaveConverter::AnnotatedSave::operator QString() const {
|
|||
switch (gb.type) {
|
||||
case GB_MBC_AUTODETECT:
|
||||
if (size & 0xFF) {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "%1 SRAM + RTC");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 SRAM + RTC");
|
||||
} else {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "%1 SRAM");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 SRAM");
|
||||
}
|
||||
break;
|
||||
case GB_MBC2:
|
||||
if (size == 0x100) {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "packed MBC2");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "packed MBC2");
|
||||
} else {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "unpacked MBC2");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "unpacked MBC2");
|
||||
}
|
||||
break;
|
||||
case GB_MBC6:
|
||||
if (size == GB_SIZE_MBC6_FLASH) {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "MBC6 flash");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 flash");
|
||||
} else if (size > GB_SIZE_MBC6_FLASH) {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "MBC6 combined SRAM + flash");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 combined SRAM + flash");
|
||||
} else {
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "MBC6 SRAM");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 SRAM");
|
||||
}
|
||||
break;
|
||||
case GB_TAMA5:
|
||||
typeFormat = QCoreApplication::translate("SaveConverter", "TAMA5");
|
||||
typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "TAMA5");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -519,17 +519,17 @@ SaveConverter::AnnotatedSave::operator QString() const {
|
|||
}
|
||||
saveType = typeFormat.arg(sizeStr);
|
||||
if (!endianStr.isEmpty()) {
|
||||
saveType = QCoreApplication::translate("SaveConverter", "%1 (%2)").arg(saveType).arg(endianStr);
|
||||
saveType = QCoreApplication::translate("QGBA::SaveConverter", "%1 (%2)").arg(saveType).arg(endianStr);
|
||||
}
|
||||
switch (container) {
|
||||
case Container::SAVESTATE:
|
||||
format = QCoreApplication::translate("SaveConverter", "%1 save state with embedded %2 save game");
|
||||
format = QCoreApplication::translate("QGBA::SaveConverter", "%1 save state with embedded %2 save game");
|
||||
break;
|
||||
case Container::SHARKPORT:
|
||||
format = QCoreApplication::translate("SaveConverter", "%1 SharkPort %2 save game");
|
||||
format = QCoreApplication::translate("QGBA::SaveConverter", "%1 SharkPort %2 save game");
|
||||
break;
|
||||
case Container::GSV:
|
||||
format = QCoreApplication::translate("SaveConverter", "%1 GameShark Advance SP %2 save game");
|
||||
format = QCoreApplication::translate("QGBA::SaveConverter", "%1 GameShark Advance SP %2 save game");
|
||||
break;
|
||||
case Container::NONE:
|
||||
break;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SaveConverter</class>
|
||||
<widget class="QDialog" name="SaveConverter">
|
||||
<class>QGBA::SaveConverter</class>
|
||||
<widget class="QDialog" name="QGBA::SaveConverter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
@ -91,7 +91,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>SaveConverter</receiver>
|
||||
<receiver>QGBA::SaveConverter</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
@ -107,7 +107,7 @@
|
|||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>SaveConverter</receiver>
|
||||
<receiver>QGBA::SaveConverter</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SensorView</class>
|
||||
<widget class="QWidget" name="SensorView">
|
||||
<class>QGBA::SensorView</class>
|
||||
<widget class="QWidget" name="QGBA::SensorView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -35,16 +35,17 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
m_ui.setupUi(this);
|
||||
|
||||
m_pageIndex[Page::AV] = 0;
|
||||
m_pageIndex[Page::INTERFACE] = 1;
|
||||
m_pageIndex[Page::UPDATE] = 2;
|
||||
m_pageIndex[Page::EMULATION] = 3;
|
||||
m_pageIndex[Page::ENHANCEMENTS] = 4;
|
||||
m_pageIndex[Page::BIOS] = 5;
|
||||
m_pageIndex[Page::PATHS] = 6;
|
||||
m_pageIndex[Page::LOGGING] = 7;
|
||||
m_pageIndex[Page::GAMEPLAY] = 1;
|
||||
m_pageIndex[Page::INTERFACE] = 2;
|
||||
m_pageIndex[Page::UPDATE] = 3;
|
||||
m_pageIndex[Page::EMULATION] = 4;
|
||||
m_pageIndex[Page::ENHANCEMENTS] = 5;
|
||||
m_pageIndex[Page::BIOS] = 6;
|
||||
m_pageIndex[Page::PATHS] = 7;
|
||||
m_pageIndex[Page::LOGGING] = 8;
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
m_pageIndex[Page::GB] = 8;
|
||||
m_pageIndex[Page::GB] = 9;
|
||||
|
||||
for (auto model : GameBoy::modelList()) {
|
||||
m_ui.gbModel->addItem(GameBoy::modelName(model), model);
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
enum class Page {
|
||||
AV,
|
||||
INTERFACE,
|
||||
GAMEPLAY,
|
||||
UPDATE,
|
||||
EMULATION,
|
||||
ENHANCEMENTS,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ShaderSelector</class>
|
||||
<widget class="QDialog" name="ShaderSelector">
|
||||
<class>QGBA::ShaderSelector</class>
|
||||
<widget class="QDialog" name="QGBA::ShaderSelector">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ShortcutView</class>
|
||||
<widget class="QWidget" name="ShortcutView">
|
||||
<class>QGBA::ShortcutView</class>
|
||||
<widget class="QWidget" name="QGBA::ShortcutView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -51,6 +51,7 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
m_ui.tile->setBoundary(2048, 0, 2);
|
||||
m_ui.tile->setMaxTile(3096);
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
|
@ -60,6 +61,7 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
m_ui.tilesBoth->setEnabled(false);
|
||||
m_ui.palette256->setEnabled(false);
|
||||
m_ui.tile->setBoundary(1024, 0, 0);
|
||||
m_ui.tile->setMaxTile(512);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
@ -74,6 +76,7 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
m_ui.tile->setBoundary(2048 >> selected, selected, selected + 2);
|
||||
m_ui.tile->setMaxTile(3096 >> selected);
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TileView</class>
|
||||
<widget class="QWidget" name="TileView">
|
||||
<class>QGBA::TileView</class>
|
||||
<widget class="QWidget" name="QGBA::TileView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VideoView</class>
|
||||
<widget class="QWidget" name="VideoView">
|
||||
<class>QGBA::VideoView</class>
|
||||
<widget class="QWidget" name="QGBA::VideoView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
|
|
|
@ -48,11 +48,11 @@ LibraryTree::LibraryTree(LibraryController* parent)
|
|||
m_widget->setAlternatingRowColors(true);
|
||||
|
||||
QTreeWidgetItem* header = new QTreeWidgetItem({
|
||||
QApplication::translate("LibraryTree", "Name", nullptr),
|
||||
QApplication::translate("LibraryTree", "Location", nullptr),
|
||||
QApplication::translate("LibraryTree", "Platform", nullptr),
|
||||
QApplication::translate("LibraryTree", "Size", nullptr),
|
||||
QApplication::translate("LibraryTree", "CRC32", nullptr),
|
||||
QApplication::translate("QGBA::LibraryTree", "Name", nullptr),
|
||||
QApplication::translate("QGBA::LibraryTree", "Location", nullptr),
|
||||
QApplication::translate("QGBA::LibraryTree", "Platform", nullptr),
|
||||
QApplication::translate("QGBA::LibraryTree", "Size", nullptr),
|
||||
QApplication::translate("QGBA::LibraryTree", "CRC32", nullptr),
|
||||
});
|
||||
header->setTextAlignment(3, Qt::AlignTrailing | Qt::AlignVCenter);
|
||||
m_widget->setHeaderItem(header);
|
||||
|
|
|
@ -61,6 +61,9 @@ void ScriptingController::setController(std::shared_ptr<CoreController> controll
|
|||
|
||||
bool ScriptingController::loadFile(const QString& path) {
|
||||
VFileDevice vf(path, QIODevice::ReadOnly);
|
||||
if (!vf.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
return load(vf, path);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,67 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptingView</class>
|
||||
<widget class="QMainWindow" name="ScriptingView">
|
||||
<class>QGBA::ScriptingView</class>
|
||||
<widget class="QMainWindow" name="QGBA::ScriptingView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
<width>843</width>
|
||||
<height>637</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Scripting</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" rowspan="3">
|
||||
<widget class="QListView" name="buffers">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="buffer">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QGBA::LogWidget" name="log">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="prompt"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QListView" name="buffers">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>90</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="prompt"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QSplitter" name="splitter_1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QPlainTextEdit" name="buffer">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QGBA::LogWidget" name="log">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
file(WRITE ${TRANSLATION_QRC} "<RCC>\n\t<qresource prefix=\"/translations/\">\n")
|
||||
foreach(TS ${TRANSLATION_FILES})
|
||||
get_filename_component(TS_BASE "${TS}" NAME)
|
||||
file(APPEND ${TRANSLATION_QRC} "\t\t<file alias=\"${TS_BASE}\">${TS}</file>\n")
|
||||
endforeach()
|
||||
file(APPEND ${TRANSLATION_QRC} "\t</qresource>\n</RCC>")
|
|
@ -38,19 +38,12 @@
|
|||
#include <signal.h>
|
||||
|
||||
#define PORT "sdl"
|
||||
#define MAX_LOG_BUF 1024
|
||||
|
||||
static void mSDLDeinit(struct mSDLRenderer* renderer);
|
||||
|
||||
static int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args);
|
||||
|
||||
static void _setLogger(struct mCore* core);
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
|
||||
|
||||
static bool _logToStdout = true;
|
||||
static struct VFile* _logFile = NULL;
|
||||
static struct mLogFilter _filter;
|
||||
static struct mLogger _logger;
|
||||
static struct mStandardLogger _logger;
|
||||
|
||||
static struct VFile* _state = NULL;
|
||||
|
||||
|
@ -136,6 +129,7 @@ int main(int argc, char** argv) {
|
|||
mCoreInitConfig(renderer.core, PORT);
|
||||
mArgumentsApply(&args, &subparser, 1, &renderer.core->config);
|
||||
|
||||
mCoreConfigSetDefaultIntValue(&renderer.core->config, "logToStdout", true);
|
||||
mCoreConfigLoadDefaults(&renderer.core->config, &opts);
|
||||
mCoreLoadConfig(renderer.core);
|
||||
|
||||
|
@ -188,7 +182,8 @@ int main(int argc, char** argv) {
|
|||
int ret;
|
||||
|
||||
// TODO: Use opts and config
|
||||
_setLogger(renderer.core);
|
||||
mStandardLoggerInit(&_logger);
|
||||
mStandardLoggerConfig(&_logger, &renderer.core->config);
|
||||
ret = mSDLRun(&renderer, &args);
|
||||
mSDLDetachPlayer(&renderer.events, &renderer.player);
|
||||
mInputMapDeinit(&renderer.core->inputMap);
|
||||
|
@ -198,6 +193,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
mSDLDeinit(&renderer);
|
||||
mStandardLoggerDeinit(&_logger);
|
||||
|
||||
mArgumentsDeinit(&args);
|
||||
mCoreConfigFreeOpts(&opts);
|
||||
|
@ -273,12 +269,8 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
|
||||
renderer->audio.samples = renderer->core->opts.audioBuffers;
|
||||
renderer->audio.sampleRate = 44100;
|
||||
|
||||
struct mThreadLogger threadLogger;
|
||||
threadLogger.d = _logger;
|
||||
threadLogger.p = &thread;
|
||||
thread.logger = threadLogger;
|
||||
|
||||
thread.logger.logger = &_logger.d;
|
||||
|
||||
bool didFail = !mCoreThreadStart(&thread);
|
||||
|
||||
if (!didFail) {
|
||||
|
@ -308,6 +300,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
if (mCoreThreadHasCrashed(&thread)) {
|
||||
didFail = true;
|
||||
printf("The game crashed!\n");
|
||||
mCoreThreadEnd(&thread);
|
||||
}
|
||||
} else {
|
||||
didFail = true;
|
||||
|
@ -342,63 +335,3 @@ static void mSDLDeinit(struct mSDLRenderer* renderer) {
|
|||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
static void _setLogger(struct mCore* core) {
|
||||
int fakeBool = 0;
|
||||
bool logToFile = false;
|
||||
|
||||
if (mCoreConfigGetIntValue(&core->config, "logToStdout", &fakeBool)) {
|
||||
_logToStdout = fakeBool;
|
||||
}
|
||||
if (mCoreConfigGetIntValue(&core->config, "logToFile", &fakeBool)) {
|
||||
logToFile = fakeBool;
|
||||
}
|
||||
const char* logFile = mCoreConfigGetValue(&core->config, "logFile");
|
||||
|
||||
if (logToFile && logFile) {
|
||||
_logFile = VFileOpen(logFile, O_WRONLY | O_CREAT | O_APPEND);
|
||||
}
|
||||
|
||||
// Create the filter
|
||||
mLogFilterInit(&_filter);
|
||||
mLogFilterLoad(&_filter, &core->config);
|
||||
|
||||
// Fill the logger
|
||||
_logger.log = _mCoreLog;
|
||||
_logger.filter = &_filter;
|
||||
}
|
||||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
if (thread && level == mLOG_FATAL) {
|
||||
mCoreThreadMarkCrashed(thread);
|
||||
}
|
||||
|
||||
if (!mLogFilterTest(logger->filter, category, level)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[MAX_LOG_BUF];
|
||||
|
||||
// Prepare the string
|
||||
size_t length = snprintf(buffer, sizeof(buffer), "%s: ", mLogCategoryName(category));
|
||||
if (length < sizeof(buffer)) {
|
||||
length += vsnprintf(buffer + length, sizeof(buffer) - length, format, args);
|
||||
}
|
||||
if (length < sizeof(buffer)) {
|
||||
length += snprintf(buffer + length, sizeof(buffer) - length, "\n");
|
||||
}
|
||||
|
||||
// Make sure the length doesn't exceed the size of the buffer when actually writing
|
||||
if (length > sizeof(buffer)) {
|
||||
length = sizeof(buffer);
|
||||
}
|
||||
|
||||
if (_logToStdout) {
|
||||
printf("%s", buffer);
|
||||
}
|
||||
|
||||
if (_logFile) {
|
||||
_logFile->write(_logFile, buffer, length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
@ -30,49 +31,37 @@
|
|||
|
||||
struct RomTestOpts {
|
||||
int exitSwiImmediate;
|
||||
unsigned int returnCodeRegister;
|
||||
char* returnCodeRegister;
|
||||
};
|
||||
|
||||
static void _romTestShutdown(int signal);
|
||||
static bool _parseRomTestOpts(struct mSubParser* parser, int option, const char* arg);
|
||||
static bool _parseSwi(const char* regStr, int* oSwi);
|
||||
static bool _parseNamedRegister(const char* regStr, unsigned int* oRegister);
|
||||
|
||||
static bool _romTestCheckResiger(void);
|
||||
|
||||
static struct mCore* core;
|
||||
|
||||
static bool _dispatchExiting = false;
|
||||
static int _exitCode = 0;
|
||||
static struct mStandardLogger _logger;
|
||||
|
||||
static void _romTestCallback(void* context);
|
||||
#ifdef M_CORE_GBA
|
||||
static void _romTestSwi3Callback(void* context);
|
||||
|
||||
static void _romTestSwi16(struct ARMCore* cpu, int immediate);
|
||||
static void _romTestSwi32(struct ARMCore* cpu, int immediate);
|
||||
|
||||
static int _exitSwiImmediate;
|
||||
static unsigned int _returnCodeRegister;
|
||||
static char* _returnCodeRegister;
|
||||
|
||||
void (*_armSwi16)(struct ARMCore* cpu, int immediate);
|
||||
void (*_armSwi32)(struct ARMCore* cpu, int immediate);
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
enum GBReg {
|
||||
GB_REG_A = 16,
|
||||
GB_REG_F,
|
||||
GB_REG_B,
|
||||
GB_REG_C,
|
||||
GB_REG_D,
|
||||
GB_REG_E,
|
||||
GB_REG_H,
|
||||
GB_REG_L
|
||||
};
|
||||
|
||||
static void _romTestGBCallback(void* context);
|
||||
#endif
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
signal(SIGINT, _romTestShutdown);
|
||||
|
||||
struct RomTestOpts romTestOpts = { 3, 0 };
|
||||
struct RomTestOpts romTestOpts = { 3, NULL };
|
||||
struct mSubParser subparser = {
|
||||
.usage = ROM_TEST_USAGE,
|
||||
.parse = _parseRomTestOpts,
|
||||
|
@ -93,7 +82,7 @@ int main(int argc, char * argv[]) {
|
|||
version(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
struct mCore* core = mCoreFind(args.fname);
|
||||
core = mCoreFind(args.fname);
|
||||
if (!core) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -102,24 +91,29 @@ int main(int argc, char * argv[]) {
|
|||
mArgumentsApply(&args, NULL, 0, &core->config);
|
||||
|
||||
mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove");
|
||||
mCoreConfigSetDefaultIntValue(&core->config, "logToStdout", true);
|
||||
|
||||
mStandardLoggerInit(&_logger);
|
||||
mStandardLoggerConfig(&_logger, &core->config);
|
||||
mLogSetDefaultLogger(&_logger.d);
|
||||
|
||||
bool cleanExit = false;
|
||||
struct mCoreCallbacks callbacks = {0};
|
||||
callbacks.context = core;
|
||||
|
||||
_returnCodeRegister = romTestOpts.returnCodeRegister;
|
||||
if (!_romTestCheckResiger()) {
|
||||
goto loadError;
|
||||
}
|
||||
|
||||
switch (core->platform(core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
((struct GBA*) core->board)->hardCrash = false;
|
||||
if (romTestOpts.returnCodeRegister >= 16) {
|
||||
goto loadError;
|
||||
}
|
||||
|
||||
_exitSwiImmediate = romTestOpts.exitSwiImmediate;
|
||||
_returnCodeRegister = romTestOpts.returnCodeRegister;
|
||||
|
||||
if (_exitSwiImmediate == 3) {
|
||||
// Hook into SWI 3 (shutdown)
|
||||
callbacks.shutdown = _romTestSwi3Callback;
|
||||
callbacks.shutdown = _romTestCallback;
|
||||
core->addCoreCallbacks(core, &callbacks);
|
||||
} else {
|
||||
// Custom SWI hooks
|
||||
|
@ -132,13 +126,7 @@ int main(int argc, char * argv[]) {
|
|||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case mPLATFORM_GB:
|
||||
if (romTestOpts.returnCodeRegister < GB_REG_A) {
|
||||
goto loadError;
|
||||
}
|
||||
|
||||
_returnCodeRegister = romTestOpts.returnCodeRegister;
|
||||
|
||||
callbacks.shutdown = _romTestGBCallback;
|
||||
callbacks.shutdown = _romTestCallback;
|
||||
core->addCoreCallbacks(core, &callbacks);
|
||||
break;
|
||||
#endif
|
||||
|
@ -185,8 +173,12 @@ int main(int argc, char * argv[]) {
|
|||
|
||||
loadError:
|
||||
mArgumentsDeinit(&args);
|
||||
mStandardLoggerDeinit(&_logger);
|
||||
mCoreConfigDeinit(&core->config);
|
||||
core->deinit(core);
|
||||
if (_returnCodeRegister) {
|
||||
free(_returnCodeRegister);
|
||||
}
|
||||
|
||||
return cleanExit ? _exitCode : 1;
|
||||
}
|
||||
|
@ -196,16 +188,66 @@ static void _romTestShutdown(int signal) {
|
|||
_dispatchExiting = true;
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
static void _romTestSwi3Callback(void* context) {
|
||||
struct mCore* core = context;
|
||||
_exitCode = ((struct GBA*) core->board)->cpu->regs.gprs[_returnCodeRegister];
|
||||
static bool _romTestCheckResiger(void) {
|
||||
if (!_returnCodeRegister) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct mCoreRegisterInfo* registers;
|
||||
const struct mCoreRegisterInfo* reg = NULL;
|
||||
size_t regCount = core->listRegisters(core, ®isters);
|
||||
size_t i;
|
||||
for (i = 0; i < regCount; ++i) {
|
||||
if (strcasecmp(_returnCodeRegister, registers[i].name) == 0) {
|
||||
reg = ®isters[i];
|
||||
break;
|
||||
}
|
||||
if (registers[i].aliases) {
|
||||
size_t j;
|
||||
for (j = 0; registers[i].aliases[j]; ++j) {
|
||||
if (strcasecmp(_returnCodeRegister, registers[i].aliases[j]) == 0) {
|
||||
reg = ®isters[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (reg) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!reg) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reg->width > 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reg->type != mCORE_REGISTER_GPR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reg->mask != 0xFFFFFFFFU >> (4 - reg->width) * 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _romTestCallback(void* context) {
|
||||
UNUSED(context);
|
||||
if (_returnCodeRegister) {
|
||||
core->readRegister(core, _returnCodeRegister, &_exitCode);
|
||||
}
|
||||
_dispatchExiting = true;
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
static void _romTestSwi16(struct ARMCore* cpu, int immediate) {
|
||||
if (immediate == _exitSwiImmediate) {
|
||||
_exitCode = cpu->regs.gprs[_returnCodeRegister];
|
||||
if (_returnCodeRegister) {
|
||||
core->readRegister(core, _returnCodeRegister, &_exitCode);
|
||||
}
|
||||
_dispatchExiting = true;
|
||||
return;
|
||||
}
|
||||
|
@ -214,7 +256,9 @@ static void _romTestSwi16(struct ARMCore* cpu, int immediate) {
|
|||
|
||||
static void _romTestSwi32(struct ARMCore* cpu, int immediate) {
|
||||
if (immediate == _exitSwiImmediate) {
|
||||
_exitCode = cpu->regs.gprs[_returnCodeRegister];
|
||||
if (_returnCodeRegister) {
|
||||
core->readRegister(core, _returnCodeRegister, &_exitCode);
|
||||
}
|
||||
_dispatchExiting = true;
|
||||
return;
|
||||
}
|
||||
|
@ -222,41 +266,6 @@ static void _romTestSwi32(struct ARMCore* cpu, int immediate) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
static void _romTestGBCallback(void* context) {
|
||||
struct mCore* core = context;
|
||||
struct SM83Core* cpu = core->cpu;
|
||||
|
||||
switch (_returnCodeRegister) {
|
||||
case GB_REG_A:
|
||||
_exitCode = cpu->a;
|
||||
break;
|
||||
case GB_REG_B:
|
||||
_exitCode = cpu->b;
|
||||
break;
|
||||
case GB_REG_C:
|
||||
_exitCode = cpu->c;
|
||||
break;
|
||||
case GB_REG_D:
|
||||
_exitCode = cpu->d;
|
||||
break;
|
||||
case GB_REG_E:
|
||||
_exitCode = cpu->e;
|
||||
break;
|
||||
case GB_REG_F:
|
||||
_exitCode = cpu->f.packed;
|
||||
break;
|
||||
case GB_REG_H:
|
||||
_exitCode = cpu->h;
|
||||
break;
|
||||
case GB_REG_L:
|
||||
_exitCode = cpu->l;
|
||||
break;
|
||||
}
|
||||
_dispatchExiting = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool _parseRomTestOpts(struct mSubParser* parser, int option, const char* arg) {
|
||||
struct RomTestOpts* opts = parser->opts;
|
||||
errno = 0;
|
||||
|
@ -264,7 +273,8 @@ static bool _parseRomTestOpts(struct mSubParser* parser, int option, const char*
|
|||
case 'S':
|
||||
return _parseSwi(arg, &opts->exitSwiImmediate);
|
||||
case 'R':
|
||||
return _parseNamedRegister(arg, &opts->returnCodeRegister);
|
||||
opts->returnCodeRegister = strdup(arg);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -279,72 +289,3 @@ static bool _parseSwi(const char* swiStr, int* oSwi) {
|
|||
*oSwi = swi;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _parseNamedRegister(const char* regStr, unsigned int* oRegister) {
|
||||
#ifdef M_CORE_GB
|
||||
static const enum GBReg gbMapping[] = {
|
||||
['a' - 'a'] = GB_REG_A,
|
||||
['b' - 'a'] = GB_REG_B,
|
||||
['c' - 'a'] = GB_REG_C,
|
||||
['d' - 'a'] = GB_REG_D,
|
||||
['e' - 'a'] = GB_REG_E,
|
||||
['f' - 'a'] = GB_REG_F,
|
||||
['h' - 'a'] = GB_REG_H,
|
||||
['l' - 'a'] = GB_REG_L,
|
||||
};
|
||||
#endif
|
||||
|
||||
switch (regStr[0]) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
++regStr;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
break;
|
||||
#ifdef M_CORE_GB
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
case 'h':
|
||||
case 'l':
|
||||
if (regStr[1] != '\0') {
|
||||
return false;
|
||||
}
|
||||
*oRegister = gbMapping[regStr[0] - 'a'];
|
||||
break;
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'H':
|
||||
case 'L':
|
||||
if (regStr[1] != '\0') {
|
||||
return false;
|
||||
}
|
||||
*oRegister = gbMapping[regStr[0] - 'A'];
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
char* parseEnd;
|
||||
unsigned long regId = strtoul(regStr, &parseEnd, 10);
|
||||
if (errno || regId > 15 || *parseEnd) {
|
||||
return false;
|
||||
}
|
||||
*oRegister = regId;
|
||||
return true;
|
||||
}
|
||||
|
|