Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2017-03-05 18:56:59 -08:00
commit e1e627e496
97 changed files with 6291 additions and 1237 deletions

16
CHANGES
View File

@ -8,6 +8,10 @@ Features:
- Improved memory viewer - Improved memory viewer
- GB: LR35902/GB-Z80 disassembler - GB: LR35902/GB-Z80 disassembler
- Configuration of gamepad hats - 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: Bugfixes:
- LR35902: Fix core never exiting with certain event patterns - LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior - GB Timer: Improve DIV reset behavior
@ -23,6 +27,12 @@ Bugfixes:
- GB MBC: Fix ROM bank overflows getting set to bank 0 - GB MBC: Fix ROM bank overflows getting set to bank 0
- Qt: Fix timing issues on high refresh rate monitors - Qt: Fix timing issues on high refresh rate monitors
- GBA Savedata: Fix savedata unmasking (fixes mgba.io/i/441) - 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: Misc:
- SDL: Remove scancode key input - SDL: Remove scancode key input
- GBA Video: Clean up unused timers - GBA Video: Clean up unused timers
@ -60,6 +70,12 @@ Misc:
- SDL: Automatically map controllers when plugged in - SDL: Automatically map controllers when plugged in
- Qt: Automatically load controller profile when plugged in - Qt: Automatically load controller profile when plugged in
- OpenGL: Add xBR-lv2 shader - 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) 0.5.2: (2016-12-31)
Bugfixes: Bugfixes:

View File

@ -96,10 +96,16 @@ This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies th
#### Windows developer building #### 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} 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: Check out the source code by running this command:
git clone https://github.com/mgba-emu/mgba.git 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" cmake .. -G "MSYS Makefiles"
make 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 ### Dependencies

View File

@ -38,6 +38,7 @@ bool ConfigurationWrite(const struct Configuration*, const char* path);
bool ConfigurationWriteSection(const struct Configuration*, const char* path, const char* section); 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 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 CXX_GUARD_END

View File

@ -22,7 +22,10 @@ enum {
png_structp PNGWriteOpen(struct VFile* source); png_structp PNGWriteOpen(struct VFile* source);
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); 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 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); bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
void PNGWriteClose(png_structp png, png_infop info); void PNGWriteClose(png_structp png, png_infop info);

View File

@ -293,10 +293,11 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
errors[i] = INVALID_SOCKET; errors[i] = INVALID_SOCKET;
} }
} }
++maxFd;
struct timeval tv; struct timeval tv;
tv.tv_sec = timeoutMillis / 1000; tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 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 r = 0;
int w = 0; int w = 0;
int e = 0; int e = 0;

View File

@ -21,6 +21,7 @@ char* strdup(const char* str);
char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len); char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len);
bool endswith(const char* restrict s1, const char* restrict end); 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); size_t toUtf8(uint32_t unichar, char* buffer);
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length); int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length);

View File

@ -27,7 +27,8 @@ CXX_GUARD_START
void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference); \ void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference); \
void NAME ## EnsureCapacity(struct NAME* vector, size_t capacity); \ void NAME ## EnsureCapacity(struct NAME* vector, size_t capacity); \
size_t NAME ## Size(const struct NAME* vector); \ 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) \ #define DEFINE_VECTOR(NAME, TYPE) \
void NAME ## Init(struct NAME* vector, size_t capacity) { \ 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) { \ size_t NAME ## Index(const struct NAME* vector, const TYPE* member) { \
return member - (const TYPE*) vector->vector; \ 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 CXX_GUARD_END

View File

@ -19,6 +19,12 @@ struct mCoreConfig {
char* port; char* port;
}; };
enum mCoreConfigLevel {
mCONFIG_LEVEL_DEFAULT = 0,
mCONFIG_LEVEL_CUSTOM,
mCONFIG_LEVEL_OVERRIDE,
};
struct mCoreOptions { struct mCoreOptions {
char* bios; char* bios;
bool skipBios; bool skipBios;
@ -27,6 +33,7 @@ struct mCoreOptions {
int frameskip; int frameskip;
bool rewindEnable; bool rewindEnable;
int rewindBufferCapacity; int rewindBufferCapacity;
bool rewindSave;
float fpsTarget; float fpsTarget;
size_t audioBuffers; size_t audioBuffers;
unsigned sampleRate; unsigned sampleRate;
@ -89,6 +96,8 @@ void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig*
void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts); void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts);
void mCoreConfigLoadDefaults(struct mCoreConfig* config, const 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* mCoreConfigGetInput(struct mCoreConfig*);
struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig*); struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig*);
const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig*); const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig*);

View File

@ -40,7 +40,6 @@ enum mCoreChecksumType {
CHECKSUM_CRC32, CHECKSUM_CRC32,
}; };
struct mRTCSource;
struct mCoreConfig; struct mCoreConfig;
struct mCoreSync; struct mCoreSync;
struct mStateExtdata; struct mStateExtdata;
@ -59,6 +58,8 @@ struct mCore {
struct mCoreConfig config; struct mCoreConfig config;
struct mCoreOptions opts; struct mCoreOptions opts;
struct mRTCGenericSource rtc;
bool (*init)(struct mCore*); bool (*init)(struct mCore*);
void (*deinit)(struct mCore*); void (*deinit)(struct mCore*);
@ -77,7 +78,8 @@ struct mCore {
void (*setAudioBufferSize)(struct mCore*, size_t samples); void (*setAudioBufferSize)(struct mCore*, size_t samples);
size_t (*getAudioBufferSize)(struct mCore*); 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*); void (*setAVStream)(struct mCore*, struct mAVStream*);
bool (*isROM)(struct VFile* vf); bool (*isROM)(struct VFile* vf);
@ -115,7 +117,6 @@ struct mCore {
void (*getGameTitle)(const struct mCore*, char* title); void (*getGameTitle)(const struct mCore*, char* title);
void (*getGameCode)(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 (*setRotation)(struct mCore*, struct mRotationSource*);
void (*setRumble)(struct mCore*, struct mRumble*); void (*setRumble)(struct mCore*, struct mRumble*);
@ -174,6 +175,8 @@ void mCoreInitConfig(struct mCore* core, const char* port);
void mCoreLoadConfig(struct mCore* core); void mCoreLoadConfig(struct mCore* core);
void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config); void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config);
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -10,7 +10,10 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba-util/vector.h>
struct mCore; struct mCore;
struct mStateExtdataItem;
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
typedef uint16_t color_t; typedef uint16_t color_t;
@ -37,6 +40,8 @@ struct mCoreCallbacks {
void (*coreCrashed)(void* context); void (*coreCrashed)(void* context);
}; };
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
struct mAVStream { struct mAVStream {
void (*videoDimensionsChanged)(struct mAVStream*, unsigned width, unsigned height); void (*videoDimensionsChanged)(struct mAVStream*, unsigned width, unsigned height);
void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride); void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride);
@ -65,12 +70,16 @@ struct mRTCSource {
void (*sample)(struct mRTCSource*); void (*sample)(struct mRTCSource*);
time_t (*unixTime)(struct mRTCSource*); time_t (*unixTime)(struct mRTCSource*);
void (*serialize)(struct mRTCSource*, struct mStateExtdataItem*);
bool (*deserialize)(struct mRTCSource*, const struct mStateExtdataItem*);
}; };
enum mRTCGenericType { enum mRTCGenericType {
RTC_NO_OVERRIDE, RTC_NO_OVERRIDE,
RTC_FIXED, RTC_FIXED,
RTC_FAKE_EPOCH RTC_FAKE_EPOCH,
RTC_CUSTOM_START = 0x1000
}; };
struct mRTCGenericSource { struct mRTCGenericSource {
@ -78,6 +87,13 @@ struct mRTCGenericSource {
struct mCore* p; struct mCore* p;
enum mRTCGenericType override; enum mRTCGenericType override;
int64_t value; 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); void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core);

View File

@ -10,6 +10,8 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba-util/table.h>
enum mLogLevel { enum mLogLevel {
mLOG_FATAL = 0x01, mLOG_FATAL = 0x01,
mLOG_ERROR = 0x02, mLOG_ERROR = 0x02,
@ -22,29 +24,47 @@ enum mLogLevel {
mLOG_ALL = 0x7F mLOG_ALL = 0x7F
}; };
struct Table;
struct mLogFilter {
int defaultLevels;
struct Table categories;
struct Table levels;
};
struct mLogger { struct mLogger {
void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args); void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args);
struct mLogFilter* filter;
}; };
struct mLogger* mLogGetContext(void); struct mLogger* mLogGetContext(void);
void mLogSetDefaultLogger(struct mLogger*); void mLogSetDefaultLogger(struct mLogger*);
int mLogGenerateCategory(const char*); int mLogGenerateCategory(const char*, const char*);
const char* mLogCategoryName(int); 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) ATTRIBUTE_FORMAT(printf, 3, 4)
void mLog(int category, enum mLogLevel level, const char* format, ...); void mLog(int category, enum mLogLevel level, const char* format, ...);
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__) #define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__)
#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); #define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); extern const char* _mLOG_CAT_ ## CATEGORY ## _ID;
#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME) \ #define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \
int _mLOG_CAT_ ## CATEGORY (void) { \ int _mLOG_CAT_ ## CATEGORY (void) { \
static int category = 0; \ static int category = 0; \
if (!category) { \ if (!category) { \
category = mLogGenerateCategory(NAME); \ category = mLogGenerateCategory(NAME, ID); \
} \ } \
return category; \ return category; \
} } \
const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID;
mLOG_DECLARE_CATEGORY(STATUS) mLOG_DECLARE_CATEGORY(STATUS)

View File

@ -19,6 +19,7 @@ struct mCoreRewindContext {
struct mCoreRewindPatches patchMemory; struct mCoreRewindPatches patchMemory;
size_t current; size_t current;
size_t size; size_t size;
int stateFlags;
struct VFile* previousState; struct VFile* previousState;
struct VFile* currentState; struct VFile* currentState;
}; };

View File

@ -15,12 +15,14 @@ enum mStateExtdataTag {
EXTDATA_SCREENSHOT = 1, EXTDATA_SCREENSHOT = 1,
EXTDATA_SAVEDATA = 2, EXTDATA_SAVEDATA = 2,
EXTDATA_CHEATS = 3, EXTDATA_CHEATS = 3,
EXTDATA_RTC = 4,
EXTDATA_MAX EXTDATA_MAX
}; };
#define SAVESTATE_SCREENSHOT 1 #define SAVESTATE_SCREENSHOT 1
#define SAVESTATE_SAVEDATA 2 #define SAVESTATE_SAVEDATA 2
#define SAVESTATE_CHEATS 4 #define SAVESTATE_CHEATS 4
#define SAVESTATE_RTC 8
struct mStateExtdataItem { struct mStateExtdataItem {
int32_t size; int32_t size;

View File

@ -58,7 +58,6 @@ struct mCoreThread {
bool frameWasOn; bool frameWasOn;
struct mThreadLogger logger; struct mThreadLogger logger;
enum mLogLevel logLevel;
ThreadCallback startCallback; ThreadCallback startCallback;
ThreadCallback resetCallback; ThreadCallback resetCallback;
ThreadCallback cleanCallback; ThreadCallback cleanCallback;

View File

@ -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* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId);
const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, 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 CXX_GUARD_END

View File

@ -113,7 +113,7 @@ struct DS {
struct mAVStream* stream; struct mAVStream* stream;
struct mKeyCallback* keyCallback; struct mKeyCallback* keyCallback;
struct mCoreCallbacks* coreCallbacks; struct mCoreCallbacksList coreCallbacks;
struct mTimingEvent divEvent; struct mTimingEvent divEvent;
struct mTimingEvent sqrtEvent; struct mTimingEvent sqrtEvent;

View File

@ -11,6 +11,7 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/core/cpu.h> #include <mgba/core/cpu.h>
#include <mgba/core/interface.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/core/timing.h> #include <mgba/core/timing.h>
@ -46,7 +47,6 @@ enum GBIRQVector {
struct LR35902Core; struct LR35902Core;
struct mCoreSync; struct mCoreSync;
struct mAVStream; struct mAVStream;
struct mCoreCallbacks;
struct GB { struct GB {
struct mCPUComponent d; struct mCPUComponent d;
@ -63,7 +63,7 @@ struct GB {
uint8_t* keySource; uint8_t* keySource;
void* pristineRom; bool isPristine;
size_t pristineRomSize; size_t pristineRomSize;
size_t yankedRomSize; size_t yankedRomSize;
uint32_t romCrc32; uint32_t romCrc32;
@ -76,7 +76,7 @@ struct GB {
int32_t sramDirtAge; int32_t sramDirtAge;
bool sramMaskWriteback; bool sramMaskWriteback;
struct mCoreCallbacks* coreCallbacks; struct mCoreCallbacksList coreCallbacks;
struct mAVStream* stream; struct mAVStream* stream;
bool cpuBlocked; bool cpuBlocked;

View File

@ -17,7 +17,7 @@ mLOG_DECLARE_CATEGORY(GB_MBC);
struct GB; struct GB;
struct GBMemory; struct GBMemory;
void GBMBCInit(struct GB* gb); 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); void GBMBCSwitchSramBank(struct GB* gb, int bank);
struct GBMBCRTCSaveBuffer { struct GBMBCRTCSaveBuffer {

View File

@ -90,7 +90,7 @@ struct GBA {
struct mRumble* rumble; struct mRumble* rumble;
struct GBARRContext* rr; struct GBARRContext* rr;
void* pristineRom; bool isPristine;
size_t pristineRomSize; size_t pristineRomSize;
size_t yankedRomSize; size_t yankedRomSize;
uint32_t romCrc32; uint32_t romCrc32;
@ -100,7 +100,7 @@ struct GBA {
struct mAVStream* stream; struct mAVStream* stream;
struct mKeyCallback* keyCallback; struct mKeyCallback* keyCallback;
struct mStopCallback* stopCallback; struct mStopCallback* stopCallback;
struct mCoreCallbacks* coreCallbacks; struct mCoreCallbacksList coreCallbacks;
enum GBAIdleLoopOptimization idleOptimization; enum GBAIdleLoopOptimization idleOptimization;
uint32_t idleLoop; uint32_t idleLoop;

View File

@ -129,6 +129,7 @@ struct GBAVideoSoftwareRenderer {
unsigned target1Bd; unsigned target1Bd;
unsigned target2Obj; unsigned target2Obj;
unsigned target2Bd; unsigned target2Bd;
bool blendDirty;
enum BlendEffect blendEffect; enum BlendEffect blendEffect;
color_t normalPalette[512]; color_t normalPalette[512];
color_t variantPalette[512]; color_t variantPalette[512];

View File

@ -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

View File

@ -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;
}

View File

@ -13,7 +13,7 @@
const uint32_t M_CHEAT_DEVICE_ID = 0xABADC0DE; 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(mCheatList, struct mCheat);
DEFINE_VECTOR(mCheatSets, struct mCheatSet*); DEFINE_VECTOR(mCheatSets, struct mCheatSet*);

View File

@ -29,6 +29,13 @@
#define SECTION_NAME_MAX 128 #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) { static const char* _lookupValue(const struct mCoreConfig* config, const char* key) {
const char* value; const char* value;
if (config->port) { if (config->port) {
@ -321,6 +328,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
_lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "frameskip", &opts->frameskip);
_lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "volume", &opts->volume);
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
_lookupIntValue(config, "rewindSave", &opts->rewindSave);
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
unsigned audioBuffers; unsigned audioBuffers;
if (_lookupUIntValue(config, "audioBuffers", &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, "frameskip", opts->frameskip);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave);
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate); 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); 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 // These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) { struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) {
return &config->configTable; return &config->configTable;

View File

@ -240,3 +240,8 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
} }
core->loadConfig(core, config); core->loadConfig(core, config);
} }
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
core->rtc.custom = rtc;
core->rtc.override = RTC_CUSTOM_START;
}

View File

@ -6,24 +6,103 @@
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/serialize.h>
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) { static time_t _rtcGenericCallback(struct mRTCSource* source) {
struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source;
switch (rtc->override) { switch (rtc->override) {
case RTC_NO_OVERRIDE:
default: default:
if (rtc->custom->unixTime) {
return rtc->custom->unixTime(rtc->custom);
}
// Fall through
case RTC_NO_OVERRIDE:
return time(0); return time(0);
case RTC_FIXED: case RTC_FIXED:
return rtc->value; return rtc->value / 1000LL;
case RTC_FAKE_EPOCH: 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) { void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) {
rtc->p = core; rtc->p = core;
rtc->override = RTC_NO_OVERRIDE; rtc->override = RTC_NO_OVERRIDE;
rtc->value = 0; rtc->value = 0;
rtc->d.sample = 0; rtc->d.sample = _rtcGenericSample;
rtc->d.unixTime = _rtcGenericCallback; rtc->d.unixTime = _rtcGenericCallback;
rtc->d.serialize = _rtcGenericSerialize;
rtc->d.deserialize = _rtcGenericDeserialize;
} }

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/core/config.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#define MAX_CATEGORY 64 #define MAX_CATEGORY 64
@ -28,20 +29,39 @@ void mLogSetDefaultLogger(struct mLogger* logger) {
static int _category = 0; static int _category = 0;
static const char* _categoryNames[MAX_CATEGORY]; static const char* _categoryNames[MAX_CATEGORY];
static const char* _categoryIds[MAX_CATEGORY];
int mLogGenerateCategory(const char* name) { int mLogGenerateCategory(const char* name, const char* id) {
++_category;
if (_category < MAX_CATEGORY) { if (_category < MAX_CATEGORY) {
_categoryNames[_category] = name; _categoryNames[_category] = name;
_categoryIds[_category] = id;
} }
return _category; ++_category;
return _category - 1;
} }
const char* mLogCategoryName(int category) { const char* mLogCategoryName(int category) {
if (category < MAX_CATEGORY) { if (category < MAX_CATEGORY) {
return _categoryNames[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, ...) { 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_list args;
va_start(args, format); va_start(args, format);
if (context) { 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 { } else {
printf("%s: ", mLogCategoryName(category)); printf("%s: ", mLogCategoryName(category));
vprintf(format, args); vprintf(format, args);
@ -58,4 +80,67 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) {
va_end(args); 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")

View File

@ -6,6 +6,7 @@
#include <mgba/core/rewind.h> #include <mgba/core/rewind.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/serialize.h>
#include <mgba-util/patch/fast.h> #include <mgba-util/patch/fast.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -20,6 +21,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries)
context->previousState = VFileMemChunk(0, 0); context->previousState = VFileMemChunk(0, 0);
context->currentState = VFileMemChunk(0, 0); context->currentState = VFileMemChunk(0, 0);
context->size = 0; context->size = 0;
context->stateFlags = SAVESTATE_SAVEDATA;
} }
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) { void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
@ -41,7 +43,7 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) { if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
context->current = 0; context->current = 0;
} }
mCoreSaveStateNamed(core, nextState, 0); mCoreSaveStateNamed(core, nextState, context->stateFlags);
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
size_t size2 = nextState->size(nextState); size_t size2 = nextState->size(nextState);
size_t size = context->currentState->size(context->currentState); 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); patch->d.applyPatch(&patch->d, current, size, previous, size);
context->currentState->unmap(context->currentState, current, size); context->currentState->unmap(context->currentState, current, size);
context->previousState->unmap(context->previousState, previous, size); context->previousState->unmap(context->previousState, previous, size);
mCoreLoadStateNamed(core, context->previousState, 0); mCoreLoadStateNamed(core, context->previousState, context->stateFlags);
struct VFile* nextState = context->previousState; struct VFile* nextState = context->previousState;
context->previousState = context->currentState; context->previousState = context->currentState;
context->currentState = nextState; context->currentState = nextState;

View File

@ -7,6 +7,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/cheats.h> #include <mgba/core/cheats.h>
#include <mgba/core/interface.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -16,7 +17,7 @@
#include <zlib.h> #include <zlib.h>
#endif #endif
mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate"); mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate", "core.serialize");
struct mBundledState { struct mBundledState {
size_t stateSize; size_t stateSize;
@ -328,6 +329,14 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
mStateExtdataPut(&extdata, EXTDATA_CHEATS, &item); 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 #ifdef USE_PNG
if (!(flags & SAVESTATE_SCREENSHOT)) { if (!(flags & SAVESTATE_SCREENSHOT)) {
#else #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); mStateExtdataDeinit(&extdata);
return success; return success;
} }

View File

@ -6,6 +6,7 @@
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/serialize.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -35,6 +36,8 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
} }
#endif #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) { static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
MutexLock(&threadContext->stateMutex); MutexLock(&threadContext->stateMutex);
threadContext->state = newState; threadContext->state = newState;
@ -142,12 +145,20 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
.coreCrashed = _crashed, .coreCrashed = _crashed,
.context = threadContext .context = threadContext
}; };
core->setCoreCallbacks(core, &callbacks); core->addCoreCallbacks(core, &callbacks);
core->setSync(core, &threadContext->sync); core->setSync(core, &threadContext->sync);
core->reset(core); 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) { if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity); mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
} }
_changeState(threadContext, THREAD_RUNNING, true); _changeState(threadContext, THREAD_RUNNING, true);
@ -221,7 +232,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
if (threadContext->cleanCallback) { if (threadContext->cleanCallback) {
threadContext->cleanCallback(threadContext); threadContext->cleanCallback(threadContext);
} }
core->setCoreCallbacks(core, NULL); core->clearCoreCallbacks(core);
threadContext->logger.d.filter = NULL;
return 0; return 0;
} }
@ -229,7 +242,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
bool mCoreThreadStart(struct mCoreThread* threadContext) { bool mCoreThreadStart(struct mCoreThread* threadContext) {
threadContext->state = THREAD_INITIALIZED; threadContext->state = THREAD_INITIALIZED;
threadContext->logger.p = threadContext; 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) { if (!threadContext->sync.fpsTarget) {
threadContext->sync.fpsTarget = _defaultFPSTarget; 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) { static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
UNUSED(logger); UNUSED(logger);
struct mCoreThread* thread = mCoreThreadGet(); UNUSED(level);
if (thread && !(thread->logLevel & level)) {
return;
}
printf("%s: ", mLogCategoryName(category)); printf("%s: ", mLogCategoryName(category));
vprintf(format, args); vprintf(format, args);
printf("\n"); printf("\n");
@ -554,9 +567,6 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level
struct mLogger* mCoreThreadLogger(void) { struct mLogger* mCoreThreadLogger(void) {
struct mCoreThread* thread = mCoreThreadGet(); struct mCoreThread* thread = mCoreThreadGet();
if (thread) { if (thread) {
if (!thread->logger.d.log) {
thread->logger.d.log = _mCoreLog;
}
return &thread->logger.d; return &thread->logger.d;
} }
return NULL; return NULL;

View File

@ -283,3 +283,27 @@ const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileC
} }
return tile; 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];
}
}

View File

@ -15,7 +15,7 @@
const uint32_t DEBUGGER_ID = 0xDEADBEEF; 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 mDebuggerInit(void* cpu, struct mCPUComponent* component);
static void mDebuggerDeinit(struct mCPUComponent* component); static void mDebuggerDeinit(struct mCPUComponent* component);

View File

@ -7,7 +7,7 @@
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
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 DS7_BIOS_CHECKSUM = 0x1280F0D5;
const uint32_t DS9_BIOS_CHECKSUM = 0x2AB23573; const uint32_t DS9_BIOS_CHECKSUM = 0x2AB23573;

View File

@ -156,9 +156,14 @@ static size_t _DSCoreGetAudioBufferSize(struct mCore* core) {
return 2048; 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; 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) { 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); 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) { static void _DSCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
} }
@ -513,7 +513,8 @@ struct mCore* DSCoreCreate(void) {
core->getAudioChannel = _DSCoreGetAudioChannel; core->getAudioChannel = _DSCoreGetAudioChannel;
core->setAudioBufferSize = _DSCoreSetAudioBufferSize; core->setAudioBufferSize = _DSCoreSetAudioBufferSize;
core->getAudioBufferSize = _DSCoreGetAudioBufferSize; core->getAudioBufferSize = _DSCoreGetAudioBufferSize;
core->setCoreCallbacks = _DSCoreSetCoreCallbacks; core->addCoreCallbacks = _DSCoreAddCoreCallbacks;
core->clearCoreCallbacks = _DSCoreClearCoreCallbacks;
core->setAVStream = _DSCoreSetAVStream; core->setAVStream = _DSCoreSetAVStream;
core->isROM = DSIsROM; core->isROM = DSIsROM;
core->loadROM = _DSCoreLoadROM; core->loadROM = _DSCoreLoadROM;
@ -539,7 +540,6 @@ struct mCore* DSCoreCreate(void) {
core->frequency = _DSCoreFrequency; core->frequency = _DSCoreFrequency;
core->getGameTitle = _DSCoreGetGameTitle; core->getGameTitle = _DSCoreGetGameTitle;
core->getGameCode = _DSCoreGetGameCode; core->getGameCode = _DSCoreGetGameCode;
core->setRTC = _DSCoreSetRTC;
core->setRotation = _DSCoreSetRotation; core->setRotation = _DSCoreSetRotation;
core->setRumble = _DSCoreSetRumble; core->setRumble = _DSCoreSetRumble;
core->busRead8 = _DSCoreBusRead8; core->busRead8 = _DSCoreBusRead8;

View File

@ -18,7 +18,7 @@
#define SLICE_CYCLES 2048 #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_ARM946ES_FREQUENCY = 0x1FF61FE;
const uint32_t DS_ARM7TDMI_FREQUENCY = 0xFFB0FF; const uint32_t DS_ARM7TDMI_FREQUENCY = 0xFFB0FF;
@ -209,7 +209,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->romVf = NULL; ds->romVf = NULL;
DSSlot1SPIInit(ds, NULL); DSSlot1SPIInit(ds, NULL);
ds->stream = NULL;
ds->keyCallback = NULL; ds->keyCallback = NULL;
mCoreCallbacksListInit(&ds->coreCallbacks, 0);
ds->divEvent.name = "DS Hardware Divide"; ds->divEvent.name = "DS Hardware Divide";
ds->divEvent.callback = _divide; ds->divEvent.callback = _divide;
@ -240,6 +242,7 @@ void DSDestroy(struct DS* ds) {
DSGXDeinit(&ds->gx); DSGXDeinit(&ds->gx);
mTimingDeinit(&ds->ds7.timing); mTimingDeinit(&ds->ds7.timing);
mTimingDeinit(&ds->ds9.timing); mTimingDeinit(&ds->ds9.timing);
mCoreCallbacksListDeinit(&ds->coreCallbacks);
} }
void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) { 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) { void DSFrameStarted(struct DS* ds) {
struct mCoreCallbacks* callbacks = ds->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameStarted) { for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) {
callbacks->videoFrameStarted(callbacks->context); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
} }
} }
void DSFrameEnded(struct DS* ds) { void DSFrameEnded(struct DS* ds) {
struct mCoreCallbacks* callbacks = ds->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameEnded) { for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) {
callbacks->videoFrameEnded(callbacks->context); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
} }
if (ds->stream && ds->stream->postVideoFrame) { if (ds->stream && ds->stream->postVideoFrame) {

View File

@ -8,7 +8,7 @@
#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/ds.h>
#include <mgba/internal/ds/io.h> #include <mgba/internal/ds/io.h>
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_FIFO_SIZE 256
#define DS_GX_PIPE_SIZE 4 #define DS_GX_PIPE_SIZE 4

View File

@ -12,7 +12,7 @@
#include <mgba/internal/ds/slot1.h> #include <mgba/internal/ds/slot1.h>
#include <mgba/internal/ds/spi.h> #include <mgba/internal/ds/spi.h>
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) { static void _DSHaltCNT(struct DSCommon* dscore, uint8_t value) {
switch (value >> 6) { switch (value >> 6) {

View File

@ -8,7 +8,7 @@
#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/ds.h>
#include <mgba/internal/ds/io.h> #include <mgba/internal/ds/io.h>
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) { void DSIPCWriteSYNC(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) {
remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0; remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0;

View File

@ -12,7 +12,7 @@
#include <mgba-util/math.h> #include <mgba-util/math.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
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 static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
const uint32_t redzoneInstruction = 0xE7F0DEF0; const uint32_t redzoneInstruction = 0xE7F0DEF0;

View File

@ -11,7 +11,7 @@
#include <mgba-util/math.h> #include <mgba-util/math.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
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 _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate);
static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); static void _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);

View File

@ -8,7 +8,7 @@
#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/ds.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
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 _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);

View File

@ -13,7 +13,7 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video"); mLOG_DEFINE_CATEGORY(DS_VIDEO, "DS Video", "ds.video");
static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer); static void DSVideoDummyRendererInit(struct DSVideoRenderer* renderer);
static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer); static void DSVideoDummyRendererReset(struct DSVideoRenderer* renderer);

View File

@ -25,7 +25,7 @@
#include <sys/time.h> #include <sys/time.h>
mLOG_DECLARE_CATEGORY(GUI_RUNNER); 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_GRANULARITY 120
#define FPS_BUFFER_SIZE 3 #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); mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA);
break; break;
case RUNNER_LOAD_STATE: 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; break;
case RUNNER_SCREENSHOT: case RUNNER_SCREENSHOT:
mCoreTakeScreenshot(runner->core); mCoreTakeScreenshot(runner->core);

View File

@ -953,6 +953,9 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->playingCh4 = !!(*audio->nr52 & 0x0008); audio->playingCh4 = !!(*audio->nr52 & 0x0008);
audio->enable = GBAudioEnableGetEnable(*audio->nr52); 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(flags, 0, flagsIn);
LOAD_32LE(ch1Flags, 0, &state->ch1.envelope); LOAD_32LE(ch1Flags, 0, &state->ch1.envelope);
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags); 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.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags); audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags);
LOAD_32LE(when, 0, &state->ch1.nextEvent); LOAD_32LE(when, 0, &state->ch1.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch1Event);
if (audio->ch1.envelope.dead < 2 && audio->playingCh1) { if (audio->ch1.envelope.dead < 2 && audio->playingCh1) {
mTimingSchedule(audio->timing, &audio->ch1Event, when); 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.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags); audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags);
LOAD_32LE(when, 0, &state->ch2.nextEvent); LOAD_32LE(when, 0, &state->ch2.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch2Event);
if (audio->ch2.envelope.dead < 2 && audio->playingCh2) { if (audio->ch2.envelope.dead < 2 && audio->playingCh2) {
mTimingSchedule(audio->timing, &audio->ch2Event, when); 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)); memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length); LOAD_16LE(audio->ch3.length, 0, &state->ch3.length);
LOAD_32LE(when, 0, &state->ch3.nextEvent); LOAD_32LE(when, 0, &state->ch3.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch3Event);
if (audio->playingCh3) { if (audio->playingCh3) {
mTimingSchedule(audio->timing, &audio->ch3Event, when); mTimingSchedule(audio->timing, &audio->ch3Event, when);
} }
@ -1002,7 +1002,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags); audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr); LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
LOAD_32LE(when, 0, &state->ch4.nextEvent); LOAD_32LE(when, 0, &state->ch4.nextEvent);
mTimingDeschedule(audio->timing, &audio->ch4Event);
if (audio->ch4.envelope.dead < 2 && audio->playingCh4) { if (audio->ch4.envelope.dead < 2 && audio->playingCh4) {
mTimingSchedule(audio->timing, &audio->ch4Event, when); 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); GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
uint32_t when; uint32_t when;
LOAD_32LE(when, 0, &state->audio.nextSample); LOAD_32LE(when, 0, &state->audio.nextSample);
mTimingDeschedule(audio->timing, &audio->sampleEvent);
mTimingSchedule(audio->timing, &audio->sampleEvent, when); mTimingSchedule(audio->timing, &audio->sampleEvent, when);
} }

View File

@ -54,6 +54,8 @@ static bool _GBCoreInit(struct mCore* core) {
memset(gbcore->components, 0, sizeof(gbcore->components)); memset(gbcore->components, 0, sizeof(gbcore->components));
LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components); LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
LR35902Init(cpu); LR35902Init(cpu);
mRTCGenericSourceInit(&core->rtc, core);
gb->memory.rtc = &core->rtc.d;
GBVideoSoftwareRendererCreate(&gbcore->renderer); GBVideoSoftwareRendererCreate(&gbcore->renderer);
gbcore->renderer.outputBuffer = NULL; gbcore->renderer.outputBuffer = NULL;
@ -164,9 +166,14 @@ static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
return gb->audio.samples; 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; 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) { 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); 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) { static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
struct GB* gb = core->board; struct GB* gb = core->board;
gb->memory.rotation = rotation; gb->memory.rotation = rotation;
@ -595,7 +597,8 @@ struct mCore* GBCoreCreate(void) {
core->setAudioBufferSize = _GBCoreSetAudioBufferSize; core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
core->getAudioBufferSize = _GBCoreGetAudioBufferSize; core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
core->setAVStream = _GBCoreSetAVStream; core->setAVStream = _GBCoreSetAVStream;
core->setCoreCallbacks = _GBCoreSetCoreCallbacks; core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
core->isROM = GBIsROM; core->isROM = GBIsROM;
core->loadROM = _GBCoreLoadROM; core->loadROM = _GBCoreLoadROM;
core->loadBIOS = _GBCoreLoadBIOS; core->loadBIOS = _GBCoreLoadBIOS;
@ -621,7 +624,6 @@ struct mCore* GBCoreCreate(void) {
core->frequency = _GBCoreFrequency; core->frequency = _GBCoreFrequency;
core->getGameTitle = _GBCoreGetGameTitle; core->getGameTitle = _GBCoreGetGameTitle;
core->getGameCode = _GBCoreGetGameCode; core->getGameCode = _GBCoreGetGameCode;
core->setRTC = _GBCoreSetRTC;
core->setRotation = _GBCoreSetRotation; core->setRotation = _GBCoreSetRotation;
core->setRumble = _GBCoreSetRumble; core->setRumble = _GBCoreSetRumble;
core->busRead8 = _GBCoreBusRead8; core->busRead8 = _GBCoreBusRead8;

View File

@ -101,7 +101,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; 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) { 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; struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system;
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
} }

View File

@ -30,7 +30,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
#define DMG_2_BIOS_CHECKSUM 0x59C8598E #define DMG_2_BIOS_CHECKSUM 0x59C8598E
#define CGB_BIOS_CHECKSUM 0x41884E46 #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 GBInit(void* cpu, struct mCPUComponent* component);
static void GBDeinit(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->sramVf = NULL;
gb->sramRealVf = NULL; gb->sramRealVf = NULL;
gb->pristineRom = 0; gb->isPristine = false;
gb->pristineRomSize = 0; gb->pristineRomSize = 0;
gb->yankedRomSize = 0; gb->yankedRomSize = 0;
gb->coreCallbacks = NULL; mCoreCallbacksListInit(&gb->coreCallbacks, 0);
gb->stream = NULL; gb->stream = NULL;
mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent); 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->romVf = vf;
gb->pristineRomSize = vf->size(vf); gb->pristineRomSize = vf->size(vf);
vf->seek(vf, 0, SEEK_SET); vf->seek(vf, 0, SEEK_SET);
gb->isPristine = true;
#ifdef _3DS #ifdef _3DS
gb->pristineRom = 0;
if (gb->pristineRomSize <= romBufferSize) { if (gb->pristineRomSize <= romBufferSize) {
gb->pristineRom = romBuffer; gb->memory.rom = romBuffer;
vf->read(vf, romBuffer, gb->pristineRomSize); vf->read(vf, romBuffer, gb->pristineRomSize);
} }
#else #else
gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ); gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ);
#endif #endif
if (!gb->pristineRom) { if (!gb->memory.rom) {
return false; return false;
} }
gb->yankedRomSize = 0; gb->yankedRomSize = 0;
gb->memory.rom = gb->pristineRom;
gb->memory.romBase = gb->memory.rom; gb->memory.romBase = gb->memory.rom;
gb->memory.romSize = gb->pristineRomSize; gb->memory.romSize = gb->pristineRomSize;
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
GBMBCSwitchBank(&gb->memory, gb->memory.currentBank); GBMBCSwitchBank(gb, gb->memory.currentBank);
if (gb->cpu) { if (gb->cpu) {
struct LR35902Core* cpu = gb->cpu; struct LR35902Core* cpu = gb->cpu;
@ -263,26 +262,25 @@ void GBSavedataUnmask(struct GB* gb) {
void GBUnloadROM(struct GB* gb) { void GBUnloadROM(struct GB* gb) {
// TODO: Share with GBAUnloadROM // 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); free(gb->memory.romBase);
} }
if (gb->memory.rom && gb->pristineRom != gb->memory.rom) { if (gb->memory.rom && !gb->isPristine) {
if (gb->yankedRomSize) { if (gb->yankedRomSize) {
gb->yankedRomSize = 0; gb->yankedRomSize = 0;
} }
mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX); mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
gb->memory.rom = gb->pristineRom;
} }
gb->memory.rom = 0;
if (gb->romVf) { if (gb->romVf) {
#ifndef _3DS #ifndef _3DS
gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize); gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
#endif #endif
gb->romVf->close(gb->romVf); gb->romVf->close(gb->romVf);
gb->romVf = 0; gb->romVf = NULL;
} }
gb->pristineRom = 0; gb->memory.rom = NULL;
gb->isPristine = false;
GBSavedataUnmask(gb); GBSavedataUnmask(gb);
GBSramDeinit(gb); GBSramDeinit(gb);
@ -317,14 +315,26 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
if (patchedSize > GB_SIZE_CART_MAX) { if (patchedSize > GB_SIZE_CART_MAX) {
patchedSize = GB_SIZE_CART_MAX; patchedSize = GB_SIZE_CART_MAX;
} }
gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX); void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX);
if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) { if (!patch->applyPatch(patch, gb->memory.rom, gb->pristineRomSize, newRom, patchedSize)) {
mappedMemoryFree(gb->memory.rom, patchedSize); mappedMemoryFree(newRom, GB_SIZE_CART_MAX);
gb->memory.rom = gb->pristineRom;
return; 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->memory.romSize = patchedSize;
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
} }
void GBDestroy(struct GB* gb) { void GBDestroy(struct GB* gb) {
@ -339,6 +349,7 @@ void GBDestroy(struct GB* gb) {
GBAudioDeinit(&gb->audio); GBAudioDeinit(&gb->audio);
GBVideoDeinit(&gb->video); GBVideoDeinit(&gb->video);
GBSIODeinit(&gb->sio); GBSIODeinit(&gb->sio);
mCoreCallbacksListDeinit(&gb->coreCallbacks);
} }
void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) { void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
@ -652,9 +663,6 @@ void GBGetGameTitle(const struct GB* gb, char* out) {
if (gb->memory.rom) { if (gb->memory.rom) {
cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
} }
if (gb->pristineRom) {
cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
}
if (!cart) { if (!cart) {
return; return;
} }
@ -671,9 +679,6 @@ void GBGetGameCode(const struct GB* gb, char* out) {
if (gb->memory.rom) { if (gb->memory.rom) {
cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
} }
if (gb->pristineRom) {
cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
}
if (!cart) { if (!cart) {
return; return;
} }

View File

@ -9,7 +9,7 @@
#include <mgba/internal/gb/sio.h> #include <mgba/internal/gb/sio.h>
#include <mgba/internal/gb/serialize.h> #include <mgba/internal/gb/serialize.h>
mLOG_DEFINE_CATEGORY(GB_IO, "GB I/O"); mLOG_DEFINE_CATEGORY(GB_IO, "GB I/O", "gb.io");
const char* const GBIORegisterNames[] = { const char* const GBIORegisterNames[] = {
[REG_JOYP] = "JOYP", [REG_JOYP] = "JOYP",

View File

@ -6,11 +6,12 @@
#include <mgba/internal/gb/mbc.h> #include <mgba/internal/gb/mbc.h>
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#include <mgba/internal/lr35902/lr35902.h>
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/memory.h> #include <mgba/internal/gb/memory.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
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) { static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) {
UNUSED(gb); 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 _GBMBC7(struct GB*, uint16_t address, uint8_t value);
static void _GBHuC3(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; 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); 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; bank = bankStart / GB_SIZE_CART_BANK0;
if (!bank) { if (!bank) {
++bank; ++bank;
} }
} }
memory->romBank = &memory->rom[bankStart]; gb->memory.romBank = &gb->memory.rom[bankStart];
memory->currentBank = bank; 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) { void GBMBCSwitchSramBank(struct GB* gb, int bank) {
@ -249,12 +253,12 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
if (!bank) { if (!bank) {
++bank; ++bank;
} }
GBMBCSwitchBank(memory, bank | (memory->currentBank & 0x60)); GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60));
break; break;
case 0x2: case 0x2:
bank &= 3; bank &= 3;
if (!memory->mbcState.mbc1.mode) { if (!memory->mbcState.mbc1.mode) {
GBMBCSwitchBank(memory, (bank << 5) | (memory->currentBank & 0x1F)); GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F));
} else { } else {
GBMBCSwitchSramBank(gb, bank); GBMBCSwitchSramBank(gb, bank);
} }
@ -262,7 +266,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
case 0x3: case 0x3:
memory->mbcState.mbc1.mode = value & 1; memory->mbcState.mbc1.mode = value & 1;
if (memory->mbcState.mbc1.mode) { if (memory->mbcState.mbc1.mode) {
GBMBCSwitchBank(memory, memory->currentBank & 0x1F); GBMBCSwitchBank(gb, memory->currentBank & 0x1F);
} else { } else {
GBMBCSwitchSramBank(gb, 0); GBMBCSwitchSramBank(gb, 0);
} }
@ -297,7 +301,7 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
if (!bank) { if (!bank) {
++bank; ++bank;
} }
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
default: default:
// TODO // TODO
@ -328,7 +332,7 @@ void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
if (!bank) { if (!bank) {
++bank; ++bank;
} }
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x2: case 0x2:
if (value < 4) { if (value < 4) {
@ -372,11 +376,11 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
break; break;
case 0x2: case 0x2:
bank = (memory->currentBank & 0x100) | value; bank = (memory->currentBank & 0x100) | value;
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x3: case 0x3:
bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x4: case 0x4:
case 0x5: 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) { void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
int bank = value & 0x7F; int bank = value & 0x7F;
switch (address >> 13) { switch (address >> 13) {
case 0x1: case 0x1:
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x2: case 0x2:
if (value < 0x10) { if (value < 0x10) {
@ -616,7 +619,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
} }
break; break;
case 0x1: case 0x1:
GBMBCSwitchBank(memory, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x2: case 0x2:
GBMBCSwitchSramBank(gb, bank); GBMBCSwitchSramBank(gb, bank);

View File

@ -14,7 +14,7 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory"); mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory");
static void _pristineCow(struct GB* gba); 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->wramCurrentBank = state->memory.wramCurrentBank;
memory->sramCurrentBank = state->memory.sramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank;
GBMBCSwitchBank(memory, memory->currentBank); GBMBCSwitchBank(gb, memory->currentBank);
GBMemorySwitchWramBank(memory, memory->wramCurrentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank);
GBMBCSwitchSramBank(gb, memory->sramCurrentBank); GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
@ -630,12 +630,10 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
uint32_t when; uint32_t when;
LOAD_32LE(when, 0, &state->memory.dmaNext); LOAD_32LE(when, 0, &state->memory.dmaNext);
mTimingDeschedule(&gb->timing, &memory->dmaEvent);
if (memory->dmaRemaining) { if (memory->dmaRemaining) {
mTimingSchedule(&gb->timing, &memory->dmaEvent, when); mTimingSchedule(&gb->timing, &memory->dmaEvent, when);
} }
LOAD_32LE(when, 0, &state->memory.hdmaNext); LOAD_32LE(when, 0, &state->memory.hdmaNext);
mTimingDeschedule(&gb->timing, &memory->hdmaEvent);
if (memory->hdmaRemaining) { if (memory->hdmaRemaining) {
mTimingSchedule(&gb->timing, &memory->hdmaEvent, when); mTimingSchedule(&gb->timing, &memory->hdmaEvent, when);
} }
@ -651,14 +649,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
} }
void _pristineCow(struct GB* gb) { void _pristineCow(struct GB* gb) {
if (gb->memory.rom != gb->pristineRom) { if (!gb->isPristine) {
return; return;
} }
gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX); void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX);
memcpy(gb->memory.rom, gb->pristineRom, gb->memory.romSize); memcpy(newRom, gb->memory.rom, gb->memory.romSize);
memset(((uint8_t*) gb->memory.rom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize); memset(((uint8_t*) newRom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize);
if (gb->pristineRom == gb->memory.romBase) { if (gb->memory.rom == gb->memory.romBase) {
gb->memory.romBase = gb->memory.rom; gb->memory.romBase = newRom;
} }
GBMBCSwitchBank(&gb->memory, gb->memory.currentBank); gb->memory.rom = newRom;
GBMBCSwitchBank(gb, gb->memory.currentBank);
} }

View File

@ -9,7 +9,7 @@
#include <mgba/internal/gb/timer.h> #include <mgba/internal/gb/timer.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/lr35902/lr35902.h>
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_MAGIC = 0x00400000;
const uint32_t GB_SAVESTATE_VERSION = 0x00000001; const uint32_t GB_SAVESTATE_VERSION = 0x00000001;
@ -144,6 +144,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
if (error) { if (error) {
return false; return false;
} }
gb->timing.root = NULL;
gb->cpu->a = state->cpu.a; gb->cpu->a = state->cpu.a;
gb->cpu->f.packed = state->cpu.f; gb->cpu->f.packed = state->cpu.f;
@ -170,14 +171,13 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
uint32_t when; uint32_t when;
LOAD_32LE(when, 0, &state->cpu.eiPending); LOAD_32LE(when, 0, &state->cpu.eiPending);
mTimingDeschedule(&gb->timing, &gb->eiPending);
if (GBSerializedCpuFlagsIsEiPending(flags)) { if (GBSerializedCpuFlagsIsEiPending(flags)) {
mTimingSchedule(&gb->timing, &gb->eiPending, when); mTimingSchedule(&gb->timing, &gb->eiPending, when);
} }
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent); 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; gb->model = state->model;

View File

@ -9,7 +9,7 @@
#include <mgba/internal/gb/io.h> #include <mgba/internal/gb/io.h>
#include <mgba/internal/gb/serialize.h> #include <mgba/internal/gb/serialize.h>
mLOG_DEFINE_CATEGORY(GB_SIO, "GB Serial I/O"); mLOG_DEFINE_CATEGORY(GB_SIO, "GB Serial I/O", "gb.sio");
const int GBSIOCyclesPerTransfer[2] = { const int GBSIOCyclesPerTransfer[2] = {
512, 512,

View File

@ -109,13 +109,11 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s
uint32_t when; uint32_t when;
LOAD_32LE(when, 0, &state->timer.nextEvent); LOAD_32LE(when, 0, &state->timer.nextEvent);
mTimingDeschedule(&timer->p->timing, &timer->event);
mTimingSchedule(&timer->p->timing, &timer->event, when); mTimingSchedule(&timer->p->timing, &timer->event, when);
GBSerializedTimerFlags flags; GBSerializedTimerFlags flags;
LOAD_32LE(flags, 0, &state->timer.flags); LOAD_32LE(flags, 0, &state->timer.flags);
mTimingDeschedule(&timer->p->timing, &timer->irq);
if (GBSerializedTimerFlagsIsIrqPending(flags)) { if (GBSerializedTimerFlagsIsIrqPending(flags)) {
LOAD_32LE(when, 0, &state->timer.nextIRQ); LOAD_32LE(when, 0, &state->timer.nextIRQ);
mTimingSchedule(&timer->p->timing, &timer->irq, when); mTimingSchedule(&timer->p->timing, &timer->irq, when);

View File

@ -129,9 +129,12 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
} }
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
struct mCoreCallbacks* callbacks = video->p->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameEnded) { for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
callbacks->videoFrameEnded(callbacks->context); 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) { 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); video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
} }
struct mCoreCallbacks* callbacks = video->p->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameStarted) { for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
callbacks->videoFrameStarted(callbacks->context); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
} }
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
@ -550,12 +556,10 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
} }
uint32_t when; uint32_t when;
mTimingDeschedule(&video->p->timing, &video->modeEvent);
if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) { if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) {
LOAD_32LE(when, 0, &state->video.nextMode); LOAD_32LE(when, 0, &state->video.nextMode);
mTimingSchedule(&video->p->timing, &video->modeEvent, when); mTimingSchedule(&video->p->timing, &video->modeEvent, when);
} }
mTimingDeschedule(&video->p->timing, &video->frameEvent);
if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) { if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) {
LOAD_32LE(when, 0, &state->video.nextFrame); LOAD_32LE(when, 0, &state->video.nextFrame);
mTimingSchedule(&video->p->timing, &video->frameEvent, when); mTimingSchedule(&video->p->timing, &video->frameEvent, when);

View File

@ -18,7 +18,7 @@
#define blip_add_delta blip_add_delta_fast #define blip_add_delta blip_add_delta_fast
#endif #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_SAMPLES = 2048;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); 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; uint32_t when;
LOAD_32(when, 0, &state->audio.nextSample); LOAD_32(when, 0, &state->audio.nextSample);
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when); mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
} }

View File

@ -14,7 +14,7 @@
const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F; const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F;
const uint32_t GBA_DS_BIOS_CHECKSUM = 0xBAAE1880; 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 _unLz77(struct GBA* gba, int width);
static void _unHuffman(struct GBA* gba); static void _unHuffman(struct GBA* gba);

View File

@ -62,6 +62,8 @@ static bool _GBACoreInit(struct mCore* core) {
memset(gbacore->components, 0, sizeof(gbacore->components)); memset(gbacore->components, 0, sizeof(gbacore->components));
ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components); ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
ARMInit(cpu); ARMInit(cpu);
mRTCGenericSourceInit(&core->rtc, core);
gba->rtcSource = &core->rtc.d;
GBAVideoSoftwareRendererCreate(&gbacore->renderer); GBAVideoSoftwareRendererCreate(&gbacore->renderer);
gbacore->renderer.outputBuffer = NULL; gbacore->renderer.outputBuffer = NULL;
@ -194,9 +196,14 @@ static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
return gba->audio.samples; 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; 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) { static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
@ -331,7 +338,7 @@ static void _GBACoreReset(struct mCore* core) {
#endif #endif
ARMReset(core->cpu); ARMReset(core->cpu);
if (core->opts.skipBios && gba->pristineRom) { if (core->opts.skipBios && gba->isPristine) {
GBASkipBIOS(core->board); GBASkipBIOS(core->board);
} }
} }
@ -415,11 +422,6 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
GBAGetGameCode(core->board, 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) { static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
struct GBA* gba = core->board; struct GBA* gba = core->board;
gba->rotationSource = rotation; gba->rotationSource = rotation;
@ -608,7 +610,8 @@ struct mCore* GBACoreCreate(void) {
core->getAudioChannel = _GBACoreGetAudioChannel; core->getAudioChannel = _GBACoreGetAudioChannel;
core->setAudioBufferSize = _GBACoreSetAudioBufferSize; core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
core->getAudioBufferSize = _GBACoreGetAudioBufferSize; core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
core->setCoreCallbacks = _GBACoreSetCoreCallbacks; core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
core->setAVStream = _GBACoreSetAVStream; core->setAVStream = _GBACoreSetAVStream;
core->isROM = GBAIsROM; core->isROM = GBAIsROM;
core->loadROM = _GBACoreLoadROM; core->loadROM = _GBACoreLoadROM;
@ -635,7 +638,6 @@ struct mCore* GBACoreCreate(void) {
core->frequency = _GBACoreFrequency; core->frequency = _GBACoreFrequency;
core->getGameTitle = _GBACoreGetGameTitle; core->getGameTitle = _GBACoreGetGameTitle;
core->getGameCode = _GBACoreGetGameCode; core->getGameCode = _GBACoreGetGameCode;
core->setRTC = _GBACoreSetRTC;
core->setRotation = _GBACoreSetRotation; core->setRotation = _GBACoreSetRotation;
core->setRumble = _GBACoreSetRumble; core->setRumble = _GBACoreSetRumble;
core->busRead8 = _GBACoreBusRead8; core->busRead8 = _GBACoreBusRead8;

View File

@ -100,7 +100,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; 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 // TODO: Put back rewind
@ -119,5 +119,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
} }

View File

@ -21,8 +21,8 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(GBA, "GBA"); mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba");
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug"); mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000; const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
@ -91,7 +91,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->keyCallback = NULL; gba->keyCallback = NULL;
gba->stopCallback = NULL; gba->stopCallback = NULL;
gba->stopCallback = NULL; gba->stopCallback = NULL;
gba->coreCallbacks = NULL; mCoreCallbacksListInit(&gba->coreCallbacks, 0);
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
@ -104,7 +104,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->performingDMA = false; gba->performingDMA = false;
gba->pristineRom = 0; gba->isPristine = false;
gba->pristineRomSize = 0; gba->pristineRomSize = 0;
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
@ -112,22 +112,22 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
} }
void GBAUnloadROM(struct GBA* gba) { void GBAUnloadROM(struct GBA* gba) {
if (gba->memory.rom && gba->pristineRom != gba->memory.rom) { if (gba->memory.rom && !gba->isPristine) {
if (gba->yankedRomSize) { if (gba->yankedRomSize) {
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
} }
mappedMemoryFree(gba->memory.rom, SIZE_CART0); mappedMemoryFree(gba->memory.rom, SIZE_CART0);
} }
gba->memory.rom = 0;
if (gba->romVf) { if (gba->romVf) {
#ifndef _3DS #ifndef _3DS
gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize); gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
#endif #endif
gba->romVf->close(gba->romVf); 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); GBASavedataDeinit(&gba->memory.savedata);
if (gba->memory.savedata.realVf) { if (gba->memory.savedata.realVf) {
@ -152,6 +152,7 @@ void GBADestroy(struct GBA* gba) {
GBASIODeinit(&gba->sio); GBASIODeinit(&gba->sio);
gba->rr = 0; gba->rr = 0;
mTimingDeinit(&gba->timing); mTimingDeinit(&gba->timing);
mCoreCallbacksListDeinit(&gba->coreCallbacks);
} }
void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) { void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
@ -291,23 +292,23 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
if (gba->pristineRomSize > SIZE_WORKING_RAM) { if (gba->pristineRomSize > SIZE_WORKING_RAM) {
gba->pristineRomSize = SIZE_WORKING_RAM; gba->pristineRomSize = SIZE_WORKING_RAM;
} }
gba->isPristine = true;
#ifdef _3DS #ifdef _3DS
gba->pristineRom = 0;
if (gba->pristineRomSize <= romBufferSize) { if (gba->pristineRomSize <= romBufferSize) {
gba->pristineRom = romBuffer; gba->memory.wram = romBuffer;
vf->read(vf, romBuffer, gba->pristineRomSize); vf->read(vf, romBuffer, gba->pristineRomSize);
} }
#else #else
gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ); gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ);
#endif #endif
if (!gba->pristineRom) { if (!gba->memory.wram) {
mLOG(GBA, WARN, "Couldn't map ROM"); mLOG(GBA, WARN, "Couldn't map ROM");
return false; return false;
} }
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
gba->memory.romSize = 0; gba->memory.romSize = 0;
gba->memory.romMask = 0; gba->memory.romMask = 0;
gba->romCrc32 = doCrc32(gba->pristineRom, gba->pristineRomSize); gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize);
return true; return true;
} }
@ -322,21 +323,20 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
if (gba->pristineRomSize > SIZE_CART0) { if (gba->pristineRomSize > SIZE_CART0) {
gba->pristineRomSize = SIZE_CART0; gba->pristineRomSize = SIZE_CART0;
} }
gba->isPristine = true;
#ifdef _3DS #ifdef _3DS
gba->pristineRom = 0;
if (gba->pristineRomSize <= romBufferSize) { if (gba->pristineRomSize <= romBufferSize) {
gba->pristineRom = romBuffer; gba->memory.rom = romBuffer;
vf->read(vf, romBuffer, gba->pristineRomSize); vf->read(vf, romBuffer, gba->pristineRomSize);
} }
#else #else
gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ); gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ);
#endif #endif
if (!gba->pristineRom) { if (!gba->memory.rom) {
mLOG(GBA, WARN, "Couldn't map ROM"); mLOG(GBA, WARN, "Couldn't map ROM");
return false; return false;
} }
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
gba->memory.rom = gba->pristineRom;
gba->memory.romSize = gba->pristineRomSize; gba->memory.romSize = gba->pristineRomSize;
gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
gba->memory.mirroring = false; gba->memory.mirroring = false;
@ -389,12 +389,21 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
if (!patchedSize || patchedSize > SIZE_CART0) { if (!patchedSize || patchedSize > SIZE_CART0) {
return; return;
} }
gba->memory.rom = anonymousMemoryMap(SIZE_CART0); void* newRom = anonymousMemoryMap(SIZE_CART0);
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) { if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) {
mappedMemoryFree(gba->memory.rom, patchedSize); mappedMemoryFree(newRom, SIZE_CART0);
gba->memory.rom = gba->pristineRom;
return; 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.romSize = patchedSize;
gba->memory.romMask = SIZE_CART0 - 1; gba->memory.romMask = SIZE_CART0 - 1;
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); 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); memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12);
return; return;
} }
if (gba->pristineRom) { if (gba->isPristine && gba->memory.wram) {
memcpy(out, &((struct GBACartridge*) gba->pristineRom)->title, 12); memcpy(out, &((struct GBACartridge*) gba->memory.wram)->title, 12);
return; return;
} }
strncpy(out, "(BIOS)", 12); strncpy(out, "(BIOS)", 12);
@ -567,6 +576,10 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
// TODO: More sensible category? // TODO: More sensible category?
mLOG(GBA, WARN, "Illegal opcode: %08x", opcode); 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 #ifdef USE_DEBUGGERS
if (gba->debugger) { if (gba->debugger) {
struct mDebuggerEntryInfo info = { struct mDebuggerEntryInfo info = {
@ -623,9 +636,12 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
void GBAFrameStarted(struct GBA* gba) { void GBAFrameStarted(struct GBA* gba) {
UNUSED(gba); UNUSED(gba);
struct mCoreCallbacks* callbacks = gba->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameStarted) { for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
callbacks->videoFrameStarted(callbacks->context); 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); GBAHardwarePlayerUpdate(gba);
} }
struct mCoreCallbacks* callbacks = gba->coreCallbacks; size_t c;
if (callbacks && callbacks->videoFrameEnded) { for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
callbacks->videoFrameEnded(callbacks->context); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
} }
} }

View File

@ -11,7 +11,7 @@
#include <mgba-util/formatting.h> #include <mgba-util/formatting.h>
#include <mgba-util/hash.h> #include <mgba-util/hash.h>
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 }; 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); 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); mTimingSchedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent, 2048);
} }
value &= 0x78FB; value &= 0x78FB;

View File

@ -11,7 +11,7 @@
#include <mgba/internal/gba/rr/rr.h> #include <mgba/internal/gba/rr/rr.h>
#include <mgba/internal/gba/serialize.h> #include <mgba/internal/gba/serialize.h>
mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O"); mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O", "gba.io");
const char* const GBAIORegisterNames[] = { const char* const GBAIORegisterNames[] = {
// Video // 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,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
// Audio // Audio
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Audio // Audio
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
// DMA // DMA
@ -960,7 +960,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
gba->timers[i].lastEvent = when + mTimingCurrentTime(&gba->timing); gba->timers[i].lastEvent = when + mTimingCurrentTime(&gba->timing);
} }
LOAD_32(when, 0, &state->timers[i].nextEvent); LOAD_32(when, 0, &state->timers[i].nextEvent);
mTimingDeschedule(&gba->timing, &gba->timers[i].event);
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].event, when); mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
} }

View File

@ -19,7 +19,7 @@
#define IDLE_LOOP_THRESHOLD 10000 #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 void _pristineCow(struct GBA* gba);
static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb 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) { void GBAMemoryReset(struct GBA* gba) {
if (gba->memory.wram) { if (gba->memory.rom || gba->memory.fullBios) {
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); // Not multiboot
} if (gba->memory.wram) {
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
if (gba->pristineRom && !gba->memory.rom) { }
// Multiboot gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
memcpy(gba->memory.wram, gba->pristineRom, gba->pristineRomSize);
} }
if (gba->memory.iwram) { if (gba->memory.iwram) {
@ -300,9 +299,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
cpu->memory.activeMask = 0; cpu->memory.activeMask = 0;
if (gba->yankedRomSize || !gba->hardCrash) { if (gba->yankedRomSize || !gba->hardCrash) {
mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address); 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); 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 { } else {
mLOG(GBA_MEM, FATAL, "Jumped to invalid address: %08X", address); 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) { void _pristineCow(struct GBA* gba) {
if (gba->memory.rom != gba->pristineRom) { if (!gba->isPristine) {
return; return;
} }
gba->memory.rom = anonymousMemoryMap(SIZE_CART0); void* newRom = anonymousMemoryMap(SIZE_CART0);
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize); memcpy(newRom, gba->memory.rom, gba->memory.romSize);
memset(((uint8_t*) gba->memory.rom) + gba->memory.romSize, 0xFF, SIZE_CART0 - 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];
} }

View File

@ -381,7 +381,6 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
SPRITE_NORMAL_LOOP(256, OBJWIN); SPRITE_NORMAL_LOOP(256, OBJWIN);
} else if (mosaicH > 1) { } else if (mosaicH > 1) {
if (objwinSlowPath) { if (objwinSlowPath) {
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN); SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
} else { } else {
SPRITE_MOSAIC_LOOP(256, NORMAL); SPRITE_MOSAIC_LOOP(256, NORMAL);

View File

@ -104,6 +104,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
} }
softwareRenderer->objExtPalette = NULL; softwareRenderer->objExtPalette = NULL;
softwareRenderer->objExtVariantPalette = NULL; softwareRenderer->objExtVariantPalette = NULL;
softwareRenderer->blendDirty = false;
_updatePalettes(softwareRenderer); _updatePalettes(softwareRenderer);
softwareRenderer->blda = 0; softwareRenderer->blda = 0;
@ -270,11 +271,14 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
value &= 0x1F1F; value &= 0x1F1F;
break; break;
case REG_BLDY: case REG_BLDY:
softwareRenderer->bldy = value & 0x1F; value &= 0x1F;
if (softwareRenderer->bldy > 0x10) { if (value > 0x10) {
softwareRenderer->bldy = 0x10; value = 0x10;
}
if (softwareRenderer->bldy != value) {
softwareRenderer->bldy = value;
softwareRenderer->blendDirty = true;
} }
_updatePalettes(softwareRenderer);
break; break;
case REG_WIN0H: case REG_WIN0H:
softwareRenderer->winN[0].h.end = value; softwareRenderer->winN[0].h.end = value;
@ -509,6 +513,10 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y); GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y); int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
softwareRenderer->d.vramOBJ[0] = objVramBase; softwareRenderer->d.vramOBJ[0] = objVramBase;
if (softwareRenderer->blendDirty) {
_updatePalettes(softwareRenderer);
softwareRenderer->blendDirty = false;
}
int w; int w;
unsigned priority; unsigned priority;
@ -688,7 +696,7 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer*
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
if (oldEffect != renderer->blendEffect) { if (oldEffect != renderer->blendEffect) {
_updatePalettes(renderer); renderer->blendDirty = true;
} }
} }

View File

@ -9,7 +9,7 @@
#include <mgba/core/serialize.h> #include <mgba/core/serialize.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR"); mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR", "gba.rr");
void GBARRInitRecord(struct GBA* gba) { void GBARRInitRecord(struct GBA* gba) {
if (!gba || !gba->rr) { if (!gba || !gba->rr) {

View File

@ -26,7 +26,7 @@
#define EEPROM_SETTLE_CYCLES 115000 #define EEPROM_SETTLE_CYCLES 115000
#define CLEANUP_THRESHOLD 15 #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 _flashSwitchBank(struct GBASavedata* savedata, int bank);
static void _flashErase(struct GBASavedata* savedata); static void _flashErase(struct GBASavedata* savedata);

View File

@ -17,7 +17,7 @@
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002; 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 GBABundledState {
struct GBASerializedState* state; struct GBASerializedState* state;
@ -146,7 +146,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (error) { if (error) {
return false; return false;
} }
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles); gba->timing.root = NULL;
size_t i; size_t i;
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs); LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);

View File

@ -8,7 +8,7 @@
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
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] = { const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = {
{ 38326, 73003, 107680, 142356 }, { 38326, 73003, 107680, 142356 },

View File

@ -155,9 +155,6 @@ void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struc
timer->lastEvent = timing->masterCycles + cpu->cycles; timer->lastEvent = timing->masterCycles + cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) { } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
mTimingDeschedule(timing, &timer->event); 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)) { } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
mTimingDeschedule(timing, &timer->event); mTimingDeschedule(timing, &timer->event);
mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent); mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);

View File

@ -15,7 +15,7 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
mLOG_DEFINE_CATEGORY(GBA_VIDEO, "GBA Video"); mLOG_DEFINE_CATEGORY(GBA_VIDEO, "GBA Video", "gba.video");
static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer);
@ -338,7 +338,6 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
} else { } else {
video->event.callback = _startHblank; video->event.callback = _startHblank;
} }
mTimingDeschedule(&video->p->timing, &video->event);
mTimingSchedule(&video->p->timing, &video->event, when); mTimingSchedule(&video->p->timing, &video->event, when);
LOAD_16(video->vcount, REG_VCOUNT, state->io); LOAD_16(video->vcount, REG_VCOUNT, state->io);

View File

@ -52,7 +52,7 @@ DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;)
#define DEFINE_LD_DECODER_LR35902_MEM(NAME, REG) \ #define DEFINE_LD_DECODER_LR35902_MEM(NAME, REG) \
DEFINE_DECODER_LR35902(LD ## NAME ## _ ## REG, info->mnemonic = LR35902_MN_LD; \ 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.reg = LR35902_REG_ ## REG; \
info->op2.flags = LR35902_OP_FLAG_MEMORY;) info->op2.flags = LR35902_OP_FLAG_MEMORY;)

View File

@ -12,7 +12,7 @@
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
mLOG_DECLARE_CATEGORY(OPENGL); mLOG_DECLARE_CATEGORY(OPENGL);
mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL"); mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL", "video.ogl");
#define MAX_PASSES 8 #define MAX_PASSES 8

View File

@ -7,6 +7,8 @@
#include <QTimer> #include <QTimer>
#include <mgba/core/tile-cache.h>
using namespace QGBA; using namespace QGBA;
AssetView::AssetView(GameController* controller, QWidget* parent) AssetView::AssetView(GameController* controller, QWidget* parent)
@ -24,7 +26,7 @@ AssetView::AssetView(GameController* controller, QWidget* parent)
} }
void AssetView::updateTiles(bool force) { void AssetView::updateTiles(bool force) {
if (!m_controller->thread() || !m_controller->thread()->core) { if (!m_controller->isLoaded()) {
return; return;
} }
@ -51,3 +53,47 @@ void AssetView::resizeEvent(QResizeEvent*) {
void AssetView::showEvent(QShowEvent*) { void AssetView::showEvent(QShowEvent*) {
updateTiles(true); 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<uint8_t*>(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;
}
}

View File

@ -18,6 +18,8 @@ Q_OBJECT
public: public:
AssetView(GameController* controller, QWidget* parent = nullptr); 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: protected slots:
void updateTiles(bool force = false); void updateTiles(bool force = false);

View File

@ -46,19 +46,19 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
enterCheat(GBA_CHEAT_AUTODETECT); 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); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() { connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_GAMESHARK); 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); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() { connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_PRO_ACTION_REPLAY); 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); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() { connect(add, &QPushButton::clicked, [this]() {
enterCheat(GBA_CHEAT_CODEBREAKER); enterCheat(GBA_CHEAT_CODEBREAKER);
@ -71,13 +71,13 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
enterCheat(GB_CHEAT_AUTODETECT); 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); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() { connect(add, &QPushButton::clicked, [this]() {
enterCheat(GB_CHEAT_GAMESHARK); 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); m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
connect(add, &QPushButton::clicked, [this]() { connect(add, &QPushButton::clicked, [this]() {
enterCheat(GB_CHEAT_GAME_GENIE); enterCheat(GB_CHEAT_GAME_GENIE);

View File

@ -110,6 +110,7 @@ ConfigController::ConfigController(QObject* parent)
m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL; m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL;
m_opts.rewindEnable = false; m_opts.rewindEnable = false;
m_opts.rewindBufferCapacity = 300; m_opts.rewindBufferCapacity = 300;
m_opts.rewindSave = true;
m_opts.useBios = true; m_opts.useBios = true;
m_opts.suspendScreensaver = true; m_opts.suspendScreensaver = true;
m_opts.lockAspectRatio = true; m_opts.lockAspectRatio = true;

View File

@ -29,11 +29,10 @@ using namespace QGBA;
static GBAApp* g_app = nullptr; static GBAApp* g_app = nullptr;
mLOG_DEFINE_CATEGORY(QT, "Qt"); mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt");
GBAApp::GBAApp(int& argc, char* argv[]) GBAApp::GBAApp(int& argc, char* argv[])
: QApplication(argc, argv) : QApplication(argc, argv)
, m_windows{}
, m_db(nullptr) , m_db(nullptr)
{ {
g_app = this; g_app = this;
@ -80,10 +79,10 @@ GBAApp::GBAApp(int& argc, char* argv[])
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt())); AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
} }
Window* w = new Window(&m_configController); Window* w = new Window(&m_configController);
connect(w, &Window::destroyed, [this]() { connect(w, &Window::destroyed, [this, w]() {
m_windows[0] = nullptr; m_windows.removeAll(w);
}); });
m_windows[0] = w; m_windows.append(w);
if (loaded) { if (loaded) {
w->argumentsPassed(&args); w->argumentsPassed(&args);
@ -121,15 +120,15 @@ bool GBAApp::event(QEvent* event) {
} }
Window* GBAApp::newWindow() { Window* GBAApp::newWindow() {
if (m_multiplayer.attached() >= MAX_GBAS) { if (m_windows.count() >= MAX_GBAS) {
return nullptr; return nullptr;
} }
Window* w = new Window(&m_configController, m_multiplayer.attached()); Window* w = new Window(&m_configController, m_multiplayer.attached());
int windowId = m_multiplayer.attached(); int windowId = m_multiplayer.attached();
connect(w, &Window::destroyed, [this, windowId]() { connect(w, &Window::destroyed, [this, w]() {
m_windows[windowId] = nullptr; m_windows.removeAll(w);
}); });
m_windows[windowId] = w; m_windows.append(w);
w->setAttribute(Qt::WA_DeleteOnClose); w->setAttribute(Qt::WA_DeleteOnClose);
w->loadConfig(); w->loadConfig();
w->show(); w->show();
@ -142,27 +141,27 @@ GBAApp* GBAApp::app() {
return g_app; return g_app;
} }
void GBAApp::pauseAll(QList<int>* paused) { void GBAApp::pauseAll(QList<Window*>* paused) {
for (int i = 0; i < MAX_GBAS; ++i) { for (auto& window : m_windows) {
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded() || m_windows[i]->controller()->isPaused()) { if (!window->controller()->isLoaded() || window->controller()->isPaused()) {
continue; continue;
} }
m_windows[i]->controller()->setPaused(true); window->controller()->setPaused(true);
paused->append(i); paused->append(window);
} }
} }
void GBAApp::continueAll(const QList<int>* paused) { void GBAApp::continueAll(const QList<Window*>& paused) {
for (int i : *paused) { for (auto& window : paused) {
m_windows[i]->controller()->setPaused(false); window->controller()->setPaused(false);
} }
} }
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) { QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
QList<int> paused; QList<Window*> paused;
pauseAll(&paused); pauseAll(&paused);
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter); QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
continueAll(&paused); continueAll(paused);
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); 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) { QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) {
QList<int> paused; QList<Window*> paused;
pauseAll(&paused); pauseAll(&paused);
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter); QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
continueAll(&paused); continueAll(paused);
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); 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) { QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
QList<int> paused; QList<Window*> paused;
pauseAll(&paused); pauseAll(&paused);
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory")); QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory"));
continueAll(&paused); continueAll(paused);
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); 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() { int GBAApp::FileDialog::exec() {
QList<int> paused; QList<Window*> paused;
m_app->pauseAll(&paused); m_app->pauseAll(&paused);
bool didAccept = QFileDialog::exec() == QDialog::Accepted; bool didAccept = QFileDialog::exec() == QDialog::Accepted;
QStringList filenames = selectedFiles(); QStringList filenames = selectedFiles();
if (!filenames.isEmpty()) { if (!filenames.isEmpty()) {
m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path()); m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path());
} }
m_app->continueAll(&paused); m_app->continueAll(paused);
return didAccept; return didAccept;
} }

View File

@ -39,7 +39,6 @@ private:
}; };
#endif #endif
class GBAApp : public QApplication { class GBAApp : public QApplication {
Q_OBJECT Q_OBJECT
@ -78,11 +77,11 @@ private:
Window* newWindowInternal(); Window* newWindowInternal();
void pauseAll(QList<int>* paused); void pauseAll(QList<Window*>* paused);
void continueAll(const QList<int>* paused); void continueAll(const QList<Window*>& paused);
ConfigController m_configController; ConfigController m_configController;
Window* m_windows[MAX_GBAS]; QList<Window*> m_windows;
MultiplayerController m_multiplayer; MultiplayerController m_multiplayer;
NoIntroDB* m_db; NoIntroDB* m_db;

View File

@ -23,7 +23,6 @@
#include <mgba/core/tile-cache.h> #include <mgba/core/tile-cache.h>
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
#include <mgba/gba/interface.h> #include <mgba/gba/interface.h>
#include <mgba/internal/gba/bios.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/gba/core.h> #include <mgba/gba/core.h>
#include <mgba/internal/gba/renderers/tile-cache.h> #include <mgba/internal/gba/renderers/tile-cache.h>
@ -70,8 +69,8 @@ GameController::GameController(QObject* parent)
, m_stateSlot(1) , m_stateSlot(1)
, m_backupLoadState(nullptr) , m_backupLoadState(nullptr)
, m_backupSaveState(nullptr) , m_backupSaveState(nullptr)
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS) , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
, m_loadStateFlags(SAVESTATE_SCREENSHOT) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
, m_override(nullptr) , m_override(nullptr)
{ {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -90,8 +89,6 @@ GameController::GameController(QObject* parent)
m_threadContext.startCallback = [](mCoreThread* context) { m_threadContext.startCallback = [](mCoreThread* context) {
GameController* controller = static_cast<GameController*>(context->userData); GameController* controller = static_cast<GameController*>(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->setRotation(context->core, controller->m_inputController->rotationSource());
context->core->setRumble(context->core, controller->m_inputController->rumble()); 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* savestateMessage = "State %i loaded";
static const char* savestateFailedMessage = "State %i failed to load"; static const char* savestateFailedMessage = "State %i failed to load";
static int biosCat = -1;
static int statusCat = -1;
if (!context) { if (!context) {
return; return;
} }
GameController* controller = static_cast<GameController*>(context->userData); GameController* controller = static_cast<GameController*>(context->userData);
QString message; QString message;
if (biosCat < 0) {
biosCat = mLogCategoryById("gba.bios");
}
if (statusCat < 0) {
statusCat = mLogCategoryById("core.status");
}
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
if (level == mLOG_STUB && category == _mLOG_CAT_GBA_BIOS()) { if (level == mLOG_STUB && category == biosCat) {
va_list argc; va_list argc;
va_copy(argc, args); va_copy(argc, args);
int immediate = va_arg(argc, int); int immediate = va_arg(argc, int);
@ -255,7 +260,7 @@ GameController::GameController(QObject* parent)
QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate)); QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
} else } else
#endif #endif
if (category == _mLOG_CAT_STATUS()) { if (category == statusCat) {
// Slot 0 is reserved for suspend points // Slot 0 is reserved for suspend points
if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
va_list argc; 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) { if (m_gameOpen) {
Interrupter interrupter(this); Interrupter interrupter(this);
if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) { 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.rewindEnable = enable;
m_threadContext.core->opts.rewindBufferCapacity = capacity; m_threadContext.core->opts.rewindBufferCapacity = capacity;
m_threadContext.core->opts.rewindSave = rewindSave;
if (enable && capacity > 0) { if (enable && capacity > 0) {
mCoreRewindContextInit(&m_threadContext.rewind, capacity); 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() { 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) { void GameController::setFixedTime(const QDateTime& time) {
m_rtc.override = RTC_FIXED; if (!isLoaded()) {
m_rtc.value = time.toMSecsSinceEpoch() / 1000; return;
}
m_threadContext.core->rtc.override = RTC_FIXED;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
} }
void GameController::setFakeEpoch(const QDateTime& time) { void GameController::setFakeEpoch(const QDateTime& time) {
m_rtc.override = RTC_FAKE_EPOCH; if (!isLoaded()) {
m_rtc.value = time.toMSecsSinceEpoch() / 1000; return;
}
m_threadContext.core->rtc.override = RTC_FAKE_EPOCH;
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
} }
void GameController::updateKeys() { void GameController::updateKeys() {

View File

@ -127,7 +127,7 @@ public slots:
void setPaused(bool paused); void setPaused(bool paused);
void reset(); void reset();
void frameAdvance(); void frameAdvance();
void setRewind(bool enable, int capacity); void setRewind(bool enable, int capacity, bool rewindSave);
void rewind(int states = 0); void rewind(int states = 0);
void startRewinding(); void startRewinding();
void stopRewinding(); void stopRewinding();
@ -247,8 +247,6 @@ private:
} m_lux; } m_lux;
uint8_t m_luxValue; uint8_t m_luxValue;
int m_luxLevel; int m_luxLevel;
mRTCGenericSource m_rtc;
}; };
} }

View File

@ -89,7 +89,12 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent)
m_library->ref(); m_library->ref();
} else { } else {
m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path); 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 { } else {
m_library = new LibraryHandle(mLibraryCreateEmpty()); m_library = new LibraryHandle(mLibraryCreateEmpty());
@ -280,7 +285,9 @@ LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
LibraryModel::LibraryHandle::~LibraryHandle() { LibraryModel::LibraryHandle::~LibraryHandle() {
m_loaderThread.quit(); m_loaderThread.quit();
m_loaderThread.wait(); m_loaderThread.wait();
mLibraryDestroy(library); if (library) {
mLibraryDestroy(library);
}
} }
void LibraryModel::LibraryHandle::ref() { void LibraryModel::LibraryHandle::ref() {

View File

@ -10,11 +10,17 @@
#include <QFontDatabase> #include <QFontDatabase>
#include <QTimer> #include <QTimer>
#include "LogController.h"
#include "VFileDevice.h"
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#endif
#ifdef M_CORE_GB #ifdef M_CORE_GB
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/io.h> #include <mgba/internal/gb/io.h>
#endif #endif
#include <mgba-util/png-io.h>
using namespace QGBA; using namespace QGBA;
@ -45,6 +51,7 @@ ObjView::ObjView(GameController* controller, QWidget* parent)
connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() { connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
updateTiles(true); updateTiles(true);
}); });
connect(m_ui.exportButton, SIGNAL(clicked()), this, SLOT(exportObj()));
} }
void ObjView::selectObj(int obj) { void ObjView::selectObj(int obj) {
@ -68,64 +75,63 @@ void ObjView::updateTilesGBA(bool force) {
unsigned width = GBAVideoObjSizes[shape * 4 + size][0]; unsigned width = GBAVideoObjSizes[shape * 4 + size][0];
unsigned height = GBAVideoObjSizes[shape * 4 + size][1]; unsigned height = GBAVideoObjSizes[shape * 4 + size][1];
unsigned tile = GBAObjAttributesCGetTile(obj->c); unsigned tile = GBAObjAttributesCGetTile(obj->c);
ObjInfo newInfo{
tile,
width / 8,
height / 8,
width / 8
};
m_ui.tiles->setTileCount(width * height / 64); m_ui.tiles->setTileCount(width * height / 64);
m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value()); m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value()); m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value());
unsigned palette = GBAObjAttributesCGetPalette(obj->c); unsigned palette = GBAObjAttributesCGetPalette(obj->c);
GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues unsigned tileBase = tile;
if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) { unsigned paletteSet;
newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a)); 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) { if (newInfo != m_objInfo) {
force = true; 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; 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; 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.x->setText(QString::number(GBAObjAttributesBGetX(obj->b)));
m_ui.y->setText(QString::number(GBAObjAttributesAGetY(obj->a))); m_ui.y->setText(QString::number(GBAObjAttributesAGetY(obj->a)));
@ -175,20 +181,10 @@ void ObjView::updateTilesGB(bool force) {
height = 16; height = 16;
} }
unsigned tile = obj->tile; 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->setTileCount(width * height / 64);
m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value()); m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
m_ui.tiles->resize(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 (gb->model >= GB_MODEL_CGB) {
if (GBObjAttributesIsBank(obj->attr)) { if (GBObjAttributesIsBank(obj->attr)) {
tile += 512; tile += 512;
@ -197,21 +193,37 @@ void ObjView::updateTilesGB(bool force) {
} else { } else {
palette = GBObjAttributesGetPalette(obj->attr); palette = GBObjAttributesGetPalette(obj->attr);
} }
int i = 0;
m_ui.palette->setText(QString::number(palette)); 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); mTileCacheSetPalette(m_tileCache.get(), 0);
m_ui.tile->setPalette(palette + 8); m_ui.tile->setPalette(palette);
m_ui.tile->setPaletteSet(0, 512, 1024); m_ui.tile->setPaletteSet(0, 512, 1024);
for (int y = 0; y < height / 8; ++y, ++i) { for (int y = 0; y < height / 8; ++y, ++i) {
unsigned t = tile + i; unsigned t = tile + i;
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette + 8); const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette);
if (data) { if (data) {
m_ui.tiles->setTile(i, data); m_ui.tiles->setTile(i, data);
} else if (force) { } else if (force) {
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette + 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.x->setText(QString::number(obj->x));
m_ui.y->setText(QString::number(obj->y)); m_ui.y->setText(QString::number(obj->y));
@ -230,10 +242,56 @@ void ObjView::updateTilesGB(bool force) {
} }
#endif #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<void*>(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<void*>(buffer));
PNGWriteClose(png, info);
delete[] buffer;
}
bool ObjView::ObjInfo::operator!=(const ObjInfo& other) { bool ObjView::ObjInfo::operator!=(const ObjInfo& other) {
return other.tile != tile || return other.tile != tile ||
other.width != width || other.width != width ||
other.height != height || other.height != height ||
other.stride != stride; other.stride != stride ||
other.paletteId != paletteId ||
other.paletteSet != paletteSet;
} }

View File

@ -21,6 +21,9 @@ Q_OBJECT
public: public:
ObjView(GameController* controller, QWidget* parent = nullptr); ObjView(GameController* controller, QWidget* parent = nullptr);
public slots:
void exportObj();
private slots: private slots:
void selectObj(int); void selectObj(int);
void translateIndex(int); void translateIndex(int);
@ -43,6 +46,9 @@ private:
unsigned width; unsigned width;
unsigned height; unsigned height;
unsigned stride; unsigned stride;
unsigned paletteId;
unsigned paletteSet;
unsigned bits;
bool operator!=(const ObjInfo&); bool operator!=(const ObjInfo&);
} m_objInfo; } m_objInfo;

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>454</width> <width>454</width>
<height>375</height> <height>385</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -70,6 +70,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="exportButton">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">

View File

@ -148,7 +148,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this); GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this);
m_ui.stackedWidget->addWidget(editor); 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())); connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save()));
GBAKeyEditor* buttonEditor = nullptr; GBAKeyEditor* buttonEditor = nullptr;
@ -157,7 +157,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); const char* profile = inputController->profileForType(SDL_BINDING_BUTTON);
buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile);
m_ui.stackedWidget->addWidget(buttonEditor); 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())); connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save()));
#endif #endif
@ -176,7 +176,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
shortcutView->setController(shortcutController); shortcutView->setController(shortcutController);
shortcutView->setInputController(inputController); shortcutView->setInputController(inputController);
m_ui.stackedWidget->addWidget(shortcutView); m_ui.stackedWidget->addWidget(shortcutView);
m_ui.tabs->addItem("Shortcuts"); m_ui.tabs->addItem(tr("Shortcuts"));
} }
void SettingsView::selectBios(QLineEdit* bios) { void SettingsView::selectBios(QLineEdit* bios) {
@ -206,6 +206,7 @@ void SettingsView::updateConfig() {
saveSetting("mute", m_ui.mute); saveSetting("mute", m_ui.mute);
saveSetting("rewindEnable", m_ui.rewind); saveSetting("rewindEnable", m_ui.rewind);
saveSetting("rewindBufferCapacity", m_ui.rewindCapacity); saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);
saveSetting("rewindSave", m_ui.rewindSave);
saveSetting("resampleVideo", m_ui.resampleVideo); saveSetting("resampleVideo", m_ui.resampleVideo);
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections); saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
saveSetting("suspendScreensaver", m_ui.suspendScreensaver); saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
@ -234,13 +235,13 @@ void SettingsView::updateConfig() {
break; break;
} }
int loadState = 0; int loadState = SAVESTATE_RTC;
loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
saveSetting("loadStateExtdata", loadState); saveSetting("loadStateExtdata", loadState);
int saveState = 0; int saveState = SAVESTATE_RTC;
saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
@ -287,6 +288,7 @@ void SettingsView::reloadConfig() {
loadSetting("mute", m_ui.mute); loadSetting("mute", m_ui.mute);
loadSetting("rewindEnable", m_ui.rewind); loadSetting("rewindEnable", m_ui.rewind);
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity); loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
loadSetting("rewindSave", m_ui.rewindSave);
loadSetting("resampleVideo", m_ui.resampleVideo); loadSetting("resampleVideo", m_ui.resampleVideo);
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections); loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
loadSetting("suspendScreensaver", m_ui.suspendScreensaver); loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
@ -319,7 +321,7 @@ void SettingsView::reloadConfig() {
bool ok; bool ok;
int loadState = loadSetting("loadStateExtdata").toInt(&ok); int loadState = loadSetting("loadStateExtdata").toInt(&ok);
if (!ok) { if (!ok) {
loadState = SAVESTATE_SCREENSHOT; loadState = SAVESTATE_SCREENSHOT | SAVESTATE_RTC;
} }
m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT); m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT);
m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA); m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA);
@ -327,7 +329,7 @@ void SettingsView::reloadConfig() {
int saveState = loadSetting("saveStateExtdata").toInt(&ok); int saveState = loadSetting("saveStateExtdata").toInt(&ok);
if (!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.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT);
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA); m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);

View File

@ -383,7 +383,7 @@
<item row="11" column="1"> <item row="11" column="1">
<widget class="QCheckBox" name="resampleVideo"> <widget class="QCheckBox" name="resampleVideo">
<property name="text"> <property name="text">
<string>Resample video</string> <string>Bilinear filtering</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -535,21 +535,21 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_15"> <widget class="QLabel" name="label_15">
<property name="text"> <property name="text">
<string>Idle loops:</string> <string>Idle loops:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QComboBox" name="idleOptimization"> <widget class="QComboBox" name="idleOptimization">
<item> <item>
<property name="text"> <property name="text">
@ -568,21 +568,21 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="2"> <item row="7" column="0" colspan="2">
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Savestate extra data:</string> <string>Savestate extra data:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QCheckBox" name="saveStateScreenshot"> <widget class="QCheckBox" name="saveStateScreenshot">
<property name="text"> <property name="text">
<string>Screenshot</string> <string>Screenshot</string>
@ -592,7 +592,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="QCheckBox" name="saveStateSave"> <widget class="QCheckBox" name="saveStateSave">
<property name="text"> <property name="text">
<string>Save data</string> <string>Save data</string>
@ -602,7 +602,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QCheckBox" name="saveStateCheats"> <widget class="QCheckBox" name="saveStateCheats">
<property name="text"> <property name="text">
<string>Cheat codes</string> <string>Cheat codes</string>
@ -612,14 +612,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item row="12" column="0">
<widget class="QLabel" name="label_25"> <widget class="QLabel" name="label_25">
<property name="text"> <property name="text">
<string>Load extra data:</string> <string>Load extra data:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="12" column="1">
<widget class="QCheckBox" name="loadStateScreenshot"> <widget class="QCheckBox" name="loadStateScreenshot">
<property name="text"> <property name="text">
<string>Screenshot</string> <string>Screenshot</string>
@ -629,27 +629,37 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QCheckBox" name="loadStateSave"> <widget class="QCheckBox" name="loadStateSave">
<property name="text"> <property name="text">
<string>Save data</string> <string>Save data</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="QCheckBox" name="loadStateCheats"> <widget class="QCheckBox" name="loadStateCheats">
<property name="text"> <property name="text">
<string>Cheat codes</string> <string>Cheat codes</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0" colspan="2"> <item row="11" column="0" colspan="2">
<widget class="Line" name="line_9"> <widget class="Line" name="line_9">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1">
<widget class="QCheckBox" name="rewindSave">
<property name="text">
<string>Rewind affects save data</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="bios"> <widget class="QWidget" name="bios">

View File

@ -1275,7 +1275,7 @@ void Window::setupMenu(QMenuBar* menubar) {
m_config->updateOption("lockAspectRatio"); m_config->updateOption("lockAspectRatio");
ConfigOption* resampleVideo = m_config->addOption("resampleVideo"); ConfigOption* resampleVideo = m_config->addOption("resampleVideo");
resampleVideo->addBoolean(tr("Resample video"), avMenu); resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu);
resampleVideo->connect([this](const QVariant& value) { resampleVideo->connect([this](const QVariant& value) {
m_display->filter(value.toBool()); m_display->filter(value.toBool());
}, this); }, this);
@ -1482,12 +1482,17 @@ void Window::setupMenu(QMenuBar* menubar) {
ConfigOption* rewindEnable = m_config->addOption("rewindEnable"); ConfigOption* rewindEnable = m_config->addOption("rewindEnable");
rewindEnable->connect([this](const QVariant& value) { 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); }, this);
ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity"); ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity");
rewindBufferCapacity->connect([this](const QVariant& value) { 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); }, this);
ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections"); ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ int main(int argc, char** argv) {
.useBios = true, .useBios = true,
.rewindEnable = true, .rewindEnable = true,
.rewindBufferCapacity = 600, .rewindBufferCapacity = 600,
.rewindSave = true,
.audioBuffers = 1024, .audioBuffers = 1024,
.videoSync = false, .videoSync = false,
.audioSync = true, .audioSync = true,

View File

@ -14,7 +14,7 @@
#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2) #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); static void _mSDLAudioCallback(void* context, Uint8* data, int len);

View File

@ -25,7 +25,7 @@
#define RUMBLE_PWM 16 #define RUMBLE_PWM 16
#define RUMBLE_STEPS 2 #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); DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);

View File

@ -18,6 +18,11 @@ struct ConfigurationSectionHandlerData {
void* data; void* data;
}; };
struct ConfigurationHandlerData {
void (*handler)(const char* key, const char* value, void* data);
void* data;
};
static void _tableDeinit(void* table) { static void _tableDeinit(void* table) {
TableDeinit(table); TableDeinit(table);
free(table); free(table);
@ -63,6 +68,11 @@ static void _sectionEnumHandler(const char* key, void* section, void* user) {
data->handler(key, data->data); 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) { void ConfigurationInit(struct Configuration* configuration) {
HashTableInit(&configuration->sections, 0, _tableDeinit); HashTableInit(&configuration->sections, 0, _tableDeinit);
HashTableInit(&configuration->root, 0, _sectionDeinit); HashTableInit(&configuration->root, 0, _sectionDeinit);
@ -199,3 +209,14 @@ void ConfigurationEnumerateSections(const struct Configuration* configuration, v
struct ConfigurationSectionHandlerData handlerData = { handler, user }; struct ConfigurationSectionHandlerData handlerData = { handler, user };
HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData); 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);
}
}

View File

@ -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) { if (patch->vf->read(patch->vf, &byte, 1) != 1) {
return false; return false;
} }
if (offset >= outSize) {
return false;
}
buf[offset] ^= byte; buf[offset] ^= byte;
++offset; ++offset;
if (!byte) { if (!byte) {

View File

@ -51,6 +51,40 @@ png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
return info; 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) { bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
png_bytep row = malloc(sizeof(png_byte) * width * 3); png_bytep row = malloc(sizeof(png_byte) * width * 3);
if (!row) { if (!row) {
@ -94,6 +128,19 @@ bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned s
return true; 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) { bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
char realName[5]; char realName[5];
strncpy(realName, name, 4); strncpy(realName, name, 4);

View File

@ -48,6 +48,15 @@ bool endswith(const char* restrict s1, const char* restrict end) {
return strcmp(&s1[len - endLen], end) == 0; 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) { uint32_t utf16Char(const uint16_t** unicode, size_t* length) {
if (*length < 2) { if (*length < 2) {
*length = 0; *length = 0;

View File

@ -96,6 +96,7 @@ def updateMachO(bin, execPath, root):
if os.access(newPath, os.F_OK): if os.access(newPath, os.F_OK):
if verbose: if verbose:
print('Skipping copying {}, already done.'.format(oldPath)) print('Skipping copying {}, already done.'.format(oldPath))
newPath = None
elif os.path.abspath(oldPath) != os.path.abspath(newPath): elif os.path.abspath(oldPath) != os.path.abspath(newPath):
if verbose: if verbose:
print('Copying {} to {}...'.format(oldPath, newPath)) print('Copying {} to {}...'.format(oldPath, newPath))
@ -111,7 +112,8 @@ def updateMachO(bin, execPath, root):
args = [installNameTool] args = [installNameTool]
for path, oldExecPath, newExecPath in toUpdate: for path, oldExecPath, newExecPath in toUpdate:
if path != bin: if path != bin:
updateMachO(path, execPath, root) if path:
updateMachO(path, execPath, root)
if verbose: if verbose:
print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath)) print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath))
args.extend(['-change', oldExecPath, newExecPath]) args.extend(['-change', oldExecPath, newExecPath])