mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
e1e627e496
16
CHANGES
16
CHANGES
|
@ -8,6 +8,10 @@ Features:
|
|||
- Improved memory viewer
|
||||
- GB: LR35902/GB-Z80 disassembler
|
||||
- Configuration of gamepad hats
|
||||
- Qt: Spanish translation (by Kevin López)
|
||||
- Add option for whether rewinding restores save games
|
||||
- Qt: German translation (by Lothar Serra Mari)
|
||||
- Savestates now contain any RTC override data
|
||||
Bugfixes:
|
||||
- LR35902: Fix core never exiting with certain event patterns
|
||||
- GB Timer: Improve DIV reset behavior
|
||||
|
@ -23,6 +27,12 @@ Bugfixes:
|
|||
- GB MBC: Fix ROM bank overflows getting set to bank 0
|
||||
- Qt: Fix timing issues on high refresh rate monitors
|
||||
- GBA Savedata: Fix savedata unmasking (fixes mgba.io/i/441)
|
||||
- Util: Fix overflow when loading invalid UPS patches
|
||||
- Tools: Fix recurring multiple times over the same library
|
||||
- GBA I/O: Handle audio registers specially when deserializing
|
||||
- Util: Fix highest-fd socket not being returned by SocketAccept
|
||||
- Qt: Fix linking after some windows have been closed
|
||||
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
||||
Misc:
|
||||
- SDL: Remove scancode key input
|
||||
- GBA Video: Clean up unused timers
|
||||
|
@ -60,6 +70,12 @@ Misc:
|
|||
- SDL: Automatically map controllers when plugged in
|
||||
- Qt: Automatically load controller profile when plugged in
|
||||
- OpenGL: Add xBR-lv2 shader
|
||||
- GBA, GB: ROM is now unloaded if a patch is applied
|
||||
- Util: Add 8-bit PNG write support
|
||||
- Qt: Rename "Resample video" option to "Bilinear filtering"
|
||||
- GBA Video: Optimize when BLD* registers are written frequently
|
||||
- Core: Cores can now have multiple sets of callbacks
|
||||
- GBA: Ignore invalid opcodes used by the Wii U VC emulator
|
||||
|
||||
0.5.2: (2016-12-31)
|
||||
Bugfixes:
|
||||
|
|
10
README.md
10
README.md
|
@ -96,10 +96,16 @@ This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies th
|
|||
|
||||
#### Windows developer building
|
||||
|
||||
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MinGW-w64 Win32 Shell") and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time):
|
||||
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MinGW-w64 Win32 Shell") (or the 64-bit version "MinGW-w64 Win64 Shell" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time):
|
||||
|
||||
For x86 (32 bit) builds:
|
||||
|
||||
pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2}
|
||||
|
||||
For x86_64 (64 bit) builds:
|
||||
|
||||
pacman -Sy mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2}
|
||||
|
||||
Check out the source code by running this command:
|
||||
|
||||
git clone https://github.com/mgba-emu/mgba.git
|
||||
|
@ -112,7 +118,7 @@ Then finally build it by running these commands:
|
|||
cmake .. -G "MSYS Makefiles"
|
||||
make
|
||||
|
||||
Please note that this build of mGBA for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development.
|
||||
Please note that this build of mGBA for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), a tool called "[Dependency Walker](http://dependencywalker.com)" can be used to see which additional DLL files need to be shipped with the mGBA executable.
|
||||
|
||||
### Dependencies
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ bool ConfigurationWrite(const struct Configuration*, const char* path);
|
|||
bool ConfigurationWriteSection(const struct Configuration*, const char* path, const char* section);
|
||||
|
||||
void ConfigurationEnumerateSections(const struct Configuration* configuration, void (*handler)(const char* sectionName, void* user), void* user);
|
||||
void ConfigurationEnumerate(const struct Configuration* configuration, const char* section, void (*handler)(const char* key, const char* value, void* user), void* user);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -22,7 +22,10 @@ enum {
|
|||
|
||||
png_structp PNGWriteOpen(struct VFile* source);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||
void PNGWriteClose(png_structp png, png_infop info);
|
||||
|
||||
|
|
|
@ -293,10 +293,11 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
|
|||
errors[i] = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
++maxFd;
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeoutMillis / 1000;
|
||||
tv.tv_usec = (timeoutMillis % 1000) * 1000;
|
||||
int result = select(maxFd + 1, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
|
||||
int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
|
||||
int r = 0;
|
||||
int w = 0;
|
||||
int e = 0;
|
||||
|
|
|
@ -21,6 +21,7 @@ char* strdup(const char* str);
|
|||
|
||||
char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len);
|
||||
bool endswith(const char* restrict s1, const char* restrict end);
|
||||
bool startswith(const char* restrict s1, const char* restrict start);
|
||||
|
||||
size_t toUtf8(uint32_t unichar, char* buffer);
|
||||
int utfcmp(const uint16_t* utf16, const char* utf8, size_t utf16Length, size_t utf8Length);
|
||||
|
|
|
@ -27,7 +27,8 @@ CXX_GUARD_START
|
|||
void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference); \
|
||||
void NAME ## EnsureCapacity(struct NAME* vector, size_t capacity); \
|
||||
size_t NAME ## Size(const struct NAME* vector); \
|
||||
size_t NAME ## Index(const struct NAME* vector, const TYPE* member);
|
||||
size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \
|
||||
void NAME ## Copy(struct NAME* dest, const struct NAME* src);
|
||||
|
||||
#define DEFINE_VECTOR(NAME, TYPE) \
|
||||
void NAME ## Init(struct NAME* vector, size_t capacity) { \
|
||||
|
@ -85,6 +86,11 @@ CXX_GUARD_START
|
|||
size_t NAME ## Index(const struct NAME* vector, const TYPE* member) { \
|
||||
return member - (const TYPE*) vector->vector; \
|
||||
} \
|
||||
void NAME ## Copy(struct NAME* dest, const struct NAME* src) { \
|
||||
NAME ## EnsureCapacity(dest, src->size); \
|
||||
memcpy(dest->vector, src->vector, src->size * sizeof(TYPE)); \
|
||||
dest->size = src->size; \
|
||||
} \
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -19,6 +19,12 @@ struct mCoreConfig {
|
|||
char* port;
|
||||
};
|
||||
|
||||
enum mCoreConfigLevel {
|
||||
mCONFIG_LEVEL_DEFAULT = 0,
|
||||
mCONFIG_LEVEL_CUSTOM,
|
||||
mCONFIG_LEVEL_OVERRIDE,
|
||||
};
|
||||
|
||||
struct mCoreOptions {
|
||||
char* bios;
|
||||
bool skipBios;
|
||||
|
@ -27,6 +33,7 @@ struct mCoreOptions {
|
|||
int frameskip;
|
||||
bool rewindEnable;
|
||||
int rewindBufferCapacity;
|
||||
bool rewindSave;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
unsigned sampleRate;
|
||||
|
@ -89,6 +96,8 @@ void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig*
|
|||
void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts);
|
||||
void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts);
|
||||
|
||||
void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user);
|
||||
|
||||
struct Configuration* mCoreConfigGetInput(struct mCoreConfig*);
|
||||
struct Configuration* mCoreConfigGetOverrides(struct mCoreConfig*);
|
||||
const struct Configuration* mCoreConfigGetOverridesConst(const struct mCoreConfig*);
|
||||
|
|
|
@ -40,7 +40,6 @@ enum mCoreChecksumType {
|
|||
CHECKSUM_CRC32,
|
||||
};
|
||||
|
||||
struct mRTCSource;
|
||||
struct mCoreConfig;
|
||||
struct mCoreSync;
|
||||
struct mStateExtdata;
|
||||
|
@ -59,6 +58,8 @@ struct mCore {
|
|||
struct mCoreConfig config;
|
||||
struct mCoreOptions opts;
|
||||
|
||||
struct mRTCGenericSource rtc;
|
||||
|
||||
bool (*init)(struct mCore*);
|
||||
void (*deinit)(struct mCore*);
|
||||
|
||||
|
@ -77,7 +78,8 @@ struct mCore {
|
|||
void (*setAudioBufferSize)(struct mCore*, size_t samples);
|
||||
size_t (*getAudioBufferSize)(struct mCore*);
|
||||
|
||||
void (*setCoreCallbacks)(struct mCore*, struct mCoreCallbacks*);
|
||||
void (*addCoreCallbacks)(struct mCore*, struct mCoreCallbacks*);
|
||||
void (*clearCoreCallbacks)(struct mCore*);
|
||||
void (*setAVStream)(struct mCore*, struct mAVStream*);
|
||||
|
||||
bool (*isROM)(struct VFile* vf);
|
||||
|
@ -115,7 +117,6 @@ struct mCore {
|
|||
void (*getGameTitle)(const struct mCore*, char* title);
|
||||
void (*getGameCode)(const struct mCore*, char* title);
|
||||
|
||||
void (*setRTC)(struct mCore*, struct mRTCSource*);
|
||||
void (*setRotation)(struct mCore*, struct mRotationSource*);
|
||||
void (*setRumble)(struct mCore*, struct mRumble*);
|
||||
|
||||
|
@ -174,6 +175,8 @@ void mCoreInitConfig(struct mCore* core, const char* port);
|
|||
void mCoreLoadConfig(struct mCore* core);
|
||||
void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config);
|
||||
|
||||
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct mCore;
|
||||
struct mStateExtdataItem;
|
||||
|
||||
#ifdef COLOR_16_BIT
|
||||
typedef uint16_t color_t;
|
||||
|
@ -37,6 +40,8 @@ struct mCoreCallbacks {
|
|||
void (*coreCrashed)(void* context);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
|
||||
|
||||
struct mAVStream {
|
||||
void (*videoDimensionsChanged)(struct mAVStream*, unsigned width, unsigned height);
|
||||
void (*postVideoFrame)(struct mAVStream*, const color_t* buffer, size_t stride);
|
||||
|
@ -65,12 +70,16 @@ struct mRTCSource {
|
|||
void (*sample)(struct mRTCSource*);
|
||||
|
||||
time_t (*unixTime)(struct mRTCSource*);
|
||||
|
||||
void (*serialize)(struct mRTCSource*, struct mStateExtdataItem*);
|
||||
bool (*deserialize)(struct mRTCSource*, const struct mStateExtdataItem*);
|
||||
};
|
||||
|
||||
enum mRTCGenericType {
|
||||
RTC_NO_OVERRIDE,
|
||||
RTC_FIXED,
|
||||
RTC_FAKE_EPOCH
|
||||
RTC_FAKE_EPOCH,
|
||||
RTC_CUSTOM_START = 0x1000
|
||||
};
|
||||
|
||||
struct mRTCGenericSource {
|
||||
|
@ -78,6 +87,13 @@ struct mRTCGenericSource {
|
|||
struct mCore* p;
|
||||
enum mRTCGenericType override;
|
||||
int64_t value;
|
||||
struct mRTCSource* custom;
|
||||
};
|
||||
|
||||
struct mRTCGenericState {
|
||||
int32_t type;
|
||||
int32_t padding;
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/table.h>
|
||||
|
||||
enum mLogLevel {
|
||||
mLOG_FATAL = 0x01,
|
||||
mLOG_ERROR = 0x02,
|
||||
|
@ -22,29 +24,47 @@ enum mLogLevel {
|
|||
mLOG_ALL = 0x7F
|
||||
};
|
||||
|
||||
struct Table;
|
||||
struct mLogFilter {
|
||||
int defaultLevels;
|
||||
struct Table categories;
|
||||
struct Table levels;
|
||||
};
|
||||
|
||||
struct mLogger {
|
||||
void (*log)(struct mLogger*, int category, enum mLogLevel level, const char* format, va_list args);
|
||||
struct mLogFilter* filter;
|
||||
};
|
||||
|
||||
struct mLogger* mLogGetContext(void);
|
||||
void mLogSetDefaultLogger(struct mLogger*);
|
||||
int mLogGenerateCategory(const char*);
|
||||
int mLogGenerateCategory(const char*, const char*);
|
||||
const char* mLogCategoryName(int);
|
||||
const char* mLogCategoryId(int);
|
||||
int mLogCategoryById(const char*);
|
||||
|
||||
struct mCoreConfig;
|
||||
void mLogFilterInit(struct mLogFilter*);
|
||||
void mLogFilterDeinit(struct mLogFilter*);
|
||||
void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*);
|
||||
void mLogFilterSet(struct mLogFilter*, const char* category, int levels);
|
||||
bool mLogFilterTest(struct mLogFilter*, int category, enum mLogLevel level);
|
||||
|
||||
ATTRIBUTE_FORMAT(printf, 3, 4)
|
||||
void mLog(int category, enum mLogLevel level, const char* format, ...);
|
||||
|
||||
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__)
|
||||
|
||||
#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void);
|
||||
#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME) \
|
||||
#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); extern const char* _mLOG_CAT_ ## CATEGORY ## _ID;
|
||||
#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \
|
||||
int _mLOG_CAT_ ## CATEGORY (void) { \
|
||||
static int category = 0; \
|
||||
if (!category) { \
|
||||
category = mLogGenerateCategory(NAME); \
|
||||
category = mLogGenerateCategory(NAME, ID); \
|
||||
} \
|
||||
return category; \
|
||||
}
|
||||
} \
|
||||
const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID;
|
||||
|
||||
mLOG_DECLARE_CATEGORY(STATUS)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ struct mCoreRewindContext {
|
|||
struct mCoreRewindPatches patchMemory;
|
||||
size_t current;
|
||||
size_t size;
|
||||
int stateFlags;
|
||||
struct VFile* previousState;
|
||||
struct VFile* currentState;
|
||||
};
|
||||
|
|
|
@ -15,12 +15,14 @@ enum mStateExtdataTag {
|
|||
EXTDATA_SCREENSHOT = 1,
|
||||
EXTDATA_SAVEDATA = 2,
|
||||
EXTDATA_CHEATS = 3,
|
||||
EXTDATA_RTC = 4,
|
||||
EXTDATA_MAX
|
||||
};
|
||||
|
||||
#define SAVESTATE_SCREENSHOT 1
|
||||
#define SAVESTATE_SAVEDATA 2
|
||||
#define SAVESTATE_CHEATS 4
|
||||
#define SAVESTATE_RTC 8
|
||||
|
||||
struct mStateExtdataItem {
|
||||
int32_t size;
|
||||
|
|
|
@ -58,7 +58,6 @@ struct mCoreThread {
|
|||
bool frameWasOn;
|
||||
|
||||
struct mThreadLogger logger;
|
||||
enum mLogLevel logLevel;
|
||||
ThreadCallback startCallback;
|
||||
ThreadCallback resetCallback;
|
||||
ThreadCallback cleanCallback;
|
||||
|
|
|
@ -58,6 +58,8 @@ void mTileCacheSetPalette(struct mTileCache* cache, int palette);
|
|||
|
||||
const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId);
|
||||
const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId);
|
||||
const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId);
|
||||
const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ struct DS {
|
|||
|
||||
struct mAVStream* stream;
|
||||
struct mKeyCallback* keyCallback;
|
||||
struct mCoreCallbacks* coreCallbacks;
|
||||
struct mCoreCallbacksList coreCallbacks;
|
||||
|
||||
struct mTimingEvent divEvent;
|
||||
struct mTimingEvent sqrtEvent;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/timing.h>
|
||||
|
||||
|
@ -46,7 +47,6 @@ enum GBIRQVector {
|
|||
struct LR35902Core;
|
||||
struct mCoreSync;
|
||||
struct mAVStream;
|
||||
struct mCoreCallbacks;
|
||||
struct GB {
|
||||
struct mCPUComponent d;
|
||||
|
||||
|
@ -63,7 +63,7 @@ struct GB {
|
|||
|
||||
uint8_t* keySource;
|
||||
|
||||
void* pristineRom;
|
||||
bool isPristine;
|
||||
size_t pristineRomSize;
|
||||
size_t yankedRomSize;
|
||||
uint32_t romCrc32;
|
||||
|
@ -76,7 +76,7 @@ struct GB {
|
|||
int32_t sramDirtAge;
|
||||
bool sramMaskWriteback;
|
||||
|
||||
struct mCoreCallbacks* coreCallbacks;
|
||||
struct mCoreCallbacksList coreCallbacks;
|
||||
struct mAVStream* stream;
|
||||
|
||||
bool cpuBlocked;
|
||||
|
|
|
@ -17,7 +17,7 @@ mLOG_DECLARE_CATEGORY(GB_MBC);
|
|||
struct GB;
|
||||
struct GBMemory;
|
||||
void GBMBCInit(struct GB* gb);
|
||||
void GBMBCSwitchBank(struct GBMemory* memory, int bank);
|
||||
void GBMBCSwitchBank(struct GB* gb, int bank);
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank);
|
||||
|
||||
struct GBMBCRTCSaveBuffer {
|
||||
|
|
|
@ -90,7 +90,7 @@ struct GBA {
|
|||
struct mRumble* rumble;
|
||||
|
||||
struct GBARRContext* rr;
|
||||
void* pristineRom;
|
||||
bool isPristine;
|
||||
size_t pristineRomSize;
|
||||
size_t yankedRomSize;
|
||||
uint32_t romCrc32;
|
||||
|
@ -100,7 +100,7 @@ struct GBA {
|
|||
struct mAVStream* stream;
|
||||
struct mKeyCallback* keyCallback;
|
||||
struct mStopCallback* stopCallback;
|
||||
struct mCoreCallbacks* coreCallbacks;
|
||||
struct mCoreCallbacksList coreCallbacks;
|
||||
|
||||
enum GBAIdleLoopOptimization idleOptimization;
|
||||
uint32_t idleLoop;
|
||||
|
|
|
@ -129,6 +129,7 @@ struct GBAVideoSoftwareRenderer {
|
|||
unsigned target1Bd;
|
||||
unsigned target2Obj;
|
||||
unsigned target2Bd;
|
||||
bool blendDirty;
|
||||
enum BlendEffect blendEffect;
|
||||
color_t normalPalette[512];
|
||||
color_t variantPalette[512];
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
const uint32_t M_CHEAT_DEVICE_ID = 0xABADC0DE;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(CHEATS, "Cheats");
|
||||
mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
|
||||
|
||||
DEFINE_VECTOR(mCheatList, struct mCheat);
|
||||
DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
|
||||
|
|
|
@ -29,6 +29,13 @@
|
|||
|
||||
#define SECTION_NAME_MAX 128
|
||||
|
||||
struct mCoreConfigEnumerateData {
|
||||
void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user);
|
||||
const char* prefix;
|
||||
void* user;
|
||||
enum mCoreConfigLevel level;
|
||||
};
|
||||
|
||||
static const char* _lookupValue(const struct mCoreConfig* config, const char* key) {
|
||||
const char* value;
|
||||
if (config->port) {
|
||||
|
@ -321,6 +328,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
|||
_lookupIntValue(config, "frameskip", &opts->frameskip);
|
||||
_lookupIntValue(config, "volume", &opts->volume);
|
||||
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
|
||||
_lookupIntValue(config, "rewindSave", &opts->rewindSave);
|
||||
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
|
||||
unsigned audioBuffers;
|
||||
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
||||
|
@ -376,6 +384,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
|
|||
ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindSave", opts->rewindSave);
|
||||
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
|
||||
|
@ -391,6 +400,22 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
|
|||
ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
|
||||
}
|
||||
|
||||
static void _configEnum(const char* key, const char* value, void* user) {
|
||||
struct mCoreConfigEnumerateData* data = user;
|
||||
if (!data->prefix || startswith(key, data->prefix)) {
|
||||
data->handler(key, value, data->level, data->user);
|
||||
}
|
||||
}
|
||||
|
||||
void mCoreConfigEnumerate(const struct mCoreConfig* config, const char* prefix, void (*handler)(const char* key, const char* value, enum mCoreConfigLevel type, void* user), void* user) {
|
||||
struct mCoreConfigEnumerateData handlerData = { handler, prefix, user, mCONFIG_LEVEL_DEFAULT };
|
||||
ConfigurationEnumerate(&config->defaultsTable, config->port, _configEnum, &handlerData);
|
||||
handlerData.level = mCONFIG_LEVEL_CUSTOM;
|
||||
ConfigurationEnumerate(&config->configTable, config->port, _configEnum, &handlerData);
|
||||
handlerData.level = mCONFIG_LEVEL_OVERRIDE;
|
||||
ConfigurationEnumerate(&config->overridesTable, config->port, _configEnum, &handlerData);
|
||||
}
|
||||
|
||||
// These two are basically placeholders in case the internal layout changes, e.g. for loading separate files
|
||||
struct Configuration* mCoreConfigGetInput(struct mCoreConfig* config) {
|
||||
return &config->configTable;
|
||||
|
|
|
@ -240,3 +240,8 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
|
|||
}
|
||||
core->loadConfig(core, config);
|
||||
}
|
||||
|
||||
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
|
||||
core->rtc.custom = rtc;
|
||||
core->rtc.override = RTC_CUSTOM_START;
|
||||
}
|
||||
|
|
|
@ -6,24 +6,103 @@
|
|||
#include <mgba/core/interface.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) {
|
||||
struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source;
|
||||
switch (rtc->override) {
|
||||
case RTC_NO_OVERRIDE:
|
||||
default:
|
||||
if (rtc->custom->unixTime) {
|
||||
return rtc->custom->unixTime(rtc->custom);
|
||||
}
|
||||
// Fall through
|
||||
case RTC_NO_OVERRIDE:
|
||||
return time(0);
|
||||
case RTC_FIXED:
|
||||
return rtc->value;
|
||||
return rtc->value / 1000LL;
|
||||
case RTC_FAKE_EPOCH:
|
||||
return rtc->value + rtc->p->frameCounter(rtc->p) * (int64_t) rtc->p->frameCycles(rtc->p) / rtc->p->frequency(rtc->p);
|
||||
return (rtc->value + rtc->p->frameCounter(rtc->p) * (rtc->p->frameCycles(rtc->p) * 1000LL) / rtc->p->frequency(rtc->p)) / 1000LL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _rtcGenericSerialize(struct mRTCSource* source, struct mStateExtdataItem* item) {
|
||||
struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source;
|
||||
struct mRTCGenericState state = {
|
||||
.type = rtc->override,
|
||||
.padding = 0,
|
||||
.value = rtc->value
|
||||
};
|
||||
void* data;
|
||||
if (rtc->override >= RTC_CUSTOM_START && rtc->custom->serialize) {
|
||||
rtc->custom->serialize(rtc->custom, item);
|
||||
data = malloc(item->size + sizeof(state));
|
||||
uint8_t* oldData = data;
|
||||
oldData += sizeof(state);
|
||||
memcpy(oldData, item->data, item->size);
|
||||
item->size += sizeof(state);
|
||||
if (item->clean) {
|
||||
item->clean(item->data);
|
||||
}
|
||||
} else {
|
||||
item->size = sizeof(state);
|
||||
data = malloc(item->size);
|
||||
}
|
||||
memcpy(data, &state, sizeof(state));
|
||||
item->data = data;
|
||||
item->clean = free;
|
||||
}
|
||||
|
||||
static bool _rtcGenericDeserialize(struct mRTCSource* source, const struct mStateExtdataItem* item) {
|
||||
struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source;
|
||||
struct mRTCGenericState* state = item->data;
|
||||
if (!state || item->size < (ssize_t) sizeof(*state)) {
|
||||
return false;
|
||||
}
|
||||
if (state->type >= RTC_CUSTOM_START) {
|
||||
if (!rtc->custom) {
|
||||
return false;
|
||||
}
|
||||
if (rtc->custom->deserialize) {
|
||||
uint8_t* oldData = item->data;
|
||||
oldData += sizeof(state);
|
||||
struct mStateExtdataItem fakeItem = {
|
||||
.size = item->size - sizeof(*state),
|
||||
.data = oldData,
|
||||
.clean = NULL
|
||||
};
|
||||
if (!rtc->custom->deserialize(rtc->custom, &fakeItem)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
rtc->value = state->value;
|
||||
rtc->override = state->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) {
|
||||
rtc->p = core;
|
||||
rtc->override = RTC_NO_OVERRIDE;
|
||||
rtc->value = 0;
|
||||
rtc->d.sample = 0;
|
||||
rtc->d.sample = _rtcGenericSample;
|
||||
rtc->d.unixTime = _rtcGenericCallback;
|
||||
rtc->d.serialize = _rtcGenericSerialize;
|
||||
rtc->d.deserialize = _rtcGenericDeserialize;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/core/log.h>
|
||||
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba/core/thread.h>
|
||||
|
||||
#define MAX_CATEGORY 64
|
||||
|
@ -28,20 +29,39 @@ void mLogSetDefaultLogger(struct mLogger* logger) {
|
|||
|
||||
static int _category = 0;
|
||||
static const char* _categoryNames[MAX_CATEGORY];
|
||||
static const char* _categoryIds[MAX_CATEGORY];
|
||||
|
||||
int mLogGenerateCategory(const char* name) {
|
||||
++_category;
|
||||
int mLogGenerateCategory(const char* name, const char* id) {
|
||||
if (_category < MAX_CATEGORY) {
|
||||
_categoryNames[_category] = name;
|
||||
_categoryIds[_category] = id;
|
||||
}
|
||||
return _category;
|
||||
++_category;
|
||||
return _category - 1;
|
||||
}
|
||||
|
||||
const char* mLogCategoryName(int category) {
|
||||
if (category < MAX_CATEGORY) {
|
||||
return _categoryNames[category];
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* mLogCategoryId(int category) {
|
||||
if (category < MAX_CATEGORY) {
|
||||
return _categoryIds[category];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mLogCategoryById(const char* id) {
|
||||
int i;
|
||||
for (i = 0; i < _category; ++i) {
|
||||
if (strcmp(_categoryIds[i], id) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
||||
|
@ -49,7 +69,9 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
|||
va_list args;
|
||||
va_start(args, format);
|
||||
if (context) {
|
||||
context->log(context, category, level, format, args);
|
||||
if (!context->filter || mLogFilterTest(context->filter, category, level)) {
|
||||
context->log(context, category, level, format, args);
|
||||
}
|
||||
} else {
|
||||
printf("%s: ", mLogCategoryName(category));
|
||||
vprintf(format, args);
|
||||
|
@ -58,4 +80,67 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
mLOG_DEFINE_CATEGORY(STATUS, "Status")
|
||||
void mLogFilterInit(struct mLogFilter* filter) {
|
||||
HashTableInit(&filter->categories, 8, NULL);
|
||||
TableInit(&filter->levels, 8, NULL);
|
||||
}
|
||||
|
||||
void mLogFilterDeinit(struct mLogFilter* filter) {
|
||||
HashTableDeinit(&filter->categories);
|
||||
TableDeinit(&filter->levels);
|
||||
}
|
||||
|
||||
static void _setFilterLevel(const char* key, const char* value, enum mCoreConfigLevel level, void* user) {
|
||||
UNUSED(level);
|
||||
struct mLogFilter* filter = user;
|
||||
key = strchr(key, '.');
|
||||
if (!key || !key[1]) {
|
||||
return;
|
||||
}
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
++key;
|
||||
char* end;
|
||||
int ivalue = strtol(value, &end, 10);
|
||||
if (ivalue == 0) {
|
||||
ivalue = INT_MIN; // Zero is reserved
|
||||
}
|
||||
if (!end) {
|
||||
return;
|
||||
}
|
||||
mLogFilterSet(filter, key, ivalue);
|
||||
}
|
||||
|
||||
void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) {
|
||||
mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter);
|
||||
filter->defaultLevels = mLOG_ALL;
|
||||
mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels);
|
||||
}
|
||||
|
||||
void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) {
|
||||
HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels);
|
||||
// Can't do this eagerly because not all categories are initialized immediately
|
||||
int cat = mLogCategoryById(category);
|
||||
if (cat >= 0) {
|
||||
TableInsert(&filter->levels, cat, (void*)(intptr_t) levels);
|
||||
}
|
||||
|
||||
}
|
||||
bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) {
|
||||
int value = (int) TableLookup(&filter->levels, category);
|
||||
if (value) {
|
||||
return value & level;
|
||||
}
|
||||
const char* cat = mLogCategoryId(category);
|
||||
if (cat) {
|
||||
value = (int) HashTableLookup(&filter->categories, cat);
|
||||
if (value) {
|
||||
TableInsert(&filter->levels, category, (void*)(intptr_t) value);
|
||||
return value & level;
|
||||
}
|
||||
}
|
||||
return level & filter->defaultLevels;
|
||||
}
|
||||
|
||||
mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/core/rewind.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba-util/patch/fast.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -20,6 +21,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries)
|
|||
context->previousState = VFileMemChunk(0, 0);
|
||||
context->currentState = VFileMemChunk(0, 0);
|
||||
context->size = 0;
|
||||
context->stateFlags = SAVESTATE_SAVEDATA;
|
||||
}
|
||||
|
||||
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
|
||||
|
@ -41,7 +43,7 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
|
|||
if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
|
||||
context->current = 0;
|
||||
}
|
||||
mCoreSaveStateNamed(core, nextState, 0);
|
||||
mCoreSaveStateNamed(core, nextState, context->stateFlags);
|
||||
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
|
||||
size_t size2 = nextState->size(nextState);
|
||||
size_t size = context->currentState->size(context->currentState);
|
||||
|
@ -75,7 +77,7 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core)
|
|||
patch->d.applyPatch(&patch->d, current, size, previous, size);
|
||||
context->currentState->unmap(context->currentState, current, size);
|
||||
context->previousState->unmap(context->previousState, previous, size);
|
||||
mCoreLoadStateNamed(core, context->previousState, 0);
|
||||
mCoreLoadStateNamed(core, context->previousState, context->stateFlags);
|
||||
struct VFile* nextState = context->previousState;
|
||||
context->previousState = context->currentState;
|
||||
context->currentState = nextState;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -16,7 +17,7 @@
|
|||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate");
|
||||
mLOG_DEFINE_CATEGORY(SAVESTATE, "Savestate", "core.serialize");
|
||||
|
||||
struct mBundledState {
|
||||
size_t stateSize;
|
||||
|
@ -328,6 +329,14 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
mStateExtdataPut(&extdata, EXTDATA_CHEATS, &item);
|
||||
}
|
||||
}
|
||||
if (flags & SAVESTATE_RTC) {
|
||||
mLOG(SAVESTATE, INFO, "Loading RTC");
|
||||
struct mStateExtdataItem item;
|
||||
if (core->rtc.d.serialize) {
|
||||
core->rtc.d.serialize(&core->rtc.d, &item);
|
||||
mStateExtdataPut(&extdata, EXTDATA_RTC, &item);
|
||||
}
|
||||
}
|
||||
#ifdef USE_PNG
|
||||
if (!(flags & SAVESTATE_SCREENSHOT)) {
|
||||
#else
|
||||
|
@ -425,6 +434,12 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (flags & SAVESTATE_RTC && mStateExtdataGet(&extdata, EXTDATA_RTC, &item)) {
|
||||
mLOG(SAVESTATE, INFO, "Loading RTC");
|
||||
if (core->rtc.d.deserialize) {
|
||||
core->rtc.d.deserialize(&core->rtc.d, &item);
|
||||
}
|
||||
}
|
||||
mStateExtdataDeinit(&extdata);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/core/thread.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -35,6 +36,8 @@ static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args);
|
||||
|
||||
static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadState newState, bool broadcast) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
threadContext->state = newState;
|
||||
|
@ -142,12 +145,20 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
.coreCrashed = _crashed,
|
||||
.context = threadContext
|
||||
};
|
||||
core->setCoreCallbacks(core, &callbacks);
|
||||
core->addCoreCallbacks(core, &callbacks);
|
||||
core->setSync(core, &threadContext->sync);
|
||||
core->reset(core);
|
||||
|
||||
struct mLogFilter filter;
|
||||
if (!threadContext->logger.d.filter) {
|
||||
threadContext->logger.d.filter = &filter;
|
||||
mLogFilterInit(threadContext->logger.d.filter);
|
||||
mLogFilterLoad(threadContext->logger.d.filter, &core->config);
|
||||
}
|
||||
|
||||
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
|
||||
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
|
||||
threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
|
||||
}
|
||||
|
||||
_changeState(threadContext, THREAD_RUNNING, true);
|
||||
|
@ -221,7 +232,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
if (threadContext->cleanCallback) {
|
||||
threadContext->cleanCallback(threadContext);
|
||||
}
|
||||
core->setCoreCallbacks(core, NULL);
|
||||
core->clearCoreCallbacks(core);
|
||||
|
||||
threadContext->logger.d.filter = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -229,7 +242,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
bool mCoreThreadStart(struct mCoreThread* threadContext) {
|
||||
threadContext->state = THREAD_INITIALIZED;
|
||||
threadContext->logger.p = threadContext;
|
||||
threadContext->logLevel = threadContext->core->opts.logLevel;
|
||||
if (!threadContext->logger.d.log) {
|
||||
threadContext->logger.d.log = _mCoreLog;
|
||||
threadContext->logger.d.filter = NULL;
|
||||
}
|
||||
|
||||
if (!threadContext->sync.fpsTarget) {
|
||||
threadContext->sync.fpsTarget = _defaultFPSTarget;
|
||||
|
@ -542,10 +558,7 @@ struct mCoreThread* mCoreThreadGet(void) {
|
|||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(logger);
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
if (thread && !(thread->logLevel & level)) {
|
||||
return;
|
||||
}
|
||||
UNUSED(level);
|
||||
printf("%s: ", mLogCategoryName(category));
|
||||
vprintf(format, args);
|
||||
printf("\n");
|
||||
|
@ -554,9 +567,6 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level
|
|||
struct mLogger* mCoreThreadLogger(void) {
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
if (thread) {
|
||||
if (!thread->logger.d.log) {
|
||||
thread->logger.d.log = _mCoreLog;
|
||||
}
|
||||
return &thread->logger.d;
|
||||
}
|
||||
return NULL;
|
||||
|
|
|
@ -283,3 +283,27 @@ const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileC
|
|||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId) {
|
||||
unsigned bpp = cache->bpp;
|
||||
switch (bpp) {
|
||||
case 0:
|
||||
return NULL;
|
||||
default:
|
||||
return (uint8_t*) &cache->vram[tileId << (2 + bpp)];
|
||||
}
|
||||
}
|
||||
|
||||
const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
|
||||
unsigned bpp = cache->bpp;
|
||||
switch (bpp) {
|
||||
default:
|
||||
return NULL;
|
||||
case 1:
|
||||
return &cache->palette[paletteId << 2];
|
||||
case 2:
|
||||
return &cache->palette[paletteId << 4];
|
||||
case 3:
|
||||
return &cache->palette[paletteId << 8];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
const uint32_t DEBUGGER_ID = 0xDEADBEEF;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger");
|
||||
mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger");
|
||||
|
||||
static void mDebuggerInit(void* cpu, struct mCPUComponent* component);
|
||||
static void mDebuggerDeinit(struct mCPUComponent* component);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#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 DS9_BIOS_CHECKSUM = 0x2AB23573;
|
||||
|
|
|
@ -156,9 +156,14 @@ static size_t _DSCoreGetAudioBufferSize(struct mCore* core) {
|
|||
return 2048;
|
||||
}
|
||||
|
||||
static void _DSCoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
static void _DSCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
struct DS* ds = core->board;
|
||||
ds->coreCallbacks = coreCallbacks;
|
||||
*mCoreCallbacksListAppend(&ds->coreCallbacks) = *coreCallbacks;
|
||||
}
|
||||
|
||||
static void _DSCoreClearCoreCallbacks(struct mCore* core) {
|
||||
struct DS* ds = core->board;
|
||||
mCoreCallbacksListClear(&ds->coreCallbacks);
|
||||
}
|
||||
|
||||
static void _DSCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
||||
|
@ -370,11 +375,6 @@ static void _DSCoreGetGameCode(const struct mCore* core, char* title) {
|
|||
DSGetGameCode(core->board, title);
|
||||
}
|
||||
|
||||
static void _DSCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
|
||||
struct DS* ds = core->board;
|
||||
ds->rtcSource = rtc;
|
||||
}
|
||||
|
||||
static void _DSCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
|
||||
}
|
||||
|
||||
|
@ -513,7 +513,8 @@ struct mCore* DSCoreCreate(void) {
|
|||
core->getAudioChannel = _DSCoreGetAudioChannel;
|
||||
core->setAudioBufferSize = _DSCoreSetAudioBufferSize;
|
||||
core->getAudioBufferSize = _DSCoreGetAudioBufferSize;
|
||||
core->setCoreCallbacks = _DSCoreSetCoreCallbacks;
|
||||
core->addCoreCallbacks = _DSCoreAddCoreCallbacks;
|
||||
core->clearCoreCallbacks = _DSCoreClearCoreCallbacks;
|
||||
core->setAVStream = _DSCoreSetAVStream;
|
||||
core->isROM = DSIsROM;
|
||||
core->loadROM = _DSCoreLoadROM;
|
||||
|
@ -539,7 +540,6 @@ struct mCore* DSCoreCreate(void) {
|
|||
core->frequency = _DSCoreFrequency;
|
||||
core->getGameTitle = _DSCoreGetGameTitle;
|
||||
core->getGameCode = _DSCoreGetGameCode;
|
||||
core->setRTC = _DSCoreSetRTC;
|
||||
core->setRotation = _DSCoreSetRotation;
|
||||
core->setRumble = _DSCoreSetRumble;
|
||||
core->busRead8 = _DSCoreBusRead8;
|
||||
|
|
23
src/ds/ds.c
23
src/ds/ds.c
|
@ -18,7 +18,7 @@
|
|||
|
||||
#define SLICE_CYCLES 2048
|
||||
|
||||
mLOG_DEFINE_CATEGORY(DS, "DS");
|
||||
mLOG_DEFINE_CATEGORY(DS, "DS", "ds");
|
||||
|
||||
const uint32_t DS_ARM946ES_FREQUENCY = 0x1FF61FE;
|
||||
const uint32_t DS_ARM7TDMI_FREQUENCY = 0xFFB0FF;
|
||||
|
@ -209,7 +209,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
|
|||
ds->romVf = NULL;
|
||||
DSSlot1SPIInit(ds, NULL);
|
||||
|
||||
ds->stream = NULL;
|
||||
ds->keyCallback = NULL;
|
||||
mCoreCallbacksListInit(&ds->coreCallbacks, 0);
|
||||
|
||||
ds->divEvent.name = "DS Hardware Divide";
|
||||
ds->divEvent.callback = _divide;
|
||||
|
@ -240,6 +242,7 @@ void DSDestroy(struct DS* ds) {
|
|||
DSGXDeinit(&ds->gx);
|
||||
mTimingDeinit(&ds->ds7.timing);
|
||||
mTimingDeinit(&ds->ds9.timing);
|
||||
mCoreCallbacksListDeinit(&ds->coreCallbacks);
|
||||
}
|
||||
|
||||
void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {
|
||||
|
@ -824,16 +827,22 @@ void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq) {
|
|||
}
|
||||
|
||||
void DSFrameStarted(struct DS* ds) {
|
||||
struct mCoreCallbacks* callbacks = ds->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c);
|
||||
if (callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSFrameEnded(struct DS* ds) {
|
||||
struct mCoreCallbacks* callbacks = ds->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&ds->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&ds->coreCallbacks, c);
|
||||
if (callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
}
|
||||
}
|
||||
|
||||
if (ds->stream && ds->stream->postVideoFrame) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <mgba/internal/ds/ds.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_PIPE_SIZE 4
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <mgba/internal/ds/slot1.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) {
|
||||
switch (value >> 6) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <mgba/internal/ds/ds.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) {
|
||||
remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0;
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <mgba-util/math.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
|
||||
const uint32_t redzoneInstruction = 0xE7F0DEF0;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <mgba-util/math.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 _transferEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <mgba/internal/ds/ds.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 _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#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 DSVideoDummyRendererReset(struct DSVideoRenderer* renderer);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <sys/time.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GUI_RUNNER);
|
||||
mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner");
|
||||
mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner", "gui.runner");
|
||||
|
||||
#define FPS_GRANULARITY 120
|
||||
#define FPS_BUFFER_SIZE 3
|
||||
|
@ -434,7 +434,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA);
|
||||
break;
|
||||
case RUNNER_LOAD_STATE:
|
||||
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT);
|
||||
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
break;
|
||||
case RUNNER_SCREENSHOT:
|
||||
mCoreTakeScreenshot(runner->core);
|
||||
|
|
|
@ -953,6 +953,9 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->playingCh4 = !!(*audio->nr52 & 0x0008);
|
||||
audio->enable = GBAudioEnableGetEnable(*audio->nr52);
|
||||
|
||||
LOAD_32LE(when, 0, &state->ch1.nextFrame);
|
||||
mTimingSchedule(audio->timing, &audio->frameEvent, when);
|
||||
|
||||
LOAD_32LE(flags, 0, flagsIn);
|
||||
LOAD_32LE(ch1Flags, 0, &state->ch1.envelope);
|
||||
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags);
|
||||
|
@ -964,7 +967,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags);
|
||||
audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags);
|
||||
LOAD_32LE(when, 0, &state->ch1.nextEvent);
|
||||
mTimingDeschedule(audio->timing, &audio->ch1Event);
|
||||
if (audio->ch1.envelope.dead < 2 && audio->playingCh1) {
|
||||
mTimingSchedule(audio->timing, &audio->ch1Event, when);
|
||||
}
|
||||
|
@ -976,7 +978,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->ch2.control.length = GBSerializedAudioEnvelopeGetLength(ch2Flags);
|
||||
audio->ch2.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch2Flags);
|
||||
LOAD_32LE(when, 0, &state->ch2.nextEvent);
|
||||
mTimingDeschedule(audio->timing, &audio->ch2Event);
|
||||
if (audio->ch2.envelope.dead < 2 && audio->playingCh2) {
|
||||
mTimingSchedule(audio->timing, &audio->ch2Event, when);
|
||||
}
|
||||
|
@ -986,7 +987,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
memcpy(audio->ch3.wavedata32, state->ch3.wavebanks, sizeof(audio->ch3.wavedata32));
|
||||
LOAD_16LE(audio->ch3.length, 0, &state->ch3.length);
|
||||
LOAD_32LE(when, 0, &state->ch3.nextEvent);
|
||||
mTimingDeschedule(audio->timing, &audio->ch3Event);
|
||||
if (audio->playingCh3) {
|
||||
mTimingSchedule(audio->timing, &audio->ch3Event, when);
|
||||
}
|
||||
|
@ -1002,7 +1002,6 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
|||
audio->ch4.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch4Flags);
|
||||
LOAD_32LE(audio->ch4.lfsr, 0, &state->ch4.lfsr);
|
||||
LOAD_32LE(when, 0, &state->ch4.nextEvent);
|
||||
mTimingDeschedule(audio->timing, &audio->ch4Event);
|
||||
if (audio->ch4.envelope.dead < 2 && audio->playingCh4) {
|
||||
mTimingSchedule(audio->timing, &audio->ch4Event, when);
|
||||
}
|
||||
|
@ -1017,6 +1016,5 @@ void GBAudioDeserialize(struct GBAudio* audio, const struct GBSerializedState* s
|
|||
GBAudioPSGDeserialize(audio, &state->audio.psg, &state->audio.flags);
|
||||
uint32_t when;
|
||||
LOAD_32LE(when, 0, &state->audio.nextSample);
|
||||
mTimingDeschedule(audio->timing, &audio->sampleEvent);
|
||||
mTimingSchedule(audio->timing, &audio->sampleEvent, when);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ static bool _GBCoreInit(struct mCore* core) {
|
|||
memset(gbcore->components, 0, sizeof(gbcore->components));
|
||||
LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components);
|
||||
LR35902Init(cpu);
|
||||
mRTCGenericSourceInit(&core->rtc, core);
|
||||
gb->memory.rtc = &core->rtc.d;
|
||||
|
||||
GBVideoSoftwareRendererCreate(&gbcore->renderer);
|
||||
gbcore->renderer.outputBuffer = NULL;
|
||||
|
@ -164,9 +166,14 @@ static size_t _GBCoreGetAudioBufferSize(struct mCore* core) {
|
|||
return gb->audio.samples;
|
||||
}
|
||||
|
||||
static void _GBCoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
static void _GBCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
struct GB* gb = core->board;
|
||||
gb->coreCallbacks = coreCallbacks;
|
||||
*mCoreCallbacksListAppend(&gb->coreCallbacks) = *coreCallbacks;
|
||||
}
|
||||
|
||||
static void _GBCoreClearCoreCallbacks(struct mCore* core) {
|
||||
struct GB* gb = core->board;
|
||||
mCoreCallbacksListClear(&gb->coreCallbacks);
|
||||
}
|
||||
|
||||
static void _GBCoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
||||
|
@ -403,11 +410,6 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
|
|||
GBGetGameCode(core->board, title);
|
||||
}
|
||||
|
||||
static void _GBCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
|
||||
struct GB* gb = core->board;
|
||||
gb->memory.rtc = rtc;
|
||||
}
|
||||
|
||||
static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
|
||||
struct GB* gb = core->board;
|
||||
gb->memory.rotation = rotation;
|
||||
|
@ -595,7 +597,8 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->setAudioBufferSize = _GBCoreSetAudioBufferSize;
|
||||
core->getAudioBufferSize = _GBCoreGetAudioBufferSize;
|
||||
core->setAVStream = _GBCoreSetAVStream;
|
||||
core->setCoreCallbacks = _GBCoreSetCoreCallbacks;
|
||||
core->addCoreCallbacks = _GBCoreAddCoreCallbacks;
|
||||
core->clearCoreCallbacks = _GBCoreClearCoreCallbacks;
|
||||
core->isROM = GBIsROM;
|
||||
core->loadROM = _GBCoreLoadROM;
|
||||
core->loadBIOS = _GBCoreLoadBIOS;
|
||||
|
@ -621,7 +624,6 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->frequency = _GBCoreFrequency;
|
||||
core->getGameTitle = _GBCoreGetGameTitle;
|
||||
core->getGameCode = _GBCoreGetGameCode;
|
||||
core->setRTC = _GBCoreSetRTC;
|
||||
core->setRotation = _GBCoreSetRotation;
|
||||
core->setRumble = _GBCoreSetRumble;
|
||||
core->busRead8 = _GBCoreBusRead8;
|
||||
|
|
|
@ -101,7 +101,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system;
|
||||
|
||||
mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
|
||||
mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
}
|
||||
|
||||
static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -118,5 +118,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system;
|
||||
|
||||
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
|
||||
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
}
|
||||
|
|
57
src/gb/gb.c
57
src/gb/gb.c
|
@ -30,7 +30,7 @@ static const uint8_t _knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66};
|
|||
#define DMG_2_BIOS_CHECKSUM 0x59C8598E
|
||||
#define CGB_BIOS_CHECKSUM 0x41884E46
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GB, "GB");
|
||||
mLOG_DEFINE_CATEGORY(GB, "GB", "gb");
|
||||
|
||||
static void GBInit(void* cpu, struct mCPUComponent* component);
|
||||
static void GBDeinit(struct mCPUComponent* component);
|
||||
|
@ -79,11 +79,11 @@ static void GBInit(void* cpu, struct mCPUComponent* component) {
|
|||
gb->sramVf = NULL;
|
||||
gb->sramRealVf = NULL;
|
||||
|
||||
gb->pristineRom = 0;
|
||||
gb->isPristine = false;
|
||||
gb->pristineRomSize = 0;
|
||||
gb->yankedRomSize = 0;
|
||||
|
||||
gb->coreCallbacks = NULL;
|
||||
mCoreCallbacksListInit(&gb->coreCallbacks, 0);
|
||||
gb->stream = NULL;
|
||||
|
||||
mTimingInit(&gb->timing, &gb->cpu->cycles, &gb->cpu->nextEvent);
|
||||
|
@ -108,24 +108,23 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) {
|
|||
gb->romVf = vf;
|
||||
gb->pristineRomSize = vf->size(vf);
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
gb->isPristine = true;
|
||||
#ifdef _3DS
|
||||
gb->pristineRom = 0;
|
||||
if (gb->pristineRomSize <= romBufferSize) {
|
||||
gb->pristineRom = romBuffer;
|
||||
gb->memory.rom = romBuffer;
|
||||
vf->read(vf, romBuffer, gb->pristineRomSize);
|
||||
}
|
||||
#else
|
||||
gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ);
|
||||
gb->memory.rom = vf->map(vf, gb->pristineRomSize, MAP_READ);
|
||||
#endif
|
||||
if (!gb->pristineRom) {
|
||||
if (!gb->memory.rom) {
|
||||
return false;
|
||||
}
|
||||
gb->yankedRomSize = 0;
|
||||
gb->memory.rom = gb->pristineRom;
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
gb->memory.romSize = gb->pristineRomSize;
|
||||
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
|
||||
GBMBCSwitchBank(&gb->memory, gb->memory.currentBank);
|
||||
GBMBCSwitchBank(gb, gb->memory.currentBank);
|
||||
|
||||
if (gb->cpu) {
|
||||
struct LR35902Core* cpu = gb->cpu;
|
||||
|
@ -263,26 +262,25 @@ void GBSavedataUnmask(struct GB* gb) {
|
|||
|
||||
void GBUnloadROM(struct GB* gb) {
|
||||
// TODO: Share with GBAUnloadROM
|
||||
if (gb->memory.rom && gb->memory.romBase != gb->memory.rom && gb->memory.romBase != gb->pristineRom) {
|
||||
if (gb->memory.rom && gb->memory.romBase != gb->memory.rom && !gb->isPristine) {
|
||||
free(gb->memory.romBase);
|
||||
}
|
||||
if (gb->memory.rom && gb->pristineRom != gb->memory.rom) {
|
||||
if (gb->memory.rom && !gb->isPristine) {
|
||||
if (gb->yankedRomSize) {
|
||||
gb->yankedRomSize = 0;
|
||||
}
|
||||
mappedMemoryFree(gb->memory.rom, GB_SIZE_CART_MAX);
|
||||
gb->memory.rom = gb->pristineRom;
|
||||
}
|
||||
gb->memory.rom = 0;
|
||||
|
||||
if (gb->romVf) {
|
||||
#ifndef _3DS
|
||||
gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize);
|
||||
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
|
||||
#endif
|
||||
gb->romVf->close(gb->romVf);
|
||||
gb->romVf = 0;
|
||||
gb->romVf = NULL;
|
||||
}
|
||||
gb->pristineRom = 0;
|
||||
gb->memory.rom = NULL;
|
||||
gb->isPristine = false;
|
||||
|
||||
GBSavedataUnmask(gb);
|
||||
GBSramDeinit(gb);
|
||||
|
@ -317,14 +315,26 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
|
|||
if (patchedSize > GB_SIZE_CART_MAX) {
|
||||
patchedSize = GB_SIZE_CART_MAX;
|
||||
}
|
||||
gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
|
||||
if (!patch->applyPatch(patch, gb->pristineRom, gb->pristineRomSize, gb->memory.rom, patchedSize)) {
|
||||
mappedMemoryFree(gb->memory.rom, patchedSize);
|
||||
gb->memory.rom = gb->pristineRom;
|
||||
void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX);
|
||||
if (!patch->applyPatch(patch, gb->memory.rom, gb->pristineRomSize, newRom, patchedSize)) {
|
||||
mappedMemoryFree(newRom, GB_SIZE_CART_MAX);
|
||||
return;
|
||||
}
|
||||
if (gb->romVf) {
|
||||
#ifndef _3DS
|
||||
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
|
||||
#endif
|
||||
gb->romVf->close(gb->romVf);
|
||||
gb->romVf = NULL;
|
||||
}
|
||||
gb->isPristine = false;
|
||||
if (gb->memory.romBase == gb->memory.rom) {
|
||||
gb->memory.romBase = newRom;
|
||||
}
|
||||
gb->memory.rom = newRom;
|
||||
gb->memory.romSize = patchedSize;
|
||||
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
}
|
||||
|
||||
void GBDestroy(struct GB* gb) {
|
||||
|
@ -339,6 +349,7 @@ void GBDestroy(struct GB* gb) {
|
|||
GBAudioDeinit(&gb->audio);
|
||||
GBVideoDeinit(&gb->video);
|
||||
GBSIODeinit(&gb->sio);
|
||||
mCoreCallbacksListDeinit(&gb->coreCallbacks);
|
||||
}
|
||||
|
||||
void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
|
||||
|
@ -652,9 +663,6 @@ void GBGetGameTitle(const struct GB* gb, char* out) {
|
|||
if (gb->memory.rom) {
|
||||
cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
}
|
||||
if (gb->pristineRom) {
|
||||
cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
|
||||
}
|
||||
if (!cart) {
|
||||
return;
|
||||
}
|
||||
|
@ -671,9 +679,6 @@ void GBGetGameCode(const struct GB* gb, char* out) {
|
|||
if (gb->memory.rom) {
|
||||
cart = (const struct GBCartridge*) &gb->memory.rom[0x100];
|
||||
}
|
||||
if (gb->pristineRom) {
|
||||
cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100];
|
||||
}
|
||||
if (!cart) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <mgba/internal/gb/sio.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[] = {
|
||||
[REG_JOYP] = "JOYP",
|
||||
|
|
35
src/gb/mbc.c
35
src/gb/mbc.c
|
@ -6,11 +6,12 @@
|
|||
#include <mgba/internal/gb/mbc.h>
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/memory.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) {
|
||||
UNUSED(gb);
|
||||
|
@ -28,18 +29,21 @@ static void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
|
|||
static void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
|
||||
static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
|
||||
|
||||
void GBMBCSwitchBank(struct GBMemory* memory, int bank) {
|
||||
void GBMBCSwitchBank(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_CART_BANK0;
|
||||
if (bankStart + GB_SIZE_CART_BANK0 > memory->romSize) {
|
||||
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
|
||||
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
|
||||
bankStart &= (memory->romSize - 1);
|
||||
bankStart &= (gb->memory.romSize - 1);
|
||||
bank = bankStart / GB_SIZE_CART_BANK0;
|
||||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
}
|
||||
memory->romBank = &memory->rom[bankStart];
|
||||
memory->currentBank = bank;
|
||||
gb->memory.romBank = &gb->memory.rom[bankStart];
|
||||
gb->memory.currentBank = bank;
|
||||
if (gb->cpu->pc < GB_BASE_VRAM) {
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank) {
|
||||
|
@ -249,12 +253,12 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(memory, bank | (memory->currentBank & 0x60));
|
||||
GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60));
|
||||
break;
|
||||
case 0x2:
|
||||
bank &= 3;
|
||||
if (!memory->mbcState.mbc1.mode) {
|
||||
GBMBCSwitchBank(memory, (bank << 5) | (memory->currentBank & 0x1F));
|
||||
GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F));
|
||||
} else {
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
}
|
||||
|
@ -262,7 +266,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x3:
|
||||
memory->mbcState.mbc1.mode = value & 1;
|
||||
if (memory->mbcState.mbc1.mode) {
|
||||
GBMBCSwitchBank(memory, memory->currentBank & 0x1F);
|
||||
GBMBCSwitchBank(gb, memory->currentBank & 0x1F);
|
||||
} else {
|
||||
GBMBCSwitchSramBank(gb, 0);
|
||||
}
|
||||
|
@ -297,7 +301,7 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
|
@ -328,7 +332,7 @@ void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value < 4) {
|
||||
|
@ -372,11 +376,11 @@ void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
break;
|
||||
case 0x2:
|
||||
bank = (memory->currentBank & 0x100) | value;
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x3:
|
||||
bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
|
@ -402,11 +406,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
|
||||
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x7F;
|
||||
switch (address >> 13) {
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value < 0x10) {
|
||||
|
@ -616,7 +619,7 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(memory, bank);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#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);
|
||||
|
||||
|
@ -614,7 +614,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memory->wramCurrentBank = state->memory.wramCurrentBank;
|
||||
memory->sramCurrentBank = state->memory.sramCurrentBank;
|
||||
|
||||
GBMBCSwitchBank(memory, memory->currentBank);
|
||||
GBMBCSwitchBank(gb, memory->currentBank);
|
||||
GBMemorySwitchWramBank(memory, memory->wramCurrentBank);
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
|
||||
|
@ -630,12 +630,10 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
|
||||
uint32_t when;
|
||||
LOAD_32LE(when, 0, &state->memory.dmaNext);
|
||||
mTimingDeschedule(&gb->timing, &memory->dmaEvent);
|
||||
if (memory->dmaRemaining) {
|
||||
mTimingSchedule(&gb->timing, &memory->dmaEvent, when);
|
||||
}
|
||||
LOAD_32LE(when, 0, &state->memory.hdmaNext);
|
||||
mTimingDeschedule(&gb->timing, &memory->hdmaEvent);
|
||||
if (memory->hdmaRemaining) {
|
||||
mTimingSchedule(&gb->timing, &memory->hdmaEvent, when);
|
||||
}
|
||||
|
@ -651,14 +649,15 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
}
|
||||
|
||||
void _pristineCow(struct GB* gb) {
|
||||
if (gb->memory.rom != gb->pristineRom) {
|
||||
if (!gb->isPristine) {
|
||||
return;
|
||||
}
|
||||
gb->memory.rom = anonymousMemoryMap(GB_SIZE_CART_MAX);
|
||||
memcpy(gb->memory.rom, gb->pristineRom, gb->memory.romSize);
|
||||
memset(((uint8_t*) gb->memory.rom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize);
|
||||
if (gb->pristineRom == gb->memory.romBase) {
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
void* newRom = anonymousMemoryMap(GB_SIZE_CART_MAX);
|
||||
memcpy(newRom, gb->memory.rom, gb->memory.romSize);
|
||||
memset(((uint8_t*) newRom) + gb->memory.romSize, 0xFF, GB_SIZE_CART_MAX - gb->memory.romSize);
|
||||
if (gb->memory.rom == gb->memory.romBase) {
|
||||
gb->memory.romBase = newRom;
|
||||
}
|
||||
GBMBCSwitchBank(&gb->memory, gb->memory.currentBank);
|
||||
gb->memory.rom = newRom;
|
||||
GBMBCSwitchBank(gb, gb->memory.currentBank);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <mgba/internal/gb/timer.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_VERSION = 0x00000001;
|
||||
|
@ -144,6 +144,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
if (error) {
|
||||
return false;
|
||||
}
|
||||
gb->timing.root = NULL;
|
||||
|
||||
gb->cpu->a = state->cpu.a;
|
||||
gb->cpu->f.packed = state->cpu.f;
|
||||
|
@ -170,14 +171,13 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
|
||||
uint32_t when;
|
||||
LOAD_32LE(when, 0, &state->cpu.eiPending);
|
||||
mTimingDeschedule(&gb->timing, &gb->eiPending);
|
||||
if (GBSerializedCpuFlagsIsEiPending(flags)) {
|
||||
mTimingSchedule(&gb->timing, &gb->eiPending, when);
|
||||
}
|
||||
|
||||
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
|
||||
LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
|
||||
LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles);
|
||||
gb->timing.root = NULL;
|
||||
|
||||
gb->model = state->model;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <mgba/internal/gb/io.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] = {
|
||||
512,
|
||||
|
|
|
@ -109,13 +109,11 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s
|
|||
|
||||
uint32_t when;
|
||||
LOAD_32LE(when, 0, &state->timer.nextEvent);
|
||||
mTimingDeschedule(&timer->p->timing, &timer->event);
|
||||
mTimingSchedule(&timer->p->timing, &timer->event, when);
|
||||
|
||||
GBSerializedTimerFlags flags;
|
||||
LOAD_32LE(flags, 0, &state->timer.flags);
|
||||
|
||||
mTimingDeschedule(&timer->p->timing, &timer->irq);
|
||||
if (GBSerializedTimerFlagsIsIrqPending(flags)) {
|
||||
LOAD_32LE(when, 0, &state->timer.nextIRQ);
|
||||
mTimingSchedule(&timer->p->timing, &timer->irq, when);
|
||||
|
|
|
@ -129,9 +129,12 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
}
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
||||
|
||||
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||
if (callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
||||
|
@ -244,9 +247,12 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
||||
}
|
||||
|
||||
struct mCoreCallbacks* callbacks = video->p->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||
if (callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
}
|
||||
}
|
||||
|
||||
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||
|
@ -550,12 +556,10 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
|
|||
}
|
||||
|
||||
uint32_t when;
|
||||
mTimingDeschedule(&video->p->timing, &video->modeEvent);
|
||||
if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) {
|
||||
LOAD_32LE(when, 0, &state->video.nextMode);
|
||||
mTimingSchedule(&video->p->timing, &video->modeEvent, when);
|
||||
}
|
||||
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||
if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) {
|
||||
LOAD_32LE(when, 0, &state->video.nextFrame);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, when);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#define blip_add_delta blip_add_delta_fast
|
||||
#endif
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio");
|
||||
mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio");
|
||||
|
||||
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
||||
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
|
||||
|
@ -344,7 +344,6 @@ void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState
|
|||
|
||||
uint32_t when;
|
||||
LOAD_32(when, 0, &state->audio.nextSample);
|
||||
mTimingDeschedule(&audio->p->timing, &audio->sampleEvent);
|
||||
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, when);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
const uint32_t GBA_BIOS_CHECKSUM = 0xBAAE187F;
|
||||
const uint32_t GBA_DS_BIOS_CHECKSUM = 0xBAAE1880;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_BIOS, "GBA BIOS");
|
||||
mLOG_DEFINE_CATEGORY(GBA_BIOS, "GBA BIOS", "gba.bios");
|
||||
|
||||
static void _unLz77(struct GBA* gba, int width);
|
||||
static void _unHuffman(struct GBA* gba);
|
||||
|
|
|
@ -62,6 +62,8 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
memset(gbacore->components, 0, sizeof(gbacore->components));
|
||||
ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components);
|
||||
ARMInit(cpu);
|
||||
mRTCGenericSourceInit(&core->rtc, core);
|
||||
gba->rtcSource = &core->rtc.d;
|
||||
|
||||
GBAVideoSoftwareRendererCreate(&gbacore->renderer);
|
||||
gbacore->renderer.outputBuffer = NULL;
|
||||
|
@ -194,9 +196,14 @@ static size_t _GBACoreGetAudioBufferSize(struct mCore* core) {
|
|||
return gba->audio.samples;
|
||||
}
|
||||
|
||||
static void _GBACoreSetCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
static void _GBACoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->coreCallbacks = coreCallbacks;
|
||||
*mCoreCallbacksListAppend(&gba->coreCallbacks) = *coreCallbacks;
|
||||
}
|
||||
|
||||
static void _GBACoreClearCoreCallbacks(struct mCore* core) {
|
||||
struct GBA* gba = core->board;
|
||||
mCoreCallbacksListClear(&gba->coreCallbacks);
|
||||
}
|
||||
|
||||
static void _GBACoreSetAVStream(struct mCore* core, struct mAVStream* stream) {
|
||||
|
@ -331,7 +338,7 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
#endif
|
||||
|
||||
ARMReset(core->cpu);
|
||||
if (core->opts.skipBios && gba->pristineRom) {
|
||||
if (core->opts.skipBios && gba->isPristine) {
|
||||
GBASkipBIOS(core->board);
|
||||
}
|
||||
}
|
||||
|
@ -415,11 +422,6 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
|
|||
GBAGetGameCode(core->board, title);
|
||||
}
|
||||
|
||||
static void _GBACoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->rtcSource = rtc;
|
||||
}
|
||||
|
||||
static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->rotationSource = rotation;
|
||||
|
@ -608,7 +610,8 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->getAudioChannel = _GBACoreGetAudioChannel;
|
||||
core->setAudioBufferSize = _GBACoreSetAudioBufferSize;
|
||||
core->getAudioBufferSize = _GBACoreGetAudioBufferSize;
|
||||
core->setCoreCallbacks = _GBACoreSetCoreCallbacks;
|
||||
core->addCoreCallbacks = _GBACoreAddCoreCallbacks;
|
||||
core->clearCoreCallbacks = _GBACoreClearCoreCallbacks;
|
||||
core->setAVStream = _GBACoreSetAVStream;
|
||||
core->isROM = GBAIsROM;
|
||||
core->loadROM = _GBACoreLoadROM;
|
||||
|
@ -635,7 +638,6 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->frequency = _GBACoreFrequency;
|
||||
core->getGameTitle = _GBACoreGetGameTitle;
|
||||
core->getGameCode = _GBACoreGetGameCode;
|
||||
core->setRTC = _GBACoreSetRTC;
|
||||
core->setRotation = _GBACoreSetRotation;
|
||||
core->setRumble = _GBACoreSetRumble;
|
||||
core->busRead8 = _GBACoreBusRead8;
|
||||
|
|
|
@ -100,7 +100,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
||||
|
||||
mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
|
||||
mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
}
|
||||
|
||||
// TODO: Put back rewind
|
||||
|
@ -119,5 +119,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
||||
|
||||
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
|
||||
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA, "GBA");
|
||||
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug");
|
||||
mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba");
|
||||
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
|
||||
|
||||
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
|
||||
|
||||
|
@ -91,7 +91,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
gba->keyCallback = NULL;
|
||||
gba->stopCallback = NULL;
|
||||
gba->stopCallback = NULL;
|
||||
gba->coreCallbacks = NULL;
|
||||
mCoreCallbacksListInit(&gba->coreCallbacks, 0);
|
||||
|
||||
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
|
||||
|
||||
|
@ -104,7 +104,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
|
||||
gba->performingDMA = false;
|
||||
|
||||
gba->pristineRom = 0;
|
||||
gba->isPristine = false;
|
||||
gba->pristineRomSize = 0;
|
||||
gba->yankedRomSize = 0;
|
||||
|
||||
|
@ -112,22 +112,22 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
}
|
||||
|
||||
void GBAUnloadROM(struct GBA* gba) {
|
||||
if (gba->memory.rom && gba->pristineRom != gba->memory.rom) {
|
||||
if (gba->memory.rom && !gba->isPristine) {
|
||||
if (gba->yankedRomSize) {
|
||||
gba->yankedRomSize = 0;
|
||||
}
|
||||
mappedMemoryFree(gba->memory.rom, SIZE_CART0);
|
||||
}
|
||||
gba->memory.rom = 0;
|
||||
|
||||
if (gba->romVf) {
|
||||
#ifndef _3DS
|
||||
gba->romVf->unmap(gba->romVf, gba->pristineRom, gba->pristineRomSize);
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
|
||||
#endif
|
||||
gba->romVf->close(gba->romVf);
|
||||
gba->romVf = 0;
|
||||
gba->romVf = NULL;
|
||||
}
|
||||
gba->pristineRom = 0;
|
||||
gba->memory.rom = NULL;
|
||||
gba->isPristine = false;
|
||||
|
||||
GBASavedataDeinit(&gba->memory.savedata);
|
||||
if (gba->memory.savedata.realVf) {
|
||||
|
@ -152,6 +152,7 @@ void GBADestroy(struct GBA* gba) {
|
|||
GBASIODeinit(&gba->sio);
|
||||
gba->rr = 0;
|
||||
mTimingDeinit(&gba->timing);
|
||||
mCoreCallbacksListDeinit(&gba->coreCallbacks);
|
||||
}
|
||||
|
||||
void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
|
||||
|
@ -291,23 +292,23 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
|
|||
if (gba->pristineRomSize > SIZE_WORKING_RAM) {
|
||||
gba->pristineRomSize = SIZE_WORKING_RAM;
|
||||
}
|
||||
gba->isPristine = true;
|
||||
#ifdef _3DS
|
||||
gba->pristineRom = 0;
|
||||
if (gba->pristineRomSize <= romBufferSize) {
|
||||
gba->pristineRom = romBuffer;
|
||||
gba->memory.wram = romBuffer;
|
||||
vf->read(vf, romBuffer, gba->pristineRomSize);
|
||||
}
|
||||
#else
|
||||
gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ);
|
||||
gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ);
|
||||
#endif
|
||||
if (!gba->pristineRom) {
|
||||
if (!gba->memory.wram) {
|
||||
mLOG(GBA, WARN, "Couldn't map ROM");
|
||||
return false;
|
||||
}
|
||||
gba->yankedRomSize = 0;
|
||||
gba->memory.romSize = 0;
|
||||
gba->memory.romMask = 0;
|
||||
gba->romCrc32 = doCrc32(gba->pristineRom, gba->pristineRomSize);
|
||||
gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -322,21 +323,20 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
if (gba->pristineRomSize > SIZE_CART0) {
|
||||
gba->pristineRomSize = SIZE_CART0;
|
||||
}
|
||||
gba->isPristine = true;
|
||||
#ifdef _3DS
|
||||
gba->pristineRom = 0;
|
||||
if (gba->pristineRomSize <= romBufferSize) {
|
||||
gba->pristineRom = romBuffer;
|
||||
gba->memory.rom = romBuffer;
|
||||
vf->read(vf, romBuffer, gba->pristineRomSize);
|
||||
}
|
||||
#else
|
||||
gba->pristineRom = vf->map(vf, gba->pristineRomSize, MAP_READ);
|
||||
gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ);
|
||||
#endif
|
||||
if (!gba->pristineRom) {
|
||||
if (!gba->memory.rom) {
|
||||
mLOG(GBA, WARN, "Couldn't map ROM");
|
||||
return false;
|
||||
}
|
||||
gba->yankedRomSize = 0;
|
||||
gba->memory.rom = gba->pristineRom;
|
||||
gba->memory.romSize = gba->pristineRomSize;
|
||||
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
|
||||
gba->memory.mirroring = false;
|
||||
|
@ -389,12 +389,21 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
|
|||
if (!patchedSize || patchedSize > SIZE_CART0) {
|
||||
return;
|
||||
}
|
||||
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
|
||||
if (!patch->applyPatch(patch, gba->pristineRom, gba->pristineRomSize, gba->memory.rom, patchedSize)) {
|
||||
mappedMemoryFree(gba->memory.rom, patchedSize);
|
||||
gba->memory.rom = gba->pristineRom;
|
||||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||
if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) {
|
||||
mappedMemoryFree(newRom, SIZE_CART0);
|
||||
return;
|
||||
}
|
||||
if (gba->romVf) {
|
||||
#ifndef _3DS
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
|
||||
#endif
|
||||
gba->romVf->close(gba->romVf);
|
||||
gba->romVf = NULL;
|
||||
}
|
||||
gba->isPristine = false;
|
||||
gba->memory.rom = newRom;
|
||||
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
|
||||
gba->memory.romSize = patchedSize;
|
||||
gba->memory.romMask = SIZE_CART0 - 1;
|
||||
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
|
||||
|
@ -539,8 +548,8 @@ void GBAGetGameTitle(const struct GBA* gba, char* out) {
|
|||
memcpy(out, &((struct GBACartridge*) gba->memory.rom)->title, 12);
|
||||
return;
|
||||
}
|
||||
if (gba->pristineRom) {
|
||||
memcpy(out, &((struct GBACartridge*) gba->pristineRom)->title, 12);
|
||||
if (gba->isPristine && gba->memory.wram) {
|
||||
memcpy(out, &((struct GBACartridge*) gba->memory.wram)->title, 12);
|
||||
return;
|
||||
}
|
||||
strncpy(out, "(BIOS)", 12);
|
||||
|
@ -567,6 +576,10 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) {
|
|||
// TODO: More sensible category?
|
||||
mLOG(GBA, WARN, "Illegal opcode: %08x", opcode);
|
||||
}
|
||||
if (cpu->executionMode == MODE_THUMB && (opcode & 0xFFC0) == 0xE800) {
|
||||
mLOG(GBA, DEBUG, "Hit Wii U VC opcode: %08x", opcode);
|
||||
return;
|
||||
}
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (gba->debugger) {
|
||||
struct mDebuggerEntryInfo info = {
|
||||
|
@ -623,9 +636,12 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
|
|||
void GBAFrameStarted(struct GBA* gba) {
|
||||
UNUSED(gba);
|
||||
|
||||
struct mCoreCallbacks* callbacks = gba->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
if (callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,9 +672,12 @@ void GBAFrameEnded(struct GBA* gba) {
|
|||
GBAHardwarePlayerUpdate(gba);
|
||||
}
|
||||
|
||||
struct mCoreCallbacks* callbacks = gba->coreCallbacks;
|
||||
if (callbacks && callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
if (callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <mgba-util/formatting.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 };
|
||||
|
||||
|
@ -554,6 +554,7 @@ uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uin
|
|||
gbp->p->p->rumble->setRumble(gbp->p->p->rumble, (rx & mask) == 0x22);
|
||||
}
|
||||
}
|
||||
mTimingDeschedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent);
|
||||
mTimingSchedule(&gbp->p->p->timing, &gbp->p->gbpNextEvent, 2048);
|
||||
}
|
||||
value &= 0x78FB;
|
||||
|
|
13
src/gba/io.c
13
src/gba/io.c
|
@ -11,7 +11,7 @@
|
|||
#include <mgba/internal/gba/rr/rr.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[] = {
|
||||
// Video
|
||||
|
@ -251,8 +251,8 @@ static const int _isRSpecialRegister[REG_MAX >> 1] = {
|
|||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// Audio
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 1, 0,
|
||||
0, 0, 1, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
|
@ -292,9 +292,9 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = {
|
|||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// Audio
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 0, 1, 0, 1, 0,
|
||||
1, 1, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0, 0, 0, 0,
|
||||
// DMA
|
||||
|
@ -960,7 +960,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
gba->timers[i].lastEvent = when + mTimingCurrentTime(&gba->timing);
|
||||
}
|
||||
LOAD_32(when, 0, &state->timers[i].nextEvent);
|
||||
mTimingDeschedule(&gba->timing, &gba->timers[i].event);
|
||||
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
|
||||
mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#define IDLE_LOOP_THRESHOLD 10000
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory");
|
||||
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory", "gba.memory");
|
||||
|
||||
static void _pristineCow(struct GBA* gba);
|
||||
static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
|
||||
|
@ -98,13 +98,12 @@ void GBAMemoryDeinit(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBAMemoryReset(struct GBA* gba) {
|
||||
if (gba->memory.wram) {
|
||||
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
|
||||
}
|
||||
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
|
||||
if (gba->pristineRom && !gba->memory.rom) {
|
||||
// Multiboot
|
||||
memcpy(gba->memory.wram, gba->pristineRom, gba->pristineRomSize);
|
||||
if (gba->memory.rom || gba->memory.fullBios) {
|
||||
// Not multiboot
|
||||
if (gba->memory.wram) {
|
||||
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
|
||||
}
|
||||
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
|
||||
}
|
||||
|
||||
if (gba->memory.iwram) {
|
||||
|
@ -300,9 +299,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
cpu->memory.activeMask = 0;
|
||||
if (gba->yankedRomSize || !gba->hardCrash) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address);
|
||||
} else if (gba->coreCallbacks && gba->coreCallbacks->coreCrashed) {
|
||||
} else if (mCoreCallbacksListSize(&gba->coreCallbacks)) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Jumped to invalid address: %08X", address);
|
||||
gba->coreCallbacks->coreCrashed(gba->coreCallbacks->context);
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
if (callbacks->coreCrashed) {
|
||||
callbacks->coreCrashed(callbacks->context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mLOG(GBA_MEM, FATAL, "Jumped to invalid address: %08X", address);
|
||||
}
|
||||
|
@ -1537,10 +1542,19 @@ void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedSt
|
|||
}
|
||||
|
||||
void _pristineCow(struct GBA* gba) {
|
||||
if (gba->memory.rom != gba->pristineRom) {
|
||||
if (!gba->isPristine) {
|
||||
return;
|
||||
}
|
||||
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
|
||||
memcpy(gba->memory.rom, gba->pristineRom, gba->memory.romSize);
|
||||
memset(((uint8_t*) gba->memory.rom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize);
|
||||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||
memcpy(newRom, gba->memory.rom, gba->memory.romSize);
|
||||
memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize);
|
||||
if (gba->romVf) {
|
||||
#ifndef _3DS
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
|
||||
#endif
|
||||
gba->romVf->close(gba->romVf);
|
||||
gba->romVf = NULL;
|
||||
}
|
||||
gba->memory.rom = newRom;
|
||||
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
|
||||
}
|
||||
|
|
|
@ -381,7 +381,6 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
SPRITE_NORMAL_LOOP(256, OBJWIN);
|
||||
} else if (mosaicH > 1) {
|
||||
if (objwinSlowPath) {
|
||||
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
|
||||
SPRITE_MOSAIC_LOOP(256, NORMAL_OBJWIN);
|
||||
} else {
|
||||
SPRITE_MOSAIC_LOOP(256, NORMAL);
|
||||
|
|
|
@ -104,6 +104,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
|
|||
}
|
||||
softwareRenderer->objExtPalette = NULL;
|
||||
softwareRenderer->objExtVariantPalette = NULL;
|
||||
softwareRenderer->blendDirty = false;
|
||||
_updatePalettes(softwareRenderer);
|
||||
|
||||
softwareRenderer->blda = 0;
|
||||
|
@ -270,11 +271,14 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender
|
|||
value &= 0x1F1F;
|
||||
break;
|
||||
case REG_BLDY:
|
||||
softwareRenderer->bldy = value & 0x1F;
|
||||
if (softwareRenderer->bldy > 0x10) {
|
||||
softwareRenderer->bldy = 0x10;
|
||||
value &= 0x1F;
|
||||
if (value > 0x10) {
|
||||
value = 0x10;
|
||||
}
|
||||
if (softwareRenderer->bldy != value) {
|
||||
softwareRenderer->bldy = value;
|
||||
softwareRenderer->blendDirty = true;
|
||||
}
|
||||
_updatePalettes(softwareRenderer);
|
||||
break;
|
||||
case REG_WIN0H:
|
||||
softwareRenderer->winN[0].h.end = value;
|
||||
|
@ -509,6 +513,10 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
|
|||
GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
|
||||
int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
|
||||
softwareRenderer->d.vramOBJ[0] = objVramBase;
|
||||
if (softwareRenderer->blendDirty) {
|
||||
_updatePalettes(softwareRenderer);
|
||||
softwareRenderer->blendDirty = false;
|
||||
}
|
||||
|
||||
int w;
|
||||
unsigned priority;
|
||||
|
@ -688,7 +696,7 @@ static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer*
|
|||
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
|
||||
|
||||
if (oldEffect != renderer->blendEffect) {
|
||||
_updatePalettes(renderer);
|
||||
renderer->blendDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <mgba/core/serialize.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) {
|
||||
if (!gba || !gba->rr) {
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#define EEPROM_SETTLE_CYCLES 115000
|
||||
#define CLEANUP_THRESHOLD 15
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");
|
||||
mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata", "gba.savedata");
|
||||
|
||||
static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
|
||||
static void _flashErase(struct GBASavedata* savedata);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
||||
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate");
|
||||
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");
|
||||
|
||||
struct GBABundledState {
|
||||
struct GBASerializedState* state;
|
||||
|
@ -146,7 +146,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
if (error) {
|
||||
return false;
|
||||
}
|
||||
LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles);
|
||||
gba->timing.root = NULL;
|
||||
size_t i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include <mgba/internal/gba/gba.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] = {
|
||||
{ 38326, 73003, 107680, 142356 },
|
||||
|
|
|
@ -155,9 +155,6 @@ void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struc
|
|||
timer->lastEvent = timing->masterCycles + cpu->cycles;
|
||||
} else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
|
||||
mTimingDeschedule(timing, &timer->event);
|
||||
if (!GBATimerFlagsIsCountUp(timer->flags)) {
|
||||
*io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
|
||||
}
|
||||
} else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
|
||||
mTimingDeschedule(timing, &timer->event);
|
||||
mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#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 GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer);
|
||||
|
@ -338,7 +338,6 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
|||
} else {
|
||||
video->event.callback = _startHblank;
|
||||
}
|
||||
mTimingDeschedule(&video->p->timing, &video->event);
|
||||
mTimingSchedule(&video->p->timing, &video->event, when);
|
||||
|
||||
LOAD_16(video->vcount, REG_VCOUNT, state->io);
|
||||
|
|
|
@ -52,7 +52,7 @@ DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;)
|
|||
|
||||
#define DEFINE_LD_DECODER_LR35902_MEM(NAME, REG) \
|
||||
DEFINE_DECODER_LR35902(LD ## NAME ## _ ## REG, info->mnemonic = LR35902_MN_LD; \
|
||||
info->op1.reg = LR35902_REG_ ## A; \
|
||||
info->op1.reg = LR35902_REG_ ## NAME; \
|
||||
info->op2.reg = LR35902_REG_ ## REG; \
|
||||
info->op2.flags = LR35902_OP_FLAG_MEMORY;)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <mgba-util/vfs.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(OPENGL);
|
||||
mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL");
|
||||
mLOG_DEFINE_CATEGORY(OPENGL, "OpenGL", "video.ogl");
|
||||
|
||||
#define MAX_PASSES 8
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <QTimer>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
AssetView::AssetView(GameController* controller, QWidget* parent)
|
||||
|
@ -24,7 +26,7 @@ AssetView::AssetView(GameController* controller, QWidget* parent)
|
|||
}
|
||||
|
||||
void AssetView::updateTiles(bool force) {
|
||||
if (!m_controller->thread() || !m_controller->thread()->core) {
|
||||
if (!m_controller->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -51,3 +53,47 @@ void AssetView::resizeEvent(QResizeEvent*) {
|
|||
void AssetView::showEvent(QShowEvent*) {
|
||||
updateTiles(true);
|
||||
}
|
||||
|
||||
void AssetView::compositeTile(unsigned tileId, void* buffer, size_t stride, size_t x, size_t y, int depth) {
|
||||
const uint8_t* tile = mTileCacheGetRawTile(m_tileCache.get(), tileId);
|
||||
uint8_t* pixels = static_cast<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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ Q_OBJECT
|
|||
public:
|
||||
AssetView(GameController* controller, QWidget* parent = nullptr);
|
||||
|
||||
void compositeTile(unsigned tileId, void* image, size_t stride, size_t x, size_t y, int depth = 8);
|
||||
|
||||
protected slots:
|
||||
void updateTiles(bool force = false);
|
||||
|
||||
|
|
|
@ -46,19 +46,19 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
|
|||
enterCheat(GBA_CHEAT_AUTODETECT);
|
||||
});
|
||||
|
||||
add = new QPushButton("Add GameShark");
|
||||
add = new QPushButton(tr("Add GameShark"));
|
||||
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
|
||||
connect(add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GBA_CHEAT_GAMESHARK);
|
||||
});
|
||||
|
||||
add = new QPushButton("Add Pro Action Replay");
|
||||
add = new QPushButton(tr("Add Pro Action Replay"));
|
||||
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
|
||||
connect(add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GBA_CHEAT_PRO_ACTION_REPLAY);
|
||||
});
|
||||
|
||||
add = new QPushButton("Add CodeBreaker");
|
||||
add = new QPushButton(tr("Add CodeBreaker"));
|
||||
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
|
||||
connect(add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GBA_CHEAT_CODEBREAKER);
|
||||
|
@ -71,13 +71,13 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
|
|||
enterCheat(GB_CHEAT_AUTODETECT);
|
||||
});
|
||||
|
||||
add = new QPushButton("Add GameShark");
|
||||
add = new QPushButton(tr("Add GameShark"));
|
||||
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
|
||||
connect(add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GB_CHEAT_GAMESHARK);
|
||||
});
|
||||
|
||||
add = new QPushButton("Add GameGenie");
|
||||
add = new QPushButton(tr("Add GameGenie"));
|
||||
m_ui.gridLayout->addWidget(add, m_ui.gridLayout->rowCount(), 2, 1, 2);
|
||||
connect(add, &QPushButton::clicked, [this]() {
|
||||
enterCheat(GB_CHEAT_GAME_GENIE);
|
||||
|
|
|
@ -110,6 +110,7 @@ ConfigController::ConfigController(QObject* parent)
|
|||
m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL;
|
||||
m_opts.rewindEnable = false;
|
||||
m_opts.rewindBufferCapacity = 300;
|
||||
m_opts.rewindSave = true;
|
||||
m_opts.useBios = true;
|
||||
m_opts.suspendScreensaver = true;
|
||||
m_opts.lockAspectRatio = true;
|
||||
|
|
|
@ -29,11 +29,10 @@ using namespace QGBA;
|
|||
|
||||
static GBAApp* g_app = nullptr;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(QT, "Qt");
|
||||
mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt");
|
||||
|
||||
GBAApp::GBAApp(int& argc, char* argv[])
|
||||
: QApplication(argc, argv)
|
||||
, m_windows{}
|
||||
, m_db(nullptr)
|
||||
{
|
||||
g_app = this;
|
||||
|
@ -80,10 +79,10 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
|||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
|
||||
}
|
||||
Window* w = new Window(&m_configController);
|
||||
connect(w, &Window::destroyed, [this]() {
|
||||
m_windows[0] = nullptr;
|
||||
connect(w, &Window::destroyed, [this, w]() {
|
||||
m_windows.removeAll(w);
|
||||
});
|
||||
m_windows[0] = w;
|
||||
m_windows.append(w);
|
||||
|
||||
if (loaded) {
|
||||
w->argumentsPassed(&args);
|
||||
|
@ -121,15 +120,15 @@ bool GBAApp::event(QEvent* event) {
|
|||
}
|
||||
|
||||
Window* GBAApp::newWindow() {
|
||||
if (m_multiplayer.attached() >= MAX_GBAS) {
|
||||
if (m_windows.count() >= MAX_GBAS) {
|
||||
return nullptr;
|
||||
}
|
||||
Window* w = new Window(&m_configController, m_multiplayer.attached());
|
||||
int windowId = m_multiplayer.attached();
|
||||
connect(w, &Window::destroyed, [this, windowId]() {
|
||||
m_windows[windowId] = nullptr;
|
||||
connect(w, &Window::destroyed, [this, w]() {
|
||||
m_windows.removeAll(w);
|
||||
});
|
||||
m_windows[windowId] = w;
|
||||
m_windows.append(w);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose);
|
||||
w->loadConfig();
|
||||
w->show();
|
||||
|
@ -142,27 +141,27 @@ GBAApp* GBAApp::app() {
|
|||
return g_app;
|
||||
}
|
||||
|
||||
void GBAApp::pauseAll(QList<int>* paused) {
|
||||
for (int i = 0; i < MAX_GBAS; ++i) {
|
||||
if (!m_windows[i] || !m_windows[i]->controller()->isLoaded() || m_windows[i]->controller()->isPaused()) {
|
||||
void GBAApp::pauseAll(QList<Window*>* paused) {
|
||||
for (auto& window : m_windows) {
|
||||
if (!window->controller()->isLoaded() || window->controller()->isPaused()) {
|
||||
continue;
|
||||
}
|
||||
m_windows[i]->controller()->setPaused(true);
|
||||
paused->append(i);
|
||||
window->controller()->setPaused(true);
|
||||
paused->append(window);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAApp::continueAll(const QList<int>* paused) {
|
||||
for (int i : *paused) {
|
||||
m_windows[i]->controller()->setPaused(false);
|
||||
void GBAApp::continueAll(const QList<Window*>& paused) {
|
||||
for (auto& window : paused) {
|
||||
window->controller()->setPaused(false);
|
||||
}
|
||||
}
|
||||
|
||||
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
QList<int> paused;
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||
continueAll(&paused);
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
|
@ -170,10 +169,10 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr
|
|||
}
|
||||
|
||||
QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
QList<int> paused;
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||
continueAll(&paused);
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
|
@ -181,10 +180,10 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr
|
|||
}
|
||||
|
||||
QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
|
||||
QList<int> paused;
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory"));
|
||||
continueAll(&paused);
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
||||
}
|
||||
|
@ -249,14 +248,14 @@ GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& capt
|
|||
}
|
||||
|
||||
int GBAApp::FileDialog::exec() {
|
||||
QList<int> paused;
|
||||
QList<Window*> paused;
|
||||
m_app->pauseAll(&paused);
|
||||
bool didAccept = QFileDialog::exec() == QDialog::Accepted;
|
||||
QStringList filenames = selectedFiles();
|
||||
if (!filenames.isEmpty()) {
|
||||
m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path());
|
||||
}
|
||||
m_app->continueAll(&paused);
|
||||
m_app->continueAll(paused);
|
||||
return didAccept;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ private:
|
|||
};
|
||||
#endif
|
||||
|
||||
|
||||
class GBAApp : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -78,11 +77,11 @@ private:
|
|||
|
||||
Window* newWindowInternal();
|
||||
|
||||
void pauseAll(QList<int>* paused);
|
||||
void continueAll(const QList<int>* paused);
|
||||
void pauseAll(QList<Window*>* paused);
|
||||
void continueAll(const QList<Window*>& paused);
|
||||
|
||||
ConfigController m_configController;
|
||||
Window* m_windows[MAX_GBAS];
|
||||
QList<Window*> m_windows;
|
||||
MultiplayerController m_multiplayer;
|
||||
|
||||
NoIntroDB* m_db;
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <mgba/core/tile-cache.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/interface.h>
|
||||
#include <mgba/internal/gba/bios.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/gba/core.h>
|
||||
#include <mgba/internal/gba/renderers/tile-cache.h>
|
||||
|
@ -70,8 +69,8 @@ GameController::GameController(QObject* parent)
|
|||
, m_stateSlot(1)
|
||||
, m_backupLoadState(nullptr)
|
||||
, m_backupSaveState(nullptr)
|
||||
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS)
|
||||
, m_loadStateFlags(SAVESTATE_SCREENSHOT)
|
||||
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
|
||||
, m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
|
||||
, m_override(nullptr)
|
||||
{
|
||||
#ifdef M_CORE_GBA
|
||||
|
@ -90,8 +89,6 @@ GameController::GameController(QObject* parent)
|
|||
|
||||
m_threadContext.startCallback = [](mCoreThread* context) {
|
||||
GameController* controller = static_cast<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->setRumble(context->core, controller->m_inputController->rumble());
|
||||
|
||||
|
@ -241,13 +238,21 @@ GameController::GameController(QObject* parent)
|
|||
|
||||
static const char* savestateMessage = "State %i loaded";
|
||||
static const char* savestateFailedMessage = "State %i failed to load";
|
||||
static int biosCat = -1;
|
||||
static int statusCat = -1;
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
GameController* controller = static_cast<GameController*>(context->userData);
|
||||
QString message;
|
||||
if (biosCat < 0) {
|
||||
biosCat = mLogCategoryById("gba.bios");
|
||||
}
|
||||
if (statusCat < 0) {
|
||||
statusCat = mLogCategoryById("core.status");
|
||||
}
|
||||
#ifdef M_CORE_GBA
|
||||
if (level == mLOG_STUB && category == _mLOG_CAT_GBA_BIOS()) {
|
||||
if (level == mLOG_STUB && category == biosCat) {
|
||||
va_list argc;
|
||||
va_copy(argc, args);
|
||||
int immediate = va_arg(argc, int);
|
||||
|
@ -255,7 +260,7 @@ GameController::GameController(QObject* parent)
|
|||
QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
|
||||
} else
|
||||
#endif
|
||||
if (category == _mLOG_CAT_STATUS()) {
|
||||
if (category == statusCat) {
|
||||
// Slot 0 is reserved for suspend points
|
||||
if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
|
||||
va_list argc;
|
||||
|
@ -731,7 +736,7 @@ void GameController::frameAdvance() {
|
|||
}
|
||||
}
|
||||
|
||||
void GameController::setRewind(bool enable, int capacity) {
|
||||
void GameController::setRewind(bool enable, int capacity, bool rewindSave) {
|
||||
if (m_gameOpen) {
|
||||
Interrupter interrupter(this);
|
||||
if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) {
|
||||
|
@ -739,8 +744,10 @@ void GameController::setRewind(bool enable, int capacity) {
|
|||
}
|
||||
m_threadContext.core->opts.rewindEnable = enable;
|
||||
m_threadContext.core->opts.rewindBufferCapacity = capacity;
|
||||
m_threadContext.core->opts.rewindSave = rewindSave;
|
||||
if (enable && capacity > 0) {
|
||||
mCoreRewindContextInit(&m_threadContext.rewind, capacity);
|
||||
m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1194,17 +1201,26 @@ void GameController::setLuminanceLevel(int level) {
|
|||
}
|
||||
|
||||
void GameController::setRealTime() {
|
||||
m_rtc.override = RTC_NO_OVERRIDE;
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
m_threadContext.core->rtc.override = RTC_NO_OVERRIDE;
|
||||
}
|
||||
|
||||
void GameController::setFixedTime(const QDateTime& time) {
|
||||
m_rtc.override = RTC_FIXED;
|
||||
m_rtc.value = time.toMSecsSinceEpoch() / 1000;
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
m_threadContext.core->rtc.override = RTC_FIXED;
|
||||
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void GameController::setFakeEpoch(const QDateTime& time) {
|
||||
m_rtc.override = RTC_FAKE_EPOCH;
|
||||
m_rtc.value = time.toMSecsSinceEpoch() / 1000;
|
||||
if (!isLoaded()) {
|
||||
return;
|
||||
}
|
||||
m_threadContext.core->rtc.override = RTC_FAKE_EPOCH;
|
||||
m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void GameController::updateKeys() {
|
||||
|
|
|
@ -127,7 +127,7 @@ public slots:
|
|||
void setPaused(bool paused);
|
||||
void reset();
|
||||
void frameAdvance();
|
||||
void setRewind(bool enable, int capacity);
|
||||
void setRewind(bool enable, int capacity, bool rewindSave);
|
||||
void rewind(int states = 0);
|
||||
void startRewinding();
|
||||
void stopRewinding();
|
||||
|
@ -247,8 +247,6 @@ private:
|
|||
} m_lux;
|
||||
uint8_t m_luxValue;
|
||||
int m_luxLevel;
|
||||
|
||||
mRTCGenericSource m_rtc;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -89,7 +89,12 @@ LibraryModel::LibraryModel(const QString& path, QObject* parent)
|
|||
m_library->ref();
|
||||
} else {
|
||||
m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
|
||||
s_handles[path] = m_library;
|
||||
if (m_library->library) {
|
||||
s_handles[path] = m_library;
|
||||
} else {
|
||||
delete m_library;
|
||||
m_library = new LibraryHandle(mLibraryCreateEmpty());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_library = new LibraryHandle(mLibraryCreateEmpty());
|
||||
|
@ -280,7 +285,9 @@ LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
|
|||
LibraryModel::LibraryHandle::~LibraryHandle() {
|
||||
m_loaderThread.quit();
|
||||
m_loaderThread.wait();
|
||||
mLibraryDestroy(library);
|
||||
if (library) {
|
||||
mLibraryDestroy(library);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryModel::LibraryHandle::ref() {
|
||||
|
|
|
@ -10,11 +10,17 @@
|
|||
#include <QFontDatabase>
|
||||
#include <QTimer>
|
||||
|
||||
#include "LogController.h"
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
#endif
|
||||
#include <mgba-util/png-io.h>
|
||||
|
||||
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]() {
|
||||
updateTiles(true);
|
||||
});
|
||||
connect(m_ui.exportButton, SIGNAL(clicked()), this, SLOT(exportObj()));
|
||||
}
|
||||
|
||||
void ObjView::selectObj(int obj) {
|
||||
|
@ -68,64 +75,63 @@ void ObjView::updateTilesGBA(bool force) {
|
|||
unsigned width = GBAVideoObjSizes[shape * 4 + size][0];
|
||||
unsigned height = GBAVideoObjSizes[shape * 4 + size][1];
|
||||
unsigned tile = GBAObjAttributesCGetTile(obj->c);
|
||||
ObjInfo newInfo{
|
||||
tile,
|
||||
width / 8,
|
||||
height / 8,
|
||||
width / 8
|
||||
};
|
||||
m_ui.tiles->setTileCount(width * height / 64);
|
||||
m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
|
||||
m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value());
|
||||
unsigned palette = GBAObjAttributesCGetPalette(obj->c);
|
||||
GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues
|
||||
if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) {
|
||||
newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a));
|
||||
unsigned tileBase = tile;
|
||||
unsigned paletteSet;
|
||||
unsigned bits;
|
||||
if (GBAObjAttributesAIs256Color(obj->a)) {
|
||||
m_ui.palette->setText("256-color");
|
||||
paletteSet = 1;
|
||||
m_ui.tile->setPalette(0);
|
||||
m_ui.tile->setPaletteSet(1, 1024, 1536);
|
||||
palette = 1;
|
||||
tile = tile / 2 + 1024;
|
||||
bits = 8;
|
||||
} else {
|
||||
m_ui.palette->setText(QString::number(palette));
|
||||
paletteSet = 0;
|
||||
m_ui.tile->setPalette(palette);
|
||||
m_ui.tile->setPaletteSet(0, 2048, 3072);
|
||||
palette += 16;
|
||||
tile += 2048;
|
||||
bits = 4;
|
||||
}
|
||||
ObjInfo newInfo{
|
||||
tile,
|
||||
width / 8,
|
||||
height / 8,
|
||||
width / 8,
|
||||
palette,
|
||||
paletteSet,
|
||||
bits
|
||||
};
|
||||
if (newInfo != m_objInfo) {
|
||||
force = true;
|
||||
}
|
||||
GBARegisterDISPCNT dispcnt = gba->memory.io[0]; // FIXME: Register name can't be imported due to namespacing issues
|
||||
if (!GBARegisterDISPCNTIsObjCharacterMapping(dispcnt)) {
|
||||
newInfo.stride = 0x20 >> (GBAObjAttributesAGet256Color(obj->a));
|
||||
};
|
||||
m_objInfo = newInfo;
|
||||
int i = 0;
|
||||
if (GBAObjAttributesAIs256Color(obj->a)) {
|
||||
m_ui.palette->setText("256-color");
|
||||
mTileCacheSetPalette(m_tileCache.get(), 1);
|
||||
m_ui.tile->setPalette(0);
|
||||
m_ui.tile->setPaletteSet(1, 1024, 1536);
|
||||
tile /= 2;
|
||||
unsigned t = tile + i;
|
||||
for (int y = 0; y < height / 8; ++y) {
|
||||
for (int x = 0; x < width / 8; ++x, ++i, ++t) {
|
||||
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * t], t + 1024, 1);
|
||||
if (data) {
|
||||
m_ui.tiles->setTile(i, data);
|
||||
} else if (force) {
|
||||
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t + 1024, 1));
|
||||
}
|
||||
}
|
||||
t += newInfo.stride - width / 8;
|
||||
}
|
||||
tile += 1024;
|
||||
} else {
|
||||
m_ui.palette->setText(QString::number(palette));
|
||||
mTileCacheSetPalette(m_tileCache.get(), 0);
|
||||
m_ui.tile->setPalette(palette);
|
||||
m_ui.tile->setPaletteSet(0, 2048, 3072);
|
||||
unsigned t = tile + i;
|
||||
for (int y = 0; y < height / 8; ++y) {
|
||||
for (int x = 0; x < width / 8; ++x, ++i, ++t) {
|
||||
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * t], t + 2048, palette + 16);
|
||||
if (data) {
|
||||
m_ui.tiles->setTile(i, data);
|
||||
} else if (force) {
|
||||
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t + 2048, palette + 16));
|
||||
}
|
||||
}
|
||||
t += newInfo.stride - width / 8;
|
||||
}
|
||||
tile += 2048;
|
||||
}
|
||||
m_tileOffset = tile;
|
||||
mTileCacheSetPalette(m_tileCache.get(), paletteSet);
|
||||
|
||||
int i = 0;
|
||||
for (int y = 0; y < height / 8; ++y) {
|
||||
for (int x = 0; x < width / 8; ++x, ++i, ++tile, ++tileBase) {
|
||||
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[32 * tileBase], tile, palette);
|
||||
if (data) {
|
||||
m_ui.tiles->setTile(i, data);
|
||||
} else if (force) {
|
||||
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), tile, palette));
|
||||
}
|
||||
}
|
||||
tile += newInfo.stride - width / 8;
|
||||
tileBase += newInfo.stride - width / 8;
|
||||
}
|
||||
|
||||
m_ui.x->setText(QString::number(GBAObjAttributesBGetX(obj->b)));
|
||||
m_ui.y->setText(QString::number(GBAObjAttributesAGetY(obj->a)));
|
||||
|
@ -175,20 +181,10 @@ void ObjView::updateTilesGB(bool force) {
|
|||
height = 16;
|
||||
}
|
||||
unsigned tile = obj->tile;
|
||||
ObjInfo newInfo{
|
||||
tile,
|
||||
1,
|
||||
height / 8,
|
||||
1
|
||||
};
|
||||
if (newInfo != m_objInfo) {
|
||||
force = true;
|
||||
}
|
||||
m_objInfo = newInfo;
|
||||
m_ui.tiles->setTileCount(width * height / 64);
|
||||
m_ui.tiles->setMinimumSize(QSize(width, height) * m_ui.magnification->value());
|
||||
m_ui.tiles->resize(QSize(width, height) * m_ui.magnification->value());
|
||||
int palette = 0;
|
||||
unsigned palette = 0;
|
||||
if (gb->model >= GB_MODEL_CGB) {
|
||||
if (GBObjAttributesIsBank(obj->attr)) {
|
||||
tile += 512;
|
||||
|
@ -197,21 +193,37 @@ void ObjView::updateTilesGB(bool force) {
|
|||
} else {
|
||||
palette = GBObjAttributesGetPalette(obj->attr);
|
||||
}
|
||||
int i = 0;
|
||||
m_ui.palette->setText(QString::number(palette));
|
||||
palette += 8;
|
||||
|
||||
ObjInfo newInfo{
|
||||
tile,
|
||||
1,
|
||||
height / 8,
|
||||
1,
|
||||
palette,
|
||||
0,
|
||||
2
|
||||
};
|
||||
if (newInfo != m_objInfo) {
|
||||
force = true;
|
||||
}
|
||||
m_objInfo = newInfo;
|
||||
m_tileOffset = tile;
|
||||
|
||||
int i = 0;
|
||||
mTileCacheSetPalette(m_tileCache.get(), 0);
|
||||
m_ui.tile->setPalette(palette + 8);
|
||||
m_ui.tile->setPalette(palette);
|
||||
m_ui.tile->setPaletteSet(0, 512, 1024);
|
||||
for (int y = 0; y < height / 8; ++y, ++i) {
|
||||
unsigned t = tile + i;
|
||||
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette + 8);
|
||||
const uint16_t* data = mTileCacheGetTileIfDirty(m_tileCache.get(), &m_tileStatus[16 * t], t, palette);
|
||||
if (data) {
|
||||
m_ui.tiles->setTile(i, data);
|
||||
} else if (force) {
|
||||
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette + 8));
|
||||
m_ui.tiles->setTile(i, mTileCacheGetTile(m_tileCache.get(), t, palette));
|
||||
}
|
||||
}
|
||||
m_tileOffset = tile;
|
||||
|
||||
m_ui.x->setText(QString::number(obj->x));
|
||||
m_ui.y->setText(QString::number(obj->y));
|
||||
|
@ -230,10 +242,56 @@ void ObjView::updateTilesGB(bool force) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void ObjView::exportObj() {
|
||||
GameController::Interrupter interrupter(m_controller);
|
||||
QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export sprite"),
|
||||
tr("Portable Network Graphics (*.png)"));
|
||||
if (!dialog->exec()) {
|
||||
return;
|
||||
}
|
||||
QString filename = dialog->selectedFiles()[0];
|
||||
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (!vf) {
|
||||
LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
mTileCacheSetPalette(m_tileCache.get(), m_objInfo.paletteSet);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader8(png, m_objInfo.width * 8, m_objInfo.height * 8);
|
||||
|
||||
const uint16_t* rawPalette = mTileCacheGetPalette(m_tileCache.get(), m_objInfo.paletteId);
|
||||
unsigned colors = 1 << m_objInfo.bits;
|
||||
uint32_t palette[256];
|
||||
for (unsigned c = 0; c < colors && c < 256; ++c) {
|
||||
uint16_t color = rawPalette[c];
|
||||
palette[c] = M_R8(rawPalette[c]);
|
||||
palette[c] |= M_G8(rawPalette[c]) << 8;
|
||||
palette[c] |= M_B8(rawPalette[c]) << 16;
|
||||
if (c) {
|
||||
palette[c] |= 0xFF000000;
|
||||
}
|
||||
}
|
||||
PNGWritePalette(png, info, palette, colors);
|
||||
|
||||
uint8_t* buffer = new uint8_t[m_objInfo.width * m_objInfo.height * 8 * 8];
|
||||
unsigned t = m_objInfo.tile;
|
||||
for (int y = 0; y < m_objInfo.height; ++y) {
|
||||
for (int x = 0; x < m_objInfo.width; ++x, ++t) {
|
||||
compositeTile(t, static_cast<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) {
|
||||
return other.tile != tile ||
|
||||
other.width != width ||
|
||||
other.height != height ||
|
||||
other.stride != stride;
|
||||
other.stride != stride ||
|
||||
other.paletteId != paletteId ||
|
||||
other.paletteSet != paletteSet;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ Q_OBJECT
|
|||
public:
|
||||
ObjView(GameController* controller, QWidget* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void exportObj();
|
||||
|
||||
private slots:
|
||||
void selectObj(int);
|
||||
void translateIndex(int);
|
||||
|
@ -43,6 +46,9 @@ private:
|
|||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned stride;
|
||||
unsigned paletteId;
|
||||
unsigned paletteSet;
|
||||
unsigned bits;
|
||||
|
||||
bool operator!=(const ObjInfo&);
|
||||
} m_objInfo;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>454</width>
|
||||
<height>375</height>
|
||||
<height>385</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -70,6 +70,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exportButton">
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
|
|
|
@ -148,7 +148,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
|
||||
GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this);
|
||||
m_ui.stackedWidget->addWidget(editor);
|
||||
m_ui.tabs->addItem("Keyboard");
|
||||
m_ui.tabs->addItem(tr("Keyboard"));
|
||||
connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save()));
|
||||
|
||||
GBAKeyEditor* buttonEditor = nullptr;
|
||||
|
@ -157,7 +157,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
const char* profile = inputController->profileForType(SDL_BINDING_BUTTON);
|
||||
buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile);
|
||||
m_ui.stackedWidget->addWidget(buttonEditor);
|
||||
m_ui.tabs->addItem("Controllers");
|
||||
m_ui.tabs->addItem(tr("Controllers"));
|
||||
connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save()));
|
||||
#endif
|
||||
|
||||
|
@ -176,7 +176,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
shortcutView->setController(shortcutController);
|
||||
shortcutView->setInputController(inputController);
|
||||
m_ui.stackedWidget->addWidget(shortcutView);
|
||||
m_ui.tabs->addItem("Shortcuts");
|
||||
m_ui.tabs->addItem(tr("Shortcuts"));
|
||||
}
|
||||
|
||||
void SettingsView::selectBios(QLineEdit* bios) {
|
||||
|
@ -206,6 +206,7 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("mute", m_ui.mute);
|
||||
saveSetting("rewindEnable", m_ui.rewind);
|
||||
saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
saveSetting("rewindSave", m_ui.rewindSave);
|
||||
saveSetting("resampleVideo", m_ui.resampleVideo);
|
||||
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
|
@ -234,13 +235,13 @@ void SettingsView::updateConfig() {
|
|||
break;
|
||||
}
|
||||
|
||||
int loadState = 0;
|
||||
int loadState = SAVESTATE_RTC;
|
||||
loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
|
||||
loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
|
||||
loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
saveSetting("loadStateExtdata", loadState);
|
||||
|
||||
int saveState = 0;
|
||||
int saveState = SAVESTATE_RTC;
|
||||
saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
|
||||
saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
|
||||
saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
|
@ -287,6 +288,7 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("mute", m_ui.mute);
|
||||
loadSetting("rewindEnable", m_ui.rewind);
|
||||
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
loadSetting("rewindSave", m_ui.rewindSave);
|
||||
loadSetting("resampleVideo", m_ui.resampleVideo);
|
||||
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
|
@ -319,7 +321,7 @@ void SettingsView::reloadConfig() {
|
|||
bool ok;
|
||||
int loadState = loadSetting("loadStateExtdata").toInt(&ok);
|
||||
if (!ok) {
|
||||
loadState = SAVESTATE_SCREENSHOT;
|
||||
loadState = SAVESTATE_SCREENSHOT | SAVESTATE_RTC;
|
||||
}
|
||||
m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT);
|
||||
m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA);
|
||||
|
@ -327,7 +329,7 @@ void SettingsView::reloadConfig() {
|
|||
|
||||
int saveState = loadSetting("saveStateExtdata").toInt(&ok);
|
||||
if (!ok) {
|
||||
saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS;
|
||||
saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC;
|
||||
}
|
||||
m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT);
|
||||
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
||||
|
|
|
@ -383,7 +383,7 @@
|
|||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="resampleVideo">
|
||||
<property name="text">
|
||||
<string>Resample video</string>
|
||||
<string>Bilinear filtering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -535,21 +535,21 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Idle loops:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="idleOptimization">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
@ -568,21 +568,21 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>Savestate extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="saveStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
|
@ -592,7 +592,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="saveStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
|
@ -602,7 +602,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QCheckBox" name="saveStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
|
@ -612,14 +612,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>Load extra data:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="loadStateScreenshot">
|
||||
<property name="text">
|
||||
<string>Screenshot</string>
|
||||
|
@ -629,27 +629,37 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="loadStateSave">
|
||||
<property name="text">
|
||||
<string>Save data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<item row="14" column="1">
|
||||
<widget class="QCheckBox" name="loadStateCheats">
|
||||
<property name="text">
|
||||
<string>Cheat codes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<item row="11" column="0" colspan="2">
|
||||
<widget class="Line" name="line_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
<widget class="QWidget" name="bios">
|
||||
|
|
|
@ -1275,7 +1275,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_config->updateOption("lockAspectRatio");
|
||||
|
||||
ConfigOption* resampleVideo = m_config->addOption("resampleVideo");
|
||||
resampleVideo->addBoolean(tr("Resample video"), avMenu);
|
||||
resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu);
|
||||
resampleVideo->connect([this](const QVariant& value) {
|
||||
m_display->filter(value.toBool());
|
||||
}, this);
|
||||
|
@ -1482,12 +1482,17 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
ConfigOption* rewindEnable = m_config->addOption("rewindEnable");
|
||||
rewindEnable->connect([this](const QVariant& value) {
|
||||
m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt());
|
||||
m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt(), m_config->getOption("rewindSave").toInt());
|
||||
}, this);
|
||||
|
||||
ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity");
|
||||
rewindBufferCapacity->connect([this](const QVariant& value) {
|
||||
m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt());
|
||||
m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt(), m_config->getOption("rewindSave").toInt());
|
||||
}, this);
|
||||
|
||||
ConfigOption* rewindSave = m_config->addOption("rewindSave");
|
||||
rewindBufferCapacity->connect([this](const QVariant& value) {
|
||||
m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), m_config->getOption("rewindBufferCapacity").toInt(), value.toBool());
|
||||
}, this);
|
||||
|
||||
ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,7 @@ int main(int argc, char** argv) {
|
|||
.useBios = true,
|
||||
.rewindEnable = true,
|
||||
.rewindBufferCapacity = 600,
|
||||
.rewindSave = true,
|
||||
.audioBuffers = 1024,
|
||||
.videoSync = false,
|
||||
.audioSync = true,
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#define BUFFER_SIZE (GBA_AUDIO_SAMPLES >> 2)
|
||||
|
||||
mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio");
|
||||
mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio");
|
||||
|
||||
static void _mSDLAudioCallback(void* context, Uint8* data, int len);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#define RUMBLE_PWM 16
|
||||
#define RUMBLE_STEPS 2
|
||||
|
||||
mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events");
|
||||
mLOG_DEFINE_CATEGORY(SDL_EVENTS, "SDL Events", "platform.sdl.events");
|
||||
|
||||
DEFINE_VECTOR(SDL_JoystickList, struct SDL_JoystickCombo);
|
||||
|
||||
|
|
|
@ -18,6 +18,11 @@ struct ConfigurationSectionHandlerData {
|
|||
void* data;
|
||||
};
|
||||
|
||||
struct ConfigurationHandlerData {
|
||||
void (*handler)(const char* key, const char* value, void* data);
|
||||
void* data;
|
||||
};
|
||||
|
||||
static void _tableDeinit(void* table) {
|
||||
TableDeinit(table);
|
||||
free(table);
|
||||
|
@ -63,6 +68,11 @@ static void _sectionEnumHandler(const char* key, void* section, void* user) {
|
|||
data->handler(key, data->data);
|
||||
}
|
||||
|
||||
static void _enumHandler(const char* key, void* value, void* user) {
|
||||
struct ConfigurationHandlerData* data = user;
|
||||
data->handler(key, value, data->data);
|
||||
}
|
||||
|
||||
void ConfigurationInit(struct Configuration* configuration) {
|
||||
HashTableInit(&configuration->sections, 0, _tableDeinit);
|
||||
HashTableInit(&configuration->root, 0, _sectionDeinit);
|
||||
|
@ -199,3 +209,14 @@ void ConfigurationEnumerateSections(const struct Configuration* configuration, v
|
|||
struct ConfigurationSectionHandlerData handlerData = { handler, user };
|
||||
HashTableEnumerate(&configuration->sections, _sectionEnumHandler, &handlerData);
|
||||
}
|
||||
|
||||
void ConfigurationEnumerate(const struct Configuration* configuration, const char* section, void (*handler)(const char* key, const char* value, void* user), void* user) {
|
||||
struct ConfigurationHandlerData handlerData = { handler, user };
|
||||
const struct Table* currentSection = &configuration->root;
|
||||
if (section) {
|
||||
currentSection = HashTableLookup(&configuration->sections, section);
|
||||
}
|
||||
if (currentSection) {
|
||||
HashTableEnumerate(currentSection, _enumHandler, &handlerData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,9 @@ bool _UPSApplyPatch(struct Patch* patch, const void* in, size_t inSize, void* ou
|
|||
if (patch->vf->read(patch->vf, &byte, 1) != 1) {
|
||||
return false;
|
||||
}
|
||||
if (offset >= outSize) {
|
||||
return false;
|
||||
}
|
||||
buf[offset] ^= byte;
|
||||
++offset;
|
||||
if (!byte) {
|
||||
|
|
|
@ -51,6 +51,40 @@ png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
|
|||
return info;
|
||||
}
|
||||
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
|
||||
png_infop info = png_create_info_struct(png);
|
||||
if (!info) {
|
||||
return 0;
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return 0;
|
||||
}
|
||||
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
return info;
|
||||
}
|
||||
|
||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries) {
|
||||
if (!palette || !entries) {
|
||||
return false;
|
||||
}
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
png_color colors[256];
|
||||
png_byte trans[256];
|
||||
unsigned i;
|
||||
for (i = 0; i < entries && i < 256; ++i) {
|
||||
colors[i].red = palette[i];
|
||||
colors[i].green = palette[i] >> 8;
|
||||
colors[i].blue = palette[i] >> 16;
|
||||
trans[i] = palette[i] >> 24;
|
||||
}
|
||||
png_set_PLTE(png, info, colors, entries);
|
||||
png_set_tRNS(png, info, trans, entries, NULL);
|
||||
png_write_info(png, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
|
||||
png_bytep row = malloc(sizeof(png_byte) * width * 3);
|
||||
if (!row) {
|
||||
|
@ -94,6 +128,19 @@ bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned s
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels) {
|
||||
UNUSED(width);
|
||||
const png_byte* pixelData = pixels;
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
unsigned i;
|
||||
for (i = 0; i < height; ++i) {
|
||||
png_write_row(png, &pixelData[stride * i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) {
|
||||
char realName[5];
|
||||
strncpy(realName, name, 4);
|
||||
|
|
|
@ -48,6 +48,15 @@ bool endswith(const char* restrict s1, const char* restrict end) {
|
|||
return strcmp(&s1[len - endLen], end) == 0;
|
||||
}
|
||||
|
||||
bool startswith(const char* restrict s1, const char* restrict start) {
|
||||
size_t len = strlen(s1);
|
||||
size_t startLen = strlen(start);
|
||||
if (len < startLen) {
|
||||
return false;
|
||||
}
|
||||
return strncmp(s1, start, startLen) == 0;
|
||||
}
|
||||
|
||||
uint32_t utf16Char(const uint16_t** unicode, size_t* length) {
|
||||
if (*length < 2) {
|
||||
*length = 0;
|
||||
|
|
|
@ -96,6 +96,7 @@ def updateMachO(bin, execPath, root):
|
|||
if os.access(newPath, os.F_OK):
|
||||
if verbose:
|
||||
print('Skipping copying {}, already done.'.format(oldPath))
|
||||
newPath = None
|
||||
elif os.path.abspath(oldPath) != os.path.abspath(newPath):
|
||||
if verbose:
|
||||
print('Copying {} to {}...'.format(oldPath, newPath))
|
||||
|
@ -111,7 +112,8 @@ def updateMachO(bin, execPath, root):
|
|||
args = [installNameTool]
|
||||
for path, oldExecPath, newExecPath in toUpdate:
|
||||
if path != bin:
|
||||
updateMachO(path, execPath, root)
|
||||
if path:
|
||||
updateMachO(path, execPath, root)
|
||||
if verbose:
|
||||
print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath))
|
||||
args.extend(['-change', oldExecPath, newExecPath])
|
||||
|
|
Loading…
Reference in New Issue