Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2017-04-13 00:37:17 -07:00
commit f85a18737e
19 changed files with 340 additions and 123 deletions

View File

@ -43,6 +43,8 @@ Features:
- Add option for whether rewinding restores save games - Add option for whether rewinding restores save games
- Qt: German translation (by Lothar Serra Mari) - Qt: German translation (by Lothar Serra Mari)
- Savestates now contain any RTC override data - Savestates now contain any RTC override data
- Command line ability to override configuration values
- Add option to allow preloading the entire ROM before running
Bugfixes: Bugfixes:
- LR35902: Fix core never exiting with certain event patterns - LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior - GB Timer: Improve DIV reset behavior
@ -118,6 +120,8 @@ Misc:
- Qt: Make "Mute" able to be bound to a key - Qt: Make "Mute" able to be bound to a key
- Core: Restore sleep callback - Core: Restore sleep callback
- Qt: Add .gb/.gbc files to the extension list in Info.plist - Qt: Add .gb/.gbc files to the extension list in Info.plist
- Feature: Make -l option explicit
- Core: Ability to enumerate and modify video and audio channels
medusa alpha 1: (2017-04-08) medusa alpha 1: (2017-04-08)
Features: Features:

View File

@ -92,6 +92,8 @@ CXX_GUARD_START
dest->size = src->size; \ dest->size = src->size; \
} \ } \
DECLARE_VECTOR(StringList, char*);
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -48,7 +48,6 @@ struct mCheat {
mLOG_DECLARE_CATEGORY(CHEATS); mLOG_DECLARE_CATEGORY(CHEATS);
DECLARE_VECTOR(mCheatList, struct mCheat); DECLARE_VECTOR(mCheatList, struct mCheat);
DECLARE_VECTOR(StringList, char*);
struct mCheatDevice; struct mCheatDevice;
struct mCheatSet { struct mCheatSet {

View File

@ -147,12 +147,20 @@ struct mCore {
size_t (*savedataClone)(struct mCore*, void** sram); size_t (*savedataClone)(struct mCore*, void** sram);
bool (*savedataRestore)(struct mCore*, const void* sram, size_t size, bool writeback); bool (*savedataRestore)(struct mCore*, const void* sram, size_t size, bool writeback);
size_t (*listVideoLayers)(const struct mCore*, const struct mCoreChannelInfo**);
size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**);
void (*enableVideoLayer)(struct mCore*, size_t id, bool enable);
void (*enableAudioChannel)(struct mCore*, size_t id, bool enable);
}; };
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct mCore* mCoreFind(const char* path); struct mCore* mCoreFind(const char* path);
bool mCoreLoadFile(struct mCore* core, const char* path); bool mCoreLoadFile(struct mCore* core, const char* path);
bool mCorePreloadVF(struct mCore* core, struct VFile* vf);
bool mCorePreloadFile(struct mCore* core, const char* path);
bool mCoreAutoloadSave(struct mCore* core); bool mCoreAutoloadSave(struct mCore* core);
bool mCoreAutoloadPatch(struct mCore* core); bool mCoreAutoloadPatch(struct mCore* core);

View File

@ -105,6 +105,13 @@ struct mRumble {
void (*setRumble)(struct mRumble*, int enable); void (*setRumble)(struct mRumble*, int enable);
}; };
struct mCoreChannelInfo {
size_t id;
const char* internalName;
const char* visibleName;
const char* visibleType;
};
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -17,7 +17,6 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
DEFINE_VECTOR(mCheatList, struct mCheat); DEFINE_VECTOR(mCheatList, struct mCheat);
DEFINE_VECTOR(mCheatSets, struct mCheatSet*); DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
DEFINE_VECTOR(StringList, char*);
static int32_t _readMem(struct mCore* core, uint32_t address, int width) { static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
switch (width) { switch (width) {

View File

@ -122,6 +122,35 @@ bool mCoreLoadFile(struct mCore* core, const char* path) {
return ret; return ret;
} }
bool mCorePreloadVF(struct mCore* core, struct VFile* vf) {
struct VFile* vfm = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
vf->seek(vf, 0, SEEK_SET);
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfm->write(vfm, buffer, read);
}
vf->close(vf);
bool ret = core->loadROM(core, vfm);
if (!ret) {
vfm->close(vfm);
}
return ret;
}
bool mCorePreloadFile(struct mCore* core, const char* path) {
struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
if (!rom) {
return false;
}
bool ret = mCorePreloadVF(core, rom);
if (!ret) {
rom->close(rom);
}
return ret;
}
bool mCoreAutoloadSave(struct mCore* core) { bool mCoreAutoloadSave(struct mCore* core) {
return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR)); return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR));
} }

View File

@ -18,6 +18,38 @@
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
const static struct mCoreChannelInfo _DSVideoLayers[] = {
{ 0, "abg0", "A BG0", "2D/3D" },
{ 1, "abg1", "A BG1", NULL },
{ 2, "abg2", "A BG2", NULL },
{ 3, "abg3", "A BG3", NULL },
{ 4, "aobj", "A OBJ", NULL },
{ 5, "bbg0", "B BG0", "2D/3D" },
{ 6, "bbg1", "B BG1", NULL },
{ 7, "bbg2", "B BG2", NULL },
{ 8, "bbg3", "B BG3", NULL },
{ 9, "bobj", "B OBJ", NULL },
};
const static struct mCoreChannelInfo _DSAudioChannels[] = {
{ 0, "ch00", "Channel 0", NULL },
{ 1, "ch01", "Channel 1", NULL },
{ 2, "ch02", "Channel 2", NULL },
{ 3, "ch03", "Channel 3", NULL },
{ 4, "ch04", "Channel 4", NULL },
{ 5, "ch05", "Channel 5", NULL },
{ 6, "ch06", "Channel 6", NULL },
{ 7, "ch07", "Channel 7", NULL },
{ 8, "ch08", "Channel 8", NULL },
{ 9, "ch09", "Channel 9", NULL },
{ 10, "ch10", "Channel 10", NULL },
{ 11, "ch11", "Channel 11", NULL },
{ 12, "ch12", "Channel 12", NULL },
{ 13, "ch13", "Channel 13", NULL },
{ 14, "ch14", "Channel 14", NULL },
{ 15, "ch15", "Channel 15", NULL },
};
struct DSCore { struct DSCore {
struct mCore d; struct mCore d;
struct ARMCore* arm7; struct ARMCore* arm7;
@ -515,6 +547,27 @@ static bool _DSCoreSavedataRestore(struct mCore* core, const void* sram, size_t
return false; return false;
} }
static size_t _DSCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _DSVideoLayers;
return sizeof(_DSVideoLayers) / sizeof(*_DSVideoLayers);
}
static size_t _DSCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _DSAudioChannels;
return sizeof(_DSAudioChannels) / sizeof(*_DSAudioChannels);
}
static void _DSCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct DS* ds = core->board;
// TODO
}
static void _DSCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
struct DS* ds = core->board;
}
struct mCore* DSCoreCreate(void) { struct mCore* DSCoreCreate(void) {
struct DSCore* dscore = malloc(sizeof(*dscore)); struct DSCore* dscore = malloc(sizeof(*dscore));
struct mCore* core = &dscore->d; struct mCore* core = &dscore->d;
@ -584,5 +637,9 @@ struct mCore* DSCoreCreate(void) {
core->cheatDevice = _DSCoreCheatDevice; core->cheatDevice = _DSCoreCheatDevice;
core->savedataClone = _DSCoreSavedataClone; core->savedataClone = _DSCoreSavedataClone;
core->savedataRestore = _DSCoreSavedataRestore; core->savedataRestore = _DSCoreSavedataRestore;
core->listVideoLayers = _DSCoreListVideoLayers;
core->listAudioChannels = _DSCoreListAudioChannels;
core->enableVideoLayer = _DSCoreEnableVideoLayer;
core->enableAudioChannel = _DSCoreEnableAudioChannel;
return core; return core;
} }

View File

@ -38,6 +38,7 @@ static const struct option _options[] = {
{ "gdb", no_argument, 0, 'g' }, { "gdb", no_argument, 0, 'g' },
#endif #endif
{ "help", no_argument, 0, 'h' }, { "help", no_argument, 0, 'h' },
{ "log-level", required_argument, 0, 'l' },
{ "movie", required_argument, 0, 'v' }, { "movie", required_argument, 0, 'v' },
{ "patch", required_argument, 0, 'p' }, { "patch", required_argument, 0, 'p' },
{ "version", no_argument, 0, '\0' }, { "version", no_argument, 0, '\0' },
@ -47,10 +48,27 @@ static const struct option _options[] = {
static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg); static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg);
static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config); static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config);
static void _tableInsert(struct Table* table, const char* pair) {
char* eq = strchr(pair, '=');
if (eq) {
char option[128] = "";
strncpy(option, pair, eq - pair);
option[sizeof(option) - 1] = '\0';
HashTableInsert(table, option, strdup(&eq[1]));
} else {
HashTableInsert(table, pair, strdup("1"));
}
}
static void _tableApply(const char* key, void* value, void* user) {
struct mCoreConfig* config = user;
mCoreConfigSetOverrideValue(config, key, value);
}
bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) { bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) {
int ch; int ch;
char options[64] = char options[64] =
"b:c:hl:p:s:v:" "b:c:C:hl:p:s:v:"
#ifdef USE_EDITLINE #ifdef USE_EDITLINE
"d" "d"
#endif #endif
@ -61,6 +79,7 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
memset(args, 0, sizeof(*args)); memset(args, 0, sizeof(*args));
args->frameskip = -1; args->frameskip = -1;
args->logLevel = INT_MIN; args->logLevel = INT_MIN;
HashTableInit(&args->configOverrides, 0, free);
if (subparser && subparser->extraOptions) { if (subparser && subparser->extraOptions) {
// TODO: modularize options to subparsers // TODO: modularize options to subparsers
strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1); strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1);
@ -82,6 +101,9 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
case 'c': case 'c':
args->cheatsFile = strdup(optarg); args->cheatsFile = strdup(optarg);
break; break;
case 'C':
_tableInsert(&args->configOverrides, optarg);
break;
#ifdef USE_EDITLINE #ifdef USE_EDITLINE
case 'd': case 'd':
if (args->debuggerType != DEBUGGER_NONE) { if (args->debuggerType != DEBUGGER_NONE) {
@ -144,6 +166,7 @@ void applyArguments(const struct mArguments* args, struct mSubParser* subparser,
if (args->bios) { if (args->bios) {
mCoreConfigSetOverrideValue(config, "bios", args->bios); mCoreConfigSetOverrideValue(config, "bios", args->bios);
} }
HashTableEnumerate(&args->configOverrides, _tableApply, config);
if (subparser) { if (subparser) {
subparser->apply(subparser, config); subparser->apply(subparser, config);
} }
@ -164,6 +187,8 @@ void freeArguments(struct mArguments* args) {
free(args->bios); free(args->bios);
args->bios = 0; args->bios = 0;
HashTableDeinit(&args->configOverrides);
} }
void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) { void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) {
@ -209,18 +234,20 @@ void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) {
void usage(const char* arg0, const char* extraOptions) { void usage(const char* arg0, const char* extraOptions) {
printf("usage: %s [option ...] file\n", arg0); printf("usage: %s [option ...] file\n", arg0);
puts("\nGeneric options:"); puts("\nGeneric options:");
puts(" -b, --bios FILE GBA BIOS file to use"); puts(" -b, --bios FILE GBA BIOS file to use");
puts(" -c, --cheats FILE Apply cheat codes from a file"); puts(" -c, --cheats FILE Apply cheat codes from a file");
puts(" -C, --config OPTION=VALUE Override config value");
#ifdef USE_EDITLINE #ifdef USE_EDITLINE
puts(" -d, --debug Use command-line debugger"); puts(" -d, --debug Use command-line debugger");
#endif #endif
#ifdef USE_GDB_STUB #ifdef USE_GDB_STUB
puts(" -g, --gdb Start GDB session (default port 2345)"); puts(" -g, --gdb Start GDB session (default port 2345)");
#endif #endif
puts(" -v, --movie FILE Play back a movie of recorded input"); puts(" -l, --log-level N Log level mask");
puts(" -p, --patch FILE Apply a specified patch file when running"); puts(" -v, --movie FILE Play back a movie of recorded input");
puts(" -s, --frameskip N Skip every N frames"); puts(" -p, --patch FILE Apply a specified patch file when running");
puts(" --version Print version and exit"); puts(" -s, --frameskip N Skip every N frames");
puts(" --version Print version and exit");
if (extraOptions) { if (extraOptions) {
puts(extraOptions); puts(extraOptions);
} }

View File

@ -10,6 +10,8 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba-util/table.h>
#include <mgba/internal/debugger/debugger.h> #include <mgba/internal/debugger/debugger.h>
struct mArguments { struct mArguments {
@ -21,6 +23,8 @@ struct mArguments {
int logLevel; int logLevel;
int frameskip; int frameskip;
struct Table configOverrides;
enum mDebuggerType debuggerType; enum mDebuggerType debuggerType;
bool debugAtStart; bool debugAtStart;
bool showHelp; bool showHelp;

View File

@ -25,6 +25,19 @@
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#endif #endif
const static struct mCoreChannelInfo _GBVideoLayers[] = {
{ 0, "bg", "Background", NULL },
{ 1, "obj", "Objects", NULL },
{ 2, "win", "Window", NULL },
};
const static struct mCoreChannelInfo _GBAudioChannels[] = {
{ 0, "ch0", "Channel 0", "Square/Sweep" },
{ 1, "ch1", "Channel 1", "Square" },
{ 2, "ch2", "Channel 2", "PCM" },
{ 3, "ch3", "Channel 3", "Noise" },
};
struct GBCore { struct GBCore {
struct mCore d; struct mCore d;
struct GBVideoSoftwareRenderer renderer; struct GBVideoSoftwareRenderer renderer;
@ -599,6 +612,37 @@ static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t
return true; return true;
} }
static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBVideoLayers;
return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
}
static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAudioChannels;
return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
}
static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct GB* gb = core->board;
// TODO
}
static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
struct GB* gb = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gb->audio.forceDisableCh[id] = !enable;
break;
default:
break;
}
}
struct mCore* GBCoreCreate(void) { struct mCore* GBCoreCreate(void) {
struct GBCore* gbcore = malloc(sizeof(*gbcore)); struct GBCore* gbcore = malloc(sizeof(*gbcore));
struct mCore* core = &gbcore->d; struct mCore* core = &gbcore->d;
@ -669,5 +713,9 @@ struct mCore* GBCoreCreate(void) {
core->cheatDevice = _GBCoreCheatDevice; core->cheatDevice = _GBCoreCheatDevice;
core->savedataClone = _GBCoreSavedataClone; core->savedataClone = _GBCoreSavedataClone;
core->savedataRestore = _GBCoreSavedataRestore; core->savedataRestore = _GBCoreSavedataRestore;
core->listVideoLayers = _GBCoreListVideoLayers;
core->listAudioChannels = _GBCoreListAudioChannels;
core->enableVideoLayer = _GBCoreEnableVideoLayer;
core->enableAudioChannel = _GBCoreEnableAudioChannel;
return core; return core;
} }

View File

@ -26,6 +26,23 @@
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>
#endif #endif
const static struct mCoreChannelInfo _GBAVideoLayers[] = {
{ 0, "bg0", "Background 0", NULL },
{ 1, "bg1", "Background 1", NULL },
{ 2, "bg2", "Background 2", NULL },
{ 3, "bg3", "Background 3", NULL },
{ 4, "obj", "Objects", NULL },
};
const static struct mCoreChannelInfo _GBAAudioChannels[] = {
{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
{ 1, "ch1", "PSG Channel 1", "Square" },
{ 2, "ch2", "PSG Channel 2", "PCM" },
{ 3, "ch3", "PSG Channel 3", "Noise" },
{ 4, "chA", "FIFO Channel A", NULL },
{ 5, "chB", "FIFO Channel B", NULL },
};
struct GBACore { struct GBACore {
struct mCore d; struct mCore d;
struct GBAVideoSoftwareRenderer renderer; struct GBAVideoSoftwareRenderer renderer;
@ -600,6 +617,54 @@ static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t
return success; return success;
} }
static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAVideoLayers;
return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
}
static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAAudioChannels;
return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
}
static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct GBA* gba = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gba->video.renderer->disableBG[id] = !enable;
break;
case 4:
gba->video.renderer->disableOBJ = !enable;
break;
default:
break;
}
}
static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
struct GBA* gba = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gba->audio.psg.forceDisableCh[id] = !enable;
break;
case 4:
gba->audio.forceDisableChA = !enable;
case 5:
gba->audio.forceDisableChB = !enable;
break;
default:
break;
}
}
struct mCore* GBACoreCreate(void) { struct mCore* GBACoreCreate(void) {
struct GBACore* gbacore = malloc(sizeof(*gbacore)); struct GBACore* gbacore = malloc(sizeof(*gbacore));
struct mCore* core = &gbacore->d; struct mCore* core = &gbacore->d;
@ -670,5 +735,9 @@ struct mCore* GBACoreCreate(void) {
core->cheatDevice = _GBACoreCheatDevice; core->cheatDevice = _GBACoreCheatDevice;
core->savedataClone = _GBACoreSavedataClone; core->savedataClone = _GBACoreSavedataClone;
core->savedataRestore = _GBACoreSavedataRestore; core->savedataRestore = _GBACoreSavedataRestore;
core->listVideoLayers = _GBACoreListVideoLayers;
core->listAudioChannels = _GBACoreListAudioChannels;
core->enableVideoLayer = _GBACoreEnableVideoLayer;
core->enableAudioChannel = _GBACoreEnableAudioChannel;
return core; return core;
} }

View File

@ -92,7 +92,7 @@ QString ConfigController::s_configDir;
ConfigController::ConfigController(QObject* parent) ConfigController::ConfigController(QObject* parent)
: QObject(parent) : QObject(parent)
, m_opts() , m_opts{}
{ {
QString fileName = configDir(); QString fileName = configDir();
fileName.append(QDir::separator()); fileName.append(QDir::separator());

View File

@ -57,8 +57,8 @@ GameController::GameController(QObject* parent)
, m_turboForced(false) , m_turboForced(false)
, m_turboSpeed(-1) , m_turboSpeed(-1)
, m_wasPaused(false) , m_wasPaused(false)
, m_audioChannels{ true, true, true, true, true, true } , m_audioChannels()
, m_videoLayers{ true, true, true, true, true } , m_videoLayers()
, m_autofire{} , m_autofire{}
, m_autofireStatus{} , m_autofireStatus{}
, m_inputController(nullptr) , m_inputController(nullptr)
@ -69,6 +69,7 @@ GameController::GameController(QObject* parent)
, m_backupSaveState(nullptr) , m_backupSaveState(nullptr)
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
, m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
, m_preload(false)
, m_override(nullptr) , m_override(nullptr)
{ {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -90,35 +91,17 @@ GameController::GameController(QObject* parent)
context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource()); context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource());
context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble()); context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble());
#ifdef M_CORE_GBA for (size_t i = 0; i < controller->m_audioChannels.size(); ++i) {
GBA* gba = static_cast<GBA*>(context->core->board); context->core->enableAudioChannel(context->core, i, controller->m_audioChannels[i]);
#endif }
#ifdef M_CORE_GB for (size_t i = 0; i < controller->m_videoLayers.size(); ++i) {
GB* gb = static_cast<GB*>(context->core->board); context->core->enableVideoLayer(context->core, i, controller->m_videoLayers[i]);
#endif }
switch (context->core->platform(context->core)) { switch (context->core->platform(context->core)) {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
case PLATFORM_GBA: case PLATFORM_GBA:
context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, &controller->m_lux); context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, &controller->m_lux);
gba->audio.psg.forceDisableCh[0] = !controller->m_audioChannels[0];
gba->audio.psg.forceDisableCh[1] = !controller->m_audioChannels[1];
gba->audio.psg.forceDisableCh[2] = !controller->m_audioChannels[2];
gba->audio.psg.forceDisableCh[3] = !controller->m_audioChannels[3];
gba->audio.forceDisableChA = !controller->m_audioChannels[4];
gba->audio.forceDisableChB = !controller->m_audioChannels[5];
gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0];
gba->video.renderer->disableBG[1] = !controller->m_videoLayers[1];
gba->video.renderer->disableBG[2] = !controller->m_videoLayers[2];
gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3];
gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
break;
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
gb->audio.forceDisableCh[0] = !controller->m_audioChannels[0];
gb->audio.forceDisableCh[1] = !controller->m_audioChannels[1];
gb->audio.forceDisableCh[2] = !controller->m_audioChannels[2];
gb->audio.forceDisableCh[3] = !controller->m_audioChannels[3];
break; break;
#endif #endif
default: default:
@ -476,11 +459,20 @@ void GameController::openGame(bool biosOnly) {
QByteArray bytes; QByteArray bytes;
if (!biosOnly) { if (!biosOnly) {
bytes = m_fname.toUtf8(); bytes = m_fname.toUtf8();
if (m_vf) { if (m_preload) {
m_threadContext.core->loadROM(m_threadContext.core, m_vf); if (m_vf) {
mCorePreloadVF(m_threadContext.core, m_vf);
} else {
mCorePreloadFile(m_threadContext.core, bytes.constData());
mDirectorySetDetachBase(&m_threadContext.core->dirs);
}
} else { } else {
mCoreLoadFile(m_threadContext.core, bytes.constData()); if (m_vf) {
mDirectorySetDetachBase(&m_threadContext.core->dirs); m_threadContext.core->loadROM(m_threadContext.core, m_vf);
} else {
mCoreLoadFile(m_threadContext.core, bytes.constData());
mDirectorySetDetachBase(&m_threadContext.core->dirs);
}
} }
} else { } else {
bytes = m_bios.toUtf8(); bytes = m_bios.toUtf8();
@ -880,47 +872,13 @@ void GameController::setAudioChannelEnabled(int channel, bool enable) {
if (channel > 5 || channel < 0) { if (channel > 5 || channel < 0) {
return; return;
} }
#ifdef M_CORE_GBA m_audioChannels.reserve(channel + 1);
GBA* gba = static_cast<GBA*>(m_threadContext.core->board); while (m_audioChannels.size() <= channel) {
#endif m_audioChannels.append(true);
#ifdef M_CORE_GB }
GB* gb = static_cast<GB*>(m_threadContext.core->board);
#endif
m_audioChannels[channel] = enable; m_audioChannels[channel] = enable;
if (isLoaded()) { if (isLoaded()) {
switch (channel) { m_threadContext.core->enableAudioChannel(m_threadContext.core, channel, enable);
case 0:
case 1:
case 2:
case 3:
switch (m_threadContext.core->platform(m_threadContext.core)) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
gba->audio.psg.forceDisableCh[channel] = !enable;
break;
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
gb->audio.forceDisableCh[channel] = !enable;
break;
#endif
default:
break;
}
break;
#ifdef M_CORE_GBA
case 4:
if (m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) {
gba->audio.forceDisableChA = !enable;
}
break;
case 5:
if (m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) {
gba->audio.forceDisableChB = !enable;
}
break;
#endif
}
} }
} }
@ -939,23 +897,14 @@ void GameController::setVideoLayerEnabled(int layer, bool enable) {
if (layer > 4 || layer < 0) { if (layer > 4 || layer < 0) {
return; return;
} }
m_videoLayers[layer] = enable; m_videoLayers.reserve(layer + 1);
#ifdef M_CORE_GBA while (m_videoLayers.size() <= layer) {
if (isLoaded() && m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) { m_videoLayers.append(true);
GBA* gba = static_cast<GBA*>(m_threadContext.core->board); }
switch (layer) { m_videoLayers[layer] = enable;
case 0: if (isLoaded()) {
case 1: m_threadContext.core->enableVideoLayer(m_threadContext.core, layer, enable);
case 2:
case 3:
gba->video.renderer->disableBG[layer] = !enable;
break;
case 4:
gba->video.renderer->disableOBJ = !enable;
break;
}
} }
#endif
} }
void GameController::setFPSTarget(float fps) { void GameController::setFPSTarget(float fps) {
@ -1182,6 +1131,10 @@ void GameController::setLoadStateExtdata(int flags) {
m_loadStateFlags = flags; m_loadStateFlags = flags;
} }
void GameController::setPreload(bool preload) {
m_preload = preload;
}
void GameController::setLuminanceValue(uint8_t value) { void GameController::setLuminanceValue(uint8_t value) {
m_luxValue = value; m_luxValue = value;
value = std::max<int>(value - 0x16, 0); value = std::max<int>(value - 0x16, 0);

View File

@ -156,6 +156,7 @@ public slots:
void reloadAudioDriver(); void reloadAudioDriver();
void setSaveStateExtdata(int flags); void setSaveStateExtdata(int flags);
void setLoadStateExtdata(int flags); void setLoadStateExtdata(int flags);
void setPreload(bool);
#ifdef USE_PNG #ifdef USE_PNG
void screenshot(); void screenshot();
@ -224,8 +225,8 @@ private:
std::shared_ptr<mTileCache> m_tileCache; std::shared_ptr<mTileCache> m_tileCache;
bool m_audioChannels[6]; QList<bool> m_audioChannels;
bool m_videoLayers[5]; QList<bool> m_videoLayers;
bool m_autofire[GBA_KEY_MAX]; bool m_autofire[GBA_KEY_MAX];
int m_autofireStatus[GBA_KEY_MAX]; int m_autofireStatus[GBA_KEY_MAX];
@ -236,6 +237,8 @@ private:
int m_saveStateFlags; int m_saveStateFlags;
int m_loadStateFlags; int m_loadStateFlags;
bool m_preload;
InputController* m_inputController; InputController* m_inputController;
MultiplayerController* m_multiplayer; MultiplayerController* m_multiplayer;

View File

@ -191,6 +191,7 @@ void SettingsView::updateConfig() {
saveSetting("screenshotPath", m_ui.screenshotPath); saveSetting("screenshotPath", m_ui.screenshotPath);
saveSetting("patchPath", m_ui.patchPath); saveSetting("patchPath", m_ui.patchPath);
saveSetting("showLibrary", m_ui.showLibrary); saveSetting("showLibrary", m_ui.showLibrary);
saveSetting("preload", m_ui.preload);
if (m_ui.fastForwardUnbounded->isChecked()) { if (m_ui.fastForwardUnbounded->isChecked()) {
saveSetting("fastForwardRatio", "-1"); saveSetting("fastForwardRatio", "-1");
@ -273,6 +274,7 @@ void SettingsView::reloadConfig() {
loadSetting("screenshotPath", m_ui.screenshotPath); loadSetting("screenshotPath", m_ui.screenshotPath);
loadSetting("patchPath", m_ui.patchPath); loadSetting("patchPath", m_ui.patchPath);
loadSetting("showLibrary", m_ui.showLibrary); loadSetting("showLibrary", m_ui.showLibrary);
loadSetting("preload", m_ui.preload);
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble(); double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
if (fastForwardRatio <= 0) { if (fastForwardRatio <= 0) {

View File

@ -568,21 +568,21 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2"> <item row="8" column="0" colspan="2">
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Savestate extra data:</string> <string>Savestate extra data:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="QCheckBox" name="saveStateScreenshot"> <widget class="QCheckBox" name="saveStateScreenshot">
<property name="text"> <property name="text">
<string>Screenshot</string> <string>Screenshot</string>
@ -592,7 +592,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QCheckBox" name="saveStateSave"> <widget class="QCheckBox" name="saveStateSave">
<property name="text"> <property name="text">
<string>Save data</string> <string>Save data</string>
@ -602,7 +602,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<widget class="QCheckBox" name="saveStateCheats"> <widget class="QCheckBox" name="saveStateCheats">
<property name="text"> <property name="text">
<string>Cheat codes</string> <string>Cheat codes</string>
@ -612,14 +612,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0"> <item row="13" column="0">
<widget class="QLabel" name="label_25"> <widget class="QLabel" name="label_25">
<property name="text"> <property name="text">
<string>Load extra data:</string> <string>Load extra data:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QCheckBox" name="loadStateScreenshot"> <widget class="QCheckBox" name="loadStateScreenshot">
<property name="text"> <property name="text">
<string>Screenshot</string> <string>Screenshot</string>
@ -629,21 +629,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="QCheckBox" name="loadStateSave"> <widget class="QCheckBox" name="loadStateSave">
<property name="text"> <property name="text">
<string>Save data</string> <string>Save data</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="15" column="1">
<widget class="QCheckBox" name="loadStateCheats"> <widget class="QCheckBox" name="loadStateCheats">
<property name="text"> <property name="text">
<string>Cheat codes</string> <string>Cheat codes</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0" colspan="2"> <item row="12" column="0" colspan="2">
<widget class="Line" name="line_9"> <widget class="Line" name="line_9">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -660,6 +660,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1">
<widget class="QCheckBox" name="preload">
<property name="text">
<string>Preload entire ROM into memory</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="bios"> <widget class="QWidget" name="bios">

View File

@ -335,19 +335,6 @@ void Window::reloadConfig() {
m_log.setLevels(opts->logLevel); m_log.setLevels(opts->logLevel);
QString saveStateExtdata = m_config->getOption("saveStateExtdata");
bool ok;
int flags = saveStateExtdata.toInt(&ok);
if (ok) {
m_controller->setSaveStateExtdata(flags);
}
QString loadStateExtdata = m_config->getOption("loadStateExtdata");
flags = loadStateExtdata.toInt(&ok);
if (ok) {
m_controller->setLoadStateExtdata(flags);
}
m_controller->setConfig(m_config->config()); m_controller->setConfig(m_config->config());
m_display->lockAspectRatio(opts->lockAspectRatio); m_display->lockAspectRatio(opts->lockAspectRatio);
m_display->filter(opts->resampleVideo); m_display->filter(opts->resampleVideo);
@ -1547,11 +1534,19 @@ void Window::setupMenu(QMenuBar* menubar) {
saveStateExtdata->connect([this](const QVariant& value) { saveStateExtdata->connect([this](const QVariant& value) {
m_controller->setSaveStateExtdata(value.toInt()); m_controller->setSaveStateExtdata(value.toInt());
}, this); }, this);
m_config->updateOption("saveStateExtdata");
ConfigOption* loadStateExtdata = m_config->addOption("loadStateExtdata"); ConfigOption* loadStateExtdata = m_config->addOption("loadStateExtdata");
loadStateExtdata->connect([this](const QVariant& value) { loadStateExtdata->connect([this](const QVariant& value) {
m_controller->setLoadStateExtdata(value.toInt()); m_controller->setLoadStateExtdata(value.toInt());
}, this); }, this);
m_config->updateOption("loadStateExtdata");
ConfigOption* preload = m_config->addOption("preload");
preload->connect([this](const QVariant& value) {
m_controller->setPreload(value.toBool());
}, this);
m_config->updateOption("preload");
QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu); QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu);
connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen())); connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen()));

View File

@ -5,8 +5,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/string.h> #include <mgba-util/string.h>
#include <mgba-util/vector.h>
#include <string.h> #include <string.h>
DEFINE_VECTOR(StringList, char*);
#ifndef HAVE_STRNDUP #ifndef HAVE_STRNDUP
char* strndup(const char* start, size_t len) { char* strndup(const char* start, size_t len) {
// This is suboptimal, but anything recent should have strndup // This is suboptimal, but anything recent should have strndup