mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
cda444b27f
2
CHANGES
2
CHANGES
|
@ -40,6 +40,7 @@ Misc:
|
|||
|
||||
0.10.0: (Future)
|
||||
Features:
|
||||
- Preliminary Lua scripting support
|
||||
- Presets for Game Boy palettes
|
||||
- Add Super Game Boy palettes for original Game Boy games
|
||||
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
|
||||
|
@ -115,6 +116,7 @@ Misc:
|
|||
- Qt: Show warning if XQ audio is toggled while loaded (fixes mgba.io/i/2295)
|
||||
- Qt: Add e-Card passing to the command line (closes mgba.io/i/2474)
|
||||
- Qt: Boot both a multiboot image and ROM with CLI args (closes mgba.io/i/1941)
|
||||
- Qt: Improve cheat parsing (fixes mgba.io/i/2297)
|
||||
- Windows: Attach to console if present
|
||||
- Vita: Add bilinear filtering option (closes mgba.io/i/344)
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ if(NOT LIBMGBA_ONLY)
|
|||
set(USE_LIBZIP ON CACHE BOOL "Whether or not to enable LIBZIP support")
|
||||
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
|
||||
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
|
||||
set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting 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")
|
||||
|
@ -81,6 +82,7 @@ if(NOT LIBMGBA_ONLY)
|
|||
set(BUILD_GL ON CACHE BOOL "Build with OpenGL")
|
||||
set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2")
|
||||
set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3")
|
||||
set(BUILD_DOCGEN OFF CACHE BOOL "Build the scripting API documentation generator")
|
||||
set(USE_EPOXY ON CACHE STRING "Build with libepoxy")
|
||||
set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies")
|
||||
set(DISTBUILD OFF CACHE BOOL "Build distribution packages")
|
||||
|
@ -88,6 +90,7 @@ if(NOT LIBMGBA_ONLY)
|
|||
set(WIN32_UNIX_PATHS OFF CACHE BOOL "Use Unix-like paths")
|
||||
mark_as_advanced(WIN32_UNIX_PATHS)
|
||||
endif()
|
||||
mark_as_advanced(BUILD_DOCGEN)
|
||||
else()
|
||||
set(DISABLE_FRONTENDS ON)
|
||||
set(DISABLE_DEPS ON)
|
||||
|
@ -751,6 +754,17 @@ endif()
|
|||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND ENABLES SCRIPTING)
|
||||
if(NOT USE_LUA VERSION_LESS 5.1)
|
||||
find_feature(USE_LUA "Lua" ${USE_LUA})
|
||||
else()
|
||||
find_feature(USE_LUA "Lua")
|
||||
endif()
|
||||
if(USE_LUA)
|
||||
list(APPEND FEATURE_DEFINES USE_LUA)
|
||||
include_directories(AFTER ${LUA_INCLUDE_DIR})
|
||||
list(APPEND FEATURE_DEFINES LUA_VERSION_ONLY=\"${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}\")
|
||||
list(APPEND DEPENDENCY_LIB ${LUA_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(BUILD_PYTHON)
|
||||
find_package(PythonLibs ${USE_PYTHON_VERSION})
|
||||
|
@ -758,6 +772,7 @@ if(ENABLE_SCRIPTING)
|
|||
include_directories(AFTER ${PYTHON_INCLUDE_DIRS})
|
||||
list(APPEND ENABLES PYTHON)
|
||||
endif()
|
||||
add_subdirectory(src/script)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src/arm)
|
||||
|
@ -801,6 +816,11 @@ if(USE_DEBUGGERS)
|
|||
list(APPEND FEATURES DEBUGGERS)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND FEATURE_SRC ${SCRIPT_SRC})
|
||||
list(APPEND TEST_SRC ${SCRIPT_TEST_SRC})
|
||||
endif()
|
||||
|
||||
foreach(FEATURE IN LISTS FEATURES)
|
||||
list(APPEND FEATURE_DEFINES "USE_${FEATURE}")
|
||||
endforeach()
|
||||
|
@ -970,6 +990,11 @@ if(BUILD_UPDATER)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_SCRIPTING AND BUILD_DOCGEN)
|
||||
add_executable(docgen ${CMAKE_CURRENT_SOURCE_DIR}/src/script/docgen.c)
|
||||
target_link_libraries(docgen ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME})
|
||||
endif()
|
||||
|
||||
if(BUILD_SDL)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl)
|
||||
endif()
|
||||
|
@ -1248,6 +1273,14 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY)
|
|||
message(STATUS " ELF loading support: ${USE_ELF}")
|
||||
message(STATUS " Discord Rich Presence support: ${USE_DISCORD_RPC}")
|
||||
message(STATUS " OpenGL support: ${SUMMARY_GL}")
|
||||
message(STATUS "Scripting support: ${ENABLE_SCRIPTING}")
|
||||
if(ENABLE_SCRIPTING)
|
||||
if(LUA_VERSION_STRING)
|
||||
message(STATUS " Lua: ${LUA_VERSION_STRING}")
|
||||
else()
|
||||
message(STATUS " Lua: ${USE_LUA}")
|
||||
endif()
|
||||
endif()
|
||||
message(STATUS "Frontends:")
|
||||
message(STATUS " Qt: ${BUILD_QT}")
|
||||
message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}")
|
||||
|
|
|
@ -23,6 +23,7 @@ Features
|
|||
- Solar sensor support for Boktai games.
|
||||
- Game Boy Camera and Game Boy Printer support.
|
||||
- A built-in GBA BIOS implementation, and ability to load external BIOS files. DS currently requires BIOS and firmware dumps[<sup>[2]</sup>](#dscaveat).
|
||||
- Scripting support using Lua.
|
||||
- Turbo/fast-forward support by holding Tab.
|
||||
- Rewind by holding Backquote.
|
||||
- Frameskip, configurable up to 10.
|
||||
|
@ -172,7 +173,7 @@ This will build and install medusa into `/usr/bin` and `/usr/lib`. Dependencies
|
|||
|
||||
If you are on macOS, the steps are a little different. Assuming you are using the homebrew package manager, the recommended commands to obtain the dependencies and build are:
|
||||
|
||||
brew install cmake ffmpeg libzip qt5 sdl2 libedit pkg-config
|
||||
brew install cmake ffmpeg libzip qt5 sdl2 libedit lua pkg-config
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
|
||||
|
@ -186,7 +187,7 @@ Note that you should not do a `make install` on macOS, as it will not work prope
|
|||
|
||||
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 1100MiB of packages, so it will take a long time):
|
||||
|
||||
pacman -Sy --needed base-devel git ${MINGW_PACKAGE_PREFIX}-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,pkgconf,qt5,SDL2,ntldd-git}
|
||||
pacman -Sy --needed base-devel git ${MINGW_PACKAGE_PREFIX}-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,lua,pkgconf,qt5,SDL2,ntldd-git}
|
||||
|
||||
Check out the source code by running this command:
|
||||
|
||||
|
@ -205,7 +206,7 @@ Please note that this build of medusa for Windows is not suitable for distributi
|
|||
|
||||
To build using Visual Studio is a similarly complicated setup. To begin you will need to install [vcpkg](https://github.com/Microsoft/vcpkg). After installing vcpkg you will need to install several additional packages:
|
||||
|
||||
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3
|
||||
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip lua sdl2 sqlite3
|
||||
|
||||
Note that this installation won't support hardware accelerated video encoding on Nvidia hardware. If you care about this, you'll need to install CUDA beforehand, and then substitute `ffmpeg[vpx,x264,nvcodec]` into the previous command.
|
||||
|
||||
|
@ -243,6 +244,7 @@ medusa has no hard dependencies, however, the following optional dependencies ar
|
|||
- libzip or zlib: for loading ROMs stored in zip files.
|
||||
- SQLite3: for game databases.
|
||||
- libelf: for ELF loading.
|
||||
- Lua: for scripting.
|
||||
|
||||
SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* Copyright (c) 2013-2022 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 M_MACROS_H
|
||||
#define M_MACROS_H
|
||||
|
||||
#define _mCPP_CAT(A, B) A ## B
|
||||
|
||||
#define _mIDENT(...) __VA_ARGS__
|
||||
#define _mCALL(FN, ...) _mIDENT(FN(__VA_ARGS__))
|
||||
#define _mCAT(A, B) _mCPP_CAT(A, B)
|
||||
#define _mSTRINGIFY(X, ...) #X
|
||||
|
||||
#define _mCALL_0(FN, ...)
|
||||
#define _mCALL_1(FN, A) FN(A)
|
||||
#define _mCALL_2(FN, A, B) FN(A), FN(B)
|
||||
#define _mCALL_3(FN, A, ...) FN(A), _mCALL_2(FN, __VA_ARGS__)
|
||||
#define _mCALL_4(FN, A, ...) FN(A), _mCALL_3(FN, __VA_ARGS__)
|
||||
#define _mCALL_5(FN, A, ...) FN(A), _mCALL_4(FN, __VA_ARGS__)
|
||||
#define _mCALL_6(FN, A, ...) FN(A), _mCALL_5(FN, __VA_ARGS__)
|
||||
#define _mCALL_7(FN, A, ...) FN(A), _mCALL_6(FN, __VA_ARGS__)
|
||||
#define _mCALL_8(FN, A, ...) FN(A), _mCALL_7(FN, __VA_ARGS__)
|
||||
#define _mCALL_9(FN, A, ...) FN(A), _mCALL_8(FN, __VA_ARGS__)
|
||||
|
||||
#define _mCOMMA_0(N, ...) N
|
||||
#define _mCOMMA_1(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_2(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_3(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_4(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_5(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_6(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_7(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_8(N, ...) N, __VA_ARGS__
|
||||
#define _mCOMMA_9(N, ...) N, __VA_ARGS__
|
||||
|
||||
#define _mEVEN_0(...)
|
||||
#define _mEVEN_1(A, B, ...) A
|
||||
#define _mEVEN_2(A, B, ...) A, _mIDENT(_mEVEN_1(__VA_ARGS__))
|
||||
#define _mEVEN_3(A, B, ...) A, _mIDENT(_mEVEN_2(__VA_ARGS__))
|
||||
#define _mEVEN_4(A, B, ...) A, _mIDENT(_mEVEN_3(__VA_ARGS__))
|
||||
#define _mEVEN_5(A, B, ...) A, _mIDENT(_mEVEN_4(__VA_ARGS__))
|
||||
#define _mEVEN_6(A, B, ...) A, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
#define _mEVEN_7(A, B, ...) A, _mIDENT(_mEVEN_6(__VA_ARGS__))
|
||||
#define _mEVEN_8(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
#define _mEVEN_9(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
|
||||
#define _mODD_0(...)
|
||||
#define _mODD_1(A, B, ...) B
|
||||
#define _mODD_2(A, B, ...) B, _mIDENT(_mODD_1(__VA_ARGS__))
|
||||
#define _mODD_3(A, B, ...) B, _mIDENT(_mODD_2(__VA_ARGS__))
|
||||
#define _mODD_4(A, B, ...) B, _mIDENT(_mODD_3(__VA_ARGS__))
|
||||
#define _mODD_5(A, B, ...) B, _mIDENT(_mODD_4(__VA_ARGS__))
|
||||
#define _mODD_6(A, B, ...) B, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
#define _mODD_7(A, B, ...) B, _mIDENT(_mODD_6(__VA_ARGS__))
|
||||
#define _mODD_8(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
#define _mODD_9(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
|
||||
#define _mSUCC_0 1
|
||||
#define _mSUCC_1 2
|
||||
#define _mSUCC_2 3
|
||||
#define _mSUCC_3 4
|
||||
#define _mSUCC_4 5
|
||||
#define _mSUCC_5 6
|
||||
#define _mSUCC_6 7
|
||||
#define _mSUCC_7 8
|
||||
#define _mSUCC_8 9
|
||||
|
||||
#define _mPRED_1 0
|
||||
#define _mPRED_2 1
|
||||
#define _mPRED_3 2
|
||||
#define _mPRED_4 3
|
||||
#define _mPRED_5 4
|
||||
#define _mPRED_6 5
|
||||
#define _mPRED_7 6
|
||||
#define _mPRED_8 7
|
||||
#define _mPRED_9 8
|
||||
|
||||
#endif
|
|
@ -70,7 +70,7 @@ void HashTableClear(struct Table*);
|
|||
|
||||
void HashTableEnumerate(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
||||
void HashTableEnumerateBinary(const struct Table*, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user);
|
||||
void HashTableEnumerateCustom(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
||||
void HashTableEnumerateCustom(const struct Table*, void (*handler)(void* key, void* value, void* user), void* user);
|
||||
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user);
|
||||
const char* HashTableSearchPointer(const struct Table* table, const void* value);
|
||||
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
|
||||
|
|
|
@ -192,6 +192,7 @@ struct VFile* mCoreGetState(struct mCore* core, int slot, bool write);
|
|||
void mCoreDeleteState(struct mCore* core, int slot);
|
||||
|
||||
void mCoreTakeScreenshot(struct mCore* core);
|
||||
bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf);
|
||||
#endif
|
||||
|
||||
struct mCore* mCoreFindVF(struct VFile* vf);
|
||||
|
|
|
@ -56,6 +56,9 @@ int mLogFilterLevels(const struct mLogFilter*, int category);
|
|||
ATTRIBUTE_FORMAT(printf, 3, 4)
|
||||
void mLog(int category, enum mLogLevel level, const char* format, ...);
|
||||
|
||||
ATTRIBUTE_FORMAT(printf, 4, 5)
|
||||
void mLogExplicit(struct mLogger*, int category, enum mLogLevel level, const char* format, ...);
|
||||
|
||||
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY, mLOG_ ## LEVEL, __VA_ARGS__)
|
||||
|
||||
#define mLOG_DECLARE_CATEGORY(CATEGORY) extern int _mLOG_CAT_ ## CATEGORY;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2022 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
|
||||
|
@ -10,9 +10,21 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/log.h>
|
||||
#ifdef USE_DEBUGGERS
|
||||
#include <mgba/debugger/debugger.h>
|
||||
#endif
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(SCRIPT);
|
||||
|
||||
struct mCore;
|
||||
struct mScriptTextBuffer;
|
||||
mSCRIPT_DECLARE_STRUCT(mCore);
|
||||
mSCRIPT_DECLARE_STRUCT(mLogger);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptConsole);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptTextBuffer);
|
||||
|
||||
struct mScriptBridge;
|
||||
struct VFile;
|
||||
|
@ -31,6 +43,24 @@ struct mScriptEngine {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct mScriptTextBuffer {
|
||||
void (*init)(struct mScriptTextBuffer*, const char* name);
|
||||
void (*deinit)(struct mScriptTextBuffer*);
|
||||
|
||||
void (*setName)(struct mScriptTextBuffer*, const char* text);
|
||||
|
||||
uint32_t (*getX)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*getY)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*cols)(const struct mScriptTextBuffer*);
|
||||
uint32_t (*rows)(const struct mScriptTextBuffer*);
|
||||
|
||||
void (*print)(struct mScriptTextBuffer*, const char* text);
|
||||
void (*clear)(struct mScriptTextBuffer*);
|
||||
void (*setSize)(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows);
|
||||
void (*moveCursor)(struct mScriptTextBuffer*, uint32_t x, uint32_t y);
|
||||
void (*advance)(struct mScriptTextBuffer*, int32_t);
|
||||
};
|
||||
|
||||
struct mScriptBridge* mScriptBridgeCreate(void);
|
||||
void mScriptBridgeDestroy(struct mScriptBridge*);
|
||||
|
||||
|
@ -47,6 +77,16 @@ bool mScriptBridgeLoadScript(struct mScriptBridge*, const char* name);
|
|||
|
||||
bool mScriptBridgeLookupSymbol(struct mScriptBridge*, const char* name, int32_t* out);
|
||||
|
||||
struct mScriptContext;
|
||||
void mScriptContextAttachCore(struct mScriptContext*, struct mCore*);
|
||||
void mScriptContextDetachCore(struct mScriptContext*);
|
||||
|
||||
void mScriptContextAttachLogger(struct mScriptContext*, struct mLogger*);
|
||||
void mScriptContextDetachLogger(struct mScriptContext*);
|
||||
|
||||
typedef struct mScriptTextBuffer* (*mScriptContextBufferFactory)(void*);
|
||||
void mScriptContextSetTextBufferFactory(struct mScriptContext*, mScriptContextBufferFactory factory, void* cbContext);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,7 @@ enum mStateExtdataTag {
|
|||
#define SAVESTATE_CHEATS 4
|
||||
#define SAVESTATE_RTC 8
|
||||
#define SAVESTATE_METADATA 16
|
||||
#define SAVESTATE_ALL 31
|
||||
|
||||
struct mStateExtdataItem {
|
||||
int32_t size;
|
||||
|
|
|
@ -23,6 +23,9 @@ struct mThreadLogger {
|
|||
struct mCoreThread* p;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
struct mScriptContext;
|
||||
#endif
|
||||
struct mCoreThreadInternal;
|
||||
struct mCoreThread {
|
||||
// Input
|
||||
|
@ -39,6 +42,10 @@ struct mCoreThread {
|
|||
void* userData;
|
||||
void (*run)(struct mCoreThread*);
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
struct mScriptContext* scriptContext;
|
||||
#endif
|
||||
|
||||
struct mCoreThreadInternal* impl;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ extern const uint32_t SGB_SM83_FREQUENCY;
|
|||
|
||||
mLOG_DECLARE_CATEGORY(GB);
|
||||
|
||||
// TODO: Prefix GBAIRQ
|
||||
enum GBIRQ {
|
||||
GB_IRQ_VBLANK = 0x0,
|
||||
GB_IRQ_LCDSTAT = 0x1,
|
||||
|
|
|
@ -22,20 +22,20 @@ CXX_GUARD_START
|
|||
#define GBA_ARM7TDMI_FREQUENCY 0x1000000U
|
||||
|
||||
enum GBAIRQ {
|
||||
IRQ_VBLANK = 0x0,
|
||||
IRQ_HBLANK = 0x1,
|
||||
IRQ_VCOUNTER = 0x2,
|
||||
IRQ_TIMER0 = 0x3,
|
||||
IRQ_TIMER1 = 0x4,
|
||||
IRQ_TIMER2 = 0x5,
|
||||
IRQ_TIMER3 = 0x6,
|
||||
IRQ_SIO = 0x7,
|
||||
IRQ_DMA0 = 0x8,
|
||||
IRQ_DMA1 = 0x9,
|
||||
IRQ_DMA2 = 0xA,
|
||||
IRQ_DMA3 = 0xB,
|
||||
IRQ_KEYPAD = 0xC,
|
||||
IRQ_GAMEPAK = 0xD
|
||||
GBA_IRQ_VBLANK = 0x0,
|
||||
GBA_IRQ_HBLANK = 0x1,
|
||||
GBA_IRQ_VCOUNTER = 0x2,
|
||||
GBA_IRQ_TIMER0 = 0x3,
|
||||
GBA_IRQ_TIMER1 = 0x4,
|
||||
GBA_IRQ_TIMER2 = 0x5,
|
||||
GBA_IRQ_TIMER3 = 0x6,
|
||||
GBA_IRQ_SIO = 0x7,
|
||||
GBA_IRQ_DMA0 = 0x8,
|
||||
GBA_IRQ_DMA1 = 0x9,
|
||||
GBA_IRQ_DMA2 = 0xA,
|
||||
GBA_IRQ_DMA3 = 0xB,
|
||||
GBA_IRQ_KEYPAD = 0xC,
|
||||
GBA_IRQ_GAMEPAK = 0xD
|
||||
};
|
||||
|
||||
enum GBAIdleLoopOptimization {
|
||||
|
@ -45,9 +45,9 @@ enum GBAIdleLoopOptimization {
|
|||
};
|
||||
|
||||
enum {
|
||||
SP_BASE_SYSTEM = 0x03007F00,
|
||||
SP_BASE_IRQ = 0x03007FA0,
|
||||
SP_BASE_SUPERVISOR = 0x03007FE0
|
||||
GBA_SP_BASE_SYSTEM = 0x03007F00,
|
||||
GBA_SP_BASE_IRQ = 0x03007FA0,
|
||||
GBA_SP_BASE_SUPERVISOR = 0x03007FE0
|
||||
};
|
||||
|
||||
struct ARMCore;
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Copyright (c) 2013-2022 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 M_SCRIPT_LUA_H
|
||||
#define M_SCRIPT_LUA_H
|
||||
#include <mgba/script/context.h>
|
||||
|
||||
extern struct mScriptEngine2* const mSCRIPT_ENGINE_LUA;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,95 @@
|
|||
/* Copyright (c) 2013-2022 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 M_SCRIPT_CONTEXT_H
|
||||
#define M_SCRIPT_CONTEXT_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#define mSCRIPT_CONSTANT_PAIR(NS, CONST) { #CONST, mScriptValueCreateFromSInt(NS ## _ ## CONST) }
|
||||
#define mSCRIPT_CONSTANT_SENTINEL { NULL, NULL }
|
||||
|
||||
struct mScriptFrame;
|
||||
struct mScriptFunction;
|
||||
struct mScriptEngineContext;
|
||||
|
||||
struct mScriptContext {
|
||||
struct Table rootScope;
|
||||
struct Table engines;
|
||||
struct mScriptList refPool;
|
||||
struct Table weakrefs;
|
||||
uint32_t nextWeakref;
|
||||
struct Table callbacks;
|
||||
struct mScriptValue* constants;
|
||||
};
|
||||
|
||||
struct mScriptEngine2 {
|
||||
const char* name;
|
||||
|
||||
void (*init)(struct mScriptEngine2*);
|
||||
void (*deinit)(struct mScriptEngine2*);
|
||||
|
||||
struct mScriptEngineContext* (*create)(struct mScriptEngine2*, struct mScriptContext*);
|
||||
};
|
||||
|
||||
struct mScriptEngineContext {
|
||||
struct mScriptContext* context;
|
||||
void (*destroy)(struct mScriptEngineContext*);
|
||||
|
||||
bool (*isScript)(struct mScriptEngineContext*, const char* name, struct VFile* vf);
|
||||
|
||||
bool (*setGlobal)(struct mScriptEngineContext*, const char* name, struct mScriptValue*);
|
||||
struct mScriptValue* (*getGlobal)(struct mScriptEngineContext*, const char* name);
|
||||
|
||||
bool (*load)(struct mScriptEngineContext*, const char* filename, struct VFile*);
|
||||
bool (*run)(struct mScriptEngineContext*);
|
||||
const char* (*getError)(struct mScriptEngineContext*);
|
||||
};
|
||||
|
||||
struct mScriptKVPair {
|
||||
const char* key;
|
||||
struct mScriptValue* value;
|
||||
};
|
||||
|
||||
void mScriptContextInit(struct mScriptContext*);
|
||||
void mScriptContextDeinit(struct mScriptContext*);
|
||||
|
||||
void mScriptContextFillPool(struct mScriptContext*, struct mScriptValue*);
|
||||
void mScriptContextDrainPool(struct mScriptContext*);
|
||||
|
||||
struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext*, struct mScriptEngine2*);
|
||||
void mScriptContextRegisterEngines(struct mScriptContext*);
|
||||
|
||||
void mScriptContextSetGlobal(struct mScriptContext*, const char* key, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextGetGlobal(struct mScriptContext*, const char* key);
|
||||
void mScriptContextRemoveGlobal(struct mScriptContext*, const char* key);
|
||||
struct mScriptValue* mScriptContextEnsureGlobal(struct mScriptContext*, const char* key, const struct mScriptType* type);
|
||||
|
||||
uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
||||
|
||||
void mScriptContextAttachStdlib(struct mScriptContext* context);
|
||||
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants);
|
||||
|
||||
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback);
|
||||
void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
||||
|
||||
struct VFile;
|
||||
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
||||
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);
|
||||
|
||||
bool mScriptInvoke(const struct mScriptValue* fn, struct mScriptFrame* frame);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,488 @@
|
|||
/* Copyright (c) 2013-2022 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 M_SCRIPT_MACROS_H
|
||||
#define M_SCRIPT_MACROS_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#define mSCRIPT_POP(STACK, TYPE, NAME) \
|
||||
mSCRIPT_TYPE_C_ ## TYPE NAME; \
|
||||
do { \
|
||||
struct mScriptValue* _val = mScriptListGetPointer(STACK, mScriptListSize(STACK) - 1); \
|
||||
bool deref = true; \
|
||||
if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \
|
||||
if (_val->type->base == mSCRIPT_TYPE_WRAPPER) { \
|
||||
_val = mScriptValueUnwrap(_val); \
|
||||
deref = false; \
|
||||
if (!(mSCRIPT_TYPE_CMP(TYPE, _val->type))) { \
|
||||
return false; \
|
||||
} \
|
||||
} else { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
NAME = _val->value.mSCRIPT_TYPE_FIELD_ ## TYPE; \
|
||||
if (deref) { \
|
||||
mScriptValueDeref(_val); \
|
||||
} \
|
||||
mScriptListResize(STACK, -1); \
|
||||
} while (0)
|
||||
|
||||
#define mSCRIPT_POP_0(...)
|
||||
#define mSCRIPT_POP_1(FRAME, T0) mSCRIPT_POP(FRAME, T0, p0)
|
||||
#define mSCRIPT_POP_2(FRAME, T0, T1) mSCRIPT_POP(FRAME, T1, p1); mSCRIPT_POP_1(FRAME, T0)
|
||||
#define mSCRIPT_POP_3(FRAME, T0, T1, T2) mSCRIPT_POP(FRAME, T2, p2); mSCRIPT_POP_2(FRAME, T0, T1)
|
||||
#define mSCRIPT_POP_4(FRAME, T0, T1, T2, T3) mSCRIPT_POP(FRAME, T3, p3); mSCRIPT_POP_3(FRAME, T0, T1, T2)
|
||||
#define mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4) mSCRIPT_POP(FRAME, T4, p4); mSCRIPT_POP_4(FRAME, T0, T1, T2, T3)
|
||||
#define mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5) mSCRIPT_POP(FRAME, T5, p5); mSCRIPT_POP_5(FRAME, T0, T1, T2, T3, T4)
|
||||
#define mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6) mSCRIPT_POP(FRAME, T6, p6); mSCRIPT_POP_6(FRAME, T0, T1, T2, T3, T4, T5)
|
||||
#define mSCRIPT_POP_8(FRAME, T0, T1, T2, T3, T4, T5, T6, T7) mSCRIPT_POP(FRAME, T7, p7); mSCRIPT_POP_7(FRAME, T0, T1, T2, T3, T4, T5, T6)
|
||||
|
||||
#define mSCRIPT_PUSH(STACK, TYPE, NAME) \
|
||||
do { \
|
||||
struct mScriptValue* _val = mScriptListAppend(STACK); \
|
||||
_val->type = mSCRIPT_TYPE_MS_ ## TYPE; \
|
||||
_val->refs = mSCRIPT_VALUE_UNREF; \
|
||||
_val->flags = 0; \
|
||||
_val->value.mSCRIPT_TYPE_FIELD_ ## TYPE = NAME; \
|
||||
} while (0)
|
||||
|
||||
#define mSCRIPT_ARG_NAMES_0
|
||||
#define mSCRIPT_ARG_NAMES_1 p0
|
||||
#define mSCRIPT_ARG_NAMES_2 p0, p1
|
||||
#define mSCRIPT_ARG_NAMES_3 p0, p1, p2
|
||||
#define mSCRIPT_ARG_NAMES_4 p0, p1, p2, p3
|
||||
#define mSCRIPT_ARG_NAMES_5 p0, p1, p2, p3, p4
|
||||
#define mSCRIPT_ARG_NAMES_6 p0, p1, p2, p3, p4, p5
|
||||
#define mSCRIPT_ARG_NAMES_7 p0, p1, p2, p3, p4, p5, p6
|
||||
#define mSCRIPT_ARG_NAMES_8 p0, p1, p2, p3, p4, p5, p6, p7
|
||||
|
||||
#define mSCRIPT_PREFIX_0(PREFIX, ...)
|
||||
#define mSCRIPT_PREFIX_1(PREFIX, T0) PREFIX ## T0
|
||||
#define mSCRIPT_PREFIX_2(PREFIX, T0, T1) PREFIX ## T0, PREFIX ## T1
|
||||
#define mSCRIPT_PREFIX_3(PREFIX, T0, T1, T2) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2
|
||||
#define mSCRIPT_PREFIX_4(PREFIX, T0, T1, T2, T3) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3
|
||||
#define mSCRIPT_PREFIX_5(PREFIX, T0, T1, T2, T3, T4) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4
|
||||
#define mSCRIPT_PREFIX_6(PREFIX, T0, T1, T2, T3, T4, T5) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5
|
||||
#define mSCRIPT_PREFIX_7(PREFIX, T0, T1, T2, T3, T4, T5, T6) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6
|
||||
#define mSCRIPT_PREFIX_8(PREFIX, T0, T1, T2, T3, T4, T5, T6, T7) PREFIX ## T0, PREFIX ## T1, PREFIX ## T2, PREFIX ## T3, PREFIX ## T4, PREFIX ## T5, PREFIX ## T6, PREFIX ## T7
|
||||
#define mSCRIPT_PREFIX_N(N) mSCRIPT_PREFIX_ ## N
|
||||
|
||||
#define _mSCRIPT_FIELD_NAME(V) (V)->name
|
||||
|
||||
#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS))
|
||||
#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \
|
||||
mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \
|
||||
mSCRIPT_PUSH(&frame->returnValues, RETURN, out)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT(STRUCT) \
|
||||
extern const struct mScriptType mSTStruct_ ## STRUCT; \
|
||||
extern const struct mScriptType mSTStructConst_ ## STRUCT; \
|
||||
extern const struct mScriptType mSTStructPtr_ ## STRUCT; \
|
||||
extern const struct mScriptType mSTStructPtrConst_ ## STRUCT;
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT(STRUCT) \
|
||||
const struct mScriptType mSTStruct_ ## STRUCT; \
|
||||
const struct mScriptType mSTStructConst_ ## STRUCT; \
|
||||
const struct mScriptType mSTStructPtr_ ## STRUCT; \
|
||||
const struct mScriptType mSTStructPtrConst_ ## STRUCT; \
|
||||
static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT; \
|
||||
static bool _mSTStructPtrCast_ ## STRUCT(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) { \
|
||||
if (input->type == type || (input->type->constType == type)) { \
|
||||
output->type = type; \
|
||||
output->value.opaque = input->value.opaque; \
|
||||
return true; \
|
||||
} \
|
||||
if (input->type != &mSTStructPtr_ ## STRUCT && input->type != &mSTStructPtrConst_ ## STRUCT) { \
|
||||
return false; \
|
||||
} \
|
||||
if (type == &mSTStructConst_ ## STRUCT || (!input->type->isConst && type == &mSTStruct_ ## STRUCT)) { \
|
||||
output->type = type; \
|
||||
output->value.opaque = *(void**) input->value.opaque; \
|
||||
return true; \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
const struct mScriptType mSTStruct_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_OBJECT, \
|
||||
.details = { \
|
||||
.cls = &_mSTStructDetails_ ## STRUCT \
|
||||
}, \
|
||||
.size = sizeof(struct STRUCT), \
|
||||
.name = "struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = mScriptObjectFree, \
|
||||
.cast = mScriptObjectCast, \
|
||||
.constType = &mSTStructConst_ ## STRUCT, \
|
||||
}; \
|
||||
const struct mScriptType mSTStructConst_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_OBJECT, \
|
||||
.isConst = true, \
|
||||
.details = { \
|
||||
.cls = &_mSTStructDetails_ ## STRUCT \
|
||||
}, \
|
||||
.size = sizeof(struct STRUCT), \
|
||||
.name = "const struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = NULL, \
|
||||
.cast = mScriptObjectCast, \
|
||||
}; \
|
||||
const struct mScriptType mSTStructPtr_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_OPAQUE, \
|
||||
.details = { \
|
||||
.type = &mSTStruct_ ## STRUCT \
|
||||
}, \
|
||||
.size = sizeof(void*), \
|
||||
.name = "ptr struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = NULL, \
|
||||
.cast = _mSTStructPtrCast_ ## STRUCT, \
|
||||
.constType = &mSTStructPtrConst_ ## STRUCT, \
|
||||
}; \
|
||||
const struct mScriptType mSTStructPtrConst_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_OPAQUE, \
|
||||
.details = { \
|
||||
.type = &mSTStructConst_ ## STRUCT \
|
||||
}, \
|
||||
.isConst = true, \
|
||||
.size = sizeof(void*), \
|
||||
.name = "ptr const struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = NULL, \
|
||||
.cast = _mSTStructPtrCast_ ## STRUCT, \
|
||||
}; \
|
||||
const struct mScriptType mSTWrapper_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_WRAPPER, \
|
||||
.details = { \
|
||||
.type = &mSTStruct_ ## STRUCT \
|
||||
}, \
|
||||
.size = sizeof(struct mScriptValue), \
|
||||
.name = "wrapper struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = NULL, \
|
||||
}; \
|
||||
const struct mScriptType mSTWrapperConst_ ## STRUCT = { \
|
||||
.base = mSCRIPT_TYPE_WRAPPER, \
|
||||
.details = { \
|
||||
.type = &mSTStructConst_ ## STRUCT \
|
||||
}, \
|
||||
.size = sizeof(struct mScriptValue), \
|
||||
.name = "wrapper const struct::" #STRUCT, \
|
||||
.alloc = NULL, \
|
||||
.free = NULL, \
|
||||
}; \
|
||||
static struct mScriptTypeClass _mSTStructDetails_ ## STRUCT = { \
|
||||
.init = false, \
|
||||
.details = (const struct mScriptClassInitDetails[]) {
|
||||
|
||||
#define mSCRIPT_DEFINE_DOCSTRING(DOCSTRING) { \
|
||||
.type = mSCRIPT_CLASS_INIT_DOCSTRING, \
|
||||
.info = { \
|
||||
.comment = DOCSTRING \
|
||||
} \
|
||||
},
|
||||
#define mSCRIPT_DEFINE_CLASS_DOCSTRING(DOCSTRING) { \
|
||||
.type = mSCRIPT_CLASS_INIT_CLASS_DOCSTRING, \
|
||||
.info = { \
|
||||
.comment = DOCSTRING \
|
||||
} \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \
|
||||
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
|
||||
.info = { \
|
||||
.member = { \
|
||||
.name = #EXPORTED_NAME, \
|
||||
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
||||
.offset = offsetof(struct STRUCT, NAME) \
|
||||
} \
|
||||
} \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
|
||||
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
||||
.info = { \
|
||||
.parent = mSCRIPT_TYPE_MS_S(PARENT) \
|
||||
} \
|
||||
},
|
||||
|
||||
#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \
|
||||
_mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define _mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, NRET, RETURN, NPARAMS, DEFAULTS, ...) \
|
||||
static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx); \
|
||||
static const struct mScriptFunction _mSTStructBindingFunction_ ## TYPE ## _ ## NAME = { \
|
||||
.call = &_mSTStructBinding_ ## TYPE ## _ ## NAME \
|
||||
}; \
|
||||
\
|
||||
static void _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME(struct mScriptValue* val) { \
|
||||
val->value.copaque = &_mSTStructBindingFunction_ ## TYPE ## _ ## NAME; \
|
||||
}\
|
||||
static const struct mScriptType _mSTStructBindingType_ ## TYPE ## _ ## NAME = { \
|
||||
.base = mSCRIPT_TYPE_FUNCTION, \
|
||||
.name = "struct::" #TYPE "." #NAME, \
|
||||
.alloc = _mSTStructBindingAlloc_ ## TYPE ## _ ## NAME, \
|
||||
.details = { \
|
||||
.function = { \
|
||||
.parameters = { \
|
||||
.count = _mSUCC_ ## NPARAMS, \
|
||||
.entries = { mSCRIPT_TYPE_MS_ ## S(TYPE), _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
.names = { "this", _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
.defaults = DEFAULTS, \
|
||||
}, \
|
||||
.returnType = { \
|
||||
.count = NRET, \
|
||||
.entries = { RETURN } \
|
||||
}, \
|
||||
}, \
|
||||
} \
|
||||
};
|
||||
|
||||
#define _mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, RETURN, NAME, CONST, NPARAMS, ...) \
|
||||
typedef RETURN (*_mSTStructFunctionType_ ## TYPE ## _ ## NAME)(_mCOMMA_ ## NPARAMS(CONST struct TYPE* , mSCRIPT_PREFIX_ ## NPARAMS(mSCRIPT_TYPE_C_, __VA_ARGS__)))
|
||||
|
||||
#define _mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, T, NPARAMS, ...) \
|
||||
static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_CALL(RETURN, FUNCTION, _mSUCC_ ## NPARAMS); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define _mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, T, NPARAMS, ...) \
|
||||
static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mSCRIPT_STRUCT_METHOD_POP(TYPE, T, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_CALL_VOID(FUNCTION, _mSUCC_ ## NPARAMS); \
|
||||
return true; \
|
||||
} \
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, 0, NPARAMS, NULL, __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, NULL, __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, 0, NPARAMS, NULL, __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, , NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, S, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, S, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, mSCRIPT_TYPE_C_ ## RETURN, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 1, mSCRIPT_TYPE_MS_##RETURN, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_BINDING(TYPE, RETURN, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAULTS(TYPE, NAME, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD_SIGNATURE(TYPE, void, NAME, const, NPARAMS, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
_mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, NAME, CS, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_ ## TYPE ## _ ## NAME, __VA_ARGS__) \
|
||||
_mSCRIPT_DECLARE_STRUCT_VOID_METHOD_BINDING(TYPE, NAME, FUNCTION, CS, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_D_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TYPE, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_CD_METHOD(TYPE, RETURN, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD(TYPE, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_D_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_CD_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(TYPE, RETURN, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DECLARE_STRUCT_VOID_CD_METHOD_WITH_DEFAULTS(TYPE, NAME, NPARAMS, ...) \
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD_WITH_DEFAU(TYPE, NAME, p0->NAME, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
|
||||
#define mSCRIPT_DEFINE_DEFAULTS_END }
|
||||
|
||||
#define _mSCRIPT_DEFINE_STRUCT_BINDING(INIT_TYPE, TYPE, EXPORTED_NAME, NAME) { \
|
||||
.type = mSCRIPT_CLASS_INIT_ ## INIT_TYPE, \
|
||||
.info = { \
|
||||
.member = { \
|
||||
.name = #EXPORTED_NAME, \
|
||||
.type = &_mSTStructBindingType_ ## TYPE ## _ ## NAME \
|
||||
} \
|
||||
}, \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, EXPORTED_NAME, NAME) \
|
||||
_mSCRIPT_DEFINE_STRUCT_BINDING(INSTANCE_MEMBER, TYPE, EXPORTED_NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_METHOD(TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(TYPE, NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_INIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, _init)
|
||||
#define mSCRIPT_DEFINE_STRUCT_INIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(INIT, TYPE, _init, NAME)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(TYPE, CAST_TYPE, MEMBER) { \
|
||||
.type = mSCRIPT_CLASS_INIT_CAST_TO_MEMBER, \
|
||||
.info = { \
|
||||
.castMember = { \
|
||||
.type = mSCRIPT_TYPE_MS_ ## CAST_TYPE, \
|
||||
.member = #MEMBER \
|
||||
} \
|
||||
}, \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }
|
||||
|
||||
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
||||
static struct mScriptFunction _function_ ## NAME = { \
|
||||
.call = _binding_ ## NAME \
|
||||
}; \
|
||||
static void _alloc_ ## NAME(struct mScriptValue* val) { \
|
||||
val->value.copaque = &_function_ ## NAME; \
|
||||
} \
|
||||
static const struct mScriptType _type_ ## NAME = { \
|
||||
.base = mSCRIPT_TYPE_FUNCTION, \
|
||||
.name = "function::" #NAME, \
|
||||
.alloc = _alloc_ ## NAME, \
|
||||
.details = { \
|
||||
.function = { \
|
||||
.parameters = { \
|
||||
.count = NPARAMS, \
|
||||
.entries = { _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
.names = { _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
}, \
|
||||
.returnType = { \
|
||||
.count = NRET, \
|
||||
.entries = { RETURN } \
|
||||
}, \
|
||||
}, \
|
||||
} \
|
||||
}; \
|
||||
const struct mScriptValue NAME = { \
|
||||
.type = &_type_ ## NAME, \
|
||||
.refs = mSCRIPT_VALUE_UNREF, \
|
||||
.value = { \
|
||||
.copaque = &_function_ ## NAME \
|
||||
} \
|
||||
}
|
||||
|
||||
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
return false; \
|
||||
} \
|
||||
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
||||
return true; \
|
||||
} \
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
return false; \
|
||||
} \
|
||||
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
||||
return true; \
|
||||
} \
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 0, , NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_MAKE(TYPE, VALUE) (struct mScriptValue) { \
|
||||
.type = (mSCRIPT_TYPE_MS_ ## TYPE), \
|
||||
.refs = mSCRIPT_VALUE_UNREF, \
|
||||
.value = { \
|
||||
.mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \
|
||||
}, \
|
||||
} \
|
||||
|
||||
#define mSCRIPT_VAL(TYPE, VALUE) { \
|
||||
.type = (mSCRIPT_TYPE_MS_ ## TYPE), \
|
||||
.refs = mSCRIPT_VALUE_UNREF, \
|
||||
.value = { \
|
||||
.mSCRIPT_TYPE_FIELD_ ## TYPE = (VALUE) \
|
||||
}, \
|
||||
} \
|
||||
|
||||
#define mSCRIPT_MAKE_S8(VALUE) mSCRIPT_MAKE(S8, VALUE)
|
||||
#define mSCRIPT_MAKE_U8(VALUE) mSCRIPT_MAKE(U8, VALUE)
|
||||
#define mSCRIPT_MAKE_S16(VALUE) mSCRIPT_MAKE(S16, VALUE)
|
||||
#define mSCRIPT_MAKE_U16(VALUE) mSCRIPT_MAKE(U16, VALUE)
|
||||
#define mSCRIPT_MAKE_S32(VALUE) mSCRIPT_MAKE(S32, VALUE)
|
||||
#define mSCRIPT_MAKE_U32(VALUE) mSCRIPT_MAKE(U32, VALUE)
|
||||
#define mSCRIPT_MAKE_F32(VALUE) mSCRIPT_MAKE(F32, VALUE)
|
||||
#define mSCRIPT_MAKE_S64(VALUE) mSCRIPT_MAKE(S64, VALUE)
|
||||
#define mSCRIPT_MAKE_U64(VALUE) mSCRIPT_MAKE(U64, VALUE)
|
||||
#define mSCRIPT_MAKE_F64(VALUE) mSCRIPT_MAKE(F64, VALUE)
|
||||
#define mSCRIPT_MAKE_CHARP(VALUE) mSCRIPT_MAKE(CHARP, VALUE)
|
||||
#define mSCRIPT_MAKE_S(STRUCT, VALUE) mSCRIPT_MAKE(S(STRUCT), VALUE)
|
||||
#define mSCRIPT_MAKE_CS(STRUCT, VALUE) mSCRIPT_MAKE(CS(STRUCT), VALUE)
|
||||
|
||||
#define mSCRIPT_S8(VALUE) mSCRIPT_VAL(S8, VALUE)
|
||||
#define mSCRIPT_U8(VALUE) mSCRIPT_VAL(U8, VALUE)
|
||||
#define mSCRIPT_S16(VALUE) mSCRIPT_VAL(S16, VALUE)
|
||||
#define mSCRIPT_U16(VALUE) mSCRIPT_VAL(U16, VALUE)
|
||||
#define mSCRIPT_S32(VALUE) mSCRIPT_VAL(S32, VALUE)
|
||||
#define mSCRIPT_U32(VALUE) mSCRIPT_VAL(U32, VALUE)
|
||||
#define mSCRIPT_F32(VALUE) mSCRIPT_VAL(F32, VALUE)
|
||||
#define mSCRIPT_S64(VALUE) mSCRIPT_VAL(S64, VALUE)
|
||||
#define mSCRIPT_U64(VALUE) mSCRIPT_VAL(U64, VALUE)
|
||||
#define mSCRIPT_F64(VALUE) mSCRIPT_VAL(F64, VALUE)
|
||||
#define mSCRIPT_CHARP(VALUE) mSCRIPT_VAL(CHARP, VALUE)
|
||||
#define mSCRIPT_S(STRUCT, VALUE) mSCRIPT_VAL(S(STRUCT), VALUE)
|
||||
#define mSCRIPT_CS(STRUCT, VALUE) mSCRIPT_VAL(CS(STRUCT), VALUE)
|
||||
|
||||
#define mSCRIPT_NO_DEFAULT { \
|
||||
.type = NULL, \
|
||||
.refs = mSCRIPT_VALUE_UNREF, \
|
||||
.value = {0} \
|
||||
}
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,331 @@
|
|||
/* Copyright (c) 2013-2021 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 M_SCRIPT_TYPES_H
|
||||
#define M_SCRIPT_TYPES_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/macros.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
#define mSCRIPT_VALUE_UNREF -1
|
||||
#define mSCRIPT_PARAMS_MAX 8
|
||||
|
||||
#define mSCRIPT_TYPE_C_S8 int8_t
|
||||
#define mSCRIPT_TYPE_C_U8 uint8_t
|
||||
#define mSCRIPT_TYPE_C_S16 int16_t
|
||||
#define mSCRIPT_TYPE_C_U16 uint16_t
|
||||
#define mSCRIPT_TYPE_C_S32 int32_t
|
||||
#define mSCRIPT_TYPE_C_U32 uint32_t
|
||||
#define mSCRIPT_TYPE_C_F32 float
|
||||
#define mSCRIPT_TYPE_C_S64 int64_t
|
||||
#define mSCRIPT_TYPE_C_U64 uint64_t
|
||||
#define mSCRIPT_TYPE_C_F64 double
|
||||
#define mSCRIPT_TYPE_C_STR struct mScriptString*
|
||||
#define mSCRIPT_TYPE_C_CHARP const char*
|
||||
#define mSCRIPT_TYPE_C_PTR void*
|
||||
#define mSCRIPT_TYPE_C_CPTR const void*
|
||||
#define mSCRIPT_TYPE_C_LIST struct mScriptList*
|
||||
#define mSCRIPT_TYPE_C_TABLE Table*
|
||||
#define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_WEAKREF uint32_t
|
||||
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME
|
||||
#define mSCRIPT_TYPE_C_PS(X) void
|
||||
#define mSCRIPT_TYPE_C_PCS(X) void
|
||||
#define mSCRIPT_TYPE_C_WSTR struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_W(X) struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_CW(X) const struct mScriptValue*
|
||||
|
||||
#define mSCRIPT_TYPE_FIELD_S8 s32
|
||||
#define mSCRIPT_TYPE_FIELD_U8 s32
|
||||
#define mSCRIPT_TYPE_FIELD_S16 s32
|
||||
#define mSCRIPT_TYPE_FIELD_U16 s32
|
||||
#define mSCRIPT_TYPE_FIELD_S32 s32
|
||||
#define mSCRIPT_TYPE_FIELD_U32 u32
|
||||
#define mSCRIPT_TYPE_FIELD_F32 f32
|
||||
#define mSCRIPT_TYPE_FIELD_S64 s64
|
||||
#define mSCRIPT_TYPE_FIELD_U64 u64
|
||||
#define mSCRIPT_TYPE_FIELD_F64 f64
|
||||
#define mSCRIPT_TYPE_FIELD_STR string
|
||||
#define mSCRIPT_TYPE_FIELD_CHARP copaque
|
||||
#define mSCRIPT_TYPE_FIELD_PTR opaque
|
||||
#define mSCRIPT_TYPE_FIELD_LIST list
|
||||
#define mSCRIPT_TYPE_FIELD_TABLE table
|
||||
#define mSCRIPT_TYPE_FIELD_WRAPPER opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WEAKREF u32
|
||||
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_WSTR opaque
|
||||
#define mSCRIPT_TYPE_FIELD_W(TYPE) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque
|
||||
|
||||
#define mSCRIPT_TYPE_MS_S8 (&mSTSInt8)
|
||||
#define mSCRIPT_TYPE_MS_U8 (&mSTUInt8)
|
||||
#define mSCRIPT_TYPE_MS_S16 (&mSTSInt16)
|
||||
#define mSCRIPT_TYPE_MS_U16 (&mSTUInt16)
|
||||
#define mSCRIPT_TYPE_MS_S32 (&mSTSInt32)
|
||||
#define mSCRIPT_TYPE_MS_U32 (&mSTUInt32)
|
||||
#define mSCRIPT_TYPE_MS_F32 (&mSTFloat32)
|
||||
#define mSCRIPT_TYPE_MS_S64 (&mSTSInt64)
|
||||
#define mSCRIPT_TYPE_MS_U64 (&mSTUInt64)
|
||||
#define mSCRIPT_TYPE_MS_F64 (&mSTFloat64)
|
||||
#define mSCRIPT_TYPE_MS_STR (&mSTString)
|
||||
#define mSCRIPT_TYPE_MS_CHARP (&mSTCharPtr)
|
||||
#define mSCRIPT_TYPE_MS_LIST (&mSTList)
|
||||
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
|
||||
#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper)
|
||||
#define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref)
|
||||
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
|
||||
#define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper)
|
||||
#define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE)
|
||||
#define mSCRIPT_TYPE_MS_CW(TYPE) (&mSTWrapperConst_ ## TYPE)
|
||||
|
||||
#define mSCRIPT_TYPE_CMP_GENERIC(TYPE0, TYPE1) (TYPE0 == TYPE1)
|
||||
#define mSCRIPT_TYPE_CMP_U8(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U8, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_S8(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S8, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_U16(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U16, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_S16(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S16, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_U32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U32, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_S32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S32, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_F32(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F32, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_U64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_U64, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_S64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_S64, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_F64(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_F64, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
|
||||
#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true)
|
||||
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)
|
||||
#define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) || mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE))
|
||||
|
||||
enum mScriptTypeBase {
|
||||
mSCRIPT_TYPE_VOID = 0,
|
||||
mSCRIPT_TYPE_SINT,
|
||||
mSCRIPT_TYPE_UINT,
|
||||
mSCRIPT_TYPE_FLOAT,
|
||||
mSCRIPT_TYPE_STRING,
|
||||
mSCRIPT_TYPE_FUNCTION,
|
||||
mSCRIPT_TYPE_OPAQUE,
|
||||
mSCRIPT_TYPE_OBJECT,
|
||||
mSCRIPT_TYPE_LIST,
|
||||
mSCRIPT_TYPE_TABLE,
|
||||
mSCRIPT_TYPE_WRAPPER,
|
||||
mSCRIPT_TYPE_WEAKREF,
|
||||
};
|
||||
|
||||
enum mScriptClassInitType {
|
||||
mSCRIPT_CLASS_INIT_END = 0,
|
||||
mSCRIPT_CLASS_INIT_CLASS_DOCSTRING,
|
||||
mSCRIPT_CLASS_INIT_DOCSTRING,
|
||||
mSCRIPT_CLASS_INIT_INSTANCE_MEMBER,
|
||||
mSCRIPT_CLASS_INIT_INHERIT,
|
||||
mSCRIPT_CLASS_INIT_CAST_TO_MEMBER,
|
||||
mSCRIPT_CLASS_INIT_INIT,
|
||||
mSCRIPT_CLASS_INIT_DEINIT,
|
||||
mSCRIPT_CLASS_INIT_GET,
|
||||
mSCRIPT_CLASS_INIT_SET,
|
||||
};
|
||||
|
||||
enum {
|
||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1
|
||||
};
|
||||
|
||||
struct mScriptType;
|
||||
extern const struct mScriptType mSTVoid;
|
||||
extern const struct mScriptType mSTSInt8;
|
||||
extern const struct mScriptType mSTUInt8;
|
||||
extern const struct mScriptType mSTSInt16;
|
||||
extern const struct mScriptType mSTUInt16;
|
||||
extern const struct mScriptType mSTSInt32;
|
||||
extern const struct mScriptType mSTUInt32;
|
||||
extern const struct mScriptType mSTFloat32;
|
||||
extern const struct mScriptType mSTSInt64;
|
||||
extern const struct mScriptType mSTUInt64;
|
||||
extern const struct mScriptType mSTFloat64;
|
||||
extern const struct mScriptType mSTString;
|
||||
extern const struct mScriptType mSTCharPtr;
|
||||
extern const struct mScriptType mSTList;
|
||||
extern const struct mScriptType mSTTable;
|
||||
extern const struct mScriptType mSTWrapper;
|
||||
extern const struct mScriptType mSTWeakref;
|
||||
extern const struct mScriptType mSTStringWrapper;
|
||||
|
||||
struct mScriptType;
|
||||
struct mScriptValue {
|
||||
const struct mScriptType* type;
|
||||
int refs;
|
||||
uint32_t flags;
|
||||
union {
|
||||
int32_t s32;
|
||||
uint32_t u32;
|
||||
float f32;
|
||||
int64_t s64;
|
||||
uint64_t u64;
|
||||
double f64;
|
||||
void* opaque;
|
||||
const void* copaque;
|
||||
struct mScriptString* string;
|
||||
struct Table* table;
|
||||
struct mScriptList* list;
|
||||
} value;
|
||||
};
|
||||
|
||||
struct mScriptTypeTuple {
|
||||
size_t count;
|
||||
const struct mScriptType* entries[mSCRIPT_PARAMS_MAX];
|
||||
const char* names[mSCRIPT_PARAMS_MAX];
|
||||
const struct mScriptValue* defaults;
|
||||
bool variable;
|
||||
};
|
||||
|
||||
struct mScriptTypeFunction {
|
||||
struct mScriptTypeTuple parameters;
|
||||
struct mScriptTypeTuple returnType;
|
||||
// TODO: kwargs
|
||||
};
|
||||
|
||||
struct mScriptClassMember {
|
||||
const char* name;
|
||||
const char* docstring;
|
||||
const struct mScriptType* type;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
struct mScriptClassCastMember {
|
||||
const struct mScriptType* type;
|
||||
const char* member;
|
||||
};
|
||||
|
||||
struct mScriptClassInitDetails {
|
||||
enum mScriptClassInitType type;
|
||||
union {
|
||||
const char* comment;
|
||||
const struct mScriptType* parent;
|
||||
struct mScriptClassMember member;
|
||||
struct mScriptClassCastMember castMember;
|
||||
} info;
|
||||
};
|
||||
|
||||
struct mScriptTypeClass {
|
||||
bool init;
|
||||
const struct mScriptClassInitDetails* details;
|
||||
const struct mScriptType* parent;
|
||||
const char* docstring;
|
||||
struct Table instanceMembers;
|
||||
struct Table castToMembers;
|
||||
struct mScriptClassMember* alloc; // TODO
|
||||
struct mScriptClassMember* free;
|
||||
struct mScriptClassMember* get;
|
||||
struct mScriptClassMember* set; // TODO
|
||||
};
|
||||
|
||||
struct mScriptType {
|
||||
enum mScriptTypeBase base : 8;
|
||||
bool isConst;
|
||||
|
||||
size_t size;
|
||||
const char* name;
|
||||
union {
|
||||
struct mScriptTypeTuple tuple;
|
||||
struct mScriptTypeFunction function;
|
||||
struct mScriptTypeClass* cls;
|
||||
const struct mScriptType* type;
|
||||
void* opaque;
|
||||
} details;
|
||||
const struct mScriptType* constType;
|
||||
void (*alloc)(struct mScriptValue*);
|
||||
void (*free)(struct mScriptValue*);
|
||||
uint32_t (*hash)(const struct mScriptValue*);
|
||||
bool (*equal)(const struct mScriptValue*, const struct mScriptValue*);
|
||||
bool (*cast)(const struct mScriptValue*, const struct mScriptType*, struct mScriptValue*);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mScriptList, struct mScriptValue)
|
||||
|
||||
struct mScriptString {
|
||||
size_t length; // Size of the string in code points
|
||||
size_t size; // Size of the buffer in bytes, excluding NULL byte terminator
|
||||
char* buffer; // UTF-8 representation of the string
|
||||
};
|
||||
|
||||
struct mScriptFrame {
|
||||
struct mScriptList arguments;
|
||||
struct mScriptList returnValues;
|
||||
// TODO: Exception/failure codes
|
||||
};
|
||||
|
||||
struct mScriptFunction {
|
||||
bool (*call)(struct mScriptFrame*, void* context);
|
||||
void* context;
|
||||
};
|
||||
|
||||
struct mScriptValue* mScriptValueAlloc(const struct mScriptType* type);
|
||||
void mScriptValueRef(struct mScriptValue* val);
|
||||
void mScriptValueDeref(struct mScriptValue* val);
|
||||
|
||||
void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out);
|
||||
struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val);
|
||||
const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val);
|
||||
|
||||
struct mScriptValue* mScriptStringCreateEmpty(size_t size);
|
||||
struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size);
|
||||
struct mScriptValue* mScriptStringCreateFromUTF8(const char* string);
|
||||
struct mScriptValue* mScriptStringCreateFromASCII(const char* string);
|
||||
struct mScriptValue* mScriptValueCreateFromUInt(uint32_t value);
|
||||
struct mScriptValue* mScriptValueCreateFromSInt(int32_t value);
|
||||
|
||||
bool mScriptTableInsert(struct mScriptValue* table, struct mScriptValue* key, struct mScriptValue* value);
|
||||
bool mScriptTableRemove(struct mScriptValue* table, struct mScriptValue* key);
|
||||
struct mScriptValue* mScriptTableLookup(struct mScriptValue* table, struct mScriptValue* key);
|
||||
bool mScriptTableClear(struct mScriptValue* table);
|
||||
size_t mScriptTableSize(struct mScriptValue* table);
|
||||
bool mScriptTableIteratorStart(struct mScriptValue* table, struct TableIterator*);
|
||||
bool mScriptTableIteratorNext(struct mScriptValue* table, struct TableIterator*);
|
||||
struct mScriptValue* mScriptTableIteratorGetKey(struct mScriptValue* table, struct TableIterator*);
|
||||
struct mScriptValue* mScriptTableIteratorGetValue(struct mScriptValue* table, struct TableIterator*);
|
||||
bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator*, struct mScriptValue* key);
|
||||
|
||||
void mScriptFrameInit(struct mScriptFrame* frame);
|
||||
void mScriptFrameDeinit(struct mScriptFrame* frame);
|
||||
|
||||
void mScriptClassInit(struct mScriptTypeClass* cls);
|
||||
void mScriptClassDeinit(struct mScriptTypeClass* cls);
|
||||
|
||||
bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScriptValue*);
|
||||
bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, struct mScriptValue*);
|
||||
bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*);
|
||||
bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) ;
|
||||
void mScriptObjectFree(struct mScriptValue* obj);
|
||||
|
||||
bool mScriptPopS32(struct mScriptList* list, int32_t* out);
|
||||
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
|
||||
bool mScriptPopF32(struct mScriptList* list, float* out);
|
||||
bool mScriptPopS64(struct mScriptList* list, int64_t* out);
|
||||
bool mScriptPopU64(struct mScriptList* list, uint64_t* out);
|
||||
bool mScriptPopF64(struct mScriptList* list, double* out);
|
||||
bool mScriptPopPointer(struct mScriptList* list, void** out);
|
||||
|
||||
bool mScriptCast(const struct mScriptType* type, const struct mScriptValue* input, struct mScriptValue* output);
|
||||
bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList* frame);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,515 @@
|
|||
local Game = {
|
||||
new = function (self, game)
|
||||
self.__index = self
|
||||
setmetatable(game, self)
|
||||
return game
|
||||
end
|
||||
}
|
||||
|
||||
function Game.getParty(game)
|
||||
local party = {}
|
||||
local monStart = game._party
|
||||
local nameStart = game._partyNames
|
||||
local otStart = game._partyOt
|
||||
for i = 1, emu:read8(game._partyCount) do
|
||||
party[i] = game:_readPartyMon(monStart, nameStart, otStart)
|
||||
monStart = monStart + game._partyMonSize
|
||||
if game._partyNames then
|
||||
nameStart = nameStart + game._monNameLength + 1
|
||||
end
|
||||
if game._partyOt then
|
||||
otStart = otStart + game._playerNameLength + 1
|
||||
end
|
||||
end
|
||||
return party
|
||||
end
|
||||
|
||||
function Game.toString(game, rawstring)
|
||||
local string = ""
|
||||
for _, char in ipairs({rawstring:byte(1, #rawstring)}) do
|
||||
if char == game._terminator then
|
||||
break
|
||||
end
|
||||
string = string..game._charmap[char]
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
function Game.getSpeciesName(game, id)
|
||||
if game._speciesIndex then
|
||||
local index = game._index
|
||||
if not index then
|
||||
index = {}
|
||||
for i = 0, 255 do
|
||||
index[emu.memory.cart0:read8(game._speciesIndex + i)] = i
|
||||
end
|
||||
game._index = index
|
||||
end
|
||||
id = index[id]
|
||||
end
|
||||
local pointer = game._speciesNameTable + (game._speciesNameLength) * id
|
||||
return game:toString(emu.memory.cart0:readRange(pointer, game._monNameLength))
|
||||
end
|
||||
|
||||
local GBGameEn = Game:new{
|
||||
_terminator=0x50,
|
||||
_monNameLength=10,
|
||||
_speciesNameLength=10,
|
||||
_playerNameLength=10,
|
||||
}
|
||||
|
||||
local GBAGameEn = Game:new{
|
||||
_terminator=0xFF,
|
||||
_monNameLength=10,
|
||||
_speciesNameLength=11,
|
||||
_playerNameLength=10,
|
||||
}
|
||||
|
||||
local Generation1En = GBGameEn:new{
|
||||
_boxMonSize=33,
|
||||
_partyMonSize=44,
|
||||
_readBoxMon=readBoxMonGen1,
|
||||
_readPartyMon=readPartyMonGen1,
|
||||
}
|
||||
|
||||
local Generation2En = GBGameEn:new{
|
||||
_boxMonSize=32,
|
||||
_partyMonSize=48,
|
||||
_readBoxMon=readBoxMonGen2,
|
||||
_readPartyMon=readPartyMonGen2,
|
||||
}
|
||||
|
||||
local Generation3En = GBAGameEn:new{
|
||||
_boxMonSize=80,
|
||||
_partyMonSize=100,
|
||||
_readBoxMon=readBoxMonGen3,
|
||||
_readPartyMon=readPartyMonGen3,
|
||||
}
|
||||
|
||||
GBGameEn._charmap = { [0]=
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", " ",
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "(", ")", ":", ";", "[", "]",
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
|
||||
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "é", "ʼd", "ʼl", "ʼs", "ʼt", "ʼv",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>",
|
||||
"'", "P\u{200d}k", "M\u{200d}n", "-", "ʼr", "ʼm", "?", "!", ".", "ァ", "ゥ", "ェ", "▹", "▸", "▾", "♂",
|
||||
"$", "×", ".", "/", ",", "♀", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
|
||||
}
|
||||
|
||||
GBAGameEn._charmap = { [0]=
|
||||
" ", "À", "Á", "Â", "Ç", "È", "É", "Ê", "Ë", "Ì", "こ", "Î", "Ï", "Ò", "Ó", "Ô",
|
||||
"Œ", "Ù", "Ú", "Û", "Ñ", "ß", "à", "á", "ね", "ç", "è", "é", "ê", "ë", "ì", "ま",
|
||||
"î", "ï", "ò", "ó", "ô", "œ", "ù", "ú", "û", "ñ", "º", "ª", "<EFBFBD>", "&", "+", "あ",
|
||||
"ぃ", "ぅ", "ぇ", "ぉ", "v", "=", "ょ", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ",
|
||||
"ぞ", "だ", "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",
|
||||
"っ", "¿", "¡", "P\u{200d}k", "M\u{200d}n", "P\u{200d}o", "K\u{200d}é", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "Í", "%", "(", ")", "セ", "ソ",
|
||||
"タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "â", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "í",
|
||||
"ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "⬆", "⬇", "⬅", "➡", "ヲ", "ン", "ァ",
|
||||
"ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ",
|
||||
"ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ",
|
||||
"ッ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "!", "?", ".", "-", "・",
|
||||
"…", "“", "”", "‘", "’", "♂", "♀", "$", ",", "×", "/", "A", "B", "C", "D", "E",
|
||||
"F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
|
||||
"V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
|
||||
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "▶",
|
||||
":", "Ä", "Ö", "Ü", "ä", "ö", "ü", "⬆", "⬇", "⬅", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", ""
|
||||
}
|
||||
|
||||
function _read16BE(emu, address)
|
||||
return (emu:read8(address) << 8) | emu:read8(address + 1)
|
||||
end
|
||||
|
||||
function Generation1En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
local mon = {}
|
||||
mon.species = emu.memory.cart0:read8(game._speciesIndex + emu:read8(address + 0) - 1)
|
||||
mon.hp = _read16BE(emu, address + 1)
|
||||
mon.level = emu:read8(address + 3)
|
||||
mon.status = emu:read8(address + 4)
|
||||
mon.type1 = emu:read8(address + 5)
|
||||
mon.type2 = emu:read8(address + 6)
|
||||
mon.catchRate = emu:read8(address + 7)
|
||||
mon.moves = {
|
||||
emu:read8(address + 8),
|
||||
emu:read8(address + 9),
|
||||
emu:read8(address + 10),
|
||||
emu:read8(address + 11),
|
||||
}
|
||||
mon.otId = _read16BE(emu, address + 12)
|
||||
mon.experience = (_read16BE(emu, address + 14) << 8)| emu:read8(address + 16)
|
||||
mon.hpEV = _read16BE(emu, address + 17)
|
||||
mon.attackEV = _read16BE(emu, address + 19)
|
||||
mon.defenseEV = _read16BE(emu, address + 21)
|
||||
mon.speedEV = _read16BE(emu, address + 23)
|
||||
mon.spAttackEV = _read16BE(emu, address + 25)
|
||||
mon.spDefenseEV = mon.spAttackEv
|
||||
local iv = _read16BE(emu, address + 27)
|
||||
mon.attackIV = (iv >> 4) & 0xF
|
||||
mon.defenseIV = iv & 0xF
|
||||
mon.speedIV = (iv >> 12) & 0xF
|
||||
mon.spAttackIV = (iv >> 8) & 0xF
|
||||
mon.spDefenseIV = mon.spAttackIV
|
||||
mon.pp = {
|
||||
emu:read8(address + 28),
|
||||
emu:read8(address + 29),
|
||||
emu:read8(address + 30),
|
||||
emu:read8(address + 31),
|
||||
}
|
||||
mon.nickname = game:toString(emu:readRange(nameAddress, game._monNameLength))
|
||||
mon.otName = game:toString(emu:readRange(otAddress, game._playerNameLength))
|
||||
return mon
|
||||
end
|
||||
|
||||
function Generation1En._readPartyMon(game, address, nameAddress, otAddress)
|
||||
local mon = game:_readBoxMon(address, nameAddress, otAddress)
|
||||
mon.level = emu:read8(address + 33)
|
||||
mon.maxHP = _read16BE(emu, address + 34)
|
||||
mon.attack = _read16BE(emu, address + 36)
|
||||
mon.defense = _read16BE(emu, address + 38)
|
||||
mon.speed = _read16BE(emu, address + 40)
|
||||
mon.spAttack = _read16BE(emu, address + 42)
|
||||
mon.spDefense = mon.spAttack
|
||||
return mon
|
||||
end
|
||||
|
||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
local mon = {}
|
||||
mon.species = emu:read8(address + 0)
|
||||
mon.item = emu:read8(address + 1)
|
||||
mon.moves = {
|
||||
emu:read8(address + 2),
|
||||
emu:read8(address + 3),
|
||||
emu:read8(address + 4),
|
||||
emu:read8(address + 5),
|
||||
}
|
||||
mon.otId = _read16BE(emu, address + 6)
|
||||
mon.experience = (_read16BE(emu, address + 8) << 8)| emu:read8(address + 10)
|
||||
mon.hpEV = _read16BE(emu, address + 11)
|
||||
mon.attackEV = _read16BE(emu, address + 13)
|
||||
mon.defenseEV = _read16BE(emu, address + 15)
|
||||
mon.speedEV = _read16BE(emu, address + 17)
|
||||
mon.spAttackEV = _read16BE(emu, address + 19)
|
||||
mon.spDefenseEV = mon.spAttackEv
|
||||
local iv = _read16BE(emu, address + 21)
|
||||
mon.attackIV = (iv >> 4) & 0xF
|
||||
mon.defenseIV = iv & 0xF
|
||||
mon.speedIV = (iv >> 12) & 0xF
|
||||
mon.spAttackIV = (iv >> 8) & 0xF
|
||||
mon.spDefenseIV = mon.spAttackIV
|
||||
mon.pp = {
|
||||
emu:read8(address + 23),
|
||||
emu:read8(address + 24),
|
||||
emu:read8(address + 25),
|
||||
emu:read8(address + 26),
|
||||
}
|
||||
mon.friendship = emu:read8(address + 27)
|
||||
mon.pokerus = emu:read8(address + 28)
|
||||
local caughtData = _read16BE(emu, address + 29)
|
||||
mon.metLocation = (caughtData >> 8) & 0x7F
|
||||
mon.metLevel = caughtData & 0x1F
|
||||
mon.level = emu:read8(address + 31)
|
||||
mon.nickname = game:toString(emu:readRange(nameAddress, game._monNameLength))
|
||||
mon.otName = game:toString(emu:readRange(otAddress, game._playerNameLength))
|
||||
return mon
|
||||
end
|
||||
|
||||
function Generation2En._readPartyMon(game, address, nameAddress, otAddress)
|
||||
local mon = game:_readBoxMon(address, nameAddress, otAddress)
|
||||
mon.status = emu:read8(address + 32)
|
||||
mon.hp = _read16BE(emu, address + 34)
|
||||
mon.maxHP = _read16BE(emu, address + 36)
|
||||
mon.attack = _read16BE(emu, address + 38)
|
||||
mon.defense = _read16BE(emu, address + 40)
|
||||
mon.speed = _read16BE(emu, address + 42)
|
||||
mon.spAttack = _read16BE(emu, address + 44)
|
||||
mon.spDefense = _read16BE(emu, address + 46)
|
||||
return mon
|
||||
end
|
||||
|
||||
function Generation3En._readBoxMon(game, address)
|
||||
local mon = {}
|
||||
mon.personality = emu:read32(address + 0)
|
||||
mon.otId = emu:read32(address + 4)
|
||||
mon.nickname = game:toString(emu:readRange(address + 8, game._monNameLength))
|
||||
mon.language = emu:read8(address + 18)
|
||||
local flags = emu:read8(address + 19)
|
||||
mon.isBadEgg = flags & 1
|
||||
mon.hasSpecies = (flags >> 1) & 1
|
||||
mon.isEgg = (flags >> 2) & 1
|
||||
mon.otName = game:toString(emu:readRange(address + 20, game._playerNameLength))
|
||||
mon.markings = emu:read8(address + 27)
|
||||
|
||||
local key = mon.otId ~ mon.personality
|
||||
local substructSelector = {
|
||||
[ 0] = {0, 1, 2, 3},
|
||||
[ 1] = {0, 1, 3, 2},
|
||||
[ 2] = {0, 2, 1, 3},
|
||||
[ 3] = {0, 3, 1, 2},
|
||||
[ 4] = {0, 2, 3, 1},
|
||||
[ 5] = {0, 3, 2, 1},
|
||||
[ 6] = {1, 0, 2, 3},
|
||||
[ 7] = {1, 0, 3, 2},
|
||||
[ 8] = {2, 0, 1, 3},
|
||||
[ 9] = {3, 0, 1, 2},
|
||||
[10] = {2, 0, 3, 1},
|
||||
[11] = {3, 0, 2, 1},
|
||||
[12] = {1, 2, 0, 3},
|
||||
[13] = {1, 3, 0, 2},
|
||||
[14] = {2, 1, 0, 3},
|
||||
[15] = {3, 1, 0, 2},
|
||||
[16] = {2, 3, 0, 1},
|
||||
[17] = {3, 2, 0, 1},
|
||||
[18] = {1, 2, 3, 0},
|
||||
[19] = {1, 3, 2, 0},
|
||||
[20] = {2, 1, 3, 0},
|
||||
[21] = {3, 1, 2, 0},
|
||||
[22] = {2, 3, 1, 0},
|
||||
[23] = {3, 2, 1, 0},
|
||||
}
|
||||
|
||||
local pSel = substructSelector[mon.personality % 24]
|
||||
local ss0 = {}
|
||||
local ss1 = {}
|
||||
local ss2 = {}
|
||||
local ss3 = {}
|
||||
|
||||
for i = 0, 2 do
|
||||
ss0[i] = emu:read32(address + 32 + pSel[1] * 12 + i * 4) ~ key
|
||||
ss1[i] = emu:read32(address + 32 + pSel[2] * 12 + i * 4) ~ key
|
||||
ss2[i] = emu:read32(address + 32 + pSel[3] * 12 + i * 4) ~ key
|
||||
ss3[i] = emu:read32(address + 32 + pSel[4] * 12 + i * 4) ~ key
|
||||
end
|
||||
|
||||
mon.species = ss0[0] & 0xFFFF
|
||||
mon.heldItem = ss0[0] >> 16
|
||||
mon.experience = ss0[1]
|
||||
mon.ppBonuses = ss0[2] & 0xFF
|
||||
mon.friendship = (ss0[2] >> 8) & 0xFF
|
||||
|
||||
mon.moves = {
|
||||
ss1[0] & 0xFFFF,
|
||||
ss1[0] >> 16,
|
||||
ss1[1] & 0xFFFF,
|
||||
ss1[1] >> 16
|
||||
}
|
||||
mon.pp = {
|
||||
ss1[2] & 0xFF,
|
||||
(ss1[2] >> 8) & 0xFF,
|
||||
(ss1[2] >> 16) & 0xFF,
|
||||
ss1[2] >> 24
|
||||
}
|
||||
|
||||
mon.hpEV = ss2[0] & 0xFF
|
||||
mon.attackEV = (ss2[0] >> 8) & 0xFF
|
||||
mon.defenseEV = (ss2[0] >> 16) & 0xFF
|
||||
mon.speedEV = ss2[0] >> 24
|
||||
mon.spAttackEV = ss2[1] & 0xFF
|
||||
mon.spDefenseEV = (ss2[1] >> 8) & 0xFF
|
||||
mon.cool = (ss2[1] >> 16) & 0xFF
|
||||
mon.beauty = ss2[1] >> 24
|
||||
mon.cute = ss2[2] & 0xFF
|
||||
mon.smart = (ss2[2] >> 8) & 0xFF
|
||||
mon.tough = (ss2[2] >> 16) & 0xFF
|
||||
mon.sheen = ss2[2] >> 24
|
||||
|
||||
mon.pokerus = ss3[0] & 0xFF
|
||||
mon.metLocation = (ss3[0] >> 8) & 0xFF
|
||||
flags = ss3[0] >> 16
|
||||
mon.metLevel = flags & 0x7F
|
||||
mon.metGame = (flags >> 7) & 0xF
|
||||
mon.pokeball = (flags >> 11) & 0xF
|
||||
mon.otGender = (flags >> 15) & 0x1
|
||||
flags = ss3[1]
|
||||
mon.hpIV = flags & 0x1F
|
||||
mon.attackIV = (flags >> 5) & 0x1F
|
||||
mon.defenseIV = (flags >> 10) & 0x1F
|
||||
mon.speedIV = (flags >> 15) & 0x1F
|
||||
mon.spAttackIV = (flags >> 20) & 0x1F
|
||||
mon.spDefenseIV = (flags >> 25) & 0x1F
|
||||
-- Bit 30 is another "isEgg" bit
|
||||
mon.altAbility = (flags >> 31) & 1
|
||||
flags = ss3[2]
|
||||
mon.coolRibbon = flags & 7
|
||||
mon.beautyRibbon = (flags >> 3) & 7
|
||||
mon.cuteRibbon = (flags >> 6) & 7
|
||||
mon.smartRibbon = (flags >> 9) & 7
|
||||
mon.toughRibbon = (flags >> 12) & 7
|
||||
mon.championRibbon = (flags >> 15) & 1
|
||||
mon.winningRibbon = (flags >> 16) & 1
|
||||
mon.victoryRibbon = (flags >> 17) & 1
|
||||
mon.artistRibbon = (flags >> 18) & 1
|
||||
mon.effortRibbon = (flags >> 19) & 1
|
||||
mon.marineRibbon = (flags >> 20) & 1
|
||||
mon.landRibbon = (flags >> 21) & 1
|
||||
mon.skyRibbon = (flags >> 22) & 1
|
||||
mon.countryRibbon = (flags >> 23) & 1
|
||||
mon.nationalRibbon = (flags >> 24) & 1
|
||||
mon.earthRibbon = (flags >> 25) & 1
|
||||
mon.worldRibbon = (flags >> 26) & 1
|
||||
mon.eventLegal = (flags >> 27) & 0x1F
|
||||
return mon
|
||||
end
|
||||
|
||||
function Generation3En._readPartyMon(game, address)
|
||||
local mon = game:_readBoxMon(address)
|
||||
mon.status = emu:read32(address + 80)
|
||||
mon.level = emu:read8(address + 84)
|
||||
mon.mail = emu:read32(address + 85)
|
||||
mon.hp = emu:read16(address + 86)
|
||||
mon.maxHP = emu:read16(address + 88)
|
||||
mon.attack = emu:read16(address + 90)
|
||||
mon.defense = emu:read16(address + 92)
|
||||
mon.speed = emu:read16(address + 94)
|
||||
mon.spAttack = emu:read16(address + 96)
|
||||
mon.spDefense = emu:read16(address + 98)
|
||||
return mon
|
||||
end
|
||||
|
||||
local gameRBEn = Generation1En:new{
|
||||
name="Red/Blue (USA)",
|
||||
_party=0xd16b,
|
||||
_partyCount=0xd163,
|
||||
_partyNames=0xd2b5,
|
||||
_partyOt=0xd273,
|
||||
_speciesNameTable=0x1c21e,
|
||||
_speciesIndex=0x41024,
|
||||
}
|
||||
|
||||
local gameYellowEn = Generation1En:new{
|
||||
name="Yellow (USA)",
|
||||
_party=0xd16a,
|
||||
_partyCount=0xd162,
|
||||
_partyNames=0xd2b4,
|
||||
_partyOt=0xd272,
|
||||
_speciesNameTable=0xe8000,
|
||||
_speciesIndex=0x410b1,
|
||||
}
|
||||
|
||||
local gameGSEn = Generation2En:new{
|
||||
name="Gold/Silver (USA)",
|
||||
_party=0xda2a,
|
||||
_partyCount=0xda22,
|
||||
_partyNames=0xdb8c,
|
||||
_partyOt=0xdb4a,
|
||||
_speciesNameTable=0x1b0b6a,
|
||||
}
|
||||
|
||||
local gameCrystalEn = Generation2En:new{
|
||||
name="Crystal (USA)",
|
||||
_party=0xdcdf,
|
||||
_partyCount=0xdcd7,
|
||||
_partyNames=0xde41,
|
||||
_partyOt=0xddff,
|
||||
_speciesNameTable=0x5337a,
|
||||
}
|
||||
|
||||
local gameRubyEn = Generation3En:new{
|
||||
name="Ruby (USA)",
|
||||
_party=0x3004360,
|
||||
_partyCount=0x3004350,
|
||||
_speciesNameTable=0x1f716c,
|
||||
}
|
||||
|
||||
local gameSapphireEn = Generation3En:new{
|
||||
name="Sapphire (USA)",
|
||||
_party=0x3004360,
|
||||
_partyCount=0x3004350,
|
||||
_speciesNameTable=0x1f70fc,
|
||||
}
|
||||
|
||||
local gameEmeraldEn = Generation3En:new{
|
||||
name="Emerald (USA)",
|
||||
_party=0x20244ec,
|
||||
_partyCount=0x20244e9,
|
||||
_speciesNameTable=0x3185c8,
|
||||
}
|
||||
|
||||
local gameFireRedEn = Generation3En:new{
|
||||
name="FireRed (USA)",
|
||||
_party=0x2024284,
|
||||
_partyCount=0x2024029,
|
||||
_speciesNameTable=0x245ee0,
|
||||
}
|
||||
|
||||
local gameFireRedEnR1 = gameFireRedEn:new{
|
||||
name="FireRed (USA) (Rev 1)",
|
||||
_speciesNameTable=0x245f50,
|
||||
}
|
||||
|
||||
local gameLeafGreenEn = Generation3En:new{
|
||||
name="LeafGreen (USA)",
|
||||
_party=0x2024284,
|
||||
_partyCount=0x2024029,
|
||||
_speciesNameTable=0x245ebc,
|
||||
}
|
||||
|
||||
local gameLeafGreenEnR1 = gameLeafGreenEn:new{
|
||||
name="LeafGreen (USA)",
|
||||
_party=0x2024284,
|
||||
_partyCount=0x2024029,
|
||||
_speciesNameTable=0x245f2c,
|
||||
}
|
||||
|
||||
gameCodes = {
|
||||
["DMG-AAUE"]=gameGSEn, -- Gold
|
||||
["DMG-AAXE"]=gameGSEn, -- Silver
|
||||
["CGB-BYTE"]=gameCrystalEn,
|
||||
["AGB-AXVE"]=gameRubyEn,
|
||||
["AGB-AXPE"]=gameSapphireEn,
|
||||
["AGB-BPEE"]=gameEmeraldEn,
|
||||
["AGB-BPRE"]=gameFireRedEn,
|
||||
["AGB-BPGE"]=gameLeafGreenEn,
|
||||
}
|
||||
|
||||
-- These versions have slight differences and/or cannot be uniquely
|
||||
-- identified by their in-header game codes, so fall back on a CRC32
|
||||
gameCrc32 = {
|
||||
[0x9f7fdd53] = gameRBEn, -- Red
|
||||
[0xd6da8a1a] = gameRBEn, -- Blue
|
||||
[0x7d527d62] = gameYellowEn,
|
||||
[0x3358e30a] = gameCrystal, -- Crystal rev 1
|
||||
[0x84ee4776] = gameFireRedEnR1,
|
||||
[0xdaffecec] = gameLeafGreenEnR1,
|
||||
}
|
||||
|
||||
function printPartyStatus(game, buffer)
|
||||
buffer:clear()
|
||||
for _, mon in ipairs(game:getParty()) do
|
||||
buffer:print(string.format("%-10s (Lv%3i %10s): %3i/%3i\n",
|
||||
mon.nickname,
|
||||
mon.level,
|
||||
game:getSpeciesName(mon.species),
|
||||
mon.hp,
|
||||
mon.maxHP))
|
||||
end
|
||||
end
|
||||
|
||||
function detectGame()
|
||||
local checksum = 0
|
||||
for i, v in ipairs({emu:checksum(C.CHECKSUM.CRC32):byte(1, 4)}) do
|
||||
checksum = checksum * 256 + v
|
||||
end
|
||||
game = gameCrc32[checksum]
|
||||
if not game then
|
||||
game = gameCodes[emu:getGameCode()]
|
||||
end
|
||||
|
||||
if not game then
|
||||
console:error("Unknown game!")
|
||||
else
|
||||
console:log("Found game: " .. game.name)
|
||||
end
|
||||
end
|
||||
|
||||
callbacks:add("start", detectGame)
|
||||
if emu then
|
||||
detectGame()
|
||||
end
|
|
@ -14,7 +14,6 @@ set(SOURCE_FILES
|
|||
map-cache.c
|
||||
mem-search.c
|
||||
rewind.c
|
||||
scripting.c
|
||||
serialize.c
|
||||
sync.c
|
||||
thread.c
|
||||
|
@ -24,8 +23,18 @@ set(SOURCE_FILES
|
|||
set(TEST_FILES
|
||||
test/core.c)
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND SOURCE_FILES
|
||||
scripting.c)
|
||||
|
||||
if(USE_LUA)
|
||||
list(APPEND TEST_FILES
|
||||
test/scripting.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
source_group("mCore" FILES ${SOURCE_FILES})
|
||||
source_group("mCore tests" FILES ${TEST_FILES})
|
||||
|
||||
export_directory(CORE SOURCE_FILES)
|
||||
export_directory(CORE_TEST TEST_FILES)
|
||||
export_directory(CORE_TEST TEST_FILES)
|
||||
|
|
|
@ -308,6 +308,9 @@ struct VFile* mCoreGetState(struct mCore* core, int slot, bool write) {
|
|||
if (!core->dirs.state) {
|
||||
return NULL;
|
||||
}
|
||||
if (slot < 0) {
|
||||
return NULL;
|
||||
}
|
||||
char name[PATH_MAX + 14]; // Quash warning
|
||||
snprintf(name, sizeof(name), "%s.ss%i", core->dirs.baseName, slot);
|
||||
return core->dirs.state->openFile(core->dirs.state, name, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY);
|
||||
|
@ -321,10 +324,6 @@ void mCoreDeleteState(struct mCore* core, int slot) {
|
|||
|
||||
void mCoreTakeScreenshot(struct mCore* core) {
|
||||
#ifdef USE_PNG
|
||||
size_t stride;
|
||||
const void* pixels = 0;
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
struct VFile* vf;
|
||||
#ifndef PSP2
|
||||
vf = VDirFindNextAvailable(core->dirs.screenshot, core->dirs.baseName, "-", ".png", O_CREAT | O_TRUNC | O_WRONLY);
|
||||
|
@ -333,11 +332,7 @@ void mCoreTakeScreenshot(struct mCore* core) {
|
|||
#endif
|
||||
bool success = false;
|
||||
if (vf) {
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
success = PNGWritePixels(png, width, height, stride, pixels);
|
||||
PNGWriteClose(png, info);
|
||||
success = mCoreTakeScreenshotVF(core, vf);
|
||||
#ifdef PSP2
|
||||
void* data = vf->map(vf, 0, 0);
|
||||
PhotoExportParam param = {
|
||||
|
@ -362,6 +357,23 @@ void mCoreTakeScreenshot(struct mCore* core) {
|
|||
}
|
||||
#endif
|
||||
|
||||
bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
|
||||
#ifdef USE_PNG
|
||||
size_t stride;
|
||||
const void* pixels = 0;
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
bool success = PNGWritePixels(png, width, height, stride, pixels);
|
||||
PNGWriteClose(png, info);
|
||||
return success;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mCoreInitConfig(struct mCore* core, const char* port) {
|
||||
mCoreConfigInit(&core->config, port);
|
||||
}
|
||||
|
|
|
@ -80,6 +80,14 @@ void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
void mLogExplicit(struct mLogger* context, int category, enum mLogLevel level, const char* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (!context->filter || mLogFilterTest(context->filter, category, level)) {
|
||||
context->log(context, category, level, format, args);
|
||||
}
|
||||
}
|
||||
|
||||
void mLogFilterInit(struct mLogFilter* filter) {
|
||||
HashTableInit(&filter->categories, 8, NULL);
|
||||
TableInit(&filter->levels, 8, NULL);
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2022 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/scripting.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
mLOG_DEFINE_CATEGORY(SCRIPT, "Scripting", "script");
|
||||
|
||||
struct mScriptBridge {
|
||||
struct Table engines;
|
||||
struct mDebugger* debugger;
|
||||
|
@ -91,8 +96,16 @@ void mScriptBridgeInstallEngine(struct mScriptBridge* sb, struct mScriptEngine*
|
|||
|
||||
#ifdef USE_DEBUGGERS
|
||||
void mScriptBridgeSetDebugger(struct mScriptBridge* sb, struct mDebugger* debugger) {
|
||||
if (sb->debugger == debugger) {
|
||||
return;
|
||||
}
|
||||
if (sb->debugger) {
|
||||
sb->debugger->bridge = NULL;
|
||||
}
|
||||
sb->debugger = debugger;
|
||||
debugger->bridge = sb;
|
||||
if (debugger) {
|
||||
debugger->bridge = sb;
|
||||
}
|
||||
}
|
||||
|
||||
struct mDebugger* mScriptBridgeGetDebugger(struct mScriptBridge* sb) {
|
||||
|
@ -136,3 +149,636 @@ bool mScriptBridgeLookupSymbol(struct mScriptBridge* sb, const char* name, int32
|
|||
HashTableEnumerate(&sb->engines, _seLookupSymbol, &info);
|
||||
return info.success;
|
||||
}
|
||||
|
||||
struct mScriptMemoryDomain {
|
||||
struct mCore* core;
|
||||
struct mCoreMemoryBlock block;
|
||||
};
|
||||
|
||||
struct mScriptCoreAdapter {
|
||||
struct mCore* core;
|
||||
struct mScriptContext* context;
|
||||
struct mScriptValue memory;
|
||||
};
|
||||
|
||||
struct mScriptConsole {
|
||||
struct mLogger* logger;
|
||||
mScriptContextBufferFactory textBufferFactory;
|
||||
void* textBufferContext;
|
||||
};
|
||||
|
||||
#define CALCULATE_SEGMENT_INFO \
|
||||
uint32_t segmentSize = adapter->block.end - adapter->block.start; \
|
||||
uint32_t segmentStart = adapter->block.segmentStart - adapter->block.start; \
|
||||
if (adapter->block.segmentStart) { \
|
||||
segmentSize -= segmentStart; \
|
||||
}
|
||||
|
||||
#define CALCULATE_SEGMENT_ADDRESS \
|
||||
uint32_t segmentAddress = address % segmentSize; \
|
||||
int segment = address / segmentSize; \
|
||||
segmentAddress += adapter->block.start; \
|
||||
if (adapter->block.segmentStart && segment) { \
|
||||
segmentAddress += segmentStart; \
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainRead8(struct mScriptMemoryDomain* adapter, uint32_t address) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
return adapter->core->rawRead8(adapter->core, segmentAddress, segment);
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainRead16(struct mScriptMemoryDomain* adapter, uint32_t address) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
return adapter->core->rawRead16(adapter->core, segmentAddress, segment);
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainRead32(struct mScriptMemoryDomain* adapter, uint32_t address) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
return adapter->core->rawRead32(adapter->core, segmentAddress, segment);
|
||||
}
|
||||
|
||||
static struct mScriptValue* mScriptMemoryDomainReadRange(struct mScriptMemoryDomain* adapter, uint32_t address, uint32_t length) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
struct mScriptValue* value = mScriptStringCreateEmpty(length);
|
||||
char* buffer = value->value.string->buffer;
|
||||
uint32_t i;
|
||||
for (i = 0; i < length; ++i, ++address) {
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
buffer[i] = adapter->core->rawRead8(adapter->core, segmentAddress, segment);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void mScriptMemoryDomainWrite8(struct mScriptMemoryDomain* adapter, uint32_t address, uint8_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite8(adapter->core, address, segmentAddress, value);
|
||||
}
|
||||
|
||||
static void mScriptMemoryDomainWrite16(struct mScriptMemoryDomain* adapter, uint32_t address, uint16_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite16(adapter->core, address, segmentAddress, value);
|
||||
}
|
||||
|
||||
static void mScriptMemoryDomainWrite32(struct mScriptMemoryDomain* adapter, uint32_t address, uint32_t value) {
|
||||
CALCULATE_SEGMENT_INFO;
|
||||
CALCULATE_SEGMENT_ADDRESS;
|
||||
adapter->core->rawWrite32(adapter->core, address, segmentAddress, value);
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainBase(struct mScriptMemoryDomain* adapter) {
|
||||
return adapter->block.start;
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainEnd(struct mScriptMemoryDomain* adapter) {
|
||||
return adapter->block.end;
|
||||
}
|
||||
|
||||
static uint32_t mScriptMemoryDomainSize(struct mScriptMemoryDomain* adapter) {
|
||||
return adapter->block.size;
|
||||
}
|
||||
|
||||
static struct mScriptValue* mScriptMemoryDomainName(struct mScriptMemoryDomain* adapter) {
|
||||
return mScriptStringCreateFromUTF8(adapter->block.shortName);
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptMemoryDomain);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read8, mScriptMemoryDomainRead8, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read16, mScriptMemoryDomainRead16, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, read32, mScriptMemoryDomainRead32, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WSTR, readRange, mScriptMemoryDomainReadRange, 2, U32, address, U32, length);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write8, mScriptMemoryDomainWrite8, 2, U32, address, U8, value);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write16, mScriptMemoryDomainWrite16, 2, U32, address, U16, value);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptMemoryDomain, write32, mScriptMemoryDomainWrite32, 2, U32, address, U32, value);
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, base, mScriptMemoryDomainBase, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, bound, mScriptMemoryDomainEnd, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, U32, size, mScriptMemoryDomainSize, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptMemoryDomain, WSTR, name, mScriptMemoryDomainName, 0);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptMemoryDomain)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"An object used for access directly to a memory domain, e.g. the cartridge, "
|
||||
"instead of through a whole address space, as with the functions directly on struct::mCore."
|
||||
)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read8)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read16)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, read32)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read byte range from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, readRange)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write8)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write16)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, write32)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the address of the base of this memory domain")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, base)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the address of the end bound of this memory domain. Note that this address is not in the domain itself, and is the address of the first byte past it")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, bound)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the size of this memory domain in bytes")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, size)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get a short, human-readable name for this memory domain")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptMemoryDomain, name)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
static struct mScriptValue* _mScriptCoreGetGameTitle(const struct mCore* core) {
|
||||
char title[32] = {0};
|
||||
core->getGameTitle(core, title);
|
||||
return mScriptStringCreateFromASCII(title);
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreGetGameCode(const struct mCore* core) {
|
||||
char code[16] = {0};
|
||||
core->getGameCode(core, code);
|
||||
return mScriptStringCreateFromASCII(code);
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreChecksum(const struct mCore* core, int t) {
|
||||
enum mCoreChecksumType type = (enum mCoreChecksumType) t;
|
||||
size_t size = 0;
|
||||
switch (type) {
|
||||
case mCHECKSUM_CRC32:
|
||||
size = 4;
|
||||
break;
|
||||
}
|
||||
if (!size) {
|
||||
return NULL;
|
||||
}
|
||||
void* data = calloc(1, size);
|
||||
core->checksum(core, data, type);
|
||||
if (type == mCHECKSUM_CRC32) {
|
||||
// This checksum is endian-dependent...let's just make it big endian for Lua
|
||||
uint32_t* crc = data;
|
||||
STORE_32BE(*crc, 0, crc);
|
||||
}
|
||||
struct mScriptValue* ret = mScriptStringCreateFromBytes(data, size);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _mScriptCoreAddKey(struct mCore* core, int32_t key) {
|
||||
core->addKeys(core, 1 << key);
|
||||
}
|
||||
|
||||
static void _mScriptCoreClearKey(struct mCore* core, int32_t key) {
|
||||
core->clearKeys(core, 1 << key);
|
||||
}
|
||||
|
||||
static int32_t _mScriptCoreGetKey(struct mCore* core, int32_t key) {
|
||||
return (core->getKeys(core) >> key) & 1;
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreReadRange(struct mCore* core, uint32_t address, uint32_t length) {
|
||||
struct mScriptValue* value = mScriptStringCreateEmpty(length);
|
||||
char* buffer = value->value.string->buffer;
|
||||
uint32_t i;
|
||||
for (i = 0; i < length; ++i, ++address) {
|
||||
buffer[i] = core->busRead8(core, address);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreReadRegister(const struct mCore* core, const char* regName) {
|
||||
int32_t out;
|
||||
if (!core->readRegister(core, regName, &out)) {
|
||||
return NULL;
|
||||
}
|
||||
struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||
value->value.s32 = out;
|
||||
return value;
|
||||
}
|
||||
|
||||
static void _mScriptCoreWriteRegister(struct mCore* core, const char* regName, int32_t in) {
|
||||
core->writeRegister(core, regName, &in);
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t flags) {
|
||||
struct VFile* vf = VFileMemChunk(NULL, 0);
|
||||
if (!mCoreSaveStateNamed(core, vf, flags)) {
|
||||
vf->close(vf);
|
||||
return NULL;
|
||||
}
|
||||
void* buffer = vf->map(vf, vf->size(vf), MAP_READ);
|
||||
struct mScriptValue* value = mScriptStringCreateFromBytes(buffer, vf->size(vf));
|
||||
vf->close(vf);
|
||||
return value;
|
||||
}
|
||||
|
||||
static int32_t _mScriptCoreLoadState(struct mCore* core, struct mScriptString* buffer, int32_t flags) {
|
||||
struct VFile* vf = VFileFromConstMemory(buffer->buffer, buffer->size);
|
||||
int ret = mCoreLoadStateNamed(core, vf, flags);
|
||||
vf->close(vf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) {
|
||||
if (filename) {
|
||||
struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
mCoreTakeScreenshotVF(core, vf);
|
||||
vf->close(vf);
|
||||
} else {
|
||||
mCoreTakeScreenshot(core);
|
||||
}
|
||||
}
|
||||
|
||||
// Info functions
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, platform, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, U32, frameCounter, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frameCycles, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mCore, S32, frequency, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameTitle, _mScriptCoreGetGameTitle, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(mCore, WSTR, getGameCode, _mScriptCoreGetGameCode, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mCore, WSTR, checksum, _mScriptCoreChecksum, 1, S32, type);
|
||||
|
||||
// Run functions
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, reset, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, runFrame, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, step, 0);
|
||||
|
||||
// Key functions
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, setKeys, 1, U32, keys);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, addKeys, 1, U32, keys);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, clearKeys, 1, U32, keys);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, addKey, _mScriptCoreAddKey, 1, S32, key);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, clearKey, _mScriptCoreClearKey, 1, S32, key);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, S32, getKey, _mScriptCoreGetKey, 1, S32, key);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, getKeys, 0);
|
||||
|
||||
// Memory functions
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead8, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead16, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(mCore, U32, busRead32, 1, U32, address);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRange, _mScriptCoreReadRange, 2, U32, address, U32, length);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite8, 2, U32, address, U8, value);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, value);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value);
|
||||
|
||||
// Register functions
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value);
|
||||
|
||||
// Savestate functions
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, saveStateSlot, mCoreSaveState, 2, S32, slot, S32, flags);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WSTR, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateSlot, mCoreLoadState, 2, S32, slot, S32, flags);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, S32, loadStateBuffer, _mScriptCoreLoadState, 2, STR, buffer, S32, flags);
|
||||
|
||||
// Miscellaneous functions
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mCore)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"An instance of an emulator core."
|
||||
)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get which platform is being emulated. See C.PLATFORM for possible values")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, platform)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the number of the current frame")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, currentFrame, frameCounter)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per frame")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frameCycles)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the number of cycles per second")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, frequency)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the checksum of the loaded ROM")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, checksum)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get internal title of the game from the ROM header")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameTitle)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get internal product code for the game from the ROM header, if available")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getGameCode)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Reset the emulation. This does not invoke the **reset** callback")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, reset)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Run until the next frame")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, runFrame)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Run a single instruction")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, step)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the currently active key list")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, setKeys)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Add a single key to the currently active key list")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, addKey)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Add a bitmask of keys to the currently active key list")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, addKeys)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Remove a single key from the currently active key list")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, clearKey)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Remove a bitmask of keys from the currently active key list")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, clearKeys)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the active state of a given key")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getKey)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the currently active keys as a bitmask")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, getKeys)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read an 8-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read8, busRead8)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read16, busRead16)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read a 32-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, read32, busRead32)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read byte range from the given offset")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, readRange)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write an 8-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write8, busWrite8)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write16, busWrite16)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write a 32-bit value from the given bus address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(mCore, write32, busWrite32)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Read the value of the register with the given name")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, readRegister)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Write the value of the register with the given name")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, writeRegister)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Save state to the slot number. See C.SAVESTATE for possible values for `flags`")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateSlot)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Save state and return as a buffer. See C.SAVESTATE for possible values for `flags`")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateBuffer)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Load state from the slot number. See C.SAVESTATE for possible values for `flags`")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateSlot)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Load state a buffer. See C.SAVESTATE for possible values for `flags`")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateBuffer)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Save a screenshot")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum)
|
||||
mSCRIPT_MAKE_S32(mCHECKSUM_CRC32)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateSlot)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_MAKE_S32(SAVESTATE_ALL)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateSlot)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, saveStateBuffer)
|
||||
mSCRIPT_MAKE_S32(SAVESTATE_ALL)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, loadStateBuffer)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_MAKE_S32(SAVESTATE_ALL & ~SAVESTATE_SAVEDATA)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, screenshot)
|
||||
mSCRIPT_MAKE_CHARP(NULL)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
static void _clearMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter, bool clear) {
|
||||
struct TableIterator iter;
|
||||
if (mScriptTableIteratorStart(&adapter->memory, &iter)) {
|
||||
while (true) {
|
||||
struct mScriptValue* weakref = mScriptTableIteratorGetValue(&adapter->memory, &iter);
|
||||
if (weakref) {
|
||||
if (clear) {
|
||||
mScriptContextClearWeakref(context, weakref->value.s32);
|
||||
}
|
||||
mScriptValueDeref(weakref);
|
||||
}
|
||||
if (!mScriptTableIteratorNext(&adapter->memory, &iter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mScriptTableClear(&adapter->memory);
|
||||
}
|
||||
|
||||
static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCoreAdapter* adapter) {
|
||||
_clearMemoryMap(context, adapter, true);
|
||||
|
||||
const struct mCoreMemoryBlock* blocks;
|
||||
size_t nBlocks = adapter->core->listMemoryBlocks(adapter->core, &blocks);
|
||||
size_t i;
|
||||
for (i = 0; i < nBlocks; ++i) {
|
||||
if (blocks[i].flags == mCORE_MEMORY_VIRTUAL) {
|
||||
continue;
|
||||
}
|
||||
struct mScriptMemoryDomain* memadapter = calloc(1, sizeof(*memadapter));
|
||||
memadapter->core = adapter->core;
|
||||
memcpy(&memadapter->block, &blocks[i], sizeof(memadapter->block));
|
||||
struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptMemoryDomain));
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
value->value.opaque = memadapter;
|
||||
struct mScriptValue* key = mScriptStringCreateFromUTF8(blocks[i].internalName);
|
||||
mScriptTableInsert(&adapter->memory, key, mScriptContextMakeWeakref(context, value));
|
||||
mScriptValueDeref(key);
|
||||
}
|
||||
}
|
||||
|
||||
static void _mScriptCoreAdapterDeinit(struct mScriptCoreAdapter* adapter) {
|
||||
_clearMemoryMap(adapter->context, adapter, false);
|
||||
adapter->memory.type->free(&adapter->memory);
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* adapter, const char* name) {
|
||||
struct mScriptValue val;
|
||||
struct mScriptValue core = mSCRIPT_MAKE(S(mCore), adapter->core);
|
||||
if (!mScriptObjectGet(&core, name, &val)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mScriptValue* ret = malloc(sizeof(*ret));
|
||||
memcpy(ret, &val, sizeof(*ret));
|
||||
ret->refs = 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) {
|
||||
adapter->core->reset(adapter->core);
|
||||
mScriptContextTriggerCallback(adapter->context, "reset");
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, W(mCore), _get, _mScriptCoreAdapterGet, 1, CHARP, name);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, reset, _mScriptCoreAdapterReset, 0);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"A wrapper around a struct::mCore object that exposes more functionality. "
|
||||
"It can be implicity cast to a Core object, and exposes the same methods. "
|
||||
"Please see the documentation on struct::mCore for details on those methods."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(mScriptCoreAdapter, PS(mCore), _core, core)
|
||||
mSCRIPT_DEFINE_DOCSTRING("A table containing a platform-specific set of struct::mScriptMemoryDomain objects")
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptCoreAdapter, TABLE, memory)
|
||||
mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptCoreAdapter)
|
||||
mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Reset the emulation. As opposed to struct::mCore.reset, this version calls the **reset** callback")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, reset)
|
||||
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core)
|
||||
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) {
|
||||
struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCoreAdapter));
|
||||
struct mScriptCoreAdapter* adapter = calloc(1, sizeof(*adapter));
|
||||
adapter->core = core;
|
||||
adapter->context = context;
|
||||
|
||||
adapter->memory.refs = mSCRIPT_VALUE_UNREF;
|
||||
adapter->memory.flags = 0;
|
||||
adapter->memory.type = mSCRIPT_TYPE_MS_TABLE;
|
||||
adapter->memory.type->alloc(&adapter->memory);
|
||||
|
||||
_rebuildMemoryMap(context, adapter);
|
||||
|
||||
coreValue->value.opaque = adapter;
|
||||
coreValue->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
mScriptContextSetGlobal(context, "emu", coreValue);
|
||||
}
|
||||
|
||||
void mScriptContextDetachCore(struct mScriptContext* context) {
|
||||
struct mScriptValue* value = HashTableLookup(&context->rootScope, "emu");
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
value = mScriptContextAccessWeakref(context, value);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
_clearMemoryMap(context, value->value.opaque, true);
|
||||
mScriptContextRemoveGlobal(context, "emu");
|
||||
}
|
||||
|
||||
static struct mScriptTextBuffer* _mScriptConsoleCreateBuffer(struct mScriptConsole* lib, const char* name) {
|
||||
struct mScriptTextBuffer* buffer = lib->textBufferFactory(lib->textBufferContext);
|
||||
buffer->init(buffer, name);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void mScriptConsoleLog(struct mScriptConsole* console, struct mScriptString* msg) {
|
||||
if (console->logger) {
|
||||
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer);
|
||||
} else {
|
||||
mLog(_mLOG_CAT_SCRIPT, mLOG_INFO, "%s", msg->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void mScriptConsoleWarn(struct mScriptConsole* console, struct mScriptString* msg) {
|
||||
if (console->logger) {
|
||||
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
|
||||
} else {
|
||||
mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void mScriptConsoleError(struct mScriptConsole* console, struct mScriptString* msg) {
|
||||
if (console->logger) {
|
||||
mLogExplicit(console->logger, _mLOG_CAT_SCRIPT, mLOG_ERROR, "%s", msg->buffer);
|
||||
} else {
|
||||
mLog(_mLOG_CAT_SCRIPT, mLOG_WARN, "%s", msg->buffer);
|
||||
}
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, log, mScriptConsoleLog, 1, STR, msg);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, warn, mScriptConsoleWarn, 1, STR, msg);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptConsole, error, mScriptConsoleError, 1, STR, msg);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptConsole, S(mScriptTextBuffer), createBuffer, _mScriptConsoleCreateBuffer, 1, CHARP, name);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptConsole)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"A global singleton object `console` that can be used for presenting textual information to the user via a console."
|
||||
)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Print a log to the console")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, log)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Print a warning to the console")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, warn)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Print an error to the console")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, error)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Create a text buffer that can be used to display custom information")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptConsole, createBuffer)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer)
|
||||
mSCRIPT_MAKE_CHARP(NULL)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
void mScriptContextAttachLogger(struct mScriptContext* context, struct mLogger* logger) {
|
||||
struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole));
|
||||
struct mScriptConsole* console = value->value.opaque;
|
||||
if (!console) {
|
||||
console = calloc(1, sizeof(*console));
|
||||
value->value.opaque = console;
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
}
|
||||
console->logger = logger;
|
||||
}
|
||||
|
||||
void mScriptContextDetachLogger(struct mScriptContext* context) {
|
||||
struct mScriptValue* value = mScriptContextGetGlobal(context, "console");
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
struct mScriptConsole* console = value->value.opaque;
|
||||
console->logger = mLogGetContext();
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, deinit, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getX, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, getY, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, cols, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(mScriptTextBuffer, U32, rows, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, print, 1, CHARP, text);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, clear, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setSize, 2, U32, cols, U32, rows);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, moveCursor, 2, U32, x, U32, y);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, advance, 1, S32, adv);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mScriptTextBuffer, setName, 1, CHARP, name);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptTextBuffer)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"An object that can be used to present texual data to the user. It is displayed monospaced, "
|
||||
"and text can be edited after sending by moving the cursor or clearing the buffer."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(mScriptTextBuffer, deinit)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the current x position of the cursor")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getX)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get the current y position of the cursor")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, getY)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get number of columns in the buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, cols)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get number of rows in the buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, rows)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Print a string to the buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, print)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Clear the buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, clear)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the number of rows and columns")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setSize)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the posiiton of the cursor")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, moveCursor)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Advance the cursor a number of columns")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, advance)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set the user-visible name of this buffer")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptTextBuffer, setName)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
void mScriptContextSetTextBufferFactory(struct mScriptContext* context, mScriptContextBufferFactory factory, void* cbContext) {
|
||||
struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole));
|
||||
struct mScriptConsole* console = value->value.opaque;
|
||||
if (!console) {
|
||||
console = calloc(1, sizeof(*console));
|
||||
console->logger = mLogGetContext();
|
||||
value->value.opaque = console;
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
}
|
||||
console->textBufferFactory = factory;
|
||||
console->textBufferContext = cbContext;
|
||||
}
|
||||
|
|
|
@ -457,6 +457,9 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
else {
|
||||
bool success = _savePNGState(core, vf, &extdata);
|
||||
mStateExtdataDeinit(&extdata);
|
||||
if (cheatVf) {
|
||||
cheatVf->close(cheatVf);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
/* Copyright (c) 2013-2022 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 "util/test/suite.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
#define TEST_PLATFORM mPLATFORM_GBA
|
||||
#define RAM_BASE BASE_WORKING_IRAM
|
||||
#elif defined(M_CORE_GB)
|
||||
#include <mgba/internal/gb/memory.h>
|
||||
#define TEST_PLATFORM mPLATFORM_GB
|
||||
#define RAM_BASE GB_BASE_WORKING_RAM_BANK0
|
||||
#else
|
||||
#error "Need a valid platform for testing"
|
||||
#endif
|
||||
|
||||
struct mScriptTestLogger {
|
||||
struct mLogger d;
|
||||
char* log;
|
||||
char* warn;
|
||||
char* error;
|
||||
};
|
||||
|
||||
static const uint8_t _fakeGBROM[0x4000] = {
|
||||
[0x100] = 0x18, // Loop forever
|
||||
[0x101] = 0xFE, // jr, $-2
|
||||
[0x102] = 0xCE, // Enough of the header to fool the core
|
||||
[0x103] = 0xED,
|
||||
[0x104] = 0x66,
|
||||
[0x105] = 0x66,
|
||||
};
|
||||
|
||||
#define SETUP_LUA \
|
||||
struct mScriptContext context; \
|
||||
mScriptContextInit(&context); \
|
||||
struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA)
|
||||
|
||||
#define CREATE_CORE \
|
||||
struct mCore* core = mCoreCreate(TEST_PLATFORM); \
|
||||
assert_non_null(core); \
|
||||
assert_true(core->init(core)); \
|
||||
switch (core->platform(core)) { \
|
||||
case mPLATFORM_GBA: \
|
||||
core->busWrite32(core, 0x020000C0, 0xEAFFFFFE); \
|
||||
break; \
|
||||
case mPLATFORM_GB: \
|
||||
assert_true(core->loadROM(core, VFileFromConstMemory(_fakeGBROM, sizeof(_fakeGBROM)))); \
|
||||
break; \
|
||||
case mPLATFORM_NONE: \
|
||||
break; \
|
||||
} \
|
||||
mCoreInitConfig(core, NULL); \
|
||||
mScriptContextAttachCore(&context, core)
|
||||
|
||||
#define TEARDOWN_CORE \
|
||||
mCoreConfigDeinit(&core->config); \
|
||||
core->deinit(core)
|
||||
|
||||
#define LOAD_PROGRAM(PROG) \
|
||||
do { \
|
||||
struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \
|
||||
assert_true(lua->load(lua, NULL, vf)); \
|
||||
vf->close(vf); \
|
||||
} while(0)
|
||||
|
||||
#define TEST_VALUE(TYPE, NAME, VALUE) \
|
||||
do { \
|
||||
struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \
|
||||
struct mScriptValue* global = lua->getGlobal(lua, NAME); \
|
||||
assert_non_null(global); \
|
||||
assert_true(global->type->equal(global, &val)); \
|
||||
mScriptValueDeref(global); \
|
||||
} while(0)
|
||||
|
||||
static void _mScriptTestLog(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(category);
|
||||
struct mScriptTestLogger* logger = (struct mScriptTestLogger*) log;
|
||||
|
||||
char* message;
|
||||
#ifdef HAVE_VASPRINTF
|
||||
vasprintf(&message, format, args);
|
||||
#else
|
||||
char messageBuf[64];
|
||||
vsnprintf(messageBuf, format, args);
|
||||
message = strdup(messageBuf);
|
||||
#endif
|
||||
switch (level) {
|
||||
case mLOG_INFO:
|
||||
if (logger->log) {
|
||||
free(logger->log);
|
||||
}
|
||||
logger->log = message;
|
||||
break;
|
||||
case mLOG_WARN:
|
||||
if (logger->warn) {
|
||||
free(logger->warn);
|
||||
}
|
||||
logger->warn = message;
|
||||
break;
|
||||
case mLOG_ERROR:
|
||||
if (logger->error) {
|
||||
free(logger->error);
|
||||
}
|
||||
logger->error = message;
|
||||
break;
|
||||
default:
|
||||
free(message);
|
||||
}
|
||||
}
|
||||
|
||||
static void mScriptTestLoggerInit(struct mScriptTestLogger* logger) {
|
||||
memset(logger, 0, sizeof(*logger));
|
||||
logger->d.log = _mScriptTestLog;
|
||||
}
|
||||
|
||||
static void mScriptTestLoggerDeinit(struct mScriptTestLogger* logger) {
|
||||
if (logger->log) {
|
||||
free(logger->log);
|
||||
}
|
||||
if (logger->warn) {
|
||||
free(logger->warn);
|
||||
}
|
||||
if (logger->error) {
|
||||
free(logger->error);
|
||||
}
|
||||
}
|
||||
|
||||
M_TEST_SUITE_SETUP(mScriptCore) {
|
||||
if (mSCRIPT_ENGINE_LUA->init) {
|
||||
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_SUITE_TEARDOWN(mScriptCore) {
|
||||
if (mSCRIPT_ENGINE_LUA->deinit) {
|
||||
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(globals) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM("assert(emu)");
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(infoFuncs) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"frequency = emu:frequency()\n"
|
||||
"frameCycles = emu:frameCycles()\n"
|
||||
);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
TEST_VALUE(S32, "frequency", core->frequency(core));
|
||||
TEST_VALUE(S32, "frameCycles", core->frameCycles(core));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(detach) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(emu)\n"
|
||||
"assert(emu.memory)\n"
|
||||
"a = emu\n"
|
||||
"b = emu.memory\n"
|
||||
);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
mScriptContextDetachCore(&context);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(not emu)\n"
|
||||
);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"a:frequency()\n"
|
||||
);
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(memory.cart0)\n"
|
||||
);
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(runFrame) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"frame = emu:currentFrame()\n"
|
||||
"emu:runFrame()\n"
|
||||
);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
assert_true(lua->run(lua));
|
||||
TEST_VALUE(S32, "frame", i);
|
||||
}
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(memoryRead) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"a8 = emu:read8(base + 0)\n"
|
||||
"b8 = emu:read8(base + 1)\n"
|
||||
"c8 = emu:read8(base + 2)\n"
|
||||
"d8 = emu:read8(base + 3)\n"
|
||||
"a16 = emu:read16(base + 4)\n"
|
||||
"b16 = emu:read16(base + 6)\n"
|
||||
"a32 = emu:read32(base + 8)\n"
|
||||
);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 12; ++i) {
|
||||
core->busWrite8(core, RAM_BASE + i, i + 1);
|
||||
}
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
TEST_VALUE(S32, "a8", 1);
|
||||
TEST_VALUE(S32, "b8", 2);
|
||||
TEST_VALUE(S32, "c8", 3);
|
||||
TEST_VALUE(S32, "d8", 4);
|
||||
TEST_VALUE(S32, "a16", 0x0605);
|
||||
TEST_VALUE(S32, "b16", 0x0807);
|
||||
TEST_VALUE(S32, "a32", 0x0C0B0A09);
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(memoryWrite) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
core->reset(core);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"emu:write8(base + 0, 1)\n"
|
||||
"emu:write8(base + 1, 2)\n"
|
||||
"emu:write8(base + 2, 3)\n"
|
||||
"emu:write8(base + 3, 4)\n"
|
||||
"emu:write16(base + 4, 0x0605)\n"
|
||||
"emu:write16(base + 6, 0x0807)\n"
|
||||
"emu:write32(base + 8, 0x0C0B0A09)\n"
|
||||
);
|
||||
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 12; ++i) {
|
||||
assert_int_equal(core->busRead8(core, RAM_BASE + i), i + 1);
|
||||
}
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(logging) {
|
||||
SETUP_LUA;
|
||||
struct mScriptTestLogger logger;
|
||||
mScriptTestLoggerInit(&logger);
|
||||
|
||||
mScriptContextAttachLogger(&context, &logger.d);
|
||||
|
||||
LOAD_PROGRAM(
|
||||
"assert(console)\n"
|
||||
"console:log(\"log\")\n"
|
||||
"console:warn(\"warn\")\n"
|
||||
"console:error(\"error\")\n"
|
||||
"a = console\n"
|
||||
);
|
||||
|
||||
assert_true(lua->run(lua));
|
||||
assert_non_null(logger.log);
|
||||
assert_non_null(logger.warn);
|
||||
assert_non_null(logger.error);
|
||||
assert_string_equal(logger.log, "log");
|
||||
assert_string_equal(logger.warn, "warn");
|
||||
assert_string_equal(logger.error, "error");
|
||||
|
||||
mScriptContextDetachLogger(&context);
|
||||
mScriptTestLoggerDeinit(&logger);
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||
cmocka_unit_test(globals),
|
||||
cmocka_unit_test(infoFuncs),
|
||||
cmocka_unit_test(detach),
|
||||
cmocka_unit_test(runFrame),
|
||||
cmocka_unit_test(memoryRead),
|
||||
cmocka_unit_test(memoryWrite),
|
||||
cmocka_unit_test(logging),
|
||||
)
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#include <mgba/core/blip_buf.h>
|
||||
#include <mgba/core/core.h>
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
#endif
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -183,6 +187,42 @@ void _coreShutdown(void* context) {
|
|||
_changeState(thread->impl, mTHREAD_EXITING, true);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#define ADD_CALLBACK(NAME) \
|
||||
void _script_ ## NAME(void* context) { \
|
||||
struct mCoreThread* threadContext = context; \
|
||||
if (!threadContext->scriptContext) { \
|
||||
return; \
|
||||
} \
|
||||
mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \
|
||||
}
|
||||
|
||||
ADD_CALLBACK(frame)
|
||||
ADD_CALLBACK(crashed)
|
||||
ADD_CALLBACK(sleep)
|
||||
ADD_CALLBACK(stop)
|
||||
ADD_CALLBACK(keysRead)
|
||||
ADD_CALLBACK(savedataUpdated)
|
||||
ADD_CALLBACK(alarm)
|
||||
|
||||
#undef ADD_CALLBACK
|
||||
#define CALLBACK(NAME) _script_ ## NAME
|
||||
|
||||
static void _mCoreThreadAddCallbacks(struct mCoreThread* threadContext) {
|
||||
struct mCoreCallbacks callbacks = {
|
||||
.videoFrameEnded = CALLBACK(frame),
|
||||
.coreCrashed = CALLBACK(crashed),
|
||||
.sleep = CALLBACK(sleep),
|
||||
.shutdown = CALLBACK(stop),
|
||||
.keysRead = CALLBACK(keysRead),
|
||||
.savedataUpdated = CALLBACK(savedataUpdated),
|
||||
.alarm = CALLBACK(alarm),
|
||||
.context = threadContext
|
||||
};
|
||||
threadContext->core->addCoreCallbacks(threadContext->core, &callbacks);
|
||||
}
|
||||
#endif
|
||||
|
||||
static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||
struct mCoreThread* threadContext = context;
|
||||
#ifdef USE_PTHREADS
|
||||
|
@ -219,10 +259,30 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
mLogFilterLoad(threadContext->logger.d.filter, &core->config);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
struct mScriptContext* scriptContext = threadContext->scriptContext;
|
||||
if (scriptContext) {
|
||||
mScriptContextAttachCore(scriptContext, core);
|
||||
_mCoreThreadAddCallbacks(threadContext);
|
||||
}
|
||||
#endif
|
||||
|
||||
mCoreThreadRewindParamsChanged(threadContext);
|
||||
if (threadContext->startCallback) {
|
||||
threadContext->startCallback(threadContext);
|
||||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
// startCallback could add a script context
|
||||
if (scriptContext != threadContext->scriptContext) {
|
||||
scriptContext = threadContext->scriptContext;
|
||||
if (scriptContext) {
|
||||
_mCoreThreadAddCallbacks(threadContext);
|
||||
}
|
||||
}
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "start");
|
||||
}
|
||||
#endif
|
||||
|
||||
core->reset(core);
|
||||
threadContext->impl->core = core;
|
||||
|
@ -232,6 +292,19 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
threadContext->resetCallback(threadContext);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
// resetCallback could add a script context
|
||||
if (scriptContext != threadContext->scriptContext) {
|
||||
scriptContext = threadContext->scriptContext;
|
||||
if (scriptContext) {
|
||||
_mCoreThreadAddCallbacks(threadContext);
|
||||
}
|
||||
}
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "reset");
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mCoreThreadInternal* impl = threadContext->impl;
|
||||
bool wasPaused = false;
|
||||
int pendingRequests = 0;
|
||||
|
@ -277,6 +350,14 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
MutexLock(&impl->stateMutex);
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (scriptContext != threadContext->scriptContext) {
|
||||
scriptContext = threadContext->scriptContext;
|
||||
if (scriptContext) {
|
||||
_mCoreThreadAddCallbacks(threadContext);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (wasPaused && !(impl->requested & mTHREAD_REQ_PAUSE)) {
|
||||
break;
|
||||
}
|
||||
|
@ -318,6 +399,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
if (threadContext->resetCallback) {
|
||||
threadContext->resetCallback(threadContext);
|
||||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "reset");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (pendingRequests & mTHREAD_REQ_RUN_ON) {
|
||||
if (threadContext->run) {
|
||||
|
@ -337,6 +423,12 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
if (threadContext->cleanCallback) {
|
||||
threadContext->cleanCallback(threadContext);
|
||||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "shutdown");
|
||||
mScriptContextDetachCore(scriptContext);
|
||||
}
|
||||
#endif
|
||||
core->clearCoreCallbacks(core);
|
||||
|
||||
if (threadContext->logger.d.filter == &filter) {
|
||||
|
|
|
@ -40,14 +40,14 @@ static void _SoftReset(struct GBA* gba) {
|
|||
ARMSetPrivilegeMode(cpu, MODE_IRQ);
|
||||
cpu->spsr.packed = 0;
|
||||
cpu->gprs[ARM_LR] = 0;
|
||||
cpu->gprs[ARM_SP] = SP_BASE_IRQ;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_IRQ;
|
||||
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
|
||||
cpu->spsr.packed = 0;
|
||||
cpu->gprs[ARM_LR] = 0;
|
||||
cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_SUPERVISOR;
|
||||
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
|
||||
cpu->gprs[ARM_LR] = 0;
|
||||
cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM;
|
||||
int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA];
|
||||
memset(((int8_t*) gba->memory.iwram) + SIZE_WORKING_IRAM - 0x200, 0, 0x200);
|
||||
if (flag) {
|
||||
|
|
|
@ -718,7 +718,7 @@ void _eReaderReadData(struct GBACartEReader* ereader) {
|
|||
if (led > 0x4000) {
|
||||
led = 0x4000;
|
||||
}
|
||||
GBARaiseIRQ(ereader->p, IRQ_GAMEPAK, -led);
|
||||
GBARaiseIRQ(ereader->p, GBA_IRQ_GAMEPAK, -led);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
dma->nextDest = dma->dest;
|
||||
}
|
||||
if (GBADMARegisterIsDoIRQ(dma->reg)) {
|
||||
GBARaiseIRQ(gba, IRQ_DMA0 + memory->activeDMA, cyclesLate);
|
||||
GBARaiseIRQ(gba, GBA_IRQ_DMA0 + memory->activeDMA, cyclesLate);
|
||||
}
|
||||
GBADMAUpdate(gba);
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
|
|||
gate->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0;
|
||||
gate->d.p->siocnt = GBASIONormalClearStart(gate->d.p->siocnt);
|
||||
if (GBASIONormalIsIrq(gate->d.p->siocnt)) {
|
||||
GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
|
||||
GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -194,6 +194,6 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
|
|||
gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
|
||||
|
||||
if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) {
|
||||
GBARaiseIRQ(gate->d.p->p, IRQ_SIO, cyclesLate);
|
||||
GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,11 +198,11 @@ void GBAInterruptHandlerInit(struct ARMInterruptHandler* irqh) {
|
|||
|
||||
void GBAReset(struct ARMCore* cpu) {
|
||||
ARMSetPrivilegeMode(cpu, MODE_IRQ);
|
||||
cpu->gprs[ARM_SP] = SP_BASE_IRQ;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_IRQ;
|
||||
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
|
||||
cpu->gprs[ARM_SP] = SP_BASE_SUPERVISOR;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_SUPERVISOR;
|
||||
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
|
||||
cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM;
|
||||
|
||||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
gba->memory.savedata.maskWriteback = false;
|
||||
|
@ -465,7 +465,7 @@ void GBAYankROM(struct GBA* gba) {
|
|||
gba->yankedRomSize = gba->memory.romSize;
|
||||
gba->memory.romSize = 0;
|
||||
gba->memory.romMask = 0;
|
||||
GBARaiseIRQ(gba, IRQ_GAMEPAK, 0);
|
||||
GBARaiseIRQ(gba, GBA_IRQ_GAMEPAK, 0);
|
||||
}
|
||||
|
||||
void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
|
||||
|
@ -553,7 +553,7 @@ void GBAHalt(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBAStop(struct GBA* gba) {
|
||||
int validIrqs = (1 << IRQ_GAMEPAK) | (1 << IRQ_KEYPAD) | (1 << IRQ_SIO);
|
||||
int validIrqs = (1 << GBA_IRQ_GAMEPAK) | (1 << GBA_IRQ_KEYPAD) | (1 << GBA_IRQ_SIO);
|
||||
int sleep = gba->memory.io[REG_IE >> 1] & validIrqs;
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
|
@ -920,9 +920,9 @@ void GBATestKeypadIRQ(struct GBA* gba) {
|
|||
if (keysLast == keysActive) {
|
||||
return;
|
||||
}
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD, 0);
|
||||
GBARaiseIRQ(gba, GBA_IRQ_KEYPAD, 0);
|
||||
} else if (!isAnd && (keysActive & keycnt)) {
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD, 0);
|
||||
GBARaiseIRQ(gba, GBA_IRQ_KEYPAD, 0);
|
||||
} else {
|
||||
gba->keysLast = 0x400;
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
|
|||
if ((value & 0x0081) == 0x0081) {
|
||||
if (value & 0x4000) {
|
||||
// TODO: Test this on hardware to see if this is correct
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
value &= ~0x0080;
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
|
|||
gbp->p->memory.io[REG_SIODATA32_LO >> 1] = tx;
|
||||
gbp->p->memory.io[REG_SIODATA32_HI >> 1] = tx >> 16;
|
||||
if (GBASIONormalIsIrq(gbp->d.p->siocnt)) {
|
||||
GBARaiseIRQ(gbp->p, IRQ_SIO, cyclesLate);
|
||||
GBARaiseIRQ(gbp->p, GBA_IRQ_SIO, cyclesLate);
|
||||
}
|
||||
gbp->d.p->siocnt = GBASIONormalClearStart(gbp->d.p->siocnt);
|
||||
gbp->p->memory.io[REG_SIOCNT >> 1] = gbp->d.p->siocnt & ~0x0080;
|
||||
|
|
|
@ -46,7 +46,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
|
|||
case JOY_RESET:
|
||||
sio->p->p->memory.io[REG_JOYCNT >> 1] |= JOYCNT_RESET;
|
||||
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
|
||||
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
// Fall through
|
||||
case JOY_POLL:
|
||||
|
@ -68,7 +68,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
|
|||
mLOG(GBA_SIO, DEBUG, "JOY recv: %02X (%02X)", data[0], sio->p->p->memory.io[REG_JOYCNT >> 1]);
|
||||
|
||||
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
|
||||
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
return 1;
|
||||
case JOY_TRANS:
|
||||
|
@ -84,7 +84,7 @@ int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command
|
|||
mLOG(GBA_SIO, DEBUG, "JOY trans: %02X%02X%02X%02X:%02X (%02X)", data[0], data[1], data[2], data[3], data[4], sio->p->p->memory.io[REG_JOYCNT >> 1]);
|
||||
|
||||
if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) {
|
||||
GBARaiseIRQ(sio->p->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt);
|
||||
sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, node->id);
|
||||
if (GBASIOMultiplayerIsIrq(sio->siocnt)) {
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
break;
|
||||
case SIO_NORMAL_8:
|
||||
|
@ -243,7 +243,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
node->d.p->p->memory.io[REG_SIODATA8 >> 1] = 0xFFFF;
|
||||
}
|
||||
if (GBASIONormalIsIrq(sio->siocnt)) {
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
break;
|
||||
case SIO_NORMAL_32:
|
||||
|
@ -258,7 +258,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
|||
node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF;
|
||||
}
|
||||
if (GBASIONormalIsIrq(sio->siocnt)) {
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO, 0);
|
||||
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
static void GBATimerIrq(struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
struct GBATimer* timer = &gba->timers[timerId];
|
||||
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId, cyclesLate);
|
||||
GBARaiseIRQ(gba, GBA_IRQ_TIMER0 + timerId, cyclesLate);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
|
||||
dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
|
||||
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_VCOUNTER, cyclesLate);
|
||||
GBARaiseIRQ(video->p, GBA_IRQ_VCOUNTER, cyclesLate);
|
||||
}
|
||||
} else {
|
||||
dispstat = GBARegisterDISPSTATClearVcounter(dispstat);
|
||||
|
@ -199,7 +199,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
}
|
||||
GBADMARunVblank(video->p, -cyclesLate);
|
||||
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_VBLANK, cyclesLate);
|
||||
GBARaiseIRQ(video->p, GBA_IRQ_VBLANK, cyclesLate);
|
||||
}
|
||||
GBAFrameEnded(video->p);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
|
@ -235,7 +235,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
GBADMARunDisplayStart(video->p, -cyclesLate);
|
||||
}
|
||||
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate - 6); // TODO: Where does this fudge factor come from?
|
||||
GBARaiseIRQ(video->p, GBA_IRQ_HBLANK, cyclesLate - 6); // TODO: Where does this fudge factor come from?
|
||||
}
|
||||
video->shouldStall = 0;
|
||||
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
|
||||
|
|
|
@ -43,6 +43,11 @@ function(find_feature FEATURE_NAME FEATURE_REQUIRES)
|
|||
elseif(DEFINED ${UREQUIRE}_INCLUDE_DIRS)
|
||||
set(${UREQUIRE}_INCLUDE_DIRS ${${UREQUIRE}_INCLUDE_DIRS} PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ${REQUIRE}_INCLUDE_DIR)
|
||||
set(${UREQUIRE}_INCLUDE_DIR ${${REQUIRE}_INCLUDE_DIR} PARENT_SCOPE)
|
||||
elseif(DEFINED ${UREQUIRE}_INCLUDE_DIR)
|
||||
set(${UREQUIRE}_INCLUDE_DIR ${${UREQUIRE}_INCLUDE_DIR} PARENT_SCOPE)
|
||||
endif()
|
||||
if(DEFINED ${REQUIRE}_VERSION_STRING)
|
||||
set(${UREQUIRE}_VERSION_STRING ${${REQUIRE}_VERSION_STRING} PARENT_SCOPE)
|
||||
elseif(DEFINED ${UREQUIRE}_VERSION_STRING)
|
||||
|
|
|
@ -88,6 +88,7 @@ set(SOURCE_FILES
|
|||
AudioProcessor.cpp
|
||||
CheatsModel.cpp
|
||||
CheatsView.cpp
|
||||
CheckBoxDelegate.cpp
|
||||
ConfigController.cpp
|
||||
ColorPicker.cpp
|
||||
CoreManager.cpp
|
||||
|
@ -250,11 +251,22 @@ if(USE_DISCORD_RPC)
|
|||
list(APPEND SOURCE_FILES DiscordCoordinator.cpp)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND SOURCE_FILES
|
||||
ScriptingController.cpp
|
||||
ScriptingTextBuffer.cpp
|
||||
ScriptingView.cpp)
|
||||
|
||||
list(APPEND UI_FILES
|
||||
ScriptingView.ui)
|
||||
endif()
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
qt_add_resources(RESOURCES resources.qrc)
|
||||
else()
|
||||
qt5_add_resources(RESOURCES resources.qrc)
|
||||
endif()
|
||||
|
||||
if(BUILD_UPDATER)
|
||||
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/updater.qrc INPUT ${CMAKE_CURRENT_SOURCE_DIR}/updater.qrc.in)
|
||||
if(TARGET Qt6::Core)
|
||||
|
@ -294,6 +306,10 @@ endif()
|
|||
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
|
||||
endif()
|
||||
if(ENABLE_SCRIPTING AND USE_LUA)
|
||||
message(STATUS ${CMAKE_SOURCE_DIR}/res/scripts)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/scripts DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
|
||||
endif()
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
if(DATADIR MATCHES "^\\.[.\\]")
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
|
||||
#include "GBAApp.h"
|
||||
#include "CoreController.h"
|
||||
#include "LogController.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QButtonGroup>
|
||||
#include <QClipboard>
|
||||
#include <QRadioButton>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <mgba/core/cheats.h>
|
||||
#ifdef M_CORE_GBA
|
||||
|
@ -110,7 +112,7 @@ void CheatsView::removeSet() {
|
|||
return;
|
||||
}
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
for (const QModelIndex& index : selection) {
|
||||
for (const QModelIndex& index ATTRIBUTE_UNUSED : selection) {
|
||||
m_model.removeAt(selection[0]);
|
||||
}
|
||||
}
|
||||
|
@ -149,18 +151,40 @@ void CheatsView::enterCheat() {
|
|||
index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex());
|
||||
m_ui.cheatList->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
}
|
||||
// TODO: Update API to handle this splitting in the core
|
||||
QRegularExpression regexp("\\s");
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', Qt::SkipEmptyParts);
|
||||
QStringList cheats = m_ui.codeEntry->toPlainText().split(regexp, Qt::SkipEmptyParts);
|
||||
#else
|
||||
QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts);
|
||||
QStringList cheats = m_ui.codeEntry->toPlainText().split(regexp, QString::SkipEmptyParts);
|
||||
#endif
|
||||
int failure = 0;
|
||||
QString buffer;
|
||||
for (const QString& string : cheats) {
|
||||
m_model.beginAppendRow(index);
|
||||
mCheatAddLine(set, string.toUtf8().constData(), m_codeType);
|
||||
if (!buffer.isEmpty()) {
|
||||
buffer += " " + string;
|
||||
if (mCheatAddLine(set, buffer.toUtf8().constData(), m_codeType)) {
|
||||
buffer.clear();
|
||||
} else if (mCheatAddLine(set, string.toUtf8().constData(), m_codeType)) {
|
||||
buffer.clear();
|
||||
} else {
|
||||
buffer = string;
|
||||
++failure;
|
||||
}
|
||||
} else if (!mCheatAddLine(set, string.toUtf8().constData(), m_codeType)) {
|
||||
buffer = string;
|
||||
}
|
||||
m_model.endAppendRow();
|
||||
}
|
||||
if (!buffer.isEmpty()) {
|
||||
++failure;
|
||||
}
|
||||
if (set->refresh) {
|
||||
set->refresh(set, m_controller->cheatDevice());
|
||||
}
|
||||
if (failure) {
|
||||
LOG(QT, ERROR) << tr("Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types.");
|
||||
}
|
||||
m_ui.codeEntry->clear();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright (c) 2013-2022 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 "CheckBoxDelegate.h"
|
||||
#include <QStyle>
|
||||
#include <QAbstractItemView>
|
||||
#include <QtDebug>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
CheckBoxDelegate::CheckBoxDelegate(QObject* parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
// initializers only
|
||||
}
|
||||
|
||||
void CheckBoxDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
||||
QAbstractItemView* view = qobject_cast<QAbstractItemView*>(option.styleObject);
|
||||
if (view && (index.flags() & Qt::ItemIsUserCheckable)) {
|
||||
// Set up style options
|
||||
QStyleOptionViewItem newOption(option);
|
||||
initStyleOption(&newOption, index);
|
||||
if (view->currentIndex() == index && (newOption.state & QStyle::State_HasFocus)) {
|
||||
newOption.state |= QStyle::State_KeyboardFocusChange;
|
||||
}
|
||||
if (newOption.checkState == Qt::PartiallyChecked) {
|
||||
newOption.state |= QStyle::State_NoChange;
|
||||
} else if (newOption.checkState == Qt::Checked) {
|
||||
newOption.state |= QStyle::State_On;
|
||||
} else {
|
||||
newOption.state |= QStyle::State_Off;
|
||||
}
|
||||
|
||||
// Draw background
|
||||
view->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &newOption, painter, view);
|
||||
|
||||
// Draw checkbox
|
||||
int checkWidth = view->style()->pixelMetric(QStyle::PM_IndicatorWidth);
|
||||
int checkHeight = view->style()->pixelMetric(QStyle::PM_IndicatorHeight);
|
||||
int xMargin = (newOption.rect.width() - checkWidth) / 2;
|
||||
int yMargin = (newOption.rect.height() - checkHeight) / 2;
|
||||
newOption.rect.setRect(newOption.rect.left() + xMargin, newOption.rect.top() + yMargin, checkWidth, checkHeight);
|
||||
view->style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &newOption, painter, view);
|
||||
} else {
|
||||
QStyledItemDelegate::paint(painter, option, index);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/* Copyright (c) 2013-2022 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class CheckBoxDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
CheckBoxDelegate(QObject* parent = nullptr);
|
||||
|
||||
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
};
|
||||
|
||||
}
|
|
@ -301,9 +301,9 @@ void ConfigController::setQtOption(const QString& key, const QVariant& value, co
|
|||
}
|
||||
}
|
||||
|
||||
QList<QString> ConfigController::getMRU() const {
|
||||
QList<QString> mru;
|
||||
m_settings->beginGroup("mru");
|
||||
QStringList ConfigController::getMRU(ConfigController::MRU mruType) const {
|
||||
QStringList mru;
|
||||
m_settings->beginGroup(mruName(mruType));
|
||||
for (int i = 0; i < MRU_LIST_SIZE; ++i) {
|
||||
QString item = m_settings->value(QString::number(i)).toString();
|
||||
if (item.isNull()) {
|
||||
|
@ -315,9 +315,9 @@ QList<QString> ConfigController::getMRU() const {
|
|||
return mru;
|
||||
}
|
||||
|
||||
void ConfigController::setMRU(const QList<QString>& mru) {
|
||||
void ConfigController::setMRU(const QStringList& mru, ConfigController::MRU mruType) {
|
||||
int i = 0;
|
||||
m_settings->beginGroup("mru");
|
||||
m_settings->beginGroup(mruName(mruType));
|
||||
for (const QString& item : mru) {
|
||||
m_settings->setValue(QString::number(i), item);
|
||||
++i;
|
||||
|
@ -331,6 +331,15 @@ void ConfigController::setMRU(const QList<QString>& mru) {
|
|||
m_settings->endGroup();
|
||||
}
|
||||
|
||||
constexpr const char* ConfigController::mruName(ConfigController::MRU mru) {
|
||||
switch (mru) {
|
||||
case MRU::ROM:
|
||||
return "mru";
|
||||
case MRU::Script:
|
||||
return "recentScripts";
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigController::write() {
|
||||
mCoreConfigSave(&m_config);
|
||||
m_settings->sync();
|
||||
|
|
|
@ -67,6 +67,11 @@ public:
|
|||
constexpr static const char* const PORT = "qt";
|
||||
static const int MRU_LIST_SIZE = 10;
|
||||
|
||||
enum class MRU {
|
||||
ROM,
|
||||
Script
|
||||
};
|
||||
|
||||
ConfigController(QObject* parent = nullptr);
|
||||
~ConfigController();
|
||||
|
||||
|
@ -84,8 +89,8 @@ public:
|
|||
QVariant getArgvOption(const QString& key) const;
|
||||
QVariant takeArgvOption(const QString& key);
|
||||
|
||||
QList<QString> getMRU() const;
|
||||
void setMRU(const QList<QString>& mru);
|
||||
QStringList getMRU(MRU = MRU::ROM) const;
|
||||
void setMRU(const QStringList& mru, MRU = MRU::ROM);
|
||||
|
||||
Configuration* overrides() { return mCoreConfigGetOverrides(&m_config); }
|
||||
void saveOverride(const Override&);
|
||||
|
@ -114,7 +119,7 @@ public slots:
|
|||
void write();
|
||||
|
||||
private:
|
||||
void addArgvOption(const QString& key, const QVariant& value);
|
||||
static constexpr const char* mruName(ConfigController::MRU);
|
||||
|
||||
Configuration* defaults() { return &m_config.defaultsTable; }
|
||||
|
||||
|
|
|
@ -164,6 +164,9 @@ public slots:
|
|||
void replaceGame(const QString&);
|
||||
void yankPak();
|
||||
|
||||
void addKey(int key);
|
||||
void clearKey(int key);
|
||||
|
||||
#ifdef USE_PNG
|
||||
void screenshot();
|
||||
#endif
|
||||
|
|
|
@ -11,14 +11,58 @@
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
QTextCharFormat LogWidget::s_warn;
|
||||
QTextCharFormat LogWidget::s_error;
|
||||
QTextCharFormat LogWidget::s_prompt;
|
||||
|
||||
LogWidget::LogWidget(QWidget* parent)
|
||||
: QTextEdit(parent)
|
||||
: QPlainTextEdit(parent)
|
||||
{
|
||||
setFont(GBAApp::app()->monospaceFont());
|
||||
|
||||
QPalette palette = QApplication::palette();
|
||||
s_warn.setFontWeight(QFont::DemiBold);
|
||||
s_warn.setFontItalic(true);
|
||||
s_warn.setForeground(Qt::yellow);
|
||||
s_warn.setBackground(QColor(255, 255, 0, 64));
|
||||
s_error.setFontWeight(QFont::Bold);
|
||||
s_error.setForeground(Qt::red);
|
||||
s_error.setBackground(QColor(255, 0, 0, 64));
|
||||
s_prompt.setForeground(palette.brush(QPalette::Disabled, QPalette::Text));
|
||||
}
|
||||
|
||||
void LogWidget::log(const QString& line) {
|
||||
moveCursor(QTextCursor::End);
|
||||
insertPlainText(line);
|
||||
textCursor().insertText(line, {});
|
||||
if (m_newlineTerminated) {
|
||||
textCursor().insertText("\n");
|
||||
}
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void LogWidget::warn(const QString& line) {
|
||||
moveCursor(QTextCursor::End);
|
||||
textCursor().insertText(WARN_PREFIX + line, s_warn);
|
||||
if (m_newlineTerminated) {
|
||||
textCursor().insertText("\n");
|
||||
}
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void LogWidget::error(const QString& line) {
|
||||
moveCursor(QTextCursor::End);
|
||||
textCursor().insertText(ERROR_PREFIX + line, s_error);
|
||||
if (m_newlineTerminated) {
|
||||
textCursor().insertText("\n");
|
||||
}
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
void LogWidget::echo(const QString& line) {
|
||||
moveCursor(QTextCursor::End);
|
||||
textCursor().insertText(PROMPT_PREFIX + line, s_prompt);
|
||||
if (m_newlineTerminated) {
|
||||
textCursor().insertText("\n");
|
||||
}
|
||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||
}
|
||||
|
|
|
@ -5,16 +5,36 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class LogWidget : public QTextEdit {
|
||||
class LogHighlighter;
|
||||
|
||||
class LogWidget : public QPlainTextEdit {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static constexpr const char* WARN_PREFIX = "[WARNING] ";
|
||||
static constexpr const char* ERROR_PREFIX = "[ERROR] ";
|
||||
static constexpr const char* PROMPT_PREFIX = "> ";
|
||||
|
||||
LogWidget(QWidget* parent = nullptr);
|
||||
|
||||
void setNewlineTerminated(bool newlineTerminated) { m_newlineTerminated = newlineTerminated; }
|
||||
|
||||
public slots:
|
||||
void log(const QString&);
|
||||
void warn(const QString&);
|
||||
void error(const QString&);
|
||||
void echo(const QString&);
|
||||
|
||||
private:
|
||||
static QTextCharFormat s_warn;
|
||||
static QTextCharFormat s_error;
|
||||
static QTextCharFormat s_prompt;
|
||||
|
||||
bool m_newlineTerminated = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/* Copyright (c) 2013-2022 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 "ScriptingController.h"
|
||||
|
||||
#include "CoreController.h"
|
||||
#include "ScriptingTextBuffer.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ScriptingController::ScriptingController(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_logger.p = this;
|
||||
m_logger.log = [](mLogger* log, int, enum mLogLevel level, const char* format, va_list args) {
|
||||
Logger* logger = static_cast<Logger*>(log);
|
||||
va_list argc;
|
||||
va_copy(argc, args);
|
||||
QString message = QString::vasprintf(format, argc);
|
||||
va_end(argc);
|
||||
switch (level) {
|
||||
case mLOG_WARN:
|
||||
emit logger->p->warn(message);
|
||||
break;
|
||||
case mLOG_ERROR:
|
||||
emit logger->p->error(message);
|
||||
break;
|
||||
default:
|
||||
emit logger->p->log(message);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
ScriptingController::~ScriptingController() {
|
||||
clearController();
|
||||
mScriptContextDeinit(&m_scriptContext);
|
||||
}
|
||||
|
||||
void ScriptingController::setController(std::shared_ptr<CoreController> controller) {
|
||||
if (controller == m_controller) {
|
||||
return;
|
||||
}
|
||||
clearController();
|
||||
m_controller = controller;
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
m_controller->thread()->scriptContext = &m_scriptContext;
|
||||
if (m_controller->hasStarted()) {
|
||||
mScriptContextAttachCore(&m_scriptContext, m_controller->thread()->core);
|
||||
}
|
||||
connect(m_controller.get(), &CoreController::stopping, this, &ScriptingController::clearController);
|
||||
}
|
||||
|
||||
bool ScriptingController::loadFile(const QString& path) {
|
||||
VFileDevice vf(path, QIODevice::ReadOnly);
|
||||
return load(vf, path);
|
||||
}
|
||||
|
||||
bool ScriptingController::load(VFileDevice& vf, const QString& name) {
|
||||
if (!m_activeEngine) {
|
||||
return false;
|
||||
}
|
||||
QByteArray utf8 = name.toUtf8();
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
if (!m_activeEngine->load(m_activeEngine, utf8.constData(), vf) || !m_activeEngine->run(m_activeEngine)) {
|
||||
emit error(QString::fromUtf8(m_activeEngine->getError(m_activeEngine)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptingController::clearController() {
|
||||
if (!m_controller) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
mScriptContextDetachCore(&m_scriptContext);
|
||||
m_controller->thread()->scriptContext = nullptr;
|
||||
}
|
||||
m_controller.reset();
|
||||
}
|
||||
|
||||
void ScriptingController::reset() {
|
||||
CoreController::Interrupter interrupter(m_controller);
|
||||
for (ScriptingTextBuffer* buffer : m_buffers) {
|
||||
delete buffer;
|
||||
}
|
||||
m_buffers.clear();
|
||||
mScriptContextDetachCore(&m_scriptContext);
|
||||
mScriptContextDeinit(&m_scriptContext);
|
||||
m_engines.clear();
|
||||
m_activeEngine = nullptr;
|
||||
init();
|
||||
if (m_controller && m_controller->hasStarted()) {
|
||||
mScriptContextAttachCore(&m_scriptContext, m_controller->thread()->core);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingController::runCode(const QString& code) {
|
||||
VFileDevice vf(code.toUtf8());
|
||||
load(vf, "*prompt");
|
||||
}
|
||||
|
||||
mScriptTextBuffer* ScriptingController::createTextBuffer(void* context) {
|
||||
ScriptingController* self = static_cast<ScriptingController*>(context);
|
||||
ScriptingTextBuffer* buffer = new ScriptingTextBuffer(self);
|
||||
self->m_buffers.append(buffer);
|
||||
emit self->textBufferCreated(buffer);
|
||||
return buffer->textBuffer();
|
||||
}
|
||||
|
||||
void ScriptingController::init() {
|
||||
mScriptContextInit(&m_scriptContext);
|
||||
mScriptContextAttachStdlib(&m_scriptContext);
|
||||
mScriptContextRegisterEngines(&m_scriptContext);
|
||||
|
||||
mScriptContextAttachLogger(&m_scriptContext, &m_logger);
|
||||
mScriptContextSetTextBufferFactory(&m_scriptContext, &ScriptingController::createTextBuffer, this);
|
||||
|
||||
HashTableEnumerate(&m_scriptContext.engines, [](const char* key, void* engine, void* context) {
|
||||
ScriptingController* self = static_cast<ScriptingController*>(context);
|
||||
self->m_engines[QString::fromUtf8(key)] = static_cast<mScriptEngineContext*>(engine);
|
||||
}, this);
|
||||
|
||||
if (m_engines.count() == 1) {
|
||||
m_activeEngine = *m_engines.begin();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* Copyright (c) 2013-2022 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class CoreController;
|
||||
class ScriptingTextBuffer;
|
||||
|
||||
class ScriptingController : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptingController(QObject* parent = nullptr);
|
||||
~ScriptingController();
|
||||
|
||||
void setController(std::shared_ptr<CoreController> controller);
|
||||
|
||||
bool loadFile(const QString& path);
|
||||
bool load(VFileDevice& vf, const QString& name);
|
||||
|
||||
mScriptContext* context() { return &m_scriptContext; }
|
||||
QList<ScriptingTextBuffer*> textBuffers() { return m_buffers; }
|
||||
|
||||
signals:
|
||||
void log(const QString&);
|
||||
void warn(const QString&);
|
||||
void error(const QString&);
|
||||
void textBufferCreated(ScriptingTextBuffer*);
|
||||
|
||||
public slots:
|
||||
void clearController();
|
||||
void reset();
|
||||
void runCode(const QString& code);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
static mScriptTextBuffer* createTextBuffer(void* context);
|
||||
|
||||
struct Logger : mLogger {
|
||||
ScriptingController* p;
|
||||
} m_logger{};
|
||||
|
||||
mScriptContext m_scriptContext;
|
||||
|
||||
mScriptEngineContext* m_activeEngine = nullptr;
|
||||
QHash<QString, mScriptEngineContext*> m_engines;
|
||||
QList<ScriptingTextBuffer*> m_buffers;
|
||||
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/* Copyright (c) 2013-2022 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 "ScriptingTextBuffer.h"
|
||||
|
||||
#include "GBAApp.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QPlainTextDocumentLayout>
|
||||
#include <QTextBlock>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ScriptingTextBuffer::ScriptingTextBuffer(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_shim.init = &ScriptingTextBuffer::init;
|
||||
m_shim.deinit = &ScriptingTextBuffer::deinit;
|
||||
m_shim.setName = &ScriptingTextBuffer::setName;
|
||||
m_shim.getX = &ScriptingTextBuffer::getX;
|
||||
m_shim.getY = &ScriptingTextBuffer::getY;
|
||||
m_shim.cols = &ScriptingTextBuffer::cols;
|
||||
m_shim.rows = &ScriptingTextBuffer::rows;
|
||||
m_shim.print = &ScriptingTextBuffer::print;
|
||||
m_shim.clear = &ScriptingTextBuffer::clear;
|
||||
m_shim.setSize = &ScriptingTextBuffer::setSize;
|
||||
m_shim.moveCursor = &ScriptingTextBuffer::moveCursor;
|
||||
m_shim.advance = &ScriptingTextBuffer::advance;
|
||||
m_shim.p = this;
|
||||
m_shim.cursor = QTextCursor(&m_document);
|
||||
|
||||
auto layout = new QPlainTextDocumentLayout(&m_document);
|
||||
m_document.setDocumentLayout(layout);
|
||||
m_document.setDefaultFont(GBAApp::app()->monospaceFont());
|
||||
m_document.setMaximumBlockCount(m_dims.height());
|
||||
|
||||
QTextOption textOption;
|
||||
textOption.setWrapMode(QTextOption::NoWrap);
|
||||
m_document.setDefaultTextOption(textOption);
|
||||
setBufferName(tr("Untitled buffer"));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setBufferName(const QString& name) {
|
||||
m_name = name;
|
||||
m_document.setMetaInformation(QTextDocument::DocumentTitle, name);
|
||||
emit bufferNameChanged(name);
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::print(const QString& text) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
QString split(text);
|
||||
m_shim.cursor.beginEditBlock();
|
||||
while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) {
|
||||
int cut = m_dims.width() - m_shim.cursor.positionInBlock();
|
||||
if (!m_shim.cursor.atBlockEnd()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
}
|
||||
m_shim.cursor.insertText(split.left(cut));
|
||||
if (m_shim.cursor.atEnd()) {
|
||||
m_shim.cursor.insertBlock();
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1);
|
||||
}
|
||||
split = split.mid(cut);
|
||||
}
|
||||
if (!m_shim.cursor.atBlockEnd()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length());
|
||||
}
|
||||
m_shim.cursor.insertText(split);
|
||||
m_shim.cursor.endEditBlock();
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::clear() {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_document.clear();
|
||||
m_document.setMetaInformation(QTextDocument::DocumentTitle, m_name);
|
||||
m_shim.cursor = QTextCursor(&m_document);
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setSize(const QSize& size) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_dims = size;
|
||||
m_document.setMaximumBlockCount(m_dims.height());
|
||||
for (int i = 0; i < m_document.blockCount(); ++i) {
|
||||
if (m_document.findBlockByNumber(i).length() - 1 > m_dims.width()) {
|
||||
QTextCursor deleter(m_document.findBlockByNumber(i));
|
||||
deleter.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, size.width());
|
||||
deleter.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
deleter.removeSelectedText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::moveCursor(const QPoint& pos) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_shim.cursor.movePosition(QTextCursor::Start);
|
||||
int y = pos.y();
|
||||
if (y >= m_dims.height()) {
|
||||
y = m_dims.height() - 1;
|
||||
}
|
||||
if (y >= m_document.blockCount()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::End);
|
||||
while (y >= m_document.blockCount()) {
|
||||
m_shim.cursor.insertBlock();
|
||||
}
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, y);
|
||||
}
|
||||
|
||||
int x = pos.x();
|
||||
if (x >= m_dims.width()) {
|
||||
x = m_dims.width() - 1;
|
||||
}
|
||||
|
||||
if (x >= m_shim.cursor.block().length()) {
|
||||
m_shim.cursor.movePosition(QTextCursor::EndOfBlock);
|
||||
m_shim.cursor.insertText(QString(x - m_shim.cursor.block().length() + 1, QChar(' ')));
|
||||
} else {
|
||||
m_shim.cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, x);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::advance(int increment) {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
int x = m_shim.cursor.positionInBlock();
|
||||
int y = m_shim.cursor.blockNumber();
|
||||
x += increment;
|
||||
if (x > 0) {
|
||||
y += x / m_dims.width();
|
||||
x %= m_dims.width();
|
||||
} else if (x < 0) {
|
||||
y += (x - m_dims.width() + 1) / m_dims.width();
|
||||
x %= m_dims.width();
|
||||
if (x) {
|
||||
x += m_dims.width();
|
||||
}
|
||||
}
|
||||
locker.unlock();
|
||||
moveCursor({x, y});
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::init(struct mScriptTextBuffer* buffer, const char* name) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (name) {
|
||||
QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name)));
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "clear");
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::deinit(struct mScriptTextBuffer*) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setName(struct mScriptTextBuffer* buffer, const char* name) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "setBufferName", Q_ARG(const QString&, QString::fromUtf8(name)));
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::getX(const struct mScriptTextBuffer* buffer) {
|
||||
const ScriptingBufferShim* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer);
|
||||
QMutexLocker locker(&self->p->m_mutex);
|
||||
return self->cursor.positionInBlock();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::getY(const struct mScriptTextBuffer* buffer) {
|
||||
const ScriptingBufferShim* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer);
|
||||
QMutexLocker locker(&self->p->m_mutex);
|
||||
return self->cursor.blockNumber();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::cols(const struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMutexLocker locker(&self->m_mutex);
|
||||
return self->m_dims.width();
|
||||
}
|
||||
|
||||
uint32_t ScriptingTextBuffer::rows(const struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<const ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMutexLocker locker(&self->m_mutex);
|
||||
return self->m_dims.height();
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::print(struct mScriptTextBuffer* buffer, const char* text) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "print", Q_ARG(const QString&, QString::fromUtf8(text)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::clear(struct mScriptTextBuffer* buffer) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
QMetaObject::invokeMethod(self, "clear");
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::setSize(struct mScriptTextBuffer* buffer, uint32_t cols, uint32_t rows) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (cols > 500) {
|
||||
cols = 500;
|
||||
}
|
||||
if (rows > 10000) {
|
||||
rows = 10000;
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "setSize", Q_ARG(QSize, QSize(cols, rows)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::moveCursor(struct mScriptTextBuffer* buffer, uint32_t x, uint32_t y) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
if (x > INT_MAX) {
|
||||
x = INT_MAX;
|
||||
}
|
||||
if (y > INT_MAX) {
|
||||
y = INT_MAX;
|
||||
}
|
||||
QMetaObject::invokeMethod(self, "moveCursor", Q_ARG(QPoint, QPoint(x, y)));
|
||||
}
|
||||
|
||||
void ScriptingTextBuffer::advance(struct mScriptTextBuffer* buffer, int32_t adv) {
|
||||
ScriptingTextBuffer* self = static_cast<ScriptingTextBuffer::ScriptingBufferShim*>(buffer)->p;
|
||||
emit self->advance(adv);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright (c) 2013-2022 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <mgba/core/scripting.h>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ScriptingTextBuffer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptingTextBuffer(QObject* parent);
|
||||
|
||||
QTextDocument* document() { return &m_document; };
|
||||
mScriptTextBuffer* textBuffer() { return &m_shim; }
|
||||
|
||||
public slots:
|
||||
void setBufferName(const QString&);
|
||||
void print(const QString&);
|
||||
void clear();
|
||||
void setSize(const QSize&);
|
||||
void moveCursor(const QPoint&);
|
||||
void advance(int);
|
||||
|
||||
signals:
|
||||
void bufferNameChanged(const QString&);
|
||||
|
||||
private:
|
||||
struct ScriptingBufferShim : public mScriptTextBuffer {
|
||||
ScriptingTextBuffer* p;
|
||||
QTextCursor cursor;
|
||||
} m_shim;
|
||||
QTextDocument m_document;
|
||||
QMutex m_mutex;
|
||||
QString m_name;
|
||||
|
||||
static void init(struct mScriptTextBuffer*, const char* name);
|
||||
static void deinit(struct mScriptTextBuffer*);
|
||||
|
||||
static void setName(struct mScriptTextBuffer*, const char* name);
|
||||
|
||||
static uint32_t getX(const struct mScriptTextBuffer*);
|
||||
static uint32_t getY(const struct mScriptTextBuffer*);
|
||||
static uint32_t cols(const struct mScriptTextBuffer*);
|
||||
static uint32_t rows(const struct mScriptTextBuffer*);
|
||||
|
||||
static void print(struct mScriptTextBuffer*, const char* text);
|
||||
static void clear(struct mScriptTextBuffer*);
|
||||
static void setSize(struct mScriptTextBuffer*, uint32_t cols, uint32_t rows);
|
||||
static void moveCursor(struct mScriptTextBuffer*, uint32_t x, uint32_t y);
|
||||
static void advance(struct mScriptTextBuffer*, int32_t);
|
||||
|
||||
QSize m_dims{80, 24};
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* Copyright (c) 2013-2022 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 "ScriptingView.h"
|
||||
|
||||
#include "GBAApp.h"
|
||||
#include "ConfigController.h"
|
||||
#include "ScriptingController.h"
|
||||
#include "ScriptingTextBuffer.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ScriptingView::ScriptingView(ScriptingController* controller, ConfigController* config, QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
, m_config(config)
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.prompt->setFont(GBAApp::app()->monospaceFont());
|
||||
m_ui.log->setNewlineTerminated(true);
|
||||
|
||||
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &ScriptingView::submitRepl);
|
||||
connect(m_ui.runButton, &QAbstractButton::clicked, this, &ScriptingView::submitRepl);
|
||||
connect(m_controller, &ScriptingController::log, m_ui.log, &LogWidget::log);
|
||||
connect(m_controller, &ScriptingController::warn, m_ui.log, &LogWidget::warn);
|
||||
connect(m_controller, &ScriptingController::error, m_ui.log, &LogWidget::error);
|
||||
connect(m_controller, &ScriptingController::textBufferCreated, this, &ScriptingView::addTextBuffer);
|
||||
|
||||
connect(m_ui.buffers, &QListWidget::currentRowChanged, this, &ScriptingView::selectBuffer);
|
||||
connect(m_ui.load, &QAction::triggered, this, &ScriptingView::load);
|
||||
connect(m_ui.reset, &QAction::triggered, controller, &ScriptingController::reset);
|
||||
|
||||
m_mruFiles = m_config->getMRU(ConfigController::MRU::Script);
|
||||
updateMRU();
|
||||
|
||||
for (ScriptingTextBuffer* buffer : controller->textBuffers()) {
|
||||
addTextBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingView::submitRepl() {
|
||||
m_ui.log->echo(m_ui.prompt->text());
|
||||
m_controller->runCode(m_ui.prompt->text());
|
||||
m_ui.prompt->clear();
|
||||
}
|
||||
|
||||
void ScriptingView::load() {
|
||||
QString filename = GBAApp::app()->getOpenFileName(this, tr("Select script to load"), getFilters());
|
||||
if (!filename.isEmpty()) {
|
||||
if (!m_controller->loadFile(filename)) {
|
||||
return;
|
||||
}
|
||||
appendMRU(filename);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptingView::addTextBuffer(ScriptingTextBuffer* buffer) {
|
||||
QTextDocument* document = buffer->document();
|
||||
m_textBuffers.append(buffer);
|
||||
QListWidgetItem* item = new QListWidgetItem(document->metaInformation(QTextDocument::DocumentTitle));
|
||||
connect(buffer, &ScriptingTextBuffer::bufferNameChanged, this, [item](const QString& name) {
|
||||
item->setText(name);
|
||||
});
|
||||
connect(buffer, &QObject::destroyed, this, [this, buffer, item]() {
|
||||
m_textBuffers.removeAll(buffer);
|
||||
m_ui.buffers->removeItemWidget(item);
|
||||
});
|
||||
m_ui.buffers->addItem(item);
|
||||
m_ui.buffers->setCurrentItem(item);
|
||||
}
|
||||
|
||||
void ScriptingView::selectBuffer(int index) {
|
||||
m_ui.buffer->setDocument(m_textBuffers[index]->document());
|
||||
}
|
||||
|
||||
QString ScriptingView::getFilters() const {
|
||||
QStringList filters;
|
||||
#ifdef USE_LUA
|
||||
filters.append(tr("Lua scripts (*.lua)"));
|
||||
#endif
|
||||
filters.append(tr("All files (*.*)"));
|
||||
return filters.join(";;");
|
||||
}
|
||||
|
||||
void ScriptingView::appendMRU(const QString& fname) {
|
||||
int index = m_mruFiles.indexOf(fname);
|
||||
if (index >= 0) {
|
||||
m_mruFiles.removeAt(index);
|
||||
}
|
||||
m_mruFiles.prepend(fname);
|
||||
while (m_mruFiles.size() > ConfigController::MRU_LIST_SIZE) {
|
||||
m_mruFiles.removeLast();
|
||||
}
|
||||
updateMRU();
|
||||
}
|
||||
|
||||
void ScriptingView::updateMRU() {
|
||||
m_config->setMRU(m_mruFiles, ConfigController::MRU::Script);
|
||||
m_ui.mru->clear();
|
||||
for (const auto& fname : m_mruFiles) {
|
||||
m_ui.mru->addAction(fname, [this, fname]() {
|
||||
m_controller->loadFile(fname);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* Copyright (c) 2013-2022 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/. */
|
||||
#pragma once
|
||||
|
||||
#include "ui_ScriptingView.h"
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
class ScriptingController;
|
||||
class ScriptingTextBuffer;
|
||||
|
||||
class ScriptingView : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ScriptingView(ScriptingController* controller, ConfigController* config, QWidget* parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void submitRepl();
|
||||
void load();
|
||||
|
||||
void addTextBuffer(ScriptingTextBuffer*);
|
||||
void selectBuffer(int);
|
||||
|
||||
private:
|
||||
QString getFilters() const;
|
||||
|
||||
void appendMRU(const QString&);
|
||||
void updateMRU();
|
||||
|
||||
Ui::ScriptingView m_ui;
|
||||
|
||||
ConfigController* m_config;
|
||||
ScriptingController* m_controller;
|
||||
QList<ScriptingTextBuffer*> m_textBuffers;
|
||||
QStringList m_mruFiles;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScriptingView</class>
|
||||
<widget class="QMainWindow" name="ScriptingView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Scripting</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
|
||||
<item row="0" column="0" rowspan="3">
|
||||
<widget class="QListWidget" name="buffers">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QPlainTextEdit" name="buffer">
|
||||
<property name="lineWrapMode">
|
||||
<enum>QPlainTextEdit::NoWrap</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QGBA::LogWidget" name="log">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="prompt"/>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="mru">
|
||||
<property name="title">
|
||||
<string>Load recent script</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="load"/>
|
||||
<addaction name="mru"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="reset"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="load">
|
||||
<property name="text">
|
||||
<string>Load script...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="reset">
|
||||
<property name="text">
|
||||
<string>&Reset</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action0">
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QGBA::LogWidget</class>
|
||||
<extends>QPlainTextEdit</extends>
|
||||
<header>LogWidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -6,6 +6,7 @@
|
|||
#include "SettingsView.h"
|
||||
|
||||
#include "AudioProcessor.h"
|
||||
#include "CheckBoxDelegate.h"
|
||||
#include "ConfigController.h"
|
||||
#include "Display.h"
|
||||
#include "GBAApp.h"
|
||||
|
@ -354,9 +355,11 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
}
|
||||
|
||||
m_ui.loggingView->setModel(&m_logModel);
|
||||
m_ui.loggingView->setItemDelegate(new CheckBoxDelegate(m_ui.loggingView));
|
||||
m_ui.loggingView->setHorizontalHeader(new RotatedHeaderView(Qt::Horizontal));
|
||||
m_ui.loggingView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_ui.loggingView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
connect(m_ui.loggingView, SIGNAL(clicked(QModelIndex)), m_ui.loggingView, SLOT(setCurrentIndex(QModelIndex)));
|
||||
|
||||
connect(m_ui.logFileBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getSaveFileName(this, "Select log file");
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "ReportView.h"
|
||||
#include "ROMInfo.h"
|
||||
#include "SaveConverter.h"
|
||||
#include "ScriptingView.h"
|
||||
#include "SensorView.h"
|
||||
#include "ShaderSelector.h"
|
||||
#include "ShortcutController.h"
|
||||
|
@ -702,6 +703,19 @@ void Window::consoleOpen() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
void Window::scriptingOpen() {
|
||||
if (!m_scripting) {
|
||||
m_scripting = std::make_unique<ScriptingController>();
|
||||
if (m_controller) {
|
||||
m_scripting->setController(m_controller);
|
||||
}
|
||||
}
|
||||
ScriptingView* view = new ScriptingView(m_scripting.get(), m_config);
|
||||
openView(view);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Window::resizeEvent(QResizeEvent*) {
|
||||
if (!isFullScreen()) {
|
||||
m_config->setOption("height", m_screenWidget->height());
|
||||
|
@ -1735,15 +1749,20 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_actions.addAction(tr("Settings..."), "settings", this, &Window::openSettingsWindow, "tools")->setRole(Action::Role::SETTINGS);
|
||||
m_actions.addAction(tr("Make portable"), "makePortable", this, &Window::tryMakePortable, "tools");
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
m_actions.addSeparator("tools");
|
||||
#ifdef USE_DEBUGGERS
|
||||
m_actions.addAction(tr("Open debugger console..."), "debuggerWindow", this, &Window::consoleOpen, "tools");
|
||||
#ifdef USE_GDB_STUB
|
||||
Action* gdbWindow = addGameAction(tr("Start &GDB server..."), "gdbWindow", this, &Window::gdbOpen, "tools");
|
||||
m_platformActions.insert(mPLATFORM_GBA, gdbWindow);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools");
|
||||
#endif
|
||||
#if defined(USE_DEBUGGERS) || defined(ENABLE_SCRIPTING)
|
||||
m_actions.addSeparator("tools");
|
||||
#endif
|
||||
|
||||
addGameAction(tr("View &palette..."), "paletteWindow", openControllerTView<PaletteView>(), "tools");
|
||||
addGameAction(tr("View &sprites..."), "spriteWindow", openControllerTView<ObjView>(), "tools");
|
||||
|
@ -2139,6 +2158,12 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
m_pendingPatch = QString();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (m_scripting) {
|
||||
m_scripting->setController(m_controller);
|
||||
}
|
||||
#endif
|
||||
|
||||
attachDisplay();
|
||||
m_controller->loadConfig(m_config);
|
||||
m_config->updateOption("showOSD");
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include "LoadSaveState.h"
|
||||
#include "LogController.h"
|
||||
#include "SettingsView.h"
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "ScriptingController.h"
|
||||
#endif
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
|
@ -113,6 +116,10 @@ public slots:
|
|||
void gdbOpen();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
void scriptingOpen();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
virtual void showEvent(QShowEvent*) override;
|
||||
|
@ -213,7 +220,7 @@ private:
|
|||
QTimer m_fpsTimer;
|
||||
QTimer m_mustRestart;
|
||||
QTimer m_mustReset;
|
||||
QList<QString> m_mruFiles;
|
||||
QStringList m_mruFiles;
|
||||
ShortcutController* m_shortcutController;
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||
std::unique_ptr<ShaderSelector> m_shaderView;
|
||||
|
@ -250,6 +257,10 @@ private:
|
|||
#ifdef USE_SQLITE3
|
||||
LibraryController* m_libraryView;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
std::unique_ptr<ScriptingController> m_scripting;
|
||||
#endif
|
||||
};
|
||||
|
||||
class WindowBackground : public QWidget {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
include(ExportDirectory)
|
||||
set(SOURCE_FILES
|
||||
context.c
|
||||
stdlib.c
|
||||
types.c)
|
||||
|
||||
set(TEST_FILES
|
||||
test/classes.c
|
||||
test/types.c)
|
||||
|
||||
if(USE_LUA)
|
||||
list(APPEND SOURCE_FILES engines/lua.c)
|
||||
list(APPEND TEST_FILES test/lua.c)
|
||||
endif()
|
||||
|
||||
source_group("Scripting" FILES ${SOURCE_FILES})
|
||||
source_group("Scripting tests" FILES ${TEST_FILES})
|
||||
|
||||
export_directory(SCRIPT SOURCE_FILES)
|
||||
export_directory(SCRIPT_TEST TEST_FILES)
|
|
@ -0,0 +1,273 @@
|
|||
/* Copyright (c) 2013-2022 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/script/context.h>
|
||||
#ifdef USE_LUA
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#endif
|
||||
|
||||
struct mScriptFileInfo {
|
||||
const char* name;
|
||||
struct VFile* vf;
|
||||
struct mScriptEngineContext* context;
|
||||
};
|
||||
|
||||
static void _engineContextDestroy(void* ctx) {
|
||||
struct mScriptEngineContext* context = ctx;
|
||||
context->destroy(context);
|
||||
}
|
||||
|
||||
static void _engineAddGlobal(const char* key, void* value, void* user) {
|
||||
struct mScriptEngineContext* context = user;
|
||||
context->setGlobal(context, key, value);
|
||||
}
|
||||
|
||||
static void _contextAddGlobal(const char* key, void* value, void* user) {
|
||||
UNUSED(key);
|
||||
struct mScriptEngineContext* context = value;
|
||||
struct mScriptKVPair* pair = user;
|
||||
context->setGlobal(context, pair->key, pair->value);
|
||||
}
|
||||
|
||||
static void _contextRemoveGlobal(const char* key, void* value, void* user) {
|
||||
UNUSED(key);
|
||||
struct mScriptEngineContext* context = value;
|
||||
context->setGlobal(context, user, NULL);
|
||||
}
|
||||
|
||||
static void _contextFindForFile(const char* key, void* value, void* user) {
|
||||
UNUSED(key);
|
||||
struct mScriptFileInfo* info = user;
|
||||
struct mScriptEngineContext* context = value;
|
||||
if (info->context) {
|
||||
return;
|
||||
}
|
||||
if (context->isScript(context, info->name, info->vf)) {
|
||||
info->context = context;
|
||||
}
|
||||
}
|
||||
|
||||
void mScriptContextInit(struct mScriptContext* context) {
|
||||
HashTableInit(&context->rootScope, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
HashTableInit(&context->engines, 0, _engineContextDestroy);
|
||||
mScriptListInit(&context->refPool, 0);
|
||||
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
context->nextWeakref = 1;
|
||||
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
context->constants = NULL;
|
||||
}
|
||||
|
||||
void mScriptContextDeinit(struct mScriptContext* context) {
|
||||
HashTableDeinit(&context->rootScope);
|
||||
HashTableDeinit(&context->weakrefs);
|
||||
mScriptContextDrainPool(context);
|
||||
mScriptListDeinit(&context->refPool);
|
||||
HashTableDeinit(&context->callbacks);
|
||||
HashTableDeinit(&context->engines);
|
||||
}
|
||||
|
||||
void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
if (value->refs == mSCRIPT_VALUE_UNREF) {
|
||||
return;
|
||||
}
|
||||
switch (value->type->base) {
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
case mSCRIPT_TYPE_FLOAT:
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
struct mScriptValue* poolEntry = mScriptListAppend(&context->refPool);
|
||||
poolEntry->type = mSCRIPT_TYPE_MS_WRAPPER;
|
||||
poolEntry->value.opaque = value;
|
||||
poolEntry->refs = mSCRIPT_VALUE_UNREF;
|
||||
}
|
||||
|
||||
void mScriptContextDrainPool(struct mScriptContext* context) {
|
||||
size_t i;
|
||||
for (i = 0; i < mScriptListSize(&context->refPool); ++i) {
|
||||
struct mScriptValue* value = mScriptValueUnwrap(mScriptListGetPointer(&context->refPool, i));
|
||||
if (value) {
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
}
|
||||
mScriptListClear(&context->refPool);
|
||||
}
|
||||
|
||||
struct mScriptEngineContext* mScriptContextRegisterEngine(struct mScriptContext* context, struct mScriptEngine2* engine) {
|
||||
struct mScriptEngineContext* ectx = engine->create(engine, context);
|
||||
if (ectx) {
|
||||
HashTableInsert(&context->engines, engine->name, ectx);
|
||||
HashTableEnumerate(&context->rootScope, _engineAddGlobal, ectx);
|
||||
}
|
||||
return ectx;
|
||||
}
|
||||
|
||||
void mScriptContextRegisterEngines(struct mScriptContext* context) {
|
||||
UNUSED(context);
|
||||
#ifdef USE_LUA
|
||||
mScriptContextRegisterEngine(context, mSCRIPT_ENGINE_LUA);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mScriptContextSetGlobal(struct mScriptContext* context, const char* key, struct mScriptValue* value) {
|
||||
struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key);
|
||||
if (oldValue) {
|
||||
mScriptContextClearWeakref(context, oldValue->value.u32);
|
||||
}
|
||||
value = mScriptContextMakeWeakref(context, value);
|
||||
HashTableInsert(&context->rootScope, key, value);
|
||||
struct mScriptKVPair pair = {
|
||||
.key = key,
|
||||
.value = value
|
||||
};
|
||||
HashTableEnumerate(&context->engines, _contextAddGlobal, &pair);
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextGetGlobal(struct mScriptContext* context, const char* key) {
|
||||
struct mScriptValue* weakref = HashTableLookup(&context->rootScope, key);
|
||||
if (!weakref) {
|
||||
return NULL;
|
||||
}
|
||||
return mScriptContextAccessWeakref(context, weakref);
|
||||
}
|
||||
|
||||
void mScriptContextRemoveGlobal(struct mScriptContext* context, const char* key) {
|
||||
if (!HashTableLookup(&context->rootScope, key)) {
|
||||
return;
|
||||
}
|
||||
// Since _contextRemoveGlobal doesn't mutate |key|, this cast should be safe
|
||||
HashTableEnumerate(&context->engines, _contextRemoveGlobal, (char*) key);
|
||||
struct mScriptValue* oldValue = HashTableLookup(&context->rootScope, key);
|
||||
if (oldValue) {
|
||||
mScriptContextClearWeakref(context, oldValue->value.u32);
|
||||
HashTableRemove(&context->rootScope, key);
|
||||
}
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextEnsureGlobal(struct mScriptContext* context, const char* key, const struct mScriptType* type) {
|
||||
struct mScriptValue* value = mScriptContextGetGlobal(context, key);
|
||||
if (!value) {
|
||||
mScriptContextSetGlobal(context, key, mScriptValueAlloc(type));
|
||||
value = mScriptContextGetGlobal(context, key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t mScriptContextSetWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
mScriptValueRef(value);
|
||||
TableInsert(&context->weakrefs, context->nextWeakref, value);
|
||||
|
||||
uint32_t nextWeakref = context->nextWeakref;
|
||||
++context->nextWeakref;
|
||||
while (TableLookup(&context->weakrefs, context->nextWeakref)) {
|
||||
++context->nextWeakref;
|
||||
}
|
||||
return nextWeakref;
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
uint32_t weakref = mScriptContextSetWeakref(context, value);
|
||||
mScriptValueDeref(value);
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_WEAKREF);
|
||||
value->value.u32 = weakref;
|
||||
return value;
|
||||
}
|
||||
|
||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext* context, struct mScriptValue* value) {
|
||||
if (value->type != mSCRIPT_TYPE_MS_WEAKREF) {
|
||||
return value;
|
||||
}
|
||||
return TableLookup(&context->weakrefs, value->value.u32);
|
||||
}
|
||||
|
||||
void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref) {
|
||||
TableRemove(&context->weakrefs, weakref);
|
||||
}
|
||||
|
||||
void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) {
|
||||
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < mScriptListSize(list->value.list); ++i) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i);
|
||||
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
fn = mScriptValueUnwrap(fn);
|
||||
}
|
||||
mScriptInvoke(fn, &frame);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
}
|
||||
|
||||
void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
|
||||
if (fn->type->base != mSCRIPT_TYPE_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
|
||||
if (!list) {
|
||||
list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
|
||||
HashTableInsert(&context->callbacks, callback, list);
|
||||
}
|
||||
mScriptValueWrap(fn, mScriptListAppend(list->value.list));
|
||||
}
|
||||
|
||||
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) {
|
||||
if (!context->constants) {
|
||||
context->constants = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
}
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
size_t i;
|
||||
for (i = 0; constants[i].key; ++i) {
|
||||
struct mScriptValue* key = mScriptStringCreateFromUTF8(constants[i].key);
|
||||
mScriptTableInsert(table, key, constants[i].value);
|
||||
mScriptValueDeref(key);
|
||||
mScriptValueDeref(constants[i].value);
|
||||
}
|
||||
struct mScriptValue* key = mScriptStringCreateFromUTF8(nspace);
|
||||
mScriptTableInsert(context->constants, key, table);
|
||||
mScriptValueDeref(key);
|
||||
mScriptValueDeref(table);
|
||||
}
|
||||
|
||||
bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) {
|
||||
struct mScriptFileInfo info = {
|
||||
.name = name,
|
||||
.vf = vf,
|
||||
.context = NULL
|
||||
};
|
||||
HashTableEnumerate(&context->engines, _contextFindForFile, &info);
|
||||
if (!info.context) {
|
||||
return false;
|
||||
}
|
||||
return info.context->load(info.context, name, vf);
|
||||
}
|
||||
|
||||
bool mScriptContextLoadFile(struct mScriptContext* context, const char* path) {
|
||||
struct VFile* vf = VFileOpen(path, O_RDONLY);
|
||||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
bool ret = mScriptContextLoadVF(context, path, vf);
|
||||
vf->close(vf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) {
|
||||
if (val->type->base != mSCRIPT_TYPE_FUNCTION) {
|
||||
return false;
|
||||
}
|
||||
const struct mScriptTypeFunction* signature = &val->type->details.function;
|
||||
if (!mScriptCoerceFrame(&signature->parameters, &frame->arguments)) {
|
||||
return false;
|
||||
}
|
||||
const struct mScriptFunction* fn = val->value.opaque;
|
||||
return fn->call(frame, fn->context);
|
||||
}
|
|
@ -0,0 +1,497 @@
|
|||
/* Copyright (c) 2013-2022 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/core.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/script/context.h>
|
||||
|
||||
struct mScriptContext context;
|
||||
struct Table types;
|
||||
FILE* out;
|
||||
|
||||
void explainValue(struct mScriptValue* value, int level);
|
||||
void explainType(struct mScriptType* type, int level);
|
||||
|
||||
void addTypesFromTuple(const struct mScriptTypeTuple*);
|
||||
void addTypesFromTable(struct Table*);
|
||||
|
||||
void addType(const struct mScriptType* type) {
|
||||
if (HashTableLookup(&types, type->name) || type->isConst) {
|
||||
return;
|
||||
}
|
||||
HashTableInsert(&types, type->name, (struct mScriptType*) type);
|
||||
switch (type->base) {
|
||||
case mSCRIPT_TYPE_FUNCTION:
|
||||
addTypesFromTuple(&type->details.function.parameters);
|
||||
addTypesFromTuple(&type->details.function.returnType);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OBJECT:
|
||||
mScriptClassInit(type->details.cls);
|
||||
if (type->details.cls->parent) {
|
||||
addType(type->details.cls->parent);
|
||||
}
|
||||
addTypesFromTable(&type->details.cls->instanceMembers);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OPAQUE:
|
||||
case mSCRIPT_TYPE_WRAPPER:
|
||||
if (type->details.type) {
|
||||
addType(type->details.type);
|
||||
}
|
||||
case mSCRIPT_TYPE_VOID:
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
case mSCRIPT_TYPE_FLOAT:
|
||||
case mSCRIPT_TYPE_STRING:
|
||||
case mSCRIPT_TYPE_LIST:
|
||||
case mSCRIPT_TYPE_TABLE:
|
||||
case mSCRIPT_TYPE_WEAKREF:
|
||||
// No subtypes
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void addTypesFromTuple(const struct mScriptTypeTuple* tuple) {
|
||||
size_t i;
|
||||
for (i = 0; i < tuple->count; ++i) {
|
||||
addType(tuple->entries[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void addTypesFromTable(struct Table* table) {
|
||||
struct TableIterator iter;
|
||||
if (!HashTableIteratorStart(table, &iter)) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter);
|
||||
addType(member->type);
|
||||
} while(HashTableIteratorNext(table, &iter));
|
||||
}
|
||||
|
||||
void printchomp(const char* string, int level) {
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
|
||||
const char* start = string;
|
||||
char lineBuffer[1024];
|
||||
while (true) {
|
||||
const char* end = strchr(start, '\n');
|
||||
if (end) {
|
||||
size_t size = end - start;
|
||||
if (sizeof(lineBuffer) - 1 < size) {
|
||||
size = sizeof(lineBuffer) - 1;
|
||||
}
|
||||
strncpy(lineBuffer, start, size);
|
||||
lineBuffer[size] = '\0';
|
||||
fprintf(out, "%s%s\n", indent, lineBuffer);
|
||||
} else {
|
||||
fprintf(out, "%s%s\n", indent, start);
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
if (!*end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool printval(const struct mScriptValue* value, char* buffer, size_t bufferSize) {
|
||||
struct mScriptValue sval;
|
||||
switch (value->type->base) {
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
if (value->type->size <= 4) {
|
||||
snprintf(buffer, bufferSize, "%"PRId32, value->value.s32);
|
||||
return true;
|
||||
}
|
||||
if (value->type->size == 8) {
|
||||
snprintf(buffer, bufferSize, "%"PRId64, value->value.s64);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
if (value->type->size <= 4) {
|
||||
snprintf(buffer, bufferSize, "%"PRIu32, value->value.u32);
|
||||
return true;
|
||||
}
|
||||
if (value->type->size == 8) {
|
||||
snprintf(buffer, bufferSize, "%"PRIu64, value->value.u64);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case mSCRIPT_TYPE_FLOAT:
|
||||
if (value->type->size <= 4) {
|
||||
snprintf(buffer, bufferSize, "%g", value->value.f32);
|
||||
return true;
|
||||
}
|
||||
if (value->type->size == 8) {
|
||||
snprintf(buffer, bufferSize, "%g", value->value.f64);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case mSCRIPT_TYPE_STRING:
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_CHARP, value, &sval)) {
|
||||
return false;
|
||||
}
|
||||
if (sval.value.copaque) {
|
||||
snprintf(buffer, bufferSize, "\"%s\"", (const char*) sval.value.copaque);
|
||||
} else {
|
||||
snprintf(buffer, bufferSize, "null");
|
||||
}
|
||||
return true;
|
||||
case mSCRIPT_TYPE_VOID:
|
||||
snprintf(buffer, bufferSize, "null");
|
||||
return true;
|
||||
case mSCRIPT_TYPE_OPAQUE:
|
||||
case mSCRIPT_TYPE_LIST:
|
||||
case mSCRIPT_TYPE_TABLE:
|
||||
// Not scalar or string values
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void explainTable(struct mScriptValue* value, int level) {
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
|
||||
struct TableIterator iter;
|
||||
if (mScriptTableIteratorStart(value, &iter)) {
|
||||
do {
|
||||
char keyval[1024];
|
||||
struct mScriptValue* k = mScriptTableIteratorGetKey(value, &iter);
|
||||
printval(k, keyval, sizeof(keyval));
|
||||
fprintf(out, "%s- key: %s\n", indent, keyval);
|
||||
struct mScriptValue* v = mScriptTableIteratorGetValue(value, &iter);
|
||||
explainValue(v, level + 1);
|
||||
} while (mScriptTableIteratorNext(value, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
void explainClass(struct mScriptTypeClass* cls, int level) {
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
|
||||
if (cls->parent) {
|
||||
fprintf(out, "%sparent: %s\n", indent, cls->parent->name);
|
||||
}
|
||||
if (cls->docstring) {
|
||||
if (strchr(cls->docstring, '\n')) {
|
||||
fprintf(out, "%scomment: |-\n", indent);
|
||||
printchomp(cls->docstring, level + 1);
|
||||
} else {
|
||||
fprintf(out, "%scomment: \"%s\"\n", indent, cls->docstring);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out, "%smembers:\n", indent);
|
||||
const char* docstring = NULL;
|
||||
const struct mScriptClassInitDetails* details;
|
||||
size_t i;
|
||||
for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
|
||||
details = &cls->details[i];
|
||||
switch (details->type) {
|
||||
case mSCRIPT_CLASS_INIT_DOCSTRING:
|
||||
docstring = details->info.comment;
|
||||
break;
|
||||
case mSCRIPT_CLASS_INIT_INSTANCE_MEMBER:
|
||||
fprintf(out, "%s %s:\n", indent, details->info.member.name);
|
||||
if (docstring) {
|
||||
fprintf(out, "%s comment: \"%s\"\n", indent, docstring);
|
||||
docstring = NULL;
|
||||
}
|
||||
fprintf(out, "%s type: %s\n", indent, details->info.member.type->name);
|
||||
break;
|
||||
case mSCRIPT_CLASS_INIT_END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void explainObject(struct mScriptValue* value, int level) {
|
||||
char indent[(level + 2) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
|
||||
struct mScriptTypeClass* cls = value->type->details.cls;
|
||||
const struct mScriptClassInitDetails* details;
|
||||
size_t i;
|
||||
for (i = 0; cls->details[i].type != mSCRIPT_CLASS_INIT_END; ++i) {
|
||||
struct mScriptValue member;
|
||||
details = &cls->details[i];
|
||||
|
||||
if (cls->details[i].type != mSCRIPT_CLASS_INIT_INSTANCE_MEMBER) {
|
||||
continue;
|
||||
}
|
||||
fprintf(out, "%s%s:\n", indent, details->info.member.name);
|
||||
addType(details->info.member.type);
|
||||
if (mScriptObjectGet(value, details->info.member.name, &member)) {
|
||||
struct mScriptValue* unwrappedMember;
|
||||
if (member.type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
unwrappedMember = mScriptValueUnwrap(&member);
|
||||
explainValue(unwrappedMember, level + 2);
|
||||
} else {
|
||||
explainValue(&member, level + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void explainValue(struct mScriptValue* value, int level) {
|
||||
char valstring[1024];
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
value = mScriptContextAccessWeakref(&context, value);
|
||||
addType(value->type);
|
||||
fprintf(out, "%stype: %s\n", indent, value->type->name);
|
||||
switch (value->type->base) {
|
||||
case mSCRIPT_TYPE_TABLE:
|
||||
fprintf(out, "%svalue:\n", indent);
|
||||
explainTable(value, level);
|
||||
break;
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
case mSCRIPT_TYPE_STRING:
|
||||
printval(value, valstring, sizeof(valstring));
|
||||
fprintf(out, "%svalue: %s\n", indent, valstring);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OBJECT:
|
||||
fprintf(out, "%svalue:\n", indent);
|
||||
explainObject(value, level);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void explainTypeTuple(struct mScriptTypeTuple* tuple, int level) {
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
fprintf(out, "%svariable: %s\n", indent, tuple->variable ? "yes" : "no");
|
||||
fprintf(out, "%slist:\n", indent);
|
||||
size_t i;
|
||||
for (i = 0; i < tuple->count; ++i) {
|
||||
if (tuple->names[i]) {
|
||||
fprintf(out, "%s- name: %s\n", indent, tuple->names[i]);
|
||||
fprintf(out, "%s type: %s\n", indent, tuple->entries[i]->name);
|
||||
} else {
|
||||
fprintf(out, "%s- type: %s\n", indent, tuple->entries[i]->name);
|
||||
}
|
||||
if (tuple->defaults && tuple->defaults[i].type) {
|
||||
char defaultValue[128];
|
||||
printval(&tuple->defaults[i], defaultValue, sizeof(defaultValue));
|
||||
fprintf(out, "%s default: %s\n", indent, defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void explainType(struct mScriptType* type, int level) {
|
||||
char indent[(level + 1) * 2 + 1];
|
||||
memset(indent, ' ', sizeof(indent) - 1);
|
||||
indent[sizeof(indent) - 1] = '\0';
|
||||
fprintf(out, "%sbase: ", indent);
|
||||
switch (type->base) {
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
fputs("sint\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
fputs("uint\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_FLOAT:
|
||||
fputs("float\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_STRING:
|
||||
fputs("string\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_FUNCTION:
|
||||
fputs("function\n", out);
|
||||
fprintf(out, "%sparameters:\n", indent);
|
||||
explainTypeTuple(&type->details.function.parameters, level + 1);
|
||||
fprintf(out, "%sreturn:\n", indent);
|
||||
explainTypeTuple(&type->details.function.returnType, level + 1);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OPAQUE:
|
||||
fputs("opaque\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OBJECT:
|
||||
fputs("object\n", out);
|
||||
explainClass(type->details.cls, level);
|
||||
break;
|
||||
case mSCRIPT_TYPE_LIST:
|
||||
fputs("list\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_TABLE:
|
||||
fputs("table\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_WRAPPER:
|
||||
fputs("wrapper\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_WEAKREF:
|
||||
fputs("weakref\n", out);
|
||||
break;
|
||||
case mSCRIPT_TYPE_VOID:
|
||||
fputs("void\n", out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool call(struct mScriptValue* obj, const char* method, struct mScriptFrame* frame) {
|
||||
struct mScriptValue fn;
|
||||
if (!mScriptObjectGet(obj, method, &fn)) {
|
||||
return false;
|
||||
}
|
||||
struct mScriptValue* this = mScriptListAppend(&frame->arguments);
|
||||
this->type = mSCRIPT_TYPE_MS_WRAPPER;
|
||||
this->refs = mSCRIPT_VALUE_UNREF;
|
||||
this->flags = 0;
|
||||
this->value.opaque = obj;
|
||||
return mScriptInvoke(&fn, frame);
|
||||
}
|
||||
|
||||
void explainCore(struct mCore* core) {
|
||||
struct mScriptValue wrapper;
|
||||
size_t size;
|
||||
size_t i;
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu");
|
||||
addType(emu->type);
|
||||
|
||||
if (mScriptObjectGet(emu, "memory", &wrapper)) {
|
||||
struct mScriptValue* memory = mScriptValueUnwrap(&wrapper);
|
||||
struct TableIterator iter;
|
||||
fputs(" memory:\n", out);
|
||||
if (mScriptTableIteratorStart(memory, &iter)) {
|
||||
do {
|
||||
struct mScriptValue* name = mScriptTableIteratorGetKey(memory, &iter);
|
||||
struct mScriptValue* value = mScriptTableIteratorGetValue(memory, &iter);
|
||||
|
||||
fprintf(out, " %s:\n", name->value.string->buffer);
|
||||
value = mScriptContextAccessWeakref(&context, value);
|
||||
|
||||
struct mScriptFrame frame;
|
||||
uint32_t baseVal;
|
||||
struct mScriptValue* shortName;
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
call(value, "base", &frame);
|
||||
mScriptPopU32(&frame.returnValues, &baseVal);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
call(value, "name", &frame);
|
||||
shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.returnValues, 0));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
fprintf(out, " base: 0x%x\n", baseVal);
|
||||
fprintf(out, " name: \"%s\"\n", shortName->value.string->buffer);
|
||||
|
||||
mScriptValueDeref(shortName);
|
||||
} while (mScriptTableIteratorNext(memory, &iter));
|
||||
}
|
||||
}
|
||||
|
||||
const struct mCoreRegisterInfo* registers;
|
||||
size = core->listRegisters(core, ®isters);
|
||||
if (size) {
|
||||
fputs(" registers:\n", out);
|
||||
for (i = 0; i < size; ++i) {
|
||||
if (strncmp(registers[i].name, "spsr", 4) == 0) {
|
||||
// SPSR access is not implemented yet
|
||||
continue;
|
||||
}
|
||||
fprintf(out, " - name: \"%s\"\n", registers[i].name);
|
||||
if (registers[i].aliases && registers[i].aliases[0]) {
|
||||
size_t alias;
|
||||
fputs(" aliases:\n", out);
|
||||
for (alias = 0; registers[i].aliases[alias]; ++alias) {
|
||||
fprintf(out, " - \"%s\"\n", registers[i].aliases[alias]);
|
||||
}
|
||||
}
|
||||
fprintf(out, " width: %u\n", registers[i].width);
|
||||
}
|
||||
}
|
||||
mScriptContextDetachCore(&context);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
out = stdout;
|
||||
if (argc > 1) {
|
||||
out = fopen(argv[1], "w");
|
||||
if (!out) {
|
||||
perror("Couldn't open output");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
mScriptContextInit(&context);
|
||||
mScriptContextAttachStdlib(&context);
|
||||
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
||||
HashTableInit(&types, 0, NULL);
|
||||
|
||||
addType(mSCRIPT_TYPE_MS_S8);
|
||||
addType(mSCRIPT_TYPE_MS_U8);
|
||||
addType(mSCRIPT_TYPE_MS_S16);
|
||||
addType(mSCRIPT_TYPE_MS_U16);
|
||||
addType(mSCRIPT_TYPE_MS_S32);
|
||||
addType(mSCRIPT_TYPE_MS_U32);
|
||||
addType(mSCRIPT_TYPE_MS_F32);
|
||||
addType(mSCRIPT_TYPE_MS_S64);
|
||||
addType(mSCRIPT_TYPE_MS_U64);
|
||||
addType(mSCRIPT_TYPE_MS_F64);
|
||||
addType(mSCRIPT_TYPE_MS_STR);
|
||||
addType(mSCRIPT_TYPE_MS_CHARP);
|
||||
addType(mSCRIPT_TYPE_MS_LIST);
|
||||
addType(mSCRIPT_TYPE_MS_TABLE);
|
||||
addType(mSCRIPT_TYPE_MS_WRAPPER);
|
||||
|
||||
fputs("version:\n", out);
|
||||
fprintf(out, " string: \"%s\"\n", projectVersion);
|
||||
fprintf(out, " commit: \"%s\"\n", gitCommit);
|
||||
fputs("root:\n", out);
|
||||
struct TableIterator iter;
|
||||
if (HashTableIteratorStart(&context.rootScope, &iter)) {
|
||||
do {
|
||||
const char* name = HashTableIteratorGetKey(&context.rootScope, &iter);
|
||||
fprintf(out, " %s:\n", name);
|
||||
struct mScriptValue* value = HashTableIteratorGetValue(&context.rootScope, &iter);
|
||||
explainValue(value, 1);
|
||||
} while (HashTableIteratorNext(&context.rootScope, &iter));
|
||||
}
|
||||
fputs("emu:\n", out);
|
||||
struct mCore* core;
|
||||
core = mCoreCreate(mPLATFORM_GBA);
|
||||
if (core) {
|
||||
fputs(" gba:\n", out);
|
||||
core->init(core);
|
||||
explainCore(core);
|
||||
core->deinit(core);
|
||||
}
|
||||
core = mCoreCreate(mPLATFORM_GB);
|
||||
if (core) {
|
||||
fputs(" gb:\n", out);
|
||||
core->init(core);
|
||||
explainCore(core);
|
||||
core->deinit(core);
|
||||
}
|
||||
fputs("types:\n", out);
|
||||
if (HashTableIteratorStart(&types, &iter)) {
|
||||
do {
|
||||
const char* name = HashTableIteratorGetKey(&types, &iter);
|
||||
fprintf(out, " %s:\n", name);
|
||||
struct mScriptType* type = HashTableIteratorGetValue(&types, &iter);
|
||||
explainType(type, 1);
|
||||
} while (HashTableIteratorNext(&types, &iter));
|
||||
}
|
||||
|
||||
HashTableDeinit(&types);
|
||||
mScriptContextDeinit(&context);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,872 @@
|
|||
/* Copyright (c) 2013-2022 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/script/lua.h>
|
||||
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#define MAX_KEY_SIZE 128
|
||||
|
||||
static struct mScriptEngineContext* _luaCreate(struct mScriptEngine2*, struct mScriptContext*);
|
||||
|
||||
static void _luaDestroy(struct mScriptEngineContext*);
|
||||
static bool _luaIsScript(struct mScriptEngineContext*, const char*, struct VFile*);
|
||||
static struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext*, const char* name);
|
||||
static bool _luaSetGlobal(struct mScriptEngineContext*, const char* name, struct mScriptValue*);
|
||||
static bool _luaLoad(struct mScriptEngineContext*, const char*, struct VFile*);
|
||||
static bool _luaRun(struct mScriptEngineContext*);
|
||||
static const char* _luaGetError(struct mScriptEngineContext*);
|
||||
|
||||
static bool _luaCall(struct mScriptFrame*, void* context);
|
||||
|
||||
struct mScriptEngineContextLua;
|
||||
static bool _luaPushFrame(struct mScriptEngineContextLua*, struct mScriptList*, bool internal);
|
||||
static bool _luaPopFrame(struct mScriptEngineContextLua*, struct mScriptList*);
|
||||
static bool _luaInvoke(struct mScriptEngineContextLua*, struct mScriptFrame*);
|
||||
|
||||
static struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext);
|
||||
static bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue*);
|
||||
|
||||
static void _luaDeref(struct mScriptValue*);
|
||||
|
||||
static int _luaThunk(lua_State* lua);
|
||||
static int _luaGetObject(lua_State* lua);
|
||||
static int _luaSetObject(lua_State* lua);
|
||||
static int _luaGcObject(lua_State* lua);
|
||||
static int _luaGetTable(lua_State* lua);
|
||||
static int _luaLenTable(lua_State* lua);
|
||||
static int _luaPairsTable(lua_State* lua);
|
||||
static int _luaGetList(lua_State* lua);
|
||||
static int _luaLenList(lua_State* lua);
|
||||
|
||||
#if LUA_VERSION_NUM < 503
|
||||
#define lua_pushinteger lua_pushnumber
|
||||
#endif
|
||||
|
||||
const struct mScriptType mSTLuaFunc = {
|
||||
.base = mSCRIPT_TYPE_FUNCTION,
|
||||
.size = 0,
|
||||
.name = "lua-" LUA_VERSION_ONLY "::function",
|
||||
.details = {
|
||||
.function = {
|
||||
.parameters = {
|
||||
.variable = true
|
||||
},
|
||||
.returnType = {
|
||||
.variable = true
|
||||
}
|
||||
}
|
||||
},
|
||||
.alloc = NULL,
|
||||
.free = _luaDeref,
|
||||
.hash = NULL,
|
||||
.equal = NULL,
|
||||
.cast = NULL,
|
||||
};
|
||||
|
||||
struct mScriptEngineContextLua {
|
||||
struct mScriptEngineContext d;
|
||||
lua_State* lua;
|
||||
int func;
|
||||
char* lastError;
|
||||
};
|
||||
|
||||
struct mScriptEngineContextLuaRef {
|
||||
struct mScriptEngineContextLua* context;
|
||||
int ref;
|
||||
};
|
||||
|
||||
static struct mScriptEngineLua {
|
||||
struct mScriptEngine2 d;
|
||||
} _engineLua = {
|
||||
.d = {
|
||||
.name = "lua-" LUA_VERSION_ONLY,
|
||||
.init = NULL,
|
||||
.deinit = NULL,
|
||||
.create = _luaCreate
|
||||
}
|
||||
};
|
||||
|
||||
struct mScriptEngine2* const mSCRIPT_ENGINE_LUA = &_engineLua.d;
|
||||
|
||||
static const luaL_Reg _mSTStruct[] = {
|
||||
{ "__index", _luaGetObject },
|
||||
{ "__newindex", _luaSetObject },
|
||||
{ "__gc", _luaGcObject },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg _mSTTable[] = {
|
||||
{ "__index", _luaGetTable },
|
||||
{ "__len", _luaLenTable },
|
||||
{ "__pairs", _luaPairsTable },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg _mSTList[] = {
|
||||
{ "__index", _luaGetList },
|
||||
{ "__len", _luaLenList },
|
||||
{ "__gc", _luaGcObject },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mScriptContext* context) {
|
||||
UNUSED(engine);
|
||||
struct mScriptEngineContextLua* luaContext = calloc(1, sizeof(*luaContext));
|
||||
luaContext->d = (struct mScriptEngineContext) {
|
||||
.context = context,
|
||||
.destroy = _luaDestroy,
|
||||
.isScript = _luaIsScript,
|
||||
.getGlobal = _luaGetGlobal,
|
||||
.setGlobal = _luaSetGlobal,
|
||||
.load = _luaLoad,
|
||||
.run = _luaRun,
|
||||
.getError = _luaGetError
|
||||
};
|
||||
luaContext->lua = luaL_newstate();
|
||||
luaContext->func = -1;
|
||||
|
||||
luaL_openlibs(luaContext->lua);
|
||||
|
||||
luaL_newmetatable(luaContext->lua, "mSTStruct");
|
||||
#if LUA_VERSION_NUM < 502
|
||||
luaL_register(luaContext->lua, NULL, _mSTStruct);
|
||||
#else
|
||||
luaL_setfuncs(luaContext->lua, _mSTStruct, 0);
|
||||
#endif
|
||||
lua_pop(luaContext->lua, 1);
|
||||
|
||||
luaL_newmetatable(luaContext->lua, "mSTTable");
|
||||
#if LUA_VERSION_NUM < 502
|
||||
luaL_register(luaContext->lua, NULL, _mSTTable);
|
||||
#else
|
||||
luaL_setfuncs(luaContext->lua, _mSTTable, 0);
|
||||
#endif
|
||||
lua_pop(luaContext->lua, 1);
|
||||
|
||||
luaL_newmetatable(luaContext->lua, "mSTList");
|
||||
#if LUA_VERSION_NUM < 502
|
||||
luaL_register(luaContext->lua, NULL, _mSTList);
|
||||
#else
|
||||
luaL_setfuncs(luaContext->lua, _mSTList, 0);
|
||||
#endif
|
||||
lua_pop(luaContext->lua, 1);
|
||||
|
||||
return &luaContext->d;
|
||||
}
|
||||
|
||||
void _luaDestroy(struct mScriptEngineContext* ctx) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
|
||||
if (luaContext->lastError) {
|
||||
free(luaContext->lastError);
|
||||
luaContext->lastError = NULL;
|
||||
}
|
||||
if (luaContext->func > 0) {
|
||||
luaL_unref(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func);
|
||||
}
|
||||
lua_close(luaContext->lua);
|
||||
free(luaContext);
|
||||
}
|
||||
|
||||
bool _luaIsScript(struct mScriptEngineContext* ctx, const char* name, struct VFile* vf) {
|
||||
UNUSED(ctx);
|
||||
UNUSED(vf);
|
||||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
return endswith(name, ".lua");
|
||||
}
|
||||
|
||||
struct mScriptValue* _luaGetGlobal(struct mScriptEngineContext* ctx, const char* name) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
|
||||
lua_getglobal(luaContext->lua, name);
|
||||
return _luaCoerce(luaContext);
|
||||
}
|
||||
|
||||
bool _luaSetGlobal(struct mScriptEngineContext* ctx, const char* name, struct mScriptValue* value) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
|
||||
if (!value) {
|
||||
lua_pushnil(luaContext->lua);
|
||||
} else if (!_luaWrap(luaContext, value)) {
|
||||
return false;
|
||||
}
|
||||
lua_setglobal(luaContext->lua, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mScriptValue* _luaCoerceFunction(struct mScriptEngineContextLua* luaContext) {
|
||||
struct mScriptValue* value = mScriptValueAlloc(&mSTLuaFunc);
|
||||
struct mScriptFunction* fn = calloc(1, sizeof(*fn));
|
||||
struct mScriptEngineContextLuaRef* ref = calloc(1, sizeof(*ref));
|
||||
fn->call = _luaCall;
|
||||
fn->context = ref;
|
||||
ref->context = luaContext;
|
||||
ref->ref = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
||||
value->value.opaque = fn;
|
||||
return value;
|
||||
}
|
||||
|
||||
struct mScriptValue* _luaCoerce(struct mScriptEngineContextLua* luaContext) {
|
||||
if (lua_isnone(luaContext->lua, -1)) {
|
||||
lua_pop(luaContext->lua, 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
const void* buffer;
|
||||
struct mScriptValue* value = NULL;
|
||||
switch (lua_type(luaContext->lua, -1)) {
|
||||
case LUA_TNUMBER:
|
||||
#if LUA_VERSION_NUM >= 503
|
||||
if (lua_isinteger(luaContext->lua, -1)) {
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64);
|
||||
value->value.s64 = lua_tointeger(luaContext->lua, -1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_F64);
|
||||
value->value.f64 = lua_tonumber(luaContext->lua, -1);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||
value->value.s32 = lua_toboolean(luaContext->lua, -1);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
buffer = lua_tolstring(luaContext->lua, -1, &size);
|
||||
value = mScriptStringCreateFromBytes(buffer, size);
|
||||
mScriptContextFillPool(luaContext->d.context, value);
|
||||
break;
|
||||
case LUA_TFUNCTION:
|
||||
// This function pops the value internally via luaL_ref
|
||||
return _luaCoerceFunction(luaContext);
|
||||
case LUA_TUSERDATA:
|
||||
if (!lua_getmetatable(luaContext->lua, -1)) {
|
||||
break;
|
||||
}
|
||||
luaL_getmetatable(luaContext->lua, "mSTStruct");
|
||||
if (!lua_rawequal(luaContext->lua, -1, -2)) {
|
||||
lua_pop(luaContext->lua, 2);
|
||||
break;
|
||||
}
|
||||
lua_pop(luaContext->lua, 2);
|
||||
value = lua_touserdata(luaContext->lua, -1);
|
||||
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||
break;
|
||||
}
|
||||
lua_pop(luaContext->lua, 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* value) {
|
||||
if (!value) {
|
||||
lua_pushnil(luaContext->lua);
|
||||
return true;
|
||||
}
|
||||
uint32_t weakref;
|
||||
bool needsWeakref = false;
|
||||
if (value->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
value = mScriptValueUnwrap(value);
|
||||
if (!value) {
|
||||
lua_pushnil(luaContext->lua);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (value->type == mSCRIPT_TYPE_MS_WEAKREF) {
|
||||
weakref = value->value.u32;
|
||||
value = mScriptContextAccessWeakref(luaContext->d.context, value);
|
||||
if (!value) {
|
||||
lua_pushnil(luaContext->lua);
|
||||
return true;
|
||||
}
|
||||
needsWeakref = true;
|
||||
}
|
||||
bool ok = true;
|
||||
struct mScriptValue* newValue;
|
||||
switch (value->type->base) {
|
||||
case mSCRIPT_TYPE_SINT:
|
||||
if (value->type->size <= 4) {
|
||||
lua_pushinteger(luaContext->lua, value->value.s32);
|
||||
} else if (value->type->size == 8) {
|
||||
lua_pushinteger(luaContext->lua, value->value.s64);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case mSCRIPT_TYPE_UINT:
|
||||
if (value->type->size <= 4) {
|
||||
lua_pushinteger(luaContext->lua, value->value.u32);
|
||||
} else if (value->type->size == 8) {
|
||||
lua_pushinteger(luaContext->lua, value->value.u64);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case mSCRIPT_TYPE_FLOAT:
|
||||
if (value->type->size == 4) {
|
||||
lua_pushnumber(luaContext->lua, value->value.f32);
|
||||
} else if (value->type->size == 8) {
|
||||
lua_pushnumber(luaContext->lua, value->value.f64);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case mSCRIPT_TYPE_STRING:
|
||||
lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size);
|
||||
break;
|
||||
case mSCRIPT_TYPE_LIST:
|
||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||
if (needsWeakref) {
|
||||
*newValue = mSCRIPT_MAKE(WEAKREF, weakref);
|
||||
} else {
|
||||
mScriptValueWrap(value, newValue);
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
luaL_setmetatable(luaContext->lua, "mSTList");
|
||||
break;
|
||||
case mSCRIPT_TYPE_TABLE:
|
||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||
if (needsWeakref) {
|
||||
*newValue = mSCRIPT_MAKE(WEAKREF, weakref);
|
||||
} else {
|
||||
mScriptValueWrap(value, newValue);
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
luaL_setmetatable(luaContext->lua, "mSTTable");
|
||||
break;
|
||||
case mSCRIPT_TYPE_FUNCTION:
|
||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||
newValue->type = value->type;
|
||||
newValue->refs = mSCRIPT_VALUE_UNREF;
|
||||
newValue->type->alloc(newValue);
|
||||
lua_pushcclosure(luaContext->lua, _luaThunk, 1);
|
||||
mScriptValueDeref(value);
|
||||
break;
|
||||
case mSCRIPT_TYPE_OBJECT:
|
||||
newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue));
|
||||
if (needsWeakref) {
|
||||
*newValue = mSCRIPT_MAKE(WEAKREF, weakref);
|
||||
} else {
|
||||
mScriptValueWrap(value, newValue);
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
luaL_setmetatable(luaContext->lua, "mSTStruct");
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define LUA_BLOCKSIZE 0x1000
|
||||
struct mScriptEngineLuaReader {
|
||||
struct VFile* vf;
|
||||
char block[LUA_BLOCKSIZE];
|
||||
};
|
||||
|
||||
static const char* _reader(lua_State* lua, void* context, size_t* size) {
|
||||
UNUSED(lua);
|
||||
struct mScriptEngineLuaReader* reader = context;
|
||||
ssize_t s = reader->vf->read(reader->vf, reader->block, sizeof(reader->block));
|
||||
if (s < 0) {
|
||||
return NULL;
|
||||
}
|
||||
*size = s;
|
||||
return reader->block;
|
||||
}
|
||||
|
||||
bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFile* vf) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) ctx;
|
||||
struct mScriptEngineLuaReader data = {
|
||||
.vf = vf
|
||||
};
|
||||
if (luaContext->lastError) {
|
||||
free(luaContext->lastError);
|
||||
luaContext->lastError = NULL;
|
||||
}
|
||||
char name[80];
|
||||
if (filename) {
|
||||
if (*filename == '*') {
|
||||
snprintf(name, sizeof(name), "=%s", filename + 1);
|
||||
} else {
|
||||
const char* lastSlash = strrchr(filename, '/');
|
||||
const char* lastBackslash = strrchr(filename, '\\');
|
||||
if (lastSlash && lastBackslash) {
|
||||
if (lastSlash > lastBackslash) {
|
||||
filename = lastSlash + 1;
|
||||
} else {
|
||||
filename = lastBackslash + 1;
|
||||
}
|
||||
} else if (lastSlash) {
|
||||
filename = lastSlash + 1;
|
||||
} else if (lastBackslash) {
|
||||
filename = lastBackslash + 1;
|
||||
}
|
||||
snprintf(name, sizeof(name), "@%s", filename);
|
||||
}
|
||||
filename = name;
|
||||
}
|
||||
int ret = lua_load(luaContext->lua, _reader, &data, filename, "t");
|
||||
switch (ret) {
|
||||
case LUA_OK:
|
||||
luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
||||
return true;
|
||||
case LUA_ERRSYNTAX:
|
||||
luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1));
|
||||
lua_pop(luaContext->lua, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _luaRun(struct mScriptEngineContext* context) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context;
|
||||
lua_rawgeti(luaContext->lua, LUA_REGISTRYINDEX, luaContext->func);
|
||||
return _luaInvoke(luaContext, NULL);
|
||||
}
|
||||
|
||||
const char* _luaGetError(struct mScriptEngineContext* context) {
|
||||
struct mScriptEngineContextLua* luaContext = (struct mScriptEngineContextLua*) context;
|
||||
return luaContext->lastError;
|
||||
}
|
||||
|
||||
bool _luaPushFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame, bool internal) {
|
||||
bool ok = true;
|
||||
if (frame) {
|
||||
size_t i;
|
||||
for (i = 0; i < mScriptListSize(frame); ++i) {
|
||||
struct mScriptValue* value = mScriptListGetPointer(frame, i);
|
||||
if (internal && value->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
value = mScriptValueUnwrap(value);
|
||||
mScriptContextFillPool(luaContext->d.context, value);
|
||||
}
|
||||
if (!_luaWrap(luaContext, value)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
lua_pop(luaContext->lua, lua_gettop(luaContext->lua));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool _luaPopFrame(struct mScriptEngineContextLua* luaContext, struct mScriptList* frame) {
|
||||
int count = lua_gettop(luaContext->lua);
|
||||
bool ok = true;
|
||||
if (frame) {
|
||||
int i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
struct mScriptValue* value = _luaCoerce(luaContext);
|
||||
if (!value) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
mScriptValueWrap(value, mScriptListAppend(frame));
|
||||
mScriptValueDeref(value);
|
||||
}
|
||||
if (count > i) {
|
||||
lua_pop(luaContext->lua, count - i);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
for (i = 0; i < (ssize_t) (mScriptListSize(frame) / 2); ++i) {
|
||||
struct mScriptValue buffer;
|
||||
memcpy(&buffer, mScriptListGetPointer(frame, i), sizeof(buffer));
|
||||
memcpy(mScriptListGetPointer(frame, i), mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), sizeof(buffer));
|
||||
memcpy(mScriptListGetPointer(frame, mScriptListSize(frame) - i - 1), &buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool _luaCall(struct mScriptFrame* frame, void* context) {
|
||||
struct mScriptEngineContextLuaRef* ref = context;
|
||||
lua_rawgeti(ref->context->lua, LUA_REGISTRYINDEX, ref->ref);
|
||||
if (!_luaInvoke(ref->context, frame)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) {
|
||||
int nargs = 0;
|
||||
if (frame) {
|
||||
nargs = mScriptListSize(&frame->arguments);
|
||||
}
|
||||
|
||||
if (luaContext->lastError) {
|
||||
free(luaContext->lastError);
|
||||
luaContext->lastError = NULL;
|
||||
}
|
||||
|
||||
if (frame && !_luaPushFrame(luaContext, &frame->arguments, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pushliteral(luaContext->lua, "mCtx");
|
||||
lua_pushlightuserdata(luaContext->lua, luaContext);
|
||||
lua_rawset(luaContext->lua, LUA_REGISTRYINDEX);
|
||||
int ret = lua_pcall(luaContext->lua, nargs, LUA_MULTRET, 0);
|
||||
lua_pushliteral(luaContext->lua, "mCtx");
|
||||
lua_pushnil(luaContext->lua);
|
||||
lua_rawset(luaContext->lua, LUA_REGISTRYINDEX);
|
||||
|
||||
if (ret == LUA_ERRRUN) {
|
||||
luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1));
|
||||
lua_pop(luaContext->lua, 1);
|
||||
}
|
||||
if (ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame && !_luaPopFrame(luaContext, &frame->returnValues)) {
|
||||
mScriptContextDrainPool(luaContext->d.context);
|
||||
return false;
|
||||
}
|
||||
mScriptContextDrainPool(luaContext->d.context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _luaDeref(struct mScriptValue* value) {
|
||||
struct mScriptEngineContextLuaRef* ref;
|
||||
if (value->type->base == mSCRIPT_TYPE_FUNCTION) {
|
||||
struct mScriptFunction* function = value->value.opaque;
|
||||
ref = function->context;
|
||||
free(function);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
luaL_unref(ref->context->lua, LUA_REGISTRYINDEX, ref->ref);
|
||||
free(ref);
|
||||
}
|
||||
|
||||
static struct mScriptEngineContextLua* _luaGetContext(lua_State* lua) {
|
||||
lua_pushliteral(lua, "mCtx");
|
||||
int type = lua_rawget(lua, LUA_REGISTRYINDEX);
|
||||
if (type != LUA_TLIGHTUSERDATA) {
|
||||
lua_pop(lua, 1);
|
||||
lua_pushliteral(lua, "Function called from invalid context");
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
struct mScriptEngineContextLua* luaContext = lua_touserdata(lua, -1);
|
||||
lua_pop(lua, 1);
|
||||
if (luaContext->lua != lua) {
|
||||
lua_pushliteral(lua, "Function called from invalid context");
|
||||
lua_error(lua);
|
||||
}
|
||||
return luaContext;
|
||||
}
|
||||
|
||||
int _luaThunk(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
if (!_luaPopFrame(luaContext, &frame.arguments)) {
|
||||
mScriptContextDrainPool(luaContext->d.context);
|
||||
mScriptFrameDeinit(&frame);
|
||||
luaL_traceback(lua, lua, "Error calling function (translating arguments into runtime)", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
struct mScriptValue* fn = lua_touserdata(lua, lua_upvalueindex(1));
|
||||
if (!fn || !mScriptInvoke(fn, &frame)) {
|
||||
mScriptFrameDeinit(&frame);
|
||||
luaL_traceback(lua, lua, "Error calling function (invoking failed)", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!_luaPushFrame(luaContext, &frame.returnValues, true)) {
|
||||
mScriptFrameDeinit(&frame);
|
||||
luaL_traceback(lua, lua, "Error calling function (translating return values from runtime)", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
mScriptContextDrainPool(luaContext->d.context);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
return lua_gettop(luaContext->lua);
|
||||
}
|
||||
|
||||
int _luaGetObject(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
char key[MAX_KEY_SIZE];
|
||||
const char* keyPtr = lua_tostring(lua, -1);
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
||||
struct mScriptValue val;
|
||||
|
||||
if (!keyPtr) {
|
||||
lua_pop(lua, 2);
|
||||
luaL_traceback(lua, lua, "Invalid key", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
strlcpy(key, keyPtr, sizeof(key));
|
||||
lua_pop(lua, 2);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
luaL_traceback(lua, lua, "Invalid object", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!mScriptObjectGet(obj, key, &val)) {
|
||||
char error[MAX_KEY_SIZE + 16];
|
||||
snprintf(error, sizeof(error), "Invalid key '%s'", key);
|
||||
luaL_traceback(lua, lua, "Invalid key", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!_luaWrap(luaContext, &val)) {
|
||||
luaL_traceback(lua, lua, "Error translating value from runtime", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _luaSetObject(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
char key[MAX_KEY_SIZE];
|
||||
const char* keyPtr = lua_tostring(lua, -2);
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -3);
|
||||
struct mScriptValue* val = _luaCoerce(luaContext);
|
||||
|
||||
if (!keyPtr) {
|
||||
lua_pop(lua, 2);
|
||||
luaL_traceback(lua, lua, "Invalid key", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
strlcpy(key, keyPtr, sizeof(key));
|
||||
lua_pop(lua, 2);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
luaL_traceback(lua, lua, "Invalid object", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
luaL_traceback(lua, lua, "Error translating value to runtime", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!mScriptObjectSet(obj, key, val)) {
|
||||
mScriptValueDeref(val);
|
||||
char error[MAX_KEY_SIZE + 16];
|
||||
snprintf(error, sizeof(error), "Invalid key '%s'", key);
|
||||
luaL_traceback(lua, lua, "Invalid key", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
mScriptValueDeref(val);
|
||||
mScriptContextDrainPool(luaContext->d.context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _luaGcObject(lua_State* lua) {
|
||||
struct mScriptValue* val = lua_touserdata(lua, -1);
|
||||
val = mScriptValueUnwrap(val);
|
||||
if (!val) {
|
||||
return 0;
|
||||
}
|
||||
mScriptValueDeref(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _luaGetTable(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
char key[MAX_KEY_SIZE];
|
||||
int type = lua_type(luaContext->lua, -1);
|
||||
const char* keyPtr = NULL;
|
||||
int64_t intKey;
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
intKey = lua_tointeger(luaContext->lua, -1);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
keyPtr = lua_tostring(lua, -1);
|
||||
break;
|
||||
default:
|
||||
lua_pop(lua, 2);
|
||||
return 0;
|
||||
}
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
||||
if (keyPtr) {
|
||||
strlcpy(key, keyPtr, sizeof(key));
|
||||
}
|
||||
lua_pop(lua, 2);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
luaL_traceback(lua, lua, "Invalid table", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
struct mScriptValue keyVal;
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
keyVal = mSCRIPT_MAKE_S64(intKey);
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
keyVal = mSCRIPT_MAKE_CHARP(key);
|
||||
break;
|
||||
}
|
||||
struct mScriptValue* val = mScriptTableLookup(obj, &keyVal);
|
||||
if (!val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_luaWrap(luaContext, val)) {
|
||||
luaL_traceback(lua, lua, "Error translating value from runtime", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _luaLenTable(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -1);
|
||||
lua_pop(lua, 1);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (!obj) {
|
||||
luaL_traceback(lua, lua, "Invalid table", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
struct mScriptValue val = mSCRIPT_MAKE_U64(mScriptTableSize(obj));
|
||||
|
||||
if (!_luaWrap(luaContext, &val)) {
|
||||
luaL_traceback(lua, lua, "Error translating value from runtime", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _luaNextTable(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
char key[MAX_KEY_SIZE];
|
||||
int type = lua_type(luaContext->lua, -1);
|
||||
const char* keyPtr = NULL;
|
||||
struct mScriptValue keyVal = {0};
|
||||
switch (type) {
|
||||
case LUA_TNUMBER:
|
||||
keyVal = mSCRIPT_MAKE_S64(lua_tointeger(luaContext->lua, -1));
|
||||
break;
|
||||
case LUA_TSTRING:
|
||||
keyPtr = lua_tostring(lua, -1);
|
||||
break;
|
||||
}
|
||||
struct mScriptValue* table = lua_touserdata(lua, -2);
|
||||
if (keyPtr) {
|
||||
strlcpy(key, keyPtr, sizeof(key));
|
||||
keyVal = mSCRIPT_MAKE_CHARP(key);
|
||||
}
|
||||
lua_pop(lua, 2);
|
||||
|
||||
table = mScriptContextAccessWeakref(luaContext->d.context, table);
|
||||
if (!table) {
|
||||
luaL_traceback(lua, lua, "Invalid table", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
struct TableIterator iter;
|
||||
if (keyVal.type) {
|
||||
if (!mScriptTableIteratorLookup(table, &iter, &keyVal)) {
|
||||
return 0;
|
||||
}
|
||||
if (!mScriptTableIteratorNext(table, &iter)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!mScriptTableIteratorStart(table, &iter)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_luaWrap(luaContext, mScriptTableIteratorGetKey(table, &iter))) {
|
||||
luaL_traceback(lua, lua, "Iteration error", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
if (!_luaWrap(luaContext, mScriptTableIteratorGetValue(table, &iter))) {
|
||||
luaL_traceback(lua, lua, "Iteration error", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int _luaPairsTable(lua_State* lua) {
|
||||
lua_pushcfunction(lua, _luaNextTable);
|
||||
lua_insert(lua, -2);
|
||||
lua_pushnil(lua);
|
||||
return 3;
|
||||
}
|
||||
|
||||
int _luaGetList(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
ssize_t index;
|
||||
#if LUA_VERSION_NUM >= 503
|
||||
index = lua_tointeger(luaContext->lua, -1);
|
||||
#else
|
||||
index = lua_tonumber(luaContext->lua, -1);
|
||||
#endif
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -2);
|
||||
lua_pop(lua, 2);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (obj->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
obj = mScriptValueUnwrap(obj);
|
||||
}
|
||||
if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) {
|
||||
luaL_traceback(lua, lua, "Invalid list", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
struct mScriptList* list = obj->value.list;
|
||||
|
||||
// Lua indexes from 1
|
||||
if (index < 1) {
|
||||
luaL_traceback(lua, lua, "Invalid index", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
if ((size_t) index > mScriptListSize(list)) {
|
||||
return 0;
|
||||
}
|
||||
--index;
|
||||
|
||||
struct mScriptValue* val = mScriptListGetPointer(list, index);
|
||||
if (!_luaWrap(luaContext, val)) {
|
||||
luaL_traceback(lua, lua, "Error translating value from runtime", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _luaLenList(lua_State* lua) {
|
||||
struct mScriptEngineContextLua* luaContext = _luaGetContext(lua);
|
||||
struct mScriptValue* obj = lua_touserdata(lua, -1);
|
||||
lua_pop(lua, 1);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (obj->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
obj = mScriptValueUnwrap(obj);
|
||||
}
|
||||
if (!obj || obj->type != mSCRIPT_TYPE_MS_LIST) {
|
||||
luaL_traceback(lua, lua, "Invalid list", 1);
|
||||
lua_error(lua);
|
||||
}
|
||||
struct mScriptList* list = obj->value.list;
|
||||
lua_pushinteger(lua, mScriptListSize(list));
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* Copyright (c) 2013-2022 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/script/context.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/gb/input.h>
|
||||
#endif
|
||||
|
||||
struct mScriptCallbackManager {
|
||||
struct mScriptContext* context;
|
||||
};
|
||||
|
||||
static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) {
|
||||
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
fn = mScriptValueUnwrap(fn);
|
||||
}
|
||||
mScriptContextAddCallback(adapter->context, name->buffer, fn);
|
||||
mScriptValueDeref(fn);
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
"A global singleton object `callbacks` used for managing callbacks. The following callbacks are defined:\n\n"
|
||||
"- **alarm**: An in-game alarm went off\n"
|
||||
"- **crashed**: The emulation crashed\n"
|
||||
"- **frame**: The emulation finished a frame\n"
|
||||
"- **keysRead**: The emulation is about to read the key input\n"
|
||||
"- **reset**: The emulation has been reset\n"
|
||||
"- **savedataUpdated**: The emulation has just finished modifying save data\n"
|
||||
"- **sleep**: The emulation has used the sleep feature to enter a low-power mode\n"
|
||||
"- **shutdown**: The emulation has been powered off\n"
|
||||
"- **start**: The emulation has started\n"
|
||||
"- **stop**: The emulation has voluntarily shut down\n"
|
||||
)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
void mScriptContextAttachStdlib(struct mScriptContext* context) {
|
||||
struct mScriptValue* lib;
|
||||
|
||||
lib = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCallbackManager));
|
||||
lib->value.opaque = calloc(1, sizeof(struct mScriptCallbackManager));
|
||||
*(struct mScriptCallbackManager*) lib->value.opaque = (struct mScriptCallbackManager) {
|
||||
.context = context
|
||||
};
|
||||
lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
mScriptContextSetGlobal(context, "callbacks", lib);
|
||||
|
||||
mScriptContextExportConstants(context, "SAVESTATE", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, SCREENSHOT),
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, SAVEDATA),
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, CHEATS),
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, RTC),
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, METADATA),
|
||||
mSCRIPT_CONSTANT_PAIR(SAVESTATE, ALL),
|
||||
mSCRIPT_CONSTANT_SENTINEL
|
||||
});
|
||||
mScriptContextExportConstants(context, "PLATFORM", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_CONSTANT_PAIR(mPLATFORM, NONE),
|
||||
mSCRIPT_CONSTANT_PAIR(mPLATFORM, GBA),
|
||||
mSCRIPT_CONSTANT_PAIR(mPLATFORM, GB),
|
||||
mSCRIPT_CONSTANT_SENTINEL
|
||||
});
|
||||
mScriptContextExportConstants(context, "CHECKSUM", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_CONSTANT_PAIR(mCHECKSUM, CRC32),
|
||||
mSCRIPT_CONSTANT_SENTINEL
|
||||
});
|
||||
#ifdef M_CORE_GBA
|
||||
mScriptContextExportConstants(context, "GBA_KEY", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, A),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, B),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, SELECT),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, START),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, RIGHT),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, LEFT),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, UP),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, DOWN),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, R),
|
||||
mSCRIPT_CONSTANT_PAIR(GBA_KEY, L),
|
||||
mSCRIPT_CONSTANT_SENTINEL
|
||||
});
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
mScriptContextExportConstants(context, "GB_KEY", (struct mScriptKVPair[]) {
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, A),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, B),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, SELECT),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, START),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, RIGHT),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, LEFT),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, UP),
|
||||
mSCRIPT_CONSTANT_PAIR(GB_KEY, DOWN),
|
||||
mSCRIPT_CONSTANT_SENTINEL
|
||||
});
|
||||
#endif
|
||||
mScriptContextSetGlobal(context, "C", context->constants);
|
||||
}
|
|
@ -0,0 +1,985 @@
|
|||
/* Copyright (c) 2013-2022 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 "util/test/suite.h"
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
struct TestA {
|
||||
int32_t i;
|
||||
int32_t i2;
|
||||
int8_t b8;
|
||||
int16_t hUnaligned;
|
||||
int32_t (*ifn0)(struct TestA*);
|
||||
int32_t (*ifn1)(struct TestA*, int);
|
||||
void (*vfn0)(struct TestA*);
|
||||
void (*vfn1)(struct TestA*, int);
|
||||
int32_t (*icfn0)(const struct TestA*);
|
||||
int32_t (*icfn1)(const struct TestA*, int);
|
||||
};
|
||||
|
||||
struct TestB {
|
||||
struct TestA d;
|
||||
int32_t i3;
|
||||
};
|
||||
|
||||
struct TestC {
|
||||
int32_t i;
|
||||
};
|
||||
|
||||
struct TestD {
|
||||
struct TestC a;
|
||||
struct TestC b;
|
||||
};
|
||||
|
||||
struct TestE {
|
||||
};
|
||||
|
||||
struct TestF {
|
||||
int* ref;
|
||||
};
|
||||
|
||||
static int32_t testAi0(struct TestA* a) {
|
||||
return a->i;
|
||||
}
|
||||
|
||||
static int32_t testAi1(struct TestA* a, int b) {
|
||||
return a->i + b;
|
||||
}
|
||||
|
||||
static int32_t testAic0(const struct TestA* a) {
|
||||
return a->i;
|
||||
}
|
||||
|
||||
static int32_t testAic1(const struct TestA* a, int b) {
|
||||
return a->i + b;
|
||||
}
|
||||
|
||||
static void testAv0(struct TestA* a) {
|
||||
++a->i;
|
||||
}
|
||||
|
||||
static void testAv1(struct TestA* a, int b) {
|
||||
a->i += b;
|
||||
}
|
||||
|
||||
static void testAv2(struct TestA* a, int b, int c) {
|
||||
a->i += b + c;
|
||||
}
|
||||
|
||||
static int32_t testGet(struct TestE* e, const char* name) {
|
||||
UNUSED(e);
|
||||
return name[0];
|
||||
}
|
||||
|
||||
static void testDeinit(struct TestF* f) {
|
||||
++*f->ref;
|
||||
}
|
||||
|
||||
#define MEMBER_A_DOCSTRING "Member a"
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(TestA);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(TestA, S32, ifn1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(TestA, S32, icfn1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(TestA, vfn1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i0, testAi0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(TestA, S32, i1, testAi1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic0, testAic0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(TestA, S32, ic1, testAic1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v0, testAv0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestA, v1, testAv1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(TestA, v2, testAv2, 2, S32, b, S32, c);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestA)
|
||||
mSCRIPT_DEFINE_DOCSTRING(MEMBER_A_DOCSTRING)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, vfn1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, i0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, i1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ic1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(TestA, v2)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TestA, v2)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_MAKE_S32(0)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestB)
|
||||
mSCRIPT_DEFINE_INHERIT(TestA)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestB, S32, i3)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestC)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestC, S32, i)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestD)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), a)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(TestD, S(TestC), b)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(TestE);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(TestE, S32, _get, testGet, 1, CHARP, name);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestE)
|
||||
mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TestE)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(TestF);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(TestF, _deinit, testDeinit, 0);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(TestF)
|
||||
mSCRIPT_DEFINE_STRUCT_DEINIT(TestF)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
M_TEST_DEFINE(testALayout) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct mScriptClassMember* member;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "i");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "i");
|
||||
assert_string_equal(member->docstring, MEMBER_A_DOCSTRING);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(member->offset, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "i2");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "i2");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(member->offset, sizeof(int32_t));
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "b8");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "b8");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8);
|
||||
assert_int_equal(member->offset, sizeof(int32_t) * 2);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "hUnaligned");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "hUnaligned");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16);
|
||||
assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "unknown");
|
||||
assert_null(member);
|
||||
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testASignatures) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct mScriptClassMember* member;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "ifn0");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "ifn0");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA));
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "ifn1");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "ifn1");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 2);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA));
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32);
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_string_equal(member->type->details.function.parameters.names[1], "b");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "vfn0");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "vfn0");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA));
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "vfn1");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "vfn1");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 2);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA));
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32);
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_string_equal(member->type->details.function.parameters.names[1], "b");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "icfn0");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "icfn0");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_CS(TestA));
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "icfn1");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "icfn1");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 2);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_CS(TestA));
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[1], mSCRIPT_TYPE_MS_S32);
|
||||
assert_string_equal(member->type->details.function.parameters.names[0], "this");
|
||||
assert_string_equal(member->type->details.function.parameters.names[1], "b");
|
||||
assert_int_equal(member->type->details.function.returnType.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.returnType.entries[0], mSCRIPT_TYPE_MS_S32);
|
||||
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testAGet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
|
||||
struct TestA s = {
|
||||
.i = 1,
|
||||
.i2 = 2,
|
||||
.b8 = 3,
|
||||
.hUnaligned = 4
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s);
|
||||
struct mScriptValue val;
|
||||
struct mScriptValue compare;
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(mScriptObjectGet(&sval, "i", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectGet(&sval, "i2", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectGet(&sval, "b8", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectGet(&sval, "hUnaligned", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
assert_false(mScriptObjectGet(&sval, "unknown", &val));
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testASet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
|
||||
struct TestA s = {
|
||||
.i = 1,
|
||||
.i2 = 2,
|
||||
.b8 = 3,
|
||||
.hUnaligned = 4
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s);
|
||||
struct mScriptValue val;
|
||||
|
||||
val = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectSet(&sval, "i", &val));
|
||||
assert_int_equal(s.i, 2);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectSet(&sval, "i2", &val));
|
||||
assert_int_equal(s.i2, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectSet(&sval, "b8", &val));
|
||||
assert_int_equal(s.b8, 4);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_true(mScriptObjectSet(&sval, "hUnaligned", &val));
|
||||
assert_int_equal(s.hUnaligned, 5);
|
||||
|
||||
sval = mSCRIPT_MAKE_CS(TestA, &s);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(3);
|
||||
assert_false(mScriptObjectSet(&sval, "i", &val));
|
||||
assert_int_equal(s.i, 2);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_false(mScriptObjectSet(&sval, "i2", &val));
|
||||
assert_int_equal(s.i2, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_false(mScriptObjectSet(&sval, "b8", &val));
|
||||
assert_int_equal(s.b8, 4);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(6);
|
||||
assert_false(mScriptObjectSet(&sval, "hUnaligned", &val));
|
||||
assert_int_equal(s.hUnaligned, 5);
|
||||
|
||||
assert_false(mScriptObjectSet(&sval, "unknown", &val));
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testAStatic) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct TestA s = {
|
||||
.i = 1,
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s);
|
||||
struct mScriptValue val;
|
||||
struct mScriptFrame frame;
|
||||
int32_t rval;
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "i0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "i1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ic0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ic0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ic1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ic1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "v0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "i0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "ic0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "v1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "v2", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, -2);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "v2", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "i0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 4);
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "ic0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 4);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testADynamic) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct mScriptClassMember* member;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "ifn0");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "ifn0");
|
||||
assert_int_equal(member->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
assert_int_equal(member->type->details.function.parameters.count, 1);
|
||||
assert_ptr_equal(member->type->details.function.parameters.entries[0], mSCRIPT_TYPE_MS_S(TestA));
|
||||
|
||||
struct TestA s = {
|
||||
.i = 1,
|
||||
.ifn0 = testAi0,
|
||||
.ifn1 = testAi1,
|
||||
.icfn0 = testAic0,
|
||||
.icfn1 = testAic1,
|
||||
.vfn0 = testAv0,
|
||||
.vfn1 = testAv1,
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s);
|
||||
struct mScriptValue val;
|
||||
struct mScriptFrame frame;
|
||||
int32_t rval;
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ifn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "ifn1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "icfn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "icfn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "icfn1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "icfn1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "vfn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "ifn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "icfn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 2);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(mScriptObjectGet(&sval, "vfn1", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "ifn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 4);
|
||||
mScriptFrameDeinit(&frame);
|
||||
assert_true(mScriptObjectGet(&sval, "icfn0", &val));
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s);
|
||||
assert_true(mScriptInvoke(&val, &frame));
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &rval));
|
||||
assert_int_equal(rval, 4);
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testBLayout) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct mScriptClassMember* member;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "i");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "i");
|
||||
assert_string_equal(member->docstring, MEMBER_A_DOCSTRING);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(member->offset, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "i2");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "i2");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(member->offset, sizeof(int32_t));
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "b8");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "b8");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S8);
|
||||
assert_int_equal(member->offset, sizeof(int32_t) * 2);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "hUnaligned");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "hUnaligned");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16);
|
||||
assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1);
|
||||
size_t hOffset = member->offset;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "i3");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "i3");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_true(member->offset >= hOffset + sizeof(int16_t));
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "_super");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "_super");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestA));
|
||||
assert_int_equal(member->offset, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "unknown");
|
||||
assert_null(member);
|
||||
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testBGet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
|
||||
|
||||
struct TestB s = {
|
||||
.d = {
|
||||
.i = 1,
|
||||
.i2 = 2,
|
||||
.b8 = 3,
|
||||
.hUnaligned = 4
|
||||
},
|
||||
.i3 = 5
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s);
|
||||
struct mScriptValue super;
|
||||
struct mScriptValue val;
|
||||
struct mScriptValue compare;
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(mScriptObjectGet(&sval, "i", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectGet(&sval, "i2", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectGet(&sval, "b8", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectGet(&sval, "hUnaligned", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(5);
|
||||
assert_true(mScriptObjectGet(&sval, "i3", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
// Superclass explicit access
|
||||
assert_true(mScriptObjectGet(&sval, "_super", &super));
|
||||
assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(mScriptObjectGet(&super, "i", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectGet(&super, "i2", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectGet(&super, "b8", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectGet(&super, "hUnaligned", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
assert_false(mScriptObjectGet(&super, "i3", &val));
|
||||
|
||||
// Test const-correctness
|
||||
sval = mSCRIPT_MAKE_CS(TestB, &s);
|
||||
assert_true(mScriptObjectGet(&sval, "_super", &super));
|
||||
assert_true(super.type == mSCRIPT_TYPE_MS_CS(TestA));
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testBSet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestB)->details.cls;
|
||||
|
||||
struct TestB s = {
|
||||
.d = {
|
||||
.i = 1,
|
||||
.i2 = 2,
|
||||
.b8 = 3,
|
||||
.hUnaligned = 4
|
||||
},
|
||||
.i3 = 5
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestB, &s);
|
||||
struct mScriptValue super;
|
||||
struct mScriptValue val;
|
||||
|
||||
val = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectSet(&sval, "i", &val));
|
||||
assert_int_equal(s.d.i, 2);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectSet(&sval, "i2", &val));
|
||||
assert_int_equal(s.d.i2, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectSet(&sval, "b8", &val));
|
||||
assert_int_equal(s.d.b8, 4);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_true(mScriptObjectSet(&sval, "hUnaligned", &val));
|
||||
assert_int_equal(s.d.hUnaligned, 5);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(6);
|
||||
assert_true(mScriptObjectSet(&sval, "i3", &val));
|
||||
assert_int_equal(s.i3, 6);
|
||||
|
||||
// Superclass explicit access
|
||||
assert_true(mScriptObjectGet(&sval, "_super", &super));
|
||||
assert_true(super.type == mSCRIPT_TYPE_MS_S(TestA));
|
||||
|
||||
val = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectSet(&super, "i", &val));
|
||||
assert_int_equal(s.d.i, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectSet(&super, "i2", &val));
|
||||
assert_int_equal(s.d.i2, 4);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_true(mScriptObjectSet(&super, "b8", &val));
|
||||
assert_int_equal(s.d.b8, 5);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(6);
|
||||
assert_true(mScriptObjectSet(&super, "hUnaligned", &val));
|
||||
assert_int_equal(s.d.hUnaligned, 6);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(7);
|
||||
assert_false(mScriptObjectSet(&super, "i3", &val));
|
||||
assert_int_equal(s.i3, 6);
|
||||
|
||||
// Const access
|
||||
sval = mSCRIPT_MAKE_CS(TestB, &s);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_false(mScriptObjectSet(&sval, "i", &val));
|
||||
assert_int_equal(s.d.i, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_false(mScriptObjectSet(&sval, "i2", &val));
|
||||
assert_int_equal(s.d.i2, 4);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(6);
|
||||
assert_false(mScriptObjectSet(&sval, "b8", &val));
|
||||
assert_int_equal(s.d.b8, 5);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(7);
|
||||
assert_false(mScriptObjectSet(&sval, "hUnaligned", &val));
|
||||
assert_int_equal(s.d.hUnaligned, 6);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(8);
|
||||
assert_false(mScriptObjectSet(&sval, "i3", &val));
|
||||
assert_int_equal(s.i3, 6);
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testDLayout) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
|
||||
assert_false(cls->init);
|
||||
mScriptClassInit(cls);
|
||||
assert_true(cls->init);
|
||||
|
||||
struct mScriptClassMember* member;
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "a");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "a");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC));
|
||||
assert_int_equal(member->offset, 0);
|
||||
|
||||
member = HashTableLookup(&cls->instanceMembers, "b");
|
||||
assert_non_null(member);
|
||||
assert_string_equal(member->name, "b");
|
||||
assert_null(member->docstring);
|
||||
assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S(TestC));
|
||||
assert_int_equal(member->offset, sizeof(struct TestC));
|
||||
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testDGet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
|
||||
|
||||
struct TestD s = {
|
||||
.a = { 1 },
|
||||
.b = { 2 },
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s);
|
||||
struct mScriptValue val;
|
||||
struct mScriptValue member;
|
||||
struct mScriptValue compare;
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(mScriptObjectGet(&sval, "a", &member));
|
||||
assert_true(mScriptObjectGet(&member, "i", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectGet(&sval, "b", &member));
|
||||
assert_true(mScriptObjectGet(&member, "i", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testDSet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestD)->details.cls;
|
||||
|
||||
struct TestD s = {
|
||||
.a = { 1 },
|
||||
.b = { 2 },
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestD, &s);
|
||||
struct mScriptValue member;
|
||||
struct mScriptValue val;
|
||||
|
||||
val = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(mScriptObjectGet(&sval, "a", &member));
|
||||
assert_true(mScriptObjectSet(&member, "i", &val));
|
||||
assert_int_equal(s.a.i, 2);
|
||||
assert_int_equal(s.b.i, 2);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(3);
|
||||
assert_true(mScriptObjectGet(&sval, "b", &member));
|
||||
assert_true(mScriptObjectSet(&member, "i", &val));
|
||||
assert_int_equal(s.a.i, 2);
|
||||
assert_int_equal(s.b.i, 3);
|
||||
|
||||
sval = mSCRIPT_MAKE_CS(TestD, &s);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(4);
|
||||
assert_true(mScriptObjectGet(&sval, "a", &member));
|
||||
assert_false(mScriptObjectSet(&member, "i", &val));
|
||||
assert_int_equal(s.a.i, 2);
|
||||
assert_int_equal(s.b.i, 3);
|
||||
|
||||
val = mSCRIPT_MAKE_S32(5);
|
||||
assert_true(mScriptObjectGet(&sval, "b", &member));
|
||||
assert_false(mScriptObjectSet(&member, "i", &val));
|
||||
assert_int_equal(s.a.i, 2);
|
||||
assert_int_equal(s.b.i, 3);
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testEGet) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestE)->details.cls;
|
||||
|
||||
struct TestE s = {
|
||||
};
|
||||
|
||||
struct mScriptValue sval = mSCRIPT_MAKE_S(TestE, &s);
|
||||
struct mScriptValue val;
|
||||
struct mScriptValue compare;
|
||||
|
||||
compare = mSCRIPT_MAKE_S32('a');
|
||||
assert_true(mScriptObjectGet(&sval, "a", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
compare = mSCRIPT_MAKE_S32('b');
|
||||
assert_true(mScriptObjectGet(&sval, "b", &val));
|
||||
assert_true(compare.type->equal(&compare, &val));
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(testFDeinit) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestF)->details.cls;
|
||||
|
||||
int ref = 0;
|
||||
struct TestF* s = calloc(1, sizeof(struct TestF));
|
||||
s->ref = &ref;
|
||||
struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(TestF));
|
||||
val->value.opaque = s;
|
||||
val->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
mScriptValueDeref(val);
|
||||
assert_int_equal(ref, 1);
|
||||
|
||||
assert_true(cls->init);
|
||||
mScriptClassDeinit(cls);
|
||||
assert_false(cls->init);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE(mScriptClasses,
|
||||
cmocka_unit_test(testALayout),
|
||||
cmocka_unit_test(testASignatures),
|
||||
cmocka_unit_test(testAGet),
|
||||
cmocka_unit_test(testASet),
|
||||
cmocka_unit_test(testAStatic),
|
||||
cmocka_unit_test(testADynamic),
|
||||
cmocka_unit_test(testBLayout),
|
||||
cmocka_unit_test(testBGet),
|
||||
cmocka_unit_test(testBSet),
|
||||
cmocka_unit_test(testDLayout),
|
||||
cmocka_unit_test(testDGet),
|
||||
cmocka_unit_test(testDSet),
|
||||
cmocka_unit_test(testEGet),
|
||||
cmocka_unit_test(testFDeinit),
|
||||
)
|
|
@ -0,0 +1,637 @@
|
|||
/* Copyright (c) 2013-2022 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 "util/test/suite.h"
|
||||
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/macros.h>
|
||||
|
||||
#define SETUP_LUA \
|
||||
struct mScriptContext context; \
|
||||
mScriptContextInit(&context); \
|
||||
struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA)
|
||||
|
||||
#define LOAD_PROGRAM(PROG) \
|
||||
do { \
|
||||
struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \
|
||||
assert_true(lua->load(lua, NULL, vf)); \
|
||||
vf->close(vf); \
|
||||
} while(0)
|
||||
|
||||
#define TEST_PROGRAM(PROG) \
|
||||
LOAD_PROGRAM(PROG); \
|
||||
assert_true(lua->run(lua)); \
|
||||
|
||||
struct Test {
|
||||
int32_t i;
|
||||
int32_t (*ifn0)(struct Test*);
|
||||
int32_t (*ifn1)(struct Test*, int);
|
||||
void (*vfn0)(struct Test*);
|
||||
void (*vfn1)(struct Test*, int);
|
||||
int32_t (*icfn0)(const struct Test*);
|
||||
};
|
||||
|
||||
static int identityInt(int in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
static int addInts(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
static int32_t testI0(struct Test* a) {
|
||||
return a->i;
|
||||
}
|
||||
|
||||
static int32_t testI1(struct Test* a, int b) {
|
||||
return a->i + b;
|
||||
}
|
||||
|
||||
static int32_t testIC0(const struct Test* a) {
|
||||
return a->i;
|
||||
}
|
||||
|
||||
static void testV0(struct Test* a) {
|
||||
++a->i;
|
||||
}
|
||||
|
||||
static void testV1(struct Test* a, int b) {
|
||||
a->i += b;
|
||||
}
|
||||
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, a);
|
||||
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b);
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(Test);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_D_METHOD(Test, S32, ifn1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_CD_METHOD(Test, S32, icfn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(Test, vfn1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i0, testI0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(Test, S32, i1, testI1, 1, S32, b);
|
||||
mSCRIPT_DECLARE_STRUCT_C_METHOD(Test, S32, ic0, testIC0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v0, testV0, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32, b);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(Test)
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER(Test, S32, i)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, icfn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, vfn0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, vfn1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, i0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, i1)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, ic0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, v0)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(Test, v1)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
M_TEST_SUITE_SETUP(mScriptLua) {
|
||||
if (mSCRIPT_ENGINE_LUA->init) {
|
||||
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_SUITE_TEARDOWN(mScriptLua) {
|
||||
if (mSCRIPT_ENGINE_LUA->deinit) {
|
||||
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(create) {
|
||||
struct mScriptContext context;
|
||||
mScriptContextInit(&context);
|
||||
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
|
||||
lua->destroy(lua);
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(loadGood) {
|
||||
struct mScriptContext context;
|
||||
mScriptContextInit(&context);
|
||||
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
|
||||
|
||||
const char* program = "-- test\n";
|
||||
struct VFile* vf = VFileFromConstMemory(program, strlen(program));
|
||||
assert_true(lua->load(lua, NULL, vf));
|
||||
|
||||
lua->destroy(lua);
|
||||
mScriptContextDeinit(&context);
|
||||
vf->close(vf);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(loadBadSyntax) {
|
||||
struct mScriptContext context;
|
||||
mScriptContextInit(&context);
|
||||
struct mScriptEngineContext* lua = mSCRIPT_ENGINE_LUA->create(mSCRIPT_ENGINE_LUA, &context);
|
||||
|
||||
const char* program = "Invalid syntax! )\n";
|
||||
struct VFile* vf = VFileFromConstMemory(program, strlen(program));
|
||||
assert_false(lua->load(lua, NULL, vf));
|
||||
|
||||
lua->destroy(lua);
|
||||
mScriptContextDeinit(&context);
|
||||
vf->close(vf);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(runNop) {
|
||||
SETUP_LUA;
|
||||
|
||||
LOAD_PROGRAM("return");
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
// Make sure we can run it twice
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(getGlobal) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct mScriptValue a = mSCRIPT_MAKE_S32(1);
|
||||
struct mScriptValue* val;
|
||||
|
||||
TEST_PROGRAM("a = 1");
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
TEST_PROGRAM("b = 1");
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_S32(2);
|
||||
TEST_PROGRAM("a = 2");
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_S32(3);
|
||||
TEST_PROGRAM("b = a + b");
|
||||
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(setGlobal) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct mScriptValue a = mSCRIPT_MAKE_S32(1);
|
||||
struct mScriptValue* val;
|
||||
|
||||
LOAD_PROGRAM("a = b");
|
||||
assert_true(lua->setGlobal(lua, "b", &a));
|
||||
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
a = mSCRIPT_MAKE_S32(2);
|
||||
assert_false(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_false(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
assert_true(lua->setGlobal(lua, "b", &a));
|
||||
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
assert_true(lua->setGlobal(lua, "b", NULL));
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_null(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(callLuaFunc) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct mScriptValue* fn;
|
||||
|
||||
TEST_PROGRAM("function a(b) return b + 1 end; function c(d, e) return d + e end");
|
||||
assert_null(lua->getError(lua));
|
||||
|
||||
fn = lua->getGlobal(lua, "a");
|
||||
assert_non_null(fn);
|
||||
assert_int_equal(fn->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(fn, &frame));
|
||||
int64_t val;
|
||||
assert_true(mScriptPopS64(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 2);
|
||||
|
||||
mScriptFrameDeinit(&frame);
|
||||
mScriptValueDeref(fn);
|
||||
|
||||
fn = lua->getGlobal(lua, "c");
|
||||
assert_non_null(fn);
|
||||
assert_int_equal(fn->type->base, mSCRIPT_TYPE_FUNCTION);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||
assert_true(mScriptInvoke(fn, &frame));
|
||||
assert_true(mScriptPopS64(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 3);
|
||||
|
||||
mScriptFrameDeinit(&frame);
|
||||
mScriptValueDeref(fn);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(callCFunc) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct mScriptValue a = mSCRIPT_MAKE_S32(1);
|
||||
struct mScriptValue* val;
|
||||
|
||||
assert_true(lua->setGlobal(lua, "b", &boundIdentityInt));
|
||||
assert_true(lua->setGlobal(lua, "d", &boundAddInts));
|
||||
TEST_PROGRAM("a = b(1); c = d(1, 2)");
|
||||
assert_null(lua->getError(lua));
|
||||
|
||||
val = lua->getGlobal(lua, "a");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_S32(3);
|
||||
val = lua->getGlobal(lua, "c");
|
||||
assert_non_null(val);
|
||||
assert_true(a.type->equal(&a, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(globalStructFieldGet) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct Test s = {
|
||||
.i = 1,
|
||||
};
|
||||
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
struct mScriptValue* val;
|
||||
|
||||
LOAD_PROGRAM("b = a.i");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
s.i = 2;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(globalStructFieldSet) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct Test s = {
|
||||
.i = 1,
|
||||
};
|
||||
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
LOAD_PROGRAM("a.i = b");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(lua->setGlobal(lua, "b", &b));
|
||||
assert_true(lua->run(lua));
|
||||
assert_int_equal(s.i, 2);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(lua->setGlobal(lua, "b", &b));
|
||||
assert_false(lua->run(lua));
|
||||
assert_int_equal(s.i, 1);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
|
||||
M_TEST_DEFINE(globalStructMethods) {
|
||||
SETUP_LUA;
|
||||
|
||||
struct Test s = {
|
||||
.i = 1,
|
||||
.ifn0 = testI0,
|
||||
.ifn1 = testI1,
|
||||
.icfn0 = testIC0,
|
||||
.vfn0 = testV0,
|
||||
.vfn1 = testV1,
|
||||
};
|
||||
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
struct mScriptValue* val;
|
||||
|
||||
// ifn0
|
||||
LOAD_PROGRAM("b = a:ifn0()");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
// ifn1
|
||||
LOAD_PROGRAM("b = a:ifn1(c)");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(lua->setGlobal(lua, "c", &b));
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(lua->setGlobal(lua, "c", &b));
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(3);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
// vfn0
|
||||
LOAD_PROGRAM("a:vfn0()");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
assert_int_equal(s.i, 2);
|
||||
assert_true(lua->run(lua));
|
||||
assert_int_equal(s.i, 3);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
// vfn1
|
||||
LOAD_PROGRAM("a:vfn1(c)");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(lua->setGlobal(lua, "c", &b));
|
||||
assert_true(lua->run(lua));
|
||||
assert_int_equal(s.i, 2);
|
||||
b = mSCRIPT_MAKE_S32(2);
|
||||
assert_true(lua->setGlobal(lua, "c", &b));
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
assert_int_equal(s.i, 3);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
assert_true(lua->setGlobal(lua, "c", &b));
|
||||
assert_false(lua->run(lua));
|
||||
assert_int_equal(s.i, 1);
|
||||
|
||||
// icfn0
|
||||
LOAD_PROGRAM("b = a:icfn0()");
|
||||
|
||||
a = mSCRIPT_MAKE_S(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
a = mSCRIPT_MAKE_CS(Test, &s);
|
||||
assert_true(lua->setGlobal(lua, "a", &a));
|
||||
s.i = 1;
|
||||
assert_true(lua->run(lua));
|
||||
b = mSCRIPT_MAKE_S32(1);
|
||||
val = lua->getGlobal(lua, "b");
|
||||
assert_non_null(val);
|
||||
assert_true(b.type->equal(&b, val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(errorReporting) {
|
||||
SETUP_LUA;
|
||||
|
||||
assert_null(lua->getError(lua));
|
||||
|
||||
LOAD_PROGRAM("assert(false)");
|
||||
|
||||
assert_false(lua->run(lua));
|
||||
const char* errorBuffer = lua->getError(lua);
|
||||
assert_non_null(errorBuffer);
|
||||
assert_non_null(strstr(errorBuffer, "assertion failed"));
|
||||
|
||||
LOAD_PROGRAM("assert(true)");
|
||||
|
||||
assert_true(lua->run(lua));
|
||||
assert_null(lua->getError(lua));
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(tableLookup) {
|
||||
SETUP_LUA;
|
||||
|
||||
assert_null(lua->getError(lua));
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
assert_non_null(table);
|
||||
struct mScriptValue* val;
|
||||
|
||||
mScriptContextSetGlobal(&context, "t", table);
|
||||
|
||||
val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64);
|
||||
val->value.s64 = 0;
|
||||
assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_S64(0), val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
val = mScriptStringCreateFromASCII("t");
|
||||
assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("t"), val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
val = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("sub"), val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
table = val;
|
||||
val = mScriptStringCreateFromASCII("t");
|
||||
assert_true(mScriptTableInsert(table, &mSCRIPT_MAKE_CHARP("t"), val));
|
||||
mScriptValueDeref(val);
|
||||
|
||||
TEST_PROGRAM("assert(t)");
|
||||
TEST_PROGRAM("assert(t['t'] ~= nil)");
|
||||
TEST_PROGRAM("assert(t['t'] == 't')");
|
||||
TEST_PROGRAM("assert(t.t == 't')");
|
||||
TEST_PROGRAM("assert(t['x'] == nil)");
|
||||
TEST_PROGRAM("assert(t.x == nil)");
|
||||
TEST_PROGRAM("assert(t.sub ~= nil)");
|
||||
TEST_PROGRAM("assert(t.sub.t ~= nil)");
|
||||
TEST_PROGRAM("assert(t.sub.t == 't')");
|
||||
TEST_PROGRAM("assert(t[0] ~= nil)");
|
||||
TEST_PROGRAM("assert(t[0] == 0)");
|
||||
TEST_PROGRAM("assert(t[1] == nil)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(tableIterate) {
|
||||
SETUP_LUA;
|
||||
|
||||
assert_null(lua->getError(lua));
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
assert_non_null(table);
|
||||
struct mScriptValue* val;
|
||||
struct mScriptValue* key;
|
||||
|
||||
mScriptContextSetGlobal(&context, "t", table);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 50; ++i) {
|
||||
val = mScriptValueAlloc(mSCRIPT_TYPE_MS_S64);
|
||||
val->value.s64 = 1LL << i;
|
||||
key = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||
key->value.s32 = i;
|
||||
assert_true(mScriptTableInsert(table, key, val));
|
||||
mScriptValueDeref(key);
|
||||
mScriptValueDeref(val);
|
||||
}
|
||||
assert_int_equal(mScriptTableSize(table), 50);
|
||||
|
||||
TEST_PROGRAM("assert(t)");
|
||||
TEST_PROGRAM("assert(#t == 50)");
|
||||
TEST_PROGRAM(
|
||||
"i = 0\n"
|
||||
"z = 0\n"
|
||||
"for k, v in pairs(t) do\n"
|
||||
" i = i + 1\n"
|
||||
" z = z + v\n"
|
||||
" assert((1 << k) == v)\n"
|
||||
"end\n"
|
||||
);
|
||||
|
||||
TEST_PROGRAM("assert(i == #t)");
|
||||
TEST_PROGRAM("assert(z == (1 << #t) - 1)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua,
|
||||
cmocka_unit_test(create),
|
||||
cmocka_unit_test(loadGood),
|
||||
cmocka_unit_test(loadBadSyntax),
|
||||
cmocka_unit_test(runNop),
|
||||
cmocka_unit_test(getGlobal),
|
||||
cmocka_unit_test(setGlobal),
|
||||
cmocka_unit_test(callLuaFunc),
|
||||
cmocka_unit_test(callCFunc),
|
||||
cmocka_unit_test(globalStructFieldGet),
|
||||
cmocka_unit_test(globalStructFieldSet),
|
||||
cmocka_unit_test(globalStructMethods),
|
||||
cmocka_unit_test(errorReporting),
|
||||
cmocka_unit_test(tableLookup),
|
||||
cmocka_unit_test(tableIterate),
|
||||
)
|
|
@ -0,0 +1,951 @@
|
|||
/* Copyright (c) 2013-2021 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 "util/test/suite.h"
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
struct Test {
|
||||
int32_t a;
|
||||
};
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(Test)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
static int voidOne(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void discard(int ignored) {
|
||||
UNUSED(ignored);
|
||||
}
|
||||
|
||||
static int identityInt(int in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
static int64_t identityInt64(int64_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
static float identityFloat(float in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
static struct Test* identityStruct(struct Test* t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
static int addInts(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
static int subInts(int a, int b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static int isHello(const char* str) {
|
||||
return strcmp(str, "hello") == 0;
|
||||
}
|
||||
|
||||
mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0);
|
||||
mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored);
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in);
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityInt64, S64, identityInt64, 1, S64, in);
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityFloat, F32, identityFloat, 1, F32, in);
|
||||
mSCRIPT_BIND_FUNCTION(boundIdentityStruct, S(Test), identityStruct, 1, S(Test), t);
|
||||
mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b);
|
||||
mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b);
|
||||
mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str);
|
||||
|
||||
M_TEST_DEFINE(voidArgs) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
assert_true(mScriptInvoke(&boundVoidOne, &frame));
|
||||
int32_t val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(voidFunc) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&boundDiscard, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(identityFunctionS32) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&boundIdentityInt, &frame));
|
||||
int32_t val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(identityFunctionS64) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S64, 1);
|
||||
assert_true(mScriptInvoke(&boundIdentityInt64, &frame));
|
||||
int64_t val;
|
||||
assert_true(mScriptPopS64(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(identityFunctionF32) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F32, 3.125f);
|
||||
assert_true(mScriptInvoke(&boundIdentityFloat, &frame));
|
||||
float val;
|
||||
assert_true(mScriptPopF32(&frame.returnValues, &val));
|
||||
assert_float_equal(val, 3.125f, 0.f);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(identityFunctionStruct) {
|
||||
struct mScriptFrame frame;
|
||||
struct Test v = {};
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &v);
|
||||
assert_true(mScriptInvoke(&boundIdentityStruct, &frame));
|
||||
struct Test* val;
|
||||
assert_true(mScriptPopPointer(&frame.returnValues, (void**) &val));
|
||||
assert_ptr_equal(val, &v);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(addS32) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||
assert_true(mScriptInvoke(&boundAddInts, &frame));
|
||||
int32_t val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 3);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(subS32) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&boundSubInts, &frame));
|
||||
int32_t val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongArgCountLo) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
assert_false(mScriptInvoke(&boundIdentityInt, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongArgCountHi) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_false(mScriptInvoke(&boundIdentityInt, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongArgType) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_false(mScriptInvoke(&boundIdentityStruct, &frame));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongPopType) {
|
||||
struct mScriptFrame frame;
|
||||
int32_t s32;
|
||||
int64_t s64;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
float f32;
|
||||
double f64;
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 0);
|
||||
assert_false(mScriptPopU32(&frame.arguments, &u32));
|
||||
assert_false(mScriptPopF32(&frame.arguments, &f32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S64, 0);
|
||||
assert_false(mScriptPopU64(&frame.arguments, &u64));
|
||||
assert_false(mScriptPopF64(&frame.arguments, &f64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, U32, 0);
|
||||
assert_false(mScriptPopS32(&frame.arguments, &s32));
|
||||
assert_false(mScriptPopF32(&frame.arguments, &f32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, U64, 0);
|
||||
assert_false(mScriptPopS64(&frame.arguments, &s64));
|
||||
assert_false(mScriptPopF64(&frame.arguments, &f64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F32, 0);
|
||||
assert_false(mScriptPopS32(&frame.arguments, &s32));
|
||||
assert_false(mScriptPopU32(&frame.arguments, &u32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F64, 0);
|
||||
assert_false(mScriptPopS64(&frame.arguments, &s64));
|
||||
assert_false(mScriptPopU64(&frame.arguments, &u64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongPopSize) {
|
||||
struct mScriptFrame frame;
|
||||
int32_t s32;
|
||||
int64_t s64;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
float f32;
|
||||
double f64;
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 0);
|
||||
assert_false(mScriptPopS64(&frame.arguments, &s64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S64, 0);
|
||||
assert_false(mScriptPopS32(&frame.arguments, &s32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, U32, 0);
|
||||
assert_false(mScriptPopU64(&frame.arguments, &u64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, U64, 0);
|
||||
assert_false(mScriptPopU32(&frame.arguments, &u32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F32, 0);
|
||||
assert_false(mScriptPopF64(&frame.arguments, &f64));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F64, 0);
|
||||
assert_false(mScriptPopF32(&frame.arguments, &f32));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
bool mScriptPopCSTest(struct mScriptList* list, const struct Test** out) {
|
||||
mSCRIPT_POP(list, CS(Test), val);
|
||||
*out = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mScriptPopSTest(struct mScriptList* list, struct Test** out) {
|
||||
mSCRIPT_POP(list, S(Test), val);
|
||||
*out = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(wrongConst) {
|
||||
struct mScriptFrame frame;
|
||||
struct Test a;
|
||||
struct Test* b;
|
||||
const struct Test* cb;
|
||||
struct mScriptTypeTuple signature = {
|
||||
.count = 1,
|
||||
.variable = false
|
||||
};
|
||||
|
||||
mScriptClassInit(mSCRIPT_TYPE_MS_S(Test)->details.cls);
|
||||
mScriptClassInit(mSCRIPT_TYPE_MS_CS(Test)->details.cls);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_S(Test);
|
||||
assert_true(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(Test), &a);
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test);
|
||||
assert_true(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test);
|
||||
assert_true(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(Test), &a);
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_S(Test);
|
||||
assert_false(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
||||
assert_true(mScriptPopSTest(&frame.arguments, &b));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S(Test), &a);
|
||||
assert_false(mScriptPopCSTest(&frame.arguments, &cb));
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test);
|
||||
assert_true(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
assert_true(mScriptPopCSTest(&frame.arguments, &cb));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(Test), &a);
|
||||
assert_false(mScriptPopSTest(&frame.arguments, &b));
|
||||
signature.entries[0] = mSCRIPT_TYPE_MS_S(Test);
|
||||
assert_false(mScriptCoerceFrame(&signature, &frame.arguments));
|
||||
assert_false(mScriptPopSTest(&frame.arguments, &b));
|
||||
mScriptFrameDeinit(&frame);
|
||||
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CS(Test), &a);
|
||||
assert_true(mScriptPopCSTest(&frame.arguments, &cb));
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(coerceToFloat) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||
assert_true(mScriptInvoke(&boundIdentityFloat, &frame));
|
||||
float val;
|
||||
assert_true(mScriptPopF32(&frame.returnValues, &val));
|
||||
assert_float_equal(val, 1.f, 0.f);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(coerceFromFloat) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, F32, 1.25f);
|
||||
assert_true(mScriptInvoke(&boundIdentityInt, &frame));
|
||||
int val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(coerceWiden) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S32, -1);
|
||||
assert_true(mScriptInvoke(&boundIdentityInt64, &frame));
|
||||
int64_t val;
|
||||
assert_true(mScriptPopS64(&frame.returnValues, &val));
|
||||
assert_true(val == -1LL);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(coerceNarrow) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, S64, -1);
|
||||
assert_true(mScriptInvoke(&boundIdentityInt, &frame));
|
||||
int32_t val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_true(val == -1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
#define COMPARE_BOOL(EXPECT, T0, V0, T1, V1) \
|
||||
a = mSCRIPT_MAKE_ ## T0 (V0); \
|
||||
b = mSCRIPT_MAKE_ ## T1 (V1); \
|
||||
assert_ ## EXPECT (a.type->equal(&a, &b));
|
||||
|
||||
M_TEST_DEFINE(s32Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, S32, 0, S32, 0);
|
||||
COMPARE_BOOL(false, S32, 0, S32, 1);
|
||||
COMPARE_BOOL(true, S32, 1, S32, 1);
|
||||
COMPARE_BOOL(false, S32, 1, S32, -1);
|
||||
COMPARE_BOOL(true, S32, -1, S32, -1);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, S32, 0, S64, 0);
|
||||
COMPARE_BOOL(false, S32, 0, S64, 1);
|
||||
COMPARE_BOOL(true, S32, 1, S64, 1);
|
||||
COMPARE_BOOL(false, S32, 1, S64, -1);
|
||||
COMPARE_BOOL(true, S32, -1, S64, -1);
|
||||
COMPARE_BOOL(false, S32, 0, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, S32, -1, S64, 0x1FFFFFFFFLL);
|
||||
COMPARE_BOOL(false, S32, -1, S64, -0x100000001LL);
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, S32, 0, U32, 0);
|
||||
COMPARE_BOOL(false, S32, 0, U32, 1);
|
||||
COMPARE_BOOL(true, S32, 1, U32, 1);
|
||||
COMPARE_BOOL(true, S32, 0x7FFFFFFF, U32, 0x7FFFFFFFU);
|
||||
COMPARE_BOOL(false, S32, 0xFFFFFFFF, U32, 0xFFFFFFFFU);
|
||||
COMPARE_BOOL(false, S32, 0x80000000, U32, 0x80000000U);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, S32, 0, U64, 0);
|
||||
COMPARE_BOOL(false, S32, 0, U64, 1);
|
||||
COMPARE_BOOL(true, S32, 1, U64, 1);
|
||||
COMPARE_BOOL(true, S32, 0x7FFFFFFF, U64, 0x7FFFFFFFULL);
|
||||
COMPARE_BOOL(false, S32, 0xFFFFFFFF, U64, 0xFFFFFFFFULL);
|
||||
COMPARE_BOOL(false, S32, 0x80000000, U64, 0x80000000ULL);
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, S32, 0, F32, 0);
|
||||
COMPARE_BOOL(false, S32, 1, F32, 0);
|
||||
COMPARE_BOOL(false, S32, 0, F32, 1);
|
||||
COMPARE_BOOL(true, S32, 1, F32, 1);
|
||||
COMPARE_BOOL(false, S32, 0, F32, -1);
|
||||
COMPARE_BOOL(false, S32, 1, F32, -1);
|
||||
COMPARE_BOOL(true, S32, -1, F32, -1);
|
||||
COMPARE_BOOL(false, S32, 1, F32, 1.1);
|
||||
COMPARE_BOOL(false, S32, 0, F32, 0.1);
|
||||
COMPARE_BOOL(true, S32, 0x40000000, F32, 0x40000000);
|
||||
COMPARE_BOOL(true, S32, -0x40000000, F32, -0x40000000);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, S32, 0, F64, 0);
|
||||
COMPARE_BOOL(false, S32, 1, F64, 0);
|
||||
COMPARE_BOOL(false, S32, 0, F64, 1);
|
||||
COMPARE_BOOL(true, S32, 1, F64, 1);
|
||||
COMPARE_BOOL(false, S32, 0, F64, -1);
|
||||
COMPARE_BOOL(false, S32, 1, F64, -1);
|
||||
COMPARE_BOOL(true, S32, -1, F64, -1);
|
||||
COMPARE_BOOL(false, S32, 1, F64, 1.1);
|
||||
COMPARE_BOOL(false, S32, 0, F64, 0.1);
|
||||
COMPARE_BOOL(true, S32, 0x40000000, F64, 0x40000000);
|
||||
COMPARE_BOOL(true, S32, -0x40000000, F64, -0x40000000);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(s64Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, S64, 0, S32, 0);
|
||||
COMPARE_BOOL(false, S64, 0, S32, 1);
|
||||
COMPARE_BOOL(true, S64, 1, S32, 1);
|
||||
COMPARE_BOOL(false, S64, 1, S32, -1);
|
||||
COMPARE_BOOL(true, S64, -1, S32, -1);
|
||||
COMPARE_BOOL(false, S64, 0x100000000LL, S32, 0);
|
||||
COMPARE_BOOL(false, S64, 0x1FFFFFFFFLL, S32, -1);
|
||||
COMPARE_BOOL(false, S64, -0x100000001LL, S32, -1);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, S64, 0, S64, 0);
|
||||
COMPARE_BOOL(false, S64, 0, S64, 1);
|
||||
COMPARE_BOOL(true, S64, 1, S64, 1);
|
||||
COMPARE_BOOL(false, S64, 1, S64, -1);
|
||||
COMPARE_BOOL(true, S64, -1, S64, -1);
|
||||
COMPARE_BOOL(false, S64, 0, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, S64, -1, S64, 0x1FFFFFFFFLL);
|
||||
COMPARE_BOOL(false, S64, -1, S64, -0x100000001LL);
|
||||
COMPARE_BOOL(false, S64, 0x100000000LL, S64, 0);
|
||||
COMPARE_BOOL(false, S64, 0x1FFFFFFFFLL, S64, -1);
|
||||
COMPARE_BOOL(false, S64, -0x100000001LL, S64, -1);
|
||||
COMPARE_BOOL(true, S64, 0x100000000LL, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(true, S64, 0x1FFFFFFFFLL, S64, 0x1FFFFFFFFLL);
|
||||
COMPARE_BOOL(true, S64, -0x100000001LL, S64, -0x100000001LL);
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, S64, 0, U32, 0);
|
||||
COMPARE_BOOL(false, S64, 0, U32, 1);
|
||||
COMPARE_BOOL(true, S64, 1, U32, 1);
|
||||
COMPARE_BOOL(true, S64, 0x7FFFFFFFLL, U32, 0x7FFFFFFFU);
|
||||
COMPARE_BOOL(true, S64, 0xFFFFFFFFLL, U32, 0xFFFFFFFFU);
|
||||
COMPARE_BOOL(true, S64, 0x80000000LL, U32, 0x80000000U);
|
||||
COMPARE_BOOL(false, S64, -1, U32, 0xFFFFFFFFU);
|
||||
COMPARE_BOOL(false, S64, -0x80000000LL, U32, 0x80000000U);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, S64, 0, U64, 0);
|
||||
COMPARE_BOOL(false, S64, 0, U64, 1);
|
||||
COMPARE_BOOL(true, S64, 1, U64, 1);
|
||||
COMPARE_BOOL(true, S64, 0x07FFFFFFFLL, U64, 0x07FFFFFFFULL);
|
||||
COMPARE_BOOL(true, S64, 0x0FFFFFFFFLL, U64, 0x0FFFFFFFFULL);
|
||||
COMPARE_BOOL(true, S64, 0x080000000LL, U64, 0x080000000ULL);
|
||||
COMPARE_BOOL(false, S64, 0, U64, 0x100000000ULL);
|
||||
COMPARE_BOOL(false, S64, 0x100000000LL, U64, 0);
|
||||
COMPARE_BOOL(true, S64, 0x100000000LL, U64, 0x100000000ULL);
|
||||
COMPARE_BOOL(false, S64, -1, U64, 0x0FFFFFFFFULL);
|
||||
COMPARE_BOOL(false, S64, -1, U64, 0xFFFFFFFFFFFFFFFFULL);
|
||||
COMPARE_BOOL(false, S64, -0x080000000LL, U64, 0x080000000ULL);
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, S64, 0, F32, 0);
|
||||
COMPARE_BOOL(false, S64, 1, F32, 0);
|
||||
COMPARE_BOOL(false, S64, 0, F32, 1);
|
||||
COMPARE_BOOL(true, S64, 1, F32, 1);
|
||||
COMPARE_BOOL(false, S64, 0, F32, -1);
|
||||
COMPARE_BOOL(false, S64, 1, F32, -1);
|
||||
COMPARE_BOOL(true, S64, -1, F32, -1);
|
||||
COMPARE_BOOL(false, S64, 1, F32, 1.1);
|
||||
COMPARE_BOOL(false, S64, 0, F32, 0.1);
|
||||
COMPARE_BOOL(true, S64, 0x4000000000000000LL, F32, 0x4000000000000000LL);
|
||||
COMPARE_BOOL(true, S64, -0x4000000000000000LL, F32, -0x4000000000000000LL);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, S64, 0, F64, 0);
|
||||
COMPARE_BOOL(false, S64, 1, F64, 0);
|
||||
COMPARE_BOOL(false, S64, 0, F64, 1);
|
||||
COMPARE_BOOL(true, S64, 1, F64, 1);
|
||||
COMPARE_BOOL(false, S64, 0, F64, -1);
|
||||
COMPARE_BOOL(false, S64, 1, F64, -1);
|
||||
COMPARE_BOOL(true, S64, -1, F64, -1);
|
||||
COMPARE_BOOL(false, S64, 1, F64, 1.1);
|
||||
COMPARE_BOOL(false, S64, 0, F64, 0.1);
|
||||
COMPARE_BOOL(true, S64, 0x4000000000000000LL, F64, 0x4000000000000000LL);
|
||||
COMPARE_BOOL(true, S64, -0x4000000000000000LL, F64, -0x4000000000000000LL);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(u32Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, U32, 0, U32, 0);
|
||||
COMPARE_BOOL(false, U32, 0, U32, 1);
|
||||
COMPARE_BOOL(true, U32, 1, U32, 1);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, U32, 1);
|
||||
COMPARE_BOOL(true, U32, 0x80000000U, U32, 0x80000000U);
|
||||
COMPARE_BOOL(false, U32, 0x7FFFFFFFU, U32, 1);
|
||||
COMPARE_BOOL(true, U32, 0x7FFFFFFFU, U32, 0x7FFFFFFFU);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, U32, 0, U64, 0);
|
||||
COMPARE_BOOL(false, U32, 0, U64, 1);
|
||||
COMPARE_BOOL(true, U32, 1, U64, 1);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, U64, 1);
|
||||
COMPARE_BOOL(true, U32, 0x80000000U, U64, 0x080000000ULL);
|
||||
COMPARE_BOOL(false, U32, 0x7FFFFFFFU, U64, 1);
|
||||
COMPARE_BOOL(true, U32, 0x7FFFFFFFU, U64, 0x07FFFFFFFULL);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, U64, 0x180000000ULL);
|
||||
COMPARE_BOOL(false, U32, 0, U64, 0x100000000ULL);
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, U32, 0, S32, 0);
|
||||
COMPARE_BOOL(false, U32, 0, S32, 1);
|
||||
COMPARE_BOOL(true, U32, 1, S32, 1);
|
||||
COMPARE_BOOL(true, U32, 0x7FFFFFFFU, S32, 0x7FFFFFFF);
|
||||
COMPARE_BOOL(false, U32, 0xFFFFFFFFU, S32, 0xFFFFFFFF);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, S32, 0x80000000);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, U32, 0, S64, 0);
|
||||
COMPARE_BOOL(false, U32, 0, S64, 1);
|
||||
COMPARE_BOOL(true, U32, 1, S64, 1);
|
||||
COMPARE_BOOL(true, U32, 0x7FFFFFFFU, S64, 0x07FFFFFFFLL);
|
||||
COMPARE_BOOL(true, U32, 0xFFFFFFFFU, S64, 0x0FFFFFFFFLL);
|
||||
COMPARE_BOOL(true, U32, 0x80000000U, S64, 0x080000000LL);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, S64, 0x180000000LL);
|
||||
COMPARE_BOOL(false, U32, 0, S64, 0x100000000LL);
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, U32, 0, F32, 0);
|
||||
COMPARE_BOOL(false, U32, 1, F32, 0);
|
||||
COMPARE_BOOL(false, U32, 0, F32, 1);
|
||||
COMPARE_BOOL(true, U32, 1, F32, 1);
|
||||
COMPARE_BOOL(false, U32, 0, F32, -1);
|
||||
COMPARE_BOOL(false, U32, 1, F32, -1);
|
||||
COMPARE_BOOL(false, U32, 0xFFFFFFFFU, F32, -1);
|
||||
COMPARE_BOOL(true, U32, 0x80000000U, F32, 0x80000000);
|
||||
COMPARE_BOOL(false, U32, 0, F32, 0x80000000);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, F32, 0);
|
||||
COMPARE_BOOL(false, U32, 1, F32, 1.1);
|
||||
COMPARE_BOOL(false, U32, 0, F32, 0.1);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, U32, 0, F64, 0);
|
||||
COMPARE_BOOL(false, U32, 1, F64, 0);
|
||||
COMPARE_BOOL(false, U32, 0, F64, 1);
|
||||
COMPARE_BOOL(true, U32, 1, F64, 1);
|
||||
COMPARE_BOOL(false, U32, 0, F64, -1);
|
||||
COMPARE_BOOL(false, U32, 1, F64, -1);
|
||||
COMPARE_BOOL(false, U32, 0xFFFFFFFFU, F64, -1);
|
||||
COMPARE_BOOL(true, U32, 0x80000000U, F64, 0x80000000);
|
||||
COMPARE_BOOL(false, U32, 0, F64, 0x80000000);
|
||||
COMPARE_BOOL(false, U32, 0x80000000U, F64, 0);
|
||||
COMPARE_BOOL(false, U32, 1, F64, 1.1);
|
||||
COMPARE_BOOL(false, U32, 0, F64, 0.1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(u64Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, U64, 0, U32, 0);
|
||||
COMPARE_BOOL(false, U64, 0, U32, 1);
|
||||
COMPARE_BOOL(true, U64, 1, U32, 1);
|
||||
COMPARE_BOOL(false, U64, 0x080000000ULL, U32, 1);
|
||||
COMPARE_BOOL(true, U64, 0x080000000ULL, U32, 0x80000000U);
|
||||
COMPARE_BOOL(false, U64, 0x07FFFFFFFULL, U32, 1);
|
||||
COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, U32, 0x7FFFFFFFU);
|
||||
COMPARE_BOOL(false, U64, 0x180000000ULL, U32, 0x80000000U);
|
||||
COMPARE_BOOL(false, U64, 0x100000000ULL, U32, 0);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, U64, 0, U64, 0);
|
||||
COMPARE_BOOL(false, U64, 0, U64, 1);
|
||||
COMPARE_BOOL(true, U64, 1, U64, 1);
|
||||
COMPARE_BOOL(false, U64, 0x080000000ULL, U64, 1);
|
||||
COMPARE_BOOL(true, U64, 0x080000000ULL, U64, 0x080000000ULL);
|
||||
COMPARE_BOOL(false, U64, 0x07FFFFFFFULL, U64, 1);
|
||||
COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, U64, 0x07FFFFFFFULL);
|
||||
COMPARE_BOOL(true, U64, 0x180000000ULL, U64, 0x180000000ULL);
|
||||
COMPARE_BOOL(true, U64, 0x100000000ULL, U64, 0x100000000ULL);
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, U64, 0, S32, 0);
|
||||
COMPARE_BOOL(false, U64, 0, S32, 1);
|
||||
COMPARE_BOOL(true, U64, 1, S32, 1);
|
||||
COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, S32, 0x7FFFFFFF);
|
||||
COMPARE_BOOL(false, U64, 0x0FFFFFFFFULL, S32, 0xFFFFFFFF);
|
||||
COMPARE_BOOL(false, U64, 0x080000000ULL, S32, 0x80000000);
|
||||
COMPARE_BOOL(false, U64, 0x100000000ULL, S32, 0);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, U64, 0, S64, 0);
|
||||
COMPARE_BOOL(false, U64, 0, S64, 1);
|
||||
COMPARE_BOOL(true, U64, 1, S64, 1);
|
||||
COMPARE_BOOL(true, U64, 0x07FFFFFFFULL, S64, 0x07FFFFFFFLL);
|
||||
COMPARE_BOOL(true, U64, 0x0FFFFFFFFULL, S64, 0x0FFFFFFFFLL);
|
||||
COMPARE_BOOL(true, U64, 0x080000000ULL, S64, 0x080000000LL);
|
||||
COMPARE_BOOL(false, U64, 0, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, U64, 0x100000000ULL, S64, 0);
|
||||
COMPARE_BOOL(true, U64, 0x100000000ULL, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, U64, 0x0FFFFFFFFULL, S64, -1);
|
||||
COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, S64, -1);
|
||||
COMPARE_BOOL(true, U64, 0x080000000ULL, S64, 0x080000000LL);
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, U64, 0, F32, 0);
|
||||
COMPARE_BOOL(false, U64, 1, F32, 0);
|
||||
COMPARE_BOOL(false, U64, 0, F32, 1);
|
||||
COMPARE_BOOL(true, U64, 1, F32, 1);
|
||||
COMPARE_BOOL(false, U64, 0, F32, -1);
|
||||
COMPARE_BOOL(false, U64, 1, F32, -1);
|
||||
COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, F32, -1);
|
||||
COMPARE_BOOL(true, U64, 0x8000000000000000ULL, F32, 0x8000000000000000ULL);
|
||||
COMPARE_BOOL(false, U64, 0, F32, 0x8000000000000000ULL);
|
||||
COMPARE_BOOL(false, U64, 0x8000000000000000ULL, F32, 0);
|
||||
COMPARE_BOOL(false, U64, 1, F32, 1.1);
|
||||
COMPARE_BOOL(false, U64, 0, F32, 0.1);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, U64, 0, F64, 0);
|
||||
COMPARE_BOOL(false, U64, 1, F64, 0);
|
||||
COMPARE_BOOL(false, U64, 0, F64, 1);
|
||||
COMPARE_BOOL(true, U64, 1, F64, 1);
|
||||
COMPARE_BOOL(false, U64, 0, F64, -1);
|
||||
COMPARE_BOOL(false, U64, 1, F64, -1);
|
||||
COMPARE_BOOL(false, U64, 0xFFFFFFFFFFFFFFFFULL, F64, -1);
|
||||
COMPARE_BOOL(true, U64, 0x8000000000000000ULL, F64, 0x8000000000000000ULL);
|
||||
COMPARE_BOOL(false, U64, 0, F64, 0x8000000000000000ULL);
|
||||
COMPARE_BOOL(false, U64, 0x8000000000000000ULL, F64, 0);
|
||||
COMPARE_BOOL(false, U64, 1, F64, 1.1);
|
||||
COMPARE_BOOL(false, U64, 0, F64, 0.1);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(f32Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, F32, 0, F32, 0);
|
||||
COMPARE_BOOL(false, F32, 0, F32, 1);
|
||||
COMPARE_BOOL(true, F32, 1, F32, 1);
|
||||
COMPARE_BOOL(true, F32, -1, F32, -1);
|
||||
COMPARE_BOOL(false, F32, 1.1, F32, 1);
|
||||
COMPARE_BOOL(false, F32, 1, F32, 1.1);
|
||||
COMPARE_BOOL(true, F32, 1.1, F32, 1.1);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, F32, 0, F64, 0);
|
||||
COMPARE_BOOL(false, F32, 0, F64, 1);
|
||||
COMPARE_BOOL(true, F32, 1, F64, 1);
|
||||
COMPARE_BOOL(true, F32, -1, F64, -1);
|
||||
COMPARE_BOOL(false, F32, 1.1, F64, 1);
|
||||
COMPARE_BOOL(false, F32, 1, F64, 1.1);
|
||||
COMPARE_BOOL(true, F32, 1.25, F64, 1.25);
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, F32, 0, S32, 0);
|
||||
COMPARE_BOOL(false, F32, 0, S32, 1);
|
||||
COMPARE_BOOL(false, F32, 1, S32, 0);
|
||||
COMPARE_BOOL(true, F32, 1, S32, 1);
|
||||
COMPARE_BOOL(false, F32, 1.1, S32, 1);
|
||||
COMPARE_BOOL(true, F32, -1, S32, -1);
|
||||
COMPARE_BOOL(false, F32, -1.1, S32, -1);
|
||||
COMPARE_BOOL(true, F32, 0x40000000, S32, 0x40000000);
|
||||
COMPARE_BOOL(true, F32, -0x40000000, S32, -0x40000000);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, F32, 0, S64, 0);
|
||||
COMPARE_BOOL(false, F32, 0, S64, 1);
|
||||
COMPARE_BOOL(false, F32, 1, S64, 0);
|
||||
COMPARE_BOOL(true, F32, 1, S64, 1);
|
||||
COMPARE_BOOL(false, F32, 1.1, S64, 1);
|
||||
COMPARE_BOOL(true, F32, -1, S64, -1);
|
||||
COMPARE_BOOL(false, F32, -1.1, S64, -1);
|
||||
COMPARE_BOOL(true, F32, 0x040000000LL, S64, 0x040000000LL);
|
||||
COMPARE_BOOL(true, F32, 0x100000000LL, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, F32, 0x100000000LL, S64, 0);
|
||||
COMPARE_BOOL(false, F32, 0, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(true, F32, -0x040000000LL, S64, -0x040000000LL);
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, F32, 0, U32, 0);
|
||||
COMPARE_BOOL(false, F32, 0, U32, 1);
|
||||
COMPARE_BOOL(false, F32, 1, U32, 0);
|
||||
COMPARE_BOOL(true, F32, 1, U32, 1);
|
||||
COMPARE_BOOL(false, F32, 1.1, U32, 1);
|
||||
COMPARE_BOOL(true, F32, 0x40000000, U32, 0x40000000);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, F32, 0, U64, 0);
|
||||
COMPARE_BOOL(false, F32, 0, U64, 1);
|
||||
COMPARE_BOOL(false, F32, 1, U64, 0);
|
||||
COMPARE_BOOL(true, F32, 1, U64, 1);
|
||||
COMPARE_BOOL(false, F32, 1.1, U64, 1);
|
||||
COMPARE_BOOL(true, F32, 0x040000000ULL, U64, 0x040000000ULL);
|
||||
COMPARE_BOOL(true, F32, 0x100000000ULL, U64, 0x100000000ULL);
|
||||
COMPARE_BOOL(false, F32, 0x100000000ULL, U64, 0);
|
||||
COMPARE_BOOL(false, F32, 0, U64, 0x100000000ULL);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(f64Equality) {
|
||||
struct mScriptValue a;
|
||||
struct mScriptValue b;
|
||||
|
||||
// F32
|
||||
COMPARE_BOOL(true, F64, 0, F32, 0);
|
||||
COMPARE_BOOL(false, F64, 0, F32, 1);
|
||||
COMPARE_BOOL(true, F64, 1, F32, 1);
|
||||
COMPARE_BOOL(true, F64, -1, F32, -1);
|
||||
COMPARE_BOOL(false, F64, 1.1, F32, 1);
|
||||
COMPARE_BOOL(false, F64, 1, F32, 1.1);
|
||||
COMPARE_BOOL(true, F64, 1.25, F32, 1.25);
|
||||
|
||||
// F64
|
||||
COMPARE_BOOL(true, F64, 0, F64, 0);
|
||||
COMPARE_BOOL(false, F64, 0, F64, 1);
|
||||
COMPARE_BOOL(true, F64, 1, F64, 1);
|
||||
COMPARE_BOOL(true, F64, -1, F64, -1);
|
||||
COMPARE_BOOL(false, F64, 1.1, F64, 1);
|
||||
COMPARE_BOOL(false, F64, 1, F64, 1.1);
|
||||
COMPARE_BOOL(true, F64, 1.1, F64, 1.1);
|
||||
|
||||
// S32
|
||||
COMPARE_BOOL(true, F64, 0, S32, 0);
|
||||
COMPARE_BOOL(false, F64, 0, S32, 1);
|
||||
COMPARE_BOOL(false, F64, 1, S32, 0);
|
||||
COMPARE_BOOL(true, F64, 1, S32, 1);
|
||||
COMPARE_BOOL(false, F64, 1.1, S32, 1);
|
||||
COMPARE_BOOL(true, F64, -1, S32, -1);
|
||||
COMPARE_BOOL(false, F64, -1.1, S32, -1);
|
||||
COMPARE_BOOL(true, F64, 0x40000000, S32, 0x40000000);
|
||||
COMPARE_BOOL(true, F64, -0x40000000, S32, -0x40000000);
|
||||
|
||||
// S64
|
||||
COMPARE_BOOL(true, F64, 0, S64, 0);
|
||||
COMPARE_BOOL(false, F64, 0, S64, 1);
|
||||
COMPARE_BOOL(false, F64, 1, S64, 0);
|
||||
COMPARE_BOOL(true, F64, 1, S64, 1);
|
||||
COMPARE_BOOL(false, F64, 1.1, S64, 1);
|
||||
COMPARE_BOOL(true, F64, -1, S64, -1);
|
||||
COMPARE_BOOL(false, F64, -1.1, S64, -1);
|
||||
COMPARE_BOOL(true, F64, 0x040000000LL, S64, 0x040000000LL);
|
||||
COMPARE_BOOL(true, F64, 0x100000000LL, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(false, F64, 0x100000000LL, S64, 0);
|
||||
COMPARE_BOOL(false, F64, 0, S64, 0x100000000LL);
|
||||
COMPARE_BOOL(true, F64, -0x040000000LL, S64, -0x040000000LL);
|
||||
|
||||
// U32
|
||||
COMPARE_BOOL(true, F64, 0, U32, 0);
|
||||
COMPARE_BOOL(false, F64, 0, U32, 1);
|
||||
COMPARE_BOOL(false, F64, 1, U32, 0);
|
||||
COMPARE_BOOL(true, F64, 1, U32, 1);
|
||||
COMPARE_BOOL(false, F64, 1.1, U32, 1);
|
||||
COMPARE_BOOL(true, F64, 0x40000000, U32, 0x40000000);
|
||||
|
||||
// U64
|
||||
COMPARE_BOOL(true, F64, 0, U64, 0);
|
||||
COMPARE_BOOL(false, F64, 0, U64, 1);
|
||||
COMPARE_BOOL(false, F64, 1, U64, 0);
|
||||
COMPARE_BOOL(true, F64, 1, U64, 1);
|
||||
COMPARE_BOOL(false, F64, 1.1, U64, 1);
|
||||
COMPARE_BOOL(true, F64, 0x040000000ULL, U64, 0x040000000ULL);
|
||||
COMPARE_BOOL(true, F64, 0x100000000ULL, U64, 0x100000000ULL);
|
||||
COMPARE_BOOL(false, F64, 0x100000000ULL, U64, 0);
|
||||
COMPARE_BOOL(false, F64, 0, U64, 0x100000000ULL);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(stringEquality) {
|
||||
struct mScriptValue* stringA = mScriptStringCreateFromUTF8("hello");
|
||||
struct mScriptValue* stringB = mScriptStringCreateFromUTF8("world");
|
||||
struct mScriptValue* stringC = mScriptStringCreateFromUTF8("hello");
|
||||
struct mScriptValue charpA = mSCRIPT_MAKE_CHARP("hello");
|
||||
struct mScriptValue charpB = mSCRIPT_MAKE_CHARP("world");
|
||||
|
||||
assert_true(stringA->type->equal(stringA, stringC));
|
||||
assert_false(stringA->type->equal(stringA, stringB));
|
||||
|
||||
assert_true(stringA->type->equal(stringA, &charpA));
|
||||
assert_false(stringA->type->equal(stringA, &charpB));
|
||||
|
||||
assert_true(charpA.type->equal(&charpA, stringA));
|
||||
assert_false(charpA.type->equal(&charpA, stringB));
|
||||
|
||||
charpB = mSCRIPT_MAKE_CHARP("hello");
|
||||
assert_true(charpA.type->equal(&charpA, &charpB));
|
||||
|
||||
charpB = mSCRIPT_MAKE_CHARP("world");
|
||||
assert_false(charpA.type->equal(&charpA, &charpB));
|
||||
|
||||
mScriptValueDeref(stringA);
|
||||
mScriptValueDeref(stringB);
|
||||
mScriptValueDeref(stringC);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(hashTableBasic) {
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
assert_non_null(table);
|
||||
|
||||
struct mScriptValue* intValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||
assert_ptr_equal(intValue->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(intValue->value.s32, 0);
|
||||
assert_int_equal(intValue->refs, 1);
|
||||
|
||||
struct mScriptValue intKey = mSCRIPT_MAKE_S32(1234);
|
||||
struct mScriptValue badKey = mSCRIPT_MAKE_S32(1235);
|
||||
|
||||
assert_true(mScriptTableInsert(table, &intKey, intValue));
|
||||
assert_int_equal(intValue->refs, 2);
|
||||
|
||||
struct mScriptValue* lookupValue = mScriptTableLookup(table, &intKey);
|
||||
assert_non_null(lookupValue);
|
||||
assert_ptr_equal(lookupValue, intValue);
|
||||
|
||||
lookupValue = mScriptTableLookup(table, &badKey);
|
||||
assert_null(lookupValue);
|
||||
|
||||
assert_true(mScriptTableRemove(table, &intKey));
|
||||
assert_int_equal(intValue->refs, 1);
|
||||
|
||||
mScriptValueDeref(intValue);
|
||||
mScriptValueDeref(table);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(hashTableString) {
|
||||
struct mScriptValue* table = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE);
|
||||
assert_non_null(table);
|
||||
|
||||
struct mScriptValue* intValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||
assert_ptr_equal(intValue->type, mSCRIPT_TYPE_MS_S32);
|
||||
assert_int_equal(intValue->value.s32, 0);
|
||||
assert_int_equal(intValue->refs, 1);
|
||||
|
||||
struct mScriptValue key = mSCRIPT_MAKE_CHARP("key");
|
||||
struct mScriptValue badKey = mSCRIPT_MAKE_CHARP("bad");
|
||||
|
||||
assert_true(mScriptTableInsert(table, &key, intValue));
|
||||
assert_int_equal(intValue->refs, 2);
|
||||
|
||||
struct mScriptValue* lookupValue = mScriptTableLookup(table, &key);
|
||||
assert_non_null(lookupValue);
|
||||
assert_ptr_equal(lookupValue, intValue);
|
||||
|
||||
lookupValue = mScriptTableLookup(table, &badKey);
|
||||
assert_null(lookupValue);
|
||||
|
||||
assert_true(mScriptTableRemove(table, &key));
|
||||
assert_int_equal(intValue->refs, 1);
|
||||
|
||||
mScriptValueDeref(intValue);
|
||||
mScriptValueDeref(table);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(stringIsHello) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CHARP, "hello");
|
||||
assert_true(mScriptInvoke(&boundIsHello, &frame));
|
||||
int val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 1);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(stringIsNotHello) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mSCRIPT_PUSH(&frame.arguments, CHARP, "world");
|
||||
assert_true(mScriptInvoke(&boundIsHello, &frame));
|
||||
int val;
|
||||
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||
assert_int_equal(val, 0);
|
||||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
|
||||
M_TEST_SUITE_DEFINE(mScript,
|
||||
cmocka_unit_test(voidArgs),
|
||||
cmocka_unit_test(voidFunc),
|
||||
cmocka_unit_test(identityFunctionS32),
|
||||
cmocka_unit_test(identityFunctionS64),
|
||||
cmocka_unit_test(identityFunctionF32),
|
||||
cmocka_unit_test(identityFunctionStruct),
|
||||
cmocka_unit_test(addS32),
|
||||
cmocka_unit_test(subS32),
|
||||
cmocka_unit_test(wrongArgCountLo),
|
||||
cmocka_unit_test(wrongArgCountHi),
|
||||
cmocka_unit_test(wrongArgType),
|
||||
cmocka_unit_test(wrongPopType),
|
||||
cmocka_unit_test(wrongPopSize),
|
||||
cmocka_unit_test(wrongConst),
|
||||
cmocka_unit_test(coerceToFloat),
|
||||
cmocka_unit_test(coerceFromFloat),
|
||||
cmocka_unit_test(coerceNarrow),
|
||||
cmocka_unit_test(coerceWiden),
|
||||
cmocka_unit_test(s32Equality),
|
||||
cmocka_unit_test(s64Equality),
|
||||
cmocka_unit_test(u32Equality),
|
||||
cmocka_unit_test(u64Equality),
|
||||
cmocka_unit_test(f32Equality),
|
||||
cmocka_unit_test(f64Equality),
|
||||
cmocka_unit_test(stringEquality),
|
||||
cmocka_unit_test(hashTableBasic),
|
||||
cmocka_unit_test(hashTableString),
|
||||
cmocka_unit_test(stringIsHello),
|
||||
cmocka_unit_test(stringIsNotHello))
|
File diff suppressed because it is too large
Load Diff
|
@ -161,6 +161,24 @@ void* TableLookup(const struct Table* table, uint32_t key) {
|
|||
|
||||
void TableInsert(struct Table* table, uint32_t key, void* value) {
|
||||
struct TableList* list = _getList(table, key);
|
||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||
struct Table newTable;
|
||||
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
|
||||
memcpy(&newTable.fn, &table->fn, sizeof(newTable.fn));
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
struct TableList* list = &table->table[i];
|
||||
size_t j;
|
||||
for (j = 0; j < list->nEntries; ++j) {
|
||||
TableInsert(&newTable, list->list[j].key, list->list[j].value);
|
||||
}
|
||||
free(list->list);
|
||||
}
|
||||
free(table->table);
|
||||
table->tableSize = newTable.tableSize;
|
||||
table->table = newTable.table;
|
||||
list = _getList(table, key);
|
||||
}
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->fn.deinitializer) {
|
||||
|
@ -525,6 +543,17 @@ void HashTableEnumerateBinary(const struct Table* table, void (*handler)(const c
|
|||
}
|
||||
}
|
||||
|
||||
void HashTableEnumerateCustom(const struct Table* table, void (*handler)(void* key, void* value, void* user), void* user) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
const struct TableList* list = &table->table[i];
|
||||
size_t j;
|
||||
for (j = 0; j < list->nEntries; ++j) {
|
||||
handler((char*) list->list[j].stringKey, list->list[j].value, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user) {
|
||||
size_t i;
|
||||
const char* result = NULL;
|
||||
|
|
Loading…
Reference in New Issue