Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2017-01-30 11:30:03 -08:00
commit 20296e7f0e
101 changed files with 215525 additions and 994 deletions

20
CHANGES
View File

@ -6,6 +6,8 @@ Features:
- Sprite viewer
- Debugging console
- Improved memory viewer
- GB: LR35902/GB-Z80 disassembler
- Configuration of gamepad hats
Bugfixes:
- LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior
@ -16,6 +18,10 @@ Bugfixes:
- SDL: Prevent crash on cores with no audio
- ARM7: Decode MCR/MRC
- ARM7: Clean up instruction decoding for future expandability
- Libretro: Fix saving in GB games (fixes mgba.io/i/486)
- LR35902: Fix pc overflowing current region off-by-one
- GB MBC: Fix ROM bank overflows getting set to bank 0
- Qt: Fix timing issues on high refresh rate monitors
Misc:
- SDL: Remove scancode key input
- GBA Video: Clean up unused timers
@ -40,6 +46,20 @@ Misc:
- All: Add C++ header guards
- GBA I/O: Clear JOYSTAT RECV flag when reading JOY_RECV registers
- GBA I/O: Set JOYSTAT TRANS flag when writing JOY_TRANS registers
- Qt: Improved HiDPI support
- Qt: Expose configuration directory
- Feature: Move game database from flatfile to SQLite3
- GB Audio: Start implementing "zombie" audio (fixes mgba.io/i/389)
- VFS: Fix some minor VFile issues with FILEs
- Core: Add generic checksum function
- Feature: Support ImageMagick 7
- All: Move time.h include to common.h
- CMake: Add ability to just print version string
- Qt: Merge "Save" and "OK" buttons in shader options
- SDL: Automatically map controllers when plugged in
- Qt: Automatically load controller profile when plugged in
- OpenGL: Add xBR-lv2 shader
- ARM: Overhaul PSR bit access
0.5.2: (2016-12-31)
Bugfixes:

View File

@ -15,6 +15,7 @@ set(USE_MINIZIP ON CACHE BOOL "Whether or not to enable external minizip support
set(USE_PNG ON CACHE BOOL "Whether or not to enable PNG support")
set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support")
set(USE_MAGICK ON CACHE BOOL "Whether or not to enable ImageMagick support")
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
set(M_CORE_DS ON CACHE BOOL "Build DS core")
@ -162,7 +163,14 @@ list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
# Advanced settings
set(BUILD_LTO ON CACHE BOOL "Build with link-time optimization")
if(NOT 3DS)
# LTO appears to make 3DS binary slower
set(DEFAULT_LTO ON)
else()
set(DEFAULT_LTO OFF)
endif()
set(BUILD_LTO ${DEFAULT_LTO} CACHE BOOL "Build with link-time optimization")
set(BUILD_PGO OFF CACHE BOOL "Build with profiling-guided optimization")
set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated")
set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path")
@ -245,6 +253,7 @@ endif()
if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII)
set(USE_DEBUGGERS OFF)
set(USE_SQLITE3 OFF)
endif()
if(NOT M_CORE_GBA)
@ -324,6 +333,7 @@ if(HAVE_UMASK)
endif()
# Feature dependencies
set(FEATURE_DEFINES)
set(FEATURES)
if(CMAKE_SYSTEM_NAME MATCHES .*BSD)
set(LIBEDIT_LIBRARIES -ledit)
@ -355,6 +365,7 @@ endif()
set(WANT_ZLIB ${USE_ZLIB})
set(WANT_PNG ${USE_PNG})
set(WANT_LIBZIP ${USE_LIBZIP})
set(WANT_SQLITE3 ${USE_SQLITE3})
set(USE_CMOCKA ${BUILD_SUITE})
find_feature(USE_FFMPEG "libavcodec;libavformat;libavresample;libavutil;libswscale")
@ -365,6 +376,7 @@ find_feature(USE_LIBZIP "libzip")
find_feature(USE_MAGICK "MagickWand")
find_feature(USE_EPOXY "epoxy")
find_feature(USE_CMOCKA "cmocka")
find_feature(USE_SQLITE3 "sqlite3")
# Features
set(DEBUGGER_SRC
@ -445,11 +457,16 @@ if(USE_MAGICK)
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/imagemagick/imagemagick-gif-encoder.c")
list(APPEND DEPENDENCY_LIB ${MAGICKWAND_LIBRARIES})
string(REGEX MATCH "^[0-9]+\\.[0-9]+" MAGICKWAND_VERSION_PARTIAL ${MagickWand_VERSION})
string(REGEX MATCH "^[0-9]+" MAGICKWAND_VERSION_MAJOR ${MagickWand_VERSION})
if(${MAGICKWAND_VERSION_PARTIAL} EQUAL "6.7")
set(MAGICKWAND_DEB_VERSION "5")
elseif(${MagickWand_VERSION} EQUAL "6.9.7")
set(MAGICKWAND_DEB_VERSION "-6.q16-3")
else()
set(MAGICKWAND_DEB_VERSION "-6.q16-2")
endif()
list(APPEND FEATURE_DEFINES MAGICKWAND_VERSION_MAJOR=${MAGICKWAND_VERSION_MAJOR})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libmagickwand${MAGICKWAND_DEB_VERSION}")
endif()
@ -493,6 +510,12 @@ if(USE_PNG)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libpng12-0")
endif()
if(WANT_SQLITE3 AND NOT USE_SQLITE3)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/sqlite3/sqlite3.c)
include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/sqlite3/)
set(USE_SQLITE3 ON)
endif()
if(USE_LIBZIP)
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
link_directories(${LIBZIP_LIBRARY_DIRS})
@ -560,6 +583,14 @@ if(USE_EPOXY)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libepoxy0")
endif()
if(USE_SQLITE3)
list(APPEND FEATURES SQLITE3)
include_directories(AFTER ${SQLITE3_INCLUDE_DIRS})
list(APPEND DEPENDENCY_LIB ${SQLITE3_LIBRARIES})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libsqlite3-0")
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/sqlite3/no-intro.c")
endif()
set(TEST_SRC ${CORE_TEST_SRC})
if(M_CORE_GB)
add_definitions(-DM_CORE_GB)
@ -601,7 +632,6 @@ if(USE_DEBUGGERS)
list(APPEND FEATURES DEBUGGERS)
endif()
set(FEATURE_DEFINES)
foreach(FEATURE IN LISTS FEATURES)
list(APPEND FEATURE_DEFINES "USE_${FEATURE}")
endforeach()
@ -868,6 +898,7 @@ if(NOT QUIET)
message(STATUS " Screenshot/advanced savestate support: ${USE_PNG}")
message(STATUS " ZIP support: ${SUMMARY_ZIP}")
message(STATUS " 7-Zip support: ${USE_LZMA}")
message(STATUS " SQLite3 game database: ${USE_SQLITE3}")
message(STATUS " OpenGL support: ${SUMMARY_GL}")
message(STATUS "Frontends:")
message(STATUS " Qt: ${BUILD_QT}")

View File

@ -125,6 +125,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are
- ffmpeg or libav: for video recording.
- libzip or zlib: for loading ROMs stored in zip files.
- ImageMagick: for GIF recording.
- SQLite3: for game databases.
Both libpng and zlib are included with the emulator, so they do not need to be externally compiled first.
@ -155,5 +156,6 @@ mGBA contains the following third-party libraries:
- [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain.
- [MurmurHash3](https://github.com/aappleby/smhasher) implementation by Austin Appleby, which is public domain.
- [getopt for MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/), which is public domain.
- [SQLite3](https://www.sqlite.org), which is public domain.
If you are a game publisher and wish to license mGBA for commercial usage, please email [licensing@mgba.io](mailto:licensing@mgba.io) for more information.

View File

@ -28,6 +28,7 @@ CXX_GUARD_START
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
// WinSock2 gets very angry if it's included too late
@ -46,10 +47,12 @@ typedef intptr_t ssize_t;
#define strdup _strdup
#define lseek _lseek
#elif defined(__wii__)
#include <sys/time.h>
typedef intptr_t ssize_t;
#else
#include <strings.h>
#include <unistd.h>
#include <sys/time.h>
#endif
#ifndef SSIZE_MAX
@ -170,6 +173,12 @@ typedef intptr_t ssize_t;
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Fill ## FIELD (TYPE src) { \
return FILL_BITS(src, (START), (START) + (SIZE)); \
} \
ATTRIBUTE_UNUSED static inline TYPE TYPE ## OrUnsafe ## FIELD (TYPE src, TYPE bits) { \
return (src | ((bits) << (START))); \
} \
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Or ## FIELD (TYPE src, TYPE bits) { \
return (src | (((bits) << (START)) & MAKE_MASK(START, (START) + (SIZE)))); \
} \
ATTRIBUTE_UNUSED static inline TYPE TYPE ## Set ## FIELD (TYPE src, TYPE bits) { \
return INS_BITS(src, (START), (START) + (SIZE), bits); \
} \

View File

@ -85,9 +85,12 @@ struct VDir* VDirOpen7z(const char* path, int flags);
#endif
#if defined(__wii__) || defined(_3DS)
struct VDir* VDeviceList(void);
#endif
#ifdef USE_VFS_FILE
struct VFile* VFileFOpen(const char* path, const char* mode);
struct VFile* VFileFromFILE(FILE* file);
struct VDir* VDeviceList(void);
#endif
void separatePath(const char* path, char* dirname, char* basename, char* extension);

View File

@ -84,6 +84,8 @@ void mCoreConfigSetOverrideIntValue(struct mCoreConfig*, const char* key, int va
void mCoreConfigSetOverrideUIntValue(struct mCoreConfig*, const char* key, unsigned value);
void mCoreConfigSetOverrideFloatValue(struct mCoreConfig*, const char* key, float value);
void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig* src, const char* key);
void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts);
void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts);

View File

@ -36,6 +36,10 @@ enum mPlatform {
#endif
};
enum mCoreChecksumType {
CHECKSUM_CRC32,
};
struct mRTCSource;
struct mCoreConfig;
struct mCoreSync;
@ -80,6 +84,7 @@ struct mCore {
bool (*loadSave)(struct mCore*, struct VFile* vf);
bool (*loadTemporarySave)(struct mCore*, struct VFile* vf);
void (*unloadROM)(struct mCore*);
void (*checksum)(const struct mCore*, void* data, enum mCoreChecksumType type);
bool (*loadBIOS)(struct mCore*, struct VFile* vf, int biosID);
bool (*selectBIOS)(struct mCore*, int biosID);

View File

@ -12,10 +12,26 @@ CXX_GUARD_START
struct Configuration;
enum mInputHat {
M_INPUT_HAT_NEUTRAL = 0,
M_INPUT_HAT_UP = 1,
M_INPUT_HAT_RIGHT = 2,
M_INPUT_HAT_DOWN = 4,
M_INPUT_HAT_LEFT = 8
};
struct mInputHatBindings {
int up;
int right;
int down;
int left;
};
struct mInputPlatformInfo {
const char* platformName;
const char** keyId;
size_t nKeys;
struct mInputHatBindings hat;
};
struct mInputMap {
@ -48,6 +64,12 @@ void mInputUnbindAllAxes(struct mInputMap*, uint32_t type);
const struct mInputAxis* mInputQueryAxis(const struct mInputMap*, uint32_t type, int axis);
void mInputEnumerateAxes(const struct mInputMap*, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user);
int mInputMapHat(const struct mInputMap*, uint32_t type, int id, int direction);
void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInputHatBindings* bindings);
bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings);
void mInputUnbindHat(struct mInputMap*, uint32_t type, int id);
void mInputUnbindAllHats(struct mInputMap*, uint32_t type);
void mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*);
void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*);

View File

@ -14,27 +14,37 @@ CXX_GUARD_START
#include <mgba-util/vector.h>
struct mLibraryEntry {
char* filename;
char* title;
const char* base;
const char* filename;
const char* title;
char internalTitle[17];
char internalCode[9];
size_t filesize;
enum mPlatform platform;
size_t filesize;
uint32_t crc32;
};
#ifdef USE_SQLITE3
DECLARE_VECTOR(mLibraryListing, struct mLibraryEntry);
struct mLibrary {
struct mLibraryListing listing;
};
void mLibraryInit(struct mLibrary*);
void mLibraryDeinit(struct mLibrary*);
struct mLibrary;
struct mLibrary* mLibraryCreateEmpty(void);
struct mLibrary* mLibraryLoad(const char* filename);
void mLibraryDestroy(struct mLibrary*);
struct VDir;
struct VFile;
void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir);
void mLibraryAddEntry(struct mLibrary* library, const char* filename, struct VFile* vf);
void mLibraryLoadDirectory(struct mLibrary* library, const char* base);
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints);
size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints);
struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryEntry* entry);
struct NoIntroDB;
void mLibraryAttachGameDB(struct mLibrary* library, const struct NoIntroDB* db);
#endif
CXX_GUARD_END

View File

@ -68,33 +68,15 @@ enum LSMDirection {
struct ARMCore;
union PSR {
struct {
#if defined(__POWERPC__) || defined(__PPC__)
unsigned n : 1;
unsigned z : 1;
unsigned c : 1;
unsigned v : 1;
unsigned unused : 20;
unsigned i : 1;
unsigned f : 1;
unsigned t : 1;
unsigned priv : 5;
#else
unsigned priv : 5;
unsigned t : 1;
unsigned f : 1;
unsigned i : 1;
unsigned unused : 20;
unsigned v : 1;
unsigned c : 1;
unsigned z : 1;
unsigned n : 1;
#endif
};
int32_t packed;
};
DECL_BITFIELD(ARMPSR, uint32_t);
DECL_BITS(ARMPSR, Priv, 0, 5);
DECL_BIT(ARMPSR, T, 5);
DECL_BIT(ARMPSR, F, 6);
DECL_BIT(ARMPSR, I, 7);
DECL_BIT(ARMPSR, V, 28);
DECL_BIT(ARMPSR, C, 29);
DECL_BIT(ARMPSR, Z, 30);
DECL_BIT(ARMPSR, N, 31);
struct ARMMemory {
uint32_t (*load32)(struct ARMCore*, uint32_t address, int* cycleCounter);
@ -216,8 +198,8 @@ struct ARMCP15 {
struct ARMCore {
int32_t gprs[16];
union PSR cpsr;
union PSR spsr;
ARMPSR cpsr;
ARMPSR spsr;
int32_t cycles;
int32_t nextEvent;

View File

@ -10,20 +10,20 @@
#include "arm.h"
#define ARM_COND_EQ (cpu->cpsr.z)
#define ARM_COND_NE (!cpu->cpsr.z)
#define ARM_COND_CS (cpu->cpsr.c)
#define ARM_COND_CC (!cpu->cpsr.c)
#define ARM_COND_MI (cpu->cpsr.n)
#define ARM_COND_PL (!cpu->cpsr.n)
#define ARM_COND_VS (cpu->cpsr.v)
#define ARM_COND_VC (!cpu->cpsr.v)
#define ARM_COND_HI (cpu->cpsr.c && !cpu->cpsr.z)
#define ARM_COND_LS (!cpu->cpsr.c || cpu->cpsr.z)
#define ARM_COND_GE (!cpu->cpsr.n == !cpu->cpsr.v)
#define ARM_COND_LT (!cpu->cpsr.n != !cpu->cpsr.v)
#define ARM_COND_GT (!cpu->cpsr.z && !cpu->cpsr.n == !cpu->cpsr.v)
#define ARM_COND_LE (cpu->cpsr.z || !cpu->cpsr.n != !cpu->cpsr.v)
#define ARM_COND_EQ (ARMPSRIsZ(cpu->cpsr))
#define ARM_COND_NE (!ARMPSRIsZ(cpu->cpsr))
#define ARM_COND_CS (ARMPSRIsC(cpu->cpsr))
#define ARM_COND_CC (!ARMPSRIsC(cpu->cpsr))
#define ARM_COND_MI (ARMPSRIsN(cpu->cpsr))
#define ARM_COND_PL (!ARMPSRIsN(cpu->cpsr))
#define ARM_COND_VS (ARMPSRIsV(cpu->cpsr))
#define ARM_COND_VC (!ARMPSRIsV(cpu->cpsr))
#define ARM_COND_HI (ARMPSRIsC(cpu->cpsr) && !ARMPSRIsZ(cpu->cpsr))
#define ARM_COND_LS (!ARMPSRIsC(cpu->cpsr) || ARMPSRIsZ(cpu->cpsr))
#define ARM_COND_GE (!ARMPSRIsN(cpu->cpsr) == !ARMPSRIsV(cpu->cpsr))
#define ARM_COND_LT (!ARMPSRIsN(cpu->cpsr) != !ARMPSRIsV(cpu->cpsr))
#define ARM_COND_GT (!ARMPSRIsZ(cpu->cpsr) && !ARMPSRIsN(cpu->cpsr) == !ARMPSRIsV(cpu->cpsr))
#define ARM_COND_LE (ARMPSRIsZ(cpu->cpsr) || !ARMPSRIsN(cpu->cpsr) != !ARMPSRIsV(cpu->cpsr))
#define ARM_COND_AL 1
#define ARM_SIGN(I) ((I) >> 31)
@ -31,7 +31,8 @@
#define ARM_SXT_16(I) (((int16_t) (I) << 16) >> 16)
#define ARM_UXT_64(I) (uint64_t)(uint32_t) (I)
#define ARM_CARRY_FROM(M, N, D) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31))
#define ARM_CARRY_FROM(M, N, D) (UINT_MAX - (uint32_t) (M) < (uint32_t) (N))
#define ARM_CARRY_FROM_CARRY(M, N, D, C) (((uint32_t) (M) >> 31) + ((uint32_t) (N) >> 31) > ((uint32_t) (D) >> 31))
#define ARM_BORROW_FROM(M, N, D) (((uint32_t) (M)) >= ((uint32_t) (N)))
#define ARM_BORROW_FROM_CARRY(M, N, D, C) (ARM_UXT_64(M) >= (ARM_UXT_64(N)) + (uint64_t) (C))
#define ARM_V_ADDITION(M, N, D) (!(ARM_SIGN((M) ^ (N))) && (ARM_SIGN((M) ^ (D))) && (ARM_SIGN((N) ^ (D))))
@ -83,24 +84,23 @@ static inline void _ARMSetMode(struct ARMCore* cpu, enum ExecutionMode execution
cpu->executionMode = executionMode;
switch (executionMode) {
case MODE_ARM:
cpu->cpsr.t = 0;
cpu->cpsr = ARMPSRClearT(cpu->cpsr);
break;
case MODE_THUMB:
cpu->cpsr.t = 1;
cpu->cpsr = ARMPSRFillT(cpu->cpsr);
}
cpu->nextEvent = cpu->cycles;
}
static inline void _ARMReadCPSR(struct ARMCore* cpu) {
_ARMSetMode(cpu, cpu->cpsr.t);
ARMSetPrivilegeMode(cpu, cpu->cpsr.priv);
_ARMSetMode(cpu, ARMPSRGetT(cpu->cpsr));
ARMSetPrivilegeMode(cpu, ARMPSRGetPriv(cpu->cpsr));
cpu->irqh.readCPSR(cpu);
}
static inline uint32_t _ARMPCAddress(struct ARMCore* cpu) {
int instructionLength;
enum ExecutionMode mode = cpu->cpsr.t;
if (mode == MODE_ARM) {
if (ARMPSRIsT(cpu->cpsr)) {
instructionLength = WORD_SIZE_ARM;
} else {
instructionLength = WORD_SIZE_THUMB;

View File

@ -14,8 +14,6 @@ CXX_GUARD_START
#include <mgba/core/timing.h>
#include <mgba/gb/interface.h>
#include <time.h>
mLOG_DECLARE_CATEGORY(GB_MBC);
mLOG_DECLARE_CATEGORY(GB_MEM);

View File

@ -14,8 +14,6 @@ CXX_GUARD_START
#include <mgba/core/timing.h>
#include <mgba/gba/interface.h>
#include <time.h>
mLOG_DECLARE_CATEGORY(GBA_HW);
#define IS_GPIO_REGISTER(reg) ((reg) == GPIO_REG_DATA || (reg) == GPIO_REG_DIRECTION || (reg) == GPIO_REG_CONTROL)

View File

@ -11,6 +11,7 @@
CXX_GUARD_START
#include <mgba/core/log.h>
#include <mgba/core/timing.h>
mLOG_DECLARE_CATEGORY(GBA_SAVE);
@ -79,15 +80,16 @@ struct GBASavedata {
bool maskWriteback;
struct VFile* realVf;
int32_t readBitsRemaining;
int8_t readBitsRemaining;
uint32_t readAddress;
uint32_t writeAddress;
uint8_t* currentBank;
struct mTiming* timing;
bool realisticTiming;
unsigned settling;
int dust;
struct mTimingEvent dust;
enum SavedataDirty dirty;
uint32_t dirtAge;

View File

@ -11,6 +11,7 @@
CXX_GUARD_START
#include <mgba/core/core.h>
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gb/serialize.h>
@ -177,13 +178,14 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 0 - 1: Flash state machine
* | bits 2 - 3: Reserved
* | bit 4: Flash bank
* | bits 5 - 7: Reserved
* | 0x002E3 - 0x002E3: Reserved
* | 0x002E4 - 0x002E7: EEPROM read bits remaining
* | bit 5: Is settling occurring?
* | bits 6 - 7: Reserved
* | 0x002E3 - 0x002E3: EEPROM read bits remaining
* | 0x002E4 - 0x002E7: Settling cycles remaining
* | 0x002E8 - 0x002EB: EEPROM read address
* | 0x002EC - 0x002EF: EEPROM write address
* | 0x002F0 - 0x002F1: Flash settling sector
* | 0x002F2 - 0x002F3: Flash settling remaining
* | 0x002F2 - 0x002F3: Reserved
* 0x002F4 - 0x002FF: Prefetch
* | 0x002F4 - 0x002F7: GBA BIOS bus prefetch
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
@ -220,6 +222,7 @@ DECL_BITFIELD(GBASerializedHWFlags3, uint16_t);
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
DECL_BIT(GBASerializedSavedataFlags, FlashBank, 4);
DECL_BIT(GBASerializedSavedataFlags, DustSettling, 5);
DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
DECL_BIT(GBASerializedMiscFlags, Halted, 0);
@ -235,8 +238,8 @@ struct GBASerializedState {
struct {
int32_t gprs[16];
union PSR cpsr;
union PSR spsr;
ARMPSR cpsr;
ARMPSR spsr;
int32_t cycles;
int32_t nextEvent;
@ -298,12 +301,12 @@ struct GBASerializedState {
uint8_t type;
uint8_t command;
GBASerializedSavedataFlags flags;
uint8_t reserved;
int32_t readBitsRemaining;
int8_t readBitsRemaining;
uint32_t settlingDust;
uint32_t readAddress;
uint32_t writeAddress;
uint16_t settlingSector;
uint16_t settlingDust;
uint16_t reserved;
} savedata;
uint32_t biosPrefetch;

View File

@ -54,9 +54,9 @@ struct GBASIO {
unsigned internalSc : 1;
unsigned si : 1;
unsigned idleSo : 1;
unsigned : 4;
unsigned start : 1;
unsigned : 3;
unsigned start : 1;
unsigned : 4;
unsigned length : 1;
unsigned : 1;
unsigned irq : 1;

View File

@ -0,0 +1,111 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef LR35902_DECODER_H
#define LR35902_DECODER_H
#include <mgba-util/common.h>
CXX_GUARD_START
enum LR35902Condition {
LR35902_COND_NONE = 0x0,
LR35902_COND_C = 0x1,
LR35902_COND_Z = 0x2,
LR35902_COND_NC = 0x3,
LR35902_COND_NZ = 0x4
};
enum LR35902Mnemonic {
LR35902_MN_ILL = 0,
LR35902_MN_ADC,
LR35902_MN_ADD,
LR35902_MN_AND,
LR35902_MN_BIT,
LR35902_MN_CALL,
LR35902_MN_CCF,
LR35902_MN_CP,
LR35902_MN_CPL,
LR35902_MN_DAA,
LR35902_MN_DEC,
LR35902_MN_DI,
LR35902_MN_EI,
LR35902_MN_HALT,
LR35902_MN_INC,
LR35902_MN_JP,
LR35902_MN_JR,
LR35902_MN_LD,
LR35902_MN_NOP,
LR35902_MN_OR,
LR35902_MN_POP,
LR35902_MN_PUSH,
LR35902_MN_RES,
LR35902_MN_RET,
LR35902_MN_RETI,
LR35902_MN_RL,
LR35902_MN_RLC,
LR35902_MN_RR,
LR35902_MN_RRC,
LR35902_MN_RST,
LR35902_MN_SBC,
LR35902_MN_SCF,
LR35902_MN_SET,
LR35902_MN_SLA,
LR35902_MN_SRA,
LR35902_MN_SRL,
LR35902_MN_STOP,
LR35902_MN_SUB,
LR35902_MN_SWAP,
LR35902_MN_XOR,
LR35902_MN_MAX
};
enum LR35902Register {
LR35902_REG_B = 1,
LR35902_REG_C,
LR35902_REG_D,
LR35902_REG_E,
LR35902_REG_H,
LR35902_REG_L,
LR35902_REG_A,
LR35902_REG_F,
LR35902_REG_BC,
LR35902_REG_DE,
LR35902_REG_HL,
LR35902_REG_AF,
LR35902_REG_SP,
LR35902_REG_PC
};
enum {
LR35902_OP_FLAG_IMPLICIT = 1,
LR35902_OP_FLAG_MEMORY = 2,
LR35902_OP_FLAG_INCREMENT = 4,
LR35902_OP_FLAG_DECREMENT = 8,
};
struct LR35902Operand {
uint8_t reg;
uint8_t flags;
uint16_t immediate;
};
struct LR35902InstructionInfo {
uint8_t opcode[3];
uint8_t opcodeSize;
struct LR35902Operand op1;
struct LR35902Operand op2;
unsigned mnemonic;
unsigned condition;
};
size_t LR35902Decode(uint8_t opcode, struct LR35902InstructionInfo* info);
int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen);
CXX_GUARD_END
#endif

View File

@ -0,0 +1,38 @@
[shader]
name=xBR-lv2
author=Hyllian
description=xBR-lv2 upsampling filter
passes=1
[pass.0]
integerScaling=1
vertexShader=xbr.vs
fragmentShader=xbr.fs
[pass.0.uniform.XBR_Y_WEIGHT]
type=float
default=48
readableName=Y Weight
min=0
max=100
[pass.0.uniform.XBR_EQ_THRESHOLD]
type=float
readableName=Eq Threshold
default=25.0
min=0.0
max=50.0
[pass.0.uniform.XBR_SCALE]
type=float
readableName=xBR Scale
default=4.0
min=1.0
max=5.0
[pass.0.uniform.XBR_LV2_COEFFICIENT]
type=float
readableName=Lv2 Coefficient
default=2.0
min=1.0
max=3.0

View File

@ -0,0 +1,214 @@
/*
Hyllian's xBR-lv2 Shader
Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
*/
uniform float XBR_Y_WEIGHT;
uniform float XBR_EQ_THRESHOLD;
uniform float XBR_SCALE;
uniform float XBR_LV2_COEFFICIENT;
const vec4 Ao = vec4( 1.0, -1.0, -1.0, 1.0 );
const vec4 Bo = vec4( 1.0, 1.0, -1.0,-1.0 );
const vec4 Co = vec4( 1.5, 0.5, -0.5, 0.5 );
const vec4 Ax = vec4( 1.0, -1.0, -1.0, 1.0 );
const vec4 Bx = vec4( 0.5, 2.0, -0.5,-2.0 );
const vec4 Cx = vec4( 1.0, 1.0, -0.5, 0.0 );
const vec4 Ay = vec4( 1.0, -1.0, -1.0, 1.0 );
const vec4 By = vec4( 2.0, 0.5, -2.0,-0.5 );
const vec4 Cy = vec4( 2.0, 0.0, -1.0, 0.5 );
const vec4 Ci = vec4(0.25, 0.25, 0.25, 0.25);
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
vec4 df(vec4 A, vec4 B)
{
return vec4(abs(A-B));
}
float c_df(vec3 c1, vec3 c2) {
vec3 df = abs(c1 - c2);
return df.r + df.g + df.b;
}
bvec4 eq(vec4 A, vec4 B)
{
return lessThan(df(A, B), vec4(XBR_EQ_THRESHOLD));
}
bvec4 and(bvec4 A, bvec4 B)
{
return bvec4(A.x && B.x, A.y && B.y, A.z && B.z, A.w && B.w);
}
bvec4 nand(bvec4 A, bvec4 B)
{
return bvec4(!(A.x && B.x), !(A.y && B.y), !(A.z && B.z), !(A.w && B.w));
}
bvec4 or(bvec4 A, bvec4 B)
{
return bvec4(A.x || B.x, A.y || B.y, A.z || B.z, A.w || B.w);
}
vec4 weighted_distance(vec4 a, vec4 b, vec4 c, vec4 d, vec4 e, vec4 f, vec4 g, vec4 h)
{
return (df(a,b) + df(a,c) + df(d,e) + df(d,f) + 4.0*df(g,h));
}
// GLSL shader autogenerated by cg2glsl.py.
#if __VERSION__ >= 130
#define varying in
#define COMPAT_TEXTURE texture
out vec4 FragColor;
#else
#define FragColor gl_FragColor
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform sampler2D tex;
varying vec2 texCoord;
varying vec4 TEX1;
varying vec4 TEX2;
varying vec4 TEX3;
varying vec4 TEX4;
varying vec4 TEX5;
varying vec4 TEX6;
varying vec4 TEX7;
uniform vec2 texSize;
void main()
{
bvec4 edri, edr, edr_left, edr_up; // px = pixel, edr = edge detection rule
bvec4 interp_restriction_lv0, interp_restriction_lv1, interp_restriction_lv2_left, interp_restriction_lv2_up;
vec4 fx, fx_left, fx_up, px; // inequations of straight lines.
vec4 delta = vec4(1.0 / XBR_SCALE);
vec4 deltaL = vec4(0.5/ XBR_SCALE, 1.0 / XBR_SCALE, 0.5 / XBR_SCALE, 1.0 / XBR_SCALE);
vec4 deltaU = deltaL.yxwz;
vec2 fp = fract(texCoord * texSize);
vec3 A1 = COMPAT_TEXTURE(tex, TEX1.xw).rgb;
vec3 B1 = COMPAT_TEXTURE(tex, TEX1.yw).rgb;
vec3 C1 = COMPAT_TEXTURE(tex, TEX1.zw).rgb;
vec3 A = COMPAT_TEXTURE(tex, TEX2.xw).rgb;
vec3 B = COMPAT_TEXTURE(tex, TEX2.yw).rgb;
vec3 C = COMPAT_TEXTURE(tex, TEX2.zw).rgb;
vec3 D = COMPAT_TEXTURE(tex, TEX3.xw).rgb;
vec3 E = COMPAT_TEXTURE(tex, TEX3.yw).rgb;
vec3 F = COMPAT_TEXTURE(tex, TEX3.zw).rgb;
vec3 G = COMPAT_TEXTURE(tex, TEX4.xw).rgb;
vec3 H = COMPAT_TEXTURE(tex, TEX4.yw).rgb;
vec3 I = COMPAT_TEXTURE(tex, TEX4.zw).rgb;
vec3 G5 = COMPAT_TEXTURE(tex, TEX5.xw).rgb;
vec3 H5 = COMPAT_TEXTURE(tex, TEX5.yw).rgb;
vec3 I5 = COMPAT_TEXTURE(tex, TEX5.zw).rgb;
vec3 A0 = COMPAT_TEXTURE(tex, TEX6.xy).rgb;
vec3 D0 = COMPAT_TEXTURE(tex, TEX6.xz).rgb;
vec3 G0 = COMPAT_TEXTURE(tex, TEX6.xw).rgb;
vec3 C4 = COMPAT_TEXTURE(tex, TEX7.xy).rgb;
vec3 F4 = COMPAT_TEXTURE(tex, TEX7.xz).rgb;
vec3 I4 = COMPAT_TEXTURE(tex, TEX7.xw).rgb;
vec4 b = transpose(mat4x3(B, D, H, F)) * (XBR_Y_WEIGHT * Y);
vec4 c = transpose(mat4x3(C, A, G, I)) * (XBR_Y_WEIGHT * Y);
vec4 e = transpose(mat4x3(E, E, E, E)) * (XBR_Y_WEIGHT * Y);
vec4 d = b.yzwx;
vec4 f = b.wxyz;
vec4 g = c.zwxy;
vec4 h = b.zwxy;
vec4 i = c.wxyz;
vec4 i4 = transpose(mat4x3(I4, C1, A0, G5)) * (XBR_Y_WEIGHT * Y);
vec4 i5 = transpose(mat4x3(I5, C4, A1, G0)) * (XBR_Y_WEIGHT * Y);
vec4 h5 = transpose(mat4x3(H5, F4, B1, D0)) * (XBR_Y_WEIGHT * Y);
vec4 f4 = h5.yzwx;
fx = (Ao*fp.y+Bo*fp.x);
fx_left = (Ax*fp.y+Bx*fp.x);
fx_up = (Ay*fp.y+By*fp.x);
interp_restriction_lv1 = interp_restriction_lv0 = and(notEqual(e, f), notEqual(e, h));
interp_restriction_lv2_left = and(notEqual(e, g), notEqual(d, g));
interp_restriction_lv2_up = and(notEqual(e, c), notEqual(b, c));
vec4 fx45i = clamp((fx + delta - Co - Ci) / (2 * delta ), 0.0, 1.0);
vec4 fx45 = clamp((fx + delta - Co ) / (2 * delta ), 0.0, 1.0);
vec4 fx30 = clamp((fx_left + deltaL - Co ) / (2 * deltaL), 0.0, 1.0);
vec4 fx60 = clamp((fx_up + deltaU - Co ) / (2 * deltaU), 0.0, 1.0);
vec4 wd1 = weighted_distance( e, c, g, i, h5, f4, h, f);
vec4 wd2 = weighted_distance( h, d, i5, f, i4, b, e, i);
edri = and(lessThanEqual(wd1, wd2), interp_restriction_lv0);
edr = and( lessThan(wd1, wd2), interp_restriction_lv1);
edr_left = and(lessThanEqual((XBR_LV2_COEFFICIENT*df(f,g)), df(h,c)), interp_restriction_lv2_left);
edr_up = and(greaterThanEqual(df(f,g), (XBR_LV2_COEFFICIENT*df(h,c))), interp_restriction_lv2_up);
edr = and(edr, nand(edri.yzwx, edri.wxyz));
edr_left = and(and(edr_left, edr), eq(e, c));
edr_up = and(and(edr_up, edr), eq(e, g));
fx45 *= vec4(edr);
fx30 *= vec4(edr_left);
fx60 *= vec4(edr_up);
fx45i *= vec4(edri);
px = vec4(lessThanEqual(df(e, f), df(e, h)));
vec4 maximos = max(max(fx30, fx60), max(fx45, fx45i));
vec3 res1 = E;
res1 = mix(res1, mix(H, F, px.x), maximos.x);
res1 = mix(res1, mix(B, D, px.z), maximos.z);
vec3 res2 = E;
res2 = mix(res1, mix(F, B, px.y), maximos.y);
res2 = mix(res1, mix(D, H, px.w), maximos.w);
vec3 res = mix(res1, res2, step(c_df(E, res1), c_df(E, res2)));
FragColor = vec4(res, 1.0);
}

View File

@ -1,7 +1,7 @@
[shader]
name=xBR
name=xBR-lv3
author=Hyllian
description=xBR upsampling filter
description=xBR-lv3 upsampling filter
passes=1
[pass.0]

View File

@ -0,0 +1,62 @@
/*
Hyllian's xBR-lv3 Shader
Copyright (C) 2011-2015 Hyllian - sergiogdb@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Incorporates some of the ideas from SABR shader. Thanks to Joshua Street.
*/
varying vec2 texCoord;
varying vec4 TEX1;
varying vec4 TEX2;
varying vec4 TEX3;
varying vec4 TEX4;
varying vec4 TEX5;
varying vec4 TEX6;
varying vec4 TEX7;
attribute vec4 position;
uniform vec2 texSize;
/* VERTEX_SHADER */
void main()
{
gl_Position = position;
vec2 ps = vec2(1.0) / texSize;
float dx = ps.x;
float dy = ps.y;
// A1 B1 C1
// A0 A B C C4
// D0 D E F F4
// G0 G H I I4
// G5 H5 I5
texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
TEX1 = texCoord.xxxy + vec4( -dx, 0, dx,-2.0*dy); // A1 B1 C1
TEX2 = texCoord.xxxy + vec4( -dx, 0, dx, -dy); // A B C
TEX3 = texCoord.xxxy + vec4( -dx, 0, dx, 0); // D E F
TEX4 = texCoord.xxxy + vec4( -dx, 0, dx, dy); // G H I
TEX5 = texCoord.xxxy + vec4( -dx, 0, dx, 2.0*dy); // G5 H5 I5
TEX6 = texCoord.xyyy + vec4(-2.0*dx,-dy, 0, dy); // A0 D0 G0
TEX7 = texCoord.xyyy + vec4( 2.0*dx,-dy, 0, dy); // C4 F4 I4
}

View File

@ -40,8 +40,8 @@ void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) {
cpu->gprs[ARM_SP] = cpu->bankedRegisters[newBank][0];
cpu->gprs[ARM_LR] = cpu->bankedRegisters[newBank][1];
cpu->bankedSPSRs[oldBank] = cpu->spsr.packed;
cpu->spsr.packed = cpu->bankedSPSRs[newBank];
cpu->bankedSPSRs[oldBank] = cpu->spsr;
cpu->spsr = cpu->bankedSPSRs[newBank];
}
cpu->privilegeMode = mode;
}
@ -132,8 +132,8 @@ void ARMReset(struct ARMCore* cpu) {
}
cpu->privilegeMode = MODE_SYSTEM;
cpu->cpsr.packed = MODE_SYSTEM;
cpu->spsr.packed = 0;
cpu->cpsr = MODE_SYSTEM;
cpu->spsr = 0;
cpu->shifterOperand = 0;
cpu->shifterCarryOut = 0;
@ -152,10 +152,10 @@ void ARMReset(struct ARMCore* cpu) {
}
void ARMRaiseIRQ(struct ARMCore* cpu) {
if (cpu->cpsr.i) {
if (ARMPSRIsI(cpu->cpsr)) {
return;
}
union PSR cpsr = cpu->cpsr;
ARMPSR cpsr = cpu->cpsr;
int instructionWidth;
if (cpu->executionMode == MODE_THUMB) {
instructionWidth = WORD_SIZE_THUMB;
@ -163,7 +163,7 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
instructionWidth = WORD_SIZE_ARM;
}
ARMSetPrivilegeMode(cpu, MODE_IRQ);
cpu->cpsr.priv = MODE_IRQ;
cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_IRQ);
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth + WORD_SIZE_ARM;
cpu->gprs[ARM_PC] = BASE_IRQ;
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
@ -173,12 +173,12 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cpsr = ARMPSRFillI(cpu->cpsr);
cpu->cycles += currentCycles;
}
void ARMRaiseSWI(struct ARMCore* cpu) {
union PSR cpsr = cpu->cpsr;
ARMPSR cpsr = cpu->cpsr;
int instructionWidth;
if (cpu->executionMode == MODE_THUMB) {
instructionWidth = WORD_SIZE_THUMB;
@ -186,7 +186,7 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
instructionWidth = WORD_SIZE_ARM;
}
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
cpu->cpsr.priv = MODE_SUPERVISOR;
cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_SUPERVISOR);
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
cpu->gprs[ARM_PC] = BASE_SWI;
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
@ -196,12 +196,12 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cpsr = ARMPSRFillI(cpu->cpsr);
cpu->cycles += currentCycles;
}
void ARMRaiseUndefined(struct ARMCore* cpu) {
union PSR cpsr = cpu->cpsr;
ARMPSR cpsr = cpu->cpsr;
int instructionWidth;
if (cpu->executionMode == MODE_THUMB) {
instructionWidth = WORD_SIZE_THUMB;
@ -209,7 +209,7 @@ void ARMRaiseUndefined(struct ARMCore* cpu) {
instructionWidth = WORD_SIZE_ARM;
}
ARMSetPrivilegeMode(cpu, MODE_UNDEFINED);
cpu->cpsr.priv = MODE_UNDEFINED;
cpu->cpsr = ARMPSRSetPriv(cpu->cpsr, MODE_UNDEFINED);
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - instructionWidth;
cpu->gprs[ARM_PC] = BASE_UNDEF;
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
@ -219,7 +219,7 @@ void ARMRaiseUndefined(struct ARMCore* cpu) {
ARM_WRITE_PC;
_ARMSetMode(cpu, MODE_ARM);
cpu->spsr = cpsr;
cpu->cpsr.i = 1;
cpu->cpsr = ARMPSRFillI(cpu->cpsr);
cpu->cycles += currentCycles;
}

View File

@ -37,15 +37,15 @@ static struct CLIDebuggerCommandSummary _armCommands[] = {
{ 0, 0, 0, 0 }
};
static inline void _printPSR(struct CLIDebuggerBackend* be, union PSR psr) {
be->printf(be, "%08X [%c%c%c%c%c%c%c]\n", psr.packed,
psr.n ? 'N' : '-',
psr.z ? 'Z' : '-',
psr.c ? 'C' : '-',
psr.v ? 'V' : '-',
psr.i ? 'I' : '-',
psr.f ? 'F' : '-',
psr.t ? 'T' : '-');
static inline void _printPSR(struct CLIDebuggerBackend* be, ARMPSR psr) {
be->printf(be, "%08X [%c%c%c%c%c%c%c]\n", psr,
ARMPSRIsN(psr) ? 'N' : '-',
ARMPSRIsZ(psr) ? 'Z' : '-',
ARMPSRIsC(psr) ? 'C' : '-',
ARMPSRIsV(psr) ? 'V' : '-',
ARMPSRIsI(psr) ? 'I' : '-',
ARMPSRIsF(psr) ? 'F' : '-',
ARMPSRIsT(psr) ? 'T' : '-');
}
static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) {
@ -136,7 +136,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
}
_printPSR(be, cpu->cpsr);
int instructionLength;
enum ExecutionMode mode = cpu->cpsr.t;
enum ExecutionMode mode = ARMPSRIsT(cpu->cpsr);
if (mode == MODE_ARM) {
instructionLength = WORD_SIZE_ARM;
} else {
@ -196,11 +196,11 @@ static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, co
return cpu->gprs[ARM_PC];
}
if (strcmp(name, "cpsr") == 0) {
return cpu->cpsr.packed;
return cpu->cpsr;
}
// TODO: test if mode has SPSR
if (strcmp(name, "spsr") == 0) {
return cpu->spsr.packed;
return cpu->spsr;
}
if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') {
int reg = atoi(&name[1]);

View File

@ -26,7 +26,7 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
int instructionLength;
enum ExecutionMode mode = debugger->cpu->cpsr.t;
enum ExecutionMode mode = ARMPSRIsT(debugger->cpu->cpsr);
if (mode == MODE_ARM) {
instructionLength = WORD_SIZE_ARM;
} else {

View File

@ -31,7 +31,7 @@ static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) {
}
if (!shift) {
cpu->shifterOperand = shiftVal;
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else if (shift < 32) {
cpu->shifterOperand = shiftVal << shift;
cpu->shifterCarryOut = (shiftVal >> (32 - shift)) & 1;
@ -46,7 +46,7 @@ static inline void _shiftLSL(struct ARMCore* cpu, uint32_t opcode) {
int immediate = (opcode & 0x00000F80) >> 7;
if (!immediate) {
cpu->shifterOperand = cpu->gprs[rm];
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else {
cpu->shifterOperand = cpu->gprs[rm] << immediate;
cpu->shifterCarryOut = (cpu->gprs[rm] >> (32 - immediate)) & 1;
@ -70,7 +70,7 @@ static inline void _shiftLSR(struct ARMCore* cpu, uint32_t opcode) {
}
if (!shift) {
cpu->shifterOperand = shiftVal;
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else if (shift < 32) {
cpu->shifterOperand = shiftVal >> shift;
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
@ -109,7 +109,7 @@ static inline void _shiftASR(struct ARMCore* cpu, uint32_t opcode) {
}
if (!shift) {
cpu->shifterOperand = shiftVal;
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else if (shift < 32) {
cpu->shifterOperand = shiftVal >> shift;
cpu->shifterCarryOut = (shiftVal >> (shift - 1)) & 1;
@ -149,7 +149,7 @@ static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {
int rotate = shift & 0x1F;
if (!shift) {
cpu->shifterOperand = shiftVal;
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else if (rotate) {
cpu->shifterOperand = ROR(shiftVal, rotate);
cpu->shifterCarryOut = (shiftVal >> (rotate - 1)) & 1;
@ -164,7 +164,7 @@ static inline void _shiftROR(struct ARMCore* cpu, uint32_t opcode) {
cpu->shifterCarryOut = (cpu->gprs[rm] >> (immediate - 1)) & 1;
} else {
// RRX
cpu->shifterOperand = (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1);
cpu->shifterOperand = (ARMPSRGetC(cpu->cpsr) << 31) | (((uint32_t) cpu->gprs[rm]) >> 1);
cpu->shifterCarryOut = cpu->gprs[rm] & 0x00000001;
}
}
@ -175,7 +175,7 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
int immediate = opcode & 0x000000FF;
if (!rotate) {
cpu->shifterOperand = immediate;
cpu->shifterCarryOut = cpu->cpsr.c;
cpu->shifterCarryOut = ARMPSRGetC(cpu->cpsr);
} else {
cpu->shifterOperand = ROR(immediate, rotate);
cpu->shifterCarryOut = ARM_SIGN(cpu->shifterOperand);
@ -186,51 +186,76 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
// Beware pre-processor antics
#define ARM_ADDITION_S(M, N, D) \
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
if (rd == ARM_PC && _ARMModeHasSPSR(ARMPSRGetPriv(cpu->cpsr))) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_CARRY_FROM(M, N, D); \
cpu->cpsr.v = ARM_V_ADDITION(M, N, D); \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_CARRY_FROM(M, N, D)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_ADDITION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define ARM_ADDITION_CARRY_S(M, N, D, C) \
if (rd == ARM_PC && _ARMModeHasSPSR(ARMPSRGetPriv(cpu->cpsr))) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_CARRY_FROM_CARRY(M, N, D, C)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_ADDITION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define ARM_SUBTRACTION_S(M, N, D) \
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
if (rd == ARM_PC && _ARMModeHasSPSR(ARMPSRGetPriv(cpu->cpsr))) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_BORROW_FROM(M, N, D); \
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D); \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_BORROW_FROM(M, N, D)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_SUBTRACTION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define ARM_SUBTRACTION_CARRY_S(M, N, D, C) \
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
if (rd == ARM_PC && _ARMModeHasSPSR(ARMPSRGetPriv(cpu->cpsr))) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D); \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_BORROW_FROM_CARRY(M, N, D, C)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_SUBTRACTION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define ARM_NEUTRAL_S(M, N, D) \
if (rd == ARM_PC && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
if (rd == ARM_PC && _ARMModeHasSPSR(ARMPSRGetPriv(cpu->cpsr))) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = cpu->shifterCarryOut; \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, cpu->shifterCarryOut); \
cpu->cpsr = (cpu->cpsr & (0x1FFFFFFF)) | cpsr; \
}
#define ARM_NEUTRAL_HI_S(DLO, DHI) \
cpu->cpsr.n = ARM_SIGN(DHI); \
cpu->cpsr.z = !((DHI) | (DLO));
{ \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(DHI)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !((DHI) | (DLO))); \
cpu->cpsr = (cpu->cpsr & (0x3FFFFFFF)) | cpsr; \
}
#define ADDR_MODE_2_I_TEST (opcode & 0x00000F80)
#define ADDR_MODE_2_I ((opcode & 0x00000F80) >> 7)
@ -248,7 +273,7 @@ static inline void _immediate(struct ARMCore* cpu, uint32_t opcode) {
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
#define ADDR_MODE_2_LSR (ADDR_MODE_2_I_TEST ? ((uint32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : 0)
#define ADDR_MODE_2_ASR (ADDR_MODE_2_I_TEST ? ((int32_t) cpu->gprs[rm]) >> ADDR_MODE_2_I : ((int32_t) cpu->gprs[rm]) >> 31)
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (cpu->cpsr.c << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
#define ADDR_MODE_2_ROR (ADDR_MODE_2_I_TEST ? ROR(cpu->gprs[rm], ADDR_MODE_2_I) : (ARMPSRGetC(cpu->cpsr) << 31) | (((uint32_t) cpu->gprs[rm]) >> 1))
#define ADDR_MODE_3_ADDRESS ADDR_MODE_2_ADDRESS
#define ADDR_MODE_3_RN ADDR_MODE_2_RN
@ -449,9 +474,9 @@ DEFINE_ALU_INSTRUCTION_ARM(ADD, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs
int32_t n = cpu->gprs[rn];
cpu->gprs[rd] = n + cpu->shifterOperand;)
DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
DEFINE_ALU_INSTRUCTION_ARM(ADC, ARM_ADDITION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], ARMPSRGetC(cpu->cpsr)),
int32_t n = cpu->gprs[rn];
cpu->gprs[rd] = n + cpu->shifterOperand + cpu->cpsr.c;)
cpu->gprs[rd] = n + cpu->shifterOperand + ARMPSRGetC(cpu->cpsr);)
DEFINE_ALU_INSTRUCTION_ARM(AND, ARM_NEUTRAL_S(cpu->gprs[rn], cpu->shifterOperand, cpu->gprs[rd]),
cpu->gprs[rd] = cpu->gprs[rn] & cpu->shifterOperand;)
@ -481,13 +506,13 @@ DEFINE_ALU_INSTRUCTION_ARM(RSB, ARM_SUBTRACTION_S(cpu->shifterOperand, n, cpu->g
int32_t n = cpu->gprs[rn];
cpu->gprs[rd] = cpu->shifterOperand - n;)
DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !cpu->cpsr.c),
DEFINE_ALU_INSTRUCTION_ARM(RSC, ARM_SUBTRACTION_CARRY_S(cpu->shifterOperand, n, cpu->gprs[rd], !ARMPSRIsC(cpu->cpsr)),
int32_t n = cpu->gprs[rn];
cpu->gprs[rd] = cpu->shifterOperand - n - !cpu->cpsr.c;)
cpu->gprs[rd] = cpu->shifterOperand - n - !ARMPSRIsC(cpu->cpsr);)
DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !cpu->cpsr.c),
DEFINE_ALU_INSTRUCTION_ARM(SBC, ARM_SUBTRACTION_CARRY_S(n, cpu->shifterOperand, cpu->gprs[rd], !ARMPSRIsC(cpu->cpsr)),
int32_t n = cpu->gprs[rn];
cpu->gprs[rd] = n - cpu->shifterOperand - !cpu->cpsr.c;)
cpu->gprs[rd] = n - cpu->shifterOperand - !ARMPSRIsC(cpu->cpsr);)
DEFINE_ALU_INSTRUCTION_ARM(SUB, ARM_SUBTRACTION_S(n, cpu->shifterOperand, cpu->gprs[rd]),
int32_t n = cpu->gprs[rn];
@ -691,14 +716,14 @@ DEFINE_INSTRUCTION_ARM(MSR,
int32_t operand = cpu->gprs[opcode & 0x0000000F];
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
if (mask & PSR_USER_MASK) {
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
}
if (mask & PSR_STATE_MASK) {
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
}
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
}
_ARMReadCPSR(cpu);
if (cpu->executionMode == MODE_THUMB) {
@ -715,15 +740,15 @@ DEFINE_INSTRUCTION_ARM(MSRR,
int32_t operand = cpu->gprs[opcode & 0x0000000F];
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
cpu->spsr = (cpu->spsr & ~mask) | (operand & mask) | 0x00000010;)
DEFINE_INSTRUCTION_ARM(MRS, \
int rd = (opcode >> 12) & 0xF; \
cpu->gprs[rd] = cpu->cpsr.packed;)
cpu->gprs[rd] = cpu->cpsr;)
DEFINE_INSTRUCTION_ARM(MRSR, \
int rd = (opcode >> 12) & 0xF; \
cpu->gprs[rd] = cpu->spsr.packed;)
cpu->gprs[rd] = cpu->spsr;)
DEFINE_INSTRUCTION_ARM(MSRI,
int c = opcode & 0x00010000;
@ -732,14 +757,14 @@ DEFINE_INSTRUCTION_ARM(MSRI,
int32_t operand = ROR(opcode & 0x000000FF, rotate);
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
if (mask & PSR_USER_MASK) {
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_USER_MASK) | (operand & PSR_USER_MASK);
}
if (mask & PSR_STATE_MASK) {
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_STATE_MASK) | (operand & PSR_STATE_MASK);
}
if (cpu->privilegeMode != MODE_USER && (mask & PSR_PRIV_MASK)) {
ARMSetPrivilegeMode(cpu, (enum PrivilegeMode) ((operand & 0x0000000F) | 0x00000010));
cpu->cpsr.packed = (cpu->cpsr.packed & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
cpu->cpsr = (cpu->cpsr & ~PSR_PRIV_MASK) | (operand & PSR_PRIV_MASK);
}
_ARMReadCPSR(cpu);
if (cpu->executionMode == MODE_THUMB) {
@ -757,7 +782,7 @@ DEFINE_INSTRUCTION_ARM(MSRRI,
int32_t operand = ROR(opcode & 0x000000FF, rotate);
int32_t mask = (c ? 0x000000FF : 0) | (f ? 0xFF000000 : 0);
mask &= PSR_USER_MASK | PSR_PRIV_MASK | PSR_STATE_MASK;
cpu->spsr.packed = (cpu->spsr.packed & ~mask) | (operand & mask) | 0x00000010;)
cpu->spsr = (cpu->spsr & ~mask) | (operand & mask) | 0x00000010;)
DEFINE_INSTRUCTION_ARM(SWI, cpu->irqh.swi32(cpu, opcode & 0xFFFFFF))

View File

@ -12,20 +12,42 @@
// Beware pre-processor insanity
#define THUMB_ADDITION_S(M, N, D) \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_CARRY_FROM(M, N, D); \
cpu->cpsr.v = ARM_V_ADDITION(M, N, D);
{ \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_CARRY_FROM(M, N, D)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_ADDITION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define THUMB_ADDITION_CARRY_S(M, N, D, C) \
{ \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_CARRY_FROM_CARRY(M, N, D, C)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_ADDITION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define THUMB_SUBTRACTION_S(M, N, D) \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_BORROW_FROM(M, N, D); \
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
{ \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpsr = ARMPSROrUnsafeC(cpsr, ARM_BORROW_FROM(M, N, D)); \
cpsr = ARMPSROrUnsafeV(cpsr, ARM_V_SUBTRACTION(M, N, D)); \
cpu->cpsr = (cpu->cpsr & (0x0FFFFFFF)) | cpsr; \
}
#define THUMB_NEUTRAL_S(M, N, D) \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D);
{ \
ARMPSR cpsr = 0; \
cpsr = ARMPSROrUnsafeN(cpsr, ARM_SIGN(D)); \
cpsr = ARMPSROrUnsafeZ(cpsr, !(D)); \
cpu->cpsr = (cpu->cpsr & (0x3FFFFFFF)) | cpsr; \
}
#define THUMB_ADDITION(D, M, N) \
int n = N; \
@ -65,31 +87,31 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSL1,
if (!immediate) {
cpu->gprs[rd] = cpu->gprs[rm];
} else {
cpu->cpsr.c = (cpu->gprs[rm] >> (32 - immediate)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rm] >> (32 - immediate)) & 1);
cpu->gprs[rd] = cpu->gprs[rm] << immediate;
}
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSR1,
if (!immediate) {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]);
cpu->cpsr = ARMPSRSetC(cpu->cpsr, ARM_SIGN(cpu->gprs[rm]));
cpu->gprs[rd] = 0;
} else {
cpu->cpsr.c = (cpu->gprs[rm] >> (immediate - 1)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rm] >> (immediate - 1)) & 1);
cpu->gprs[rd] = ((uint32_t) cpu->gprs[rm]) >> immediate;
}
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
if (!immediate) {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]);
if (cpu->cpsr.c) {
cpu->cpsr = ARMPSRSetC(cpu->cpsr, ARM_SIGN(cpu->gprs[rm]));
if (ARMPSRIsC(cpu->cpsr)) {
cpu->gprs[rd] = 0xFFFFFFFF;
} else {
cpu->gprs[rd] = 0;
}
} else {
cpu->cpsr.c = (cpu->gprs[rm] >> (immediate - 1)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rm] >> (immediate - 1)) & 1);
cpu->gprs[rd] = cpu->gprs[rm] >> immediate;
}
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
@ -144,13 +166,13 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(LSL2,
int rs = cpu->gprs[rn] & 0xFF;
if (rs) {
if (rs < 32) {
cpu->cpsr.c = (cpu->gprs[rd] >> (32 - rs)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rd] >> (32 - rs)) & 1);
cpu->gprs[rd] <<= rs;
} else {
if (rs > 32) {
cpu->cpsr.c = 0;
cpu->cpsr = ARMPSRClearC(cpu->cpsr);
} else {
cpu->cpsr.c = cpu->gprs[rd] & 0x00000001;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, cpu->gprs[rd] & 0x00000001);
}
cpu->gprs[rd] = 0;
}
@ -161,13 +183,13 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(LSR2,
int rs = cpu->gprs[rn] & 0xFF;
if (rs) {
if (rs < 32) {
cpu->cpsr.c = (cpu->gprs[rd] >> (rs - 1)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rd] >> (rs - 1)) & 1);
cpu->gprs[rd] = (uint32_t) cpu->gprs[rd] >> rs;
} else {
if (rs > 32) {
cpu->cpsr.c = 0;
cpu->cpsr = ARMPSRClearC(cpu->cpsr);
} else {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]);
cpu->cpsr = ARMPSRSetC(cpu->cpsr, ARM_SIGN(cpu->gprs[rd]));
}
cpu->gprs[rd] = 0;
}
@ -178,11 +200,11 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ASR2,
int rs = cpu->gprs[rn] & 0xFF;
if (rs) {
if (rs < 32) {
cpu->cpsr.c = (cpu->gprs[rd] >> (rs - 1)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rd] >> (rs - 1)) & 1);
cpu->gprs[rd] >>= rs;
} else {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]);
if (cpu->cpsr.c) {
cpu->cpsr = ARMPSRSetC(cpu->cpsr, ARM_SIGN(cpu->gprs[rd]));
if (ARMPSRIsC(cpu->cpsr)) {
cpu->gprs[rd] = 0xFFFFFFFF;
} else {
cpu->gprs[rd] = 0;
@ -194,11 +216,11 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ASR2,
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ADC,
int n = cpu->gprs[rn];
int d = cpu->gprs[rd];
cpu->gprs[rd] = d + n + cpu->cpsr.c;
THUMB_ADDITION_S(d, n, cpu->gprs[rd]);)
cpu->gprs[rd] = d + n + ARMPSRGetC(cpu->cpsr);
THUMB_ADDITION_CARRY_S(d, n, cpu->gprs[rd], ARMPSRGetC(cpu->cpsr));)
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(SBC,
int n = cpu->gprs[rn] + !cpu->cpsr.c;
int n = cpu->gprs[rn] + !ARMPSRIsC(cpu->cpsr);
int d = cpu->gprs[rd];
cpu->gprs[rd] = d - n;
THUMB_SUBTRACTION_S(d, n, cpu->gprs[rd]);)
@ -207,10 +229,10 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ROR,
if (rs) {
int r4 = rs & 0x1F;
if (r4 > 0) {
cpu->cpsr.c = (cpu->gprs[rd] >> (r4 - 1)) & 1;
cpu->cpsr = ARMPSRSetC(cpu->cpsr, (cpu->gprs[rd] >> (r4 - 1)) & 1);
cpu->gprs[rd] = ROR(cpu->gprs[rd], r4);
} else {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rd]);
cpu->cpsr = ARMPSRSetC(cpu->cpsr, ARM_SIGN(cpu->gprs[rd]));
}
}
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)

View File

@ -306,6 +306,14 @@ void mCoreConfigSetOverrideFloatValue(struct mCoreConfig* config, const char* ke
ConfigurationSetFloatValue(&config->overridesTable, config->port, key, value);
}
void mCoreConfigCopyValue(struct mCoreConfig* config, const struct mCoreConfig* src, const char* key) {
const char* value = mCoreConfigGetValue(src, key);
if (!value) {
return;
}
mCoreConfigSetValue(config, key, value);
}
void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) {
_lookupCharValue(config, "bios", &opts->bios);
_lookupCharValue(config, "shader", &opts->shader);

View File

@ -87,6 +87,10 @@
#cmakedefine USE_PTHREADS
#endif
#ifndef USE_SQLITE3
#cmakedefine USE_SQLITE3
#endif
#ifndef USE_ZLIB
#cmakedefine USE_ZLIB
#endif

View File

@ -7,6 +7,7 @@
#include <mgba-util/configuration.h>
#include <mgba-util/table.h>
#include <mgba-util/vector.h>
#include <inttypes.h>
@ -15,11 +16,15 @@
#define KEY_VALUE_MAX 16
#define AXIS_INFO_MAX 12
DECLARE_VECTOR(mInputHatList, struct mInputHatBindings);
DEFINE_VECTOR(mInputHatList, struct mInputHatBindings);
struct mInputMapImpl {
int* map;
uint32_t type;
struct Table axes;
struct mInputHatList hats;
};
struct mInputAxisSave {
@ -89,6 +94,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
impl->map[i] = -1;
}
TableInit(&impl->axes, 2, free);
mInputHatListInit(&impl->hats, 1);
} else {
impl = _lookupMap(map, type);
}
@ -123,6 +129,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
}
}
TableInit(&impl->axes, 2, free);
mInputHatListInit(&impl->hats, 1);
}
return impl;
}
@ -176,6 +183,28 @@ static void _loadAxis(struct mInputMap* map, uint32_t type, const char* sectionN
mInputBindAxis(map, type, axis, &realDescription);
}
static bool _loadHat(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int hatId) {
char hatKey[KEY_NAME_MAX];
struct mInputHatBindings hatBindings = { -1, -1, -1, -1 };
bool found = false;
snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId);
found = _getIntValue(config, sectionName, hatKey, &hatBindings.up) || found;
snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId);
found = _getIntValue(config, sectionName, hatKey, &hatBindings.right) || found;
snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId);
found = _getIntValue(config, sectionName, hatKey, &hatBindings.down) || found;
snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId);
found = _getIntValue(config, sectionName, hatKey, &hatBindings.left) || found;
if (!found) {
return false;
}
mInputBindHat(map, type, hatId, &hatBindings);
return true;
}
static void _saveKey(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, int key, const char* keyName) {
char keyKey[KEY_NAME_MAX];
snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
@ -239,6 +268,24 @@ static void _saveAxis(uint32_t axis, void* dp, void* up) {
}
}
static void _saveHat(const char* sectionName, struct Configuration* config, int hatId, const struct mInputHatBindings* hat) {
char hatKey[KEY_NAME_MAX];
char hatValue[KEY_VALUE_MAX];
snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId);
snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->up);
ConfigurationSetValue(config, sectionName, hatKey, hatValue);
snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId);
snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->right);
ConfigurationSetValue(config, sectionName, hatKey, hatValue);
snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId);
snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->down);
ConfigurationSetValue(config, sectionName, hatKey, hatValue);
snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId);
snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->left);
ConfigurationSetValue(config, sectionName, hatKey, hatValue);
}
void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
struct mInputAxisEnumerate* enumUser = ep;
const struct mInputAxis* description = dp;
@ -266,6 +313,10 @@ static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionNa
_loadKey(map, type, sectionName, config, i, map->info->keyId[i]);
_loadAxis(map, type, sectionName, config, i, map->info->keyId[i]);
}
i = 0;
while (_loadHat(map, type, sectionName, config, i)) {
++i;
}
return true;
}
@ -289,6 +340,11 @@ static void _saveAll(const struct mInputMap* map, uint32_t type, const char* sec
map->info
};
TableEnumerate(&impl->axes, _saveAxis, &save);
for (i = 0; i < mInputHatListSize(&impl->hats); ++i) {
const struct mInputHatBindings* hat = mInputHatListGetConstPointer(&impl->hats, i);
_saveHat(sectionName, config, i, hat);
}
}
void mInputMapInit(struct mInputMap* map, const struct mInputPlatformInfo* info) {
@ -303,6 +359,7 @@ void mInputMapDeinit(struct mInputMap* map) {
if (map->maps[m].type) {
free(map->maps[m].map);
TableDeinit(&map->maps[m].axes);
mInputHatListDeinit(&map->maps[m].hats);
}
}
free(map->maps);
@ -451,6 +508,72 @@ void mInputEnumerateAxes(const struct mInputMap* map, uint32_t type, void (handl
TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
}
int mInputMapHat(const struct mInputMap* map, uint32_t type, int id, int direction) {
const struct mInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return 0;
}
if (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
return 0;
}
const struct mInputHatBindings* description = mInputHatListGetConstPointer(&impl->hats, id);
int mapping = 0;
if (direction & M_INPUT_HAT_UP && description->up >= 0) {
mapping |= 1 << description->up;
}
if (direction & M_INPUT_HAT_RIGHT && description->right >= 0) {
mapping |= 1 << description->right;
}
if (direction & M_INPUT_HAT_DOWN && description->down >= 0) {
mapping |= 1 << description->down;
}
if (direction & M_INPUT_HAT_LEFT && description->left >= 0) {
mapping |= 1 << description->left;
}
return mapping;
}
void mInputBindHat(struct mInputMap* map, uint32_t type, int id, const struct mInputHatBindings* bindings) {
struct mInputMapImpl* impl = _guaranteeMap(map, type);
while (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
*mInputHatListAppend(&impl->hats) = (struct mInputHatBindings) { -1, -1, -1, -1 };
}
*mInputHatListGetPointer(&impl->hats, id) = *bindings;
}
bool mInputQueryHat(const struct mInputMap* map, uint32_t type, int id, struct mInputHatBindings* bindings) {
const struct mInputMapImpl* impl = _lookupMapConst(map, type);
if (!impl) {
return false;
}
if (id >= (ssize_t) mInputHatListSize(&impl->hats)) {
return false;
}
*bindings = *mInputHatListGetConstPointer(&impl->hats, id);
return true;
}
void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) {
struct mInputMapImpl* impl = _lookupMap(map, type);
if (!impl) {
return;
}
if (mInputHatListSize(&impl->hats) && id + 1 == (ssize_t) mInputHatListSize(&impl->hats)) {
mInputHatListResize(&impl->hats, -1);
} else {
struct mInputHatBindings* description = mInputHatListGetPointer(&impl->hats, id);
memset(description, -1, sizeof(&description));
}
}
void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) {
struct mInputMapImpl* impl = _lookupMap(map, type);
if (impl) {
mInputHatListClear(&impl->hats);
}
}
void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
char sectionName[SECTION_NAME_MAX];
_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);

View File

@ -6,7 +6,6 @@
#include <mgba/core/interface.h>
#include <mgba/core/core.h>
#include <time.h>
static time_t _rtcGenericCallback(struct mRTCSource* source) {
struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source;

View File

@ -1,29 +1,250 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/library.h>
#include <mgba/core/core.h>
#include <mgba-util/vfs.h>
#ifdef USE_SQLITE3
#include <sqlite3.h>
#include "feature/sqlite3/no-intro.h"
DEFINE_VECTOR(mLibraryListing, struct mLibraryEntry);
void mLibraryInit(struct mLibrary* library) {
mLibraryListingInit(&library->listing, 0);
}
struct mLibrary {
sqlite3* db;
sqlite3_stmt* insertPath;
sqlite3_stmt* insertRom;
sqlite3_stmt* insertRoot;
sqlite3_stmt* selectRom;
sqlite3_stmt* selectRoot;
sqlite3_stmt* deletePath;
sqlite3_stmt* deleteRoot;
sqlite3_stmt* count;
sqlite3_stmt* select;
const struct NoIntroDB* gameDB;
};
void mLibraryDeinit(struct mLibrary* library) {
size_t i;
for (i = 0; i < mLibraryListingSize(&library->listing); ++i) {
struct mLibraryEntry* entry = mLibraryListingGetPointer(&library->listing, i);
free(entry->filename);
free(entry->title);
#define CONSTRAINTS_ROMONLY \
"CASE WHEN :useSize THEN roms.size = :size ELSE 1 END AND " \
"CASE WHEN :usePlatform THEN roms.platform = :platform ELSE 1 END AND " \
"CASE WHEN :useCrc32 THEN roms.crc32 = :crc32 ELSE 1 END AND " \
"CASE WHEN :useInternalCode THEN roms.internalCode = :internalCode ELSE 1 END"
#define CONSTRAINTS \
CONSTRAINTS_ROMONLY " AND " \
"CASE WHEN :useFilename THEN paths.path = :path ELSE 1 END AND " \
"CASE WHEN :useRoot THEN roots.path = :root ELSE 1 END"
static void _mLibraryDeleteEntry(struct mLibrary* library, struct mLibraryEntry* entry);
static void _mLibraryInsertEntry(struct mLibrary* library, struct mLibraryEntry* entry);
static void _mLibraryAddEntry(struct mLibrary* library, const char* filename, const char* base, struct VFile* vf);
static void _bindConstraints(sqlite3_stmt* statement, const struct mLibraryEntry* constraints) {
if (!constraints) {
return;
}
int useIndex, index;
if (constraints->crc32) {
useIndex = sqlite3_bind_parameter_index(statement, ":useCrc32");
index = sqlite3_bind_parameter_index(statement, ":crc32");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_int(statement, index, constraints->crc32);
}
if (constraints->filesize) {
useIndex = sqlite3_bind_parameter_index(statement, ":useSize");
index = sqlite3_bind_parameter_index(statement, ":size");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_int64(statement, index, constraints->filesize);
}
if (constraints->filename) {
useIndex = sqlite3_bind_parameter_index(statement, ":useFilename");
index = sqlite3_bind_parameter_index(statement, ":path");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_text(statement, index, constraints->filename, -1, SQLITE_TRANSIENT);
}
if (constraints->base) {
useIndex = sqlite3_bind_parameter_index(statement, ":useRoot");
index = sqlite3_bind_parameter_index(statement, ":root");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_text(statement, index, constraints->base, -1, SQLITE_TRANSIENT);
}
if (constraints->internalCode[0]) {
useIndex = sqlite3_bind_parameter_index(statement, ":useInternalCode");
index = sqlite3_bind_parameter_index(statement, ":internalCode");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_text(statement, index, constraints->internalCode, -1, SQLITE_TRANSIENT);
}
if (constraints->platform != PLATFORM_NONE) {
useIndex = sqlite3_bind_parameter_index(statement, ":usePlatform");
index = sqlite3_bind_parameter_index(statement, ":platform");
sqlite3_bind_int(statement, useIndex, 1);
sqlite3_bind_int(statement, index, constraints->platform);
}
mLibraryListingDeinit(&library->listing);
}
void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir) {
struct mLibrary* mLibraryCreateEmpty(void) {
return mLibraryLoad(":memory:");
}
struct mLibrary* mLibraryLoad(const char* path) {
struct mLibrary* library = malloc(sizeof(*library));
memset(library, 0, sizeof(*library));
if (sqlite3_open_v2(path, &library->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) {
goto error;
}
static const char createTables[] =
" PRAGMA foreign_keys = ON;"
"\n PRAGMA journal_mode = MEMORY;"
"\n PRAGMA synchronous = NORMAL;"
"\n CREATE TABLE IF NOT EXISTS version ("
"\n tname TEXT NOT NULL PRIMARY KEY,"
"\n version INTEGER NOT NULL DEFAULT 1"
"\n );"
"\n CREATE TABLE IF NOT EXISTS roots ("
"\n rootid INTEGER NOT NULL PRIMARY KEY ASC,"
"\n path TEXT NOT NULL UNIQUE,"
"\n mtime INTEGER NOT NULL DEFAULT 0"
"\n );"
"\n CREATE TABLE IF NOT EXISTS roms ("
"\n romid INTEGER NOT NULL PRIMARY KEY ASC,"
"\n internalTitle TEXT,"
"\n internalCode TEXT,"
"\n platform INTEGER NOT NULL DEFAULT -1,"
"\n size INTEGER,"
"\n crc32 INTEGER,"
"\n md5 BLOB,"
"\n sha1 BLOB"
"\n );"
"\n CREATE TABLE IF NOT EXISTS paths ("
"\n pathid INTEGER NOT NULL PRIMARY KEY ASC,"
"\n romid INTEGER NOT NULL REFERENCES roms(romid) ON DELETE CASCADE,"
"\n path TEXT NOT NULL,"
"\n mtime INTEGER NOT NULL DEFAULT 0,"
"\n rootid INTEGER REFERENCES roots(rootid) ON DELETE CASCADE,"
"\n customTitle TEXT,"
"\n CONSTRAINT location UNIQUE (path, rootid)"
"\n );"
"\n CREATE INDEX IF NOT EXISTS crc32 ON roms (crc32);"
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('version', 1);"
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('roots', 1);"
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('roms', 1);"
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('paths', 1);";
if (sqlite3_exec(library->db, createTables, NULL, NULL, NULL)) {
goto error;
}
static const char insertPath[] = "INSERT INTO paths (romid, path, customTitle, rootid) VALUES (?, ?, ?, ?);";
if (sqlite3_prepare_v2(library->db, insertPath, -1, &library->insertPath, NULL)) {
goto error;
}
static const char insertRom[] = "INSERT INTO roms (crc32, size, internalCode, platform) VALUES (:crc32, :size, :internalCode, :platform);";
if (sqlite3_prepare_v2(library->db, insertRom, -1, &library->insertRom, NULL)) {
goto error;
}
static const char insertRoot[] = "INSERT INTO roots (path) VALUES (?);";
if (sqlite3_prepare_v2(library->db, insertRoot, -1, &library->insertRoot, NULL)) {
goto error;
}
static const char deleteRoot[] = "DELETE FROM roots WHERE path = ?;";
if (sqlite3_prepare_v2(library->db, deleteRoot, -1, &library->deleteRoot, NULL)) {
goto error;
}
static const char deletePath[] = "DELETE FROM paths WHERE path = ?;";
if (sqlite3_prepare_v2(library->db, deletePath, -1, &library->deletePath, NULL)) {
goto error;
}
static const char selectRom[] = "SELECT romid FROM roms WHERE " CONSTRAINTS_ROMONLY ";";
if (sqlite3_prepare_v2(library->db, selectRom, -1, &library->selectRom, NULL)) {
goto error;
}
static const char selectRoot[] = "SELECT rootid FROM roots WHERE path = ? AND CASE WHEN :useMtime THEN mtime <= :mtime ELSE 1 END;";
if (sqlite3_prepare_v2(library->db, selectRoot, -1, &library->selectRoot, NULL)) {
goto error;
}
static const char count[] = "SELECT count(pathid) FROM paths JOIN roots USING (rootid) JOIN roms USING (romid) WHERE " CONSTRAINTS ";";
if (sqlite3_prepare_v2(library->db, count, -1, &library->count, NULL)) {
goto error;
}
static const char select[] = "SELECT *, paths.path AS filename, roots.path AS base FROM paths JOIN roots USING (rootid) JOIN roms USING (romid) WHERE " CONSTRAINTS " LIMIT :count OFFSET :offset;";
if (sqlite3_prepare_v2(library->db, select, -1, &library->select, NULL)) {
goto error;
}
return library;
error:
mLibraryDestroy(library);
return NULL;
}
void mLibraryDestroy(struct mLibrary* library) {
sqlite3_finalize(library->insertPath);
sqlite3_finalize(library->insertRom);
sqlite3_finalize(library->insertRoot);
sqlite3_finalize(library->deletePath);
sqlite3_finalize(library->deleteRoot);
sqlite3_finalize(library->selectRom);
sqlite3_finalize(library->selectRoot);
sqlite3_finalize(library->select);
sqlite3_finalize(library->count);
sqlite3_close(library->db);
}
void mLibraryLoadDirectory(struct mLibrary* library, const char* base) {
struct VDir* dir = VDirOpenArchive(base);
if (!dir) {
dir = VDirOpen(base);
}
sqlite3_exec(library->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
if (!dir) {
sqlite3_clear_bindings(library->deleteRoot);
sqlite3_reset(library->deleteRoot);
sqlite3_bind_text(library->deleteRoot, 1, base, -1, SQLITE_TRANSIENT);
sqlite3_step(library->deleteRoot);
sqlite3_exec(library->db, "COMMIT;", NULL, NULL, NULL);
return;
}
struct mLibraryEntry entry;
memset(&entry, 0, sizeof(entry));
entry.base = base;
struct mLibraryListing entries;
mLibraryListingInit(&entries, 0);
mLibraryGetEntries(library, &entries, 0, 0, &entry);
size_t i;
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
struct mLibraryEntry* current = mLibraryListingGetPointer(&entries, i);
struct VFile* vf = dir->openFile(dir, current->filename, O_RDONLY);
_mLibraryDeleteEntry(library, current);
if (!vf) {
continue;
}
_mLibraryAddEntry(library, current->filename, base, vf);
}
mLibraryListingDeinit(&entries);
dir->rewind(dir);
struct VDirEntry* dirent = dir->listNext(dir);
while (dirent) {
struct VFile* vf = dir->openFile(dir, dirent->name(dirent), O_RDONLY);
@ -31,33 +252,187 @@ void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir) {
dirent = dir->listNext(dir);
continue;
}
mLibraryAddEntry(library, dirent->name(dirent), vf);
_mLibraryAddEntry(library, dirent->name(dirent), base, vf);
dirent = dir->listNext(dir);
}
dir->close(dir);
sqlite3_exec(library->db, "COMMIT;", NULL, NULL, NULL);
}
void mLibraryAddEntry(struct mLibrary* library, const char* filename, struct VFile* vf) {
void _mLibraryAddEntry(struct mLibrary* library, const char* filename, const char* base, struct VFile* vf) {
struct mCore* core;
if (!vf) {
vf = VFileOpen(filename, O_RDONLY);
}
if (!vf) {
return;
}
core = mCoreFindVF(vf);
if (core) {
struct mLibraryEntry entry;
memset(&entry, 0, sizeof(entry));
core->init(core);
core->loadROM(core, vf);
struct mLibraryEntry* entry = mLibraryListingAppend(&library->listing);
core->getGameTitle(core, entry->internalTitle);
core->getGameCode(core, entry->internalCode);
entry->title = NULL;
entry->filename = strdup(filename);
entry->filesize = vf->size(vf);
core->getGameTitle(core, entry.internalTitle);
core->getGameCode(core, entry.internalCode);
core->checksum(core, &entry.crc32, CHECKSUM_CRC32);
entry.platform = core->platform(core);
entry.title = NULL;
entry.base = base;
entry.filename = filename;
entry.filesize = vf->size(vf);
_mLibraryInsertEntry(library, &entry);
// Note: this destroys the VFile
core->deinit(core);
} else {
vf->close(vf);
}
}
static void _mLibraryInsertEntry(struct mLibrary* library, struct mLibraryEntry* entry) {
sqlite3_clear_bindings(library->selectRom);
sqlite3_reset(library->selectRom);
struct mLibraryEntry constraints = *entry;
constraints.filename = NULL;
constraints.base = NULL;
_bindConstraints(library->selectRom, &constraints);
sqlite3_int64 romId;
if (sqlite3_step(library->selectRom) == SQLITE_DONE) {
sqlite3_clear_bindings(library->insertRom);
sqlite3_reset(library->insertRom);
_bindConstraints(library->insertRom, entry);
sqlite3_step(library->insertRom);
romId = sqlite3_last_insert_rowid(library->db);
} else {
romId = sqlite3_column_int64(library->selectRom, 0);
}
sqlite3_int64 rootId = 0;
if (entry->base) {
sqlite3_clear_bindings(library->selectRoot);
sqlite3_reset(library->selectRoot);
sqlite3_bind_text(library->selectRoot, 1, entry->base, -1, SQLITE_TRANSIENT);
if (sqlite3_step(library->selectRoot) == SQLITE_DONE) {
sqlite3_clear_bindings(library->insertRoot);
sqlite3_reset(library->insertRoot);
sqlite3_bind_text(library->insertRoot, 1, entry->base, -1, SQLITE_TRANSIENT);
sqlite3_step(library->insertRoot);
rootId = sqlite3_last_insert_rowid(library->db);
} else {
rootId = sqlite3_column_int64(library->selectRoot, 0);
}
}
sqlite3_clear_bindings(library->insertPath);
sqlite3_reset(library->insertPath);
sqlite3_bind_int64(library->insertPath, 1, romId);
sqlite3_bind_text(library->insertPath, 2, entry->filename, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(library->insertPath, 3, entry->title, -1, SQLITE_TRANSIENT);
if (rootId > 0) {
sqlite3_bind_int64(library->insertPath, 4, rootId);
}
sqlite3_step(library->insertPath);
}
static void _mLibraryDeleteEntry(struct mLibrary* library, struct mLibraryEntry* entry) {
sqlite3_clear_bindings(library->deletePath);
sqlite3_reset(library->deletePath);
sqlite3_bind_text(library->deletePath, 1, entry->filename, -1, SQLITE_TRANSIENT);
sqlite3_step(library->insertPath);
}
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints) {
sqlite3_clear_bindings(library->count);
sqlite3_reset(library->count);
_bindConstraints(library->count, constraints);
if (sqlite3_step(library->count) != SQLITE_ROW) {
return 0;
}
return sqlite3_column_int64(library->count, 0);
}
size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints) {
mLibraryListingClear(out); // TODO: Free memory
sqlite3_clear_bindings(library->select);
sqlite3_reset(library->select);
_bindConstraints(library->select, constraints);
int countIndex = sqlite3_bind_parameter_index(library->select, ":count");
int offsetIndex = sqlite3_bind_parameter_index(library->select, ":offset");
sqlite3_bind_int64(library->select, countIndex, numEntries ? numEntries : -1);
sqlite3_bind_int64(library->select, offsetIndex, offset);
size_t entryIndex;
for (entryIndex = 0; (!numEntries || entryIndex < numEntries) && sqlite3_step(library->select) == SQLITE_ROW; ++entryIndex) {
struct mLibraryEntry* entry = mLibraryListingAppend(out);
memset(entry, 0, sizeof(*entry));
int nCols = sqlite3_column_count(library->select);
int i;
for (i = 0; i < nCols; ++i) {
const char* colName = sqlite3_column_name(library->select, i);
if (strcmp(colName, "crc32") == 0) {
entry->crc32 = sqlite3_column_int(library->select, i);
struct NoIntroGame game;
if (NoIntroDBLookupGameByCRC(library->gameDB, entry->crc32, &game)) {
entry->title = strdup(game.name);
}
} else if (strcmp(colName, "platform") == 0) {
entry->platform = sqlite3_column_int(library->select, i);
} else if (strcmp(colName, "size") == 0) {
entry->filesize = sqlite3_column_int64(library->select, i);
} else if (strcmp(colName, "internalCode") == 0 && sqlite3_column_type(library->select, i) == SQLITE_TEXT) {
strncpy(entry->internalCode, (const char*) sqlite3_column_text(library->select, i), sizeof(entry->internalCode) - 1);
} else if (strcmp(colName, "internalTitle") == 0 && sqlite3_column_type(library->select, i) == SQLITE_TEXT) {
strncpy(entry->internalTitle, (const char*) sqlite3_column_text(library->select, i), sizeof(entry->internalTitle) - 1);
} else if (strcmp(colName, "filename") == 0) {
entry->filename = strdup((const char*) sqlite3_column_text(library->select, i));
} else if (strcmp(colName, "base") == 0) {
entry->base = strdup((const char*) sqlite3_column_text(library->select, i));
}
}
}
return mLibraryListingSize(out);
}
struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryEntry* entry) {
struct mLibraryListing entries;
mLibraryListingInit(&entries, 0);
if (!mLibraryGetEntries(library, &entries, 0, 0, entry)) {
mLibraryListingDeinit(&entries);
return NULL;
}
struct VFile* vf = NULL;
size_t i;
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
struct mLibraryEntry* e = mLibraryListingGetPointer(&entries, i);
struct VDir* dir = VDirOpenArchive(e->base);
bool isArchive = true;
if (!dir) {
dir = VDirOpen(e->base);
isArchive = false;
}
if (!dir) {
continue;
}
vf = dir->openFile(dir, e->filename, O_RDONLY);
if (vf && isArchive) {
struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfclone->write(vfclone, buffer, read);
}
vf->close(vf);
vf = vfclone;
}
dir->close(dir);
if (vf) {
break;
}
}
return vf;
}
void mLibraryAttachGameDB(struct mLibrary* library, const struct NoIntroDB* db) {
library->gameDB = db;
}
#endif

View File

@ -55,13 +55,11 @@ void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
}
bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent* event) {
struct mTimingEvent* const* previous = &timing->root;
const struct mTimingEvent* next = timing->root;
while (next) {
if (next == event) {
return true;
}
previous = &next->next;
next = next->next;
}
return false;

View File

@ -325,7 +325,7 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
i += 8;
}
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
_int2hex32(cpu->gprs[ARM_PC] - (ARMPSRIsT(cpu->cpsr) ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
i += 8;
stub->outgoing[i] = 0;
@ -359,7 +359,7 @@ static void _writeRegister(struct GDBStub* stub, const char* message) {
}
}
} else if (reg == 0x19) {
cpu->cpsr.packed = value;
cpu->cpsr = value;
} else {
stub->outgoing[0] = '\0';
_sendMessage(stub);
@ -379,7 +379,7 @@ static void _readRegister(struct GDBStub* stub, const char* message) {
if (reg < 0x10) {
value = cpu->gprs[reg];
} else if (reg == 0x19) {
value = cpu->cpsr.packed;
value = cpu->cpsr;
} else {
stub->outgoing[0] = '\0';
_sendMessage(stub);

View File

@ -257,7 +257,7 @@ static void DS9ProcessEvents(struct ARMCore* cpu) {
static void DSProcessEvents(struct DSCommon* dscore) {
struct ARMCore* cpu = dscore->cpu;
struct DS* ds = dscore->p;
if (dscore->springIRQ && !cpu->cpsr.i) {
if (dscore->springIRQ && !ARMPSRGetI(cpu->cpsr)) {
ARMRaiseIRQ(cpu);
dscore->springIRQ = 0;
}

View File

@ -283,6 +283,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
if (runner->core) {
mLOG(GUI_RUNNER, INFO, "Found core");
runner->core->init(runner->core);
mCoreConfigInit(&runner->core->config, runner->port);
mInputMapInit(&runner->core->inputMap, &GBAInputInfo);
found = mCoreLoadFile(runner->core, path);
if (!found) {

View File

@ -15,7 +15,11 @@ CXX_GUARD_START
#define MAGICKCORE_HDRI_ENABLE 0
#define MAGICKCORE_QUANTUM_DEPTH 8
#if MAGICKWAND_VERSION_MAJOR >= 7
#include <MagickWand/MagickWand.h>
#else
#include <wand/MagickWand.h>
#endif
struct ImageMagickGIFEncoder {
struct mAVStream d;

View File

@ -0,0 +1,299 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "no-intro.h"
#include <mgba-util/string.h>
#include <mgba-util/vector.h>
#include <mgba-util/vfs.h>
#include <sqlite3.h>
struct NoIntroDB {
sqlite3* db;
sqlite3_stmt* crc32;
};
struct NoIntroDB* NoIntroDBLoad(const char* path) {
struct NoIntroDB* db = malloc(sizeof(*db));
if (sqlite3_open_v2(path, &db->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) {
goto error;
}
static const char createTables[] =
"PRAGMA foreign_keys = ON;\n"
"PRAGMA journal_mode = MEMORY;\n"
"PRAGMA synchronous = NORMAL;\n"
"CREATE TABLE IF NOT EXISTS gamedb ("
"dbid INTEGER NOT NULL PRIMARY KEY ASC,"
"name TEXT,"
"version TEXT,"
"CONSTRAINT versioning UNIQUE (name, version)"
");\n"
"CREATE TABLE IF NOT EXISTS games ("
"gid INTEGER NOT NULL PRIMARY KEY ASC,"
"name TEXT,"
"dbid INTEGER NOT NULL REFERENCES gamedb(dbid) ON DELETE CASCADE"
");\n"
"CREATE TABLE IF NOT EXISTS roms ("
"name TEXT,"
"size INTEGER,"
"crc32 INTEGER,"
"md5 BLOB,"
"sha1 BLOB,"
"flags INTEGER DEFAULT 0,"
"gid INTEGER NOT NULL REFERENCES games(gid) ON DELETE CASCADE"
");\n"
"CREATE INDEX IF NOT EXISTS crc32 ON roms (crc32);";
if (sqlite3_exec(db->db, createTables, NULL, NULL, NULL)) {
goto error;
}
static const char selectRom[] = "SELECT * FROM games JOIN roms USING (gid) WHERE roms.crc32 = ?;";
if (sqlite3_prepare_v2(db->db, selectRom, -1, &db->crc32, NULL)) {
goto error;
}
return db;
error:
if (db->crc32) {
sqlite3_finalize(db->crc32);
}
NoIntroDBDestroy(db);
return NULL;
}
bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf) {
struct NoIntroGame buffer = { 0 };
sqlite3_stmt* gamedbTable = NULL;
sqlite3_stmt* gamedbDrop = NULL;
sqlite3_stmt* gameTable = NULL;
sqlite3_stmt* romTable = NULL;
char* fieldName = NULL;
sqlite3_int64 currentGame = -1;
sqlite3_int64 currentDb = -1;
char* dbType = NULL;
char* dbVersion = NULL;
char line[512];
static const char insertGamedb[] = "INSERT INTO gamedb (name, version) VALUES (?, ?);";
if (sqlite3_prepare_v2(db->db, insertGamedb, -1, &gamedbTable, NULL)) {
return false;
}
static const char deleteGamedb[] = "DELETE FROM gamedb WHERE name = ? AND version < ?;";
if (sqlite3_prepare_v2(db->db, deleteGamedb, -1, &gamedbDrop, NULL)) {
return false;
}
static const char insertGame[] = "INSERT INTO games (dbid, name) VALUES (?, ?);";
if (sqlite3_prepare_v2(db->db, insertGame, -1, &gameTable, NULL)) {
return false;
}
static const char insertRom[] = "INSERT INTO roms (gid, name, size, crc32, md5, sha1, flags) VALUES (:game, :name, :size, :crc32, :md5, :sha1, :flags);";
if (sqlite3_prepare_v2(db->db, insertRom, -1, &romTable, NULL)) {
return false;
}
size_t remainingInTransaction = 0;
while (true) {
ssize_t bytesRead = vf->readline(vf, line, sizeof(line));
if (!bytesRead) {
break;
}
ssize_t i;
const char* token;
for (i = 0; i < bytesRead; ++i) {
while (isspace((int) line[i]) && i < bytesRead) {
++i;
}
if (i >= bytesRead) {
break;
}
token = &line[i];
while (!isspace((int) line[i]) && i < bytesRead) {
++i;
}
if (i >= bytesRead) {
break;
}
switch (token[0]) {
case '(':
if (!fieldName) {
break;
}
if (!remainingInTransaction) {
remainingInTransaction = 16;
sqlite3_exec(db->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
} else {
--remainingInTransaction;
}
if (strcmp(fieldName, "clrmamepro") == 0) {
free((void*) dbType);
free((void*) dbVersion);
dbType = NULL;
dbVersion = NULL;
currentDb = -1;
currentGame = -1;
} else if (currentDb >= 0 && strcmp(fieldName, "game") == 0) {
free((void*) buffer.name);
free((void*) buffer.romName);
memset(&buffer, 0, sizeof(buffer));
currentGame = -1;
} else if (currentDb >= 0 && strcmp(fieldName, "rom") == 0) {
sqlite3_clear_bindings(gameTable);
sqlite3_reset(gameTable);
sqlite3_bind_int64(gameTable, 1, currentDb);
sqlite3_bind_text(gameTable, 2, buffer.name, -1, SQLITE_TRANSIENT);
sqlite3_step(gameTable);
currentGame = sqlite3_last_insert_rowid(db->db);
}
free(fieldName);
fieldName = NULL;
break;
case ')':
if (currentDb < 0 && dbType && dbVersion) {
sqlite3_clear_bindings(gamedbDrop);
sqlite3_reset(gamedbDrop);
sqlite3_bind_text(gamedbDrop, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbDrop, 2, dbVersion, -1, SQLITE_TRANSIENT);
sqlite3_step(gamedbDrop);
sqlite3_clear_bindings(gamedbTable);
sqlite3_reset(gamedbTable);
sqlite3_bind_text(gamedbTable, 1, dbType, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(gamedbTable, 2, dbVersion, -1, SQLITE_TRANSIENT);
if (sqlite3_step(gamedbTable) == SQLITE_DONE) {
currentDb = sqlite3_last_insert_rowid(db->db);
}
free((void*) dbType);
free((void*) dbVersion);
dbType = NULL;
dbVersion = NULL;
}
if (currentGame >= 0 && buffer.romName) {
sqlite3_clear_bindings(romTable);
sqlite3_reset(romTable);
sqlite3_bind_int64(romTable, 1, currentGame);
sqlite3_bind_text(romTable, 2, buffer.romName, -1, SQLITE_TRANSIENT);
sqlite3_bind_int64(romTable, 3, buffer.size);
sqlite3_bind_int(romTable, 4, buffer.crc32);
sqlite3_bind_blob(romTable, 5, buffer.md5, sizeof(buffer.md5), NULL);
sqlite3_bind_blob(romTable, 6, buffer.sha1, sizeof(buffer.sha1), NULL);
sqlite3_bind_int(romTable, 7, buffer.verified);
sqlite3_step(romTable);
free((void*) buffer.romName);
buffer.romName = NULL;
}
if (!remainingInTransaction) {
sqlite3_exec(db->db, "COMMIT;", NULL, NULL, NULL);
}
break;
case '"':
++token;
for (; line[i] != '"' && i < bytesRead; ++i);
// Fall through
default:
line[i] = '\0';
if (fieldName) {
if (currentGame >= 0) {
if (strcmp("name", fieldName) == 0) {
free((void*) buffer.romName);
buffer.romName = strdup(token);
} else if (strcmp("size", fieldName) == 0) {
char* end;
unsigned long value = strtoul(token, &end, 10);
if (end) {
buffer.size = value;
}
} else if (strcmp("crc", fieldName) == 0) {
char* end;
unsigned long value = strtoul(token, &end, 16);
if (end) {
buffer.crc32 = value;
}
} else if (strcmp("md5", fieldName) == 0) {
size_t b;
for (b = 0; b < sizeof(buffer.md5) && token && *token; ++b) {
token = hex8(token, &buffer.md5[b]);
}
} else if (strcmp("sha1", fieldName) == 0) {
size_t b;
for (b = 0; b < sizeof(buffer.sha1) && token && *token; ++b) {
token = hex8(token, &buffer.sha1[b]);
}
} else if (strcmp("flags", fieldName) == 0) {
buffer.verified = strcmp("verified", fieldName) == 0;
}
} else if (currentDb >= 0) {
if (strcmp("name", fieldName) == 0) {
free((void*) buffer.name);
buffer.name = strdup(token);
}
} else {
if (strcmp("name", fieldName) == 0) {
free((void*) dbType);
dbType = strdup(token);
} else if (strcmp("version", fieldName) == 0) {
free((void*) dbVersion);
dbVersion = strdup(token);
}
}
free(fieldName);
fieldName = NULL;
} else {
fieldName = strdup(token);
}
break;
}
}
}
free((void*) buffer.name);
free((void*) buffer.romName);
free((void*) dbType);
free((void*) dbVersion);
sqlite3_finalize(gameTable);
sqlite3_finalize(romTable);
if (remainingInTransaction) {
sqlite3_exec(db->db, "COMMIT;", NULL, NULL, NULL);
}
sqlite3_exec(db->db, "VACUUM", NULL, NULL, NULL);
return true;
}
void NoIntroDBDestroy(struct NoIntroDB* db) {
sqlite3_close(db->db);
free(db);
}
bool NoIntroDBLookupGameByCRC(const struct NoIntroDB* db, uint32_t crc32, struct NoIntroGame* game) {
if (!db) {
return false;
}
sqlite3_clear_bindings(db->crc32);
sqlite3_reset(db->crc32);
sqlite3_bind_int(db->crc32, 1, crc32);
if (sqlite3_step(db->crc32) != SQLITE_ROW) {
return false;
}
game->name = (const char*) sqlite3_column_text(db->crc32, 1);
game->romName = (const char*) sqlite3_column_text(db->crc32, 3);
game->size = sqlite3_column_int(db->crc32, 4);
game->crc32 = sqlite3_column_int(db->crc32, 5);
// TODO: md5/sha1
game->verified = sqlite3_column_int(db->crc32, 8);
return true;
}

View File

@ -1,10 +1,10 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef NOINTRO_H
#define NOINTRO_H
#ifndef NO_INTRO_H
#define NO_INTRO_H
#include <mgba-util/common.h>
@ -13,7 +13,6 @@ CXX_GUARD_START
struct NoIntroGame {
const char* name;
const char* romName;
const char* description;
size_t size;
uint32_t crc32;
uint8_t md5[16];
@ -24,7 +23,8 @@ struct NoIntroGame {
struct NoIntroDB;
struct VFile;
struct NoIntroDB* NoIntroDBLoad(struct VFile* vf);
struct NoIntroDB* NoIntroDBLoad(const char* path);
bool NoIntroDBLoadClrMamePro(struct NoIntroDB* db, struct VFile* vf);
void NoIntroDBDestroy(struct NoIntroDB* db);
bool NoIntroDBLookupGameByCRC(const struct NoIntroDB* db, uint32_t crc32, struct NoIntroGame* game);

View File

@ -698,6 +698,10 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) {
envelope->stepTime = GBAudioRegisterSweepGetStepTime(value);
envelope->direction = GBAudioRegisterSweepGetDirection(value);
envelope->initialVolume = GBAudioRegisterSweepGetInitialVolume(value);
if (!envelope->stepTime) {
// TODO: Improve "zombie" mode
++envelope->currentVolume;
}
_updateEnvelopeDead(envelope);
envelope->nextStep = envelope->stepTime;
return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;

View File

@ -103,6 +103,8 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
gb->audio.masterVolume = core->opts.volume;
}
gb->video.frameskip = core->opts.frameskip;
mCoreConfigCopyValue(&core->config, config, "gb.bios");
mCoreConfigCopyValue(&core->config, config, "gbc.bios");
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct GBCore* gbcore = (struct GBCore*) core;
@ -211,6 +213,16 @@ static void _GBCoreUnloadROM(struct mCore* core) {
return GBUnloadROM(core->board);
}
static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
struct GB* gb = (struct GB*) core->board;
switch (type) {
case CHECKSUM_CRC32:
memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
break;
}
return;
}
static void _GBCoreReset(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = (struct GB*) core->board;
@ -228,8 +240,8 @@ static void _GBCoreReset(struct mCore* core) {
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct VFile* bios = NULL;
if (core->opts.useBios) {
if (!gb->biosVf && core->opts.useBios) {
struct VFile* bios = NULL;
bool found = false;
if (core->opts.bios) {
bios = VFileOpen(core->opts.bios, O_RDONLY);
@ -241,8 +253,31 @@ static void _GBCoreReset(struct mCore* core) {
}
}
if (!found) {
char path[PATH_MAX];
GBDetectModel(gb);
const char* configPath;
switch (gb->model) {
case GB_MODEL_DMG:
case GB_MODEL_SGB: // TODO
configPath = mCoreConfigGetValue(&core->config, "gb.bios");
break;
case GB_MODEL_CGB:
case GB_MODEL_AGB:
configPath = mCoreConfigGetValue(&core->config, "gbc.bios");
break;
default:
break;
};
bios = VFileOpen(configPath, O_RDONLY);
if (bios && GBIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
bios = NULL;
}
}
if (!found) {
char path[PATH_MAX];
mCoreConfigDirectory(path, PATH_MAX);
switch (gb->model) {
case GB_MODEL_DMG:
@ -257,10 +292,16 @@ static void _GBCoreReset(struct mCore* core) {
break;
};
bios = VFileOpen(path, O_RDONLY);
if (bios && GBIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
bios = NULL;
}
}
if (bios) {
GBLoadBIOS(gb, bios);
}
}
if (bios) {
GBLoadBIOS(gb, bios);
}
#endif
@ -543,6 +584,7 @@ struct mCore* GBCoreCreate(void) {
core->loadTemporarySave = _GBCoreLoadTemporarySave;
core->loadPatch = _GBCoreLoadPatch;
core->unloadROM = _GBCoreUnloadROM;
core->checksum = _GBCoreChecksum;
core->reset = _GBCoreReset;
core->runFrame = _GBCoreRunFrame;
core->runLoop = _GBCoreRunLoop;

View File

@ -34,6 +34,9 @@ void GBMBCSwitchBank(struct GBMemory* memory, int bank) {
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
bankStart &= (memory->romSize - 1);
bank = bankStart / GB_SIZE_CART_BANK0;
if (!bank) {
++bank;
}
}
memory->romBank = &memory->rom[bankStart];
memory->currentBank = bank;

View File

@ -19,7 +19,7 @@ mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory");
static void _pristineCow(struct GB* gba);
static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) {
if (UNLIKELY(address > cpu->memory.activeRegionEnd)) {
if (UNLIKELY(address >= cpu->memory.activeRegionEnd)) {
cpu->memory.setActiveRegion(cpu, address);
return cpu->memory.cpuLoad8(cpu, address);
}

View File

@ -11,12 +11,6 @@
mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate");
#ifdef _MSC_VER
#include <time.h>
#else
#include <sys/time.h>
#endif
const uint32_t GB_SAVESTATE_MAGIC = 0x00400000;
const uint32_t GB_SAVESTATE_VERSION = 0x00000001;

View File

@ -321,6 +321,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
video->p->memory.io[REG_STAT] = video->stat;
video->ly = 0;
video->p->memory.io[REG_LY] = 0;
mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
}
video->p->memory.io[REG_STAT] = video->stat;

View File

@ -25,11 +25,11 @@ static void _unBitPack(struct GBA* gba);
static void _SoftReset(struct GBA* gba) {
struct ARMCore* cpu = gba->cpu;
ARMSetPrivilegeMode(cpu, MODE_IRQ);
cpu->spsr.packed = 0;
cpu->spsr = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = SP_BASE_IRQ;
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
cpu->spsr.packed = 0;
cpu->spsr = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);

View File

@ -135,6 +135,8 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con
}
}
mCoreConfigCopyValue(&core->config, config, "gba.bios");
#ifndef DISABLE_THREADING
mCoreConfigGetIntValue(config, "threadedVideo", &gbacore->threadedVideo);
#endif
@ -247,6 +249,16 @@ static void _GBACoreUnloadROM(struct mCore* core) {
return GBAUnloadROM(core->board);
}
static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChecksumType type) {
struct GBA* gba = (struct GBA*) core->board;
switch (type) {
case CHECKSUM_CRC32:
memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
break;
}
return;
}
static void _GBACoreReset(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = (struct GBA*) core->board;
@ -270,19 +282,43 @@ static void _GBACoreReset(struct mCore* core) {
}
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct VFile* bios = 0;
if (core->opts.useBios) {
if (!core->opts.bios) {
if (!gba->biosVf && core->opts.useBios) {
struct VFile* bios = NULL;
bool found = false;
if (core->opts.bios) {
bios = VFileOpen(core->opts.bios, O_RDONLY);
if (bios && GBAIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
bios = NULL;
}
}
if (!found) {
const char* configPath = mCoreConfigGetValue(&core->config, "gba.bios");
bios = VFileOpen(configPath, O_RDONLY);
if (bios && GBAIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
bios = NULL;
}
}
if (!found) {
char path[PATH_MAX];
mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
bios = VFileOpen(path, O_RDONLY);
} else {
bios = VFileOpen(core->opts.bios, O_RDONLY);
if (bios && GBIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
bios = NULL;
}
}
if (bios) {
GBALoadBIOS(gba, bios);
}
}
if (bios) {
GBALoadBIOS(gba, bios);
}
#endif
@ -562,6 +598,7 @@ struct mCore* GBACoreCreate(void) {
core->loadTemporarySave = _GBACoreLoadTemporarySave;
core->loadPatch = _GBACoreLoadPatch;
core->unloadROM = _GBACoreUnloadROM;
core->checksum = _GBACoreChecksum;
core->reset = _GBACoreReset;
core->runFrame = _GBACoreRunFrame;
core->runLoop = _GBACoreRunLoop;

View File

@ -61,7 +61,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
GBAInterruptHandlerInit(&gba->cpu->irqh);
GBAMemoryInit(gba);
GBASavedataInit(&gba->memory.savedata, 0);
gba->memory.savedata.timing = &gba->timing;
GBASavedataInit(&gba->memory.savedata, NULL);
gba->video.p = gba;
GBAVideoInit(&gba->video);
@ -225,7 +227,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
gba->bus |= cpu->prefetch[1] << 16;
}
if (gba->springIRQ && !cpu->cpsr.i) {
if (gba->springIRQ && !ARMPSRIsI(cpu->cpsr)) {
ARMRaiseIRQ(cpu);
gba->springIRQ = 0;
}

View File

@ -21,5 +21,11 @@ const struct mInputPlatformInfo GBAInputInfo = {
"R",
"L"
},
.nKeys = GBA_KEY_MAX
.nKeys = GBA_KEY_MAX,
.hat = {
.up = GBA_KEY_UP,
.left = GBA_KEY_LEFT,
.down = GBA_KEY_DOWN,
.right = GBA_KEY_RIGHT
}
};

View File

@ -21,9 +21,9 @@
// Other games vary from very little, with a fairly solid 20500 cycle count. (Observed on a SST (D4BF) chip).
// An average estimation is as follows.
#define FLASH_ERASE_CYCLES 30000
#define FLASH_PROGRAM_CYCLES 18000
#define FLASH_PROGRAM_CYCLES 650
// This needs real testing, and is only an estimation currently
#define EEPROM_SETTLE_CYCLES 1450
#define EEPROM_SETTLE_CYCLES 115000
#define CLEANUP_THRESHOLD 15
mLOG_DEFINE_CATEGORY(GBA_SAVE, "GBA Savedata");
@ -32,6 +32,13 @@ static void _flashSwitchBank(struct GBASavedata* savedata, int bank);
static void _flashErase(struct GBASavedata* savedata);
static void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart);
static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(user);
UNUSED(cyclesLate);
// Funk to funky
}
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->type = SAVEDATA_AUTODETECT;
savedata->data = 0;
@ -42,6 +49,10 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->mapMode = MAP_WRITE;
savedata->dirty = 0;
savedata->dirtAge = 0;
savedata->dust.name = "GBA Savedata Settling";
savedata->dust.priority = 0x70;
savedata->dust.context = savedata;
savedata->dust.callback = _ashesToAshes;
}
void GBASavedataDeinit(struct GBASavedata* savedata) {
@ -237,7 +248,6 @@ void GBASavedataInitFlash(struct GBASavedata* savedata, bool realisticTiming) {
}
savedata->currentBank = savedata->data;
savedata->dust = 0;
savedata->realisticTiming = realisticTiming;
if (end < SIZE_CART_FLASH512) {
memset(&savedata->data[end], 0xFF, flashSize - end);
@ -262,7 +272,6 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata, bool realisticTiming) {
}
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
}
savedata->dust = 0;
savedata->realisticTiming = realisticTiming;
if (end < SIZE_CART_EEPROM) {
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end);
@ -305,10 +314,7 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
}
}
}
if (savedata->dust > 0 && (address >> 12) == savedata->settling) {
// Give some overhead for waitstates and the comparison
// This estimation can probably be improved
savedata->dust -= 5000;
if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) {
return 0x5F;
}
return savedata->currentBank[address];
@ -323,7 +329,8 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8
savedata->currentBank[address] = value;
savedata->command = FLASH_COMMAND_NONE;
if (savedata->realisticTiming) {
savedata->dust = FLASH_PROGRAM_CYCLES;
mTimingDeschedule(savedata->timing, &savedata->dust);
mTimingSchedule(savedata->timing, &savedata->dust, FLASH_PROGRAM_CYCLES);
}
break;
case FLASH_COMMAND_SWITCH_BANK:
@ -433,7 +440,8 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
savedata->dirty |= SAVEDATA_DIRT_NEW;
savedata->data[savedata->writeAddress >> 3] = current;
if (savedata->realisticTiming) {
savedata->dust = EEPROM_SETTLE_CYCLES;
mTimingDeschedule(savedata->timing, &savedata->dust);
mTimingSchedule(savedata->timing, &savedata->dust, EEPROM_SETTLE_CYCLES);
}
++savedata->writeAddress;
} else {
@ -457,12 +465,9 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
if (savedata->command != EEPROM_COMMAND_READ) {
if (!savedata->realisticTiming || savedata->dust <= 0) {
if (!savedata->realisticTiming || !mTimingIsScheduled(savedata->timing, &savedata->dust)) {
return 1;
} else {
// Give some overhead for waitstates and the comparison
// This estimation can probably be improved
--savedata->dust;
return 0;
}
}
@ -513,12 +518,18 @@ void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializ
GBASerializedSavedataFlags flags = 0;
flags = GBASerializedSavedataFlagsSetFlashState(flags, savedata->flashState);
flags = GBASerializedSavedataFlagsTestFillFlashBank(flags, savedata->currentBank == &savedata->data[0x10000]);
if (mTimingIsScheduled(savedata->timing, &savedata->dust)) {
STORE_32(savedata->dust.when - mTimingCurrentTime(savedata->timing), 0, &state->savedata.settlingDust);
flags = GBASerializedSavedataFlagsFillDustSettling(flags);
}
state->savedata.flags = flags;
STORE_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
state->savedata.readBitsRemaining = savedata->readBitsRemaining;
STORE_32(savedata->readAddress, 0, &state->savedata.readAddress);
STORE_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
STORE_16(savedata->settling, 0, &state->savedata.settlingSector);
STORE_16(savedata->dust, 0, &state->savedata.settlingDust);
}
void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerializedState* state) {
@ -529,15 +540,20 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial
savedata->command = state->savedata.command;
GBASerializedSavedataFlags flags = state->savedata.flags;
savedata->flashState = GBASerializedSavedataFlagsGetFlashState(flags);
LOAD_32(savedata->readBitsRemaining, 0, &state->savedata.readBitsRemaining);
savedata->readBitsRemaining = state->savedata.readBitsRemaining;
LOAD_32(savedata->readAddress, 0, &state->savedata.readAddress);
LOAD_32(savedata->writeAddress, 0, &state->savedata.writeAddress);
LOAD_16(savedata->settling, 0, &state->savedata.settlingSector);
LOAD_16(savedata->dust, 0, &state->savedata.settlingDust);
if (savedata->type == SAVEDATA_FLASH1M) {
_flashSwitchBank(savedata, GBASerializedSavedataFlagsGetFlashBank(flags));
}
if (GBASerializedSavedataFlagsIsDustSettling(flags)) {
uint32_t when;
LOAD_32(when, 0, &state->savedata.settlingDust);
mTimingSchedule(savedata->timing, &savedata->dust, when);
}
}
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
@ -571,7 +587,8 @@ void _flashEraseSector(struct GBASavedata* savedata, uint16_t sectorStart) {
}
savedata->settling = sectorStart >> 12;
if (savedata->realisticTiming) {
savedata->dust = FLASH_ERASE_CYCLES;
mTimingDeschedule(savedata->timing, &savedata->dust);
mTimingSchedule(savedata->timing, &savedata->dust, FLASH_ERASE_CYCLES);
}
memset(&savedata->currentBank[sectorStart & ~(size - 1)], 0xFF, size);
}

View File

@ -13,11 +13,6 @@
#include <mgba-util/vfs.h>
#include <fcntl.h>
#ifdef _MSC_VER
#include <time.h>
#else
#include <sys/time.h>
#endif
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002;
@ -47,8 +42,8 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
for (i = 0; i < 16; ++i) {
STORE_32(gba->cpu->gprs[i], i * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
}
STORE_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
STORE_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
STORE_32(gba->cpu->cpsr, 0, &state->cpu.cpsr);
STORE_32(gba->cpu->spsr, 0, &state->cpu.spsr);
STORE_32(gba->cpu->cycles, 0, &state->cpu.cycles);
STORE_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
for (i = 0; i < 6; ++i) {
@ -156,8 +151,8 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
for (i = 0; i < 16; ++i) {
LOAD_32(gba->cpu->gprs[i], i * sizeof(gba->cpu->gprs[0]), state->cpu.gprs);
}
LOAD_32(gba->cpu->cpsr.packed, 0, &state->cpu.cpsr.packed);
LOAD_32(gba->cpu->spsr.packed, 0, &state->cpu.spsr.packed);
LOAD_32(gba->cpu->cpsr, 0, &state->cpu.cpsr);
LOAD_32(gba->cpu->spsr, 0, &state->cpu.spsr);
LOAD_32(gba->cpu->cycles, 0, &state->cpu.cycles);
LOAD_32(gba->cpu->nextEvent, 0, &state->cpu.nextEvent);
for (i = 0; i < 6; ++i) {
@ -167,13 +162,13 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
}
LOAD_32(gba->cpu->bankedSPSRs[i], i * sizeof(gba->cpu->bankedSPSRs[0]), state->cpu.bankedSPSRs);
}
gba->cpu->privilegeMode = gba->cpu->cpsr.priv;
gba->cpu->privilegeMode = ARMPSRGetPriv(gba->cpu->cpsr);
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) {
LOAD_32(gba->memory.biosPrefetch, 0, &state->biosPrefetch);
}
LOAD_32(gba->memory.lastPrefetchedPc, 0, &state->lastPrefetchedPc);
if (gba->cpu->cpsr.t) {
if (ARMPSRIsT(gba->cpu->cpsr)) {
gba->cpu->executionMode = MODE_THUMB;
if (state->cpuPrefetch[0] && state->cpuPrefetch[1]) {
LOAD_32(gba->cpu->prefetch[0], 0, state->cpuPrefetch);

View File

@ -7,10 +7,14 @@
#include <mgba/core/core.h>
#include <mgba/internal/debugger/cli-debugger.h>
#include <mgba/internal/lr35902/decoder.h>
#include <mgba/internal/lr35902/lr35902.h>
static void _printStatus(struct CLIDebuggerSystem*);
static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv);
static uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address, int segment);
static struct CLIDebuggerCommandSummary _lr35902Commands[] = {
{ 0, 0, 0, 0 }
};
@ -23,6 +27,55 @@ static inline void _printFlags(struct CLIDebuggerBackend* be, union FlagRegister
f.c ? 'C' : '-');
}
static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) {
struct LR35902Core* cpu = debugger->p->d.core->cpu;
uint16_t address;
size_t size;
if (!dv || dv->type != CLIDV_INT_TYPE) {
address = cpu->pc;
} else {
address = dv->intValue;
dv = dv->next;
}
if (!dv || dv->type != CLIDV_INT_TYPE) {
size = 1;
} else {
size = dv->intValue;
dv = dv->next; // TODO: Check for excess args
}
size_t i;
for (i = 0; i < size; ++i) {
address = _printLine(debugger->p, address, -1);
}
}
static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address, int segment) {
struct CLIDebuggerBackend* be = debugger->backend;
struct LR35902InstructionInfo info = {};
char disassembly[48];
char* disPtr = disassembly;
if (segment >= 0) {
be->printf(be, "%02X: ", segment);
}
be->printf(be, "%04X: ", address);
uint8_t instruction;
size_t bytesRemaining = 1;
for (bytesRemaining = 1; bytesRemaining; --bytesRemaining) {
instruction = debugger->d.core->rawRead8(debugger->d.core, address, segment);
disPtr += snprintf(disPtr, sizeof(disassembly) - (disPtr - disassembly), "%02X", instruction);
++address;
bytesRemaining += LR35902Decode(instruction, &info);
};
disPtr[0] = '\t';
++disPtr;
LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
be->printf(be, "%s\n", disassembly);
return address;
}
static void _printStatus(struct CLIDebuggerSystem* debugger) {
struct CLIDebuggerBackend* be = debugger->p->backend;
struct LR35902Core* cpu = debugger->p->d.core->cpu;
@ -32,6 +85,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl);
be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp);
_printFlags(be, cpu->f);
_printLine(debugger->p, cpu->pc, -1);
}
static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) {
@ -84,7 +138,7 @@ static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, co
void LR35902CLIDebuggerCreate(struct CLIDebuggerSystem* debugger) {
debugger->printStatus = _printStatus;
debugger->disassemble = NULL;
debugger->disassemble = _disassemble;
debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier;
debugger->platformName = "GB-Z80";
debugger->platformCommands = _lr35902Commands;

563
src/lr35902/decoder.c Normal file
View File

@ -0,0 +1,563 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/lr35902/decoder.h>
#include <mgba/internal/lr35902/emitter-lr35902.h>
#include <mgba/internal/lr35902/lr35902.h>
typedef size_t (*LR35902Decoder)(uint8_t opcode, struct LR35902InstructionInfo* info);
#define DEFINE_DECODER_LR35902(NAME, BODY) \
static size_t _LR35902Decode ## NAME (uint8_t opcode, struct LR35902InstructionInfo* info) { \
UNUSED(opcode); \
info->mnemonic = LR35902_MN_RST; \
BODY; \
return 0; \
}
DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;)
#define DEFINE_LD_DECODER_LR35902_NOHL(NAME) \
DEFINE_DECODER_LR35902(LD ## NAME ## _A, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_A) \
DEFINE_DECODER_LR35902(LD ## NAME ## _B, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_B) \
DEFINE_DECODER_LR35902(LD ## NAME ## _C, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_C) \
DEFINE_DECODER_LR35902(LD ## NAME ## _D, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_D) \
DEFINE_DECODER_LR35902(LD ## NAME ## _E, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_E) \
DEFINE_DECODER_LR35902(LD ## NAME ## _H, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_H) \
DEFINE_DECODER_LR35902(LD ## NAME ## _L, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
info->op2.reg = LR35902_REG_L)
#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->op2.reg = LR35902_REG_ ## REG; \
info->op2.flags = LR35902_OP_FLAG_MEMORY;)
#define DEFINE_LD_DECODER_LR35902_MEM_2(NAME, REG) \
DEFINE_DECODER_LR35902(LD ## REG ## _ ## NAME, info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## REG; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_ ## NAME;)
#define DEFINE_LD_DECODER_LR35902(NAME) \
DEFINE_LD_DECODER_LR35902_MEM(NAME, HL) \
DEFINE_LD_DECODER_LR35902_MEM_2(NAME, HL) \
DEFINE_DECODER_LR35902(LD ## NAME ## _, info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \
return 1;) \
DEFINE_LD_DECODER_LR35902_NOHL(NAME)
#define DEFINE_LD_2_DECODER_LR35902(NAME) \
DEFINE_DECODER_LR35902(LD ## NAME, info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_ ## NAME; \
return 2;)
DEFINE_LD_DECODER_LR35902(B);
DEFINE_LD_DECODER_LR35902(C);
DEFINE_LD_DECODER_LR35902(D);
DEFINE_LD_DECODER_LR35902(E);
DEFINE_LD_DECODER_LR35902(H);
DEFINE_LD_DECODER_LR35902(L);
DEFINE_LD_DECODER_LR35902(A);
DEFINE_LD_DECODER_LR35902_MEM(A, BC);
DEFINE_LD_DECODER_LR35902_MEM(A, DE);
DEFINE_LD_2_DECODER_LR35902(BC);
DEFINE_LD_2_DECODER_LR35902(DE);
DEFINE_LD_2_DECODER_LR35902(HL);
DEFINE_LD_2_DECODER_LR35902(SP);
DEFINE_DECODER_LR35902(LDHL_, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_HL; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
return 1;)
DEFINE_DECODER_LR35902(LDHL_SP, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_HL; \
info->op2.reg = LR35902_REG_SP; \
return 1;)
DEFINE_DECODER_LR35902(LDSP_HL, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_SP; \
info->op2.reg = LR35902_REG_HL;)
DEFINE_DECODER_LR35902(LDAIOC, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op2.reg = LR35902_REG_C; \
info->op2.immediate = 0xFF00; \
info->op2.flags = LR35902_OP_FLAG_MEMORY;)
DEFINE_DECODER_LR35902(LDIOCA, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_C; \
info->op1.immediate = 0xFF00; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A;)
DEFINE_DECODER_LR35902(LDAIO, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op2.immediate = 0xFF00; \
info->op2.flags = LR35902_OP_FLAG_MEMORY; \
return 1;)
DEFINE_DECODER_LR35902(LDIOA, \
info->mnemonic = LR35902_MN_LD; \
info->op1.immediate = 0xFF00; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A; \
return 1;)
#define DEFINE_ALU_DECODER_LR35902_NOHL(NAME) \
DEFINE_DECODER_LR35902(NAME ## A, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_A) \
DEFINE_DECODER_LR35902(NAME ## B, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_B) \
DEFINE_DECODER_LR35902(NAME ## C, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_C) \
DEFINE_DECODER_LR35902(NAME ## D, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_D) \
DEFINE_DECODER_LR35902(NAME ## E, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_E) \
DEFINE_DECODER_LR35902(NAME ## H, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_H) \
DEFINE_DECODER_LR35902(NAME ## L, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_L)
#define DEFINE_ALU_DECODER_LR35902_MEM(NAME, REG) \
DEFINE_DECODER_LR35902(NAME ## REG, info->mnemonic = LR35902_MN_ ## NAME; \
info->op1.reg = LR35902_REG_HL; \
info->op1.flags = LR35902_OP_FLAG_MEMORY;)
#define DEFINE_ALU_DECODER_LR35902(NAME) \
DEFINE_ALU_DECODER_LR35902_MEM(NAME, HL) \
DEFINE_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME; \
info->op1.reg = LR35902_REG_A; \
info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \
return 1;) \
DEFINE_ALU_DECODER_LR35902_NOHL(NAME)
DEFINE_ALU_DECODER_LR35902_NOHL(INC);
DEFINE_ALU_DECODER_LR35902_NOHL(DEC);
DEFINE_ALU_DECODER_LR35902(AND);
DEFINE_ALU_DECODER_LR35902(XOR);
DEFINE_ALU_DECODER_LR35902(OR);
DEFINE_ALU_DECODER_LR35902(CP);
DEFINE_ALU_DECODER_LR35902(ADD);
DEFINE_ALU_DECODER_LR35902(ADC);
DEFINE_ALU_DECODER_LR35902(SUB);
DEFINE_ALU_DECODER_LR35902(SBC);
#define DEFINE_ALU_DECODER_LR35902_ADD_HL(REG) \
DEFINE_DECODER_LR35902(ADDHL_ ## REG, info->mnemonic = LR35902_MN_ADD; \
info->op1.reg = LR35902_REG_HL; \
info->op2.reg = LR35902_REG_ ## REG;)
DEFINE_ALU_DECODER_LR35902_ADD_HL(BC)
DEFINE_ALU_DECODER_LR35902_ADD_HL(DE)
DEFINE_ALU_DECODER_LR35902_ADD_HL(HL)
DEFINE_ALU_DECODER_LR35902_ADD_HL(SP)
DEFINE_DECODER_LR35902(ADDSP, info->mnemonic = LR35902_MN_ADD; \
info->op1.reg = LR35902_REG_SP; \
return 1;)
#define DEFINE_CONDITIONAL_DECODER_LR35902(NAME) \
DEFINE_ ## NAME ## _INSTRUCTION_LR35902(, 0) \
DEFINE_ ## NAME ## _INSTRUCTION_LR35902(C, LR35902_COND_C) \
DEFINE_ ## NAME ## _INSTRUCTION_LR35902(Z, LR35902_COND_Z) \
DEFINE_ ## NAME ## _INSTRUCTION_LR35902(NC, LR35902_COND_NC) \
DEFINE_ ## NAME ## _INSTRUCTION_LR35902(NZ, LR35902_COND_NZ)
#define DEFINE_JP_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \
DEFINE_DECODER_LR35902(JP ## CONDITION_NAME, \
info->mnemonic = LR35902_MN_JP; \
info->condition = CONDITION; \
return 2;)
#define DEFINE_JR_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \
DEFINE_DECODER_LR35902(JR ## CONDITION_NAME, \
info->mnemonic = LR35902_MN_JR; \
info->condition = CONDITION; \
return 1;)
#define DEFINE_CALL_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \
DEFINE_DECODER_LR35902(CALL ## CONDITION_NAME, \
info->mnemonic = LR35902_MN_CALL; \
info->condition = CONDITION; \
return 2;)
#define DEFINE_RET_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \
DEFINE_DECODER_LR35902(RET ## CONDITION_NAME, \
info->mnemonic = LR35902_MN_RET; \
info->condition = CONDITION;)
DEFINE_CONDITIONAL_DECODER_LR35902(JP);
DEFINE_CONDITIONAL_DECODER_LR35902(JR);
DEFINE_CONDITIONAL_DECODER_LR35902(CALL);
DEFINE_CONDITIONAL_DECODER_LR35902(RET);
DEFINE_DECODER_LR35902(JPHL, \
info->mnemonic = LR35902_MN_JP; \
info->op1.reg = LR35902_REG_HL)
DEFINE_DECODER_LR35902(RETI, info->mnemonic = LR35902_MN_RETI)
DEFINE_DECODER_LR35902(LDBC_A, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_BC; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A;)
DEFINE_DECODER_LR35902(LDDE_A, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_DE; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A;)
DEFINE_DECODER_LR35902(LDIA, \
info->mnemonic = LR35902_MN_LD; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A; \
return 2;)
DEFINE_DECODER_LR35902(LDAI, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op2.flags = LR35902_OP_FLAG_MEMORY; \
return 2;)
DEFINE_DECODER_LR35902(LDISP, \
info->mnemonic = LR35902_MN_LD; \
info->op1.flags = LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_SP; \
return 2;)
DEFINE_DECODER_LR35902(LDIHLA, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_HL; \
info->op1.flags = LR35902_OP_FLAG_INCREMENT | LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A;)
DEFINE_DECODER_LR35902(LDDHLA, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_HL; \
info->op1.flags = LR35902_OP_FLAG_DECREMENT | LR35902_OP_FLAG_MEMORY; \
info->op2.reg = LR35902_REG_A;)
DEFINE_DECODER_LR35902(LDA_IHL, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op2.reg = LR35902_REG_HL; \
info->op2.flags = LR35902_OP_FLAG_INCREMENT | LR35902_OP_FLAG_MEMORY;)
DEFINE_DECODER_LR35902(LDA_DHL, \
info->mnemonic = LR35902_MN_LD; \
info->op1.reg = LR35902_REG_A; \
info->op2.reg = LR35902_REG_HL; \
info->op2.flags = LR35902_OP_FLAG_DECREMENT | LR35902_OP_FLAG_MEMORY;)
#define DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(REG) \
DEFINE_DECODER_LR35902(INC ## REG, info->mnemonic = LR35902_MN_INC; info->op1.reg = LR35902_REG_ ## REG) \
DEFINE_DECODER_LR35902(DEC ## REG, info->mnemonic = LR35902_MN_DEC; info->op1.reg = LR35902_REG_ ## REG)
DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(BC);
DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(DE);
DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(HL);
DEFINE_INCDEC_WIDE_INSTRUCTION_LR35902(SP);
DEFINE_DECODER_LR35902(INC_HL,
info->mnemonic = LR35902_MN_INC;
info->op1.reg = LR35902_REG_HL;
info->op1.flags = LR35902_OP_FLAG_MEMORY;)
DEFINE_DECODER_LR35902(DEC_HL,
info->mnemonic = LR35902_MN_DEC;
info->op1.reg = LR35902_REG_HL;
info->op1.flags = LR35902_OP_FLAG_MEMORY;)
DEFINE_DECODER_LR35902(SCF, info->mnemonic = LR35902_MN_SCF)
DEFINE_DECODER_LR35902(CCF, info->mnemonic = LR35902_MN_CCF)
DEFINE_DECODER_LR35902(CPL_, info->mnemonic = LR35902_MN_CPL)
DEFINE_DECODER_LR35902(DAA, info->mnemonic = LR35902_MN_DAA)
#define DEFINE_POPPUSH_DECODER_LR35902(REG) \
DEFINE_DECODER_LR35902(POP ## REG, \
info->mnemonic = LR35902_MN_POP; \
info->op1.reg = LR35902_REG_ ## REG;) \
DEFINE_DECODER_LR35902(PUSH ## REG, \
info->mnemonic = LR35902_MN_PUSH; \
info->op1.reg = LR35902_REG_ ## REG;) \
DEFINE_POPPUSH_DECODER_LR35902(BC);
DEFINE_POPPUSH_DECODER_LR35902(DE);
DEFINE_POPPUSH_DECODER_LR35902(HL);
DEFINE_POPPUSH_DECODER_LR35902(AF);
#define DEFINE_CB_2_DECODER_LR35902(NAME, BODY) \
DEFINE_DECODER_LR35902(NAME ## B, info->op1.reg = LR35902_REG_B; BODY) \
DEFINE_DECODER_LR35902(NAME ## C, info->op1.reg = LR35902_REG_C; BODY) \
DEFINE_DECODER_LR35902(NAME ## D, info->op1.reg = LR35902_REG_D; BODY) \
DEFINE_DECODER_LR35902(NAME ## E, info->op1.reg = LR35902_REG_E; BODY) \
DEFINE_DECODER_LR35902(NAME ## H, info->op1.reg = LR35902_REG_H; BODY) \
DEFINE_DECODER_LR35902(NAME ## L, info->op1.reg = LR35902_REG_L; BODY) \
DEFINE_DECODER_LR35902(NAME ## HL, info->op1.reg = LR35902_REG_HL; BODY) \
DEFINE_DECODER_LR35902(NAME ## A, info->op1.reg = LR35902_REG_A; BODY)
#define DEFINE_CB_DECODER_LR35902(NAME, BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op2.immediate = 1; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op2.immediate = 2; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op2.immediate = 4; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op2.immediate = 8; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op2.immediate = 16; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op2.immediate = 32; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op2.immediate = 64; BODY) \
DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op2.immediate = 128; BODY)
DEFINE_CB_DECODER_LR35902(BIT, info->mnemonic = LR35902_MN_BIT)
DEFINE_CB_DECODER_LR35902(RES, info->mnemonic = LR35902_MN_RES)
DEFINE_CB_DECODER_LR35902(SET, info->mnemonic = LR35902_MN_SET)
#define DEFINE_CB_X_DECODER_LR35902(NAME) \
DEFINE_CB_2_DECODER_LR35902(NAME, info->mnemonic = LR35902_MN_ ## NAME) \
DEFINE_DECODER_LR35902(NAME ## A_, info->mnemonic = LR35902_MN_ ## NAME; info->op1.reg = LR35902_REG_A)
DEFINE_CB_X_DECODER_LR35902(RL)
DEFINE_CB_X_DECODER_LR35902(RLC)
DEFINE_CB_X_DECODER_LR35902(RR)
DEFINE_CB_X_DECODER_LR35902(RRC)
DEFINE_CB_2_DECODER_LR35902(SLA, info->mnemonic = LR35902_MN_SLA)
DEFINE_CB_2_DECODER_LR35902(SRA, info->mnemonic = LR35902_MN_SRA)
DEFINE_CB_2_DECODER_LR35902(SRL, info->mnemonic = LR35902_MN_SRL)
DEFINE_CB_2_DECODER_LR35902(SWAP, info->mnemonic = LR35902_MN_SWAP)
DEFINE_DECODER_LR35902(DI, info->mnemonic = LR35902_MN_DI)
DEFINE_DECODER_LR35902(EI, info->mnemonic = LR35902_MN_EI)
DEFINE_DECODER_LR35902(HALT, info->mnemonic = LR35902_MN_HALT)
DEFINE_DECODER_LR35902(ILL, info->mnemonic = LR35902_MN_ILL)
DEFINE_DECODER_LR35902(STOP, info->mnemonic = LR35902_MN_STOP; return 1)
#define DEFINE_RST_DECODER_LR35902(VEC) \
DEFINE_DECODER_LR35902(RST ## VEC, info->op1.immediate = 0x ## VEC;)
DEFINE_RST_DECODER_LR35902(00);
DEFINE_RST_DECODER_LR35902(08);
DEFINE_RST_DECODER_LR35902(10);
DEFINE_RST_DECODER_LR35902(18);
DEFINE_RST_DECODER_LR35902(20);
DEFINE_RST_DECODER_LR35902(28);
DEFINE_RST_DECODER_LR35902(30);
DEFINE_RST_DECODER_LR35902(38);
DEFINE_DECODER_LR35902(CB, return 1)
const LR35902Decoder _lr35902DecoderTable[0x100] = {
DECLARE_LR35902_EMITTER_BLOCK(_LR35902Decode)
};
const LR35902Decoder _lr35902CBDecoderTable[0x100] = {
DECLARE_LR35902_CB_EMITTER_BLOCK(_LR35902Decode)
};
size_t LR35902Decode(uint8_t opcode, struct LR35902InstructionInfo* info) {
if (info->opcodeSize == sizeof(info->opcode)) {
return 0;
}
info->opcode[info->opcodeSize] = opcode;
LR35902Decoder decoder;
switch (info->opcodeSize) {
case 0:
decoder = _lr35902DecoderTable[opcode];
break;
case 1:
if (info->opcode[0] == 0xCB) {
decoder = _lr35902CBDecoderTable[opcode];
break;
}
// Fall through
case 2:
++info->opcodeSize;
if (info->op1.reg) {
info->op2.immediate |= opcode << ((info->opcodeSize - 2) * 8);
} else {
info->op1.immediate |= opcode << ((info->opcodeSize - 2) * 8);
}
return 0;
}
++info->opcodeSize;
return decoder(opcode, info);
}
#define ADVANCE(AMOUNT) \
if (AMOUNT > blen) { \
buffer[blen - 1] = '\0'; \
return total; \
} \
total += AMOUNT; \
buffer += AMOUNT; \
blen -= AMOUNT;
static const char* _lr35902Conditions[] = {
NULL,
"c",
"z",
"nc",
"nz",
};
static const char* _lr35902Registers[] = {
"",
"b",
"c",
"d",
"e",
"h",
"l",
"a",
"f",
"bc",
"de",
"hl",
"af",
"sp",
"pc",
};
static const char* _lr35902MnemonicStrings[] = {
"--",
"adc",
"add",
"and",
"bit",
"call",
"ccf",
"cp",
"cpl",
"daa",
"dec",
"di",
"ei",
"halt",
"inc",
"jp",
"jr",
"ld",
"nop",
"or",
"pop",
"push",
"res",
"ret",
"reti",
"rl",
"rlc",
"rr",
"rrc",
"rst",
"sbc",
"scf",
"set",
"sla",
"sra",
"srl",
"stop",
"sub",
"swap",
"xor",
"ill"
};
static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) {
int total = 0;
if (op.flags & LR35902_OP_FLAG_IMPLICIT) {
return 0;
}
if (op.flags & LR35902_OP_FLAG_MEMORY) {
strncpy(buffer, "(", blen - 1);
ADVANCE(1);
}
if (op.immediate) {
int written = snprintf(buffer, blen - 1, "$%02X", op.immediate);
ADVANCE(written);
if (op.reg) {
strncpy(buffer, "+", blen - 1);
ADVANCE(1);
}
}
if (op.reg) {
int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]);
ADVANCE(written);
}
if (op.flags & LR35902_OP_FLAG_INCREMENT) {
strncpy(buffer, "+", blen - 1);
ADVANCE(1);
}
if (op.flags & LR35902_OP_FLAG_DECREMENT) {
strncpy(buffer, "-", blen - 1);
ADVANCE(1);
}
if (op.flags & LR35902_OP_FLAG_MEMORY) {
strncpy(buffer, ")", blen - 1);
ADVANCE(1);
}
return total;
}
int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int blen) {
const char* mnemonic = _lr35902MnemonicStrings[info->mnemonic];
int written;
int total = 0;
const char* cond = _lr35902Conditions[info->condition];
written = snprintf(buffer, blen - 1, "%s ", mnemonic);
ADVANCE(written);
if (cond) {
written = snprintf(buffer, blen - 1, "%s", cond);
ADVANCE(written);
if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) {
strncpy(buffer, ", ", blen - 1);
ADVANCE(2);
}
}
written = _decodeOperand(info->op1, buffer, blen);
ADVANCE(written);
if (info->op2.reg || info->op2.immediate) {
if (written) {
strncpy(buffer, ", ", blen - 1);
ADVANCE(2);
}
written = _decodeOperand(info->op2, buffer, blen);
ADVANCE(written);
}
buffer[blen - 1] = '\0';
return total;
}

View File

@ -1,4 +1,4 @@
set(USE_VFS_3DS ON CACNE BOOL "Use 3DS-specific file support")
set(USE_VFS_3DS ON CACHE BOOL "Use 3DS-specific file support")
mark_as_advanced(USE_VFS_3DS)
find_program(3DSLINK 3dslink)

View File

@ -13,12 +13,12 @@
#include <mgba/core/version.h>
#ifdef M_CORE_GB
#include <mgba/gb/core.h>
#include <mgba/internal/gb/gb.h>
#endif
#ifdef M_CORE_GBA
#include <mgba/gba/core.h>
#include <mgba/gba/interface.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/video.h>
#endif
#include <mgba-util/circle-buffer.h>
#include <mgba-util/memory.h>
@ -524,19 +524,28 @@ size_t retro_get_memory_size(unsigned id) {
if (id != RETRO_MEMORY_SAVE_RAM) {
return 0;
}
switch (((struct GBA*) core->board)->memory.savedata.type) {
case SAVEDATA_AUTODETECT:
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;
case SAVEDATA_FLASH512:
return SIZE_CART_FLASH512;
case SAVEDATA_EEPROM:
return SIZE_CART_EEPROM;
case SAVEDATA_SRAM:
return SIZE_CART_SRAM;
case SAVEDATA_FORCE_NONE:
return 0;
#ifdef M_CORE_GBA
if (core->platform(core) == PLATFORM_GBA) {
switch (((struct GBA*) core->board)->memory.savedata.type) {
case SAVEDATA_AUTODETECT:
case SAVEDATA_FLASH1M:
return SIZE_CART_FLASH1M;
case SAVEDATA_FLASH512:
return SIZE_CART_FLASH512;
case SAVEDATA_EEPROM:
return SIZE_CART_EEPROM;
case SAVEDATA_SRAM:
return SIZE_CART_SRAM;
case SAVEDATA_FORCE_NONE:
return 0;
}
}
#endif
#ifdef M_CORE_GB
if (core->platform(core) == PLATFORM_GB) {
return ((struct GB*) core->board)->sramSize;
}
#endif
return 0;
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -13,23 +13,17 @@ ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent)
: QDialog(parent)
{
m_ui.setupUi(this);
m_dir = VDirOpenArchive(filename.toUtf8().constData());
if (m_dir) {
m_model.loadDirectory(m_dir);
}
m_ui.archiveListing->setModel(&m_model);
}
ArchiveInspector::~ArchiveInspector() {
if (m_dir) {
m_dir->close(m_dir);
}
connect(m_ui.archiveView, &LibraryView::doneLoading, [this]() {
m_ui.loading->hide();
});
connect(m_ui.archiveView, SIGNAL(accepted()), this, SIGNAL(accepted()));
m_ui.archiveView->setDirectory(filename);
}
VFile* ArchiveInspector::selectedVFile() const {
QModelIndex index = m_ui.archiveListing->selectionModel()->currentIndex();
if (!index.isValid()) {
return nullptr;
}
return m_dir->openFile(m_dir, m_model.entryAt(index.row())->filename, O_RDONLY);
return m_ui.archiveView->selectedVFile();
}
QPair<QString, QString> ArchiveInspector::selectedPath() const {
return m_ui.archiveView->selectedPath();
}

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
@ -6,11 +6,8 @@
#ifndef QGBA_ARCHIVE_INSPECTOR
#define QGBA_ARCHIVE_INSPECTOR
#include "LibraryModel.h"
#include "ui_ArchiveInspector.h"
struct VDir;
struct VFile;
namespace QGBA {
@ -20,15 +17,12 @@ Q_OBJECT
public:
ArchiveInspector(const QString& filename, QWidget* parent = nullptr);
virtual ~ArchiveInspector();
VFile* selectedVFile() const;
QPair<QString, QString> selectedPath() const;
private:
Ui::ArchiveInspector m_ui;
LibraryModel m_model;
VDir* m_dir;
};
}

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>600</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
@ -15,17 +15,32 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="loading">
<property name="text">
<string>Loading...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Open</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QListView" name="archiveListing"/>
<item row="0" column="0" colspan="2">
<widget class="QGBA::LibraryView" name="archiveView" native="true"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QGBA::LibraryView</class>
<extends>QWidget</extends>
<header>LibraryView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
@ -60,21 +75,5 @@
</hint>
</hints>
</connection>
<connection>
<sender>archiveListing</sender>
<signal>doubleClicked(QModelIndex)</signal>
<receiver>ArchiveInspector</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>199</x>
<y>129</y>
</hint>
<hint type="destinationlabel">
<x>199</x>
<y>149</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -20,6 +20,7 @@ AssetView::AssetView(GameController* controller, QWidget* parent)
connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), &m_updateTimer, SLOT(start()));
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close()));
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), &m_updateTimer, SLOT(stop()));
}
void AssetView::updateTiles(bool force) {

View File

@ -68,7 +68,6 @@ endif()
set(SOURCE_FILES
AboutScreen.cpp
ArchiveInspector.cpp
AssetTile.cpp
AssetView.cpp
AudioProcessor.cpp
@ -84,11 +83,11 @@ set(SOURCE_FILES
GameController.cpp
GamepadAxisEvent.cpp
GamepadButtonEvent.cpp
GamepadHatEvent.cpp
IOViewer.cpp
InputController.cpp
InputProfile.cpp
KeyEditor.cpp
LibraryModel.cpp
LoadSaveState.cpp
LogController.cpp
LogView.cpp
@ -121,6 +120,7 @@ set(UI_FILES
DebuggerConsole.ui
GIFView.ui
IOViewer.ui
LibraryView.ui
LoadSaveState.ui
LogView.ui
MemoryView.ui
@ -189,6 +189,13 @@ if(USE_GDB_STUB)
list(APPEND SOURCE_FILES GDBController.cpp GDBWindow.cpp)
endif()
if(USE_SQLITE3)
list(APPEND SOURCE_FILES
ArchiveInspector.cpp
LibraryModel.cpp
LibraryView.cpp)
endif()
qt5_add_resources(RESOURCES resources.qrc)
if(APPLE)
set(MACOSX_BUNDLE_ICON_FILE mgba.icns)

View File

@ -88,13 +88,13 @@ void ConfigOption::setValue(const QVariant& value) {
}
}
QString ConfigController::s_configDir;
ConfigController::ConfigController(QObject* parent)
: QObject(parent)
, m_opts()
{
char path[PATH_MAX];
mCoreConfigDirectory(path, sizeof(path));
QString fileName(path);
QString fileName = configDir();
fileName.append(QDir::separator());
fileName.append("qt.ini");
m_settings = new QSettings(fileName, QSettings::IniFormat, this);
@ -164,6 +164,10 @@ QString ConfigController::getOption(const char* key) const {
return QString(mCoreConfigGetValue(&m_config, key));
}
QString ConfigController::getOption(const QString& key) const {
return getOption(key.toUtf8().constData());
}
QVariant ConfigController::getQtOption(const QString& key, const QString& group) const {
if (!group.isNull()) {
m_settings->beginGroup(group);
@ -269,9 +273,7 @@ void ConfigController::write() {
void ConfigController::makePortable() {
mCoreConfigMakePortable(&m_config);
char path[PATH_MAX];
mCoreConfigDirectory(path, sizeof(path));
QString fileName(path);
QString fileName(configDir());
fileName.append(QDir::separator());
fileName.append("qt.ini");
QSettings* settings2 = new QSettings(fileName, QSettings::IniFormat, this);
@ -281,3 +283,12 @@ void ConfigController::makePortable() {
delete m_settings;
m_settings = settings2;
}
const QString& ConfigController::configDir() {
if (s_configDir.isNull()) {
char path[PATH_MAX];
mCoreConfigDirectory(path, sizeof(path));
s_configDir = QString::fromUtf8(path);
}
return s_configDir;
}

View File

@ -71,6 +71,7 @@ public:
void updateOption(const char* key);
QString getOption(const char* key) const;
QString getOption(const QString& key) const;
QVariant getQtOption(const QString& key, const QString& group = QString()) const;
@ -84,6 +85,8 @@ public:
const mCoreConfig* config() { return &m_config; }
static const QString& configDir();
public slots:
void setOption(const char* key, bool value);
void setOption(const char* key, int value);
@ -103,6 +106,7 @@ private:
QMap<QString, ConfigOption*> m_optionSet;
QSettings* m_settings;
static QString s_configDir;
};
}

View File

@ -7,6 +7,7 @@
#include <QApplication>
#include <QResizeEvent>
#include <QTimer>
#include <mgba/core/core.h>
#include <mgba/core/thread.h>
@ -73,7 +74,11 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
lockAspectRatio(isAspectRatioLocked());
filter(isFiltered());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF());
#else
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio());
#endif
resizePainter();
}
@ -313,6 +318,16 @@ void PainterGL::draw() {
if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) {
return;
}
if (!m_delayTimer.isValid()) {
m_delayTimer.start();
} else if (m_delayTimer.elapsed() < 16) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
QThread::usleep(500);
return;
} else {
m_delayTimer.restart();
}
if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) {
dequeue();
mCoreSyncWaitFrameEnd(&m_context->sync);
@ -360,7 +375,11 @@ void PainterGL::unpause() {
void PainterGL::performDraw() {
m_painter.beginNativePainting();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
float r = m_gl->devicePixelRatioF();
#else
float r = m_gl->devicePixelRatio();
#endif
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
m_backend->drawFrame(m_backend);
m_painter.endNativePainting();

View File

@ -15,12 +15,12 @@
#endif
#endif
#include <QElapsedTimer>
#include <QGLWidget>
#include <QList>
#include <QMouseEvent>
#include <QQueue>
#include <QThread>
#include <QTimer>
#include "platform/video-backend.h"
@ -120,6 +120,7 @@ private:
VideoBackend* m_backend;
QSize m_size;
MessagePainter* m_messagePainter;
QElapsedTimer m_delayTimer;
};
}

View File

@ -19,11 +19,12 @@
#include <mgba/core/version.h>
#include <mgba/internal/gba/video.h>
#include <mgba-util/socket.h>
#include <mgba-util/nointro.h>
#include <mgba-util/vfs.h>
/*
#include "feature/commandline.h"
*/
#ifdef USE_SQLITE3
#include "feature/sqlite3/no-intro.h"
#endif
using namespace QGBA;
static GBAApp* g_app = nullptr;
@ -104,6 +105,13 @@ GBAApp::GBAApp(int& argc, char* argv[])
w->multiplayerChanged();
}
GBAApp::~GBAApp() {
#ifdef USE_SQLITE3
m_parseThread.quit();
m_parseThread.wait();
#endif
}
bool GBAApp::event(QEvent* event) {
if (event->type() == QEvent::FileOpen) {
m_windows[0]->controller()->loadGame(static_cast<QFileOpenEvent*>(event)->file());
@ -207,22 +215,32 @@ QString GBAApp::dataDir() {
return path;
}
#ifdef USE_SQLITE3
bool GBAApp::reloadGameDB() {
NoIntroDB* db = nullptr;
VFile* vf = VFileDevice::open(dataDir() + "/nointro.dat", O_RDONLY);
if (vf) {
db = NoIntroDBLoad(vf);
vf->close(vf);
}
db = NoIntroDBLoad((ConfigController::configDir() + "/nointro.sqlite3").toUtf8().constData());
if (db && m_db) {
NoIntroDBDestroy(m_db);
}
if (db) {
if (m_parseThread.isRunning()) {
m_parseThread.quit();
m_parseThread.wait();
}
GameDBParser* parser = new GameDBParser(db);
m_parseThread.start();
parser->moveToThread(&m_parseThread);
QMetaObject::invokeMethod(parser, "parseNoIntroDB");
m_db = db;
return true;
}
return false;
}
#else
bool GBAApp::reloadGameDB() {
return false;
}
#endif
GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& caption, const QString& filter)
: QFileDialog(parent, caption, app->m_configController.getOption("lastDirectory"), filter)
@ -241,3 +259,21 @@ int GBAApp::FileDialog::exec() {
m_app->continueAll(&paused);
return didAccept;
}
#ifdef USE_SQLITE3
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
: QObject(parent)
, m_db(db)
{
// Nothing to do
}
void GameDBParser::parseNoIntroDB() {
VFile* vf = VFileDevice::open(GBAApp::dataDir() + "/nointro.dat", O_RDONLY);
if (vf) {
NoIntroDBLoadClrMamePro(m_db, vf);
vf->close(vf);
}
}
#endif

View File

@ -8,6 +8,7 @@
#include <QApplication>
#include <QFileDialog>
#include <QThread>
#include "ConfigController.h"
#include "MultiplayerController.h"
@ -23,11 +24,28 @@ namespace QGBA {
class GameController;
class Window;
#ifdef USE_SQLITE3
class GameDBParser : public QObject {
Q_OBJECT
public:
GameDBParser(NoIntroDB* db, QObject* parent = nullptr);
public slots:
void parseNoIntroDB();
private:
NoIntroDB* m_db;
};
#endif
class GBAApp : public QApplication {
Q_OBJECT
public:
GBAApp(int& argc, char* argv[]);
~GBAApp();
static GBAApp* app();
static QString dataDir();
@ -66,7 +84,11 @@ private:
ConfigController m_configController;
Window* m_windows[MAX_GBAS];
MultiplayerController m_multiplayer;
NoIntroDB* m_db;
#ifdef USE_SQLITE3
QThread m_parseThread;
#endif
};
}

View File

@ -73,6 +73,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
}
bool signalsBlocked = (*m_currentKey)->blockSignals(true);
(*m_currentKey)->clearButton();
(*m_currentKey)->clearHat();
(*m_currentKey)->blockSignals(signalsBlocked);
});
@ -119,6 +120,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString&
for (auto& key : m_keyOrder) {
connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext()));
connect(key, SIGNAL(hatChanged(int, int)), this, SLOT(setNext()));
key->installEventFilter(this);
}
@ -241,6 +243,11 @@ void GBAKeyEditor::refresh() {
lookupBinding(map, m_keyB, GBA_KEY_B);
lookupBinding(map, m_keyL, GBA_KEY_L);
lookupBinding(map, m_keyR, GBA_KEY_R);
#ifdef BUILD_SDL
lookupAxes(map);
lookupHats(map);
#endif
}
void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {
@ -272,6 +279,38 @@ void GBAKeyEditor::lookupAxes(const mInputMap* map) {
}
}, this);
}
void GBAKeyEditor::lookupHats(const mInputMap* map) {
struct mInputHatBindings bindings;
int i = 0;
while (mInputQueryHat(map, m_type, i, &bindings)) {
if (bindings.up >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.up));
if (key) {
key->setValueHat(i, GamepadHatEvent::UP);
}
}
if (bindings.right >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.right));
if (key) {
key->setValueHat(i, GamepadHatEvent::RIGHT);
}
}
if (bindings.down >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.down));
if (key) {
key->setValueHat(i, GamepadHatEvent::DOWN);
}
}
if (bindings.left >= 0) {
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.left));
if (key) {
key->setValueHat(i, GamepadHatEvent::LEFT);
}
}
++i;
}
}
#endif
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
@ -279,6 +318,9 @@ void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
}
if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) {
m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key);
}
#endif
m_controller->bindKey(m_type, keyEditor->value(), key);
}
@ -362,5 +404,6 @@ void GBAKeyEditor::updateJoysticks() {
m_profileSelect->setCurrentIndex(activeGamepad);
}
lookupAxes(m_controller->map());
lookupHats(m_controller->map());
}
#endif

View File

@ -63,6 +63,7 @@ private:
#ifdef BUILD_SDL
void lookupAxes(const mInputMap*);
void lookupHats(const mInputMap*);
#endif
KeyEditor* keyById(GBAKey);

View File

@ -145,7 +145,11 @@ GameController::GameController(QObject* parent)
controller->m_multiplayer->attachGame(controller);
}
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, controller->m_fname));
QString path = controller->m_fname;
if (!controller->m_fsub.isEmpty()) {
path += QDir::separator() + controller->m_fsub;
}
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, path));
QMetaObject::invokeMethod(controller, "startAudio");
};
@ -369,17 +373,48 @@ void GameController::loadGame(const QString& path) {
closeGame();
QFileInfo info(path);
if (!info.isReadable()) {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
QString fname = info.fileName();
QString base = info.path();
if (base.endsWith("/") || base.endsWith(QDir::separator())) {
base.chop(1);
}
VDir* dir = VDirOpenArchive(base.toUtf8().constData());
if (dir) {
VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY);
if (vf) {
struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfclone->write(vfclone, buffer, read);
}
vf->close(vf);
vf = vfclone;
}
dir->close(dir);
loadGame(vf, fname, base);
} else {
LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
}
return;
} else {
m_fname = info.canonicalFilePath();
m_fsub = QString();
}
m_fname = info.canonicalFilePath();
m_vf = nullptr;
openGame();
}
void GameController::loadGame(VFile* vf, const QString& base) {
void GameController::loadGame(VFile* vf, const QString& path, const QString& base) {
closeGame();
m_fname = base;
QFileInfo info(base);
if (info.isDir()) {
m_fname = base + QDir::separator() + path;
m_fsub = QString();
} else {
m_fname = base;
m_fsub = path;
}
m_vf = vf;
openGame();
}
@ -394,9 +429,6 @@ void GameController::openGame(bool biosOnly) {
if (m_fname.isEmpty()) {
biosOnly = true;
}
if (biosOnly && (!m_useBios || m_bios.isNull())) {
return;
}
if (isLoaded()) {
// We need to delay if the game is still cleaning up
QTimer::singleShot(10, this, SLOT(openGame()));
@ -405,14 +437,17 @@ void GameController::openGame(bool biosOnly) {
cleanGame();
}
m_threadContext.core = nullptr;
if (!biosOnly) {
if (m_vf) {
m_threadContext.core = mCoreFindVF(m_vf);
} else {
m_threadContext.core = mCoreFind(m_fname.toUtf8().constData());
}
#ifdef M_CORE_GBA
} else {
m_threadContext.core = GBACoreCreate();
#endif
}
if (!m_threadContext.core) {
@ -429,12 +464,17 @@ void GameController::openGame(bool biosOnly) {
m_threadContext.sync.audioWait = m_audioSync;
}
m_threadContext.core->init(m_threadContext.core);
mCoreInitConfig(m_threadContext.core, nullptr);
unsigned width, height;
m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
m_drawContext = new uint32_t[width * height];
m_frontBuffer = new uint32_t[width * height];
if (m_config) {
mCoreLoadForeignConfig(m_threadContext.core, m_config);
}
QByteArray bytes;
if (!biosOnly) {
bytes = m_fname.toUtf8();
@ -447,28 +487,21 @@ void GameController::openGame(bool biosOnly) {
} else {
bytes = m_bios.toUtf8();
}
if (bytes.isNull()) {
return;
}
char dirname[PATH_MAX];
separatePath(bytes.constData(), dirname, m_threadContext.core->dirs.baseName, 0);
mDirectorySetAttachBase(&m_threadContext.core->dirs, VDirOpen(dirname));
m_threadContext.core->setVideoBuffer(m_threadContext.core, m_drawContext, width);
if (!m_bios.isNull() && m_useBios) {
VFile* bios = VFileDevice::open(m_bios, O_RDONLY);
if (bios && !m_threadContext.core->loadBIOS(m_threadContext.core, bios, 0)) {
bios->close(bios);
}
}
m_inputController->recalibrateAxes();
memset(m_drawContext, 0xF8, width * height * 4);
m_threadContext.core->setAVStream(m_threadContext.core, m_stream);
if (m_config) {
mCoreLoadForeignConfig(m_threadContext.core, m_config);
}
if (!biosOnly) {
mCoreAutoloadSave(m_threadContext.core);
if (!m_patch.isNull()) {
@ -488,14 +521,16 @@ void GameController::openGame(bool biosOnly) {
}
}
void GameController::loadBIOS(const QString& path) {
void GameController::loadBIOS(int platform, const QString& path) {
if (m_bios == path) {
return;
}
m_bios = path;
if (m_gameOpen) {
if (m_gameOpen && this->platform() == platform) {
closeGame();
m_bios = path;
openGame();
} else if (!m_gameOpen) {
m_bios = path;
}
}

View File

@ -113,8 +113,8 @@ signals:
public slots:
void loadGame(const QString& path);
void loadGame(VFile* vf, const QString& base);
void loadBIOS(const QString& path);
void loadGame(VFile* vf, const QString& path, const QString& base);
void loadBIOS(int platform, const QString& path);
void loadSave(const QString& path, bool temporary = true);
void yankPak();
void replaceGame(const QString& path);
@ -198,6 +198,7 @@ private:
bool m_gameOpen;
QString m_fname;
QString m_fsub;
VFile* m_vf;
QString m_bios;
bool m_useBios;

View File

@ -0,0 +1,40 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GamepadHatEvent.h"
#include "InputController.h"
using namespace QGBA;
QEvent::Type GamepadHatEvent::s_downType = QEvent::None;
QEvent::Type GamepadHatEvent::s_upType = QEvent::None;
GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction direction, int type, InputController* controller)
: QEvent(pressType)
, m_hatId(hatId)
, m_direction(direction)
, m_controller(controller)
, m_key(GBA_KEY_NONE)
{
ignore();
if (controller) {
m_key = static_cast<GBAKey>(mInputMapHat(controller->map(), type, hatId, direction));
}
}
QEvent::Type GamepadHatEvent::Down() {
if (s_downType == None) {
s_downType = static_cast<Type>(registerEventType());
}
return s_downType;
}
QEvent::Type GamepadHatEvent::Up() {
if (s_upType == None) {
s_upType = static_cast<Type>(registerEventType());
}
return s_upType;
}

View File

@ -0,0 +1,48 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_GAMEPAD_HAT_EVENT
#define QGBA_GAMEPAD_HAT_EVENT
#include <QEvent>
#include <mgba/internal/gba/input.h>
namespace QGBA {
class InputController;
class GamepadHatEvent : public QEvent {
public:
enum Direction {
CENTER = 0,
UP = 1,
RIGHT = 2,
DOWN = 4,
LEFT = 8
};
GamepadHatEvent(Type pressType, int hatId, Direction direction, int type, InputController* controller = nullptr);
int hatId() const { return m_hatId; }
Direction direction() const { return m_direction; }
GBAKey gbaKey() const { return m_key; }
static Type Down();
static Type Up();
private:
static Type s_downType;
static Type s_upType;
int m_hatId;
Direction m_direction;
InputController* m_controller;
GBAKey m_key;
};
}
#endif

View File

@ -30,6 +30,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
, m_config(nullptr)
, m_gamepadTimer(nullptr)
#ifdef BUILD_SDL
, m_sdlPlayer{}
, m_playerAttached(false)
#endif
, m_allowOpposing(false)
@ -48,14 +49,16 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
updateJoysticks();
#endif
m_gamepadTimer = new QTimer(this);
#ifdef BUILD_SDL
connect(m_gamepadTimer, &QTimer::timeout, [this]() {
connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
testGamepad(SDL_BINDING_BUTTON);
if (m_playerId == 0) {
updateJoysticks();
}
});
#endif
m_gamepadTimer->setInterval(50);
m_gamepadTimer->start();
m_gamepadTimer.setInterval(50);
m_gamepadTimer.start();
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
@ -284,7 +287,12 @@ void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
void InputController::updateJoysticks() {
#ifdef BUILD_SDL
mSDLUpdateJoysticks(&s_sdlEvents);
QString profile = profileForType(SDL_BINDING_BUTTON);
mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
QString newProfile = profileForType(SDL_BINDING_BUTTON);
if (profile != newProfile) {
loadProfile(SDL_BINDING_BUTTON, newProfile);
}
#endif
}
@ -311,18 +319,7 @@ int InputController::pollEvents() {
int numHats = SDL_JoystickNumHats(joystick);
for (i = 0; i < numHats; ++i) {
int hat = SDL_JoystickGetHat(joystick, i);
if (hat & SDL_HAT_UP) {
activeButtons |= 1 << GBA_KEY_UP;
}
if (hat & SDL_HAT_LEFT) {
activeButtons |= 1 << GBA_KEY_LEFT;
}
if (hat & SDL_HAT_DOWN) {
activeButtons |= 1 << GBA_KEY_DOWN;
}
if (hat & SDL_HAT_RIGHT) {
activeButtons |= 1 << GBA_KEY_RIGHT;
}
activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
}
int numAxes = SDL_JoystickNumAxes(joystick);
@ -429,6 +426,60 @@ void InputController::unbindAllAxes(uint32_t type) {
mInputUnbindAllAxes(&m_inputMap, type);
}
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
#ifdef BUILD_SDL
if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
SDL_JoystickUpdate();
int numHats = SDL_JoystickNumHats(joystick);
if (numHats < 1) {
return activeHats;
}
int i;
for (i = 0; i < numHats; ++i) {
int hat = SDL_JoystickGetHat(joystick, i);
if (hat & GamepadHatEvent::UP) {
activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
}
if (hat & GamepadHatEvent::RIGHT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
}
if (hat & GamepadHatEvent::DOWN) {
activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
}
if (hat & GamepadHatEvent::LEFT) {
activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
}
}
}
#endif
return activeHats;
}
void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
mInputHatBindings bindings{ -1, -1, -1, -1 };
mInputQueryHat(&m_inputMap, type, hat, &bindings);
switch (direction) {
case GamepadHatEvent::UP:
bindings.up = gbaKey;
break;
case GamepadHatEvent::RIGHT:
bindings.right = gbaKey;
break;
case GamepadHatEvent::DOWN:
bindings.down = gbaKey;
break;
case GamepadHatEvent::LEFT:
bindings.left = gbaKey;
break;
default:
return;
}
mInputBindHat(&m_inputMap, type, hat, &bindings);
}
void InputController::testGamepad(int type) {
auto activeAxes = activeGamepadAxes(type);
auto oldAxes = m_activeAxes;
@ -438,6 +489,10 @@ void InputController::testGamepad(int type) {
auto oldButtons = m_activeButtons;
m_activeButtons = activeButtons;
auto activeHats = activeGamepadHats(type);
auto oldHats = m_activeHats;
m_activeHats = activeHats;
if (!QApplication::focusWidget()) {
return;
}
@ -482,6 +537,23 @@ void InputController::testGamepad(int type) {
clearPendingEvent(event->gbaKey());
sendGamepadEvent(event);
}
activeHats.subtract(oldHats);
oldHats.subtract(m_activeHats);
for (auto& hat : activeHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
postPendingEvent(event->gbaKey());
sendGamepadEvent(event);
if (!event->isAccepted()) {
clearPendingEvent(event->gbaKey());
}
}
for (auto& hat : oldHats) {
GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
clearPendingEvent(event->gbaKey());
sendGamepadEvent(event);
}
}
void InputController::sendGamepadEvent(QEvent* event) {

View File

@ -7,13 +7,13 @@
#define QGBA_INPUT_CONTROLLER_H
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include <QObject>
#include <QSet>
#include <QTimer>
#include <QVector>
class QTimer;
#include <mgba/internal/gba/input.h>
#ifdef BUILD_SDL
@ -53,17 +53,19 @@ public:
const mInputMap* map() const { return &m_inputMap; }
void updateJoysticks();
int pollEvents();
static const int32_t AXIS_THRESHOLD = 0x3000;
QSet<int> activeGamepadButtons(int type);
QSet<QPair<int, GamepadAxisEvent::Direction>> activeGamepadAxes(int type);
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
void recalibrateAxes();
void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey);
void unbindAllAxes(uint32_t type);
void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey);
QStringList connectedGamepads(uint32_t type) const;
int gamepad(uint32_t type) const;
void setGamepad(uint32_t type, int index);
@ -88,6 +90,7 @@ signals:
public slots:
void testGamepad(int type);
void updateJoysticks();
// TODO: Move these to somewhere that makes sense
void suspendScreensaver();
@ -118,7 +121,8 @@ private:
QSet<int> m_activeButtons;
QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
QTimer* m_gamepadTimer;
QSet<QPair<int, GamepadHatEvent::Direction>> m_activeHats;
QTimer m_gamepadTimer;
QSet<GBAKey> m_pendingEvents;
};

View File

@ -17,8 +17,10 @@ using namespace QGBA;
KeyEditor::KeyEditor(QWidget* parent)
: QLineEdit(parent)
, m_direction(GamepadAxisEvent::NEUTRAL)
, m_hatDirection(GamepadHatEvent::CENTER)
, m_key(-1)
, m_axis(-1)
, m_hat(-1)
, m_button(false)
{
setAlignment(Qt::AlignCenter);
@ -58,6 +60,14 @@ void KeyEditor::setValueAxis(int axis, int32_t value) {
emit axisChanged(axis, m_direction);
}
void KeyEditor::setValueHat(int hat, GamepadHatEvent::Direction direction) {
m_button = true;
m_hat = hat;
m_hatDirection = direction;
updateButtonText();
emit hatChanged(hat, m_hatDirection);
}
void KeyEditor::clearButton() {
m_button = true;
setValue(-1);
@ -71,10 +81,18 @@ void KeyEditor::clearAxis() {
emit axisChanged(m_axis, m_direction);
}
void KeyEditor::clearHat() {
m_button = true;
m_hat = -1;
m_hatDirection = GamepadHatEvent::CENTER;
updateButtonText();
emit hatChanged(m_hat, m_hatDirection);
}
QSize KeyEditor::sizeHint() const {
QSize hint = QLineEdit::sizeHint();
QFontMetrics fm(font());
hint.setWidth(fm.height() * 3);
hint.setWidth(fm.height() * 4);
return hint;
}
@ -145,6 +163,12 @@ bool KeyEditor::event(QEvent* event) {
event->accept();
return true;
}
if (event->type() == GamepadHatEvent::Down()) {
GamepadHatEvent* ghe = static_cast<GamepadHatEvent*>(event);
setValueHat(ghe->hatId(), ghe->direction());
event->accept();
return true;
}
if (event->type() == GamepadAxisEvent::Type()) {
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
if (gae->isNew()) {
@ -159,6 +183,24 @@ bool KeyEditor::event(QEvent* event) {
void KeyEditor::updateButtonText() {
QStringList text;
if (m_hat >= 0) {
switch (m_hatDirection) {
case GamepadHatEvent::UP:
text.append(QString("↑%0").arg(m_hat));
break;
case GamepadHatEvent::RIGHT:
text.append(QString("→%0").arg(m_hat));
break;
case GamepadHatEvent::DOWN:
text.append(QString("↓%0").arg(m_hat));
break;
case GamepadHatEvent::LEFT:
text.append(QString("←%0").arg(m_hat));
break;
default:
break;
}
}
if (m_key >= 0) {
text.append(QString::number(m_key));
}

View File

@ -7,6 +7,7 @@
#define QGBA_KEY_EDITOR
#include "GamepadAxisEvent.h"
#include "GamepadHatEvent.h"
#include <QLineEdit>
#include <QTimer>
@ -24,6 +25,9 @@ public:
GamepadAxisEvent::Direction direction() const { return m_direction; }
int axis() const { return m_axis; }
GamepadHatEvent::Direction hatDirection() const { return m_hatDirection; }
int hat() const { return m_hat; }
virtual QSize sizeHint() const override;
public slots:
@ -31,12 +35,15 @@ public slots:
void setValueKey(int key);
void setValueButton(int button);
void setValueAxis(int axis, int32_t value);
void setValueHat(int hat, GamepadHatEvent::Direction value);
void clearButton();
void clearAxis();
void clearHat();
signals:
void valueChanged(int key);
void axisChanged(int key, int direction);
void axisChanged(int axis, int direction);
void hatChanged(int hat, int direction);
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
@ -49,8 +56,10 @@ private:
int m_key;
int m_axis;
int m_hat;
bool m_button;
GamepadAxisEvent::Direction m_direction;
GamepadHatEvent::Direction m_hatDirection;
QTimer m_lastKey;
};

View File

@ -5,46 +5,181 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LibraryModel.h"
#include <QFontMetrics>
#include <mgba-util/vfs.h>
using namespace QGBA;
LibraryModel::LibraryModel(QObject* parent)
Q_DECLARE_METATYPE(mLibraryEntry);
QMap<QString, LibraryModel::LibraryHandle*> LibraryModel::s_handles;
QMap<QString, LibraryModel::LibraryColumn> LibraryModel::s_columns;
LibraryModel::LibraryModel(const QString& path, QObject* parent)
: QAbstractItemModel(parent)
{
mLibraryInit(&m_library);
if (s_columns.empty()) {
s_columns["name"] = {
tr("Name"),
[](const mLibraryEntry& e) -> QString {
if (e.title) {
return QString::fromUtf8(e.title);
}
return QString::fromUtf8(e.filename);
}
};
s_columns["filename"] = {
tr("Filename"),
[](const mLibraryEntry& e) -> QString {
return QString::fromUtf8(e.filename);
}
};
s_columns["size"] = {
tr("Size"),
[](const mLibraryEntry& e) -> QString {
double size = e.filesize;
QString unit = "B";
if (size >= 1024.0) {
size /= 1024.0;
unit = "kiB";
}
if (size >= 1024.0) {
size /= 1024.0;
unit = "MiB";
}
return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit);
},
Qt::AlignRight
};
s_columns["platform"] = {
tr("Platform"),
[](const mLibraryEntry& e) -> QString {
int platform = e.platform;
switch (platform) {
#ifdef M_CORE_GBA
case PLATFORM_GBA:
return tr("GBA");
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
return tr("GB");
#endif
default:
return tr("?");
}
}
};
s_columns["location"] = {
tr("Location"),
[](const mLibraryEntry& e) -> QString {
return QString::fromUtf8(e.base);
}
};
s_columns["crc32"] = {
tr("CRC32"),
[](const mLibraryEntry& e) -> QString {
return QString("%0").arg(e.crc32, 8, 16, QChar('0'));
}
};
}
if (!path.isNull()) {
if (s_handles.contains(path)) {
m_library = s_handles[path];
m_library->ref();
} else {
m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path);
s_handles[path] = m_library;
}
} else {
m_library = new LibraryHandle(mLibraryCreateEmpty());
}
memset(&m_constraints, 0, sizeof(m_constraints));
m_constraints.platform = PLATFORM_NONE;
m_columns.append(s_columns["name"]);
m_columns.append(s_columns["location"]);
m_columns.append(s_columns["platform"]);
m_columns.append(s_columns["size"]);
m_columns.append(s_columns["crc32"]);
connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&)));
}
LibraryModel::~LibraryModel() {
mLibraryDeinit(&m_library);
}
void LibraryModel::loadDirectory(VDir* dir) {
mLibraryLoadDirectory(&m_library, dir);
}
const mLibraryEntry* LibraryModel::entryAt(int row) const {
if ((unsigned) row < mLibraryListingSize(&m_library.listing)) {
return mLibraryListingGetConstPointer(&m_library.listing, row);
clearConstraints();
if (!m_library->deref()) {
s_handles.remove(m_library->path);
delete m_library;
}
return nullptr;
}
void LibraryModel::loadDirectory(const QString& path) {
m_queue.append(path);
QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path));
}
bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
mLibraryListing entries;
mLibraryListingInit(&entries, 0);
if (!mLibraryGetEntries(m_library->library, &entries, 1, row, &m_constraints)) {
mLibraryListingDeinit(&entries);
return false;
}
*out = *mLibraryListingGetPointer(&entries, 0);
mLibraryListingDeinit(&entries);
return true;
}
VFile* LibraryModel::openVFile(const QModelIndex& index) const {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return nullptr;
}
return mLibraryOpenVFile(m_library->library, &entry);
}
QString LibraryModel::filename(const QModelIndex& index) const {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return QString();
}
return QString::fromUtf8(entry.filename);
}
QString LibraryModel::location(const QModelIndex& index) const {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return QString();
}
return QString::fromUtf8(entry.base);
}
QVariant LibraryModel::data(const QModelIndex& index, int role) const {
if (!index.isValid()) {
return QVariant();
}
if (role != Qt::DisplayRole) {
mLibraryEntry entry;
if (!entryAt(index.row(), &entry)) {
return QVariant();
}
const mLibraryEntry* entry = mLibraryListingGetConstPointer(&m_library.listing, index.row());
switch (index.column()) {
case 0:
return entry->filename;
case 1:
return (unsigned long long) entry->filesize;
if (role == Qt::UserRole) {
return QVariant::fromValue(entry);
}
if (index.column() >= m_columns.count()) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
return m_columns[index.column()].value(entry);
case Qt::SizeHintRole: {
QFontMetrics fm((QFont()));
return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry));
}
case Qt::TextAlignmentRole:
return m_columns[index.column()].alignment;
default:
return QVariant();
}
return QVariant();
}
QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const {
@ -52,12 +187,10 @@ QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int
return QAbstractItemModel::headerData(section, orientation, role);
}
if (orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Filename");
case 1:
return tr("Size");
if (section >= m_columns.count()) {
return QVariant();
}
return m_columns[section].name;
}
return section;
}
@ -77,12 +210,95 @@ int LibraryModel::columnCount(const QModelIndex& parent) const {
if (parent.isValid()) {
return 0;
}
return 2;
return m_columns.count();
}
int LibraryModel::rowCount(const QModelIndex& parent) const {
if (parent.isValid()) {
return 0;
}
return mLibraryListingSize(&m_library.listing);
return mLibraryCount(m_library->library, &m_constraints);
}
void LibraryModel::attachGameDB(const NoIntroDB* gameDB) {
mLibraryAttachGameDB(m_library->library, gameDB);
}
void LibraryModel::constrainBase(const QString& path) {
if (m_constraints.base) {
free(const_cast<char*>(m_constraints.base));
}
m_constraints.base = strdup(path.toUtf8().constData());
}
void LibraryModel::clearConstraints() {
if (m_constraints.base) {
free(const_cast<char*>(m_constraints.base));
}
if (m_constraints.filename) {
free(const_cast<char*>(m_constraints.filename));
}
if (m_constraints.title) {
free(const_cast<char*>(m_constraints.title));
}
memset(&m_constraints, 0, sizeof(m_constraints));
}
void LibraryModel::directoryLoaded(const QString& path) {
m_queue.removeOne(path);
beginResetModel();
endResetModel();
if (m_queue.empty()) {
emit doneLoading();
}
}
LibraryModel::LibraryColumn::LibraryColumn() {
}
LibraryModel::LibraryColumn::LibraryColumn(const QString& name, std::function<QString(const mLibraryEntry&)> value, int alignment)
: name(name)
, value(value)
, alignment(alignment)
{
}
LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p)
: library(lib)
, loader(new LibraryLoader(library))
, path(p)
, m_ref(1)
{
if (!library) {
return;
}
loader->moveToThread(&m_loaderThread);
m_loaderThread.setObjectName("Library Loader Thread");
m_loaderThread.start();
}
LibraryModel::LibraryHandle::~LibraryHandle() {
m_loaderThread.quit();
m_loaderThread.wait();
mLibraryDestroy(library);
}
void LibraryModel::LibraryHandle::ref() {
++m_ref;
}
bool LibraryModel::LibraryHandle::deref() {
--m_ref;
return m_ref > 0;
}
LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent)
: QObject(parent)
, m_library(library)
{
}
void LibraryLoader::loadDirectory(const QString& path) {
mLibraryLoadDirectory(m_library, path.toUtf8().constData());
emit directoryLoaded(path);
}

View File

@ -7,23 +7,31 @@
#define QGBA_LIBRARY_MODEL
#include <QAbstractItemModel>
#include <QStringList>
#include <QThread>
#include <mgba/core/library.h>
#include <functional>
struct VDir;
struct VFile;
struct NoIntroDB;
namespace QGBA {
class LibraryLoader;
class LibraryModel : public QAbstractItemModel {
Q_OBJECT
public:
LibraryModel(QObject* parent = nullptr);
LibraryModel(const QString& path, QObject* parent = nullptr);
virtual ~LibraryModel();
void loadDirectory(VDir* dir);
const mLibraryEntry* entryAt(int row) const;
bool entryAt(int row, mLibraryEntry* out) const;
VFile* openVFile(const QModelIndex& index) const;
QString filename(const QModelIndex& index) const;
QString location(const QModelIndex& index) const;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
@ -34,9 +42,70 @@ public:
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
private:
mLibrary m_library;
void attachGameDB(const NoIntroDB* gameDB);
signals:
void doneLoading();
public slots:
void loadDirectory(const QString& path);
void constrainBase(const QString& path);
void clearConstraints();
private slots:
void directoryLoaded(const QString& path);
private:
struct LibraryColumn {
LibraryColumn();
LibraryColumn(const QString&, std::function<QString(const mLibraryEntry&)>, int = Qt::AlignLeft);
QString name;
std::function<QString(const mLibraryEntry&)> value;
int alignment;
};
class LibraryHandle {
public:
LibraryHandle(mLibrary*, const QString& path = QString());
~LibraryHandle();
mLibrary* const library;
LibraryLoader* const loader;
const QString path;
void ref();
bool deref();
private:
QThread m_loaderThread;
size_t m_ref;
};
LibraryHandle* m_library;
static QMap<QString, LibraryHandle*> s_handles;
mLibraryEntry m_constraints;
QStringList m_queue;
QList<LibraryColumn> m_columns;
static QMap<QString, LibraryColumn> s_columns;
};
class LibraryLoader : public QObject {
Q_OBJECT
public:
LibraryLoader(mLibrary* library, QObject* parent = nullptr);
public slots:
void loadDirectory(const QString& path);
signals:
void directoryLoaded(const QString& path);
private:
mLibrary* m_library;
};
}

View File

@ -0,0 +1,57 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LibraryView.h"
#include <mgba-util/vfs.h>
#include "ConfigController.h"
#include "GBAApp.h"
using namespace QGBA;
LibraryView::LibraryView(QWidget* parent)
: QWidget(parent)
, m_model(ConfigController::configDir() + "/library.sqlite3")
{
m_ui.setupUi(this);
m_model.attachGameDB(GBAApp::app()->gameDB());
connect(&m_model, SIGNAL(doneLoading()), this, SIGNAL(doneLoading()));
connect(&m_model, SIGNAL(doneLoading()), this, SLOT(resizeColumns()));
connect(m_ui.listing, SIGNAL(activated(const QModelIndex&)), this, SIGNAL(accepted()));
m_ui.listing->horizontalHeader()->setSectionsMovable(true);
m_ui.listing->setModel(&m_model);
m_ui.listing->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
resizeColumns();
}
void LibraryView::setDirectory(const QString& filename) {
m_model.loadDirectory(filename);
m_model.constrainBase(filename);
}
void LibraryView::addDirectory(const QString& filename) {
m_model.loadDirectory(filename);
}
VFile* LibraryView::selectedVFile() const {
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
if (!index.isValid()) {
return nullptr;
}
return m_model.openVFile(index);
}
QPair<QString, QString> LibraryView::selectedPath() const {
QModelIndex index = m_ui.listing->selectionModel()->currentIndex();
if (!index.isValid()) {
return qMakePair(QString(), QString());
}
return qMakePair(m_model.filename(index), m_model.location(index));
}
void LibraryView::resizeColumns() {
m_ui.listing->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
}

View File

@ -0,0 +1,45 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef QGBA_LIBRARY_VIEW
#define QGBA_LIBRARY_VIEW
#include "LibraryModel.h"
#include "ui_LibraryView.h"
struct VFile;
namespace QGBA {
class LibraryView : public QWidget {
Q_OBJECT
public:
LibraryView(QWidget* parent = nullptr);
VFile* selectedVFile() const;
QPair<QString, QString> selectedPath() const;
signals:
void doneLoading();
void accepted();
public slots:
void setDirectory(const QString&);
void addDirectory(const QString&);
private slots:
void resizeColumns();
private:
Ui::LibraryView m_ui;
LibraryModel m_model;
};
}
#endif

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LibraryView</class>
<widget class="QWidget" name="LibraryView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Library</string>
</property>
<layout class="QGridLayout">
<property name="margin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTableView" name="listing">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>0</number>
</attribute>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -18,6 +18,9 @@
#include <mgba/internal/gb/sio/lockstep.h>
#endif
struct GBSIOLockstepNode;
struct GBASIOLockstepNode;
namespace QGBA {
class GameController;

View File

@ -76,6 +76,7 @@ void ObjView::updateTilesGBA(bool force) {
};
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)) {
@ -186,6 +187,7 @@ void ObjView::updateTilesGB(bool force) {
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;
if (gb->model >= GB_MODEL_CGB) {
if (GBObjAttributesIsBank(obj->attr)) {

View File

@ -22,8 +22,10 @@
using namespace QGBA;
#ifdef M_CORE_GB
QList<enum GBModel> OverrideView::s_gbModelList;
QList<enum GBMemoryBankControllerType> OverrideView::s_mbcList;
#endif
OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)

View File

@ -15,7 +15,9 @@
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h>
#endif
#include <mgba-util/nointro.h>
#ifdef USE_SQLITE3
#include "feature/sqlite3/no-intro.h"
#endif
using namespace QGBA;
@ -28,7 +30,9 @@ ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
return;
}
#ifdef USE_SQLITE3
const NoIntroDB* db = GBAApp::app()->gameDB();
#endif
uint32_t crc32 = 0;
GameController::Interrupter interrupter(controller);
@ -44,12 +48,13 @@ ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
m_ui.id->setText(tr("(unknown)"));
}
core->checksum(core, &crc32, CHECKSUM_CRC32);
switch (controller->thread()->core->platform(controller->thread()->core)) {
#ifdef M_CORE_GBA
case PLATFORM_GBA: {
GBA* gba = static_cast<GBA*>(core->board);
m_ui.size->setText(QString::number(gba->pristineRomSize) + tr(" bytes"));
crc32 = gba->romCrc32;
break;
}
#endif
@ -57,7 +62,6 @@ ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
case PLATFORM_GB: {
GB* gb = static_cast<GB*>(core->board);
m_ui.size->setText(QString::number(gb->pristineRomSize) + tr(" bytes"));
crc32 = gb->romCrc32;
break;
}
#endif
@ -67,6 +71,7 @@ ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
}
if (crc32) {
m_ui.crc->setText(QString::number(crc32, 16));
#ifdef USE_SQLITE3
if (db) {
NoIntroGame game{};
if (NoIntroDBLookupGameByCRC(db, crc32, &game)) {
@ -77,6 +82,9 @@ ROMInfo::ROMInfo(GameController* controller, QWidget* parent)
} else {
m_ui.name->setText(tr("(no database present)"));
}
#else
m_ui.name->hide();
#endif
} else {
m_ui.crc->setText(tr("(unknown)"));
m_ui.name->setText(tr("(unknown)"));

View File

@ -127,7 +127,15 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
}
#endif
connect(m_ui.biosBrowse, SIGNAL(clicked()), this, SLOT(selectBios()));
connect(m_ui.gbaBiosBrowse, &QPushButton::clicked, [this]() {
selectBios(m_ui.gbaBios);
});
connect(m_ui.gbBiosBrowse, &QPushButton::clicked, [this]() {
selectBios(m_ui.gbBios);
});
connect(m_ui.gbcBiosBrowse, &QPushButton::clicked, [this]() {
selectBios(m_ui.gbcBios);
});
GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this);
m_ui.stackedWidget->addWidget(editor);
@ -162,15 +170,17 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
m_ui.tabs->addItem("Shortcuts");
}
void SettingsView::selectBios() {
void SettingsView::selectBios(QLineEdit* bios) {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS"));
if (!filename.isEmpty()) {
m_ui.bios->setText(filename);
bios->setText(filename);
}
}
void SettingsView::updateConfig() {
saveSetting("bios", m_ui.bios);
saveSetting("gba.bios", m_ui.gbaBios);
saveSetting("gb.bios", m_ui.gbBios);
saveSetting("gbc.bios", m_ui.gbcBios);
saveSetting("useBios", m_ui.useBios);
saveSetting("skipBios", m_ui.skipBios);
saveSetting("audioBuffers", m_ui.audioBufferSize);
@ -192,6 +202,7 @@ void SettingsView::updateConfig() {
saveSetting("savestatePath", m_ui.savestatePath);
saveSetting("screenshotPath", m_ui.screenshotPath);
saveSetting("patchPath", m_ui.patchPath);
saveSetting("showLibrary", m_ui.showLibrary);
if (m_ui.fastForwardUnbounded->isChecked()) {
saveSetting("fastForwardRatio", "-1");
@ -240,11 +251,14 @@ void SettingsView::updateConfig() {
m_controller->write();
emit pathsChanged();
emit biosLoaded(m_ui.bios->text());
emit biosLoaded(PLATFORM_GBA, m_ui.gbaBios->text());
}
void SettingsView::reloadConfig() {
loadSetting("bios", m_ui.bios);
loadSetting("bios", m_ui.gbaBios);
loadSetting("gba.bios", m_ui.gbaBios);
loadSetting("gb.bios", m_ui.gbBios);
loadSetting("gbc.bios", m_ui.gbcBios);
loadSetting("useBios", m_ui.useBios);
loadSetting("skipBios", m_ui.skipBios);
loadSetting("audioBuffers", m_ui.audioBufferSize);
@ -266,6 +280,7 @@ void SettingsView::reloadConfig() {
loadSetting("savestatePath", m_ui.savestatePath);
loadSetting("screenshotPath", m_ui.screenshotPath);
loadSetting("patchPath", m_ui.patchPath);
loadSetting("showLibrary", m_ui.showLibrary);
double fastForwardRatio = loadSetting("fastForwardRatio").toDouble();
if (fastForwardRatio <= 0) {

View File

@ -8,6 +8,8 @@
#include <QDialog>
#include <mgba/core/core.h>
#include "ui_SettingsView.h"
namespace QGBA {
@ -23,13 +25,13 @@ public:
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr);
signals:
void biosLoaded(const QString&);
void biosLoaded(int platform, const QString&);
void audioDriverChanged();
void displayDriverChanged();
void pathsChanged();
private slots:
void selectBios();
void selectBios(QLineEdit*);
void updateConfig();
void reloadConfig();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>548</width>
<height>431</height>
<width>650</width>
<height>450</height>
</rect>
</property>
<property name="sizePolicy">
@ -23,13 +23,6 @@
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListWidget" name="tabs">
<property name="sizePolicy">
@ -45,13 +38,18 @@
</size>
</property>
<property name="currentRow">
<number>0</number>
<number>-1</number>
</property>
<item>
<property name="text">
<string>Audio/Video</string>
</property>
</item>
<item>
<property name="text">
<string>Interface</string>
</property>
</item>
<item>
<property name="text">
<string>Emulation</string>
@ -59,7 +57,7 @@
</item>
<item>
<property name="text">
<string>Savestates</string>
<string>BIOS</string>
</property>
</item>
<item>
@ -69,12 +67,19 @@
</item>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="stackedWidgetPage1">
<widget class="QWidget" name="av">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
@ -110,7 +115,7 @@
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<property name="currentText" stdset="0">
<string>1536</string>
</property>
<property name="currentIndex">
@ -176,7 +181,7 @@
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<property name="currentText" stdset="0">
<string>44100</string>
</property>
<property name="currentIndex">
@ -384,110 +389,16 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="stackedWidgetPage2">
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<widget class="QWidget" name="interface_2">
<layout class="QFormLayout" name="formLayout_4">
<item row="3" column="1">
<widget class="QCheckBox" name="allowOpposingDirections">
<property name="text">
<string>BIOS file:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="bios">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="biosBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="useBios">
<property name="text">
<string>Use BIOS file if found</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="skipBios">
<property name="text">
<string>Skip BIOS intro</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Fast forward speed</string>
<string>Allow opposing input directions</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="fastForwardRatio">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="fastForwardUnbounded">
<property name="text">
<string>Unbounded</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="suspendScreensaver">
<property name="text">
<string>Suspend screensaver</string>
@ -497,21 +408,148 @@
</property>
</widget>
</item>
<item row="9" column="1">
<item row="5" column="1">
<widget class="QCheckBox" name="pauseOnFocusLost">
<property name="text">
<string>Pause when inactive</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_15">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Idle loops</string>
<string>Library:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="0" column="1">
<widget class="QCheckBox" name="showLibrary">
<property name="text">
<string>Show when no game open</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="clearCache">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Clear cache</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="emulation">
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Fast forward speed:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QDoubleSpinBox" name="fastForwardRatio">
<property name="enabled">
<bool>false</bool>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>20.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value">
<double>5.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fastForwardUnbounded">
<property name="text">
<string>Unbounded</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="rewind">
<property name="text">
<string>Enable rewind</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Rewind history:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item>
<widget class="QSpinBox" name="rewindCapacity">
<property name="maximum">
<number>3600</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>frames</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" 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">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Idle loops:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="idleOptimization">
<item>
<property name="text">
@ -530,28 +568,21 @@
</item>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="allowOpposingDirections">
<property name="text">
<string>Allow opposing input directions</string>
<item row="6" column="0" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="0" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Save extra data</string>
<string>Savestate extra data:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="7" column="1">
<widget class="QCheckBox" name="saveStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -561,7 +592,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="8" column="1">
<widget class="QCheckBox" name="saveStateSave">
<property name="text">
<string>Save data</string>
@ -571,7 +602,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="9" column="1">
<widget class="QCheckBox" name="saveStateCheats">
<property name="text">
<string>Cheat codes</string>
@ -581,21 +612,14 @@
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="Line" name="line_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="11" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Load extra data</string>
<string>Load extra data:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="11" column="1">
<widget class="QCheckBox" name="loadStateScreenshot">
<property name="text">
<string>Screenshot</string>
@ -605,54 +629,127 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="12" column="1">
<widget class="QCheckBox" name="loadStateSave">
<property name="text">
<string>Save data</string>
</property>
</widget>
</item>
<item row="7" column="1">
<item row="13" column="1">
<widget class="QCheckBox" name="loadStateCheats">
<property name="text">
<string>Cheat codes</string>
</property>
</widget>
</item>
<item row="8" column="0" colspan="2">
<widget class="Line" name="line_2">
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="rewind">
</layout>
</widget>
<widget class="QWidget" name="bios">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Enable rewind</string>
<string>GB BIOS file:</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Rewind history:</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_13">
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="rewindCapacity">
<property name="maximum">
<number>3600</number>
<widget class="QLineEdit" name="gbBios">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_7">
<widget class="QPushButton" name="gbBiosBrowse">
<property name="text">
<string>frames</string>
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="useBios">
<property name="text">
<string>Use BIOS file if found</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="skipBios">
<property name="text">
<string>Skip BIOS intro</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLineEdit" name="gbaBios">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="gbaBiosBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>GBA BIOS file:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>GBC BIOS file:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_30">
<item>
<widget class="QLineEdit" name="gbcBios">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="gbcBiosBrowse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
@ -660,7 +757,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="page">
<widget class="QWidget" name="paths">
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
@ -970,21 +1067,5 @@
</hint>
</hints>
</connection>
<connection>
<sender>fastForwardUnbounded</sender>
<signal>toggled(bool)</signal>
<receiver>fastForwardRatio</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>338</x>
<y>163</y>
</hint>
<hint type="destinationlabel">
<x>327</x>
<y>135</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -264,9 +264,10 @@ void ShaderSelector::buttonPressed(QAbstractButton* button) {
case QDialogButtonBox::Reset:
emit reset();
break;
case QDialogButtonBox::Save:
case QDialogButtonBox::Ok:
m_config->setOption("shader", m_shaderPath);
emit saved();
close();
break;
case QDialogButtonBox::RestoreDefaults:
emit resetToDefault();

View File

@ -104,29 +104,12 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save</set>
<set>QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ShaderSelector</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

View File

@ -15,7 +15,9 @@
#include <QStackedLayout>
#include "AboutScreen.h"
#ifdef USE_SQLITE3
#include "ArchiveInspector.h"
#endif
#include "CheatsView.h"
#include "ConfigController.h"
#include "DebuggerConsole.h"
@ -48,7 +50,7 @@
#include <mgba/internal/gb/video.h>
#endif
#include "feature/commandline.h"
#include <mgba-util/nointro.h>
#include "feature/sqlite3/no-intro.h"
#include <mgba-util/vfs.h>
using namespace QGBA;
@ -101,7 +103,30 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
m_savedScale = multiplier.toInt();
i = m_savedScale;
}
#ifdef M_CORE_GBA
#ifdef USE_SQLITE3
m_libraryView = new LibraryView(this);
ConfigOption* showLibrary = m_config->addOption("showLibrary");
showLibrary->connect([this](const QVariant& value) {
if (value.toBool()) {
if (m_controller->isLoaded()) {
m_screenWidget->layout()->addWidget(m_libraryView);
} else {
attachWidget(m_libraryView);
}
} else {
detachWidget(m_libraryView);
}
}, this);
m_config->updateOption("showLibrary");
connect(m_libraryView, &LibraryView::accepted, [this]() {
VFile* output = m_libraryView->selectedVFile();
QPair<QString, QString> path = m_libraryView->selectedPath();
if (output) {
m_controller->loadGame(output, path.first, path.second);
}
});
#elif defined(M_CORE_GBA)
m_screenWidget->setSizeHint(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i));
#endif
m_screenWidget->setPixmap(m_logo);
@ -210,6 +235,9 @@ void Window::argumentsPassed(mArguments* args) {
void Window::resizeFrame(const QSize& size) {
QSize newSize(size);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
newSize /= m_screenWidget->devicePixelRatioF();
#endif
m_screenWidget->setSizeHint(newSize);
newSize -= m_screenWidget->size();
newSize += this->size();
@ -283,10 +311,6 @@ void Window::reloadConfig() {
m_display->lockAspectRatio(opts->lockAspectRatio);
m_display->filter(opts->resampleVideo);
if (opts->bios) {
m_controller->loadBIOS(opts->bios);
}
m_inputController.setScreensaverSuspendable(opts->suspendScreensaver);
}
@ -359,16 +383,18 @@ void Window::selectROM() {
}
}
#ifdef USE_SQLITE3
void Window::selectROMInArchive() {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFiltersArchive());
if (filename.isEmpty()) {
return;
}
ArchiveInspector* archiveInspector = new ArchiveInspector(filename);
connect(archiveInspector, &QDialog::accepted, [this, archiveInspector, filename]() {
connect(archiveInspector, &QDialog::accepted, [this, archiveInspector]() {
VFile* output = archiveInspector->selectedVFile();
QPair<QString, QString> path = archiveInspector->selectedPath();
if (output) {
m_controller->loadGame(output, filename);
m_controller->loadGame(output, path.second, path.first);
}
archiveInspector->close();
});
@ -376,6 +402,15 @@ void Window::selectROMInArchive() {
archiveInspector->show();
}
void Window::addDirToLibrary() {
QString filename = GBAApp::app()->getOpenDirectoryName(this, tr("Select folder"));
if (filename.isEmpty()) {
return;
}
m_libraryView->addDirectory(filename);
}
#endif
void Window::replaceROM() {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFilters());
if (!filename.isEmpty()) {
@ -405,18 +440,6 @@ void Window::multiplayerChanged() {
}
}
void Window::selectBIOS() {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select BIOS"));
if (!filename.isEmpty()) {
QFileInfo info(filename);
m_config->setOption("bios", info.canonicalFilePath());
m_config->updateOption("bios");
m_config->setOption("useBios", true);
m_config->updateOption("useBios");
m_controller->loadBIOS(info.canonicalFilePath());
}
}
void Window::selectPatch() {
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select patch"), tr("Patches (*.ips *.ups *.bps)"));
if (!filename.isEmpty()) {
@ -446,7 +469,7 @@ void Window::exportSharkport() {
void Window::openSettingsWindow() {
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController);
connect(settingsWindow, SIGNAL(biosLoaded(const QString&)), m_controller, SLOT(loadBIOS(const QString&)));
connect(settingsWindow, SIGNAL(biosLoaded(int, const QString&)), m_controller, SLOT(loadBIOS(int, const QString&)));
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
connect(settingsWindow, SIGNAL(pathsChanged()), this, SLOT(reloadConfig()));
@ -828,35 +851,18 @@ void Window::updateTitle(float fps) {
const NoIntroDB* db = GBAApp::app()->gameDB();
NoIntroGame game{};
uint32_t crc32 = 0;
m_controller->thread()->core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32);
switch (m_controller->thread()->core->platform(m_controller->thread()->core)) {
#ifdef M_CORE_GBA
case PLATFORM_GBA: {
GBA* gba = static_cast<GBA*>(m_controller->thread()->core->board);
crc32 = gba->romCrc32;
break;
}
#endif
#ifdef M_CORE_GB
case PLATFORM_GB: {
GB* gb = static_cast<GB*>(m_controller->thread()->core->board);
crc32 = gb->romCrc32;
break;
}
#endif
default:
break;
}
char gameTitle[17] = { '\0' };
mCore* core = m_controller->thread()->core;
core->getGameTitle(core, gameTitle);
title = gameTitle;
if (db && crc32) {
NoIntroDBLookupGameByCRC(db, crc32, &game);
#ifdef USE_SQLITE3
if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) {
title = QLatin1String(game.name);
} else {
char gameTitle[17] = { '\0' };
mCore* core = m_controller->thread()->core;
core->getGameTitle(core, gameTitle);
title = gameTitle;
}
#endif
}
MultiplayerController* multiplayer = m_controller->multiplayerController();
if (multiplayer && multiplayer->attached() > 1) {
@ -912,10 +918,12 @@ void Window::setupMenu(QMenuBar* menubar) {
installEventFilter(m_shortcutController);
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open),
"loadROM");
#ifdef USE_SQLITE3
addControlledAction(fileMenu, fileMenu->addAction(tr("Load ROM in archive..."), this, SLOT(selectROMInArchive())),
"loadROMInArchive");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())), "loadBIOS");
addControlledAction(fileMenu, fileMenu->addAction(tr("Add folder to library..."), this, SLOT(addDirToLibrary())),
"addDirToLibrary");
#endif
QAction* loadTemporarySave = new QAction(tr("Load temporary save..."), fileMenu);
connect(loadTemporarySave, &QAction::triggered, [this]() { this->selectSave(true); });
@ -923,7 +931,13 @@ void Window::setupMenu(QMenuBar* menubar) {
addControlledAction(fileMenu, loadTemporarySave, "loadTemporarySave");
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())), "loadPatch");
addControlledAction(fileMenu, fileMenu->addAction(tr("Boot BIOS"), m_controller, SLOT(bootBIOS())), "bootBIOS");
QAction* bootBIOS = new QAction(tr("Boot BIOS"), fileMenu);
connect(bootBIOS, &QAction::triggered, [this]() {
m_controller->loadBIOS(PLATFORM_GBA, m_config->getOption("gba.bios"));
m_controller->bootBIOS();
});
addControlledAction(fileMenu, bootBIOS, "bootBIOS");
addControlledAction(fileMenu, fileMenu->addAction(tr("Replace ROM..."), this, SLOT(replaceROM())), "replaceROM");

View File

@ -28,6 +28,7 @@ class Display;
class GameController;
class GDBController;
class GIFView;
class LibraryView;
class LogView;
class ShaderSelector;
class ShortcutController;
@ -57,9 +58,11 @@ signals:
public slots:
void selectROM();
#ifdef USE_SQLITE3
void selectROMInArchive();
void addDirToLibrary();
#endif
void selectSave(bool temporary);
void selectBIOS();
void selectPatch();
void enterFullScreen();
void exitFullScreen();
@ -190,6 +193,10 @@ private:
#ifdef USE_GDB_STUB
GDBController* m_gdbController;
#endif
#ifdef USE_SQLITE3
LibraryView* m_libraryView;
#endif
};
class WindowBackground : public QLabel {

View File

@ -59,7 +59,7 @@ bool mSDLInitEvents(struct mSDLEvents* context) {
int nJoysticks = SDL_NumJoysticks();
SDL_JoystickListInit(&context->joysticks, nJoysticks);
if (nJoysticks > 0) {
mSDLUpdateJoysticks(context);
mSDLUpdateJoysticks(context, NULL);
// Some OSes don't do hotplug detection
if (!SDL_JoystickListSize(&context->joysticks)) {
int i;
@ -141,6 +141,8 @@ void mSDLInitBindingsGBA(struct mInputMap* inputMap) {
mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description);
description = (struct mInputAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 };
mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description);
mInputBindHat(inputMap, SDL_BINDING_BUTTON, 0, &GBAInputInfo.hat);
}
bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) {
@ -323,7 +325,7 @@ void mSDLPlayerChangeJoystick(struct mSDLEvents* events, struct mSDLPlayer* play
player->joystick = SDL_JoystickListGetPointer(&events->joysticks, index);
}
void mSDLUpdateJoysticks(struct mSDLEvents* events) {
void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration* config) {
// Pump SDL joystick events without eating the rest of the events
SDL_JoystickUpdate();
#if SDL_VERSION_ATLEAST(2, 0, 0)
@ -337,8 +339,38 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
joystick->haptic = SDL_HapticOpenFromJoystick(joystick->joystick);
#endif
const char* joystickName;
#if SDL_VERSION_ATLEAST(2, 0, 0)
joystickName = SDL_JoystickName(joystick->joystick);
#else
joystickName = SDL_JoystickName(SDL_JoystickIndex(joystick->joystick));
#endif
size_t i;
for (i = 0; (int) i < events->playersAttached; ++i) {
if (events->players[i]->joystick) {
continue;
}
if (events->preferredJoysticks[i] && strcmp(events->preferredJoysticks[i], joystickName) == 0) {
events->players[i]->joystick = joystick;
if (config) {
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
}
return;
}
}
for (i = 0; (int) i < events->playersAttached; ++i) {
if (events->players[i]->joystick) {
continue;
}
events->players[i]->joystick = joystick;
if (config) {
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
}
break;
}
} else if (event.type == SDL_JOYDEVICEREMOVED) {
SDL_JoystickID ids[MAX_PLAYERS];
SDL_JoystickID ids[MAX_PLAYERS] = { 0 };
size_t i;
for (i = 0; (int) i < events->playersAttached; ++i) {
if (events->players[i]->joystick) {
@ -498,6 +530,18 @@ static void _mSDLHandleJoyButton(struct mCore* core, struct mSDLPlayer* sdlConte
}
}
static void _mSDLHandleJoyHat(struct mCore* core, struct mSDLPlayer* sdlContext, const struct SDL_JoyHatEvent* event) {
int allKeys = mInputMapHat(sdlContext->bindings, SDL_BINDING_BUTTON, event->hat, -1);
if (allKeys == 0) {
return;
}
int keys = mInputMapHat(sdlContext->bindings, SDL_BINDING_BUTTON, event->hat, event->value);
core->clearKeys(core, allKeys ^ keys);
core->addKeys(core, keys);
}
static void _mSDLHandleJoyAxis(struct mCore* core, struct mSDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) {
int clearKeys = ~mInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, -1);
int newKeys = 0;
@ -540,7 +584,7 @@ void mSDLHandleEvent(struct mCoreThread* context, struct mSDLPlayer* sdlContext,
_mSDLHandleJoyButton(context->core, sdlContext, &event->jbutton);
break;
case SDL_JOYHATMOTION:
// TODO
_mSDLHandleJoyHat(context->core, sdlContext, &event->jhat);
break;
case SDL_JOYAXISMOTION:
_mSDLHandleJoyAxis(context->core, sdlContext, &event->jaxis);

View File

@ -96,7 +96,7 @@ bool mSDLAttachPlayer(struct mSDLEvents*, struct mSDLPlayer*);
void mSDLDetachPlayer(struct mSDLEvents*, struct mSDLPlayer*);
void mSDLEventsLoadConfig(struct mSDLEvents*, const struct Configuration*);
void mSDLPlayerChangeJoystick(struct mSDLEvents*, struct mSDLPlayer*, size_t index);
void mSDLUpdateJoysticks(struct mSDLEvents* events);
void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*);
void mSDLPlayerLoadConfig(struct mSDLPlayer*, const struct Configuration*);
void mSDLPlayerSaveConfig(const struct mSDLPlayer*, struct Configuration*);

200726
src/third-party/sqlite3/sqlite3.c vendored Normal file

File diff suppressed because it is too large Load Diff

10442
src/third-party/sqlite3/sqlite3.h vendored Normal file

File diff suppressed because it is too large Load Diff

560
src/third-party/sqlite3/sqlite3ext.h vendored Normal file
View File

@ -0,0 +1,560 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */

View File

@ -6,7 +6,6 @@
#include <mgba-util/formatting.h>
#include <float.h>
#include <time.h>
int ftostr_l(char* restrict str, size_t size, float f, locale_t locale) {
#ifdef HAVE_SNPRINTF_L

View File

@ -1,279 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba-util/nointro.h>
#include <mgba-util/table.h>
#include <mgba-util/vector.h>
#include <mgba-util/vfs.h>
#define KEY_STACK_SIZE 8
struct NoIntroDB {
struct Table categories;
struct Table gameCrc;
};
struct NoIntroItem {
union {
struct Table hash;
char* string;
};
enum NoIntroItemType {
NI_HASH,
NI_STRING
} type;
};
DECLARE_VECTOR(NoIntroCategory, struct NoIntroItem*);
DEFINE_VECTOR(NoIntroCategory, struct NoIntroItem*);
static void _indexU32x(struct NoIntroDB* db, struct Table* table, const char* categoryKey, const char* key) {
struct NoIntroCategory* category = HashTableLookup(&db->categories, categoryKey);
if (!category) {
return;
}
TableInit(table, 256, 0);
char* tmpKey = strdup(key);
const char* keyStack[KEY_STACK_SIZE] = { tmpKey };
size_t i;
for (i = 1; i < KEY_STACK_SIZE; ++i) {
char* next = strchr(keyStack[i - 1], '.');
if (!next) {
break;
}
next[0] = '\0';
keyStack[i] = next + 1;
}
for (i = 0; i < NoIntroCategorySize(category); ++i) {
struct NoIntroItem* item = *NoIntroCategoryGetPointer(category, i);
if (!item) {
continue;
}
struct NoIntroItem* keyloc = item;
size_t s;
for (s = 0; s < KEY_STACK_SIZE && keyStack[s]; ++s) {
if (keyloc->type != NI_HASH) {
keyloc = 0;
break;
}
keyloc = HashTableLookup(&keyloc->hash, keyStack[s]);
if (!keyloc) {
break;
}
}
if (!keyloc || keyloc->type != NI_STRING) {
continue;
}
char* end;
uint32_t key = strtoul(keyloc->string, &end, 16);
if (!end || *end) {
continue;
}
TableInsert(table, key, item);
}
free(tmpKey);
}
static void _itemDeinit(void* value) {
struct NoIntroItem* item = value;
switch (item->type) {
case NI_STRING:
free(item->string);
break;
case NI_HASH:
HashTableDeinit(&item->hash);
break;
}
free(item);
}
static void _dbDeinit(void* value) {
struct NoIntroCategory* category = value;
size_t i;
for (i = 0; i < NoIntroCategorySize(category); ++i) {
struct NoIntroItem* item = *NoIntroCategoryGetPointer(category, i);
switch (item->type) {
case NI_STRING:
free(item->string);
break;
case NI_HASH:
HashTableDeinit(&item->hash);
break;
}
free(item);
}
NoIntroCategoryDeinit(category);
}
static bool _itemToGame(const struct NoIntroItem* item, struct NoIntroGame* game) {
if (item->type != NI_HASH) {
return false;
}
struct NoIntroItem* subitem;
struct NoIntroItem* rom;
memset(game, 0, sizeof(*game));
subitem = HashTableLookup(&item->hash, "name");
if (subitem && subitem->type == NI_STRING) {
game->name = subitem->string;
}
subitem = HashTableLookup(&item->hash, "description");
if (subitem && subitem->type == NI_STRING) {
game->description = subitem->string;
}
rom = HashTableLookup(&item->hash, "rom");
if (!rom || rom->type != NI_HASH) {
return false;
}
subitem = HashTableLookup(&rom->hash, "name");
if (subitem && subitem->type == NI_STRING) {
game->romName = subitem->string;
}
subitem = HashTableLookup(&rom->hash, "size");
if (subitem && subitem->type == NI_STRING) {
char* end;
game->size = strtoul(subitem->string, &end, 0);
if (!end || *end) {
game->size = 0;
}
}
// TODO: md5, sha1
subitem = HashTableLookup(&rom->hash, "flags");
if (subitem && subitem->type == NI_STRING && strcmp(subitem->string, "verified")) {
game->verified = true;
}
return true;
}
struct NoIntroDB* NoIntroDBLoad(struct VFile* vf) {
struct NoIntroDB* db = malloc(sizeof(*db));
HashTableInit(&db->categories, 0, _dbDeinit);
char line[512];
struct {
char* key;
struct NoIntroItem* item;
} keyStack[KEY_STACK_SIZE];
memset(keyStack, 0, sizeof(keyStack));
struct Table* parent = 0;
size_t stackDepth = 0;
while (true) {
ssize_t bytesRead = vf->readline(vf, line, sizeof(line));
if (!bytesRead) {
break;
}
ssize_t i;
const char* token;
for (i = 0; i < bytesRead; ++i) {
while (isspace((int) line[i]) && i < bytesRead) {
++i;
}
if (i >= bytesRead) {
break;
}
token = &line[i];
while (!isspace((int) line[i]) && i < bytesRead) {
++i;
}
if (i >= bytesRead) {
break;
}
switch (token[0]) {
case '(':
if (!keyStack[stackDepth].key) {
goto error;
}
keyStack[stackDepth].item = malloc(sizeof(*keyStack[stackDepth].item));
keyStack[stackDepth].item->type = NI_HASH;
HashTableInit(&keyStack[stackDepth].item->hash, 8, _itemDeinit);
if (parent) {
HashTableInsert(parent, keyStack[stackDepth].key, keyStack[stackDepth].item);
} else {
struct NoIntroCategory* category = HashTableLookup(&db->categories, keyStack[stackDepth].key);
if (!category) {
category = malloc(sizeof(*category));
NoIntroCategoryInit(category, 0);
HashTableInsert(&db->categories, keyStack[stackDepth].key, category);
}
*NoIntroCategoryAppend(category) = keyStack[stackDepth].item;
}
parent = &keyStack[stackDepth].item->hash;
++stackDepth;
if (stackDepth >= KEY_STACK_SIZE) {
goto error;
}
keyStack[stackDepth].key = 0;
break;
case ')':
if (keyStack[stackDepth].key || !stackDepth) {
goto error;
}
--stackDepth;
if (stackDepth) {
parent = &keyStack[stackDepth - 1].item->hash;
} else {
parent = 0;
}
free(keyStack[stackDepth].key);
keyStack[stackDepth].key = 0;
break;
case '"':
++token;
for (; line[i] != '"' && i < bytesRead; ++i);
// Fall through
default:
line[i] = '\0';
if (!keyStack[stackDepth].key) {
keyStack[stackDepth].key = strdup(token);
} else {
struct NoIntroItem* item = malloc(sizeof(*keyStack[stackDepth].item));
item->type = NI_STRING;
item->string = strdup(token);
if (parent) {
HashTableInsert(parent, keyStack[stackDepth].key, item);
} else {
struct NoIntroCategory* category = HashTableLookup(&db->categories, keyStack[stackDepth].key);
if (!category) {
category = malloc(sizeof(*category));
NoIntroCategoryInit(category, 0);
HashTableInsert(&db->categories, keyStack[stackDepth].key, category);
}
*NoIntroCategoryAppend(category) = item;
}
free(keyStack[stackDepth].key);
keyStack[stackDepth].key = 0;
}
break;
}
}
}
_indexU32x(db, &db->gameCrc, "game", "rom.crc");
return db;
error:
HashTableDeinit(&db->categories);
free(db);
return 0;
}
void NoIntroDBDestroy(struct NoIntroDB* db) {
HashTableDeinit(&db->categories);
}
bool NoIntroDBLookupGameByCRC(const struct NoIntroDB* db, uint32_t crc32, struct NoIntroGame* game) {
if (!db) {
return false;
}
struct NoIntroItem* item = TableLookup(&db->gameCrc, crc32);
if (item) {
return _itemToGame(item, game);
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More