diff --git a/CHANGES b/CHANGES index fec76ac96..f650b3d50 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,10 @@ Features: - Improved memory viewer - GB: LR35902/GB-Z80 disassembler - Configuration of gamepad hats + - Qt: Spanish translation (by Kevin López) + - Add option for whether rewinding restores save games + - Qt: German translation (by Lothar Serra Mari) + - Savestates now contain any RTC override data Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior @@ -23,6 +27,12 @@ Bugfixes: - GB MBC: Fix ROM bank overflows getting set to bank 0 - Qt: Fix timing issues on high refresh rate monitors - GBA Savedata: Fix savedata unmasking (fixes mgba.io/i/441) + - Util: Fix overflow when loading invalid UPS patches + - Tools: Fix recurring multiple times over the same library + - GBA I/O: Handle audio registers specially when deserializing + - Util: Fix highest-fd socket not being returned by SocketAccept + - Qt: Fix linking after some windows have been closed + - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers @@ -60,6 +70,12 @@ Misc: - SDL: Automatically map controllers when plugged in - Qt: Automatically load controller profile when plugged in - OpenGL: Add xBR-lv2 shader + - GBA, GB: ROM is now unloaded if a patch is applied + - Util: Add 8-bit PNG write support + - Qt: Rename "Resample video" option to "Bilinear filtering" + - GBA Video: Optimize when BLD* registers are written frequently + - Core: Cores can now have multiple sets of callbacks + - GBA: Ignore invalid opcodes used by the Wii U VC emulator 0.5.2: (2016-12-31) Bugfixes: diff --git a/README.md b/README.md index d8f90ba4a..703d9986a 100644 --- a/README.md +++ b/README.md @@ -96,10 +96,16 @@ This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies th #### Windows developer building -To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MinGW-w64 Win32 Shell") and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time): +To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MinGW-w64 Win32 Shell") (or the 64-bit version "MinGW-w64 Win64 Shell" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time): +For x86 (32 bit) builds: + pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2} +For x86_64 (64 bit) builds: + + pacman -Sy mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2} + Check out the source code by running this command: git clone https://github.com/mgba-emu/mgba.git @@ -112,7 +118,7 @@ Then finally build it by running these commands: cmake .. -G "MSYS Makefiles" make -Please note that this build of mGBA for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. +Please note that this build of mGBA for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), a tool called "[Dependency Walker](http://dependencywalker.com)" can be used to see which additional DLL files need to be shipped with the mGBA executable. ### Dependencies diff --git a/include/mgba-util/configuration.h b/include/mgba-util/configuration.h index e2e6ba7d3..d9aacddeb 100644 --- a/include/mgba-util/configuration.h +++ b/include/mgba-util/configuration.h @@ -38,6 +38,7 @@ bool ConfigurationWrite(const struct Configuration*, const char* path); bool ConfigurationWriteSection(const struct Configuration*, const char* path, const char* section); void ConfigurationEnumerateSections(const struct Configuration* configuration, void (*handler)(const char* sectionName, void* user), void* user); +void ConfigurationEnumerate(const struct Configuration* configuration, const char* section, void (*handler)(const char* key, const char* value, void* user), void* user); CXX_GUARD_END diff --git a/include/mgba-util/png-io.h b/include/mgba-util/png-io.h index 1f8fea331..20803303e 100644 --- a/include/mgba-util/png-io.h +++ b/include/mgba-util/png-io.h @@ -22,7 +22,10 @@ enum { png_structp PNGWriteOpen(struct VFile* source); png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); +png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height); +bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries); bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels); +bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels); bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data); void PNGWriteClose(png_structp png, png_infop info); diff --git a/include/mgba-util/socket.h b/include/mgba-util/socket.h index 327c2c69c..ad00696c0 100644 --- a/include/mgba-util/socket.h +++ b/include/mgba-util/socket.h @@ -293,10 +293,11 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc errors[i] = INVALID_SOCKET; } } + ++maxFd; struct timeval tv; tv.tv_sec = timeoutMillis / 1000; tv.tv_usec = (timeoutMillis % 1000) * 1000; - int result = select(maxFd + 1, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv); + int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv); int r = 0; int w = 0; int e = 0; diff --git a/include/mgba-util/string.h b/include/mgba-util/string.h index 831ce8e44..7e9465719 100644 --- a/include/mgba-util/string.h +++ b/include/mgba-util/string.h @@ -21,6 +21,7 @@ char* strdup(const char* str); char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len); bool endswith(const char* restrict s1, const char* restrict end); +bool startswith(const char* restrict s1, const char* restrict start); size_t toUtf8(uint32_t unichar, char* buffer); int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length); diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index 0839d61b2..664221b7d 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -27,7 +27,8 @@ CXX_GUARD_START void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference); \ void NAME ## EnsureCapacity(struct NAME* vector, size_t capacity); \ size_t NAME ## Size(const struct NAME* vector); \ - size_t NAME ## Index(const struct NAME* vector, const TYPE* member); + size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \ + void NAME ## Copy(struct NAME* dest, const struct NAME* src); #define DEFINE_VECTOR(NAME, TYPE) \ void NAME ## Init(struct NAME* vector, size_t capacity) { \ @@ -85,6 +86,11 @@ CXX_GUARD_START size_t NAME ## Index(const struct NAME* vector, const TYPE* member) { \ return member - (const TYPE*) vector->vector; \ } \ + void NAME ## Copy(struct NAME* dest, const struct NAME* src) { \ + NAME ## EnsureCapacity(dest, src->size); \ + memcpy(dest->vector, src->vector, src->size * sizeof(TYPE)); \ + dest->size = src->size; \ + } \ CXX_GUARD_END diff --git a/include/mgba/core/config.h b/include/mgba/core/config.h index 2fac1ff37..6dbc73dc4 100644 --- a/include/mgba/core/config.h +++ b/include/mgba/core/config.h @@ -19,6 +19,12 @@ struct mCoreConfig { char* port; }; +enum mCoreConfigLevel { + mCONFIG_LEVEL_DEFAULT = 0, + mCONFIG_LEVEL_CUSTOM, + mCONFIG_LEVEL_OVERRIDE, +}; + struct mCoreOptions { char* bios; bool skipBios; @@ -27,6 +33,7 @@ struct mCoreOptions { int frameskip; bool rewindEnable; int rewindBufferCapacity; + bool rewindSave; float fpsTarget; size_t audioBuffers; unsigned sampleRate; @@ -89,6 +96,8 @@ void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig* void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts); void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts); +void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user); + struct Configuration* mCoreConfigGetInput(struct mCoreConfig*); struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig*); const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig*); diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 4954d12d1..0fc0402b3 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -40,7 +40,6 @@ enum mCoreChecksumType { CHECKSUM_CRC32, }; -struct mRTCSource; struct mCoreConfig; struct mCoreSync; struct mStateExtdata; @@ -59,6 +58,8 @@ struct mCore { struct mCoreConfig config; struct mCoreOptions opts; + struct mRTCGenericSource rtc; + bool (*init)(struct mCore*); void (*deinit)(struct mCore*); @@ -77,7 +78,8 @@ struct mCore { void (*setAudioBufferSize)(struct mCore*, size_t samples); size_t (*getAudioBufferSize)(struct mCore*); - void (*setCoreCallbacks)(struct mCore*, struct mCoreCallbacks*); + void (*addCoreCallbacks)(struct mCore*, struct mCoreCallbacks*); + void (*clearCoreCallbacks)(struct mCore*); void (*setAVStream)(struct mCore*, struct mAVStream*); bool (*isROM)(struct VFile* vf); @@ -115,7 +117,6 @@ struct mCore { void (*getGameTitle)(const struct mCore*, char* title); void (*getGameCode)(const struct mCore*, char* title); - void (*setRTC)(struct mCore*, struct mRTCSource*); void (*setRotation)(struct mCore*, struct mRotationSource*); void (*setRumble)(struct mCore*, struct mRumble*); @@ -174,6 +175,8 @@ void mCoreInitConfig(struct mCore* core, const char* port); void mCoreLoadConfig(struct mCore* core); void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config); +void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); + CXX_GUARD_END #endif diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 2e78390cf..933e8d2d5 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -10,7 +10,10 @@ CXX_GUARD_START +#include + struct mCore; +struct mStateExtdataItem; #ifdef COLOR_16_BIT typedef uint16_t color_t; @@ -37,6 +40,8 @@ struct mCoreCallbacks { void (*coreCrashed)(void* context); }; +DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); + struct mAVStream { void (*videoDimensionsChanged)(struct mAVStream*, unsigned width, unsigned height); void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride); @@ -65,12 +70,16 @@ struct mRTCSource { void (*sample)(struct mRTCSource*); time_t (*unixTime)(struct mRTCSource*); + + void (*serialize)(struct mRTCSource*, struct mStateExtdataItem*); + bool (*deserialize)(struct mRTCSource*, const struct mStateExtdataItem*); }; enum mRTCGenericType { RTC_NO_OVERRIDE, RTC_FIXED, - RTC_FAKE_EPOCH + RTC_FAKE_EPOCH, + RTC_CUSTOM_START = 0x1000 }; struct mRTCGenericSource { @@ -78,6 +87,13 @@ struct mRTCGenericSource { struct mCore* p; enum mRTCGenericType override; int64_t value; + struct mRTCSource* custom; +}; + +struct mRTCGenericState { + int32_t type; + int32_t padding; + int64_t value; }; void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core); diff --git a/include/mgba/core/log.h b/include/mgba/core/log.h index 6c8615c93..5a6fc6128 100644 --- a/include/mgba/core/log.h +++ b/include/mgba/core/log.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#include + enum mLogLevel { mLOG_FATAL = 0x01, mLOG_ERROR = 0x02, @@ -22,29 +24,47 @@ enum mLogLevel { mLOG_ALL = 0x7F }; +struct Table; +struct mLogFilter { + int defaultLevels; + struct Table categories; + struct Table levels; +}; + struct mLogger { void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args); + struct mLogFilter* filter; }; struct mLogger* mLogGetContext(void); void mLogSetDefaultLogger(struct mLogger*); -int mLogGenerateCategory(const char*); +int mLogGenerateCategory(const char*, const char*); const char* mLogCategoryName(int); +const char* mLogCategoryId(int); +int mLogCategoryById(const char*); + +struct mCoreConfig; +void mLogFilterInit(struct mLogFilter*); +void mLogFilterDeinit(struct mLogFilter*); +void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*); +void mLogFilterSet(struct mLogFilter*, const char* category, int levels); +bool mLogFilterTest(struct mLogFilter*, int category, enum mLogLevel level); ATTRIBUTE_FORMAT(printf, 3, 4) void mLog(int category, enum mLogLevel level, const char* format, ...); #define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__) -#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); -#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME) \ +#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); extern const char* _mLOG_CAT_ ## CATEGORY ## _ID; +#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \ int _mLOG_CAT_ ## CATEGORY (void) { \ static int category = 0; \ if (!category) { \ - category = mLogGenerateCategory(NAME); \ + category = mLogGenerateCategory(NAME, ID); \ } \ return category; \ - } + } \ + const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID; mLOG_DECLARE_CATEGORY(STATUS) diff --git a/include/mgba/core/rewind.h b/include/mgba/core/rewind.h index a1a748d3d..8762ff76a 100644 --- a/include/mgba/core/rewind.h +++ b/include/mgba/core/rewind.h @@ -19,6 +19,7 @@ struct mCoreRewindContext { struct mCoreRewindPatches patchMemory; size_t current; size_t size; + int stateFlags; struct VFile* previousState; struct VFile* currentState; }; diff --git a/include/mgba/core/serialize.h b/include/mgba/core/serialize.h index 266cab0ca..0951fc4c3 100644 --- a/include/mgba/core/serialize.h +++ b/include/mgba/core/serialize.h @@ -15,12 +15,14 @@ enum mStateExtdataTag { EXTDATA_SCREENSHOT = 1, EXTDATA_SAVEDATA = 2, EXTDATA_CHEATS = 3, + EXTDATA_RTC = 4, EXTDATA_MAX }; #define SAVESTATE_SCREENSHOT 1 #define SAVESTATE_SAVEDATA 2 #define SAVESTATE_CHEATS 4 +#define SAVESTATE_RTC 8 struct mStateExtdataItem { int32_t size; diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index b0a60ac80..f586bfd3b 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -58,7 +58,6 @@ struct mCoreThread { bool frameWasOn; struct mThreadLogger logger; - enum mLogLevel logLevel; ThreadCallback startCallback; ThreadCallback resetCallback; ThreadCallback cleanCallback; diff --git a/include/mgba/core/tile-cache.h b/include/mgba/core/tile-cache.h index 0a71c690a..540705af9 100644 --- a/include/mgba/core/tile-cache.h +++ b/include/mgba/core/tile-cache.h @@ -58,6 +58,8 @@ void mTileCacheSetPalette(struct mTileCache* cache, int palette); const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId); const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId); +const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId); +const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId); CXX_GUARD_END diff --git a/include/mgba/internal/ds/ds.h b/include/mgba/internal/ds/ds.h index 88edf8275..f839592b1 100644 --- a/include/mgba/internal/ds/ds.h +++ b/include/mgba/internal/ds/ds.h @@ -113,7 +113,7 @@ struct DS { struct mAVStream* stream; struct mKeyCallback* keyCallback; - struct mCoreCallbacks* coreCallbacks; + struct mCoreCallbacksList coreCallbacks; struct mTimingEvent divEvent; struct mTimingEvent sqrtEvent; diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index d047f1c2c..05362c7bd 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -11,6 +11,7 @@ CXX_GUARD_START #include +#include #include #include @@ -46,7 +47,6 @@ enum GBIRQVector { struct LR35902Core; struct mCoreSync; struct mAVStream; -struct mCoreCallbacks; struct GB { struct mCPUComponent d; @@ -63,7 +63,7 @@ struct GB { uint8_t* keySource; - void* pristineRom; + bool isPristine; size_t pristineRomSize; size_t yankedRomSize; uint32_t romCrc32; @@ -76,7 +76,7 @@ struct GB { int32_t sramDirtAge; bool sramMaskWriteback; - struct mCoreCallbacks* coreCallbacks; + struct mCoreCallbacksList coreCallbacks; struct mAVStream* stream; bool cpuBlocked; diff --git a/include/mgba/internal/gb/mbc.h b/include/mgba/internal/gb/mbc.h index 32496177f..6242343e8 100644 --- a/include/mgba/internal/gb/mbc.h +++ b/include/mgba/internal/gb/mbc.h @@ -17,7 +17,7 @@ mLOG_DECLARE_CATEGORY(GB_MBC); struct GB; struct GBMemory; void GBMBCInit(struct GB* gb); -void GBMBCSwitchBank(struct GBMemory* memory, int bank); +void GBMBCSwitchBank(struct GB* gb, int bank); void GBMBCSwitchSramBank(struct GB* gb, int bank); struct GBMBCRTCSaveBuffer { diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 73171095b..e5b1f8899 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -90,7 +90,7 @@ struct GBA { struct mRumble* rumble; struct GBARRContext* rr; - void* pristineRom; + bool isPristine; size_t pristineRomSize; size_t yankedRomSize; uint32_t romCrc32; @@ -100,7 +100,7 @@ struct GBA { struct mAVStream* stream; struct mKeyCallback* keyCallback; struct mStopCallback* stopCallback; - struct mCoreCallbacks* coreCallbacks; + struct mCoreCallbacksList coreCallbacks; enum GBAIdleLoopOptimization idleOptimization; uint32_t idleLoop; diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index 813da5e7a..8e583b25d 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -129,6 +129,7 @@ struct GBAVideoSoftwareRenderer { unsigned target1Bd; unsigned target2Obj; unsigned target2Bd; + bool blendDirty; enum BlendEffect blendEffect; color_t normalPalette[512]; color_t variantPalette[512]; diff --git a/res/shaders/wiiu.shader/manifest.ini b/res/shaders/wiiu.shader/manifest.ini new file mode 100644 index 000000000..2b92c1d74 --- /dev/null +++ b/res/shaders/wiiu.shader/manifest.ini @@ -0,0 +1,11 @@ +[shader] +name=Wii U +author=Prof. 9 +description=Uses the color palette from the Wii U Virtual Console. Enable bilinear filtering to further mimic Wii U output. +passes=1 + +[pass.0] +fragmentShader=wiiu.fs +blend=1 +width=960 +height=640 diff --git a/res/shaders/wiiu.shader/wiiu.fs b/res/shaders/wiiu.shader/wiiu.fs new file mode 100644 index 000000000..268c5d229 --- /dev/null +++ b/res/shaders/wiiu.shader/wiiu.fs @@ -0,0 +1,22 @@ +varying vec2 texCoord; +uniform sampler2D tex; +uniform vec2 texSize; + +const float scale[32] = float[]( + 0.0/255.0, 6.0/255.0, 12.0/255.0, 18.0/255.0, 24.0/255.0, 31.0/255.0, 37.0/255.0, 43.0/255.0, + 49.0/255.0, 55.0/255.0, 61.0/255.0, 67.0/255.0, 73.0/255.0, 79.0/255.0, 86.0/255.0, 92.0/255.0, + 98.0/255.0, 104.0/255.0, 111.0/255.0, 117.0/255.0, 123.0/255.0, 129.0/255.0, 135.0/255.0, 141.0/255.0, + 148.0/255.0, 154.0/255.0, 159.0/255.0, 166.0/255.0, 172.0/255.0, 178.0/255.0, 184.0/255.0, 191.0/255.0 +); + +void main() { + vec4 color = texture2D(tex, texCoord); + color.rgb = round(color.rgb * 31.0); + color = vec4( + scale[int(color.r)], + scale[int(color.g)], + scale[int(color.b)], + 1.0 + ); + gl_FragColor = color; +} \ No newline at end of file diff --git a/src/core/cheats.c b/src/core/cheats.c index fe6e59c99..83bdde0b8 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -13,7 +13,7 @@ const uint32_t M_CHEAT_DEVICE_ID = 0xABADC0DE; -mLOG_DEFINE_CATEGORY(CHEATS, "Cheats"); +mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats"); DEFINE_VECTOR(mCheatList, struct mCheat); DEFINE_VECTOR(mCheatSets, struct mCheatSet*); diff --git a/src/core/config.c b/src/core/config.c index 659efd3e8..b4d654c54 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -29,6 +29,13 @@ #define SECTION_NAME_MAX 128 +struct mCoreConfigEnumerateData { + void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user); + const char* prefix; + void* user; + enum mCoreConfigLevel level; +}; + static const char* _lookupValue(const struct mCoreConfig* config, const char* key) { const char* value; if (config->port) { @@ -321,6 +328,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) _lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); + _lookupIntValue(config, "rewindSave", &opts->rewindSave); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget); unsigned audioBuffers; if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) { @@ -376,6 +384,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity); + ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); @@ -391,6 +400,22 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver); } +static void _configEnum(const char* key, const char* value, void* user) { + struct mCoreConfigEnumerateData* data = user; + if (!data->prefix || startswith(key, data->prefix)) { + data->handler(key, value, data->level, data->user); + } +} + +void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user) { + struct mCoreConfigEnumerateData handlerData = { handler, prefix, user, mCONFIG_LEVEL_DEFAULT }; + ConfigurationEnumerate(&config->defaultsTable, config->port, _configEnum, &handlerData); + handlerData.level = mCONFIG_LEVEL_CUSTOM; + ConfigurationEnumerate(&config->configTable, config->port, _configEnum, &handlerData); + handlerData.level = mCONFIG_LEVEL_OVERRIDE; + ConfigurationEnumerate(&config->overridesTable, config->port, _configEnum, &handlerData); +} + // These two are basically placeholders in case the internal layout changes, e.g. for loading separate files struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) { return &config->configTable; diff --git a/src/core/core.c b/src/core/core.c index 43cd1f24d..5c65297c7 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -240,3 +240,8 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config } core->loadConfig(core, config); } + +void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { + core->rtc.custom = rtc; + core->rtc.override = RTC_CUSTOM_START; +} diff --git a/src/core/interface.c b/src/core/interface.c index 5317da562..961b83619 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -6,24 +6,103 @@ #include #include +#include + +DEFINE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); + +static void _rtcGenericSample(struct mRTCSource* source) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + switch (rtc->override) { + default: + if (rtc->custom->sample) { + return rtc->custom->sample(rtc->custom); + } + break; + case RTC_NO_OVERRIDE: + case RTC_FIXED: + case RTC_FAKE_EPOCH: + break; + } +} static time_t _rtcGenericCallback(struct mRTCSource* source) { struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; switch (rtc->override) { - case RTC_NO_OVERRIDE: default: + if (rtc->custom->unixTime) { + return rtc->custom->unixTime(rtc->custom); + } + // Fall through + case RTC_NO_OVERRIDE: return time(0); case RTC_FIXED: - return rtc->value; + return rtc->value / 1000LL; case RTC_FAKE_EPOCH: - return rtc->value + rtc->p->frameCounter(rtc->p) * (int64_t) rtc->p->frameCycles(rtc->p) / rtc->p->frequency(rtc->p); + return (rtc->value + rtc->p->frameCounter(rtc->p) * (rtc->p->frameCycles(rtc->p) * 1000LL) / rtc->p->frequency(rtc->p)) / 1000LL; } } +static void _rtcGenericSerialize(struct mRTCSource* source, struct mStateExtdataItem* item) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + struct mRTCGenericState state = { + .type = rtc->override, + .padding = 0, + .value = rtc->value + }; + void* data; + if (rtc->override >= RTC_CUSTOM_START && rtc->custom->serialize) { + rtc->custom->serialize(rtc->custom, item); + data = malloc(item->size + sizeof(state)); + uint8_t* oldData = data; + oldData += sizeof(state); + memcpy(oldData, item->data, item->size); + item->size += sizeof(state); + if (item->clean) { + item->clean(item->data); + } + } else { + item->size = sizeof(state); + data = malloc(item->size); + } + memcpy(data, &state, sizeof(state)); + item->data = data; + item->clean = free; +} + +static bool _rtcGenericDeserialize(struct mRTCSource* source, const struct mStateExtdataItem* item) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + struct mRTCGenericState* state = item->data; + if (!state || item->size < (ssize_t) sizeof(*state)) { + return false; + } + if (state->type >= RTC_CUSTOM_START) { + if (!rtc->custom) { + return false; + } + if (rtc->custom->deserialize) { + uint8_t* oldData = item->data; + oldData += sizeof(state); + struct mStateExtdataItem fakeItem = { + .size = item->size - sizeof(*state), + .data = oldData, + .clean = NULL + }; + if (!rtc->custom->deserialize(rtc->custom, &fakeItem)) { + return false; + } + } + } + rtc->value = state->value; + rtc->override = state->type; + return true; +} + void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) { rtc->p = core; rtc->override = RTC_NO_OVERRIDE; rtc->value = 0; - rtc->d.sample = 0; + rtc->d.sample = _rtcGenericSample; rtc->d.unixTime = _rtcGenericCallback; + rtc->d.serialize = _rtcGenericSerialize; + rtc->d.deserialize = _rtcGenericDeserialize; } diff --git a/src/core/log.c b/src/core/log.c index 65ec99924..6af5724b9 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #define MAX_CATEGORY 64 @@ -28,20 +29,39 @@ void mLogSetDefaultLogger(struct mLogger* logger) { static int _category = 0; static const char* _categoryNames[MAX_CATEGORY]; +static const char* _categoryIds[MAX_CATEGORY]; -int mLogGenerateCategory(const char* name) { - ++_category; +int mLogGenerateCategory(const char* name, const char* id) { if (_category < MAX_CATEGORY) { _categoryNames[_category] = name; + _categoryIds[_category] = id; } - return _category; + ++_category; + return _category - 1; } const char* mLogCategoryName(int category) { if (category < MAX_CATEGORY) { return _categoryNames[category]; } - return 0; + return NULL; +} + +const char* mLogCategoryId(int category) { + if (category < MAX_CATEGORY) { + return _categoryIds[category]; + } + return NULL; +} + +int mLogCategoryById(const char* id) { + int i; + for (i = 0; i < _category; ++i) { + if (strcmp(_categoryIds[i], id) == 0) { + return i; + } + } + return -1; } void mLog(int category, enum mLogLevel level, const char* format, ...) { @@ -49,7 +69,9 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) { va_list args; va_start(args, format); if (context) { - context->log(context, category, level, format, args); + if (!context->filter || mLogFilterTest(context->filter, category, level)) { + context->log(context, category, level, format, args); + } } else { printf("%s: ", mLogCategoryName(category)); vprintf(format, args); @@ -58,4 +80,67 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) { va_end(args); } -mLOG_DEFINE_CATEGORY(STATUS, "Status") +void mLogFilterInit(struct mLogFilter* filter) { + HashTableInit(&filter->categories, 8, NULL); + TableInit(&filter->levels, 8, NULL); +} + +void mLogFilterDeinit(struct mLogFilter* filter) { + HashTableDeinit(&filter->categories); + TableDeinit(&filter->levels); +} + +static void _setFilterLevel(const char* key, const char* value, enum mCoreConfigLevel level, void* user) { + UNUSED(level); + struct mLogFilter* filter = user; + key = strchr(key, '.'); + if (!key || !key[1]) { + return; + } + if (!value) { + return; + } + ++key; + char* end; + int ivalue = strtol(value, &end, 10); + if (ivalue == 0) { + ivalue = INT_MIN; // Zero is reserved + } + if (!end) { + return; + } + mLogFilterSet(filter, key, ivalue); +} + +void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) { + mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter); + filter->defaultLevels = mLOG_ALL; + mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels); +} + +void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) { + HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels); + // Can't do this eagerly because not all categories are initialized immediately + int cat = mLogCategoryById(category); + if (cat >= 0) { + TableInsert(&filter->levels, cat, (void*)(intptr_t) levels); + } + +} +bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) { + int value = (int) TableLookup(&filter->levels, category); + if (value) { + return value & level; + } + const char* cat = mLogCategoryId(category); + if (cat) { + value = (int) HashTableLookup(&filter->categories, cat); + if (value) { + TableInsert(&filter->levels, category, (void*)(intptr_t) value); + return value & level; + } + } + return level & filter->defaultLevels; +} + +mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status") diff --git a/src/core/rewind.c b/src/core/rewind.c index 56a59e47d..9919acf2e 100644 --- a/src/core/rewind.c +++ b/src/core/rewind.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -20,6 +21,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) context->previousState = VFileMemChunk(0, 0); context->currentState = VFileMemChunk(0, 0); context->size = 0; + context->stateFlags = SAVESTATE_SAVEDATA; } void mCoreRewindContextDeinit(struct mCoreRewindContext* context) { @@ -41,7 +43,7 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) { context->current = 0; } - mCoreSaveStateNamed(core, nextState, 0); + mCoreSaveStateNamed(core, nextState, context->stateFlags); struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); size_t size2 = nextState->size(nextState); size_t size = context->currentState->size(context->currentState); @@ -75,7 +77,7 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) patch->d.applyPatch(&patch->d, current, size, previous, size); context->currentState->unmap(context->currentState, current, size); context->previousState->unmap(context->previousState, previous, size); - mCoreLoadStateNamed(core, context->previousState, 0); + mCoreLoadStateNamed(core, context->previousState, context->stateFlags); struct VFile* nextState = context->previousState; context->previousState = context->currentState; context->currentState = nextState; diff --git a/src/core/serialize.c b/src/core/serialize.c index f5b143fec..bd6abd112 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -16,7 +17,7 @@ #include #endif -mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate"); +mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate", "core.serialize"); struct mBundledState { size_t stateSize; @@ -328,6 +329,14 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) { mStateExtdataPut(&extdata, EXTDATA_CHEATS, &item); } } + if (flags & SAVESTATE_RTC) { + mLOG(SAVESTATE, INFO, "Loading RTC"); + struct mStateExtdataItem item; + if (core->rtc.d.serialize) { + core->rtc.d.serialize(&core->rtc.d, &item); + mStateExtdataPut(&extdata, EXTDATA_RTC, &item); + } + } #ifdef USE_PNG if (!(flags & SAVESTATE_SCREENSHOT)) { #else @@ -425,6 +434,12 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) { } } } + if (flags & SAVESTATE_RTC && mStateExtdataGet(&extdata, EXTDATA_RTC, &item)) { + mLOG(SAVESTATE, INFO, "Loading RTC"); + if (core->rtc.d.deserialize) { + core->rtc.d.deserialize(&core->rtc.d, &item); + } + } mStateExtdataDeinit(&extdata); return success; } diff --git a/src/core/thread.c b/src/core/thread.c index f3509071e..686330615 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -35,6 +36,8 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) { } #endif +static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args); + static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) { MutexLock(&threadContext->stateMutex); threadContext->state = newState; @@ -142,12 +145,20 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { .coreCrashed = _crashed, .context = threadContext }; - core->setCoreCallbacks(core, &callbacks); + core->addCoreCallbacks(core, &callbacks); core->setSync(core, &threadContext->sync); core->reset(core); + 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); + } + if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) { mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity); + threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0; } _changeState(threadContext, THREAD_RUNNING, true); @@ -221,7 +232,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { if (threadContext->cleanCallback) { threadContext->cleanCallback(threadContext); } - core->setCoreCallbacks(core, NULL); + core->clearCoreCallbacks(core); + + threadContext->logger.d.filter = NULL; return 0; } @@ -229,7 +242,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { bool mCoreThreadStart(struct mCoreThread* threadContext) { threadContext->state = THREAD_INITIALIZED; threadContext->logger.p = threadContext; - threadContext->logLevel = threadContext->core->opts.logLevel; + if (!threadContext->logger.d.log) { + threadContext->logger.d.log = _mCoreLog; + threadContext->logger.d.filter = NULL; + } if (!threadContext->sync.fpsTarget) { threadContext->sync.fpsTarget = _defaultFPSTarget; @@ -542,10 +558,7 @@ struct mCoreThread* mCoreThreadGet(void) { static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) { UNUSED(logger); - struct mCoreThread* thread = mCoreThreadGet(); - if (thread && !(thread->logLevel & level)) { - return; - } + UNUSED(level); printf("%s: ", mLogCategoryName(category)); vprintf(format, args); printf("\n"); @@ -554,9 +567,6 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level struct mLogger* mCoreThreadLogger(void) { struct mCoreThread* thread = mCoreThreadGet(); if (thread) { - if (!thread->logger.d.log) { - thread->logger.d.log = _mCoreLog; - } return &thread->logger.d; } return NULL; diff --git a/src/core/tile-cache.c b/src/core/tile-cache.c index 85c2f7032..3c34a8d86 100644 --- a/src/core/tile-cache.c +++ b/src/core/tile-cache.c @@ -283,3 +283,27 @@ const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileC } return tile; } + +const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId) { + unsigned bpp = cache->bpp; + switch (bpp) { + case 0: + return NULL; + default: + return (uint8_t*) &cache->vram[tileId << (2 + bpp)]; + } +} + +const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) { + unsigned bpp = cache->bpp; + switch (bpp) { + default: + return NULL; + case 1: + return &cache->palette[paletteId << 2]; + case 2: + return &cache->palette[paletteId << 4]; + case 3: + return &cache->palette[paletteId << 8]; + } +} diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index 0e7e3fc1a..49da5fde6 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -15,7 +15,7 @@ const uint32_t DEBUGGER_ID = 0xDEADBEEF; -mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger"); +mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger"); static void mDebuggerInit(void* cpu, struct mCPUComponent* component); static void mDebuggerDeinit(struct mCPUComponent* component); diff --git a/src/ds/bios.c b/src/ds/bios.c index 76f6fd008..039e75b51 100644 --- a/src/ds/bios.c +++ b/src/ds/bios.c @@ -7,7 +7,7 @@ #include -mLOG_DEFINE_CATEGORY(DS_BIOS, "DS BIOS"); +mLOG_DEFINE_CATEGORY(DS_BIOS, "DS BIOS", "ds.bios"); const uint32_t DS7_BIOS_CHECKSUM = 0x1280F0D5; const uint32_t DS9_BIOS_CHECKSUM = 0x2AB23573; diff --git a/src/ds/core.c b/src/ds/core.c index 26d97448e..d71f33df2 100644 --- a/src/ds/core.c +++ b/src/ds/core.c @@ -156,9 +156,14 @@ static size_t _DSCoreGetAudioBufferSize(struct mCore* core) { return 2048; } -static void _DSCoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { +static void _DSCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { struct DS* ds = core->board; - ds->coreCallbacks = coreCallbacks; + *mCoreCallbacksListAppend(&ds->coreCallbacks) = *coreCallbacks; +} + +static void _DSCoreClearCoreCallbacks(struct mCore* core) { + struct DS* ds = core->board; + mCoreCallbacksListClear(&ds->coreCallbacks); } static void _DSCoreSetAVStream(struct mCore* core, struct mAVStream* stream) { @@ -370,11 +375,6 @@ static void _DSCoreGetGameCode(const struct mCore* core, char* title) { DSGetGameCode(core->board, title); } -static void _DSCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { - struct DS* ds = core->board; - ds->rtcSource = rtc; -} - static void _DSCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { } @@ -513,7 +513,8 @@ struct mCore* DSCoreCreate(void) { core->getAudioChannel = _DSCoreGetAudioChannel; core->setAudioBufferSize = _DSCoreSetAudioBufferSize; core->getAudioBufferSize = _DSCoreGetAudioBufferSize; - core->setCoreCallbacks = _DSCoreSetCoreCallbacks; + core->addCoreCallbacks = _DSCoreAddCoreCallbacks; + core->clearCoreCallbacks = _DSCoreClearCoreCallbacks; core->setAVStream = _DSCoreSetAVStream; core->isROM = DSIsROM; core->loadROM = _DSCoreLoadROM; @@ -539,7 +540,6 @@ struct mCore* DSCoreCreate(void) { core->frequency = _DSCoreFrequency; core->getGameTitle = _DSCoreGetGameTitle; core->getGameCode = _DSCoreGetGameCode; - core->setRTC = _DSCoreSetRTC; core->setRotation = _DSCoreSetRotation; core->setRumble = _DSCoreSetRumble; core->busRead8 = _DSCoreBusRead8; diff --git a/src/ds/ds.c b/src/ds/ds.c index 2887d85b5..e94381e6b 100644 --- a/src/ds/ds.c +++ b/src/ds/ds.c @@ -18,7 +18,7 @@ #define SLICE_CYCLES 2048 -mLOG_DEFINE_CATEGORY(DS, "DS"); +mLOG_DEFINE_CATEGORY(DS, "DS", "ds"); const uint32_t DS_ARM946ES_FREQUENCY = 0x1FF61FE; const uint32_t DS_ARM7TDMI_FREQUENCY = 0xFFB0FF; @@ -209,7 +209,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) { ds->romVf = NULL; DSSlot1SPIInit(ds, NULL); + ds->stream = NULL; ds->keyCallback = NULL; + mCoreCallbacksListInit(&ds->coreCallbacks, 0); ds->divEvent.name = "DS Hardware Divide"; ds->divEvent.callback = _divide; @@ -240,6 +242,7 @@ void DSDestroy(struct DS* ds) { DSGXDeinit(&ds->gx); mTimingDeinit(&ds->ds7.timing); mTimingDeinit(&ds->ds9.timing); + mCoreCallbacksListDeinit(&ds->coreCallbacks); } void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) { @@ -824,16 +827,22 @@ void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq) { } void DSFrameStarted(struct DS* ds) { - struct mCoreCallbacks* callbacks = ds->coreCallbacks; - if (callbacks && callbacks->videoFrameStarted) { - callbacks->videoFrameStarted(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c); + if (callbacks->videoFrameStarted) { + callbacks->videoFrameStarted(callbacks->context); + } } } void DSFrameEnded(struct DS* ds) { - struct mCoreCallbacks* callbacks = ds->coreCallbacks; - if (callbacks && callbacks->videoFrameEnded) { - callbacks->videoFrameEnded(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c); + if (callbacks->videoFrameEnded) { + callbacks->videoFrameEnded(callbacks->context); + } } if (ds->stream && ds->stream->postVideoFrame) { diff --git a/src/ds/gx.c b/src/ds/gx.c index c9a398a3a..d7a0211cf 100644 --- a/src/ds/gx.c +++ b/src/ds/gx.c @@ -8,7 +8,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_GX, "DS GX"); +mLOG_DEFINE_CATEGORY(DS_GX, "DS GX", "ds.gx"); #define DS_GX_FIFO_SIZE 256 #define DS_GX_PIPE_SIZE 4 diff --git a/src/ds/io.c b/src/ds/io.c index c981c74b2..3fbe37478 100644 --- a/src/ds/io.c +++ b/src/ds/io.c @@ -12,7 +12,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O"); +mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O", "ds.io"); static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) { switch (value >> 6) { diff --git a/src/ds/ipc.c b/src/ds/ipc.c index a487a30ea..d830dc85c 100644 --- a/src/ds/ipc.c +++ b/src/ds/ipc.c @@ -8,7 +8,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_IPC, "DS IPC"); +mLOG_DEFINE_CATEGORY(DS_IPC, "DS IPC", "ds.ipc"); void DSIPCWriteSYNC(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) { remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0; diff --git a/src/ds/memory.c b/src/ds/memory.c index 4946fd7be..6b663899c 100644 --- a/src/ds/memory.c +++ b/src/ds/memory.c @@ -12,7 +12,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory"); +mLOG_DEFINE_CATEGORY(DS_MEM, "DS Memory", "ds.memory"); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb const uint32_t redzoneInstruction = 0xE7F0DEF0; diff --git a/src/ds/slot1.c b/src/ds/slot1.c index 64cec627a..260ec3e52 100644 --- a/src/ds/slot1.c +++ b/src/ds/slot1.c @@ -11,7 +11,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1"); +mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1", "ds.slot1"); static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate); static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); diff --git a/src/ds/spi.c b/src/ds/spi.c index 2c2ef3ac6..884afab8e 100644 --- a/src/ds/spi.c +++ b/src/ds/spi.c @@ -8,7 +8,7 @@ #include #include -mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI"); +mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI", "ds.spi"); static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate); static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); diff --git a/src/ds/video.c b/src/ds/video.c index 2c7c4bd82..af83e3998 100644 --- a/src/ds/video.c +++ b/src/ds/video.c @@ -13,7 +13,7 @@ #include -mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video"); +mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video", "ds.video"); static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer); static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer); diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index c393086a0..c26cbe662 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -25,7 +25,7 @@ #include mLOG_DECLARE_CATEGORY(GUI_RUNNER); -mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner"); +mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner", "gui.runner"); #define FPS_GRANULARITY 120 #define FPS_BUFFER_SIZE 3 @@ -434,7 +434,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA); break; case RUNNER_LOAD_STATE: - mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT); + mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); break; case RUNNER_SCREENSHOT: mCoreTakeScreenshot(runner->core); diff --git a/src/gb/audio.c b/src/gb/audio.c index 17f00351d..7bed5d621 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -953,6 +953,9 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->playingCh4 = !!(*audio->nr52 & 0x0008); audio->enable = GBAudioEnableGetEnable(*audio->nr52); + LOAD_32LE(when, 0, &state->ch1.nextFrame); + mTimingSchedule(audio->timing, &audio->frameEvent, when); + LOAD_32LE(flags, 0, flagsIn); LOAD_32LE(ch1Flags, 0, &state->ch1.envelope); audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags); @@ -964,7 +967,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags); audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags); LOAD_32LE(when, 0, &state->ch1.nextEvent); - mTimingDeschedule(audio->timing, &audio->ch1Event); if (audio->ch1.envelope.dead < 2 && audio->playingCh1) { mTimingSchedule(audio->timing, &audio->ch1Event, when); } @@ -976,7 +978,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags); audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags); LOAD_32LE(when, 0, &state->ch2.nextEvent); - mTimingDeschedule(audio->timing, &audio->ch2Event); if (audio->ch2.envelope.dead < 2 && audio->playingCh2) { mTimingSchedule(audio->timing, &audio->ch2Event, when); } @@ -986,7 +987,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32)); LOAD_16LE(audio->ch3.length, 0, &state->ch3.length); LOAD_32LE(when, 0, &state->ch3.nextEvent); - mTimingDeschedule(audio->timing, &audio->ch3Event); if (audio->playingCh3) { mTimingSchedule(audio->timing, &audio->ch3Event, when); } @@ -1002,7 +1002,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags); LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr); LOAD_32LE(when, 0, &state->ch4.nextEvent); - mTimingDeschedule(audio->timing, &audio->ch4Event); if (audio->ch4.envelope.dead < 2 && audio->playingCh4) { mTimingSchedule(audio->timing, &audio->ch4Event, when); } @@ -1017,6 +1016,5 @@ void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* s GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags); uint32_t when; LOAD_32LE(when, 0, &state->audio.nextSample); - mTimingDeschedule(audio->timing, &audio->sampleEvent); mTimingSchedule(audio->timing, &audio->sampleEvent, when); } diff --git a/src/gb/core.c b/src/gb/core.c index 6757c0929..34dad3507 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -54,6 +54,8 @@ static bool _GBCoreInit(struct mCore* core) { memset(gbcore->components, 0, sizeof(gbcore->components)); LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components); LR35902Init(cpu); + mRTCGenericSourceInit(&core->rtc, core); + gb->memory.rtc = &core->rtc.d; GBVideoSoftwareRendererCreate(&gbcore->renderer); gbcore->renderer.outputBuffer = NULL; @@ -164,9 +166,14 @@ static size_t _GBCoreGetAudioBufferSize(struct mCore* core) { return gb->audio.samples; } -static void _GBCoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { +static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { struct GB* gb = core->board; - gb->coreCallbacks = coreCallbacks; + *mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks; +} + +static void _GBCoreClearCoreCallbacks(struct mCore* core) { + struct GB* gb = core->board; + mCoreCallbacksListClear(&gb->coreCallbacks); } static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) { @@ -403,11 +410,6 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) { GBGetGameCode(core->board, title); } -static void _GBCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { - struct GB* gb = core->board; - gb->memory.rtc = rtc; -} - static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { struct GB* gb = core->board; gb->memory.rotation = rotation; @@ -595,7 +597,8 @@ struct mCore* GBCoreCreate(void) { core->setAudioBufferSize = _GBCoreSetAudioBufferSize; core->getAudioBufferSize = _GBCoreGetAudioBufferSize; core->setAVStream = _GBCoreSetAVStream; - core->setCoreCallbacks = _GBCoreSetCoreCallbacks; + core->addCoreCallbacks = _GBCoreAddCoreCallbacks; + core->clearCoreCallbacks = _GBCoreClearCoreCallbacks; core->isROM = GBIsROM; core->loadROM = _GBCoreLoadROM; core->loadBIOS = _GBCoreLoadBIOS; @@ -621,7 +624,6 @@ struct mCore* GBCoreCreate(void) { core->frequency = _GBCoreFrequency; core->getGameTitle = _GBCoreGetGameTitle; core->getGameCode = _GBCoreGetGameCode; - core->setRTC = _GBCoreSetRTC; core->setRotation = _GBCoreSetRotation; core->setRumble = _GBCoreSetRumble; core->busRead8 = _GBCoreBusRead8; diff --git a/src/gb/extra/cli.c b/src/gb/extra/cli.c index 78e10a1b6..809961290 100644 --- a/src/gb/extra/cli.c +++ b/src/gb/extra/cli.c @@ -101,7 +101,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; - mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -118,5 +118,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; - mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } diff --git a/src/gb/gb.c b/src/gb/gb.c index e53a7e579..8e227a816 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -30,7 +30,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66}; #define DMG_2_BIOS_CHECKSUM 0x59C8598E #define CGB_BIOS_CHECKSUM 0x41884E46 -mLOG_DEFINE_CATEGORY(GB, "GB"); +mLOG_DEFINE_CATEGORY(GB, "GB", "gb"); static void GBInit(void* cpu, struct mCPUComponent* component); static void GBDeinit(struct mCPUComponent* component); @@ -79,11 +79,11 @@ static void GBInit(void* cpu, struct mCPUComponent* component) { gb->sramVf = NULL; gb->sramRealVf = NULL; - gb->pristineRom = 0; + gb->isPristine = false; gb->pristineRomSize = 0; gb->yankedRomSize = 0; - gb->coreCallbacks = NULL; + mCoreCallbacksListInit(&gb->coreCallbacks, 0); gb->stream = NULL; mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent); @@ -108,24 +108,23 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { gb->romVf = vf; gb->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); + gb->isPristine = true; #ifdef _3DS - gb->pristineRom = 0; if (gb->pristineRomSize <= romBufferSize) { - gb->pristineRom = romBuffer; + gb->memory.rom = romBuffer; vf->read(vf, romBuffer, gb->pristineRomSize); } #else - gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ); + gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ); #endif - if (!gb->pristineRom) { + if (!gb->memory.rom) { return false; } gb->yankedRomSize = 0; - gb->memory.rom = gb->pristineRom; gb->memory.romBase = gb->memory.rom; gb->memory.romSize = gb->pristineRomSize; gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); - GBMBCSwitchBank(&gb->memory, gb->memory.currentBank); + GBMBCSwitchBank(gb, gb->memory.currentBank); if (gb->cpu) { struct LR35902Core* cpu = gb->cpu; @@ -263,26 +262,25 @@ void GBSavedataUnmask(struct GB* gb) { void GBUnloadROM(struct GB* gb) { // TODO: Share with GBAUnloadROM - if (gb->memory.rom && gb->memory.romBase != gb->memory.rom && gb->memory.romBase != gb->pristineRom) { + if (gb->memory.rom && gb->memory.romBase != gb->memory.rom && !gb->isPristine) { free(gb->memory.romBase); } - if (gb->memory.rom && gb->pristineRom != gb->memory.rom) { + if (gb->memory.rom && !gb->isPristine) { if (gb->yankedRomSize) { gb->yankedRomSize = 0; } mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX); - gb->memory.rom = gb->pristineRom; } - gb->memory.rom = 0; if (gb->romVf) { #ifndef _3DS - gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize); + gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); #endif gb->romVf->close(gb->romVf); - gb->romVf = 0; + gb->romVf = NULL; } - gb->pristineRom = 0; + gb->memory.rom = NULL; + gb->isPristine = false; GBSavedataUnmask(gb); GBSramDeinit(gb); @@ -317,14 +315,26 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) { if (patchedSize > GB_SIZE_CART_MAX) { patchedSize = GB_SIZE_CART_MAX; } - gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX); - if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) { - mappedMemoryFree(gb->memory.rom, patchedSize); - gb->memory.rom = gb->pristineRom; + void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX); + if (!patch->applyPatch(patch, gb->memory.rom, gb->pristineRomSize, newRom, patchedSize)) { + mappedMemoryFree(newRom, GB_SIZE_CART_MAX); return; } + if (gb->romVf) { +#ifndef _3DS + gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); +#endif + gb->romVf->close(gb->romVf); + gb->romVf = NULL; + } + gb->isPristine = false; + if (gb->memory.romBase == gb->memory.rom) { + gb->memory.romBase = newRom; + } + gb->memory.rom = newRom; gb->memory.romSize = patchedSize; gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); } void GBDestroy(struct GB* gb) { @@ -339,6 +349,7 @@ void GBDestroy(struct GB* gb) { GBAudioDeinit(&gb->audio); GBVideoDeinit(&gb->video); GBSIODeinit(&gb->sio); + mCoreCallbacksListDeinit(&gb->coreCallbacks); } void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) { @@ -652,9 +663,6 @@ void GBGetGameTitle(const struct GB* gb, char* out) { if (gb->memory.rom) { cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; } - if (gb->pristineRom) { - cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100]; - } if (!cart) { return; } @@ -671,9 +679,6 @@ void GBGetGameCode(const struct GB* gb, char* out) { if (gb->memory.rom) { cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; } - if (gb->pristineRom) { - cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100]; - } if (!cart) { return; } diff --git a/src/gb/io.c b/src/gb/io.c index 82f3855ef..b7e234ca7 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -9,7 +9,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GB_IO, "GB I/O"); +mLOG_DEFINE_CATEGORY(GB_IO, "GB I/O", "gb.io"); const char* const GBIORegisterNames[] = { [REG_JOYP] = "JOYP", diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 9e8072dc8..efc294419 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -6,11 +6,12 @@ #include #include +#include #include #include #include -mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC"); +mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc"); static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { UNUSED(gb); @@ -28,18 +29,21 @@ static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); -void GBMBCSwitchBank(struct GBMemory* memory, int bank) { +void GBMBCSwitchBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0; - if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) { + if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); - bankStart &= (memory->romSize - 1); + bankStart &= (gb->memory.romSize - 1); bank = bankStart / GB_SIZE_CART_BANK0; if (!bank) { ++bank; } } - memory->romBank = &memory->rom[bankStart]; - memory->currentBank = bank; + gb->memory.romBank = &gb->memory.rom[bankStart]; + gb->memory.currentBank = bank; + if (gb->cpu->pc < GB_BASE_VRAM) { + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); + } } void GBMBCSwitchSramBank(struct GB* gb, int bank) { @@ -249,12 +253,12 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { if (!bank) { ++bank; } - GBMBCSwitchBank(memory, bank | (memory->currentBank & 0x60)); + GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60)); break; case 0x2: bank &= 3; if (!memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(memory, (bank << 5) | (memory->currentBank & 0x1F)); + GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F)); } else { GBMBCSwitchSramBank(gb, bank); } @@ -262,7 +266,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { case 0x3: memory->mbcState.mbc1.mode = value & 1; if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(memory, memory->currentBank & 0x1F); + GBMBCSwitchBank(gb, memory->currentBank & 0x1F); } else { GBMBCSwitchSramBank(gb, 0); } @@ -297,7 +301,7 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { if (!bank) { ++bank; } - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; default: // TODO @@ -328,7 +332,7 @@ void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { if (!bank) { ++bank; } - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; case 0x2: if (value < 4) { @@ -372,11 +376,11 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { break; case 0x2: bank = (memory->currentBank & 0x100) | value; - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; case 0x3: bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; case 0x4: case 0x5: @@ -402,11 +406,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { } void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; int bank = value & 0x7F; switch (address >> 13) { case 0x1: - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; case 0x2: if (value < 0x10) { @@ -616,7 +619,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { } break; case 0x1: - GBMBCSwitchBank(memory, bank); + GBMBCSwitchBank(gb, bank); break; case 0x2: GBMBCSwitchSramBank(gb, bank); diff --git a/src/gb/memory.c b/src/gb/memory.c index 6e7eb2a33..eb709b01b 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -14,7 +14,7 @@ #include -mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory"); +mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory"); static void _pristineCow(struct GB* gba); @@ -614,7 +614,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->wramCurrentBank = state->memory.wramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank; - GBMBCSwitchBank(memory, memory->currentBank); + GBMBCSwitchBank(gb, memory->currentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank); GBMBCSwitchSramBank(gb, memory->sramCurrentBank); @@ -630,12 +630,10 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { uint32_t when; LOAD_32LE(when, 0, &state->memory.dmaNext); - mTimingDeschedule(&gb->timing, &memory->dmaEvent); if (memory->dmaRemaining) { mTimingSchedule(&gb->timing, &memory->dmaEvent, when); } LOAD_32LE(when, 0, &state->memory.hdmaNext); - mTimingDeschedule(&gb->timing, &memory->hdmaEvent); if (memory->hdmaRemaining) { mTimingSchedule(&gb->timing, &memory->hdmaEvent, when); } @@ -651,14 +649,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { } void _pristineCow(struct GB* gb) { - if (gb->memory.rom != gb->pristineRom) { + if (!gb->isPristine) { return; } - gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX); - memcpy(gb->memory.rom, gb->pristineRom, gb->memory.romSize); - memset(((uint8_t*) gb->memory.rom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize); - if (gb->pristineRom == gb->memory.romBase) { - gb->memory.romBase = gb->memory.rom; + void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX); + memcpy(newRom, gb->memory.rom, gb->memory.romSize); + memset(((uint8_t*) newRom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize); + if (gb->memory.rom == gb->memory.romBase) { + gb->memory.romBase = newRom; } - GBMBCSwitchBank(&gb->memory, gb->memory.currentBank); + gb->memory.rom = newRom; + GBMBCSwitchBank(gb, gb->memory.currentBank); } diff --git a/src/gb/serialize.c b/src/gb/serialize.c index ebea64a31..1cacc4224 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -9,7 +9,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate"); +mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize"); const uint32_t GB_SAVESTATE_MAGIC = 0x00400000; const uint32_t GB_SAVESTATE_VERSION = 0x00000001; @@ -144,6 +144,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { if (error) { return false; } + gb->timing.root = NULL; gb->cpu->a = state->cpu.a; gb->cpu->f.packed = state->cpu.f; @@ -170,14 +171,13 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { uint32_t when; LOAD_32LE(when, 0, &state->cpu.eiPending); - mTimingDeschedule(&gb->timing, &gb->eiPending); if (GBSerializedCpuFlagsIsEiPending(flags)) { mTimingSchedule(&gb->timing, &gb->eiPending, when); } LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent); - LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles); + gb->timing.root = NULL; gb->model = state->model; diff --git a/src/gb/sio.c b/src/gb/sio.c index 944064752..26a326966 100644 --- a/src/gb/sio.c +++ b/src/gb/sio.c @@ -9,7 +9,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GB_SIO, "GB Serial I/O"); +mLOG_DEFINE_CATEGORY(GB_SIO, "GB Serial I/O", "gb.sio"); const int GBSIOCyclesPerTransfer[2] = { 512, diff --git a/src/gb/timer.c b/src/gb/timer.c index 0cd2571a9..e5bda7113 100644 --- a/src/gb/timer.c +++ b/src/gb/timer.c @@ -109,13 +109,11 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s uint32_t when; LOAD_32LE(when, 0, &state->timer.nextEvent); - mTimingDeschedule(&timer->p->timing, &timer->event); mTimingSchedule(&timer->p->timing, &timer->event, when); GBSerializedTimerFlags flags; LOAD_32LE(flags, 0, &state->timer.flags); - mTimingDeschedule(&timer->p->timing, &timer->irq); if (GBSerializedTimerFlagsIsIrqPending(flags)) { LOAD_32LE(when, 0, &state->timer.nextIRQ); mTimingSchedule(&timer->p->timing, &timer->irq, when); diff --git a/src/gb/video.c b/src/gb/video.c index d3f30a87e..465380fa9 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -129,9 +129,12 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); - struct mCoreCallbacks* callbacks = video->p->coreCallbacks; - if (callbacks && callbacks->videoFrameEnded) { - callbacks->videoFrameEnded(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); + if (callbacks->videoFrameEnded) { + callbacks->videoFrameEnded(callbacks->context); + } } } if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { @@ -244,9 +247,12 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat video->p->stream->postVideoFrame(video->p->stream, pixels, stride); } - struct mCoreCallbacks* callbacks = video->p->coreCallbacks; - if (callbacks && callbacks->videoFrameStarted) { - callbacks->videoFrameStarted(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); + if (callbacks->videoFrameStarted) { + callbacks->videoFrameStarted(callbacks->context); + } } if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { @@ -550,12 +556,10 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s } uint32_t when; - mTimingDeschedule(&video->p->timing, &video->modeEvent); if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) { LOAD_32LE(when, 0, &state->video.nextMode); mTimingSchedule(&video->p->timing, &video->modeEvent, when); } - mTimingDeschedule(&video->p->timing, &video->frameEvent); if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) { LOAD_32LE(when, 0, &state->video.nextFrame); mTimingSchedule(&video->p->timing, &video->frameEvent, when); diff --git a/src/gba/audio.c b/src/gba/audio.c index 52d99d883..2ceba8a5a 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -18,7 +18,7 @@ #define blip_add_delta blip_add_delta_fast #endif -mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio"); +mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio"); const unsigned GBA_AUDIO_SAMPLES = 2048; const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); @@ -344,7 +344,6 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState uint32_t when; LOAD_32(when, 0, &state->audio.nextSample); - mTimingDeschedule(&audio->p->timing, &audio->sampleEvent); mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); } diff --git a/src/gba/bios.c b/src/gba/bios.c index 3d17c961a..d119caadd 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -14,7 +14,7 @@ const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F; const uint32_t GBA_DS_BIOS_CHECKSUM = 0xBAAE1880; -mLOG_DEFINE_CATEGORY(GBA_BIOS, "GBA BIOS"); +mLOG_DEFINE_CATEGORY(GBA_BIOS, "GBA BIOS", "gba.bios"); static void _unLz77(struct GBA* gba, int width); static void _unHuffman(struct GBA* gba); diff --git a/src/gba/core.c b/src/gba/core.c index 1cb8c400c..46acc305c 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -62,6 +62,8 @@ static bool _GBACoreInit(struct mCore* core) { memset(gbacore->components, 0, sizeof(gbacore->components)); ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components); ARMInit(cpu); + mRTCGenericSourceInit(&core->rtc, core); + gba->rtcSource = &core->rtc.d; GBAVideoSoftwareRendererCreate(&gbacore->renderer); gbacore->renderer.outputBuffer = NULL; @@ -194,9 +196,14 @@ static size_t _GBACoreGetAudioBufferSize(struct mCore* core) { return gba->audio.samples; } -static void _GBACoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { +static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) { struct GBA* gba = core->board; - gba->coreCallbacks = coreCallbacks; + *mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks; +} + +static void _GBACoreClearCoreCallbacks(struct mCore* core) { + struct GBA* gba = core->board; + mCoreCallbacksListClear(&gba->coreCallbacks); } static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) { @@ -331,7 +338,7 @@ static void _GBACoreReset(struct mCore* core) { #endif ARMReset(core->cpu); - if (core->opts.skipBios && gba->pristineRom) { + if (core->opts.skipBios && gba->isPristine) { GBASkipBIOS(core->board); } } @@ -415,11 +422,6 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) { GBAGetGameCode(core->board, title); } -static void _GBACoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { - struct GBA* gba = core->board; - gba->rtcSource = rtc; -} - static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { struct GBA* gba = core->board; gba->rotationSource = rotation; @@ -608,7 +610,8 @@ struct mCore* GBACoreCreate(void) { core->getAudioChannel = _GBACoreGetAudioChannel; core->setAudioBufferSize = _GBACoreSetAudioBufferSize; core->getAudioBufferSize = _GBACoreGetAudioBufferSize; - core->setCoreCallbacks = _GBACoreSetCoreCallbacks; + core->addCoreCallbacks = _GBACoreAddCoreCallbacks; + core->clearCoreCallbacks = _GBACoreClearCoreCallbacks; core->setAVStream = _GBACoreSetAVStream; core->isROM = GBAIsROM; core->loadROM = _GBACoreLoadROM; @@ -635,7 +638,6 @@ struct mCore* GBACoreCreate(void) { core->frequency = _GBACoreFrequency; core->getGameTitle = _GBACoreGetGameTitle; core->getGameCode = _GBACoreGetGameCode; - core->setRTC = _GBACoreSetRTC; core->setRotation = _GBACoreSetRotation; core->setRumble = _GBACoreSetRumble; core->busRead8 = _GBACoreBusRead8; diff --git a/src/gba/extra/cli.c b/src/gba/extra/cli.c index a9ad7841b..44ae46917 100644 --- a/src/gba/extra/cli.c +++ b/src/gba/extra/cli.c @@ -100,7 +100,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } // TODO: Put back rewind @@ -119,5 +119,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } diff --git a/src/gba/gba.c b/src/gba/gba.c index 9627d85aa..6e167f220 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -21,8 +21,8 @@ #include #include -mLOG_DEFINE_CATEGORY(GBA, "GBA"); -mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug"); +mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba"); +mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug"); const uint32_t GBA_COMPONENT_MAGIC = 0x1000000; @@ -91,7 +91,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->keyCallback = NULL; gba->stopCallback = NULL; gba->stopCallback = NULL; - gba->coreCallbacks = NULL; + mCoreCallbacksListInit(&gba->coreCallbacks, 0); gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); @@ -104,7 +104,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->performingDMA = false; - gba->pristineRom = 0; + gba->isPristine = false; gba->pristineRomSize = 0; gba->yankedRomSize = 0; @@ -112,22 +112,22 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { } void GBAUnloadROM(struct GBA* gba) { - if (gba->memory.rom && gba->pristineRom != gba->memory.rom) { + if (gba->memory.rom && !gba->isPristine) { if (gba->yankedRomSize) { gba->yankedRomSize = 0; } mappedMemoryFree(gba->memory.rom, SIZE_CART0); } - gba->memory.rom = 0; if (gba->romVf) { #ifndef _3DS - gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize); + gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); #endif gba->romVf->close(gba->romVf); - gba->romVf = 0; + gba->romVf = NULL; } - gba->pristineRom = 0; + gba->memory.rom = NULL; + gba->isPristine = false; GBASavedataDeinit(&gba->memory.savedata); if (gba->memory.savedata.realVf) { @@ -152,6 +152,7 @@ void GBADestroy(struct GBA* gba) { GBASIODeinit(&gba->sio); gba->rr = 0; mTimingDeinit(&gba->timing); + mCoreCallbacksListDeinit(&gba->coreCallbacks); } void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) { @@ -291,23 +292,23 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) { if (gba->pristineRomSize > SIZE_WORKING_RAM) { gba->pristineRomSize = SIZE_WORKING_RAM; } + gba->isPristine = true; #ifdef _3DS - gba->pristineRom = 0; if (gba->pristineRomSize <= romBufferSize) { - gba->pristineRom = romBuffer; + gba->memory.wram = romBuffer; vf->read(vf, romBuffer, gba->pristineRomSize); } #else - gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ); + gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ); #endif - if (!gba->pristineRom) { + if (!gba->memory.wram) { mLOG(GBA, WARN, "Couldn't map ROM"); return false; } gba->yankedRomSize = 0; gba->memory.romSize = 0; gba->memory.romMask = 0; - gba->romCrc32 = doCrc32(gba->pristineRom, gba->pristineRomSize); + gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize); return true; } @@ -322,21 +323,20 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { if (gba->pristineRomSize > SIZE_CART0) { gba->pristineRomSize = SIZE_CART0; } + gba->isPristine = true; #ifdef _3DS - gba->pristineRom = 0; if (gba->pristineRomSize <= romBufferSize) { - gba->pristineRom = romBuffer; + gba->memory.rom = romBuffer; vf->read(vf, romBuffer, gba->pristineRomSize); } #else - gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ); + gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ); #endif - if (!gba->pristineRom) { + if (!gba->memory.rom) { mLOG(GBA, WARN, "Couldn't map ROM"); return false; } gba->yankedRomSize = 0; - gba->memory.rom = gba->pristineRom; gba->memory.romSize = gba->pristineRomSize; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->memory.mirroring = false; @@ -389,12 +389,21 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { if (!patchedSize || patchedSize > SIZE_CART0) { return; } - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); - if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) { - mappedMemoryFree(gba->memory.rom, patchedSize); - gba->memory.rom = gba->pristineRom; + void* newRom = anonymousMemoryMap(SIZE_CART0); + if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) { + mappedMemoryFree(newRom, SIZE_CART0); return; } + if (gba->romVf) { +#ifndef _3DS + gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); +#endif + gba->romVf->close(gba->romVf); + gba->romVf = NULL; + } + gba->isPristine = false; + gba->memory.rom = newRom; + gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; gba->memory.romSize = patchedSize; gba->memory.romMask = SIZE_CART0 - 1; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); @@ -539,8 +548,8 @@ void GBAGetGameTitle(const struct GBA* gba, char* out) { memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12); return; } - if (gba->pristineRom) { - memcpy(out, &((struct GBACartridge*) gba->pristineRom)->title, 12); + if (gba->isPristine && gba->memory.wram) { + memcpy(out, &((struct GBACartridge*) gba->memory.wram)->title, 12); return; } strncpy(out, "(BIOS)", 12); @@ -567,6 +576,10 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { // TODO: More sensible category? mLOG(GBA, WARN, "Illegal opcode: %08x", opcode); } + if (cpu->executionMode == MODE_THUMB && (opcode & 0xFFC0) == 0xE800) { + mLOG(GBA, DEBUG, "Hit Wii U VC opcode: %08x", opcode); + return; + } #ifdef USE_DEBUGGERS if (gba->debugger) { struct mDebuggerEntryInfo info = { @@ -623,9 +636,12 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) { void GBAFrameStarted(struct GBA* gba) { UNUSED(gba); - struct mCoreCallbacks* callbacks = gba->coreCallbacks; - if (callbacks && callbacks->videoFrameStarted) { - callbacks->videoFrameStarted(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); + if (callbacks->videoFrameStarted) { + callbacks->videoFrameStarted(callbacks->context); + } } } @@ -656,9 +672,12 @@ void GBAFrameEnded(struct GBA* gba) { GBAHardwarePlayerUpdate(gba); } - struct mCoreCallbacks* callbacks = gba->coreCallbacks; - if (callbacks && callbacks->videoFrameEnded) { - callbacks->videoFrameEnded(callbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); + if (callbacks->videoFrameEnded) { + callbacks->videoFrameEnded(callbacks->context); + } } } diff --git a/src/gba/hardware.c b/src/gba/hardware.c index cdd7b8665..34313fd03 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -11,7 +11,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GBA_HW, "GBA Pak Hardware"); +mLOG_DEFINE_CATEGORY(GBA_HW, "GBA Pak Hardware", "gba.hardware"); const int GBA_LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 }; @@ -554,6 +554,7 @@ uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uin gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == 0x22); } } + mTimingDeschedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent); mTimingSchedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent, 2048); } value &= 0x78FB; diff --git a/src/gba/io.c b/src/gba/io.c index 9e694b3f0..44e3ba459 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -11,7 +11,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O"); +mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O", "gba.io"); const char* const GBAIORegisterNames[] = { // Video @@ -251,8 +251,8 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // Audio - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, @@ -292,9 +292,9 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Audio - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 1, 0, 1, 0, + 1, 1, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // DMA @@ -960,7 +960,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { gba->timers[i].lastEvent = when + mTimingCurrentTime(&gba->timing); } LOAD_32(when, 0, &state->timers[i].nextEvent); - mTimingDeschedule(&gba->timing, &gba->timers[i].event); if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { mTimingSchedule(&gba->timing, &gba->timers[i].event, when); } diff --git a/src/gba/memory.c b/src/gba/memory.c index 4356f6a2a..1502bbddd 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -19,7 +19,7 @@ #define IDLE_LOOP_THRESHOLD 10000 -mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory"); +mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory", "gba.memory"); static void _pristineCow(struct GBA* gba); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb @@ -98,13 +98,12 @@ void GBAMemoryDeinit(struct GBA* gba) { } void GBAMemoryReset(struct GBA* gba) { - if (gba->memory.wram) { - mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); - } - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); - if (gba->pristineRom && !gba->memory.rom) { - // Multiboot - memcpy(gba->memory.wram, gba->pristineRom, gba->pristineRomSize); + if (gba->memory.rom || gba->memory.fullBios) { + // Not multiboot + if (gba->memory.wram) { + mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); + } + gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); } if (gba->memory.iwram) { @@ -300,9 +299,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { cpu->memory.activeMask = 0; if (gba->yankedRomSize || !gba->hardCrash) { mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); - } else if (gba->coreCallbacks && gba->coreCallbacks->coreCrashed) { + } else if (mCoreCallbacksListSize(&gba->coreCallbacks)) { mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); - gba->coreCallbacks->coreCrashed(gba->coreCallbacks->context); + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); + if (callbacks->coreCrashed) { + callbacks->coreCrashed(callbacks->context); + } + } } else { mLOG(GBA_MEM, FATAL, "Jumped to invalid address: %08X", address); } @@ -1537,10 +1542,19 @@ void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedSt } void _pristineCow(struct GBA* gba) { - if (gba->memory.rom != gba->pristineRom) { + if (!gba->isPristine) { return; } - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); - memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize); - memset(((uint8_t*) gba->memory.rom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + void* newRom = anonymousMemoryMap(SIZE_CART0); + memcpy(newRom, gba->memory.rom, gba->memory.romSize); + memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + if (gba->romVf) { +#ifndef _3DS + gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); +#endif + gba->romVf->close(gba->romVf); + gba->romVf = NULL; + } + gba->memory.rom = newRom; + gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; } diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index 0c60fee39..938bff1dd 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -381,7 +381,6 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re SPRITE_NORMAL_LOOP(256, OBJWIN); } else if (mosaicH > 1) { if (objwinSlowPath) { - objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8]; SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN); } else { SPRITE_MOSAIC_LOOP(256, NORMAL); diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 6d6315684..a1801e0c2 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -104,6 +104,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { } softwareRenderer->objExtPalette = NULL; softwareRenderer->objExtVariantPalette = NULL; + softwareRenderer->blendDirty = false; _updatePalettes(softwareRenderer); softwareRenderer->blda = 0; @@ -270,11 +271,14 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender value &= 0x1F1F; break; case REG_BLDY: - softwareRenderer->bldy = value & 0x1F; - if (softwareRenderer->bldy > 0x10) { - softwareRenderer->bldy = 0x10; + value &= 0x1F; + if (value > 0x10) { + value = 0x10; + } + if (softwareRenderer->bldy != value) { + softwareRenderer->bldy = value; + softwareRenderer->blendDirty = true; } - _updatePalettes(softwareRenderer); break; case REG_WIN0H: softwareRenderer->winN[0].h.end = value; @@ -509,6 +513,10 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y); int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y); softwareRenderer->d.vramOBJ[0] = objVramBase; + if (softwareRenderer->blendDirty) { + _updatePalettes(softwareRenderer); + softwareRenderer->blendDirty = false; + } int w; unsigned priority; @@ -688,7 +696,7 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); if (oldEffect != renderer->blendEffect) { - _updatePalettes(renderer); + renderer->blendDirty = true; } } diff --git a/src/gba/rr/rr.c b/src/gba/rr/rr.c index da6c48349..7ed957753 100644 --- a/src/gba/rr/rr.c +++ b/src/gba/rr/rr.c @@ -9,7 +9,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR"); +mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR", "gba.rr"); void GBARRInitRecord(struct GBA* gba) { if (!gba || !gba->rr) { diff --git a/src/gba/savedata.c b/src/gba/savedata.c index d82d80fd4..08eab40aa 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -26,7 +26,7 @@ #define EEPROM_SETTLE_CYCLES 115000 #define CLEANUP_THRESHOLD 15 -mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata"); +mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata", "gba.savedata"); static void _flashSwitchBank(struct GBASavedata* savedata, int bank); static void _flashErase(struct GBASavedata* savedata); diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 18692c0d3..59c6ecc34 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -17,7 +17,7 @@ const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_VERSION = 0x00000002; -mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate"); +mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize"); struct GBABundledState { struct GBASerializedState* state; @@ -146,7 +146,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { if (error) { return false; } - LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles); + gba->timing.root = NULL; size_t i; for (i = 0; i < 16; ++i) { LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs); diff --git a/src/gba/sio.c b/src/gba/sio.c index 661d9d09b..33a7c99d7 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -8,7 +8,7 @@ #include #include -mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O"); +mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio"); const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { { 38326, 73003, 107680, 142356 }, diff --git a/src/gba/timer.c b/src/gba/timer.c index 261cf19d5..0be6b6919 100644 --- a/src/gba/timer.c +++ b/src/gba/timer.c @@ -155,9 +155,6 @@ void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struc timer->lastEvent = timing->masterCycles + cpu->cycles; } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) { mTimingDeschedule(timing, &timer->event); - if (!GBATimerFlagsIsCountUp(timer->flags)) { - *io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale); - } } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) { mTimingDeschedule(timing, &timer->event); mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent); diff --git a/src/gba/video.c b/src/gba/video.c index fa1198261..688aa797b 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -15,7 +15,7 @@ #include -mLOG_DEFINE_CATEGORY(GBA_VIDEO, "GBA Video"); +mLOG_DEFINE_CATEGORY(GBA_VIDEO, "GBA Video", "gba.video"); static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer); @@ -338,7 +338,6 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState } else { video->event.callback = _startHblank; } - mTimingDeschedule(&video->p->timing, &video->event); mTimingSchedule(&video->p->timing, &video->event, when); LOAD_16(video->vcount, REG_VCOUNT, state->io); diff --git a/src/lr35902/decoder.c b/src/lr35902/decoder.c index 641af7804..6abefc6cc 100644 --- a/src/lr35902/decoder.c +++ b/src/lr35902/decoder.c @@ -52,7 +52,7 @@ DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;) #define DEFINE_LD_DECODER_LR35902_MEM(NAME, REG) \ DEFINE_DECODER_LR35902(LD ## NAME ## _ ## REG, info->mnemonic = LR35902_MN_LD; \ - info->op1.reg = LR35902_REG_ ## A; \ + info->op1.reg = LR35902_REG_ ## NAME; \ info->op2.reg = LR35902_REG_ ## REG; \ info->op2.flags = LR35902_OP_FLAG_MEMORY;) diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 781217e57..2dff30f3d 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -12,7 +12,7 @@ #include mLOG_DECLARE_CATEGORY(OPENGL); -mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL"); +mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL", "video.ogl"); #define MAX_PASSES 8 diff --git a/src/platform/qt/AssetView.cpp b/src/platform/qt/AssetView.cpp index 29fb35617..964b40761 100644 --- a/src/platform/qt/AssetView.cpp +++ b/src/platform/qt/AssetView.cpp @@ -7,6 +7,8 @@ #include +#include + using namespace QGBA; AssetView::AssetView(GameController* controller, QWidget* parent) @@ -24,7 +26,7 @@ AssetView::AssetView(GameController* controller, QWidget* parent) } void AssetView::updateTiles(bool force) { - if (!m_controller->thread() || !m_controller->thread()->core) { + if (!m_controller->isLoaded()) { return; } @@ -51,3 +53,47 @@ void AssetView::resizeEvent(QResizeEvent*) { void AssetView::showEvent(QShowEvent*) { updateTiles(true); } + +void AssetView::compositeTile(unsigned tileId, void* buffer, size_t stride, size_t x, size_t y, int depth) { + const uint8_t* tile = mTileCacheGetRawTile(m_tileCache.get(), tileId); + uint8_t* pixels = static_cast(buffer); + size_t base = stride * y + x; + switch (depth) { + case 2: + for (size_t i = 0; i < 8; ++i) { + uint8_t tileDataLower = tile[i * 2]; + uint8_t tileDataUpper = tile[i * 2 + 1]; + uint8_t pixel; + pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); + pixels[base + i * stride] = pixel; + pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); + pixels[base + i * stride + 1] = pixel; + pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); + pixels[base + i * stride + 2] = pixel; + pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); + pixels[base + i * stride + 3] = pixel; + pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); + pixels[base + i * stride + 4] = pixel; + pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); + pixels[base + i * stride + 5] = pixel; + pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); + pixels[base + i * stride + 6] = pixel; + pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1); + pixels[base + i * stride + 7] = pixel; + } + break; + case 4: + for (size_t j = 0; j < 8; ++j) { + for (size_t i = 0; i < 4; ++i) { + pixels[base + j * stride + i * 2] = tile[j * 4 + i] & 0xF; + pixels[base + j * stride + i * 2 + 1] = tile[j * 4 + i] >> 4; + } + } + break; + case 8: + for (size_t i = 0; i < 8; ++i) { + memcpy(&pixels[base + i * stride], &tile[i * 8], 8); + } + break; + } +} diff --git a/src/platform/qt/AssetView.h b/src/platform/qt/AssetView.h index a30ccbd9c..76dae708b 100644 --- a/src/platform/qt/AssetView.h +++ b/src/platform/qt/AssetView.h @@ -18,6 +18,8 @@ Q_OBJECT public: AssetView(GameController* controller, QWidget* parent = nullptr); + void compositeTile(unsigned tileId, void* image, size_t stride, size_t x, size_t y, int depth = 8); + protected slots: void updateTiles(bool force = false); diff --git a/src/platform/qt/CheatsView.cpp b/src/platform/qt/CheatsView.cpp index b3e912c55..f8ae9b77d 100644 --- a/src/platform/qt/CheatsView.cpp +++ b/src/platform/qt/CheatsView.cpp @@ -46,19 +46,19 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent) enterCheat(GBA_CHEAT_AUTODETECT); }); - add = new QPushButton("Add GameShark"); + add = new QPushButton(tr("Add GameShark")); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2); connect(add, &QPushButton::clicked, [this]() { enterCheat(GBA_CHEAT_GAMESHARK); }); - add = new QPushButton("Add Pro Action Replay"); + add = new QPushButton(tr("Add Pro Action Replay")); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2); connect(add, &QPushButton::clicked, [this]() { enterCheat(GBA_CHEAT_PRO_ACTION_REPLAY); }); - add = new QPushButton("Add CodeBreaker"); + add = new QPushButton(tr("Add CodeBreaker")); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2); connect(add, &QPushButton::clicked, [this]() { enterCheat(GBA_CHEAT_CODEBREAKER); @@ -71,13 +71,13 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent) enterCheat(GB_CHEAT_AUTODETECT); }); - add = new QPushButton("Add GameShark"); + add = new QPushButton(tr("Add GameShark")); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2); connect(add, &QPushButton::clicked, [this]() { enterCheat(GB_CHEAT_GAMESHARK); }); - add = new QPushButton("Add GameGenie"); + add = new QPushButton(tr("Add GameGenie")); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2); connect(add, &QPushButton::clicked, [this]() { enterCheat(GB_CHEAT_GAME_GENIE); diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 6f25326c5..520b6d9f2 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -110,6 +110,7 @@ ConfigController::ConfigController(QObject* parent) m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL; m_opts.rewindEnable = false; m_opts.rewindBufferCapacity = 300; + m_opts.rewindSave = true; m_opts.useBios = true; m_opts.suspendScreensaver = true; m_opts.lockAspectRatio = true; diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 20e254d9e..b54ea49ee 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -29,11 +29,10 @@ using namespace QGBA; static GBAApp* g_app = nullptr; -mLOG_DEFINE_CATEGORY(QT, "Qt"); +mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt"); GBAApp::GBAApp(int& argc, char* argv[]) : QApplication(argc, argv) - , m_windows{} , m_db(nullptr) { g_app = this; @@ -80,10 +79,10 @@ GBAApp::GBAApp(int& argc, char* argv[]) AudioProcessor::setDriver(static_cast(m_configController.getQtOption("audioDriver").toInt())); } Window* w = new Window(&m_configController); - connect(w, &Window::destroyed, [this]() { - m_windows[0] = nullptr; + connect(w, &Window::destroyed, [this, w]() { + m_windows.removeAll(w); }); - m_windows[0] = w; + m_windows.append(w); if (loaded) { w->argumentsPassed(&args); @@ -121,15 +120,15 @@ bool GBAApp::event(QEvent* event) { } Window* GBAApp::newWindow() { - if (m_multiplayer.attached() >= MAX_GBAS) { + if (m_windows.count() >= MAX_GBAS) { return nullptr; } Window* w = new Window(&m_configController, m_multiplayer.attached()); int windowId = m_multiplayer.attached(); - connect(w, &Window::destroyed, [this, windowId]() { - m_windows[windowId] = nullptr; + connect(w, &Window::destroyed, [this, w]() { + m_windows.removeAll(w); }); - m_windows[windowId] = w; + m_windows.append(w); w->setAttribute(Qt::WA_DeleteOnClose); w->loadConfig(); w->show(); @@ -142,27 +141,27 @@ GBAApp* GBAApp::app() { return g_app; } -void GBAApp::pauseAll(QList* paused) { - for (int i = 0; i < MAX_GBAS; ++i) { - if (!m_windows[i] || !m_windows[i]->controller()->isLoaded() || m_windows[i]->controller()->isPaused()) { +void GBAApp::pauseAll(QList* paused) { + for (auto& window : m_windows) { + if (!window->controller()->isLoaded() || window->controller()->isPaused()) { continue; } - m_windows[i]->controller()->setPaused(true); - paused->append(i); + window->controller()->setPaused(true); + paused->append(window); } } -void GBAApp::continueAll(const QList* paused) { - for (int i : *paused) { - m_windows[i]->controller()->setPaused(false); +void GBAApp::continueAll(const QList& paused) { + for (auto& window : paused) { + window->controller()->setPaused(false); } } QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) { - QList paused; + QList paused; pauseAll(&paused); QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter); - continueAll(&paused); + continueAll(paused); if (!filename.isEmpty()) { m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); } @@ -170,10 +169,10 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr } QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) { - QList paused; + QList paused; pauseAll(&paused); QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter); - continueAll(&paused); + continueAll(paused); if (!filename.isEmpty()) { m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); } @@ -181,10 +180,10 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr } QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) { - QList paused; + QList paused; pauseAll(&paused); QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory")); - continueAll(&paused); + continueAll(paused); if (!filename.isEmpty()) { m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); } @@ -249,14 +248,14 @@ GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& capt } int GBAApp::FileDialog::exec() { - QList paused; + QList paused; m_app->pauseAll(&paused); bool didAccept = QFileDialog::exec() == QDialog::Accepted; QStringList filenames = selectedFiles(); if (!filenames.isEmpty()) { m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path()); } - m_app->continueAll(&paused); + m_app->continueAll(paused); return didAccept; } diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 29ddfa2d3..8ade195e1 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -39,7 +39,6 @@ private: }; #endif - class GBAApp : public QApplication { Q_OBJECT @@ -78,11 +77,11 @@ private: Window* newWindowInternal(); - void pauseAll(QList* paused); - void continueAll(const QList* paused); + void pauseAll(QList* paused); + void continueAll(const QList& paused); ConfigController m_configController; - Window* m_windows[MAX_GBAS]; + QList m_windows; MultiplayerController m_multiplayer; NoIntroDB* m_db; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 754253c99..29aff4834 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -23,7 +23,6 @@ #include #ifdef M_CORE_GBA #include -#include #include #include #include @@ -70,8 +69,8 @@ GameController::GameController(QObject* parent) , m_stateSlot(1) , m_backupLoadState(nullptr) , m_backupSaveState(nullptr) - , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS) - , m_loadStateFlags(SAVESTATE_SCREENSHOT) + , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) + , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) , m_override(nullptr) { #ifdef M_CORE_GBA @@ -90,8 +89,6 @@ GameController::GameController(QObject* parent) m_threadContext.startCallback = [](mCoreThread* context) { GameController* controller = static_cast(context->userData); - mRTCGenericSourceInit(&controller->m_rtc, context->core); - context->core->setRTC(context->core, &controller->m_rtc.d); context->core->setRotation(context->core, controller->m_inputController->rotationSource()); context->core->setRumble(context->core, controller->m_inputController->rumble()); @@ -241,13 +238,21 @@ GameController::GameController(QObject* parent) static const char* savestateMessage = "State %i loaded"; static const char* savestateFailedMessage = "State %i failed to load"; + static int biosCat = -1; + static int statusCat = -1; if (!context) { return; } GameController* controller = static_cast(context->userData); QString message; + if (biosCat < 0) { + biosCat = mLogCategoryById("gba.bios"); + } + if (statusCat < 0) { + statusCat = mLogCategoryById("core.status"); + } #ifdef M_CORE_GBA - if (level == mLOG_STUB && category == _mLOG_CAT_GBA_BIOS()) { + if (level == mLOG_STUB && category == biosCat) { va_list argc; va_copy(argc, args); int immediate = va_arg(argc, int); @@ -255,7 +260,7 @@ GameController::GameController(QObject* parent) QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate)); } else #endif - if (category == _mLOG_CAT_STATUS()) { + if (category == statusCat) { // Slot 0 is reserved for suspend points if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { va_list argc; @@ -731,7 +736,7 @@ void GameController::frameAdvance() { } } -void GameController::setRewind(bool enable, int capacity) { +void GameController::setRewind(bool enable, int capacity, bool rewindSave) { if (m_gameOpen) { Interrupter interrupter(this); if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) { @@ -739,8 +744,10 @@ void GameController::setRewind(bool enable, int capacity) { } m_threadContext.core->opts.rewindEnable = enable; m_threadContext.core->opts.rewindBufferCapacity = capacity; + m_threadContext.core->opts.rewindSave = rewindSave; if (enable && capacity > 0) { mCoreRewindContextInit(&m_threadContext.rewind, capacity); + m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0; } } } @@ -1194,17 +1201,26 @@ void GameController::setLuminanceLevel(int level) { } void GameController::setRealTime() { - m_rtc.override = RTC_NO_OVERRIDE; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_NO_OVERRIDE; } void GameController::setFixedTime(const QDateTime& time) { - m_rtc.override = RTC_FIXED; - m_rtc.value = time.toMSecsSinceEpoch() / 1000; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_FIXED; + m_threadContext.core->rtc.value = time.toMSecsSinceEpoch(); } void GameController::setFakeEpoch(const QDateTime& time) { - m_rtc.override = RTC_FAKE_EPOCH; - m_rtc.value = time.toMSecsSinceEpoch() / 1000; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_FAKE_EPOCH; + m_threadContext.core->rtc.value = time.toMSecsSinceEpoch(); } void GameController::updateKeys() { diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index e1182063c..cbc2adc0e 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -127,7 +127,7 @@ public slots: void setPaused(bool paused); void reset(); void frameAdvance(); - void setRewind(bool enable, int capacity); + void setRewind(bool enable, int capacity, bool rewindSave); void rewind(int states = 0); void startRewinding(); void stopRewinding(); @@ -247,8 +247,6 @@ private: } m_lux; uint8_t m_luxValue; int m_luxLevel; - - mRTCGenericSource m_rtc; }; } diff --git a/src/platform/qt/LibraryModel.cpp b/src/platform/qt/LibraryModel.cpp index 0dc96fe2a..52173c622 100644 --- a/src/platform/qt/LibraryModel.cpp +++ b/src/platform/qt/LibraryModel.cpp @@ -89,7 +89,12 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent) m_library->ref(); } else { m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path); - s_handles[path] = m_library; + if (m_library->library) { + s_handles[path] = m_library; + } else { + delete m_library; + m_library = new LibraryHandle(mLibraryCreateEmpty()); + } } } else { m_library = new LibraryHandle(mLibraryCreateEmpty()); @@ -280,7 +285,9 @@ LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p) LibraryModel::LibraryHandle::~LibraryHandle() { m_loaderThread.quit(); m_loaderThread.wait(); - mLibraryDestroy(library); + if (library) { + mLibraryDestroy(library); + } } void LibraryModel::LibraryHandle::ref() { diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index f543adc27..2749d7a50 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -10,11 +10,17 @@ #include #include +#include "LogController.h" +#include "VFileDevice.h" + +#ifdef M_CORE_GBA #include +#endif #ifdef M_CORE_GB #include #include #endif +#include using namespace QGBA; @@ -45,6 +51,7 @@ ObjView::ObjView(GameController* controller, QWidget* parent) connect(m_ui.magnification, static_cast(&QSpinBox::valueChanged), [this]() { updateTiles(true); }); + connect(m_ui.exportButton, SIGNAL(clicked()), this, SLOT(exportObj())); } void ObjView::selectObj(int obj) { @@ -68,64 +75,63 @@ void ObjView::updateTilesGBA(bool force) { unsigned width = GBAVideoObjSizes[shape * 4 + size][0]; unsigned height = GBAVideoObjSizes[shape * 4 + size][1]; unsigned tile = GBAObjAttributesCGetTile(obj->c); - ObjInfo newInfo{ - tile, - width / 8, - height / 8, - width / 8 - }; m_ui.tiles->setTileCount(width * height / 64); m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value()); m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value()); unsigned palette = GBAObjAttributesCGetPalette(obj->c); - GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues - if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) { - newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a)); + unsigned tileBase = tile; + unsigned paletteSet; + unsigned bits; + if (GBAObjAttributesAIs256Color(obj->a)) { + m_ui.palette->setText("256-color"); + paletteSet = 1; + m_ui.tile->setPalette(0); + m_ui.tile->setPaletteSet(1, 1024, 1536); + palette = 1; + tile = tile / 2 + 1024; + bits = 8; + } else { + m_ui.palette->setText(QString::number(palette)); + paletteSet = 0; + m_ui.tile->setPalette(palette); + m_ui.tile->setPaletteSet(0, 2048, 3072); + palette += 16; + tile += 2048; + bits = 4; + } + ObjInfo newInfo{ + tile, + width / 8, + height / 8, + width / 8, + palette, + paletteSet, + bits }; if (newInfo != m_objInfo) { force = true; } + GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues + if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) { + newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a)); + }; m_objInfo = newInfo; - int i = 0; - if (GBAObjAttributesAIs256Color(obj->a)) { - m_ui.palette->setText("256-color"); - mTileCacheSetPalette(m_tileCache.get(), 1); - m_ui.tile->setPalette(0); - m_ui.tile->setPaletteSet(1, 1024, 1536); - tile /= 2; - unsigned t = tile + i; - for (int y = 0; y < height / 8; ++y) { - for (int x = 0; x < width / 8; ++x, ++i, ++t) { - const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * t], t + 1024, 1); - if (data) { - m_ui.tiles->setTile(i, data); - } else if (force) { - m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t + 1024, 1)); - } - } - t += newInfo.stride - width / 8; - } - tile += 1024; - } else { - m_ui.palette->setText(QString::number(palette)); - mTileCacheSetPalette(m_tileCache.get(), 0); - m_ui.tile->setPalette(palette); - m_ui.tile->setPaletteSet(0, 2048, 3072); - unsigned t = tile + i; - for (int y = 0; y < height / 8; ++y) { - for (int x = 0; x < width / 8; ++x, ++i, ++t) { - const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * t], t + 2048, palette + 16); - if (data) { - m_ui.tiles->setTile(i, data); - } else if (force) { - m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t + 2048, palette + 16)); - } - } - t += newInfo.stride - width / 8; - } - tile += 2048; - } m_tileOffset = tile; + mTileCacheSetPalette(m_tileCache.get(), paletteSet); + + int i = 0; + for (int y = 0; y < height / 8; ++y) { + for (int x = 0; x < width / 8; ++x, ++i, ++tile, ++tileBase) { + const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * tileBase], tile, palette); + if (data) { + m_ui.tiles->setTile(i, data); + } else if (force) { + m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), tile, palette)); + } + } + tile += newInfo.stride - width / 8; + tileBase += newInfo.stride - width / 8; + } m_ui.x->setText(QString::number(GBAObjAttributesBGetX(obj->b))); m_ui.y->setText(QString::number(GBAObjAttributesAGetY(obj->a))); @@ -175,20 +181,10 @@ void ObjView::updateTilesGB(bool force) { height = 16; } unsigned tile = obj->tile; - ObjInfo newInfo{ - tile, - 1, - height / 8, - 1 - }; - if (newInfo != m_objInfo) { - force = true; - } - m_objInfo = newInfo; m_ui.tiles->setTileCount(width * height / 64); m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value()); m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value()); - int palette = 0; + unsigned palette = 0; if (gb->model >= GB_MODEL_CGB) { if (GBObjAttributesIsBank(obj->attr)) { tile += 512; @@ -197,21 +193,37 @@ void ObjView::updateTilesGB(bool force) { } else { palette = GBObjAttributesGetPalette(obj->attr); } - int i = 0; m_ui.palette->setText(QString::number(palette)); + palette += 8; + + ObjInfo newInfo{ + tile, + 1, + height / 8, + 1, + palette, + 0, + 2 + }; + if (newInfo != m_objInfo) { + force = true; + } + m_objInfo = newInfo; + m_tileOffset = tile; + + int i = 0; mTileCacheSetPalette(m_tileCache.get(), 0); - m_ui.tile->setPalette(palette + 8); + m_ui.tile->setPalette(palette); m_ui.tile->setPaletteSet(0, 512, 1024); for (int y = 0; y < height / 8; ++y, ++i) { unsigned t = tile + i; - const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette + 8); + const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette); if (data) { m_ui.tiles->setTile(i, data); } else if (force) { - m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette + 8)); + m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette)); } } - m_tileOffset = tile; m_ui.x->setText(QString::number(obj->x)); m_ui.y->setText(QString::number(obj->y)); @@ -230,10 +242,56 @@ void ObjView::updateTilesGB(bool force) { } #endif +void ObjView::exportObj() { + GameController::Interrupter interrupter(m_controller); + QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export sprite"), + tr("Portable Network Graphics (*.png)")); + if (!dialog->exec()) { + return; + } + QString filename = dialog->selectedFiles()[0]; + VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename); + return; + } + + mTileCacheSetPalette(m_tileCache.get(), m_objInfo.paletteSet); + png_structp png = PNGWriteOpen(vf); + png_infop info = PNGWriteHeader8(png, m_objInfo.width * 8, m_objInfo.height * 8); + + const uint16_t* rawPalette = mTileCacheGetPalette(m_tileCache.get(), m_objInfo.paletteId); + unsigned colors = 1 << m_objInfo.bits; + uint32_t palette[256]; + for (unsigned c = 0; c < colors && c < 256; ++c) { + uint16_t color = rawPalette[c]; + palette[c] = M_R8(rawPalette[c]); + palette[c] |= M_G8(rawPalette[c]) << 8; + palette[c] |= M_B8(rawPalette[c]) << 16; + if (c) { + palette[c] |= 0xFF000000; + } + } + PNGWritePalette(png, info, palette, colors); + + uint8_t* buffer = new uint8_t[m_objInfo.width * m_objInfo.height * 8 * 8]; + unsigned t = m_objInfo.tile; + for (int y = 0; y < m_objInfo.height; ++y) { + for (int x = 0; x < m_objInfo.width; ++x, ++t) { + compositeTile(t, static_cast(buffer), m_objInfo.width * 8, x * 8, y * 8, m_objInfo.bits); + } + t += m_objInfo.stride - m_objInfo.width; + } + PNGWritePixels8(png, m_objInfo.width * 8, m_objInfo.height * 8, m_objInfo.width * 8, static_cast(buffer)); + PNGWriteClose(png, info); + delete[] buffer; +} bool ObjView::ObjInfo::operator!=(const ObjInfo& other) { return other.tile != tile || other.width != width || other.height != height || - other.stride != stride; + other.stride != stride || + other.paletteId != paletteId || + other.paletteSet != paletteSet; } diff --git a/src/platform/qt/ObjView.h b/src/platform/qt/ObjView.h index 725fabfd4..16a72961b 100644 --- a/src/platform/qt/ObjView.h +++ b/src/platform/qt/ObjView.h @@ -21,6 +21,9 @@ Q_OBJECT public: ObjView(GameController* controller, QWidget* parent = nullptr); +public slots: + void exportObj(); + private slots: void selectObj(int); void translateIndex(int); @@ -43,6 +46,9 @@ private: unsigned width; unsigned height; unsigned stride; + unsigned paletteId; + unsigned paletteSet; + unsigned bits; bool operator!=(const ObjInfo&); } m_objInfo; diff --git a/src/platform/qt/ObjView.ui b/src/platform/qt/ObjView.ui index f315866ec..37dcf9533 100644 --- a/src/platform/qt/ObjView.ui +++ b/src/platform/qt/ObjView.ui @@ -7,7 +7,7 @@ 0 0 454 - 375 + 385 @@ -70,6 +70,13 @@ + + + + Export + + + diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 20737b662..63ca088fc 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -148,7 +148,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this); m_ui.stackedWidget->addWidget(editor); - m_ui.tabs->addItem("Keyboard"); + m_ui.tabs->addItem(tr("Keyboard")); connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save())); GBAKeyEditor* buttonEditor = nullptr; @@ -157,7 +157,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); m_ui.stackedWidget->addWidget(buttonEditor); - m_ui.tabs->addItem("Controllers"); + m_ui.tabs->addItem(tr("Controllers")); connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save())); #endif @@ -176,7 +176,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC shortcutView->setController(shortcutController); shortcutView->setInputController(inputController); m_ui.stackedWidget->addWidget(shortcutView); - m_ui.tabs->addItem("Shortcuts"); + m_ui.tabs->addItem(tr("Shortcuts")); } void SettingsView::selectBios(QLineEdit* bios) { @@ -206,6 +206,7 @@ void SettingsView::updateConfig() { saveSetting("mute", m_ui.mute); saveSetting("rewindEnable", m_ui.rewind); saveSetting("rewindBufferCapacity", m_ui.rewindCapacity); + saveSetting("rewindSave", m_ui.rewindSave); saveSetting("resampleVideo", m_ui.resampleVideo); saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections); saveSetting("suspendScreensaver", m_ui.suspendScreensaver); @@ -234,13 +235,13 @@ void SettingsView::updateConfig() { break; } - int loadState = 0; + int loadState = SAVESTATE_RTC; loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; saveSetting("loadStateExtdata", loadState); - int saveState = 0; + int saveState = SAVESTATE_RTC; saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; @@ -287,6 +288,7 @@ void SettingsView::reloadConfig() { loadSetting("mute", m_ui.mute); loadSetting("rewindEnable", m_ui.rewind); loadSetting("rewindBufferCapacity", m_ui.rewindCapacity); + loadSetting("rewindSave", m_ui.rewindSave); loadSetting("resampleVideo", m_ui.resampleVideo); loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections); loadSetting("suspendScreensaver", m_ui.suspendScreensaver); @@ -319,7 +321,7 @@ void SettingsView::reloadConfig() { bool ok; int loadState = loadSetting("loadStateExtdata").toInt(&ok); if (!ok) { - loadState = SAVESTATE_SCREENSHOT; + loadState = SAVESTATE_SCREENSHOT | SAVESTATE_RTC; } m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT); m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA); @@ -327,7 +329,7 @@ void SettingsView::reloadConfig() { int saveState = loadSetting("saveStateExtdata").toInt(&ok); if (!ok) { - saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS; + saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC; } m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT); m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 95df60ebd..b0a07cc9a 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -383,7 +383,7 @@ - Resample video + Bilinear filtering @@ -535,21 +535,21 @@ - + Qt::Horizontal - + Idle loops: - + @@ -568,21 +568,21 @@ - + Qt::Horizontal - + Savestate extra data: - + Screenshot @@ -592,7 +592,7 @@ - + Save data @@ -602,7 +602,7 @@ - + Cheat codes @@ -612,14 +612,14 @@ - + Load extra data: - + Screenshot @@ -629,27 +629,37 @@ - + Save data - + Cheat codes - + Qt::Horizontal + + + + Rewind affects save data + + + true + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 9d0d39fff..d6ad1d945 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1275,7 +1275,7 @@ void Window::setupMenu(QMenuBar* menubar) { m_config->updateOption("lockAspectRatio"); ConfigOption* resampleVideo = m_config->addOption("resampleVideo"); - resampleVideo->addBoolean(tr("Resample video"), avMenu); + resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu); resampleVideo->connect([this](const QVariant& value) { m_display->filter(value.toBool()); }, this); @@ -1482,12 +1482,17 @@ void Window::setupMenu(QMenuBar* menubar) { ConfigOption* rewindEnable = m_config->addOption("rewindEnable"); rewindEnable->connect([this](const QVariant& value) { - m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt()); + m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt(), m_config->getOption("rewindSave").toInt()); }, this); ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity"); rewindBufferCapacity->connect([this](const QVariant& value) { - m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt()); + m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt(), m_config->getOption("rewindSave").toInt()); + }, this); + + ConfigOption* rewindSave = m_config->addOption("rewindSave"); + rewindBufferCapacity->connect([this](const QVariant& value) { + m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), m_config->getOption("rewindBufferCapacity").toInt(), value.toBool()); }, this); ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections"); diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts new file mode 100644 index 000000000..6b748558c --- /dev/null +++ b/src/platform/qt/ts/mgba-de.ts @@ -0,0 +1,4327 @@ + + + + + AboutScreen + + + About + Über + + + + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Foren / Support</a> • <a href="https://patreon.com/mgba">Spenden</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Quellcode</a> + + + + {projectName} + {projectName} + + + + {projectName} would like to thank the following patrons from Patreon: + {projectName} möchte den folgenden Unterstützern auf Patreon danken: + + + + © 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + © 2013 – 2016 Jeffrey Pfau, lizenziert unter der Mozilla Public License, Version 2.0 +Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. + + + + {patrons} + {patrons} + + + + {projectVersion} + {projectVersion} + + + + {logo} + {logo} + + + + {projectName} is an open-source Game Boy Advance emulator + {projectName} ist ein quelloffener Game Boy Advance-Emulator + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + + + + ArchiveInspector + + + Open in archive... + In Archiv öffnen... + + + + Loading... + Laden... + + + + AssetTile + + + AssetTile + AssetTile + + + + Tile # + Tile # + + + + 0 + 0 + + + + Address + Adresse + + + + 0x06000000 + 0x06000000 + + + + Red + Rot + + + + Green + Grün + + + + Blue + Blau + + + + + + 0x00 (00) + 0x00 (00) + + + + CheatsView + + + Cheats + Cheats + + + + Remove + Entfernen + + + + Save + Speichern + + + + Load + Laden + + + + Add New Set + Neues Set hinzufügen + + + + Add + Hinzufügen + + + + DebuggerConsole + + + Debugger + Debugger + + + + Enter command (try `help` for more info) + Geben Sie ein Kommando ein (versuchen Sie 'help' für weitere Informationen) + + + + Break + Unterbrechen + + + + GIFView + + + Record GIF + GIF aufzeichnen + + + + Start + Start + + + + Stop + Stop + + + + Select File + Datei wählen + + + + Frameskip + Frameskip + + + + Frame delay (ms) + Bildverzögerung (ms) + + + + Automatic + Automatisch + + + + IOViewer + + + I/O Viewer + I/O-Betrachter + + + + 0x0000 + 0x0000 + + + + 2 + 2 + + + + 5 + 5 + + + + 4 + 4 + + + + 7 + 7 + + + + 0 + 0 + + + + 9 + 9 + + + + 1 + 1 + + + + 3 + 3 + + + + 8 + 8 + + + + C + C + + + + E + E + + + + 6 + 6 + + + + D + D + + + + F + F + + + + A + A + + + + B + B + + + + LibraryView + + + Library + Bibliothek + + + + LoadSaveState + + + + %1 State + Savestate %1 + + + + + + + + + + + + No Save + Kein Savestate + + + + 1 + 1 + + + + 2 + 2 + + + + 3 + 3 + + + + 4 + 4 + + + + 5 + 5 + + + + 6 + 6 + + + + 7 + 7 + + + + 8 + 8 + + + + 9 + 9 + + + + LogView + + + Logs + Logs + + + + Enabled Levels + Aktivierte Log-Level + + + + Debug + Debug + + + + Stub + Stub + + + + Info + Info + + + + Warning + Warnung + + + + Error + Fehler + + + + Fatal + Fatal + + + + Game Error + Spiel-Fehler + + + + Clear + Leeren + + + + Max Lines + Max. Zeilen + + + + MemoryView + + + Memory + Speicher + + + + Inspect Address: + Untersuche Adresse: + + + + 0x + 0x + + + + Set Alignment: + Ausrichtung festlegen: + + + + 1 Byte + 1 Byte + + + + 2 Bytes + 2 Bytes + + + + 4 Bytes + 4 Bytes + + + + Signed Integer: + Signed Integer: + + + + String: + String: + + + + Load TBL + TBL laden + + + + Copy Selection + Auswahl kopieren + + + + Paste + Einfügen + + + + Save Selection + Auswahl speichern + + + + Load + Laden + + + + Unsigned Integer: + Unsigned Integer: + + + + ObjView + + + Sprites + Sprites + + + + + × + × + + + + Magnification + Vergrößerung + + + + Export + Exportieren + + + + Attributes + Eigenschaften + + + + Transform + Transform + + + + Off + Aus + + + + Palette + Palette + + + + + + + 0 + 0 + + + + Double Size + Doppelte Größe + + + + + + + Return, Ctrl+R + Eingabe, Strg+R + + + + Flipped + Gespiegelt + + + + H + H + + + + V + V + + + + Mode + Modus + + + + Normal + Normal + + + + Mosaic + Mosaic + + + + Enabled + Aktiviert + + + + Priority + Priorität + + + + Tile + + + + + Geometry + Geometrie + + + + Position + Position + + + + , + , + + + + Dimensions + Abmessungen + + + + + 8 + 8 + + + + Address + Adresse + + + + 0x07000000 + 0x07000000 + + + + OverrideView + + + Game Overrides + Spiel-Überschreibungen + + + + Game Boy Advance + Game Boy Advance + + + + + + + Autodetect + Automatisch erkennen + + + + Realtime clock + Echtzeituhr + + + + Gyroscope + Gyroskop + + + + Tilt + Neigungssensor + + + + Light sensor + Lichtsensor + + + + Rumble + Rüttel-Effekt + + + + Save type + Speichertyp + + + + + None + keiner + + + + SRAM + SRAM + + + + Flash 512kb + Flash 512kb + + + + Flash 1Mb + Flash 1Mb + + + + EEPROM + EEPROM + + + + Idle loop + Leerlaufprozess + + + + Game Boy Player features + Game Boy Player-Features + + + + Game Boy + Game Boy + + + + Game Boy model + Game Boy-Modell + + + + Game Boy (DMG) + Game Boy (DMG) + + + + Game Boy Color (CGB) + Game Boy Color (CGB) + + + + Game Boy Advance (AGB) + Game Boy Advance (AGB) + + + + Memory bank controller + Speicherbank-Controller + + + + MBC1 + MBC1 + + + + MBC2 + MBC2 + + + + MBC3 + MBC3 + + + + MBC3 + RTC + MBC3 + RTC + + + + MBC5 + MBC5 + + + + MBC5 + Rumble + MBC5 + Rumble + + + + MBC7 + MBC7 + + + + HuC-3 + HuC-3 + + + + PaletteView + + + Palette + Palette + + + + Background + Hintergrund + + + + Objects + Objekte + + + + Selection + Auswahl + + + + Red + Rot + + + + Green + Grün + + + + Blue + Blau + + + + + + 0x00 (00) + 0x00 (00) + + + + 16-bit value + 16-Bit-Wert + + + + Hex code + Hex-Code + + + + Palette index + Paletten-Index + + + + 0x0000 + 0x0000 + + + + #000000 + #000000 + + + + 000 + 000 + + + + Export BG + BG exportieren + + + + Export OBJ + OBJ exportieren + + + + QGBA::AssetTile + + + %0%1%2 + %0%1%2 + + + + + + 0x%0 (%1) + 0x%0 (%1) + + + + QGBA::CheatsModel + + + (untitled) + (unbenannt) + + + + Failed to open cheats file: %1 + Fehler beim Öffnen der Cheat-Datei: %1 + + + + QGBA::CheatsView + + + + Add GameShark + GameShark hinzufügen + + + + Add Pro Action Replay + Pro Action Replay hinzufügen + + + + Add CodeBreaker + CodeBreaker hinzufügen + + + + Add GameGenie + GameGenie hinzufügen + + + + + Select cheats file + Cheat-Datei auswählen + + + + QGBA::GBAKeyEditor + + + Clear Button + Button löschen + + + + Clear Analog + Analog löschen + + + + Refresh + Aktualisieren + + + + Set all + Alle belegen + + + + QGBA::GDBWindow + + + Server settings + Server-Einstellungen + + + + Local port + Lokaler Port + + + + Bind address + Bind-Adresse + + + + Break + Unterbrechen + + + + Stop + Stop + + + + Start + Start + + + + Crash + Absturz + + + + Could not start GDB server + Konnte GDB-Server nicht starten + + + + QGBA::GIFView + + + Failed to open output GIF file: %1 + Fehler beim Öffnen der Ausgabe-GIF-Datei: %1 + + + + Select output file + Ausgabedatei auswählen + + + + Graphics Interchange Format (*.gif) + Graphics Interchange Format (*.gif) + + + + QGBA::GameController + + + + Failed to open game file: %1 + Fehler beim Öffnen der Spieldatei: %1 + + + + Failed to open save file: %1 + Fehler beim Öffnen der Speicherdatei: %1 + + + + Failed to open snapshot file for reading: %1 + Konnte Snapshot-Datei %1 nicht zum Lesen öffnen + + + + Failed to open snapshot file for writing: %1 + Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen + + + + Failed to start audio processor + Fehler beim Starten des Audio-Prozessors + + + + QGBA::IOViewer + + + Background mode + Hintergrund-Modus + + + + Mode 0: 4 tile layers + Mode 0: 4 Tile-Ebenen + + + + Mode 1: 2 tile layers + 1 rotated/scaled tile layer + Mode 1: 2 Tile-Ebenen + 1 rotierte/skalierte Tile-Ebene + + + + Mode 2: 2 rotated/scaled tile layers + Mode 2: 2 rotierte/skalierte Tile-Ebenen + + + + Mode 3: Full 15-bit bitmap + Mode 3: Volles 15-Bit-Bitmap + + + + Mode 4: Full 8-bit bitmap + Mode 4: Volles 8-Bit-Bitmap + + + + Mode 5: Small 15-bit bitmap + Mode 5: Kleines 15-Bit-Bitmap + + + + CGB Mode + CGB-Modus + + + + Frame select + Bildauswahl + + + + Unlocked HBlank + HBlank entsperrt + + + + Linear OBJ tile mapping + Lineares OBJ-Tile-Mapping + + + + Force blank screen + Leeren Bildschirm erzwingen + + + + Enable background 0 + Aktiviere Hintergrund 0 + + + + Enable background 1 + Aktiviere Hintergrund 1 + + + + Enable background 2 + Aktiviere Hintergrund 2 + + + + Enable background 3 + Aktiviere Hintergrund 3 + + + + Enable OBJ + Aktiviere OBJ + + + + Enable Window 0 + Aktiviere Fenster 0 + + + + Enable Window 1 + Aktiviere Fenster 1 + + + + Enable OBJ Window + Aktiviere OBJ-Fenster + + + + Currently in VBlank + Aktuell in VBlank + + + + Currently in HBlank + Aktuell in HBlank + + + + Currently in VCounter + Aktuell in VCounter + + + + Enable VBlank IRQ generation + Aktiviere VBlank IRQ-Generierung + + + + Enable HBlank IRQ generation + Aktiviere HBlank IRQ-Generierung + + + + Enable VCounter IRQ generation + Aktiviere VCounter IRQ-Generierung + + + + VCounter scanline + VCounter-Rasterzeile + + + + Current scanline + Aktuelle Rasterzeile + + + + + + + Priority + Priorität + + + + + + + Tile data base (* 16kB) + Tile-Daten-Basis (* 16kB) + + + + + + + Enable mosaic + Aktiviere Mosaic + + + + + + + Enable 256-color + Aktiviere 256 Farben + + + + + + + Tile map base (* 2kB) + Tile-Map-Basis (* 2kB) + + + + + + + Background dimensions + Hintergrund-Abmessungen + + + + + Overflow wraps + Umbrüche + + + + + + + Horizontal offset + Horizontaler Versatz + + + + + + + Vertical offset + Vertikaler Versatz + + + + + + + + + + + + + + + Fractional part + + + + + + + + + + + + Integer part + Ganzzahl-Anteil + + + + + + + Integer part (bottom) + Ganzzahl-Anteil (unten) + + + + + + + Integer part (top) + Ganzzahl-Anteil (oben) + + + + + End x + x-Endwert + + + + + Start x + x-Startwert + + + + + End y + y-Endwert + + + + + Start y + y-Startwert + + + + Window 0 enable BG 0 + Fenster 0: aktiviere BG 0 + + + + Window 0 enable BG 1 + Fenster 0: aktiviere BG 1 + + + + Window 0 enable BG 2 + Fenster 0: aktiviere BG2 + + + + Window 0 enable BG 3 + Fenster 0: aktiviere BG 3 + + + + Window 0 enable OBJ + Fenster 0: aktiviere OBJ + + + + Window 0 enable blend + Fenster 0: aktiviere Überblendung + + + + Window 1 enable BG 0 + Fenster 1: aktiviere BG 0 + + + + Window 1 enable BG 1 + Fenster 1: aktiviere BG 1 + + + + Window 1 enable BG 2 + Fenster 1: aktiviere BG 2 + + + + Window 1 enable BG 3 + Fenster 1: aktiviere BG 3 + + + + Window 1 enable OBJ + Fenster 1: Aktiviere OBJ + + + + Window 1 enable blend + Fenster 1: aktivieren Überblendung + + + + Outside window enable BG 0 + Äußeres Fenster: aktiviere BG 0 + + + + Outside window enable BG 1 + Äußeres Fenster: aktiviere BG 1 + + + + Outside window enable BG 2 + Äußeres Fenster: aktiviere BG 2 + + + + Outside window enable BG 3 + Äußeres Fenster: aktiviere BG 3 + + + + Outside window enable OBJ + Äußeres Fenster: aktiviere OBJ + + + + Outside window enable blend + Äußeres Fenster: aktiviere Überblendung + + + + OBJ window enable BG 0 + OBJ-Fenster: aktiviere BG 0 + + + + OBJ window enable BG 1 + OBJ-Fenster: aktiviere BG 1 + + + + OBJ window enable BG 2 + OBJ-Fenster: aktiviere BG 2 + + + + OBJ window enable BG 3 + OBJ-Fenster: aktiviere BG 3 + + + + OBJ window enable OBJ + OBJ-Fenster: aktiviere OBJ + + + + OBJ window enable blend + OBJ-Fenster: aktiviere Überblendung + + + + Background mosaic size vertical + Vertikale Größe der Hintergrund-Pixelierung + + + + Background mosaic size horizontal + Horizontale Größe der Hintergrund-Pixelierung + + + + Object mosaic size vertical + Vertikale Größe der Objekt-Pixelierung + + + + Object mosaic size horizontal + Horizontale Größe der Objekt-Pixelierung + + + + BG 0 target 1 + BG 0 Ziel 1 + + + + BG 1 target 1 + BG 1 Ziel 1 + + + + BG 2 target 1 + BG 2 Ziel 1 + + + + BG 3 target 1 + BG 3 Ziel 1 + + + + OBJ target 1 + OBJ Ziel 1 + + + + Backdrop target 1 + Hintergrund Ziel 1 + + + + Blend mode + Überblend-Modus + + + + Disabled + Deaktiviert + + + + Additive blending + Additive Überblendung + + + + Brighten + Aufhellen + + + + Darken + Abdunkeln + + + + BG 0 target 2 + BG 0 Ziel 2 + + + + BG 1 target 2 + BG 1 Ziel 2 + + + + BG 2 target 2 + BG 2 Ziel 2 + + + + BG 3 target 2 + BG 3 Ziel 2 + + + + OBJ target 2 + OBJ Ziel 2 + + + + Backdrop target 2 + Hintergrund Ziel 2 + + + + Blend A (target 1) + A Überblenden (Ziel 1) + + + + Blend B (target 2) + B Überblenden (Ziel 2) + + + + Blend Y + Y Überblenden + + + + Sweep shifts + + + + + Sweep subtract + + + + + Sweep time (in 1/128s) + + + + + + + + Sound length + Sound-Länge + + + + + Duty cycle + + + + + + + Envelope step time + + + + + + + Envelope increase + Hüllkurve erhöhen + + + + + + Initial volume + Initiale Lautstärke + + + + + + Sound frequency + Sound-Frequenz + + + + + + + Timed + + + + + + + + Reset + Reset + + + + Double-size wave table + + + + + Active wave table + Aktive Wave-Table + + + + Enable channel 3 + Aktiviere Kanal 3 + + + + Volume + Lautstärke + + + + 0% + 0% + + + + + 100% + 100% + + + + + 50% + 50% + + + + + 25% + 25% + + + + + + + 75% + 75% + + + + Clock divider + Frequenzteiler + + + + Register stages + + + + + 15 + 15 + + + + 7 + 7 + + + + Shifter frequency + + + + + PSG volume right + PSG-Lautstärke rechts + + + + PSG volume left + PSG-Lautstärke links + + + + Enable channel 1 right + Aktiviere Kanal 1 rechts + + + + Enable channel 2 right + Aktiviere Kanal 2 rechts + + + + Enable channel 3 right + Aktiviere Kanal 3 rechts + + + + Enable channel 4 right + Aktiviere Kanal 4 rechts + + + + Enable channel 1 left + Aktiviere Kanal 1 links + + + + Enable channel 2 left + Aktiviere Kanal 2 links + + + + Enable channel 3 left + Aktiviere Kanal 3 links + + + + Enable channel 4 left + Aktiviere Kanal 4 links + + + + PSG master volume + PSG-Master-Lauststärke + + + + Loud channel A + + + + + Loud channel B + + + + + Enable channel A right + Aktiviere Kanal A rechts + + + + Enable channel A left + Aktiviere Kanal A links + + + + Channel A timer + Kanal A-Zeitgeber + + + + + 0 + 0 + + + + + + + + + + + + 1 + 1 + + + + Channel A reset + Kanal A zurücksetzen + + + + Enable channel B right + Aktiviere Kanal B rechts + + + + Enable channel B left + Aktiviere Kanal B links + + + + Channel B timer + Kanal B-Zeitgeber + + + + Channel B reset + Kanal B zurücksetzen + + + + Active channel 1 + Kanal 1 aktiv + + + + Active channel 2 + Kanal 2 aktiv + + + + Active channel 3 + Kanal 3 aktiv + + + + Active channel 4 + Kanal 4 aktiv + + + + Enable audio + Audio aktivieren + + + + Bias + + + + + Resolution + Auflösung + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample + Sample + + + + + + + + + + + Address (bottom) + Adresse (unten) + + + + + + + + + + + Address (top) + Adresse (oben) + + + + + + + Word count + Wort-Anzahl + + + + + + + Destination offset + Zielversatz + + + + + + + + + + + Increment + Erhöhen + + + + + + + + + + + Decrement + Verringern + + + + + + + + + + + Fixed + Fest + + + + + + + Increment and reload + Erhöhen und neu laden + + + + + + + Source offset + Quellversatz + + + + + + + Repeat + Wiederholen + + + + + + + 32-bit + 32-Bit + + + + + + + Start timing + + + + + + + + Immediate + Unmittelbar + + + + + + + + + VBlank + VBlank + + + + + + + + + HBlank + HBlank + + + + + + + + + + + + IRQ + IRQ + + + + + + + + + + + Enable + Aktivieren + + + + + + Audio FIFO + Audio FIFO + + + + Video Capture + Videoaufzeichnung + + + + DRQ + DRQ + + + + + + + Value + Wert + + + + + + + Scale + Skalierung + + + + + + + 1/64 + 1/64 + + + + + + + 1/256 + 1/256 + + + + + + + 1/1024 + 1/1024 + + + + + + Cascade + Kaskade + + + + + A + A + + + + + B + B + + + + + Select + Select + + + + + Start + Start + + + + + Right + Rechts + + + + + Left + Links + + + + + Up + Oben + + + + + Down + Unten + + + + + R + R + + + + + L + L + + + + Condition + Zustand + + + + SC + SC + + + + SD + SD + + + + SI + SI + + + + SO + SO + + + + + VCounter + VCounter + + + + + Timer 0 + Zähler 0 + + + + + Timer 1 + Zähler 1 + + + + + Timer 2 + Zähler 2 + + + + + Timer 3 + Zähler 3 + + + + + SIO + SIO + + + + + DMA 0 + DMA 0 + + + + + DMA 1 + DMA 1 + + + + + DMA 2 + DMA 2 + + + + + DMA 3 + DMA 3 + + + + + Keypad + + + + + + Gamepak + Spielmodul + + + + SRAM wait + Warten auf SRAM + + + + + + + + 4 + 4 + + + + + + + 3 + 3 + + + + + + + + 2 + 2 + + + + + + + + 8 + 8 + + + + Cart 0 non-sequential + Modul 0 nicht-sequenziell + + + + Cart 0 sequential + Modul 0 sequenziell + + + + Cart 1 non-sequential + Modul 1 nicht-sequenziell + + + + Cart 1 sequential + Modul 1 sequenziell + + + + Cart 2 non-sequential + Modul 2 nicht-sequenziell + + + + Cart 2 sequential + Modul 2 sequenziell + + + + PHI terminal + + + + + Disable + Deaktivieren + + + + 4.19MHz + 4.19MHz + + + + 8.38MHz + 8.38MHz + + + + 16.78MHz + 16.78MHz + + + + Gamepak prefetch + Spielmodul vorladen + + + + Enable IRQs + Aktiviere IRQs + + + + QGBA::KeyEditor + + + + --- + --- + + + + QGBA::LibraryModel + + + Name + Name + + + + Filename + Dateiname + + + + Size + Größe + + + + Platform + Plattform + + + + GBA + GBA + + + + GB + GB + + + + ? + ? + + + + Location + Ort + + + + CRC32 + CRC32 + + + + QGBA::LoadSaveState + + + Load State + Savestate laden + + + + Save State + Savestate speichern + + + + Empty + Leer + + + + Corrupted + Defekt + + + + Slot %1 + Speicherplatz %1 + + + + QGBA::LogController + + + DEBUG + DEBUG + + + + STUB + STUB + + + + INFO + INFO + + + + WARN + WARN + + + + ERROR + ERROR + + + + FATAL + FATAL + + + + GAME ERROR + GAME ERROR + + + + QGBA::MemoryModel + + + Copy selection + Auswahl kopieren + + + + Save selection + Auswahl speichern + + + + Paste + Einfügen + + + + Load + Laden + + + + + All + Alle + + + + Load TBL + TBL laden + + + + Save selected memory + Ausgewählten Speicher abspeichern + + + + Failed to open output file: %1 + Fehler beim Öffnen der Ausgabedatei: %1 + + + + Load memory + Lade Speicher + + + + Failed to open input file: %1 + Fehler beim Laden der Eingabedatei: %1 + + + + TBL + TBL + + + + ISO-8859-1 + ISO-8859-1 + + + + QGBA::ObjView + + + + 0x%0 + 0x%0 + + + + Off + Aus + + + + Normal + Normal + + + + Trans + Trans + + + + OBJWIN + OBJWIN + + + + Invalid + Ungültig + + + + + N/A + N/A + + + + Export sprite + Sprite exportieren + + + + Portable Network Graphics (*.png) + Portable Network Graphics (*.png) + + + + Failed to open output PNG file: %1 + Fehler beim Öffnen der Ausgabe-PNG-Datei: %1 + + + + QGBA::PaletteView + + + #%0 + #%0 + + + + 0x%0 + 0x%0 + + + + %0 + %0 + + + + + + 0x%0 (%1) + 0x%0 (%1) + + + + Export palette + Palette exportieren + + + + Windows PAL (*.pal);;Adobe Color Table (*.act) + Windows PAL (*.pal);;Adobe Color Table (*.act) + + + + Failed to open output palette file: %1 + Fehler beim Öffnen der Ausgabe-Palettendatei: %1 + + + + QGBA::ROMInfo + + + + + + + (unknown) + (unbekannt) + + + + + bytes + Bytes + + + + (no database present) + (keine Datenbank vorhanden) + + + + QGBA::SettingsView + + + Qt Multimedia + Qt Multimedia + + + + SDL + SDL + + + + Software (Qt) + Software (Qt) + + + + OpenGL + OpenGL + + + + OpenGL (force version 1.x) + OpenGL (erzwinge Version 1.x) + + + + Keyboard + Tastatur + + + + Controllers + Gamepads + + + + Shortcuts + Tastenkürzel + + + + Select BIOS + BIOS auswählen + + + + QGBA::ShaderSelector + + + No shader active + Kein Shader aktiv + + + + Load shader + Shader laden + + + + %1 Shader (%.shader) + %1 Shader (%.shader) + + + + No shader loaded + Kein Shader geladen + + + + by %1 + von %1 + + + + Preprocessing + Vorbehandlung + + + + Pass %1 + Durchlauf %1 + + + + QGBA::ShortcutController + + + Action + Aktion + + + + Keyboard + Tastatur + + + + Gamepad + Gamepad + + + + QGBA::VideoView + + + Failed to open output video file: %1 + Fehler beim Öffnen der Ausgabe-Videodatei: %1 + + + + Native (%0x%1) + Nativ (%0x%1) + + + + Select output file + Ausgabedatei auswählen + + + + QGBA::Window + + + Game Boy Advance ROMs (%1) + Game Boy Advance-ROMs (%1) + + + + Game Boy ROMs (%1) + Game Boy-ROMs (%1) + + + + All ROMs (%1) + Alle ROMs (%1) + + + + Archives (%1) + Archive (%1) + + + + + + Select ROM + ROM auswählen + + + + Game Boy Advance save files (%1) + Game Boy Advance-Speicherdateien (%1) + + + + + + Select save + Speicherdatei wählen + + + + Select patch + Patch wählen + + + + Patches (*.ips *.ups *.bps) + Patches (*.ips *.ups *.bps) + + + + + GameShark saves (*.sps *.xps) + GameShark-Speicherdaten (*.sps *.xps) + + + + Crash + Absturz + + + + The game has crashed with the following error: + +%1 + Das Spiel ist mit der folgenden Fehlermeldung abgestürzt: + +%1 + + + + Couldn't Load + Konnte nicht geladen werden + + + + Could not load game. Are you sure it's in the correct format? + Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt? + + + + Unimplemented BIOS call + Nichtimplementierter BIOS-Aufruf + + + + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. + Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. + + + + Really make portable? + Portablen Modus wirklich aktivieren? + + + + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? + Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? + + + + Restart needed + Neustart benötigt + + + + Some changes will not take effect until the emulator is restarted. + Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. + + + + - Player %1 of %2 + - Spieler %1 von %2 + + + + %1 - %2 + %1 - %2 + + + + %1 - %2 - %3 + %1 - %2 - %3 + + + + %1 - %2 (%3 fps) - %4 + %1 - %2 (%3 Bilder/Sekunde) - %4 + + + + &File + &Datei + + + + Load &ROM... + &ROM laden... + + + + Load ROM in archive... + ROM aus Archiv laden... + + + + Load temporary save... + Temporäre Speicherdatei laden... + + + + Load &patch... + &Patch laden... + + + + Boot BIOS + BIOS booten + + + + Replace ROM... + ROM ersetzen... + + + + ROM &info... + ROM-&Informationen... + + + + Recent + Zuletzt verwendet + + + + Make portable + Portablen Modus aktivieren + + + + &Load state + Savestate &laden + + + + F10 + F10 + + + + &Save state + Savestate &speichern + + + + Shift+F10 + Umschalt+F10 + + + + Quick load + Schnell laden + + + + Quick save + Schnell speichern + + + + Load recent + Lade zuletzt gespeicherten Savestate + + + + Save recent + Speichere aktuellen Stand + + + + Undo load state + Laden des Savestate rückgängig machen + + + + F11 + F11 + + + + Undo save state + Speichern des Savestate rückgängig machen + + + + Shift+F11 + Umschalt+F11 + + + + + State &%1 + Savestate &%1 + + + + F%1 + F%1 + + + + Shift+F%1 + Umschalt+F%1 + + + + Import GameShark Save + Importiere GameShark-Speicherstand + + + + Export GameShark Save + Exportiere GameShark-Speicherstand + + + + New multiplayer window + Neues Multiplayer-Fenster + + + + About + Über + + + + E&xit + &Beenden + + + + &Emulation + &Emulation + + + + &Reset + Zu&rücksetzen + + + + Ctrl+R + Strg+R + + + + Sh&utdown + B&eenden + + + + Yank game pak + Spielmodul herausziehen + + + + &Pause + &Pause + + + + Ctrl+P + Strg+P + + + + &Next frame + &Nächstes Bild + + + + Ctrl+N + Strg+N + + + + Fast forward (held) + Schneller Vorlauf (gehalten) + + + + &Fast forward + Schneller &Vorlauf + + + + Shift+Tab + Umschalt+Tab + + + + Fast forward speed + Vorlauf-Geschwindigkeit + + + + Unbounded + Unbegrenzt + + + + %0x + %0x + + + + Rewind (held) + Zurückspulen (gehalten) + + + + Re&wind + Zur&ückspulen + + + + ~ + ~ + + + + Step backwards + Schrittweiser Rücklauf + + + + Ctrl+B + Strg+B + + + + Sync to &video + Mit &Video synchronisieren + + + + Sync to &audio + Mit &Audio synchronisieren + + + + Solar sensor + Solar-Sensor + + + + Increase solar level + Sonnen-Level erhöhen + + + + Decrease solar level + Sonnen-Level verringern + + + + Brightest solar level + Hellster Sonnen-Level + + + + Darkest solar level + Dunkelster Sonnen-Level + + + + Brightness %1 + Helligkeit %1 + + + + Audio/&Video + Audio/&Video + + + + Frame size + Bildgröße + + + + %1x + %1x + + + + Toggle fullscreen + Vollbildmodus umschalten + + + + Lock aspect ratio + Seitenverhältnis sperren + + + + Frame&skip + Frame&skip + + + + Shader options... + Shader-Optionen... + + + + Mute + Stummschalten + + + + FPS target + Bildwiederholrate + + + + 15 + 15 + + + + 30 + 30 + + + + 45 + 45 + + + + Native (59.7) + Nativ (59.7) + + + + 60 + 60 + + + + 90 + 90 + + + + 120 + 120 + + + + 240 + 240 + + + + Take &screenshot + &Screenshot erstellen + + + + F12 + F12 + + + + Record output... + Ausgabe aufzeichen... + + + + Record GIF... + GIF aufzeichen... + + + + Video layers + Video-Ebenen + + + + Background %0 + Hintergrund %0 + + + + OBJ (sprites) + OBJ (Sprites) + + + + Audio channels + Audio-Kanäle + + + + Channel %0 + Kanal %0 + + + + Channel A + Kanal A + + + + Channel B + Kanal B + + + + &Tools + &Werkzeuge + + + + View &logs... + &Logs ansehen... + + + + Game &overrides... + Spiel-&Überschreibungen... + + + + Game &Pak sensors... + Game &Pak-Sensoren... + + + + &Cheats... + &Cheats... + + + + Open debugger console... + Debugger-Konsole äffnen... + + + + Start &GDB server... + &GDB-Server starten... + + + + Settings... + Einstellungen... + + + + Select folder + Ordner auswählen + + + + Add folder to library... + Ordner zur Bibliothek hinzufügen... + + + + Bilinear filtering + Bilineare Filterung + + + + View &palette... + &Palette betrachten... + + + + View &sprites... + &Sprites betrachten... + + + + View &tiles... + &Tiles betrachten... + + + + View memory... + Speicher betrachten... + + + + View &I/O registers... + &I/O-Register betrachten... + + + + Exit fullscreen + Vollbildmodus beenden + + + + Autofire + Autofeuer + + + + Autofire A + Autofeuer A + + + + Autofire B + Autofeuer B + + + + Autofire L + Autofeuer L + + + + Autofire R + Autofeuer R + + + + Autofire Start + Autofeuer Start + + + + Autofire Select + Autofeuer Select + + + + Autofire Up + Autofeuer nach oben + + + + Autofire Right + Autofeuer rechts + + + + Autofire Down + Autofeuer nach unten + + + + Autofire Left + Autofeuer links + + + + ROMInfo + + + ROM Info + ROM-Info + + + + Game name: + Spiel-Name: + + + + {NAME} + {NAME} + + + + Internal name: + Interner Name: + + + + {TITLE} + {TITLE} + + + + Game ID: + Spiele-ID: + + + + {ID} + {ID} + + + + File size: + Dateigröße: + + + + {SIZE} + {SIZE} + + + + CRC32: + CRC32: + + + + {CRC} + {CRC} + + + + SensorView + + + Sensors + Sensoren + + + + Realtime clock + Echtzeituhr + + + + Fixed time + Feste Zeit + + + + System time + Systemzeit + + + + Start time at + Starte Zeit ab + + + + Now + Jetzt + + + + MM/dd/yy hh:mm:ss AP + dd/mm/yy hh:mm:ss + + + + Light sensor + Lichtsensor + + + + Brightness + Helligkeit + + + + Tilt sensor + Neigungssensor + + + + + Set Y + Setze Y + + + + + Set X + Setze X + + + + Gyroscope + Gyroskop + + + + Sensitivity + Empfindlichkeit + + + + SettingsView + + + Settings + Einstellungen + + + + Audio/Video + Audio/Video + + + + Interface + Benutzeroberfläche + + + + Emulation + Emulation + + + + Paths + Verzeichnisse + + + + Audio driver: + Audio-Treiber: + + + + Audio buffer: + Audio-Puffer: + + + + + 1536 + 1536 + + + + 512 + 512 + + + + 768 + 768 + + + + 1024 + 1024 + + + + 2048 + 2048 + + + + 3072 + 3072 + + + + 4096 + 4096 + + + + samples + Samples + + + + Sample rate: + Sample-Rate: + + + + + 44100 + 44100 + + + + 22050 + 22050 + + + + 32000 + 32000 + + + + 48000 + 48000 + + + + Hz + Hz + + + + Volume: + Lautstärke: + + + + Mute + Stummschalten + + + + Display driver: + Anzeige-Treiber: + + + + Frameskip: + Frameskip: + + + + Skip every + Alle + + + + + frames + Bilder + + + + FPS target: + Bildwiederholrate: + + + + frames per second + Bilder pro Sekunde + + + + Sync: + Synchronisierung: + + + + Video + Video + + + + Audio + Audio + + + + Lock aspect ratio + Seitenverhältnis sperren + + + + Library: + Bibliothek: + + + + Show when no game open + Anzeigen, wenn kein Spiel geöffnet ist + + + + Clear cache + Cache leeren + + + + Fast forward speed: + Vorlauf-Geschwindigkeit: + + + + Rewind affects save data + Rücklauf beeinflusst +Speicherdaten + + + + + + + + + + Browse + Durchsuchen + + + + Use BIOS file if found + BIOS-Datei verwenden, wenn vorhanden + + + + Skip BIOS intro + BIOS-Intro überspringen + + + + × + × + + + + Unbounded + unbegrenzt + + + + Suspend screensaver + Bildschirmschoner deaktivieren + + + + BIOS + BIOS + + + + Pause when inactive + Pause, wenn inaktiv + + + + Run all + Alle ausführen + + + + Remove known + Bekannte entfernen + + + + Detect and remove + Erkennen und entfernen + + + + Allow opposing input directions + Gegensätzliche Eingaberichtungen erlauben + + + + + Screenshot + Screenshot + + + + + Save data + Speicherdaten + + + + + Cheat codes + Cheat-Codes + + + + Enable rewind + Rücklauf aktivieren + + + + Bilinear filtering + Bilineare Filterung + + + + Rewind history: + Rücklauf-Verlauf: + + + + Idle loops: + Leerlaufprozesse: + + + + Savestate extra data: + Zusätzliche Savestate-Daten: + + + + Load extra data: + Lade zusätzliche Daten: + + + + GB BIOS file: + BIOS-Datei für GB: + + + + GBA BIOS file: + BIOS-Datei für GBA: + + + + GBC BIOS file: + BIOS-Datei für GBC: + + + + Save games + Spielstände + + + + + + + Same directory as the ROM + Gleiches Verzeichnis wie ROM + + + + Save states + Savestates + + + + Screenshots + Screenshots + + + + Patches + Patches + + + + ShaderSelector + + + Shaders + Shader + + + + Active Shader: + Aktiver Shader: + + + + Name + Name + + + + Author + Autor + + + + Description + Beschreibung + + + + Unload Shader + Shader entladen + + + + Load New Shader + Neuen Shader laden + + + + ShortcutView + + + Edit Shortcuts + Tastenkürzel bearbeiten + + + + Keyboard + Tastatur + + + + Gamepad + Gamepad + + + + Clear + Löschen + + + + TileView + + + Tiles + Tiles + + + + 256 colors + 256 Farben + + + + × + × + + + + Magnification + Vergrößerung + + + + VideoView + + + Record Video + Video aufzeichen + + + + Start + Start + + + + Stop + Stop + + + + Select File + Datei wählen + + + + Presets + Vorgaben + + + + High Quality + Hohe Qualität + + + + YouTube + YouTube + + + + + WebM + WebM + + + + Lossless + Verlustfrei + + + + 1080p + 1080p + + + + 720p + 720p + + + + 480p + 480p + + + + Native + Nativ + + + + Format + Format + + + + MKV + MKV + + + + AVI + AVI + + + + MP4 + MP4 + + + + PNG + PNG + + + + h.264 + h.264 + + + + VP8 + VP8 + + + + Xvid + Xvid + + + + FFV1 + FFV1 + + + + FLAC + FLAC + + + + Opus + Opus + + + + Vorbis + Vorbis + + + + MP3 + MP3 + + + + AAC + AAC + + + + Uncompressed + Unkomprimiert + + + + Bitrate (kbps) + Bitrate (kbps) + + + + VBR + VBR + + + + ABR + ABR + + + + Dimensions + Abmessungen + + + + : + : + + + + × + × + + + + Lock aspect ratio + Seitenverhältnis sperren + + + + Show advanced + Erweiterte Optionen anzeigen + + + diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index 0b242df8d..3c48832a6 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -6,53 +6,54 @@ About - + Acerca de <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - + <a href="http://mgba.io/">Sitio web</a> • <a href="https://forums.mgba.io/">Foros / Soporte</a> • <a href="https://patreon.com/mgba">Donar</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Código fuente</a> {projectName} - + {projectName} {projectName} would like to thank the following patrons from Patreon: - + {projectName} desea agradecer a los siguientes mecenas de Patreon: © 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + © 2013 - 2016 Jeffrey Pfau, licenciado bajo la Licencia Pública de Mozilla, versión 2.0 +Game Boy Advance es una marca registrada de Nintendo Co., Ltd. {patrons} - + {patrons} {projectVersion} - + {projectVersion} {logo} - + {logo} {projectName} is an open-source Game Boy Advance emulator - + {projectName} es un emulador de Game Boy Advance de código abierto Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> - + Rama Git: <tt>{gitBranch}</tt><br/>Revisión: <tt>{gitCommit}</tt> @@ -60,12 +61,12 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Open in archive... - + Abrir dentro de archivo ... Loading... - + Cargando... @@ -73,49 +74,49 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. AssetTile - + AssetTile Tile # - + Tile Nº 0 - + 0 Address - + Dirección 0x06000000 - + 0x06000000 Red - + Rojo Green - + Verde Blue - + Azul 0x00 (00) - + 0x00 (00) @@ -123,32 +124,32 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Cheats - + Trucos Remove - + Eliminar Save - + Guardar Load - + Cargar Add New Set - + Agregar nuevo conjunto Add - + Agregar @@ -156,17 +157,17 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Debugger - + Depurador Enter command (try `help` for more info) - + Ingresa un comando (prueba con 'help' para más información) Break - + Entrar en depuración @@ -174,37 +175,37 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Record GIF - + Grabar GIF Start - + Iniciar Stop - + Detener Select File - + Elegir archivo Frameskip - + Salto de cuadros Frame delay (ms) - + Retraso entre cuadros (ms) Automatic - + Automático @@ -212,92 +213,92 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. I/O Viewer - + Visor de E/S 0x0000 - + 0x0000 2 - + 2 5 - + 5 4 - + 4 7 - + 7 0 - + 0 9 - + 9 1 - + 1 3 - + 3 8 - + 8 C - + C E - + E 6 - + 6 D - + D F - + F A - + A B - + B @@ -305,7 +306,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Library - + Biblioteca @@ -314,7 +315,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. %1 State - + %1 captura de estado @@ -327,52 +328,52 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. No Save - + Sin captura 1 - + 1 2 - + 2 3 - + 3 4 - + 4 5 - + 5 6 - + 6 7 - + 7 8 - + 8 9 - + 9 @@ -380,57 +381,57 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Logs - + Registros Enabled Levels - + Niveles habilitados Debug - + Depuración Stub - + Auxiliar Info - + Información Warning - + Advertencia Error - + Error Fatal - + Fatal Game Error - + Error del juego Clear - + Limpiar Max Lines - + Máximo de líneas @@ -438,77 +439,77 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Memory - + Memoría Inspect Address: - + Inspeccionar dirección: 0x - + 0x Set Alignment: - + Alinear en: 1 Byte - + 1 byte 2 Bytes - + 2 bytes 4 Bytes - + 4 bytes Signed Integer: - + Entero con signo: String: - + Cadena: Load TBL - + Cargar TBL Copy Selection - + Copiar selección Paste - + Pegar Save Selection - + Guardar selección Load - + Cargar Unsigned Integer: - + Entero sin signo: @@ -516,140 +517,145 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Sprites - + Sprites - + × - + × Magnification + Magnificación + + + + Export - + Attributes - + Atributos - + Transform - + Transformación - + Off - + No - + Palette - + Paleta - - - - + + + + 0 - + 0 - + Double Size - + Doble tamaño - - - - + + + + Return, Ctrl+R - + Volver, Ctrl+R - + Flipped - + Volteo - + H - + H - + V - + V - + Mode - + Modo - + Normal - + Normal - + Mosaic - + Mosaico - + Enabled - + Habilitado - + Priority - - - - - Tile - + Prioridad + Tile + Tile + + + Geometry - + Geometría - + Position - + Posición - + , - + , - + Dimensions - + Dimensiones - - + + 8 - - - - - Address - + 8 + Address + Dirección + + + 0x07000000 - + 0x07000000 @@ -657,12 +663,12 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Game Overrides - + Valores específicos por juego Game Boy Advance - + Game Boy Advance @@ -670,143 +676,143 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Autodetect - + Detección automática Realtime clock - + Reloj de tiempo real Gyroscope - + Giroscopio Tilt - + Inclinación Light sensor - + Sensor de luz Rumble - + Vibración Save type - + Tipo de guardado None - + Ninguno SRAM - + SRAM Flash 512kb - + Flash 512kb Flash 1Mb - + Flash 1Mb EEPROM - + EEPROM Idle loop - + Bucle inactivo Game Boy Player features - + Habilitar Game Boy Player Game Boy - + Game Boy Game Boy model - + Modelo de Game Boy Game Boy (DMG) - + Game Boy (DMG) Game Boy Color (CGB) - + Game Boy Color (CGB) Game Boy Advance (AGB) - + Game Boy Advance (AGB) Memory bank controller - + Controlador de banco de memoria MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + Reloj MBC5 - + MBC5 MBC5 + Rumble - + MBC5 + Vibración MBC7 - + MBC7 HuC-3 - + HuC-3 @@ -814,84 +820,84 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Palette - + Paleta Background - + Fondo (BG) Objects - + Objetos (OBJ) Selection - + Selección Red - + Rojo Green - + Verde Blue - + Azul 0x00 (00) - + 0x00 (00) 16-bit value - + Valor en 16 bits Hex code - + Código hexadecimal Palette index - + Índice en paleta 0x0000 - + 0x0000 #000000 - + #000000 000 - + 000 Export BG - + Exportar BG Export OBJ - + Exportar OBJ @@ -899,14 +905,14 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. %0%1%2 - + %0%1%2 0x%0 (%1) - + 0x%0 (%1) @@ -914,21 +920,42 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. (untitled) - + (sin título) Failed to open cheats file: %1 - + Ocurrió un error al cargar el archivo de trucos: %1 QGBA::CheatsView + + + + Add GameShark + Agregar GameShark + + + + Add Pro Action Replay + Agregar Pro Action Replay + + + + Add CodeBreaker + Agregar CodeBreaker + + + + Add GameGenie + Agregar GameGenie + Select cheats file - + Elegir archivo de trucos @@ -936,22 +963,22 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Clear Button - + Limpiar botones Clear Analog - + Limpiar análogos Refresh - + Actualizar Set all - + Configurar todo @@ -959,42 +986,42 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Server settings - + Ajustes del servidor Local port - + Puerto local Bind address - + Dirección de enlace Break - + Entrar en depuración Stop - + Detener Start - + Iniciar Crash - + Error Could not start GDB server - + No se pudo iniciar el servidor GDB @@ -1002,17 +1029,17 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Failed to open output GIF file: %1 - + Error al abrir el archivo de salida GIF: %1 Select output file - + Elegir archivo de salida Graphics Interchange Format (*.gif) - + Formato de intercambio de gráficos (*.gif) @@ -1021,27 +1048,27 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Failed to open game file: %1 - + Error al abrir el archivo del juego: %1 Failed to open save file: %1 - + Error al abrir el archivo de guardado: %1 Failed to open snapshot file for reading: %1 - + Error al leer el archivo de captura: %1 Failed to open snapshot file for writing: %1 - + Error al escribir al archivo de captura: %1 - + Failed to start audio processor - + Error al iniciar el procesador de audio @@ -1049,142 +1076,142 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Background mode - + Modo de fondo (BG) Mode 0: 4 tile layers - + Modo 0: 4 capas de tiles Mode 1: 2 tile layers + 1 rotated/scaled tile layer - + Modo 1: 2 capas de tiles + 1 capa de tiles rotados/escalados Mode 2: 2 rotated/scaled tile layers - + Modo 2: 2 capas de tiles rotados/escalados Mode 3: Full 15-bit bitmap - + Modo 3: mapa de bits de 15 bits Mode 4: Full 8-bit bitmap - + Modo 4: mapa de bits de 8 bits Mode 5: Small 15-bit bitmap - + Modo 5: mapa de bits pequeño de 15 bits CGB Mode - + Modo CGB Frame select - + Selección de cuadros Unlocked HBlank - + HBlank sin bloqueo Linear OBJ tile mapping - + Asignación de tiles OBJ lineal Force blank screen - + Forzar pantalla en blanco Enable background 0 - + Habilitar fondo 0 Enable background 1 - + Habilitar fondo 1 Enable background 2 - + Habilitar fondo 2 Enable background 3 - + Habilitar fondo 3 Enable OBJ - + Habilitar OBJ Enable Window 0 - + Habilitar Window 0 Enable Window 1 - + Habilitar Window 1 Enable OBJ Window - + Habilitar Window OBJ Currently in VBlank - + En VBlank actualmente Currently in HBlank - + En HBlank actualmente Currently in VCounter - + En VCounter actualmente Enable VBlank IRQ generation - + Generar IRQ por cada VBlank Enable HBlank IRQ generation - + Generar IRQ por cada HBlank Enable VCounter IRQ generation - + Generar IRQ por cada VCounter VCounter scanline - + Línea de exploración VCounter Current scanline - + Línea de exploración actual @@ -1192,7 +1219,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Priority - + Prioridad @@ -1200,7 +1227,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Tile data base (* 16kB) - + Base de los tiles (* 16kB) @@ -1208,7 +1235,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Enable mosaic - + Mosaico (pixelar) @@ -1216,7 +1243,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Enable 256-color - + 256 colores @@ -1224,7 +1251,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Tile map base (* 2kB) - + Base de asignación de tiles (* 2kB) @@ -1232,13 +1259,13 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Background dimensions - + Dimensiones del fondo Overflow wraps - + Envolver al desbordar @@ -1246,7 +1273,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Horizontal offset - + Compensación horizontal @@ -1254,7 +1281,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Vertical offset - + Compensación vertical @@ -1270,7 +1297,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Fractional part - + Parte fraccionaria @@ -1282,7 +1309,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Integer part - + Parte entera @@ -1290,7 +1317,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Integer part (bottom) - + Parte entera (inferior) @@ -1298,286 +1325,286 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Integer part (top) - + Parte entera (superior) End x - + Fin x Start x - + Inicio x End y - + Fin y Start y - + Inicio y Window 0 enable BG 0 - + Window 0 BG 0 Window 0 enable BG 1 - + Window 0 BG 1 Window 0 enable BG 2 - + Window 0 BG 2 Window 0 enable BG 3 - + Window 0 BG 3 Window 0 enable OBJ - + Window 0 OBJ Window 0 enable blend - + Window 0 mezcla Window 1 enable BG 0 - + Window 1 BG 0 Window 1 enable BG 1 - + Window 1 BG 1 Window 1 enable BG 2 - + Window 1 BG 2 Window 1 enable BG 3 - + Window 1 BG 3 Window 1 enable OBJ - + Window 1 OBJ Window 1 enable blend - + Window 1 mezcla Outside window enable BG 0 - + Outside window BG 0 Outside window enable BG 1 - + Outside window BG 1 Outside window enable BG 2 - + Outside window BG 2 Outside window enable BG 3 - + Outside window BG 3 Outside window enable OBJ - + Outside window OBJ Outside window enable blend - + Outside window mezcla OBJ window enable BG 0 - + OBJ window BG 0 OBJ window enable BG 1 - + OBJ window BG 1 OBJ window enable BG 2 - + OBJ window BG 2 OBJ window enable BG 3 - + OBJ window BG 3 OBJ window enable OBJ - + OBJ window OBJ OBJ window enable blend - + OBJ window mezcla Background mosaic size vertical - + Tamaño mosaico fondo vertical Background mosaic size horizontal - + Tamaño mosaico fondo horizontal Object mosaic size vertical - + Tamaño mosaico objeto vertical Object mosaic size horizontal - + Tamaño mosaico objeto horizontal BG 0 target 1 - + BG 0 target 1 BG 1 target 1 - + BG 1 target 1 BG 2 target 1 - + BG 2 target 1 BG 3 target 1 - + BG 3 target 1 OBJ target 1 - + OBJ target 1 Backdrop target 1 - + Backdrop target 1 Blend mode - + Modo mezcla Disabled - + Desactivado Additive blending - + Mezcla aditiva Brighten - + Aclarar Darken - + Oscurecer BG 0 target 2 - + BG 0 target 2 BG 1 target 2 - + BG 1 target 2 BG 2 target 2 - + BG 2 target 2 BG 3 target 2 - + BG 3 target 2 OBJ target 2 - + OBJ target 2 Backdrop target 2 - + Backdrop target 2 Blend A (target 1) - + Mezcla A (target 1) Blend B (target 2) - + Mezcla B (target 2) Blend Y - + Mezcla Y Sweep shifts - + Cambio en barrido Sweep subtract - + Sustracción en barrido Sweep time (in 1/128s) - + Tiempo de barrido (en 1/128seg) @@ -1585,41 +1612,41 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Sound length - + Longitud del sonido Duty cycle - + Ciclo de trabajo Envelope step time - + Tiempo paso envolvente Envelope increase - + Aumento envolvente Initial volume - + Volumen inicial Sound frequency - + Frecuencia del sonido @@ -1627,7 +1654,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Timed - + Cronometrado @@ -1635,50 +1662,50 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Reset - + Reinicializar Double-size wave table - + Tabla de ondas de doble tamaño Active wave table - + Tabla de ondas activa Enable channel 3 - + Canal 3 activo Volume - + Volumen 0% - + 0% 100% - + 100% 50% - + 50% 25% - + 25% @@ -1686,118 +1713,118 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 75% - + 75% Clock divider - + Divisor de reloj Register stages - + Etapas de registros 15 - + 15 7 - + 7 Shifter frequency - + Frecuencia de cambio PSG volume right - + Volumen pulso derecha PSG volume left - + Volumen pulso izquierda Enable channel 1 right - + Canal 1 derecha Enable channel 2 right - + Canal 2 derecha Enable channel 3 right - + Canal 3 derecha Enable channel 4 right - + Canal 4 derecha Enable channel 1 left - + Canal 1 izquierda Enable channel 2 left - + Canal 2 izquierda Enable channel 3 left - + Canal 3 izquierda Enable channel 4 left - + Canal 4 izquierda PSG master volume - + Volumen maestro pulso Loud channel A - + Canal A fuerte Loud channel B - + Canal B fuerte Enable channel A right - + Canal A derecha Enable channel A left - + Canal A izquierda Channel A timer - + Temporizador canal A 0 - + 0 @@ -1810,67 +1837,67 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 1 - + 1 Channel A reset - + Reinic. canal A Enable channel B right - + Canal B derecha Enable channel B left - + Canal B izquierda Channel B timer - + Temporizador canal B Channel B reset - + Reinic. canal B Active channel 1 - + Canal 1 activo Active channel 2 - + Canal 2 activo Active channel 3 - + Canal 3 activo Active channel 4 - + Canal 4 activo Enable audio - + Habilitar audio Bias - + Polarización Resolution - + Resolución @@ -1914,7 +1941,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Sample - + Muestra @@ -1926,7 +1953,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Address (bottom) - + Dirección (inferior) @@ -1938,7 +1965,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Address (top) - + Dirección (superior) @@ -1946,7 +1973,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Word count - + Contador de word @@ -1954,7 +1981,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Destination offset - + Compensación de destino @@ -1966,7 +1993,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Increment - + Incremento @@ -1978,7 +2005,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Decrement - + Decremento @@ -1990,7 +2017,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Fixed - + Fijo @@ -1998,7 +2025,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Increment and reload - + Incremento y recarga @@ -2006,7 +2033,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Source offset - + Compensación de origen @@ -2014,7 +2041,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Repeat - + Repetir @@ -2022,7 +2049,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 32-bit - + 32 bits @@ -2030,7 +2057,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Start timing - + Inicio de temporizador @@ -2038,7 +2065,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Immediate - + Inmediato @@ -2048,7 +2075,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. VBlank - + VBlank @@ -2058,7 +2085,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. HBlank - + HBlank @@ -2071,7 +2098,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. IRQ - + IRQ @@ -2083,24 +2110,24 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Enable - + Habilitar Audio FIFO - + FIFO de audio Video Capture - + Captura de video DRQ - + DRQ @@ -2108,7 +2135,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Value - + Valor @@ -2116,7 +2143,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Scale - + Escala @@ -2124,7 +2151,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 1/64 - + 1/64 @@ -2132,7 +2159,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 1/256 - + 1/256 @@ -2140,176 +2167,176 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 1/1024 - + 1/1024 Cascade - + Cacada A - + A B - + B Select - + Select Start - + Start Right - + Derecha Left - + Izquierda Up - + Arriba Down - + Abajo R - + R L - + L Condition - + Condición SC - + SC SD - + SD SI - + SI SO - + SO VCounter - + VCounter Timer 0 - + Timer 0 Timer 1 - + Timer 1 Timer 2 - + Timer 2 Timer 3 - + Timer 3 SIO - + SIO DMA 0 - + DMA 0 DMA 1 - + DMA 1 DMA 2 - + DMA 2 DMA 3 - + DMA 3 Keypad - + Teclera Gamepak - + Gamepak SRAM wait - + Espera SRAM @@ -2318,7 +2345,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 4 - + 4 @@ -2326,7 +2353,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 3 - + 3 @@ -2335,7 +2362,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 2 - + 2 @@ -2344,72 +2371,72 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. 8 - + 8 Cart 0 non-sequential - + Cart 0 no secuencial Cart 0 sequential - + Cart 0 secuencial Cart 1 non-sequential - + Cart 1 no secuencial Cart 1 sequential - + Cart 1 secuencial Cart 2 non-sequential - + Cart 2 no secuencial Cart 2 sequential - + Cart 2 secuencial PHI terminal - + PHI terminal Disable - + Desactivar 4.19MHz - + 4.19MHz 8.38MHz - + 8.38MHz 16.78MHz - + 16.78MHz Gamepak prefetch - + Gamepak prefetch Enable IRQs - + Habilitar IRQs @@ -2418,7 +2445,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. --- - + --- @@ -2426,47 +2453,47 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Name - + Nombre Filename - + Nombre de archivo Size - + Tamaño Platform - + Plataforma GBA - + GBA GB - + GB ? - + ? Location - + Ubicación CRC32 - + CRC32 @@ -2474,27 +2501,27 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Load State - + Cargar captura Save State - + Guardar captura Empty - + Vacío Corrupted - + Corrompido Slot %1 - + Espacio %1 @@ -2502,37 +2529,37 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. DEBUG - + DEPURACIÓN STUB - + AUXILIAR INFO - + INFORMACIÓN WARN - + ADVERTENCIA ERROR - + ERROR FATAL - + FATAL GAME ERROR - + ERROR DEL JUEGO @@ -2540,102 +2567,117 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Copy selection - + Copiar selección Save selection - + Guardar selección Paste - + Pegar Load - + Cargar All - + Todo Load TBL - + Cargar TBL Save selected memory - + Guardar memoria seleccionada Failed to open output file: %1 - + Error al abrir el archivo de salida: %1 Load memory - + Cargar memoria Failed to open input file: %1 - + Error al abrir el archivo de entrada: %1 TBL - + TBL ISO-8859-1 - + ISO-8859-1 QGBA::ObjView - - + + 0x%0 - + 0x%0 - + Off - - - - - Normal - - - - - Trans - + No - OBJWIN - + Normal + Normal + Trans + Trans + + + + OBJWIN + OBJWIN + + + Invalid + Inválido + + + + + N/A + n/d + + + + Export sprite - - - N/A + + Portable Network Graphics (*.png) + + + + + Failed to open output PNG file: %1 @@ -2644,39 +2686,39 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. #%0 - + #%0 0x%0 - + 0x%0 %0 - + %0 0x%0 (%1) - + 0x%0 (%1) Export palette - + Exportar paleta Windows PAL (*.pal);;Adobe Color Table (*.act) - + Paleta de WIndows (*.pal);;Tabla de colores Adobe (*.act) Failed to open output palette file: %1 - + Error al abrir el archivo de salida de paleta: %1 @@ -2688,18 +2730,18 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. (unknown) - + (desconocido) bytes - + bytes (no database present) - + (no se encuentra la base de datos) @@ -2707,32 +2749,47 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Qt Multimedia - + Qt Multimedia SDL - + SDL Software (Qt) - + Software (Qt) OpenGL - + OpenGL OpenGL (force version 1.x) - + OpenGL (forzar versión 1.x) + + + + Keyboard + Teclado + + + + Controllers + Mandos + + + + Shortcuts + Accesos directos Select BIOS - + Elegir BIOS @@ -2740,37 +2797,37 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. No shader active - + No hay programa shader activo Load shader - + Cargar programa shader %1 Shader (%.shader) - + Programa shader de %1 (%.shader) No shader loaded - + No hay programa shader cargado by %1 - + por %1 Preprocessing - + preprocesamiento Pass %1 - + Paso %1 @@ -2778,17 +2835,17 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Action - + Acción Keyboard - + Teclado Gamepad - + Mando @@ -2796,17 +2853,17 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Failed to open output video file: %1 - + Error al abrir el archivo de salida de video: %1 Native (%0x%1) - + Nativo (%0x%1) Select output file - + Elegir archivo de salida @@ -2814,700 +2871,706 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Game Boy Advance ROMs (%1) - + ROMs de Game Boy Advance (%1) Game Boy ROMs (%1) - + ROMs de Game Boy (%1) All ROMs (%1) - + Todas las ROMs (%1) Archives (%1) - + Archivos (%1) Select ROM - + Elegir ROM Game Boy Advance save files (%1) - + Archivos de guardado de Game Boy Advance (%1) Select save - + Elegir guardado Select patch - + Elegir parche Patches (*.ips *.ups *.bps) - + Parches (*.ips *.ups *.bps) GameShark saves (*.sps *.xps) - + Guardados de GameShark (*.sps *.xps) Crash - + Error fatal The game has crashed with the following error: %1 - + El juego dejó de funcionar inesperadamente debido a este error: + +%1 Couldn't Load - + No se pudo cargar Could not load game. Are you sure it's in the correct format? - + No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto? Unimplemented BIOS call - + Llamada a BIOS no implementada This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Este juego hizo una llamada al BIOS que no está implementada. Usa el BIOS oficial para obtener la mejor experiencia. Really make portable? - + ¿Realmente hacer "portable"? This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Esto hará que el emulador cargue su configuración desde el mismo directorio que su ejecutable. ¿Quieres continuar? Restart needed - + Reinicio necesario Some changes will not take effect until the emulator is restarted. - + Algunos cambios no tendrán efecto hasta que se reinicie el emulador. - Player %1 of %2 - + - Jugador %1 de %2 %1 - %2 - + %1 - %2 %1 - %2 - %3 - + %1 - %2 - %3 %1 - %2 (%3 fps) - %4 - + %1 - %2 (%3 fps) - %4 &File - + &Archivo Load &ROM... - + Cargar &ROM... Load ROM in archive... - + Cargar ROM dentro de archivo... Load temporary save... - + Cargar guardado temporal... Load &patch... - + Cargar &parche... Boot BIOS - + Iniciar al BIOS Replace ROM... - + Reemplazar ROM... ROM &info... - + &Información de ROM... Recent - + Reciente Make portable - + Hacer "portable" &Load state - + Cargar captura de estado (&L) F10 - + F10 &Save state - + Guardar captura de e&stado Shift+F10 - + DO NOT TRANSLATE + Shift+F10 Quick load - + Cargado rápido Quick save - + Guardado rápido Load recent - + Cargar reciente Save recent - + Guardar reciente Undo load state - + Deshacer cargar la captura de estado F11 - + DO NOT TRANSLATE + F11 Undo save state - + Deshacer guardar la captura de estado Shift+F11 - + DO NOT TRANSLATE + Shift+F11 State &%1 - + Captura de estado &%1 F%1 - + F%1 Shift+F%1 - + DO NOT TRANSLATE + Shift+F%1 Import GameShark Save - + Importar guardado de GameShark Export GameShark Save - + Exportar guardado de GameShark New multiplayer window - + Nueva ventana multijugador About - + Acerca de E&xit - + Salir (&X) &Emulation - + &Emulación &Reset - + &Reinicializar Ctrl+R - + Ctrl+R Sh&utdown - + Apagar (&U) Yank game pak - + Arrancar el game pak de su ranura &Pause - + &Pausar Ctrl+P - + Ctrl+P &Next frame - + Saltar al próximo cuadro (&N) Ctrl+N - + Ctrl+N Fast forward (held) - + Avance rápido (mantener) &Fast forward - + Avance rápido (&F) Shift+Tab - + Shift+Tab Fast forward speed - + Velocidad de avance rápido Unbounded - + Ilimitado %0x - + %0x Rewind (held) - + Retroceder (mantener) Re&wind - + Retroceder (&W) ~ - + ~ Step backwards - + Paso hacia atrás Ctrl+B - + Ctrl+B Sync to &video - + Sincronizar a &video Sync to &audio - + Sincronizar a &audio Solar sensor - + Sensor solar Increase solar level - + Aumentar nivel solar Decrease solar level - + Disminuir nivel solar Brightest solar level - + Nivel solar más brillante Darkest solar level - + Nivel solar más oscuro Brightness %1 - + Brillo %1 Audio/&Video - + Audio/&Video Frame size - + Tamaño del cuadro %1x - + %1x Toggle fullscreen - + Pantalla completa Lock aspect ratio - - - - - Resample video - + Bloquear relación de aspecto Frame&skip - + &Salto de cuadros Shader options... - + Opciones del programa shader... Mute - + Silenciar FPS target - + Objetivo de FPS 15 - + 15 30 - + 30 45 - + 45 Native (59.7) - + Nativo (59.7) 60 - + 60 90 - + 90 120 - + 120 240 - + 240 Take &screenshot - + Tomar pantallazo (&S) F12 - + F12 Record output... - + Grabar salida... Record GIF... - + Grabar GIF... Video layers - + Capas de video Background %0 - + Fondo %0 OBJ (sprites) - + OBJ (sprites) Audio channels - + Canales de audio Channel %0 - + Canal %0 Channel A - + Canal A Channel B - + Canal B &Tools - + Herramien&tas View &logs... - + Ver registros... (&L) Game &overrides... - + Val&ores específicos por juego... Game &Pak sensors... - + Sensores en el Game &Pak... &Cheats... - + Tru&cos... Open debugger console... - + Abrir la consola de depuración... Start &GDB server... - + Iniciar servidor &GDB... Settings... - + Ajustes... Select folder - + Elegir carpeta Add folder to library... + Agregar carpeta a la biblioteca... + + + + Bilinear filtering View &palette... - + Ver &paleta... View &sprites... - + Ver &sprites... View &tiles... - + Ver &tiles... View memory... - + Ver memoria... View &I/O registers... - - - - - Exit fullscreen - + Ver reg&istros E/S... + Exit fullscreen + Salir de pantalla completa + + + Autofire - + Botones turbo - + Autofire A - + Turbo A - + Autofire B - + Turbo B - + Autofire L - + Turbo L - + Autofire R - + Turbo R - + Autofire Start - + Turbo Start - + Autofire Select - + Turbo Select - + Autofire Up - + Turbo Arriba - + Autofire Right - + Turbo Derecha - + Autofire Down - + Turbo Abajo - + Autofire Left - + Turbo Izquierda @@ -3515,57 +3578,57 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. ROM Info - + Información de la ROM Game name: - + Nombre del juego: {NAME} - + {NAME} Internal name: - + Nombre interno: {TITLE} - + {TITLE} Game ID: - + ID del juego: {ID} - + {ID} File size: - + Tamaño del archivo: {SIZE} - + {SIZE} CRC32: - + CRC32: {CRC} - + {CRC} @@ -3573,74 +3636,74 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Sensors - + Sensores Realtime clock - + Reloj en tiempo real Fixed time - + Hora fija System time - + Hora del sistema Start time at - + Contar desde el Now - + Ahora MM/dd/yy hh:mm:ss AP - + dd/MM/yy HH:mm:ss Light sensor - + Sensor de luz Brightness - + Brillo Tilt sensor - + Sensor de inclinación Set Y - + Config. Y Set X - + Config. X Gyroscope - + Giroscopio Sensitivity - + Sensibilidad @@ -3648,347 +3711,352 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Settings - + Ajustes Audio/Video - + Audio/Video Interface - + Interfaz Emulation - + Emulación Paths - + Rutas Audio driver: - + Controlador de audio: Audio buffer: - + Búfer de audio: 1536 - + 1536 512 - + 512 768 - + 768 1024 - + 1024 2048 - + 2048 3072 - + 3072 4096 - + 4096 samples - + muestras Sample rate: - + Tasa de muestreo: 44100 - + 44100 22050 - + 22050 32000 - + 32000 48000 - + 48000 Hz - + Hz Volume: - + Volumen: Mute - + Silenciar Display driver: - + Controlador de video: Frameskip: - + Salto de cuadros: Skip every - + Saltar cada frames - + cuadros FPS target: - + Objetivo de FPS: frames per second - + cuadros por segundo Sync: - + SIncronizar a: Video - + Video Audio - + Audio Lock aspect ratio - - - - - Resample video - + Bloquear relación de aspecto Library: - + Biblioteca: Show when no game open - + Mostrar al no haber juego abierto Clear cache - + Limpiar caché Fast forward speed: - + Velocidad de avance rápido: - - - - - - - + + + + + + + Browse - - - - - Use BIOS file if found - + Examinar + Use BIOS file if found + Usar archivo BIOS si hay + + + Skip BIOS intro - + Saltar pantalla de inicio de la BIOS × - + × Unbounded - + Ilimitado Suspend screensaver - + No permitir protector de pantalla BIOS - + BIOS Pause when inactive - + Pausar al estar inactivo Run all - + Ejecutar todos Remove known - + Eliminar los conocidos Detect and remove - + Detectar y eliminar Allow opposing input directions - + Permitir direcciones opuestas Screenshot - + Pantallazo Save data - + Datos de guardado Cheat codes - + Trucos Enable rewind + Habilitar retroceso + + + + Bilinear filtering Rewind history: - + Historial de retroceso: Idle loops: - + Bucles inactivos: Savestate extra data: - + Datos extras en capturas de estado: Load extra data: + Cargar datos extra: + + + + Rewind affects save data - + GB BIOS file: - + Archivo de BIOS GB: - + GBA BIOS file: - + Archivo de BIOS GBA: - + GBC BIOS file: - + Archivo de BIOS GBC: - + Save games - + Guardados de juego - - - - + + + + Same directory as the ROM - + Mismo directorio de la ROM - + Save states - + Capturas de estado - + Screenshots - + Pantallazos - + Patches - + Parches @@ -3996,37 +4064,37 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Shaders - + Programas shader Active Shader: - + Programa shader activo: Name - + Nombre Author - + Autor Description - + Descripción Unload Shader - + Cerrar programa shader Load New Shader - + Cargar nuevo p. shader @@ -4034,22 +4102,22 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Edit Shortcuts - + Editar accesos directos de teclado Keyboard - + Teclado Gamepad - + Mando Clear - + Limpiar @@ -4057,22 +4125,22 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Tiles - + Tiles 256 colors - + 256 colores × - + × Magnification - + Magnificación @@ -4080,183 +4148,183 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Record Video - + Grabar video Start - + Iniciar Stop - + Detener Select File - + Elegir archivo Presets - + Ajustes predeterminados High Quality - + Alta calidad YouTube - + YouTube WebM - + WebM Lossless - + Sin pérdidas 1080p - + 1080p 720p - + 720p 480p - + 480p Native - + Nativo Format - + Formato MKV - + MKV AVI - + AVI MP4 - + MP4 PNG - + PNG h.264 - + h.264 VP8 - + Xvid - + Xvid FFV1 - + FFV1 FLAC - + FLAC Opus - + Opus Vorbis - + Vorbis MP3 - + MP3 AAC - + AAC Uncompressed - + Sin comprimir Bitrate (kbps) - + Tasa de bits (kbps) VBR - + VBR ABR - + ABR Dimensions - + Dimensiones : - + : × - + × Lock aspect ratio - + Bloquear relación de aspecto Show advanced - + Mostrar ajustes avanzados diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 87aed5ba5..d5188d32d 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -43,6 +43,7 @@ int main(int argc, char** argv) { .useBios = true, .rewindEnable = true, .rewindBufferCapacity = 600, + .rewindSave = true, .audioBuffers = 1024, .videoSync = false, .audioSync = true, diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index 3028cd4ec..6f9782669 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -14,7 +14,7 @@ #define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) -mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio"); +mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio"); static void _mSDLAudioCallback(void* context, Uint8* data, int len); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 18af3e005..35b383db3 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -25,7 +25,7 @@ #define RUMBLE_PWM 16 #define RUMBLE_STEPS 2 -mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events"); +mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events", "platform.sdl.events"); DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo); diff --git a/src/util/configuration.c b/src/util/configuration.c index 1021c5137..1cf26104c 100644 --- a/src/util/configuration.c +++ b/src/util/configuration.c @@ -18,6 +18,11 @@ struct ConfigurationSectionHandlerData { void* data; }; +struct ConfigurationHandlerData { + void (*handler)(const char* key, const char* value, void* data); + void* data; +}; + static void _tableDeinit(void* table) { TableDeinit(table); free(table); @@ -63,6 +68,11 @@ static void _sectionEnumHandler(const char* key, void* section, void* user) { data->handler(key, data->data); } +static void _enumHandler(const char* key, void* value, void* user) { + struct ConfigurationHandlerData* data = user; + data->handler(key, value, data->data); +} + void ConfigurationInit(struct Configuration* configuration) { HashTableInit(&configuration->sections, 0, _tableDeinit); HashTableInit(&configuration->root, 0, _sectionDeinit); @@ -199,3 +209,14 @@ void ConfigurationEnumerateSections(const struct Configuration* configuration, v struct ConfigurationSectionHandlerData handlerData = { handler, user }; HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData); } + +void ConfigurationEnumerate(const struct Configuration* configuration, const char* section, void (*handler)(const char* key, const char* value, void* user), void* user) { + struct ConfigurationHandlerData handlerData = { handler, user }; + const struct Table* currentSection = &configuration->root; + if (section) { + currentSection = HashTableLookup(&configuration->sections, section); + } + if (currentSection) { + HashTableEnumerate(currentSection, _enumHandler, &handlerData); + } +} diff --git a/src/util/patch-ups.c b/src/util/patch-ups.c index d58ee2216..5f24311ef 100644 --- a/src/util/patch-ups.c +++ b/src/util/patch-ups.c @@ -87,6 +87,9 @@ bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou if (patch->vf->read(patch->vf, &byte, 1) != 1) { return false; } + if (offset >= outSize) { + return false; + } buf[offset] ^= byte; ++offset; if (!byte) { diff --git a/src/util/png-io.c b/src/util/png-io.c index e3e17ff5e..0cb721b83 100644 --- a/src/util/png-io.c +++ b/src/util/png-io.c @@ -51,6 +51,40 @@ png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) { return info; } +png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) { + png_infop info = png_create_info_struct(png); + if (!info) { + return 0; + } + if (setjmp(png_jmpbuf(png))) { + return 0; + } + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + return info; +} + +bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries) { + if (!palette || !entries) { + return false; + } + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_color colors[256]; + png_byte trans[256]; + unsigned i; + for (i = 0; i < entries && i < 256; ++i) { + colors[i].red = palette[i]; + colors[i].green = palette[i] >> 8; + colors[i].blue = palette[i] >> 16; + trans[i] = palette[i] >> 24; + } + png_set_PLTE(png, info, colors, entries); + png_set_tRNS(png, info, trans, entries, NULL); + png_write_info(png, info); + return true; +} + bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) { png_bytep row = malloc(sizeof(png_byte) * width * 3); if (!row) { @@ -94,6 +128,19 @@ bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned s return true; } +bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) { + UNUSED(width); + const png_byte* pixelData = pixels; + if (setjmp(png_jmpbuf(png))) { + return false; + } + unsigned i; + for (i = 0; i < height; ++i) { + png_write_row(png, &pixelData[stride * i]); + } + return true; +} + bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) { char realName[5]; strncpy(realName, name, 4); diff --git a/src/util/string.c b/src/util/string.c index 7e18a8547..a776bc16d 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -48,6 +48,15 @@ bool endswith(const char* restrict s1, const char* restrict end) { return strcmp(&s1[len - endLen], end) == 0; } +bool startswith(const char* restrict s1, const char* restrict start) { + size_t len = strlen(s1); + size_t startLen = strlen(start); + if (len < startLen) { + return false; + } + return strncmp(s1, start, startLen) == 0; +} + uint32_t utf16Char(const uint16_t** unicode, size_t* length) { if (*length < 2) { *length = 0; diff --git a/tools/deploy-mac.py b/tools/deploy-mac.py index 8305a4304..e525e3012 100755 --- a/tools/deploy-mac.py +++ b/tools/deploy-mac.py @@ -96,6 +96,7 @@ def updateMachO(bin, execPath, root): if os.access(newPath, os.F_OK): if verbose: print('Skipping copying {}, already done.'.format(oldPath)) + newPath = None elif os.path.abspath(oldPath) != os.path.abspath(newPath): if verbose: print('Copying {} to {}...'.format(oldPath, newPath)) @@ -111,7 +112,8 @@ def updateMachO(bin, execPath, root): args = [installNameTool] for path, oldExecPath, newExecPath in toUpdate: if path != bin: - updateMachO(path, execPath, root) + if path: + updateMachO(path, execPath, root) if verbose: print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath)) args.extend(['-change', oldExecPath, newExecPath])