mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
20296e7f0e
20
CHANGES
20
CHANGES
|
@ -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:
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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); \
|
||||
} \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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]
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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]);)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -87,6 +87,10 @@
|
|||
#cmakedefine USE_PTHREADS
|
||||
#endif
|
||||
|
||||
#ifndef USE_SQLITE3
|
||||
#cmakedefine USE_SQLITE3
|
||||
#endif
|
||||
|
||||
#ifndef USE_ZLIB
|
||||
#cmakedefine USE_ZLIB
|
||||
#endif
|
||||
|
|
123
src/core/input.c
123
src/core/input.c
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -63,6 +63,7 @@ private:
|
|||
|
||||
#ifdef BUILD_SDL
|
||||
void lookupAxes(const mInputMap*);
|
||||
void lookupHats(const mInputMap*);
|
||||
#endif
|
||||
|
||||
KeyEditor* keyById(GBAKey);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -18,6 +18,9 @@
|
|||
#include <mgba/internal/gb/sio/lockstep.h>
|
||||
#endif
|
||||
|
||||
struct GBSIOLockstepNode;
|
||||
struct GBASIOLockstepNode;
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class GameController;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)"));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue