mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
1ec984f4fa
|
@ -1,12 +1,15 @@
|
||||||
|
# Generic files
|
||||||
*.user*
|
*.user*
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
# Build directories
|
||||||
/build
|
/build
|
||||||
/build-*
|
/build-*
|
||||||
/.vs
|
/.vs
|
||||||
|
|
||||||
|
# Build files
|
||||||
*.a
|
*.a
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
@ -18,4 +21,9 @@ CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
hle-bios.bin
|
||||||
version.c
|
version.c
|
||||||
|
|
||||||
|
# Runtime generated cruft
|
||||||
|
*.sav
|
||||||
|
*.ss0
|
||||||
|
|
30
CHANGES
30
CHANGES
|
@ -46,19 +46,36 @@ Features:
|
||||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81
|
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81
|
||||||
- Debugger: Add range watchpoints
|
- Debugger: Add range watchpoints
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
|
- GB Audio: Fix channels 1/2 staying muted if restarted after long silence
|
||||||
|
- GB Serialize: Add missing Pocket Cam state to savestates
|
||||||
- GB Video: Implement DMG-style sprite ordering
|
- GB Video: Implement DMG-style sprite ordering
|
||||||
|
- GBA BIOS: Fix clobbering registers with word-sized CpuSet
|
||||||
|
- GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722)
|
||||||
|
Other fixes:
|
||||||
|
- Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560)
|
||||||
|
Misc:
|
||||||
|
- Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826)
|
||||||
|
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
||||||
|
- GBA: Improve detection of valid ELF ROMs
|
||||||
|
- mGUI: Enable auto-softpatching (closes mgba.io/i/2899)
|
||||||
|
- Scripting: Add `callbacks:oneshot` for single-call callbacks
|
||||||
|
|
||||||
|
0.10.2: (2023-04-23)
|
||||||
|
Emulation fixes:
|
||||||
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793)
|
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793)
|
||||||
|
- GBA Audio: Clear GB audio state when disabled
|
||||||
- GBA Memory: Make VRAM access stalls only apply to BG RAM
|
- GBA Memory: Make VRAM access stalls only apply to BG RAM
|
||||||
|
- GBA Overrides: Fix saving in PMD:RRT (JP) (fixes mgba.io/i/2862)
|
||||||
- GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805)
|
- GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805)
|
||||||
- GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810)
|
- GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810)
|
||||||
- GBA SIO: Normal mode transfers with no clock should not finish (fixes mgba.io/i/2811)
|
- GBA SIO: Normal mode transfers with no clock should not finish (fixes mgba.io/i/2811)
|
||||||
- GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812)
|
- GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812)
|
||||||
- GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722)
|
- GBA Video: Fix interpolation issues with OpenGL renderer
|
||||||
Other fixes:
|
Other fixes:
|
||||||
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
||||||
|
- FFmpeg: Force lower sample rate for codecs not supporting high rates (fixes mgba.io/i/2869)
|
||||||
- Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794)
|
- Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794)
|
||||||
- Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738)
|
- Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738)
|
||||||
- Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560)
|
|
||||||
- Qt: Properly cap number of attached players by platform (fixes mgba.io/i/2807)
|
- Qt: Properly cap number of attached players by platform (fixes mgba.io/i/2807)
|
||||||
- Qt: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702)
|
- Qt: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702)
|
||||||
- Qt: Fix modifier key names in shortcut editor (fixes mgba.io/i/2817)
|
- Qt: Fix modifier key names in shortcut editor (fixes mgba.io/i/2817)
|
||||||
|
@ -67,16 +84,17 @@ Other fixes:
|
||||||
- Qt: Fix crash if loading a shader fails
|
- Qt: Fix crash if loading a shader fails
|
||||||
- Qt: Fix black screen when starting with a game (fixes mgba.io/i/2781)
|
- Qt: Fix black screen when starting with a game (fixes mgba.io/i/2781)
|
||||||
- Qt: Fix OSD on modern macOS (fixes mgba.io/i/2736)
|
- Qt: Fix OSD on modern macOS (fixes mgba.io/i/2736)
|
||||||
|
- Qt: Fix checked state of mute menu option at load (fixes mgba.io/i/2701)
|
||||||
|
- Qt: Remove OpenGL proxy thread and override SwapInterval directly instead
|
||||||
- Scripting: Fix receiving packets for client sockets
|
- Scripting: Fix receiving packets for client sockets
|
||||||
- Scripting: Fix empty receive calls returning unknown error on Windows
|
- Scripting: Fix empty receive calls returning unknown error on Windows
|
||||||
|
- Scripting: Return proper callback ID from socket.add
|
||||||
|
- Vita: Work around broken mktime implementation in Vita SDK (fixes mgba.io/i/2876)
|
||||||
Misc:
|
Misc:
|
||||||
- Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826)
|
|
||||||
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
|
||||||
- GBA: Improve detection of valid ELF ROMs
|
|
||||||
- Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796)
|
- Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796)
|
||||||
- Qt: Stop eating boolean action key events (fixes mgba.io/i/2636)
|
- Qt: Stop eating boolean action key events (fixes mgba.io/i/2636)
|
||||||
- Qt: Automatically change video file extension as appropriate
|
- Qt: Automatically change video file extension as appropriate
|
||||||
- Scripting: Add `callbacks:oneshot` for single-call callbacks
|
- Qt: Swap P1 and other player's save if P1 loaded it first (closes mgba.io/i/2750)
|
||||||
|
|
||||||
0.10.1: (2023-01-10)
|
0.10.1: (2023-01-10)
|
||||||
Emulation fixes:
|
Emulation fixes:
|
||||||
|
|
|
@ -33,7 +33,7 @@ if(NOT MSVC)
|
||||||
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
|
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
|
||||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format")
|
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int -fwrapv")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
|
||||||
else()
|
else()
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")
|
||||||
|
@ -426,6 +426,13 @@ if(BUILD_GL)
|
||||||
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
|
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
|
||||||
set(OPENGL_LIBRARY OpenGL::GL)
|
set(OPENGL_LIBRARY OpenGL::GL)
|
||||||
endif()
|
endif()
|
||||||
|
if(OpenGL_GLX_FOUND)
|
||||||
|
list(APPEND FEATURES GLX)
|
||||||
|
endif()
|
||||||
|
if(OpenGL_EGL_FOUND)
|
||||||
|
list(APPEND FEATURES EGL)
|
||||||
|
list(APPEND OPENGL_LIBRARY ${OPENGL_egl_LIBRARY})
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(BUILD_GL)
|
if(BUILD_GL)
|
||||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
||||||
|
@ -757,11 +764,6 @@ elseif(BUILD_GLES2)
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libgles2")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_EPOXY OR BUILD_GL OR BUILD_GLES2)
|
|
||||||
# This file should probably go somewhere else
|
|
||||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/video-backend.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 AND NOT (LIBMGBA_ONLY OR SKIP_LIBRARY OR USE_EPOXY))
|
if(WIN32 AND NOT (LIBMGBA_ONLY OR SKIP_LIBRARY OR USE_EPOXY))
|
||||||
message(FATAL_ERROR "Windows requires epoxy module!")
|
message(FATAL_ERROR "Windows requires epoxy module!")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -10,20 +10,21 @@
|
||||||
|
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
struct Size {
|
struct mSize {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Rectangle {
|
struct mRectangle {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add);
|
void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add);
|
||||||
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect);
|
bool mRectangleIntersection(struct mRectangle* dst, const struct mRectangle* add);
|
||||||
|
void mRectangleCenter(const struct mRectangle* ref, struct mRectangle* rect);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,326 @@
|
||||||
|
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef M_IMAGE_H
|
||||||
|
#define M_IMAGE_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#ifdef COLOR_16_BIT
|
||||||
|
typedef uint16_t color_t;
|
||||||
|
#define BYTES_PER_PIXEL 2
|
||||||
|
#else
|
||||||
|
typedef uint32_t color_t;
|
||||||
|
#define BYTES_PER_PIXEL 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define M_R5(X) ((X) & 0x1F)
|
||||||
|
#define M_G5(X) (((X) >> 5) & 0x1F)
|
||||||
|
#define M_B5(X) (((X) >> 10) & 0x1F)
|
||||||
|
|
||||||
|
#define M_R8(X) ((M_R5(X) * 0x21) >> 2)
|
||||||
|
#define M_G8(X) ((M_G5(X) * 0x21) >> 2)
|
||||||
|
#define M_B8(X) ((M_B5(X) * 0x21) >> 2)
|
||||||
|
|
||||||
|
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
|
||||||
|
#define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3))
|
||||||
|
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
|
||||||
|
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
|
||||||
|
|
||||||
|
#ifndef COLOR_16_BIT
|
||||||
|
#define M_COLOR_RED 0x000000FF
|
||||||
|
#define M_COLOR_GREEN 0x0000FF00
|
||||||
|
#define M_COLOR_BLUE 0x00FF0000
|
||||||
|
#define M_COLOR_ALPHA 0xFF000000
|
||||||
|
#define M_COLOR_WHITE 0x00FFFFFF
|
||||||
|
|
||||||
|
#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16))
|
||||||
|
#elif defined(COLOR_5_6_5)
|
||||||
|
#define M_COLOR_RED 0x001F
|
||||||
|
#define M_COLOR_GREEN 0x07E0
|
||||||
|
#define M_COLOR_BLUE 0xF800
|
||||||
|
#define M_COLOR_ALPHA 0x0000
|
||||||
|
#define M_COLOR_WHITE 0xFFDF
|
||||||
|
|
||||||
|
#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19))
|
||||||
|
#else
|
||||||
|
#define M_COLOR_RED 0x001F
|
||||||
|
#define M_COLOR_GREEN 0x03E0
|
||||||
|
#define M_COLOR_BLUE 0x7C00
|
||||||
|
#define M_COLOR_ALPHA 0x1000
|
||||||
|
#define M_COLOR_WHITE 0x7FFF
|
||||||
|
|
||||||
|
#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum mColorFormat {
|
||||||
|
mCOLOR_XBGR8 = 0x00001,
|
||||||
|
mCOLOR_XRGB8 = 0x00002,
|
||||||
|
mCOLOR_BGRX8 = 0x00004,
|
||||||
|
mCOLOR_RGBX8 = 0x00008,
|
||||||
|
mCOLOR_ABGR8 = 0x00010,
|
||||||
|
mCOLOR_ARGB8 = 0x00020,
|
||||||
|
mCOLOR_BGRA8 = 0x00040,
|
||||||
|
mCOLOR_RGBA8 = 0x00080,
|
||||||
|
mCOLOR_RGB5 = 0x00100,
|
||||||
|
mCOLOR_BGR5 = 0x00200,
|
||||||
|
mCOLOR_RGB565 = 0x00400,
|
||||||
|
mCOLOR_BGR565 = 0x00800,
|
||||||
|
mCOLOR_ARGB5 = 0x01000,
|
||||||
|
mCOLOR_ABGR5 = 0x02000,
|
||||||
|
mCOLOR_RGBA5 = 0x04000,
|
||||||
|
mCOLOR_BGRA5 = 0x08000,
|
||||||
|
mCOLOR_RGB8 = 0x10000,
|
||||||
|
mCOLOR_BGR8 = 0x20000,
|
||||||
|
mCOLOR_L8 = 0x40000,
|
||||||
|
mCOLOR_PAL8 = 0x80000,
|
||||||
|
|
||||||
|
mCOLOR_ANY = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef COLOR_16_BIT
|
||||||
|
#define mCOLOR_NATIVE mCOLOR_XBGR8
|
||||||
|
#elif !defined(COLOR_5_6_5)
|
||||||
|
#define mCOLOR_NATIVE mCOLOR_BGR5
|
||||||
|
#else
|
||||||
|
#define mCOLOR_NATIVE mCOLOR_RGB565
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mImage {
|
||||||
|
void* data;
|
||||||
|
uint32_t* palette;
|
||||||
|
unsigned width;
|
||||||
|
unsigned height;
|
||||||
|
unsigned stride;
|
||||||
|
unsigned depth;
|
||||||
|
unsigned palSize;
|
||||||
|
enum mColorFormat format;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VFile;
|
||||||
|
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
|
||||||
|
struct mImage* mImageCreateWithStride(unsigned width, unsigned height, unsigned stride, enum mColorFormat format);
|
||||||
|
struct mImage* mImageCreateFromConstBuffer(unsigned width, unsigned height, unsigned stride, enum mColorFormat format, const void* pixels);
|
||||||
|
struct mImage* mImageLoad(const char* path);
|
||||||
|
struct mImage* mImageLoadVF(struct VFile* vf);
|
||||||
|
struct mImage* mImageConvertToFormat(const struct mImage*, enum mColorFormat format);
|
||||||
|
void mImageDestroy(struct mImage*);
|
||||||
|
|
||||||
|
bool mImageSave(const struct mImage*, const char* path, const char* format);
|
||||||
|
bool mImageSaveVF(const struct mImage*, struct VFile* vf, const char* format);
|
||||||
|
|
||||||
|
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
|
||||||
|
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y);
|
||||||
|
void mImageSetPixel(struct mImage* image, unsigned x, unsigned y, uint32_t color);
|
||||||
|
void mImageSetPixelRaw(struct mImage* image, unsigned x, unsigned y, uint32_t color);
|
||||||
|
|
||||||
|
void mImageSetPaletteSize(struct mImage* image, unsigned count);
|
||||||
|
void mImageSetPaletteEntry(struct mImage* image, unsigned index, uint32_t color);
|
||||||
|
|
||||||
|
void mImageBlit(struct mImage* image, const struct mImage* source, int x, int y);
|
||||||
|
void mImageComposite(struct mImage* image, const struct mImage* source, int x, int y);
|
||||||
|
void mImageCompositeWithAlpha(struct mImage* image, const struct mImage* source, int x, int y, float alpha);
|
||||||
|
|
||||||
|
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to);
|
||||||
|
uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to);
|
||||||
|
|
||||||
|
#ifndef PYCPARSE
|
||||||
|
static inline unsigned mColorFormatBytes(enum mColorFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case mCOLOR_XBGR8:
|
||||||
|
case mCOLOR_XRGB8:
|
||||||
|
case mCOLOR_BGRX8:
|
||||||
|
case mCOLOR_RGBX8:
|
||||||
|
case mCOLOR_ABGR8:
|
||||||
|
case mCOLOR_ARGB8:
|
||||||
|
case mCOLOR_BGRA8:
|
||||||
|
case mCOLOR_RGBA8:
|
||||||
|
return 4;
|
||||||
|
case mCOLOR_RGB5:
|
||||||
|
case mCOLOR_BGR5:
|
||||||
|
case mCOLOR_RGB565:
|
||||||
|
case mCOLOR_BGR565:
|
||||||
|
case mCOLOR_ARGB5:
|
||||||
|
case mCOLOR_ABGR5:
|
||||||
|
case mCOLOR_RGBA5:
|
||||||
|
case mCOLOR_BGRA5:
|
||||||
|
return 2;
|
||||||
|
case mCOLOR_RGB8:
|
||||||
|
case mCOLOR_BGR8:
|
||||||
|
return 3;
|
||||||
|
case mCOLOR_L8:
|
||||||
|
case mCOLOR_PAL8:
|
||||||
|
return 1;
|
||||||
|
case mCOLOR_ANY:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool mColorFormatHasAlpha(enum mColorFormat format) {
|
||||||
|
switch (format) {
|
||||||
|
case mCOLOR_XBGR8:
|
||||||
|
case mCOLOR_XRGB8:
|
||||||
|
case mCOLOR_BGRX8:
|
||||||
|
case mCOLOR_RGBX8:
|
||||||
|
case mCOLOR_RGB5:
|
||||||
|
case mCOLOR_BGR5:
|
||||||
|
case mCOLOR_RGB565:
|
||||||
|
case mCOLOR_BGR565:
|
||||||
|
case mCOLOR_RGB8:
|
||||||
|
case mCOLOR_BGR8:
|
||||||
|
case mCOLOR_L8:
|
||||||
|
return false;
|
||||||
|
case mCOLOR_ABGR8:
|
||||||
|
case mCOLOR_ARGB8:
|
||||||
|
case mCOLOR_BGRA8:
|
||||||
|
case mCOLOR_RGBA8:
|
||||||
|
case mCOLOR_ARGB5:
|
||||||
|
case mCOLOR_ABGR5:
|
||||||
|
case mCOLOR_RGBA5:
|
||||||
|
case mCOLOR_BGRA5:
|
||||||
|
case mCOLOR_PAL8:
|
||||||
|
return true;
|
||||||
|
case mCOLOR_ANY:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline color_t mColorFrom555(uint16_t value) {
|
||||||
|
#ifdef COLOR_16_BIT
|
||||||
|
#ifdef COLOR_5_6_5
|
||||||
|
color_t color = 0;
|
||||||
|
color |= (value & 0x001F) << 11;
|
||||||
|
color |= (value & 0x03E0) << 1;
|
||||||
|
color |= (value & 0x7C00) >> 10;
|
||||||
|
#else
|
||||||
|
color_t color = value;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
color_t color = M_RGB5_TO_BGR8(value);
|
||||||
|
color |= (color >> 5) & 0x070707;
|
||||||
|
#endif
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) {
|
||||||
|
unsigned c = 0;
|
||||||
|
unsigned a, b;
|
||||||
|
#ifdef COLOR_16_BIT
|
||||||
|
#ifdef COLOR_5_6_5
|
||||||
|
a = colorA & 0xF81F;
|
||||||
|
b = colorB & 0xF81F;
|
||||||
|
a |= (colorA & 0x7C0) << 16;
|
||||||
|
b |= (colorB & 0x7C0) << 16;
|
||||||
|
c = ((a * weightA + b * weightB) / 16);
|
||||||
|
if (c & 0x08000000) {
|
||||||
|
c = (c & ~0x0FC00000) | 0x07C00000;
|
||||||
|
}
|
||||||
|
if (c & 0x0020) {
|
||||||
|
c = (c & ~0x003F) | 0x001F;
|
||||||
|
}
|
||||||
|
if (c & 0x10000) {
|
||||||
|
c = (c & ~0x1F800) | 0xF800;
|
||||||
|
}
|
||||||
|
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
|
||||||
|
#else
|
||||||
|
a = colorA & 0x7C1F;
|
||||||
|
b = colorB & 0x7C1F;
|
||||||
|
a |= (colorA & 0x3E0) << 16;
|
||||||
|
b |= (colorB & 0x3E0) << 16;
|
||||||
|
c = ((a * weightA + b * weightB) / 16);
|
||||||
|
if (c & 0x04000000) {
|
||||||
|
c = (c & ~0x07E00000) | 0x03E00000;
|
||||||
|
}
|
||||||
|
if (c & 0x0020) {
|
||||||
|
c = (c & ~0x003F) | 0x001F;
|
||||||
|
}
|
||||||
|
if (c & 0x8000) {
|
||||||
|
c = (c & ~0xF800) | 0x7C00;
|
||||||
|
}
|
||||||
|
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
a = colorA & 0xFF;
|
||||||
|
b = colorB & 0xFF;
|
||||||
|
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
|
||||||
|
if (c & 0x00000100) {
|
||||||
|
c = 0x000000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = colorA & 0xFF00;
|
||||||
|
b = colorB & 0xFF00;
|
||||||
|
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
|
||||||
|
if (c & 0x00010000) {
|
||||||
|
c = (c & 0x000000FF) | 0x0000FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = colorA & 0xFF0000;
|
||||||
|
b = colorB & 0xFF0000;
|
||||||
|
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
|
||||||
|
if (c & 0x01000000) {
|
||||||
|
c = (c & 0x0000FFFF) | 0x00FF0000;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTE_UNUSED static uint32_t mColorMixARGB8(uint32_t colorA, uint32_t colorB) {
|
||||||
|
uint32_t alpha = colorA >> 24;
|
||||||
|
if (!alpha) {
|
||||||
|
return colorB;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t color = 0;
|
||||||
|
uint32_t a, b;
|
||||||
|
a = colorA & 0xFF00FF;
|
||||||
|
a *= alpha + 1;
|
||||||
|
color += (a >> 8) & 0xFF00FF;
|
||||||
|
|
||||||
|
a = colorB & 0xFF00FF;
|
||||||
|
a *= 0x100 - alpha;
|
||||||
|
color += (a >> 8) & 0xFF00FF;
|
||||||
|
|
||||||
|
if (color & 0x100) {
|
||||||
|
color &= ~0xFF;
|
||||||
|
color |= 0xFF;
|
||||||
|
}
|
||||||
|
if (color & 0x1000000) {
|
||||||
|
color &= ~0xFF0000;
|
||||||
|
color |= 0xFF0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
b = 0;
|
||||||
|
a = colorA & 0xFF00;
|
||||||
|
a *= alpha + 1;
|
||||||
|
b += a & 0xFF0000;
|
||||||
|
|
||||||
|
a = colorB & 0xFF00;
|
||||||
|
a *= 0x100 - alpha;
|
||||||
|
b += a & 0xFF0000;
|
||||||
|
|
||||||
|
if (b & 0x1000000) {
|
||||||
|
b &= ~0xFF0000;
|
||||||
|
b |= 0xFF0000;
|
||||||
|
}
|
||||||
|
color |= b >> 8;
|
||||||
|
|
||||||
|
alpha += colorB >> 24;
|
||||||
|
if (alpha > 0xFF) {
|
||||||
|
color |= 0xFF000000;
|
||||||
|
} else {
|
||||||
|
color |= alpha << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,10 +1,10 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
/* Copyright (c) 2013-2023 Jeffrey Pfau
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#ifndef EXPORT_H
|
#ifndef M_IMAGE_EXPORT_H
|
||||||
#define EXPORT_H
|
#define M_IMAGE_EXPORT_H
|
||||||
|
|
||||||
#include <mgba-util/common.h>
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ CXX_GUARD_START
|
||||||
|
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
|
||||||
bool exportPaletteRIFF(struct VFile* vf, size_t entries, const uint16_t* colors);
|
bool mPaletteExportRIFF(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||||
bool exportPaletteACT(struct VFile* vf, size_t entries, const uint16_t* colors);
|
bool mPaletteExportACT(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
||||||
|
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
|
|
||||||
|
#include <mgba-util/image.h>
|
||||||
|
|
||||||
// png.h defines its own version of restrict which conflicts with mGBA's.
|
// png.h defines its own version of restrict which conflicts with mGBA's.
|
||||||
#ifdef restrict
|
#ifdef restrict
|
||||||
#undef restrict
|
#undef restrict
|
||||||
|
@ -25,13 +27,10 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
png_structp PNGWriteOpen(struct VFile* source);
|
png_structp PNGWriteOpen(struct VFile* source);
|
||||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat);
|
||||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height);
|
png_infop PNGWriteHeaderPalette(png_structp png, unsigned width, unsigned height, const uint32_t* palette, unsigned entries);
|
||||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat);
|
||||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries);
|
bool PNGWritePixelsPalette(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
|
||||||
bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
|
||||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
|
||||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||||
void PNGWriteClose(png_structp png, png_infop info);
|
void PNGWriteClose(png_structp png, png_infop info);
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
#define _mCALL_0(FN, ...)
|
#define _mCALL_0(FN, ...)
|
||||||
#define _mCALL_1(FN, A) FN(A)
|
#define _mCALL_1(FN, A) FN(A)
|
||||||
#define _mCALL_2(FN, A, B) FN(A), FN(B)
|
#define _mCALL_2(FN, A, B) FN(A), FN(B)
|
||||||
#define _mCALL_3(FN, A, ...) FN(A), _mCALL_2(FN, __VA_ARGS__)
|
#define _mCALL_3(FN, A, B, C) FN(A), FN(B), FN(C)
|
||||||
#define _mCALL_4(FN, A, ...) FN(A), _mCALL_3(FN, __VA_ARGS__)
|
#define _mCALL_4(FN, A, B, C, D) FN(A), FN(B), FN(C), FN(D)
|
||||||
#define _mCALL_5(FN, A, ...) FN(A), _mCALL_4(FN, __VA_ARGS__)
|
#define _mCALL_5(FN, A, B, C, D, E) FN(A), FN(B), FN(C), FN(D), FN(E)
|
||||||
#define _mCALL_6(FN, A, ...) FN(A), _mCALL_5(FN, __VA_ARGS__)
|
#define _mCALL_6(FN, A, B, C, D, E, F) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F)
|
||||||
#define _mCALL_7(FN, A, ...) FN(A), _mCALL_6(FN, __VA_ARGS__)
|
#define _mCALL_7(FN, A, B, C, D, E, F, G) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G)
|
||||||
#define _mCALL_8(FN, A, ...) FN(A), _mCALL_7(FN, __VA_ARGS__)
|
#define _mCALL_8(FN, A, B, C, D, E, F, G, H) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G), FN(H)
|
||||||
#define _mCALL_9(FN, A, ...) FN(A), _mCALL_8(FN, __VA_ARGS__)
|
#define _mCALL_9(FN, A, B, C, D, E, F, G, H, I) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G), FN(H), FN(I)
|
||||||
|
|
||||||
#define _mCOMMA_0(N, ...) N
|
#define _mCOMMA_0(N, ...) N
|
||||||
#define _mCOMMA_1(N, ...) N, __VA_ARGS__
|
#define _mCOMMA_1(N, ...) N, __VA_ARGS__
|
||||||
|
@ -37,25 +37,25 @@
|
||||||
|
|
||||||
#define _mEVEN_0(...)
|
#define _mEVEN_0(...)
|
||||||
#define _mEVEN_1(A, B, ...) A
|
#define _mEVEN_1(A, B, ...) A
|
||||||
#define _mEVEN_2(A, B, ...) A, _mIDENT(_mEVEN_1(__VA_ARGS__))
|
#define _mEVEN_2(A, B, C, D, ...) A, C
|
||||||
#define _mEVEN_3(A, B, ...) A, _mIDENT(_mEVEN_2(__VA_ARGS__))
|
#define _mEVEN_3(A, B, C, D, E, F, ...) A, C, E
|
||||||
#define _mEVEN_4(A, B, ...) A, _mIDENT(_mEVEN_3(__VA_ARGS__))
|
#define _mEVEN_4(A, B, C, D, E, F, G, H, ...) A, C, E, G
|
||||||
#define _mEVEN_5(A, B, ...) A, _mIDENT(_mEVEN_4(__VA_ARGS__))
|
#define _mEVEN_5(A, B, C, D, E, F, G, H, I, J, ...) A, C, E, G, I
|
||||||
#define _mEVEN_6(A, B, ...) A, _mIDENT(_mEVEN_5(__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_7(A, B, C, D, ...) A, C, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||||
#define _mEVEN_8(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
#define _mEVEN_8(A, B, C, D, E, F, ...) A, C, E, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||||
#define _mEVEN_9(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
#define _mEVEN_9(A, B, C, D, E, F, G, H, ...) A, C, E, G, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||||
|
|
||||||
#define _mODD_0(...)
|
#define _mODD_0(...)
|
||||||
#define _mODD_1(A, B, ...) B
|
#define _mODD_1(A, B, ...) B
|
||||||
#define _mODD_2(A, B, ...) B, _mIDENT(_mODD_1(__VA_ARGS__))
|
#define _mODD_2(A, B, C, D, ...) B, D
|
||||||
#define _mODD_3(A, B, ...) B, _mIDENT(_mODD_2(__VA_ARGS__))
|
#define _mODD_3(A, B, C, D, E, F, ...) B, D, F
|
||||||
#define _mODD_4(A, B, ...) B, _mIDENT(_mODD_3(__VA_ARGS__))
|
#define _mODD_4(A, B, C, D, E, F, G, H, ...) B, D, F, H
|
||||||
#define _mODD_5(A, B, ...) B, _mIDENT(_mODD_4(__VA_ARGS__))
|
#define _mODD_5(A, B, C, D, E, F, G, H, I, J, ...) B, D, F, H, J
|
||||||
#define _mODD_6(A, B, ...) B, _mIDENT(_mODD_5(__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_7(A, B, C, D, ...) B, D, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||||
#define _mODD_8(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
#define _mODD_8(A, B, C, D, E, F, ...) B, D, F, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||||
#define _mODD_9(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
#define _mODD_9(A, B, C, D, E, F, G, H, ...) B, D, F, H, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||||
|
|
||||||
#define _mIF0_0(...) __VA_ARGS__
|
#define _mIF0_0(...) __VA_ARGS__
|
||||||
#define _mIF0_1(...)
|
#define _mIF0_1(...)
|
||||||
|
|
|
@ -33,6 +33,7 @@ struct mCoreOptions {
|
||||||
int frameskip;
|
int frameskip;
|
||||||
bool rewindEnable;
|
bool rewindEnable;
|
||||||
int rewindBufferCapacity;
|
int rewindBufferCapacity;
|
||||||
|
int rewindBufferInterval;
|
||||||
float fpsTarget;
|
float fpsTarget;
|
||||||
size_t audioBuffers;
|
size_t audioBuffers;
|
||||||
unsigned sampleRate;
|
unsigned sampleRate;
|
||||||
|
|
|
@ -10,165 +10,14 @@
|
||||||
|
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba-util/image.h>
|
||||||
#include <mgba-util/vector.h>
|
#include <mgba-util/vector.h>
|
||||||
|
|
||||||
struct mCore;
|
struct mCore;
|
||||||
struct mStateExtdataItem;
|
struct mStateExtdataItem;
|
||||||
|
|
||||||
#ifdef COLOR_16_BIT
|
|
||||||
typedef uint16_t color_t;
|
|
||||||
#define BYTES_PER_PIXEL 2
|
|
||||||
#else
|
|
||||||
typedef uint32_t color_t;
|
|
||||||
#define BYTES_PER_PIXEL 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define M_R5(X) ((X) & 0x1F)
|
|
||||||
#define M_G5(X) (((X) >> 5) & 0x1F)
|
|
||||||
#define M_B5(X) (((X) >> 10) & 0x1F)
|
|
||||||
|
|
||||||
#define M_R8(X) (((((X) << 3) & 0xF8) * 0x21) >> 2)
|
|
||||||
#define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 2)
|
|
||||||
#define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 2)
|
|
||||||
|
|
||||||
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
|
|
||||||
#define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3))
|
|
||||||
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
|
|
||||||
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
|
|
||||||
|
|
||||||
#ifndef COLOR_16_BIT
|
|
||||||
#define M_COLOR_RED 0x000000FF
|
|
||||||
#define M_COLOR_GREEN 0x0000FF00
|
|
||||||
#define M_COLOR_BLUE 0x00FF0000
|
|
||||||
#define M_COLOR_ALPHA 0xFF000000
|
|
||||||
#define M_COLOR_WHITE 0x00FFFFFF
|
|
||||||
|
|
||||||
#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16))
|
|
||||||
#elif defined(COLOR_5_6_5)
|
|
||||||
#define M_COLOR_RED 0x001F
|
|
||||||
#define M_COLOR_GREEN 0x07E0
|
|
||||||
#define M_COLOR_BLUE 0xF800
|
|
||||||
#define M_COLOR_ALPHA 0x0000
|
|
||||||
#define M_COLOR_WHITE 0xFFDF
|
|
||||||
|
|
||||||
#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19))
|
|
||||||
#else
|
|
||||||
#define M_COLOR_RED 0x001F
|
|
||||||
#define M_COLOR_GREEN 0x03E0
|
|
||||||
#define M_COLOR_BLUE 0x7C00
|
|
||||||
#define M_COLOR_ALPHA 0x1000
|
|
||||||
#define M_COLOR_WHITE 0x7FFF
|
|
||||||
|
|
||||||
#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef PYCPARSE
|
|
||||||
static inline color_t mColorFrom555(uint16_t value) {
|
|
||||||
#ifdef COLOR_16_BIT
|
|
||||||
#ifdef COLOR_5_6_5
|
|
||||||
color_t color = 0;
|
|
||||||
color |= (value & 0x001F) << 11;
|
|
||||||
color |= (value & 0x03E0) << 1;
|
|
||||||
color |= (value & 0x7C00) >> 10;
|
|
||||||
#else
|
|
||||||
color_t color = value;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
color_t color = M_RGB5_TO_BGR8(value);
|
|
||||||
color |= (color >> 5) & 0x070707;
|
|
||||||
#endif
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) {
|
|
||||||
unsigned c = 0;
|
|
||||||
unsigned a, b;
|
|
||||||
#ifdef COLOR_16_BIT
|
|
||||||
#ifdef COLOR_5_6_5
|
|
||||||
a = colorA & 0xF81F;
|
|
||||||
b = colorB & 0xF81F;
|
|
||||||
a |= (colorA & 0x7C0) << 16;
|
|
||||||
b |= (colorB & 0x7C0) << 16;
|
|
||||||
c = ((a * weightA + b * weightB) / 16);
|
|
||||||
if (c & 0x08000000) {
|
|
||||||
c = (c & ~0x0FC00000) | 0x07C00000;
|
|
||||||
}
|
|
||||||
if (c & 0x0020) {
|
|
||||||
c = (c & ~0x003F) | 0x001F;
|
|
||||||
}
|
|
||||||
if (c & 0x10000) {
|
|
||||||
c = (c & ~0x1F800) | 0xF800;
|
|
||||||
}
|
|
||||||
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
|
|
||||||
#else
|
|
||||||
a = colorA & 0x7C1F;
|
|
||||||
b = colorB & 0x7C1F;
|
|
||||||
a |= (colorA & 0x3E0) << 16;
|
|
||||||
b |= (colorB & 0x3E0) << 16;
|
|
||||||
c = ((a * weightA + b * weightB) / 16);
|
|
||||||
if (c & 0x04000000) {
|
|
||||||
c = (c & ~0x07E00000) | 0x03E00000;
|
|
||||||
}
|
|
||||||
if (c & 0x0020) {
|
|
||||||
c = (c & ~0x003F) | 0x001F;
|
|
||||||
}
|
|
||||||
if (c & 0x8000) {
|
|
||||||
c = (c & ~0xF800) | 0x7C00;
|
|
||||||
}
|
|
||||||
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
a = colorA & 0xFF;
|
|
||||||
b = colorB & 0xFF;
|
|
||||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
|
|
||||||
if (c & 0x00000100) {
|
|
||||||
c = 0x000000FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = colorA & 0xFF00;
|
|
||||||
b = colorB & 0xFF00;
|
|
||||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
|
|
||||||
if (c & 0x00010000) {
|
|
||||||
c = (c & 0x000000FF) | 0x0000FF00;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = colorA & 0xFF0000;
|
|
||||||
b = colorB & 0xFF0000;
|
|
||||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
|
|
||||||
if (c & 0x01000000) {
|
|
||||||
c = (c & 0x0000FFFF) | 0x00FF0000;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct blip_t;
|
struct blip_t;
|
||||||
|
|
||||||
enum mColorFormat {
|
|
||||||
mCOLOR_XBGR8 = 0x00001,
|
|
||||||
mCOLOR_XRGB8 = 0x00002,
|
|
||||||
mCOLOR_BGRX8 = 0x00004,
|
|
||||||
mCOLOR_RGBX8 = 0x00008,
|
|
||||||
mCOLOR_ABGR8 = 0x00010,
|
|
||||||
mCOLOR_ARGB8 = 0x00020,
|
|
||||||
mCOLOR_BGRA8 = 0x00040,
|
|
||||||
mCOLOR_RGBA8 = 0x00080,
|
|
||||||
mCOLOR_RGB5 = 0x00100,
|
|
||||||
mCOLOR_BGR5 = 0x00200,
|
|
||||||
mCOLOR_RGB565 = 0x00400,
|
|
||||||
mCOLOR_BGR565 = 0x00800,
|
|
||||||
mCOLOR_ARGB5 = 0x01000,
|
|
||||||
mCOLOR_ABGR5 = 0x02000,
|
|
||||||
mCOLOR_RGBA5 = 0x04000,
|
|
||||||
mCOLOR_BGRA5 = 0x08000,
|
|
||||||
mCOLOR_RGB8 = 0x10000,
|
|
||||||
mCOLOR_BGR8 = 0x20000,
|
|
||||||
mCOLOR_L8 = 0x40000,
|
|
||||||
|
|
||||||
mCOLOR_ANY = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mCoreFeature {
|
enum mCoreFeature {
|
||||||
mCORE_FEATURE_OPENGL = 1,
|
mCORE_FEATURE_OPENGL = 1,
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct mCoreRewindContext {
|
||||||
size_t size;
|
size_t size;
|
||||||
struct VFile* previousState;
|
struct VFile* previousState;
|
||||||
struct VFile* currentState;
|
struct VFile* currentState;
|
||||||
|
int rewindFrameCounter;
|
||||||
|
|
||||||
#ifndef DISABLE_THREADING
|
#ifndef DISABLE_THREADING
|
||||||
bool onThread;
|
bool onThread;
|
||||||
|
|
|
@ -30,8 +30,8 @@ enum VideoLayer {
|
||||||
struct VideoBackend {
|
struct VideoBackend {
|
||||||
void (*init)(struct VideoBackend*, WHandle handle);
|
void (*init)(struct VideoBackend*, WHandle handle);
|
||||||
void (*deinit)(struct VideoBackend*);
|
void (*deinit)(struct VideoBackend*);
|
||||||
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct Rectangle*);
|
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct mRectangle*);
|
||||||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct Rectangle*);
|
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
|
||||||
void (*swap)(struct VideoBackend*);
|
void (*swap)(struct VideoBackend*);
|
||||||
void (*clear)(struct VideoBackend*);
|
void (*clear)(struct VideoBackend*);
|
||||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||||
|
@ -58,7 +58,7 @@ struct VideoShader {
|
||||||
size_t nPasses;
|
size_t nPasses;
|
||||||
};
|
};
|
||||||
|
|
||||||
void VideoBackendGetFrame(const struct VideoBackend*, struct Rectangle* frame);
|
void VideoBackendGetFrame(const struct VideoBackend*, struct mRectangle* frame);
|
||||||
void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height);
|
void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height);
|
||||||
|
|
||||||
CXX_GUARD_END
|
CXX_GUARD_END
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#include <mgba-util/socket.h>
|
#include <mgba-util/socket.h>
|
||||||
|
|
||||||
#define GDB_STUB_MAX_LINE 1200
|
#define GDB_STUB_MAX_LINE 1400
|
||||||
#define GDB_STUB_INTERVAL 32
|
#define GDB_STUB_INTERVAL 32
|
||||||
|
|
||||||
enum GDBStubAckState {
|
enum GDBStubAckState {
|
||||||
|
|
|
@ -419,6 +419,9 @@ struct GBSerializedState {
|
||||||
uint8_t locked;
|
uint8_t locked;
|
||||||
uint8_t bank0;
|
uint8_t bank0;
|
||||||
} mmm01;
|
} mmm01;
|
||||||
|
struct {
|
||||||
|
uint8_t registersActive;
|
||||||
|
} pocketCam;
|
||||||
struct {
|
struct {
|
||||||
uint64_t lastLatch;
|
uint64_t lastLatch;
|
||||||
uint8_t reg;
|
uint8_t reg;
|
||||||
|
@ -484,6 +487,7 @@ struct GBSerializedState {
|
||||||
|
|
||||||
union {
|
union {
|
||||||
uint8_t huc3Registers[0x80];
|
uint8_t huc3Registers[0x80];
|
||||||
|
uint8_t pocketCamRegisters[0x36];
|
||||||
struct {
|
struct {
|
||||||
uint8_t registers[4];
|
uint8_t registers[4];
|
||||||
uint8_t reserved[4];
|
uint8_t reserved[4];
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* Copyright (c) 2013-2023 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_H
|
||||||
|
#define M_SCRIPT_H
|
||||||
|
|
||||||
|
#include <mgba/script/base.h>
|
||||||
|
#include <mgba/script/context.h>
|
||||||
|
#include <mgba/script/input.h>
|
||||||
|
#include <mgba/script/macros.h>
|
||||||
|
#include <mgba/script/storage.h>
|
||||||
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* Copyright (c) 2013-2023 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_BASE_H
|
||||||
|
#define M_SCRIPT_BASE_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba/script/macros.h>
|
||||||
|
#include <mgba-util/image.h>
|
||||||
|
|
||||||
|
mSCRIPT_DECLARE_STRUCT(mImage)
|
||||||
|
|
||||||
|
struct mScriptContext;
|
||||||
|
void mScriptContextAttachImage(struct mScriptContext* context);
|
||||||
|
void mScriptContextAttachStdlib(struct mScriptContext* context);
|
||||||
|
void mScriptContextAttachSocket(struct mScriptContext* context);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -91,8 +91,6 @@ struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct
|
||||||
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
||||||
void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref);
|
void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref);
|
||||||
|
|
||||||
void mScriptContextAttachStdlib(struct mScriptContext* context);
|
|
||||||
void mScriptContextAttachSocket(struct mScriptContext* context);
|
|
||||||
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants);
|
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants);
|
||||||
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
|
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
|
||||||
|
|
||||||
|
|
|
@ -212,20 +212,30 @@ CXX_GUARD_START
|
||||||
} \
|
} \
|
||||||
},
|
},
|
||||||
|
|
||||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \
|
#define _mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, RO) { \
|
||||||
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
|
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
|
||||||
.info = { \
|
.info = { \
|
||||||
.member = { \
|
.member = { \
|
||||||
.name = #EXPORTED_NAME, \
|
.name = #EXPORTED_NAME, \
|
||||||
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
||||||
.offset = offsetof(struct STRUCT, NAME) \
|
.offset = offsetof(struct STRUCT, NAME), \
|
||||||
|
.readonly = RO \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) \
|
||||||
|
_mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, false)
|
||||||
|
|
||||||
|
#define mSCRIPT_DEFINE_STRUCT_CONST_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) \
|
||||||
|
_mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, true)
|
||||||
|
|
||||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \
|
#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \
|
||||||
mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
||||||
|
|
||||||
|
#define mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(STRUCT, TYPE, NAME) \
|
||||||
|
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
||||||
|
|
||||||
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
|
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
|
||||||
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
||||||
.info = { \
|
.info = { \
|
||||||
|
@ -393,6 +403,9 @@ CXX_GUARD_START
|
||||||
static const struct mScriptValue _mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
static const struct mScriptValue _mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||||
_mSCRIPT_DECLARE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME, S, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
_mSCRIPT_DECLARE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME, S, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
||||||
|
|
||||||
|
#define mSCRIPT_DEFINE_FUNCTION_BINDING_DEFAULTS(NAME) \
|
||||||
|
static const struct mScriptValue _bindingDefaults_ ## NAME[mSCRIPT_PARAMS_MAX] = {
|
||||||
|
|
||||||
#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \
|
#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \
|
||||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \
|
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \
|
||||||
mSCRIPT_NO_DEFAULT,
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
@ -439,7 +452,7 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }
|
#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }
|
||||||
|
|
||||||
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, DEFAULTS, NPARAMS, ...) \
|
||||||
static struct mScriptFunction _function_ ## NAME = { \
|
static struct mScriptFunction _function_ ## NAME = { \
|
||||||
.call = _binding_ ## NAME \
|
.call = _binding_ ## NAME \
|
||||||
}; \
|
}; \
|
||||||
|
@ -456,6 +469,7 @@ CXX_GUARD_START
|
||||||
.count = NPARAMS, \
|
.count = NPARAMS, \
|
||||||
.entries = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \
|
.entries = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||||
.names = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \
|
.names = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||||
|
.defaults = DEFAULTS, \
|
||||||
}, \
|
}, \
|
||||||
.returnType = { \
|
.returnType = { \
|
||||||
.count = NRET, \
|
.count = NRET, \
|
||||||
|
@ -472,7 +486,7 @@ CXX_GUARD_START
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
#define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||||
UNUSED(ctx); \
|
UNUSED(ctx); \
|
||||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||||
|
@ -482,9 +496,9 @@ CXX_GUARD_START
|
||||||
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
||||||
return true; \
|
return true; \
|
||||||
} \
|
} \
|
||||||
_mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__)
|
_mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, DEFAULTS, NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
|
#define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||||
UNUSED(ctx); \
|
UNUSED(ctx); \
|
||||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||||
|
@ -494,7 +508,21 @@ CXX_GUARD_START
|
||||||
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
||||||
return true; \
|
return true; \
|
||||||
} \
|
} \
|
||||||
_mSCRIPT_BIND_FUNCTION(NAME, 0, 0, NPARAMS, __VA_ARGS__)
|
_mSCRIPT_BIND_FUNCTION(NAME, 0, 0, NULL, NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||||
|
_mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, NULL, NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
|
||||||
|
_mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NULL, NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define mSCRIPT_BIND_FUNCTION_WITH_DEFAULTS(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||||
|
static const struct mScriptValue _bindingDefaults_ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||||
|
_mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, _mIDENT(_bindingDefaults_ ## NAME), NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define mSCRIPT_BIND_VOID_FUNCTION_WITH_DEFAULTS(NAME, FUNCTION, NPARAMS, ...) \
|
||||||
|
static const struct mScriptValue _bindingDefaults_ ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||||
|
_mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, _mIDENT(_bindingDefaults_ ## NAME), NPARAMS, __VA_ARGS__)
|
||||||
|
|
||||||
#define _mSCRIPT_DEFINE_DOC_FUNCTION(SCOPE, NAME, NRET, RETURN, NPARAMS, ...) \
|
#define _mSCRIPT_DEFINE_DOC_FUNCTION(SCOPE, NAME, NRET, RETURN, NPARAMS, ...) \
|
||||||
static const struct mScriptType _mScriptDocType_ ## NAME = { \
|
static const struct mScriptType _mScriptDocType_ ## NAME = { \
|
||||||
|
|
|
@ -164,7 +164,8 @@ enum mScriptClassInitType {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1
|
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1,
|
||||||
|
mSCRIPT_VALUE_FLAG_DEINIT = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mScriptType;
|
struct mScriptType;
|
||||||
|
@ -231,6 +232,7 @@ struct mScriptClassMember {
|
||||||
const char* docstring;
|
const char* docstring;
|
||||||
const struct mScriptType* type;
|
const struct mScriptType* type;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
bool readonly;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mScriptClassCastMember {
|
struct mScriptClassCastMember {
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Only used when Game Boy Camera is selected and a physical camera is set</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
|
|
5309
res/nointro.dat
5309
res/nointro.dat
File diff suppressed because it is too large
Load Diff
|
@ -8,9 +8,8 @@ gocha
|
||||||
Jaime J. Denizard
|
Jaime J. Denizard
|
||||||
MichaelK__
|
MichaelK__
|
||||||
Miras Absar
|
Miras Absar
|
||||||
|
Nic Losby
|
||||||
Petru-Sebastian Toader
|
Petru-Sebastian Toader
|
||||||
Stevoisiak
|
Stevoisiak
|
||||||
Tyler Jenkins
|
|
||||||
William K. Leung
|
William K. Leung
|
||||||
Zach
|
|
||||||
Zhongchao Qian
|
Zhongchao Qian
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
local state = {}
|
||||||
|
state.period = 4
|
||||||
|
state.phase = 0
|
||||||
|
state.x = 0
|
||||||
|
state.y = 0
|
||||||
|
|
||||||
|
function state.update()
|
||||||
|
state.phase = state.phase + 1
|
||||||
|
if state.phase == state.period then
|
||||||
|
state.phase = 0
|
||||||
|
end
|
||||||
|
if state.phase == 0 then
|
||||||
|
if input.activeGamepad then
|
||||||
|
local x = input.activeGamepad.axes[1] / 30000
|
||||||
|
local y = input.activeGamepad.axes[2] / 30000
|
||||||
|
-- Map the circle onto a square, since we don't
|
||||||
|
-- want to have a duty of 1/sqrt(2) on the angles
|
||||||
|
local theta = math.atan(y, x)
|
||||||
|
local r = math.sqrt(x * x + y * y)
|
||||||
|
if theta < math.pi * -3 / 4 then
|
||||||
|
r = -r / math.cos(theta)
|
||||||
|
elseif theta < math.pi * -1 / 4 then
|
||||||
|
r = -r / math.sin(theta)
|
||||||
|
elseif theta < math.pi * 1 / 4 then
|
||||||
|
r = r / math.cos(theta)
|
||||||
|
elseif theta < math.pi * 3 / 4 then
|
||||||
|
r = r / math.sin(theta)
|
||||||
|
elseif theta < math.pi * 5 / 4 then
|
||||||
|
r = -r / math.cos(theta)
|
||||||
|
end
|
||||||
|
state.x = math.cos(theta) * r
|
||||||
|
state.y = math.sin(theta) * r
|
||||||
|
else
|
||||||
|
state.x = 0
|
||||||
|
state.y = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function state.read()
|
||||||
|
emu:clearKeys(0xF0)
|
||||||
|
if math.floor(math.abs(state.x) * state.period) > state.phase then
|
||||||
|
if state.x > 0 then
|
||||||
|
emu:addKey(C.GB_KEY.RIGHT)
|
||||||
|
else
|
||||||
|
emu:addKey(C.GB_KEY.LEFT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if math.floor(math.abs(state.y) * state.period) > state.phase then
|
||||||
|
if state.y > 0 then
|
||||||
|
emu:addKey(C.GB_KEY.DOWN)
|
||||||
|
else
|
||||||
|
emu:addKey(C.GB_KEY.UP)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The duty cycle approach can confuse menus and the like,
|
||||||
|
-- so the POV hat setting should force a direction on
|
||||||
|
if input.activeGamepad and #input.activeGamepad.hats > 0 then
|
||||||
|
local hat = input.activeGamepad.hats[1]
|
||||||
|
if hat & C.INPUT_DIR.UP ~= 0 then
|
||||||
|
emu:addKey(C.GB_KEY.UP)
|
||||||
|
end
|
||||||
|
if hat & C.INPUT_DIR.DOWN ~= 0 then
|
||||||
|
emu:addKey(C.GB_KEY.DOWN)
|
||||||
|
end
|
||||||
|
if hat & C.INPUT_DIR.LEFT ~= 0 then
|
||||||
|
emu:addKey(C.GB_KEY.LEFT)
|
||||||
|
end
|
||||||
|
if hat & C.INPUT_DIR.RIGHT ~= 0 then
|
||||||
|
emu:addKey(C.GB_KEY.RIGHT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
callbacks:add("frame", state.update)
|
||||||
|
callbacks:add("keysRead", state.read)
|
|
@ -174,7 +174,7 @@ function Generation1En._readPartyMon(game, address, nameAddress, otAddress)
|
||||||
return mon
|
return mon
|
||||||
end
|
end
|
||||||
|
|
||||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||||
local mon = {}
|
local mon = {}
|
||||||
mon.species = emu:read8(address + 0)
|
mon.species = emu:read8(address + 0)
|
||||||
mon.item = emu:read8(address + 1)
|
mon.item = emu:read8(address + 1)
|
||||||
|
|
|
@ -34,12 +34,15 @@ void mCacheSetDeinit(struct mCacheSet* cache) {
|
||||||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||||
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
|
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
|
||||||
}
|
}
|
||||||
|
mMapCacheSetDeinit(&cache->maps);
|
||||||
for (i = 0; i < mBitmapCacheSetSize(&cache->bitmaps); ++i) {
|
for (i = 0; i < mBitmapCacheSetSize(&cache->bitmaps); ++i) {
|
||||||
mBitmapCacheDeinit(mBitmapCacheSetGetPointer(&cache->bitmaps, i));
|
mBitmapCacheDeinit(mBitmapCacheSetGetPointer(&cache->bitmaps, i));
|
||||||
}
|
}
|
||||||
|
mBitmapCacheSetDeinit(&cache->bitmaps);
|
||||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||||
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
|
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
|
||||||
}
|
}
|
||||||
|
mTileCacheSetDeinit(&cache->tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCacheSetAssignVRAM(struct mCacheSet* cache, void* vram) {
|
void mCacheSetAssignVRAM(struct mCacheSet* cache, void* vram) {
|
||||||
|
|
|
@ -279,8 +279,7 @@ void mCoreConfigDirectory(char* out, size_t outLength) {
|
||||||
void mCoreConfigPortablePath(char* out, size_t outLength) {
|
void mCoreConfigPortablePath(char* out, size_t outLength) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
wchar_t wpath[MAX_PATH];
|
wchar_t wpath[MAX_PATH];
|
||||||
HMODULE hModule = GetModuleHandleW(NULL);
|
GetModuleFileNameW(NULL, wpath, MAX_PATH);
|
||||||
GetModuleFileNameW(hModule, wpath, MAX_PATH);
|
|
||||||
PathRemoveFileSpecW(wpath);
|
PathRemoveFileSpecW(wpath);
|
||||||
if (PATH_SEP[0] != '\\') {
|
if (PATH_SEP[0] != '\\') {
|
||||||
WCHAR* pathSep;
|
WCHAR* pathSep;
|
||||||
|
@ -407,6 +406,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
||||||
_lookupIntValue(config, "frameskip", &opts->frameskip);
|
_lookupIntValue(config, "frameskip", &opts->frameskip);
|
||||||
_lookupIntValue(config, "volume", &opts->volume);
|
_lookupIntValue(config, "volume", &opts->volume);
|
||||||
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
|
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
|
||||||
|
_lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
|
||||||
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
|
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
|
||||||
unsigned audioBuffers;
|
unsigned audioBuffers;
|
||||||
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
||||||
|
@ -449,6 +449,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
|
||||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
|
||||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
|
||||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
|
||||||
|
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
|
||||||
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
||||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
||||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
|
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
|
||||||
|
|
|
@ -94,7 +94,7 @@ struct mCore* mCoreCreate(enum mPlatform platform) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
|
|
||||||
#ifdef PSP2
|
#ifdef PSP2
|
||||||
#include <psp2/photoexport.h>
|
#include <psp2/photoexport.h>
|
||||||
|
@ -377,8 +377,8 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
|
||||||
core->currentVideoSize(core, &width, &height);
|
core->currentVideoSize(core, &width, &height);
|
||||||
core->getPixels(core, &pixels, &stride);
|
core->getPixels(core, &pixels, &stride);
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, width, height);
|
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||||
bool success = PNGWritePixels(png, width, height, stride, pixels);
|
bool success = PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
return success;
|
return success;
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -30,6 +30,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries,
|
||||||
context->previousState = VFileMemChunk(0, 0);
|
context->previousState = VFileMemChunk(0, 0);
|
||||||
context->currentState = VFileMemChunk(0, 0);
|
context->currentState = VFileMemChunk(0, 0);
|
||||||
context->size = 0;
|
context->size = 0;
|
||||||
|
context->rewindFrameCounter = 0;
|
||||||
#ifndef DISABLE_THREADING
|
#ifndef DISABLE_THREADING
|
||||||
context->onThread = onThread;
|
context->onThread = onThread;
|
||||||
context->ready = false;
|
context->ready = false;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/serialize.h>
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba/script/base.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/context.h>
|
||||||
#include <mgba-util/table.h>
|
#include <mgba-util/table.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
@ -399,6 +400,7 @@ static int _mScriptCoreLoadStateFile(struct mCore* core, const char* path, int f
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) {
|
static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
|
@ -412,6 +414,29 @@ static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mScriptValue* _mScriptCoreTakeScreenshotToImage(struct mCore* core) {
|
||||||
|
size_t stride;
|
||||||
|
const void* pixels = 0;
|
||||||
|
unsigned width, height;
|
||||||
|
core->currentVideoSize(core, &width, &height);
|
||||||
|
core->getPixels(core, &pixels, &stride);
|
||||||
|
if (!pixels) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#ifndef COLOR_16_BIT
|
||||||
|
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_XBGR8, pixels);
|
||||||
|
#elif COLOR_5_6_5
|
||||||
|
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_RGB565, pixels);
|
||||||
|
#else
|
||||||
|
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_BGR5, pixels);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
|
||||||
|
result->value.opaque = image;
|
||||||
|
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Loading functions
|
// Loading functions
|
||||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadFile, mCoreLoadFile, 1, CHARP, path);
|
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadFile, mCoreLoadFile, 1, CHARP, path);
|
||||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, autoloadSave, mCoreAutoloadSave, 0);
|
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, autoloadSave, mCoreAutoloadSave, 0);
|
||||||
|
@ -464,6 +489,7 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateFile, _mScript
|
||||||
|
|
||||||
// Miscellaneous functions
|
// Miscellaneous functions
|
||||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename);
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, W(mImage), screenshotToImage, _mScriptCoreTakeScreenshotToImage, 0);
|
||||||
|
|
||||||
mSCRIPT_DEFINE_STRUCT(mCore)
|
mSCRIPT_DEFINE_STRUCT(mCore)
|
||||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||||
|
@ -549,8 +575,10 @@ mSCRIPT_DEFINE_STRUCT(mCore)
|
||||||
mSCRIPT_DEFINE_DOCSTRING("Load state from the given path. See C.SAVESTATE for possible values for `flags`")
|
mSCRIPT_DEFINE_DOCSTRING("Load state from the given path. See C.SAVESTATE for possible values for `flags`")
|
||||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateFile)
|
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateFile)
|
||||||
|
|
||||||
mSCRIPT_DEFINE_DOCSTRING("Save a screenshot")
|
mSCRIPT_DEFINE_DOCSTRING("Save a screenshot to a file")
|
||||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot)
|
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Get a screenshot in an struct::mImage")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshotToImage)
|
||||||
mSCRIPT_DEFINE_END;
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum)
|
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -177,13 +177,13 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
core->currentVideoSize(core, &width, &height);
|
core->currentVideoSize(core, &width, &height);
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, width, height);
|
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||||
if (!png || !info) {
|
if (!png || !info) {
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
PNGWritePixels(png, width, height, stride, pixels);
|
PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||||
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
||||||
if (extdata) {
|
if (extdata) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
@ -453,13 +453,14 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
||||||
#endif
|
#endif
|
||||||
vf->truncate(vf, stateSize);
|
vf->truncate(vf, stateSize);
|
||||||
void* state = vf->map(vf, stateSize, MAP_WRITE);
|
void* state = vf->map(vf, stateSize, MAP_WRITE);
|
||||||
if (!state || !core->saveState(core, state)) {
|
if (!state) {
|
||||||
mStateExtdataDeinit(&extdata);
|
mStateExtdataDeinit(&extdata);
|
||||||
if (cheatVf) {
|
if (cheatVf) {
|
||||||
cheatVf->close(cheatVf);
|
cheatVf->close(cheatVf);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
core->saveState(core, state);
|
||||||
vf->unmap(vf, state, stateSize);
|
vf->unmap(vf, state, stateSize);
|
||||||
vf->seek(vf, stateSize, SEEK_SET);
|
vf->seek(vf, stateSize, SEEK_SET);
|
||||||
mStateExtdataSerialize(&extdata, vf);
|
mStateExtdataSerialize(&extdata, vf);
|
||||||
|
|
|
@ -310,6 +310,24 @@ M_TEST_DEFINE(logging) {
|
||||||
mScriptContextDeinit(&context);
|
mScriptContextDeinit(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(screenshot) {
|
||||||
|
SETUP_LUA;
|
||||||
|
CREATE_CORE;
|
||||||
|
color_t* buffer = malloc(240 * 160 * sizeof(color_t));
|
||||||
|
core->setVideoBuffer(core, buffer, 240);
|
||||||
|
core->reset(core);
|
||||||
|
core->runFrame(core);
|
||||||
|
|
||||||
|
TEST_PROGRAM("im = emu:screenshotToImage()");
|
||||||
|
TEST_PROGRAM("assert(im)");
|
||||||
|
TEST_PROGRAM("assert(im.width >= 160)");
|
||||||
|
TEST_PROGRAM("assert(im.height >= 144)");
|
||||||
|
|
||||||
|
TEARDOWN_CORE;
|
||||||
|
free(buffer);
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||||
cmocka_unit_test(globals),
|
cmocka_unit_test(globals),
|
||||||
cmocka_unit_test(infoFuncs),
|
cmocka_unit_test(infoFuncs),
|
||||||
|
@ -318,4 +336,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||||
cmocka_unit_test(memoryRead),
|
cmocka_unit_test(memoryRead),
|
||||||
cmocka_unit_test(memoryWrite),
|
cmocka_unit_test(memoryWrite),
|
||||||
cmocka_unit_test(logging),
|
cmocka_unit_test(logging),
|
||||||
|
cmocka_unit_test(screenshot),
|
||||||
)
|
)
|
||||||
|
|
|
@ -157,7 +157,11 @@ void _frameStarted(void* context) {
|
||||||
}
|
}
|
||||||
if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
|
if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
|
||||||
if (!thread->impl->rewinding || !mCoreRewindRestore(&thread->impl->rewind, thread->core)) {
|
if (!thread->impl->rewinding || !mCoreRewindRestore(&thread->impl->rewind, thread->core)) {
|
||||||
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
if (thread->impl->rewind.rewindFrameCounter == 0) {
|
||||||
|
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
||||||
|
thread->impl->rewind.rewindFrameCounter = thread->core->opts.rewindBufferInterval;
|
||||||
|
}
|
||||||
|
thread->impl->rewind.rewindFrameCounter--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -809,10 +809,14 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
if (!dv) {
|
||||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (dv->type != CLIDV_INT_TYPE || dv->intValue < 0) {
|
||||||
|
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
debugger->traceRemaining = dv->intValue;
|
debugger->traceRemaining = dv->intValue;
|
||||||
if (debugger->traceVf) {
|
if (debugger->traceVf) {
|
||||||
|
|
|
@ -50,7 +50,17 @@ static const char* TARGET_XML = "<target version=\"1.0\">"
|
||||||
"<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
"<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||||
"<reg name=\"lr\" bitsize=\"32\"/>"
|
"<reg name=\"lr\" bitsize=\"32\"/>"
|
||||||
"<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
"<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||||
"<reg name=\"cpsr\" bitsize=\"32\" regnum=\"25\"/>"
|
"<flags id=\"cpsr_flags\" size=\"4\">"
|
||||||
|
"<field name=\"N\" start=\"31\" end=\"31\"/>"
|
||||||
|
"<field name=\"Z\" start=\"30\" end=\"30\"/>"
|
||||||
|
"<field name=\"C\" start=\"29\" end=\"29\"/>"
|
||||||
|
"<field name=\"V\" start=\"28\" end=\"28\"/>"
|
||||||
|
"<field name=\"I\" start=\"7\" end=\"7\"/>"
|
||||||
|
"<field name=\"F\" start=\"6\" end=\"6\"/>"
|
||||||
|
"<field name=\"T\" start=\"5\" end=\"5\"/>"
|
||||||
|
"<field name=\"M\" start=\"0\" end=\"4\"/>"
|
||||||
|
"</flags>"
|
||||||
|
"<reg name=\"cpsr\" bitsize=\"32\" regnum=\"25\" type=\"cpsr_flags\"/>"
|
||||||
"</feature>"
|
"</feature>"
|
||||||
"</target>";
|
"</target>";
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ set(SOURCE_FILES
|
||||||
commandline.c
|
commandline.c
|
||||||
thread-proxy.c
|
thread-proxy.c
|
||||||
updater.c
|
updater.c
|
||||||
|
video-backend.c
|
||||||
video-logger.c)
|
video-logger.c)
|
||||||
|
|
||||||
set(GUI_FILES
|
set(GUI_FILES
|
||||||
|
|
|
@ -158,19 +158,35 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
|
||||||
}
|
}
|
||||||
encoder->sampleRate = encoder->isampleRate;
|
encoder->sampleRate = encoder->isampleRate;
|
||||||
if (codec->supported_samplerates) {
|
if (codec->supported_samplerates) {
|
||||||
|
bool gotSampleRate = false;
|
||||||
|
int highestSampleRate = 0;
|
||||||
for (i = 0; codec->supported_samplerates[i]; ++i) {
|
for (i = 0; codec->supported_samplerates[i]; ++i) {
|
||||||
|
if (codec->supported_samplerates[i] > highestSampleRate) {
|
||||||
|
highestSampleRate = codec->supported_samplerates[i];
|
||||||
|
}
|
||||||
if (codec->supported_samplerates[i] < encoder->isampleRate) {
|
if (codec->supported_samplerates[i] < encoder->isampleRate) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (encoder->sampleRate == encoder->isampleRate || encoder->sampleRate > codec->supported_samplerates[i]) {
|
if (!gotSampleRate || encoder->sampleRate > codec->supported_samplerates[i]) {
|
||||||
encoder->sampleRate = codec->supported_samplerates[i];
|
encoder->sampleRate = codec->supported_samplerates[i];
|
||||||
|
gotSampleRate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!gotSampleRate) {
|
||||||
|
// There are no available sample rates that are higher than the input sample rate
|
||||||
|
// Let's use the highest available instead
|
||||||
|
encoder->sampleRate = highestSampleRate;
|
||||||
|
}
|
||||||
} else if (codec->id == AV_CODEC_ID_FLAC) {
|
} else if (codec->id == AV_CODEC_ID_FLAC) {
|
||||||
// HACK: FLAC doesn't support > 65535Hz unless it's divisible by 10
|
// HACK: FLAC doesn't support > 65535Hz unless it's divisible by 10
|
||||||
if (encoder->sampleRate >= 65535) {
|
if (encoder->sampleRate >= 65535) {
|
||||||
encoder->sampleRate -= encoder->isampleRate % 10;
|
encoder->sampleRate -= encoder->isampleRate % 10;
|
||||||
}
|
}
|
||||||
|
} else if (codec->id == AV_CODEC_ID_VORBIS) {
|
||||||
|
// HACK: FLAC doesn't support > 48000Hz but doesn't tell us
|
||||||
|
if (encoder->sampleRate > 48000) {
|
||||||
|
encoder->sampleRate = 48000;
|
||||||
|
}
|
||||||
} else if (codec->id == AV_CODEC_ID_AAC) {
|
} else if (codec->id == AV_CODEC_ID_AAC) {
|
||||||
// HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
|
// HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
|
||||||
encoder->sampleRate = 48000;
|
encoder->sampleRate = 48000;
|
||||||
|
@ -890,7 +906,7 @@ void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRa
|
||||||
}
|
}
|
||||||
|
|
||||||
void _ffmpegOpenResampleContext(struct FFmpegEncoder* encoder) {
|
void _ffmpegOpenResampleContext(struct FFmpegEncoder* encoder) {
|
||||||
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 4, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate });
|
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 1, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate }) * 4;
|
||||||
encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
|
encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
|
||||||
#ifdef USE_LIBAVRESAMPLE
|
#ifdef USE_LIBAVRESAMPLE
|
||||||
encoder->resampleContext = avresample_alloc_context();
|
encoder->resampleContext = avresample_alloc_context();
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
#include <mgba-util/gui/file-select.h>
|
#include <mgba-util/gui/file-select.h>
|
||||||
#include <mgba-util/gui/font.h>
|
#include <mgba-util/gui/font.h>
|
||||||
#include <mgba-util/gui/menu.h>
|
#include <mgba-util/gui/menu.h>
|
||||||
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/memory.h>
|
#include <mgba-util/memory.h>
|
||||||
#include <mgba-util/png-io.h>
|
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
#ifdef PSP2
|
#ifdef PSP2
|
||||||
|
@ -456,6 +456,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
||||||
mLOG(GUI_RUNNER, DEBUG, "Loading save...");
|
mLOG(GUI_RUNNER, DEBUG, "Loading save...");
|
||||||
mCoreAutoloadSave(runner->core);
|
mCoreAutoloadSave(runner->core);
|
||||||
mCoreAutoloadCheats(runner->core);
|
mCoreAutoloadCheats(runner->core);
|
||||||
|
mCoreAutoloadPatch(runner->core);
|
||||||
if (runner->setup) {
|
if (runner->setup) {
|
||||||
mLOG(GUI_RUNNER, DEBUG, "Setting up runner...");
|
mLOG(GUI_RUNNER, DEBUG, "Setting up runner...");
|
||||||
runner->setup(runner);
|
runner->setup(runner);
|
||||||
|
|
|
@ -3,20 +3,20 @@
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "video-backend.h"
|
#include <mgba/feature/video-backend.h>
|
||||||
|
|
||||||
void VideoBackendGetFrame(const struct VideoBackend* v, struct Rectangle* frame) {
|
void VideoBackendGetFrame(const struct VideoBackend* v, struct mRectangle* frame) {
|
||||||
memset(frame, 0, sizeof(*frame));
|
memset(frame, 0, sizeof(*frame));
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
||||||
struct Rectangle dims;
|
struct mRectangle dims;
|
||||||
v->layerDimensions(v, i, &dims);
|
v->layerDimensions(v, i, &dims);
|
||||||
RectangleUnion(frame, &dims);
|
mRectangleUnion(frame, &dims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) {
|
void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) {
|
||||||
struct Rectangle frame;
|
struct mRectangle frame;
|
||||||
VideoBackendGetFrame(v, &frame);
|
VideoBackendGetFrame(v, &frame);
|
||||||
*width = frame.width;
|
*width = frame.width;
|
||||||
*height = frame.height;
|
*height = frame.height;
|
|
@ -138,6 +138,9 @@ void GBAudioReset(struct GBAudio* audio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
|
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
|
||||||
|
if (samples > BLIP_BUFFER_SIZE / 2) {
|
||||||
|
samples = BLIP_BUFFER_SIZE / 2;
|
||||||
|
}
|
||||||
mCoreSyncLockAudio(audio->p->sync);
|
mCoreSyncLockAudio(audio->p->sync);
|
||||||
audio->samples = samples;
|
audio->samples = samples;
|
||||||
blip_clear(audio->left);
|
blip_clear(audio->left);
|
||||||
|
@ -199,6 +202,7 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
|
||||||
--audio->ch1.control.length;
|
--audio->ch1.control.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
audio->ch1.lastUpdate = mTimingCurrentTime(audio->timing);
|
||||||
_updateSquareSample(&audio->ch1);
|
_updateSquareSample(&audio->ch1);
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0001;
|
*audio->nr52 &= ~0x0001;
|
||||||
|
@ -246,6 +250,7 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
|
||||||
--audio->ch2.control.length;
|
--audio->ch2.control.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
audio->ch2.lastUpdate = mTimingCurrentTime(audio->timing);
|
||||||
_updateSquareSample(&audio->ch2);
|
_updateSquareSample(&audio->ch2);
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x0002;
|
*audio->nr52 &= ~0x0002;
|
||||||
|
|
|
@ -227,7 +227,7 @@ void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
|
||||||
mbcState->rumble = !!(value & 0x80);
|
mbcState->rumble = !!(value & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mbcState->rumble) {
|
if (mbcState->rumble && memory->rumble) {
|
||||||
memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02));
|
memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -799,6 +799,10 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
||||||
state->huc3Registers[i] |= memory->mbcState.huc3.registers[i * 2 + 1] << 4;
|
state->huc3Registers[i] |= memory->mbcState.huc3.registers[i * 2 + 1] << 4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GB_POCKETCAM:
|
||||||
|
state->memory.pocketCam.registersActive = memory->mbcState.pocketCam.registersActive;
|
||||||
|
memcpy(state->pocketCamRegisters, memory->mbcState.pocketCam.registers, sizeof(memory->mbcState.pocketCam.registers));
|
||||||
|
break;
|
||||||
case GB_MMM01:
|
case GB_MMM01:
|
||||||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||||
|
@ -950,6 +954,10 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||||
memory->mbcState.huc3.registers[i * 2 + 1] = state->huc3Registers[i] >> 4;
|
memory->mbcState.huc3.registers[i * 2 + 1] = state->huc3Registers[i] >> 4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GB_POCKETCAM:
|
||||||
|
memory->mbcState.pocketCam.registersActive = state->memory.pocketCam.registersActive;
|
||||||
|
memcpy(memory->mbcState.pocketCam.registers, state->pocketCamRegisters, sizeof(memory->mbcState.pocketCam.registers));
|
||||||
|
break;
|
||||||
case GB_MMM01:
|
case GB_MMM01:
|
||||||
memory->mbcState.mmm01.locked = state->memory.mmm01.locked;
|
memory->mbcState.mmm01.locked = state->memory.mmm01.locked;
|
||||||
memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0;
|
memory->mbcState.mmm01.currentBank0 = state->memory.mmm01.bank0;
|
||||||
|
|
|
@ -106,6 +106,9 @@ void GBAAudioDeinit(struct GBAAudio* audio) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
|
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
|
||||||
|
if (samples > 0x2000) {
|
||||||
|
samples = 0x2000;
|
||||||
|
}
|
||||||
mCoreSyncLockAudio(audio->p->sync);
|
mCoreSyncLockAudio(audio->p->sync);
|
||||||
audio->samples = samples;
|
audio->samples = samples;
|
||||||
blip_clear(audio->psg.left);
|
blip_clear(audio->psg.left);
|
||||||
|
@ -231,8 +234,22 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||||
audio->enable = GBAudioEnableGetEnable(value);
|
audio->enable = GBAudioEnableGetEnable(value);
|
||||||
GBAudioWriteNR52(&audio->psg, value);
|
GBAudioWriteNR52(&audio->psg, value);
|
||||||
|
if (!audio->enable) {
|
||||||
|
int i;
|
||||||
|
for (i = REG_SOUND1CNT_LO; i < REG_SOUNDCNT_HI; i += 2) {
|
||||||
|
audio->p->memory.io[i >> 1] = 0;
|
||||||
|
}
|
||||||
|
audio->psg.ch3.size = 0;
|
||||||
|
audio->psg.ch3.bank = 0;
|
||||||
|
audio->psg.ch3.volume = 0;
|
||||||
|
audio->volume = 0;
|
||||||
|
audio->volumeChA = 0;
|
||||||
|
audio->volumeChB = 0;
|
||||||
|
audio->p->memory.io[REG_SOUNDCNT_HI >> 1] &= 0xFF00;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
|
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value) {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
#include <mgba-util/convolve.h>
|
#include <mgba-util/convolve.h>
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -888,7 +888,11 @@ struct EReaderScan* EReaderScanLoadImagePNG(const char* filename) {
|
||||||
}
|
}
|
||||||
png_infop info = png_create_info_struct(png);
|
png_infop info = png_create_info_struct(png);
|
||||||
png_infop end = png_create_info_struct(png);
|
png_infop end = png_create_info_struct(png);
|
||||||
PNGReadHeader(png, info);
|
if (!PNGReadHeader(png, info)) {
|
||||||
|
PNGReadClose(png, info, end);
|
||||||
|
vf->close(vf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
unsigned height = png_get_image_height(png, info);
|
unsigned height = png_get_image_height(png, info);
|
||||||
unsigned width = png_get_image_width(png, info);
|
unsigned width = png_get_image_width(png, info);
|
||||||
int type = png_get_color_type(png, info);
|
int type = png_get_color_type(png, info);
|
||||||
|
@ -900,19 +904,34 @@ struct EReaderScan* EReaderScanLoadImagePNG(const char* filename) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
image = malloc(height * width * 3);
|
image = malloc(height * width * 3);
|
||||||
PNGReadPixels(png, info, image, width, height, width);
|
if (!image) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!PNGReadPixels(png, info, image, width, height, width)) {
|
||||||
|
free(image);
|
||||||
|
image = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PNG_COLOR_TYPE_RGBA:
|
case PNG_COLOR_TYPE_RGBA:
|
||||||
if (depth != 8) {
|
if (depth != 8) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
image = malloc(height * width * 4);
|
image = malloc(height * width * 4);
|
||||||
PNGReadPixelsA(png, info, image, width, height, width);
|
if (!image) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!PNGReadPixelsA(png, info, image, width, height, width)) {
|
||||||
|
free(image);
|
||||||
|
image = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PNGReadFooter(png, end);
|
PNGReadFooter(png, end);
|
||||||
|
out:
|
||||||
PNGReadClose(png, info, end);
|
PNGReadClose(png, info, end);
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
if (!image) {
|
if (!image) {
|
||||||
|
|
|
@ -50,13 +50,13 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = {
|
||||||
0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x46, 0xa0, 0xe1,
|
0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x46, 0xa0, 0xe1,
|
||||||
0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
|
0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
|
||||||
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
|
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
|
||||||
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, 0x04, 0x00, 0x55, 0xe1,
|
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xb0, 0xe8, 0x04, 0x00, 0x51, 0xe1,
|
||||||
0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
|
0x08, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
|
||||||
0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0,
|
0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0,
|
||||||
0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0,
|
0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0,
|
||||||
0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
|
0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
|
||||||
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
|
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x51, 0xe1,
|
||||||
0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
0x08, 0x00, 0xb0, 0xb8, 0x08, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
||||||
0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
|
0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
|
||||||
0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba,
|
0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba,
|
||||||
0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9,
|
0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9,
|
||||||
|
|
|
@ -209,10 +209,10 @@ tst r2, #0x04000000
|
||||||
beq 1f
|
beq 1f
|
||||||
@ Word
|
@ Word
|
||||||
add r4, r5, r4, lsr #10
|
add r4, r5, r4, lsr #10
|
||||||
ldmia r12!, {r3}
|
ldmia r0!, {r3}
|
||||||
2:
|
2:
|
||||||
cmp r5, r4
|
cmp r1, r4
|
||||||
stmltia r5!, {r3}
|
stmltia r1!, {r3}
|
||||||
blt 2b
|
blt 2b
|
||||||
b 3f
|
b 3f
|
||||||
@ Halfword
|
@ Halfword
|
||||||
|
@ -233,9 +233,9 @@ beq 1f
|
||||||
@ Word
|
@ Word
|
||||||
add r4, r5, r4, lsr #10
|
add r4, r5, r4, lsr #10
|
||||||
2:
|
2:
|
||||||
cmp r5, r4
|
cmp r1, r4
|
||||||
ldmltia r12!, {r3}
|
ldmltia r0!, {r3}
|
||||||
stmltia r5!, {r3}
|
stmltia r1!, {r3}
|
||||||
blt 2b
|
blt 2b
|
||||||
b 3f
|
b 3f
|
||||||
@ Halfword
|
@ Halfword
|
||||||
|
|
|
@ -1747,15 +1747,13 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
||||||
maxLoads -= previousLoads;
|
maxLoads -= previousLoads;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t s = cpu->memory.activeSeqCycles16;
|
|
||||||
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1;
|
|
||||||
|
|
||||||
// Figure out how many sequential loads we can jam in
|
// Figure out how many sequential loads we can jam in
|
||||||
|
int32_t s = cpu->memory.activeSeqCycles16;
|
||||||
int32_t stall = s + 1;
|
int32_t stall = s + 1;
|
||||||
int32_t loads = 1;
|
int32_t loads = 1;
|
||||||
|
|
||||||
while (stall < wait && loads < maxLoads) {
|
while (stall < wait && loads < maxLoads) {
|
||||||
stall += s;
|
stall += s + 1;
|
||||||
++loads;
|
++loads;
|
||||||
}
|
}
|
||||||
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
|
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
|
||||||
|
@ -1766,10 +1764,10 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This instruction used to have an N, convert it to an S.
|
// This instruction used to have an N, convert it to an S.
|
||||||
wait -= n2s;
|
wait -= cpu->memory.activeNonseqCycles16 - s;
|
||||||
|
|
||||||
// The next |loads|S waitstates disappear entirely, so long as they're all in a row
|
// The next |loads|S waitstates disappear entirely, so long as they're all in a row
|
||||||
wait -= stall - 1;
|
wait -= stall;
|
||||||
|
|
||||||
return wait;
|
return wait;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,10 +123,8 @@ static const struct GBACartridgeOverride _overrides[] = {
|
||||||
{ "BPEF", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false },
|
{ "BPEF", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false },
|
||||||
|
|
||||||
// Pokemon Mystery Dungeon
|
// Pokemon Mystery Dungeon
|
||||||
{ "B24J", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
|
||||||
{ "B24E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
{ "B24E", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
||||||
{ "B24P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
{ "B24P", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
||||||
{ "B24U", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
|
||||||
|
|
||||||
// Pokemon FireRed
|
// Pokemon FireRed
|
||||||
{ "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
{ "BPRJ", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
||||||
|
|
|
@ -233,6 +233,29 @@ static const char* const _interpolate =
|
||||||
" aff[2] = transform[start + 2].zw;\n"
|
" aff[2] = transform[start + 2].zw;\n"
|
||||||
" mat[3] = transform[start + 3].xy;\n"
|
" mat[3] = transform[start + 3].xy;\n"
|
||||||
" aff[3] = transform[start + 3].zw;\n"
|
" aff[3] = transform[start + 3].zw;\n"
|
||||||
|
"}\n"
|
||||||
|
|
||||||
|
"ivec2 affineInterpolate() {\n"
|
||||||
|
" ivec2 mat[4];\n"
|
||||||
|
" ivec2 offset[4];\n"
|
||||||
|
" vec2 incoord = texCoord;\n"
|
||||||
|
" if (mosaic.x > 1) {\n"
|
||||||
|
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
|
||||||
|
" }\n"
|
||||||
|
" if (mosaic.y > 1) {\n"
|
||||||
|
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
|
||||||
|
" }\n"
|
||||||
|
" loadAffine(int(incoord.y), mat, offset);\n"
|
||||||
|
" float y = fract(incoord.y);\n"
|
||||||
|
" float start = 2. / 3.;\n"
|
||||||
|
" if (int(incoord.y) - range.x < 4) {\n"
|
||||||
|
" y = incoord.y - float(range.x);\n"
|
||||||
|
" start -= 1.;\n"
|
||||||
|
" }\n"
|
||||||
|
" float lin = start + y / 3.;\n"
|
||||||
|
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
||||||
|
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
||||||
|
" return ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
static const char* const _renderMode2 =
|
static const char* const _renderMode2 =
|
||||||
|
@ -250,8 +273,7 @@ static const char* const _renderMode2 =
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
|
||||||
"int fetchTile(ivec2 coord);\n"
|
"int fetchTile(ivec2 coord);\n"
|
||||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
"ivec2 affineInterpolate();\n"
|
||||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
|
||||||
|
|
||||||
"int renderTile(ivec2 coord) {\n"
|
"int renderTile(ivec2 coord) {\n"
|
||||||
" int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
|
" int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
|
||||||
|
@ -278,26 +300,7 @@ static const char* const _renderMode2 =
|
||||||
"}\n"
|
"}\n"
|
||||||
|
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" ivec2 mat[4];\n"
|
" int paletteEntry = fetchTile(affineInterpolate());\n"
|
||||||
" ivec2 offset[4];\n"
|
|
||||||
" vec2 incoord = texCoord;\n"
|
|
||||||
" if (mosaic.x > 1) {\n"
|
|
||||||
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
|
|
||||||
" }\n"
|
|
||||||
" if (mosaic.y > 1) {\n"
|
|
||||||
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
|
|
||||||
" }\n"
|
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
|
||||||
" float y = fract(incoord.y);\n"
|
|
||||||
" float start = 0.75;\n"
|
|
||||||
" if (int(incoord.y) - range.x < 4) {\n"
|
|
||||||
" y = incoord.y - float(range.x);\n"
|
|
||||||
" start = 0.;\n"
|
|
||||||
" }\n"
|
|
||||||
" float lin = start + y * 0.25;\n"
|
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
|
||||||
" int paletteEntry = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n"
|
|
||||||
" color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n"
|
" color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n"
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
|
@ -325,30 +328,10 @@ static const char* const _renderMode35 =
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
|
||||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
"ivec2 affineInterpolate();\n"
|
||||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
|
||||||
|
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" ivec2 mat[4];\n"
|
" ivec2 coord = affineInterpolate();\n"
|
||||||
" ivec2 offset[4];\n"
|
|
||||||
" vec2 incoord = texCoord;\n"
|
|
||||||
" if (mosaic.x > 1) {\n"
|
|
||||||
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
|
|
||||||
" }\n"
|
|
||||||
" if (mosaic.y > 1) {\n"
|
|
||||||
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
|
|
||||||
" }\n"
|
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
|
||||||
" float y = fract(incoord.y);\n"
|
|
||||||
" float start = 0.75;\n"
|
|
||||||
" if (int(incoord.y) - range.x < 4) {\n"
|
|
||||||
" y = incoord.y - float(range.x);\n"
|
|
||||||
" start = 0.;\n"
|
|
||||||
" }\n"
|
|
||||||
" float lin = start + y * 0.25;\n"
|
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
|
||||||
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
|
||||||
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
@ -386,30 +369,10 @@ static const char* const _renderMode4 =
|
||||||
"uniform ivec2 mosaic;\n"
|
"uniform ivec2 mosaic;\n"
|
||||||
"OUT(0) out vec4 color;\n"
|
"OUT(0) out vec4 color;\n"
|
||||||
|
|
||||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
"ivec2 affineInterpolate();\n"
|
||||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
|
||||||
|
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" ivec2 mat[4];\n"
|
" ivec2 coord = affineInterpolate();\n"
|
||||||
" ivec2 offset[4];\n"
|
|
||||||
" vec2 incoord = texCoord;\n"
|
|
||||||
" if (mosaic.x > 1) {\n"
|
|
||||||
" incoord.x = float(MOSAIC(incoord.x, mosaic.x));\n"
|
|
||||||
" }\n"
|
|
||||||
" if (mosaic.y > 1) {\n"
|
|
||||||
" incoord.y = float(MOSAIC(incoord.y, mosaic.y));\n"
|
|
||||||
" }\n"
|
|
||||||
" loadAffine(int(incoord.y), mat, offset);\n"
|
|
||||||
" float y = fract(incoord.y);\n"
|
|
||||||
" float start = 0.75;\n"
|
|
||||||
" if (int(incoord.y) - range.x < 4) {\n"
|
|
||||||
" y = incoord.y - float(range.x);\n"
|
|
||||||
" start = 0.;\n"
|
|
||||||
" }\n"
|
|
||||||
" float lin = start + y * 0.25;\n"
|
|
||||||
" vec2 mixedTransform = interpolate(mat, lin);\n"
|
|
||||||
" vec2 mixedOffset = interpolate(offset, lin);\n"
|
|
||||||
" ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
|
|
||||||
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
||||||
" discard;\n"
|
" discard;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
#include <mgba-util/memory.h>
|
#include <mgba-util/memory.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
#ifdef PSP2
|
||||||
|
#include <psp2/rtc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
@ -637,6 +641,9 @@ void GBASavedataRTCRead(struct GBASavedata* savedata) {
|
||||||
}
|
}
|
||||||
LOAD_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
|
LOAD_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
|
||||||
|
|
||||||
|
time_t rtcTime;
|
||||||
|
|
||||||
|
#ifndef PSP2
|
||||||
struct tm date;
|
struct tm date;
|
||||||
date.tm_year = _unBCD(savedata->gpio->rtc.time[0]) + 100;
|
date.tm_year = _unBCD(savedata->gpio->rtc.time[0]) + 100;
|
||||||
date.tm_mon = _unBCD(savedata->gpio->rtc.time[1]) - 1;
|
date.tm_mon = _unBCD(savedata->gpio->rtc.time[1]) - 1;
|
||||||
|
@ -645,8 +652,40 @@ void GBASavedataRTCRead(struct GBASavedata* savedata) {
|
||||||
date.tm_min = _unBCD(savedata->gpio->rtc.time[5]);
|
date.tm_min = _unBCD(savedata->gpio->rtc.time[5]);
|
||||||
date.tm_sec = _unBCD(savedata->gpio->rtc.time[6]);
|
date.tm_sec = _unBCD(savedata->gpio->rtc.time[6]);
|
||||||
date.tm_isdst = -1;
|
date.tm_isdst = -1;
|
||||||
|
rtcTime = mktime(&date);
|
||||||
|
#else
|
||||||
|
struct SceDateTime date;
|
||||||
|
date.year = _unBCD(savedata->gpio->rtc.time[0]) + 2000;
|
||||||
|
date.month = _unBCD(savedata->gpio->rtc.time[1]);
|
||||||
|
date.day = _unBCD(savedata->gpio->rtc.time[2]);
|
||||||
|
date.hour = _unBCD(savedata->gpio->rtc.time[4]);
|
||||||
|
date.minute = _unBCD(savedata->gpio->rtc.time[5]);
|
||||||
|
date.second = _unBCD(savedata->gpio->rtc.time[6]);
|
||||||
|
date.microsecond = 0;
|
||||||
|
|
||||||
savedata->gpio->rtc.offset = savedata->gpio->rtc.lastLatch - mktime(&date);
|
struct SceRtcTick tick;
|
||||||
|
int res;
|
||||||
|
res = sceRtcConvertDateTimeToTick(&date, &tick);
|
||||||
|
if (res < 0) {
|
||||||
|
mLOG(GBA_SAVE, ERROR, "sceRtcConvertDateTimeToTick %lx", res);
|
||||||
|
}
|
||||||
|
res = sceRtcConvertLocalTimeToUtc(&tick, &tick);
|
||||||
|
if (res < 0) {
|
||||||
|
mLOG(GBA_SAVE, ERROR, "sceRtcConvertUtcToLocalTime %lx", res);
|
||||||
|
}
|
||||||
|
res = sceRtcConvertTickToDateTime(&tick, &date);
|
||||||
|
if (res < 0) {
|
||||||
|
mLOG(GBA_SAVE, ERROR, "sceRtcConvertTickToDateTime %lx", res);
|
||||||
|
}
|
||||||
|
res = sceRtcConvertDateTimeToTime_t(&date, &rtcTime);
|
||||||
|
if (res < 0) {
|
||||||
|
mLOG(GBA_SAVE, ERROR, "sceRtcConvertDateTimeToTime_t %lx", res);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
savedata->gpio->rtc.offset = savedata->gpio->rtc.lastLatch - rtcTime;
|
||||||
|
|
||||||
|
mLOG(GBA_SAVE, ERROR, "Savegame time offset set to %li", savedata->gpio->rtc.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
|
void GBASavedataSerialize(const struct GBASavedata* savedata, struct GBASerializedState* state) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba-util/gui/font.h>
|
#include <mgba-util/gui/font.h>
|
||||||
#include <mgba-util/gui/font-metrics.h>
|
#include <mgba-util/gui/font-metrics.h>
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
#include "icons.h"
|
#include "icons.h"
|
||||||
|
|
||||||
|
|
|
@ -873,7 +873,7 @@ int main(int argc, char* argv[]) {
|
||||||
u8 model = 0;
|
u8 model = 0;
|
||||||
cfguInit();
|
cfguInit();
|
||||||
CFGU_GetSystemModel(&model);
|
CFGU_GetSystemModel(&model);
|
||||||
if (model != 3 /* o2DS */) {
|
if (model != CFG_MODEL_2DS) {
|
||||||
gfxSetWide(true);
|
gfxSetWide(true);
|
||||||
}
|
}
|
||||||
cfguExit();
|
cfguExit();
|
||||||
|
|
|
@ -63,7 +63,7 @@ static inline void _setTexDims(int width, int height) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
|
static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct mRectangle* dims) {
|
||||||
struct mGLContext* context = (struct mGLContext*) v;
|
struct mGLContext* context = (struct mGLContext*) v;
|
||||||
if (layer >= VIDEO_LAYER_MAX) {
|
if (layer >= VIDEO_LAYER_MAX) {
|
||||||
return;
|
return;
|
||||||
|
@ -89,7 +89,7 @@ static void mGLContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mGLContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
|
static void mGLContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct mRectangle* dims) {
|
||||||
struct mGLContext* context = (struct mGLContext*) v;
|
struct mGLContext* context = (struct mGLContext*) v;
|
||||||
if (layer >= VIDEO_LAYER_MAX) {
|
if (layer >= VIDEO_LAYER_MAX) {
|
||||||
return;
|
return;
|
||||||
|
@ -141,13 +141,13 @@ static void _setFilter(struct VideoBackend* v) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _setFrame(struct Rectangle* dims, int frameW, int frameH) {
|
static void _setFrame(struct mRectangle* dims, struct mRectangle* frame) {
|
||||||
GLint viewport[4];
|
GLint viewport[4];
|
||||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||||
glScissor(viewport[0] + dims->x * viewport[2] / frameW,
|
glScissor(viewport[0] + (dims->x - frame->x) * viewport[2] / frame->width,
|
||||||
viewport[1] + dims->y * viewport[3] / frameH,
|
viewport[1] + (dims->y - frame->y) * viewport[3] / frame->height,
|
||||||
dims->width * viewport[2] / frameW,
|
dims->width * viewport[2] / frame->width,
|
||||||
dims->height * viewport[3] / frameH);
|
dims->height * viewport[3] / frame->height);
|
||||||
glTranslatef(dims->x, dims->y, 0);
|
glTranslatef(dims->x, dims->y, 0);
|
||||||
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
|
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
|
||||||
}
|
}
|
||||||
|
@ -162,9 +162,9 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
|
||||||
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
|
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
unsigned frameW, frameH;
|
struct mRectangle frame;
|
||||||
VideoBackendGetFrameSize(v, &frameW, &frameH);
|
VideoBackendGetFrame(v, &frame);
|
||||||
glOrtho(0, frameW, frameH, 0, 0, 1);
|
glOrtho(frame.x, frame.x + frame.width, frame.y + frame.height, frame.y, 0, 1);
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
|
||||||
|
@ -178,12 +178,12 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
|
||||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||||
_setFilter(v);
|
_setFilter(v);
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
_setFrame(&context->layerDims[layer], frameW, frameH);
|
_setFrame(&context->layerDims[layer], &frame);
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH);
|
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], &frame);
|
||||||
if (v->interframeBlending) {
|
if (v->interframeBlending) {
|
||||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||||
glBlendColor(1, 1, 1, 0.5);
|
glBlendColor(1, 1, 1, 0.5);
|
||||||
|
|
|
@ -21,7 +21,7 @@ CXX_GUARD_START
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "platform/video-backend.h"
|
#include <mgba/feature/video-backend.h>
|
||||||
|
|
||||||
struct mGLContext {
|
struct mGLContext {
|
||||||
struct VideoBackend d;
|
struct VideoBackend d;
|
||||||
|
@ -29,8 +29,8 @@ struct mGLContext {
|
||||||
int activeTex;
|
int activeTex;
|
||||||
GLuint tex[2];
|
GLuint tex[2];
|
||||||
GLuint layers[VIDEO_LAYER_MAX];
|
GLuint layers[VIDEO_LAYER_MAX];
|
||||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
struct mRectangle layerDims[VIDEO_LAYER_MAX];
|
||||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
struct mSize imageSizes[VIDEO_LAYER_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
void mGLContextCreate(struct mGLContext*);
|
void mGLContextCreate(struct mGLContext*);
|
||||||
|
|
|
@ -194,7 +194,7 @@ static inline void _setTexDims(int width, int height) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct Rectangle* dims) {
|
static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct mRectangle* dims) {
|
||||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||||
if (layer >= VIDEO_LAYER_MAX) {
|
if (layer >= VIDEO_LAYER_MAX) {
|
||||||
return;
|
return;
|
||||||
|
@ -212,10 +212,9 @@ static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLa
|
||||||
context->layerDims[layer].x = dims->x;
|
context->layerDims[layer].x = dims->x;
|
||||||
context->layerDims[layer].y = dims->y;
|
context->layerDims[layer].y = dims->y;
|
||||||
|
|
||||||
unsigned newW;
|
struct mRectangle frame;
|
||||||
unsigned newH;
|
VideoBackendGetFrame(v, &frame);
|
||||||
VideoBackendGetFrameSize(v, &newW, &newH);
|
if (frame.width != context->width || frame.height != context->height) {
|
||||||
if (newW != context->width || newH != context->height) {
|
|
||||||
size_t n;
|
size_t n;
|
||||||
for (n = 0; n < context->nShaders; ++n) {
|
for (n = 0; n < context->nShaders; ++n) {
|
||||||
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
|
if (context->shaders[n].width < 0 || context->shaders[n].height < 0) {
|
||||||
|
@ -224,12 +223,14 @@ static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLa
|
||||||
}
|
}
|
||||||
context->initialShader.dirty = true;
|
context->initialShader.dirty = true;
|
||||||
context->interframeShader.dirty = true;
|
context->interframeShader.dirty = true;
|
||||||
context->width = newW;
|
context->width = frame.width;
|
||||||
context->height = newH;
|
context->height = frame.height;
|
||||||
}
|
}
|
||||||
|
context->x = frame.x;
|
||||||
|
context->y = frame.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mGLES2ContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct Rectangle* dims) {
|
static void mGLES2ContextLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct mRectangle* dims) {
|
||||||
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
struct mGLES2Context* context = (struct mGLES2Context*) v;
|
||||||
if (layer >= VIDEO_LAYER_MAX) {
|
if (layer >= VIDEO_LAYER_MAX) {
|
||||||
return;
|
return;
|
||||||
|
@ -320,7 +321,7 @@ static void _drawShaderEx(struct mGLES2Context* context, struct mGLES2Shader* sh
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layer >= 0 && layer < VIDEO_LAYER_MAX) {
|
if (layer >= 0 && layer < VIDEO_LAYER_MAX) {
|
||||||
glViewport(context->layerDims[layer].x, context->layerDims[layer].y, context->layerDims[layer].width, context->layerDims[layer].height);
|
glViewport(context->layerDims[layer].x - context->x, context->layerDims[layer].y - context->y, context->layerDims[layer].width, context->layerDims[layer].height);
|
||||||
} else {
|
} else {
|
||||||
glViewport(padW, padH, drawW, drawH);
|
glViewport(padW, padH, drawW, drawH);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ CXX_GUARD_START
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "platform/video-backend.h"
|
#include <mgba/feature/video-backend.h>
|
||||||
|
|
||||||
union mGLES2UniformValue {
|
union mGLES2UniformValue {
|
||||||
GLfloat f;
|
GLfloat f;
|
||||||
|
@ -82,8 +82,10 @@ struct mGLES2Context {
|
||||||
GLuint tex[VIDEO_LAYER_MAX];
|
GLuint tex[VIDEO_LAYER_MAX];
|
||||||
GLuint vbo;
|
GLuint vbo;
|
||||||
|
|
||||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
struct mRectangle layerDims[VIDEO_LAYER_MAX];
|
||||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
struct mSize imageSizes[VIDEO_LAYER_MAX];
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
unsigned height;
|
unsigned height;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
|
||||||
-lScePgf_stub
|
-lScePgf_stub
|
||||||
-lScePhotoExport_stub
|
-lScePhotoExport_stub
|
||||||
-lScePower_stub
|
-lScePower_stub
|
||||||
|
-lSceRtc_stub
|
||||||
-lSceSysmodule_stub
|
-lSceSysmodule_stub
|
||||||
-lSceTouch_stub)
|
-lSceTouch_stub)
|
||||||
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
|
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
|
||||||
|
|
|
@ -53,7 +53,7 @@ void free(void*);
|
||||||
#undef PYEXPORT
|
#undef PYEXPORT
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include <mgba/gba/interface.h>
|
#include <mgba/gba/interface.h>
|
||||||
|
|
|
@ -42,7 +42,7 @@ ffi.set_source("mgba._pylib", """
|
||||||
#include <mgba/internal/sm83/sm83.h>
|
#include <mgba/internal/sm83/sm83.h>
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
#include <mgba/internal/gb/renderers/cache-set.h>
|
#include <mgba/internal/gb/renderers/cache-set.h>
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
#define PYEXPORT
|
#define PYEXPORT
|
||||||
|
|
|
@ -21,20 +21,16 @@ class PNG:
|
||||||
def write_header(self, image):
|
def write_header(self, image):
|
||||||
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
||||||
if self.mode == MODE_RGB:
|
if self.mode == MODE_RGB:
|
||||||
self._info = lib.PNGWriteHeader(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeader(self._png, image.width, image.height, lib.mCOLOR_XBGR8)
|
||||||
if self.mode == MODE_RGBA:
|
if self.mode == MODE_RGBA:
|
||||||
self._info = lib.PNGWriteHeaderA(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeader(self._png, image.width, image.height, lib.mCOLOR_ABGR8)
|
||||||
if self.mode == MODE_INDEX:
|
|
||||||
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
|
||||||
return self._info != ffi.NULL
|
return self._info != ffi.NULL
|
||||||
|
|
||||||
def write_pixels(self, image):
|
def write_pixels(self, image):
|
||||||
if self.mode == MODE_RGB:
|
if self.mode == MODE_RGB:
|
||||||
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer, lib.mCOLOR_XBGR8)
|
||||||
if self.mode == MODE_RGBA:
|
if self.mode == MODE_RGBA:
|
||||||
return lib.PNGWritePixelsA(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer, lib.mCOLOR_ABGR8)
|
||||||
if self.mode == MODE_INDEX:
|
|
||||||
return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def write_close(self):
|
def write_close(self):
|
||||||
|
|
|
@ -125,6 +125,7 @@ ConfigController::ConfigController(QObject* parent)
|
||||||
m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL;
|
m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL;
|
||||||
m_opts.rewindEnable = false;
|
m_opts.rewindEnable = false;
|
||||||
m_opts.rewindBufferCapacity = 300;
|
m_opts.rewindBufferCapacity = 300;
|
||||||
|
m_opts.rewindBufferInterval = 1;
|
||||||
m_opts.useBios = true;
|
m_opts.useBios = true;
|
||||||
m_opts.suspendScreensaver = true;
|
m_opts.suspendScreensaver = true;
|
||||||
m_opts.lockAspectRatio = true;
|
m_opts.lockAspectRatio = true;
|
||||||
|
|
|
@ -297,13 +297,6 @@ void CoreController::loadConfig(ConfigController* config) {
|
||||||
mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "mute");
|
mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "mute");
|
||||||
m_preload = config->getOption("preload").toInt();
|
m_preload = config->getOption("preload").toInt();
|
||||||
|
|
||||||
int playerId = m_multiplayer->playerId(this) + 1;
|
|
||||||
QVariant savePlayerId = config->getOption("savePlayerId");
|
|
||||||
if (m_multiplayer->attached() < 2 && savePlayerId.canConvert<int>()) {
|
|
||||||
playerId = savePlayerId.toInt();
|
|
||||||
}
|
|
||||||
mCoreConfigSetOverrideIntValue(&m_threadContext.core->config, "savePlayerId", playerId);
|
|
||||||
|
|
||||||
QSize sizeBefore = screenDimensions();
|
QSize sizeBefore = screenDimensions();
|
||||||
m_activeBuffer.resize(256 * 224 * sizeof(color_t));
|
m_activeBuffer.resize(256 * 224 * sizeof(color_t));
|
||||||
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer.data()), sizeBefore.width());
|
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer.data()), sizeBefore.width());
|
||||||
|
@ -1000,9 +993,9 @@ void CoreController::attachPrinter() {
|
||||||
}
|
}
|
||||||
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
||||||
clearMultiplayerController();
|
clearMultiplayerController();
|
||||||
GBPrinterCreate(&m_printer.d);
|
GBPrinterCreate(&m_printer);
|
||||||
m_printer.parent = this;
|
m_printer.parent = this;
|
||||||
m_printer.d.print = [](GBPrinter* printer, int height, const uint8_t* data) {
|
m_printer.print = [](GBPrinter* printer, int height, const uint8_t* data) {
|
||||||
QGBPrinter* qPrinter = reinterpret_cast<QGBPrinter*>(printer);
|
QGBPrinter* qPrinter = reinterpret_cast<QGBPrinter*>(printer);
|
||||||
QImage image(GB_VIDEO_HORIZONTAL_PIXELS, height, QImage::Format_Indexed8);
|
QImage image(GB_VIDEO_HORIZONTAL_PIXELS, height, QImage::Format_Indexed8);
|
||||||
QVector<QRgb> colors;
|
QVector<QRgb> colors;
|
||||||
|
@ -1023,7 +1016,7 @@ void CoreController::attachPrinter() {
|
||||||
QMetaObject::invokeMethod(qPrinter->parent, "imagePrinted", Q_ARG(const QImage&, image));
|
QMetaObject::invokeMethod(qPrinter->parent, "imagePrinted", Q_ARG(const QImage&, image));
|
||||||
};
|
};
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
GBSIOSetDriver(&gb->sio, &m_printer.d.d);
|
GBSIOSetDriver(&gb->sio, &m_printer.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::detachPrinter() {
|
void CoreController::detachPrinter() {
|
||||||
|
@ -1032,7 +1025,7 @@ void CoreController::detachPrinter() {
|
||||||
}
|
}
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
||||||
GBPrinterDonePrinting(&m_printer.d);
|
GBPrinterDonePrinting(&m_printer);
|
||||||
GBSIOSetDriver(&gb->sio, nullptr);
|
GBSIOSetDriver(&gb->sio, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1041,7 +1034,7 @@ void CoreController::endPrint() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
GBPrinterDonePrinting(&m_printer.d);
|
GBPrinterDonePrinting(&m_printer);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1199,7 +1192,12 @@ void CoreController::updatePlayerSave() {
|
||||||
int savePlayerId = 0;
|
int savePlayerId = 0;
|
||||||
mCoreConfigGetIntValue(&m_threadContext.core->config, "savePlayerId", &savePlayerId);
|
mCoreConfigGetIntValue(&m_threadContext.core->config, "savePlayerId", &savePlayerId);
|
||||||
if (savePlayerId == 0 || m_multiplayer->attached() > 1) {
|
if (savePlayerId == 0 || m_multiplayer->attached() > 1) {
|
||||||
savePlayerId = m_multiplayer->playerId(this) + 1;
|
if (savePlayerId == m_multiplayer->playerId(this) + 1) {
|
||||||
|
// Player 1 is using our save, so let's use theirs, at least for now.
|
||||||
|
savePlayerId = 1;
|
||||||
|
} else {
|
||||||
|
savePlayerId = m_multiplayer->playerId(this) + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString saveSuffix;
|
QString saveSuffix;
|
||||||
|
|
|
@ -313,8 +313,7 @@ private:
|
||||||
VFile* m_vlVf = nullptr;
|
VFile* m_vlVf = nullptr;
|
||||||
|
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
struct QGBPrinter {
|
struct QGBPrinter : public GBPrinter {
|
||||||
GBPrinter d;
|
|
||||||
CoreController* parent;
|
CoreController* parent;
|
||||||
} m_printer;
|
} m_printer;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,18 +19,18 @@ using namespace QGBA;
|
||||||
DebuggerConsoleController::DebuggerConsoleController(QObject* parent)
|
DebuggerConsoleController::DebuggerConsoleController(QObject* parent)
|
||||||
: DebuggerController(&m_cliDebugger.d, parent)
|
: DebuggerController(&m_cliDebugger.d, parent)
|
||||||
{
|
{
|
||||||
m_backend.d.printf = printf;
|
m_backend.printf = printf;
|
||||||
m_backend.d.init = init;
|
m_backend.init = init;
|
||||||
m_backend.d.deinit = deinit;
|
m_backend.deinit = deinit;
|
||||||
m_backend.d.readline = readLine;
|
m_backend.readline = readLine;
|
||||||
m_backend.d.lineAppend = lineAppend;
|
m_backend.lineAppend = lineAppend;
|
||||||
m_backend.d.historyLast = historyLast;
|
m_backend.historyLast = historyLast;
|
||||||
m_backend.d.historyAppend = historyAppend;
|
m_backend.historyAppend = historyAppend;
|
||||||
m_backend.d.interrupt = interrupt;
|
m_backend.interrupt = interrupt;
|
||||||
m_backend.self = this;
|
m_backend.self = this;
|
||||||
|
|
||||||
CLIDebuggerCreate(&m_cliDebugger);
|
CLIDebuggerCreate(&m_cliDebugger);
|
||||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d);
|
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerConsoleController::enterLine(const QString& line) {
|
void DebuggerConsoleController::enterLine(const QString& line) {
|
||||||
|
@ -60,7 +60,7 @@ void DebuggerConsoleController::attachInternal() {
|
||||||
CoreController::Interrupter interrupter(m_gameController);
|
CoreController::Interrupter interrupter(m_gameController);
|
||||||
QMutexLocker lock(&m_mutex);
|
QMutexLocker lock(&m_mutex);
|
||||||
mCore* core = m_gameController->thread()->core;
|
mCore* core = m_gameController->thread()->core;
|
||||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d);
|
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend);
|
||||||
CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core));
|
CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,7 @@ private:
|
||||||
QStringList m_lines;
|
QStringList m_lines;
|
||||||
QByteArray m_last;
|
QByteArray m_last;
|
||||||
|
|
||||||
struct Backend {
|
struct Backend : public CLIDebuggerBackend {
|
||||||
CLIDebuggerBackend d;
|
|
||||||
DebuggerConsoleController* self;
|
DebuggerConsoleController* self;
|
||||||
} m_backend;
|
} m_backend;
|
||||||
};
|
};
|
||||||
|
|
|
@ -129,6 +129,7 @@ void QGBA::Display::configure(ConfigController* config) {
|
||||||
filter(opts->resampleVideo);
|
filter(opts->resampleVideo);
|
||||||
config->updateOption("showOSD");
|
config->updateOption("showOSD");
|
||||||
config->updateOption("showFrameCounter");
|
config->updateOption("showFrameCounter");
|
||||||
|
config->updateOption("videoSync");
|
||||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
if (opts->shader && supportsShaders()) {
|
if (opts->shader && supportsShaders()) {
|
||||||
struct VDir* shader = VDirOpen(opts->shader);
|
struct VDir* shader = VDirOpen(opts->shader);
|
||||||
|
|
|
@ -78,6 +78,7 @@ public slots:
|
||||||
virtual void showOSDMessages(bool enable);
|
virtual void showOSDMessages(bool enable);
|
||||||
virtual void showFrameCounter(bool enable);
|
virtual void showFrameCounter(bool enable);
|
||||||
virtual void filter(bool filter);
|
virtual void filter(bool filter);
|
||||||
|
virtual void swapInterval(int interval) = 0;
|
||||||
virtual void framePosted() = 0;
|
virtual void framePosted() = 0;
|
||||||
virtual void setShaders(struct VDir*) = 0;
|
virtual void setShaders(struct VDir*) = 0;
|
||||||
virtual void clearShaders() = 0;
|
virtual void clearShaders() = 0;
|
||||||
|
|
|
@ -38,6 +38,21 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
#include <OpenGL/OpenGL.h>
|
||||||
|
#endif
|
||||||
|
#ifdef USE_GLX
|
||||||
|
#define GLX_GLXEXT_PROTOTYPES
|
||||||
|
typedef struct _XDisplay Display;
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#include <GL/glxext.h>
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EGL
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define OVERHEAD_NSEC 1000000
|
#define OVERHEAD_NSEC 1000000
|
||||||
#else
|
#else
|
||||||
|
@ -48,11 +63,6 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
enum ThreadStartFrom {
|
|
||||||
START = 1,
|
|
||||||
PROXY = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash<QSurfaceFormat, bool> DisplayGL::s_supports;
|
QHash<QSurfaceFormat, bool> DisplayGL::s_supports;
|
||||||
|
|
||||||
uint qHash(const QSurfaceFormat& format, uint seed) {
|
uint qHash(const QSurfaceFormat& format, uint seed) {
|
||||||
|
@ -206,11 +216,6 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
||||||
m_drawThread.setObjectName("Painter Thread");
|
m_drawThread.setObjectName("Painter Thread");
|
||||||
m_painter->setThread(&m_drawThread);
|
m_painter->setThread(&m_drawThread);
|
||||||
|
|
||||||
m_proxyThread.setObjectName("OpenGL Proxy Thread");
|
|
||||||
m_proxyContext = std::make_unique<QOpenGLContext>();
|
|
||||||
m_proxyContext->setFormat(format);
|
|
||||||
connect(m_painter.get(), &PainterGL::created, this, &DisplayGL::setupProxyThread);
|
|
||||||
|
|
||||||
connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create);
|
connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create);
|
||||||
connect(m_painter.get(), &PainterGL::started, this, [this] {
|
connect(m_painter.get(), &PainterGL::started, this, [this] {
|
||||||
m_hasStarted = true;
|
m_hasStarted = true;
|
||||||
|
@ -225,11 +230,6 @@ DisplayGL::~DisplayGL() {
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "destroy", Qt::BlockingQueuedConnection);
|
QMetaObject::invokeMethod(m_painter.get(), "destroy", Qt::BlockingQueuedConnection);
|
||||||
m_drawThread.exit();
|
m_drawThread.exit();
|
||||||
m_drawThread.wait();
|
m_drawThread.wait();
|
||||||
|
|
||||||
if (m_proxyThread.isRunning()) {
|
|
||||||
m_proxyThread.exit();
|
|
||||||
m_proxyThread.wait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DisplayGL::supportsShaders() const {
|
bool DisplayGL::supportsShaders() const {
|
||||||
|
@ -253,6 +253,9 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
m_painter->setContext(controller);
|
m_painter->setContext(controller);
|
||||||
m_painter->setMessagePainter(messagePainter());
|
m_painter->setMessagePainter(messagePainter());
|
||||||
m_context = controller;
|
m_context = controller;
|
||||||
|
if (videoProxy()) {
|
||||||
|
videoProxy()->moveToThread(&m_drawThread);
|
||||||
|
}
|
||||||
|
|
||||||
lockAspectRatio(isAspectRatioLocked());
|
lockAspectRatio(isAspectRatioLocked());
|
||||||
lockIntegerScaling(isIntegerScalingLocked());
|
lockIntegerScaling(isIntegerScalingLocked());
|
||||||
|
@ -267,15 +270,6 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
messagePainter()->resize(size(), devicePixelRatio());
|
messagePainter()->resize(size(), devicePixelRatio());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
startThread(ThreadStartFrom::START);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayGL::startThread(int from) {
|
|
||||||
m_threadStartPending |= from;
|
|
||||||
if (m_threadStartPending < 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreController::Interrupter interrupter(m_context);
|
CoreController::Interrupter interrupter(m_context);
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "start");
|
QMetaObject::invokeMethod(m_painter.get(), "start");
|
||||||
if (!m_gl) {
|
if (!m_gl) {
|
||||||
|
@ -353,7 +347,6 @@ void DisplayGL::stopDrawing() {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
setUpdatesEnabled(true);
|
setUpdatesEnabled(true);
|
||||||
m_threadStartPending &= ~1;
|
|
||||||
}
|
}
|
||||||
m_context.reset();
|
m_context.reset();
|
||||||
}
|
}
|
||||||
|
@ -419,6 +412,10 @@ void DisplayGL::filter(bool filter) {
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter));
|
QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DisplayGL::swapInterval(int interval) {
|
||||||
|
QMetaObject::invokeMethod(m_painter.get(), "swapInterval", Q_ARG(int, interval));
|
||||||
|
}
|
||||||
|
|
||||||
void DisplayGL::framePosted() {
|
void DisplayGL::framePosted() {
|
||||||
m_painter->enqueue(m_context->drawContext());
|
m_painter->enqueue(m_context->drawContext());
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "draw");
|
QMetaObject::invokeMethod(m_painter.get(), "draw");
|
||||||
|
@ -474,35 +471,11 @@ bool DisplayGL::shouldDisableUpdates() {
|
||||||
void DisplayGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
void DisplayGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
||||||
Display::setVideoProxy(proxy);
|
Display::setVideoProxy(proxy);
|
||||||
if (proxy) {
|
if (proxy) {
|
||||||
proxy->moveToThread(&m_proxyThread);
|
proxy->moveToThread(&m_drawThread);
|
||||||
}
|
}
|
||||||
m_painter->setVideoProxy(proxy);
|
m_painter->setVideoProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisplayGL::setupProxyThread() {
|
|
||||||
m_proxyContext->moveToThread(&m_proxyThread);
|
|
||||||
m_proxySurface.create();
|
|
||||||
connect(&m_proxyThread, &QThread::started, m_proxyContext.get(), [this]() {
|
|
||||||
m_proxyContext->setShareContext(m_painter->shareContext());
|
|
||||||
m_proxyContext->create();
|
|
||||||
m_proxyContext->makeCurrent(&m_proxySurface);
|
|
||||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
|
||||||
epoxy_handle_external_wglMakeCurrent();
|
|
||||||
#endif
|
|
||||||
QMetaObject::invokeMethod(this, "startThread", Q_ARG(int, ThreadStartFrom::PROXY));
|
|
||||||
});
|
|
||||||
connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() {
|
|
||||||
if (!m_context->hardwareAccelerated()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (videoProxy()) {
|
|
||||||
videoProxy()->processData();
|
|
||||||
}
|
|
||||||
m_painter->updateFramebufferHandle();
|
|
||||||
}, Qt::BlockingQueuedConnection);
|
|
||||||
m_proxyThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayGL::updateContentSize() {
|
void DisplayGL::updateContentSize() {
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "contentSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QSize, m_cachedContentSize));
|
QMetaObject::invokeMethod(m_painter.get(), "contentSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QSize, m_cachedContentSize));
|
||||||
}
|
}
|
||||||
|
@ -577,12 +550,6 @@ void PainterGL::create() {
|
||||||
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
|
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
|
||||||
mGLES2ContextCreate(gl2Backend);
|
mGLES2ContextCreate(gl2Backend);
|
||||||
m_backend = &gl2Backend->d;
|
m_backend = &gl2Backend->d;
|
||||||
QOpenGLFunctions* fn = m_gl->functions();
|
|
||||||
fn->glGenTextures(m_bridgeTexes.size(), m_bridgeTexes.data());
|
|
||||||
for (auto tex : m_bridgeTexes) {
|
|
||||||
m_freeTex.enqueue(tex);
|
|
||||||
}
|
|
||||||
m_bridgeTexIn = m_freeTex.dequeue();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -650,11 +617,9 @@ void PainterGL::destroy() {
|
||||||
}
|
}
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
QOpenGLFunctions* fn = m_gl->functions();
|
|
||||||
if (m_shader.passes) {
|
if (m_shader.passes) {
|
||||||
mGLES2ShaderFree(&m_shader);
|
mGLES2ShaderFree(&m_shader);
|
||||||
}
|
}
|
||||||
fn->glDeleteTextures(m_bridgeTexes.size(), m_bridgeTexes.data());
|
|
||||||
#endif
|
#endif
|
||||||
m_backend->deinit(m_backend);
|
m_backend->deinit(m_backend);
|
||||||
m_gl->doneCurrent();
|
m_gl->doneCurrent();
|
||||||
|
@ -686,7 +651,7 @@ void PainterGL::resizeContext() {
|
||||||
}
|
}
|
||||||
dequeueAll(false);
|
dequeueAll(false);
|
||||||
|
|
||||||
Rectangle dims = {0, 0, size.width(), size.height()};
|
mRectangle dims = {0, 0, size.width(), size.height()};
|
||||||
m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_IMAGE, &dims);
|
m_backend->setLayerDimensions(m_backend, VIDEO_LAYER_IMAGE, &dims);
|
||||||
recenterLayers();
|
recenterLayers();
|
||||||
}
|
}
|
||||||
|
@ -699,26 +664,20 @@ void PainterGL::recenterLayers() {
|
||||||
if (!m_context) {
|
if (!m_context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND, VIDEO_LAYER_IMAGE};
|
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND};
|
||||||
Rectangle frame = {0};
|
int width, height;
|
||||||
|
mRectangle frame = {0};
|
||||||
|
m_backend->imageSize(m_backend, VIDEO_LAYER_IMAGE, &width, &height);
|
||||||
|
frame.width = width;
|
||||||
|
frame.height = height;
|
||||||
unsigned scale = std::max(1U, m_context->videoScale());
|
unsigned scale = std::max(1U, m_context->videoScale());
|
||||||
|
|
||||||
for (VideoLayer l : centeredLayers) {
|
for (VideoLayer l : centeredLayers) {
|
||||||
Rectangle dims{};
|
mRectangle dims{};
|
||||||
int width, height;
|
|
||||||
m_backend->imageSize(m_backend, l, &width, &height);
|
m_backend->imageSize(m_backend, l, &width, &height);
|
||||||
dims.width = width;
|
dims.width = width * scale;
|
||||||
dims.height = height;
|
dims.height = height * scale;
|
||||||
if (l != VIDEO_LAYER_IMAGE) {
|
mRectangleCenter(&frame, &dims);
|
||||||
dims.width *= scale;
|
|
||||||
dims.height *= scale;
|
|
||||||
m_backend->setLayerDimensions(m_backend, l, &dims);
|
|
||||||
}
|
|
||||||
RectangleUnion(&frame, &dims);
|
|
||||||
}
|
|
||||||
for (VideoLayer l : centeredLayers) {
|
|
||||||
Rectangle dims;
|
|
||||||
m_backend->layerDimensions(m_backend, l, &dims);
|
|
||||||
RectangleCenter(&frame, &dims);
|
|
||||||
m_backend->setLayerDimensions(m_backend, l, &dims);
|
m_backend->setLayerDimensions(m_backend, l, &dims);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -764,6 +723,32 @@ void PainterGL::filter(bool filter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PainterGL::swapInterval(int interval) {
|
||||||
|
if (!m_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_swapInterval = interval;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
wglSwapIntervalEXT(interval);
|
||||||
|
#elif defined(Q_OS_MAC)
|
||||||
|
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
|
||||||
|
#else
|
||||||
|
#ifdef USE_GLX
|
||||||
|
if (QGuiApplication::platformName() == "xcb") {
|
||||||
|
::Display* display = glXGetCurrentDisplay();
|
||||||
|
GLXDrawable drawable = glXGetCurrentDrawable();
|
||||||
|
glXSwapIntervalEXT(display, drawable, interval);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_EGL
|
||||||
|
if (QGuiApplication::platformName().contains("egl") || QGuiApplication::platformName() == "wayland") {
|
||||||
|
EGLDisplay display = eglGetCurrentDisplay();
|
||||||
|
eglSwapInterval(display, interval);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
|
#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
|
||||||
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
||||||
#endif
|
#endif
|
||||||
|
@ -785,16 +770,16 @@ void PainterGL::start() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
resizeContext();
|
resizeContext();
|
||||||
m_context->addFrameAction(std::bind(&PainterGL::swapTex, this));
|
|
||||||
|
|
||||||
m_buffer = nullptr;
|
m_buffer = nullptr;
|
||||||
m_active = true;
|
m_active = true;
|
||||||
m_started = true;
|
m_started = true;
|
||||||
|
swapInterval(1);
|
||||||
emit started();
|
emit started();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::draw() {
|
void PainterGL::draw() {
|
||||||
if (!m_started || (m_queue.isEmpty() && m_queueTex.isEmpty())) {
|
if (!m_started || m_queue.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,12 +805,16 @@ void PainterGL::draw() {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int wantSwap = sync->audioWait || sync->videoFrameWait;
|
||||||
|
if (m_swapInterval != wantSwap) {
|
||||||
|
swapInterval(wantSwap);
|
||||||
|
}
|
||||||
dequeue();
|
dequeue();
|
||||||
bool forceRedraw = true;
|
bool forceRedraw = true;
|
||||||
if (!m_delayTimer.isValid()) {
|
if (!m_delayTimer.isValid()) {
|
||||||
m_delayTimer.start();
|
m_delayTimer.start();
|
||||||
} else {
|
} else {
|
||||||
if (sync->audioWait || sync->videoFrameWait) {
|
if (wantSwap) {
|
||||||
while (m_delayTimer.nsecsElapsed() + OVERHEAD_NSEC < 1000000000 / sync->fpsTarget) {
|
while (m_delayTimer.nsecsElapsed() + OVERHEAD_NSEC < 1000000000 / sync->fpsTarget) {
|
||||||
QThread::usleep(500);
|
QThread::usleep(500);
|
||||||
}
|
}
|
||||||
|
@ -877,6 +866,11 @@ void PainterGL::doStop() {
|
||||||
}
|
}
|
||||||
m_backend->clear(m_backend);
|
m_backend->clear(m_backend);
|
||||||
m_backend->swap(m_backend);
|
m_backend->swap(m_backend);
|
||||||
|
if (m_videoProxy) {
|
||||||
|
m_videoProxy->reset();
|
||||||
|
m_videoProxy->moveToThread(m_window->thread());
|
||||||
|
m_videoProxy.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::pause() {
|
void PainterGL::pause() {
|
||||||
|
@ -904,33 +898,22 @@ void PainterGL::performDraw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::enqueue(const uint32_t* backing) {
|
void PainterGL::enqueue(const uint32_t* backing) {
|
||||||
if (!backing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
uint32_t* buffer = nullptr;
|
uint32_t* buffer = nullptr;
|
||||||
if (m_free.isEmpty()) {
|
if (backing) {
|
||||||
buffer = m_queue.dequeue();
|
if (m_free.isEmpty()) {
|
||||||
} else {
|
buffer = m_queue.dequeue();
|
||||||
buffer = m_free.takeLast();
|
} else {
|
||||||
}
|
buffer = m_free.takeLast();
|
||||||
if (buffer) {
|
}
|
||||||
QSize size = m_context->screenDimensions();
|
if (buffer) {
|
||||||
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
|
QSize size = m_context->screenDimensions();
|
||||||
|
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_queue.enqueue(buffer);
|
m_queue.enqueue(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::enqueue(GLuint tex) {
|
|
||||||
QMutexLocker locker(&m_mutex);
|
|
||||||
if (m_freeTex.isEmpty()) {
|
|
||||||
m_bridgeTexIn = m_queueTex.dequeue();
|
|
||||||
} else {
|
|
||||||
m_bridgeTexIn = m_freeTex.takeLast();
|
|
||||||
}
|
|
||||||
m_queueTex.enqueue(tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PainterGL::dequeue() {
|
void PainterGL::dequeue() {
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
if (!m_queue.isEmpty()) {
|
if (!m_queue.isEmpty()) {
|
||||||
|
@ -940,19 +923,6 @@ void PainterGL::dequeue() {
|
||||||
}
|
}
|
||||||
m_buffer = buffer;
|
m_buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_queueTex.isEmpty()) {
|
|
||||||
if (m_bridgeTexOut != std::numeric_limits<GLuint>::max()) {
|
|
||||||
m_freeTex.enqueue(m_bridgeTexOut);
|
|
||||||
}
|
|
||||||
m_bridgeTexOut = m_queueTex.dequeue();
|
|
||||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
|
||||||
if (supportsShaders()) {
|
|
||||||
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
|
|
||||||
gl2Backend->tex[VIDEO_LAYER_IMAGE] = m_bridgeTexOut;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::dequeueAll(bool keep) {
|
void PainterGL::dequeueAll(bool keep) {
|
||||||
|
@ -973,19 +943,6 @@ void PainterGL::dequeueAll(bool keep) {
|
||||||
m_free.append(m_buffer);
|
m_free.append(m_buffer);
|
||||||
m_buffer = nullptr;
|
m_buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_queueTex.clear();
|
|
||||||
m_freeTex.clear();
|
|
||||||
for (auto tex : m_bridgeTexes) {
|
|
||||||
if (keep && tex == m_bridgeTexIn) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
m_freeTex.enqueue(tex);
|
|
||||||
}
|
|
||||||
if (!keep) {
|
|
||||||
m_bridgeTexIn = m_freeTex.dequeue();
|
|
||||||
m_bridgeTexOut = std::numeric_limits<GLuint>::max();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
void PainterGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
||||||
|
@ -1073,23 +1030,6 @@ QOpenGLContext* PainterGL::shareContext() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::updateFramebufferHandle() {
|
|
||||||
QOpenGLFunctions* fn = m_gl->functions();
|
|
||||||
// TODO: Figure out why glFlush doesn't work here on Intel/Windows
|
|
||||||
if (glContextHasBug(OpenGLBug::CROSS_THREAD_FLUSH)) {
|
|
||||||
fn->glFinish();
|
|
||||||
} else {
|
|
||||||
fn->glFlush();
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreController::Interrupter interrupter(m_context);
|
|
||||||
if (!m_context->hardwareAccelerated()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
enqueue(m_bridgeTexIn);
|
|
||||||
m_context->setFramebufferHandle(m_bridgeTexIn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PainterGL::setBackgroundImage(const QImage& image) {
|
void PainterGL::setBackgroundImage(const QImage& image) {
|
||||||
if (!m_started) {
|
if (!m_started) {
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
|
@ -1111,14 +1051,4 @@ void PainterGL::setBackgroundImage(const QImage& image) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::swapTex() {
|
|
||||||
if (!m_started) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreController::Interrupter interrupter(m_context);
|
|
||||||
emit texSwapped();
|
|
||||||
m_context->addFrameAction(std::bind(&PainterGL::swapTex, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
#include "CoreController.h"
|
#include "CoreController.h"
|
||||||
#include "VideoProxy.h"
|
#include "VideoProxy.h"
|
||||||
|
|
||||||
#include "platform/video-backend.h"
|
#include <mgba/feature/video-backend.h>
|
||||||
|
|
||||||
class QOpenGLPaintDevice;
|
class QOpenGLPaintDevice;
|
||||||
class QOpenGLWidget;
|
class QOpenGLWidget;
|
||||||
|
@ -109,6 +109,7 @@ public slots:
|
||||||
void showOSDMessages(bool enable) override;
|
void showOSDMessages(bool enable) override;
|
||||||
void showFrameCounter(bool enable) override;
|
void showFrameCounter(bool enable) override;
|
||||||
void filter(bool filter) override;
|
void filter(bool filter) override;
|
||||||
|
void swapInterval(int interval) override;
|
||||||
void framePosted() override;
|
void framePosted() override;
|
||||||
void setShaders(struct VDir*) override;
|
void setShaders(struct VDir*) override;
|
||||||
void clearShaders() override;
|
void clearShaders() override;
|
||||||
|
@ -121,8 +122,6 @@ protected:
|
||||||
virtual void resizeEvent(QResizeEvent*) override;
|
virtual void resizeEvent(QResizeEvent*) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startThread(int);
|
|
||||||
void setupProxyThread();
|
|
||||||
void updateContentSize();
|
void updateContentSize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -133,14 +132,10 @@ private:
|
||||||
|
|
||||||
bool m_isDrawing = false;
|
bool m_isDrawing = false;
|
||||||
bool m_hasStarted = false;
|
bool m_hasStarted = false;
|
||||||
int m_threadStartPending = 0;
|
|
||||||
std::unique_ptr<PainterGL> m_painter;
|
std::unique_ptr<PainterGL> m_painter;
|
||||||
QThread m_drawThread;
|
QThread m_drawThread;
|
||||||
QThread m_proxyThread;
|
|
||||||
std::shared_ptr<CoreController> m_context;
|
std::shared_ptr<CoreController> m_context;
|
||||||
mGLWidget* m_gl;
|
mGLWidget* m_gl;
|
||||||
QOffscreenSurface m_proxySurface;
|
|
||||||
std::unique_ptr<QOpenGLContext> m_proxyContext;
|
|
||||||
QSize m_cachedContentSize;
|
QSize m_cachedContentSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -155,7 +150,6 @@ public:
|
||||||
void setContext(std::shared_ptr<CoreController>);
|
void setContext(std::shared_ptr<CoreController>);
|
||||||
void setMessagePainter(MessagePainter*);
|
void setMessagePainter(MessagePainter*);
|
||||||
void enqueue(const uint32_t* backing);
|
void enqueue(const uint32_t* backing);
|
||||||
void enqueue(GLuint tex);
|
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
@ -167,9 +161,6 @@ public:
|
||||||
void setVideoProxy(std::shared_ptr<VideoProxy>);
|
void setVideoProxy(std::shared_ptr<VideoProxy>);
|
||||||
void interrupt();
|
void interrupt();
|
||||||
|
|
||||||
// Run on main thread
|
|
||||||
void swapTex();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void create();
|
void create();
|
||||||
void destroy();
|
void destroy();
|
||||||
|
@ -186,8 +177,8 @@ public slots:
|
||||||
void showOSD(bool enable);
|
void showOSD(bool enable);
|
||||||
void showFrameCounter(bool enable);
|
void showFrameCounter(bool enable);
|
||||||
void filter(bool filter);
|
void filter(bool filter);
|
||||||
|
void swapInterval(int interval);
|
||||||
void resizeContext();
|
void resizeContext();
|
||||||
void updateFramebufferHandle();
|
|
||||||
void setBackgroundImage(const QImage&);
|
void setBackgroundImage(const QImage&);
|
||||||
|
|
||||||
void setShaders(struct VDir*);
|
void setShaders(struct VDir*);
|
||||||
|
@ -215,13 +206,6 @@ private:
|
||||||
QQueue<uint32_t*> m_queue;
|
QQueue<uint32_t*> m_queue;
|
||||||
uint32_t* m_buffer = nullptr;
|
uint32_t* m_buffer = nullptr;
|
||||||
|
|
||||||
std::array<GLuint, 3> m_bridgeTexes;
|
|
||||||
QQueue<GLuint> m_freeTex;
|
|
||||||
QQueue<GLuint> m_queueTex;
|
|
||||||
|
|
||||||
GLuint m_bridgeTexIn = std::numeric_limits<GLuint>::max();
|
|
||||||
GLuint m_bridgeTexOut = std::numeric_limits<GLuint>::max();
|
|
||||||
|
|
||||||
QPainter m_painter;
|
QPainter m_painter;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QWindow* m_window;
|
QWindow* m_window;
|
||||||
|
@ -248,6 +232,7 @@ private:
|
||||||
MessagePainter* m_messagePainter = nullptr;
|
MessagePainter* m_messagePainter = nullptr;
|
||||||
QElapsedTimer m_delayTimer;
|
QElapsedTimer m_delayTimer;
|
||||||
std::shared_ptr<VideoProxy> m_videoProxy;
|
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||||
|
int m_swapInterval = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public slots:
|
||||||
void lockAspectRatio(bool lock) override;
|
void lockAspectRatio(bool lock) override;
|
||||||
void lockIntegerScaling(bool lock) override;
|
void lockIntegerScaling(bool lock) override;
|
||||||
void interframeBlending(bool enable) override;
|
void interframeBlending(bool enable) override;
|
||||||
|
void swapInterval(int) override {};
|
||||||
void filter(bool filter) override;
|
void filter(bool filter) override;
|
||||||
void framePosted() override;
|
void framePosted() override;
|
||||||
void setShaders(struct VDir*) override {}
|
void setShaders(struct VDir*) override {}
|
||||||
|
|
|
@ -548,6 +548,11 @@ void FrameView::newVl() {
|
||||||
m_vl->deinit(m_vl);
|
m_vl->deinit(m_vl);
|
||||||
}
|
}
|
||||||
m_vl = mCoreFindVF(m_currentFrame);
|
m_vl = mCoreFindVF(m_currentFrame);
|
||||||
|
if (!m_vl) {
|
||||||
|
m_currentFrame->close(m_currentFrame);
|
||||||
|
m_currentFrame = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_vl->init(m_vl);
|
m_vl->init(m_vl);
|
||||||
m_vl->loadROM(m_vl, m_currentFrame);
|
m_vl->loadROM(m_vl, m_currentFrame);
|
||||||
m_currentFrame = nullptr;
|
m_currentFrame = nullptr;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
#include "LogController.h"
|
#include "LogController.h"
|
||||||
|
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include <mgba/internal/gba/gba.h>
|
#include <mgba/internal/gba/gba.h>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
#endif
|
#endif
|
||||||
#include <mgba-util/export.h>
|
#include <mgba-util/image/export.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
@ -145,9 +145,9 @@ void PaletteView::exportPalette(int start, int length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (filename.endsWith(".pal", Qt::CaseInsensitive)) {
|
if (filename.endsWith(".pal", Qt::CaseInsensitive)) {
|
||||||
exportPaletteRIFF(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
mPaletteExportRIFF(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
||||||
} else if (filename.endsWith(".act", Qt::CaseInsensitive)) {
|
} else if (filename.endsWith(".act", Qt::CaseInsensitive)) {
|
||||||
exportPaletteACT(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
mPaletteExportACT(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
||||||
}
|
}
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <mgba/core/cheats.h>
|
#include <mgba/core/cheats.h>
|
||||||
#include <mgba/core/serialize.h>
|
#include <mgba/core/serialize.h>
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
#include "CoreController.h"
|
#include "CoreController.h"
|
||||||
|
@ -127,6 +127,7 @@ void ReportView::generateReport() {
|
||||||
swReport << QString("Build architecture: %1").arg(QSysInfo::buildCpuArchitecture());
|
swReport << QString("Build architecture: %1").arg(QSysInfo::buildCpuArchitecture());
|
||||||
swReport << QString("Run architecture: %1").arg(QSysInfo::currentCpuArchitecture());
|
swReport << QString("Run architecture: %1").arg(QSysInfo::currentCpuArchitecture());
|
||||||
swReport << QString("Qt version: %1").arg(QLatin1String(qVersion()));
|
swReport << QString("Qt version: %1").arg(QLatin1String(qVersion()));
|
||||||
|
swReport << QString("Qt QPA platform: %1").arg(QGuiApplication::platformName());
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
QStringList libavVers;
|
QStringList libavVers;
|
||||||
libavVers << QLatin1String(LIBAVCODEC_IDENT);
|
libavVers << QLatin1String(LIBAVCODEC_IDENT);
|
||||||
|
|
|
@ -483,6 +483,7 @@ void SettingsView::updateConfig() {
|
||||||
saveSetting("fastForwardMute", m_ui.muteFf);
|
saveSetting("fastForwardMute", m_ui.muteFf);
|
||||||
saveSetting("rewindEnable", m_ui.rewind);
|
saveSetting("rewindEnable", m_ui.rewind);
|
||||||
saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||||
|
saveSetting("rewindBufferInterval", m_ui.rewindBufferInterval);
|
||||||
saveSetting("resampleVideo", m_ui.resampleVideo);
|
saveSetting("resampleVideo", m_ui.resampleVideo);
|
||||||
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||||
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||||
|
@ -716,6 +717,7 @@ void SettingsView::reloadConfig() {
|
||||||
loadSetting("fastForwardMute", m_ui.muteFf, m_ui.mute->isChecked());
|
loadSetting("fastForwardMute", m_ui.muteFf, m_ui.mute->isChecked());
|
||||||
loadSetting("rewindEnable", m_ui.rewind);
|
loadSetting("rewindEnable", m_ui.rewind);
|
||||||
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||||
|
loadSetting("rewindBufferInterval", m_ui.rewindBufferInterval);
|
||||||
loadSetting("resampleVideo", m_ui.resampleVideo);
|
loadSetting("resampleVideo", m_ui.resampleVideo);
|
||||||
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||||
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||||
|
|
|
@ -1195,21 +1195,51 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_54">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rewind speed:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_23">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="rewindBufferInterval">
|
||||||
|
<property name="suffix">
|
||||||
|
<string notr="true">×</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="Line" name="line_3">
|
<widget class="Line" name="line_3">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0">
|
<item row="8" column="0">
|
||||||
<widget class="QLabel" name="label_15">
|
<widget class="QLabel" name="label_15">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Idle loops:</string>
|
<string>Idle loops:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="8" column="1">
|
||||||
<widget class="QComboBox" name="idleOptimization">
|
<widget class="QComboBox" name="idleOptimization">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -1228,21 +1258,21 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QCheckBox" name="preload">
|
<widget class="QCheckBox" name="preload">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Preload entire ROM into memory</string>
|
<string>Preload entire ROM into memory</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="10" column="1">
|
||||||
<widget class="QCheckBox" name="forceGbp">
|
<widget class="QCheckBox" name="forceGbp">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable Game Boy Player features by default</string>
|
<string>Enable Game Boy Player features by default</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="11" column="1">
|
||||||
<widget class="QCheckBox" name="vbaBugCompat">
|
<widget class="QCheckBox" name="vbaBugCompat">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Enable VBA bug compatibility in ROM hacks</string>
|
<string>Enable VBA bug compatibility in ROM hacks</string>
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
|
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
|
#include <mgba/feature/video-backend.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
#include "platform/video-backend.h"
|
|
||||||
|
|
||||||
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||||
|
|
||||||
|
|
|
@ -12,39 +12,40 @@
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
VideoProxy::VideoProxy() {
|
VideoProxy::VideoProxy() {
|
||||||
mVideoLoggerRendererCreate(&m_logger.d, false);
|
mVideoLoggerRendererCreate(&m_logger, false);
|
||||||
m_logger.d.block = true;
|
m_logger.p = this;
|
||||||
m_logger.d.waitOnFlush = true;
|
m_logger.block = true;
|
||||||
|
m_logger.waitOnFlush = true;
|
||||||
|
|
||||||
m_logger.d.init = &cbind<&VideoProxy::init>;
|
m_logger.init = &cbind<&VideoProxy::init>;
|
||||||
m_logger.d.reset = &cbind<&VideoProxy::reset>;
|
m_logger.reset = &cbind<&VideoProxy::reset>;
|
||||||
m_logger.d.deinit = &cbind<&VideoProxy::deinit>;
|
m_logger.deinit = &cbind<&VideoProxy::deinit>;
|
||||||
m_logger.d.lock = &cbind<&VideoProxy::lock>;
|
m_logger.lock = &cbind<&VideoProxy::lock>;
|
||||||
m_logger.d.unlock = &cbind<&VideoProxy::unlock>;
|
m_logger.unlock = &cbind<&VideoProxy::unlock>;
|
||||||
m_logger.d.wait = &cbind<&VideoProxy::wait>;
|
m_logger.wait = &cbind<&VideoProxy::wait>;
|
||||||
m_logger.d.wake = &callback<void, int>::func<&VideoProxy::wake>;
|
m_logger.wake = &callback<void, int>::func<&VideoProxy::wake>;
|
||||||
|
|
||||||
m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>;
|
m_logger.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>;
|
||||||
m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>;
|
m_logger.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>;
|
||||||
m_logger.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
|
m_logger.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
|
||||||
|
|
||||||
connect(this, &VideoProxy::dataAvailable, this, &VideoProxy::processData);
|
connect(this, &VideoProxy::dataAvailable, this, &VideoProxy::processData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoProxy::attach(CoreController* controller) {
|
void VideoProxy::attach(CoreController* controller) {
|
||||||
CoreController::Interrupter interrupter(controller);
|
CoreController::Interrupter interrupter(controller);
|
||||||
controller->thread()->core->videoLogger = &m_logger.d;
|
controller->thread()->core->videoLogger = &m_logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoProxy::detach(CoreController* controller) {
|
void VideoProxy::detach(CoreController* controller) {
|
||||||
CoreController::Interrupter interrupter(controller);
|
CoreController::Interrupter interrupter(controller);
|
||||||
if (controller->thread()->core->videoLogger == &m_logger.d) {
|
if (controller->thread()->core->videoLogger == &m_logger) {
|
||||||
controller->thread()->core->videoLogger = nullptr;
|
controller->thread()->core->videoLogger = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoProxy::processData() {
|
void VideoProxy::processData() {
|
||||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
mVideoLoggerRendererRun(&m_logger, false);
|
||||||
m_fromThreadCond.wakeAll();
|
m_fromThreadCond.wakeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ bool VideoProxy::writeData(const void* data, size_t length) {
|
||||||
while (!RingFIFOWrite(&m_dirtyQueue, data, length)) {
|
while (!RingFIFOWrite(&m_dirtyQueue, data, length)) {
|
||||||
if (QThread::currentThread() == thread()) {
|
if (QThread::currentThread() == thread()) {
|
||||||
// We're on the main thread
|
// We're on the main thread
|
||||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
mVideoLoggerRendererRun(&m_logger, false);
|
||||||
} else {
|
} else {
|
||||||
emit dataAvailable();
|
emit dataAvailable();
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
|
@ -105,7 +106,7 @@ void VideoProxy::postEvent(enum mVideoLoggerEvent event) {
|
||||||
|
|
||||||
void VideoProxy::handleEvent(int event) {
|
void VideoProxy::handleEvent(int event) {
|
||||||
m_mutex.lock();
|
m_mutex.lock();
|
||||||
m_logger.d.handleEvent(&m_logger.d, static_cast<enum mVideoLoggerEvent>(event));
|
m_logger.handleEvent(&m_logger, static_cast<enum mVideoLoggerEvent>(event));
|
||||||
m_mutex.unlock();
|
m_mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +123,7 @@ void VideoProxy::wait() {
|
||||||
while (RingFIFOSize(&m_dirtyQueue)) {
|
while (RingFIFOSize(&m_dirtyQueue)) {
|
||||||
if (QThread::currentThread() == thread()) {
|
if (QThread::currentThread() == thread()) {
|
||||||
// We're on the main thread
|
// We're on the main thread
|
||||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
mVideoLoggerRendererRun(&m_logger, false);
|
||||||
} else {
|
} else {
|
||||||
emit dataAvailable();
|
emit dataAvailable();
|
||||||
m_toThreadCond.wakeAll();
|
m_toThreadCond.wakeAll();
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
|
|
||||||
void attach(CoreController*);
|
void attach(CoreController*);
|
||||||
void detach(CoreController*);
|
void detach(CoreController*);
|
||||||
void setBlocking(bool block) { m_logger.d.waitOnFlush = block; }
|
void setBlocking(bool block) { m_logger.waitOnFlush = block; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataAvailable();
|
void dataAvailable();
|
||||||
|
@ -51,17 +51,16 @@ private:
|
||||||
using type = T (VideoProxy::*)(A...);
|
using type = T (VideoProxy::*)(A...);
|
||||||
|
|
||||||
template<type F> static T func(mVideoLogger* logger, A... args) {
|
template<type F> static T func(mVideoLogger* logger, A... args) {
|
||||||
VideoProxy* proxy = reinterpret_cast<Logger*>(logger)->p;
|
VideoProxy* proxy = static_cast<Logger*>(logger)->p;
|
||||||
return (proxy->*F)(args...);
|
return (proxy->*F)(args...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<void (VideoProxy::*F)()> static void cbind(mVideoLogger* logger) { callback<void>::func<F>(logger); }
|
template<void (VideoProxy::*F)()> static void cbind(mVideoLogger* logger) { callback<void>::func<F>(logger); }
|
||||||
|
|
||||||
struct Logger {
|
struct Logger : public mVideoLogger {
|
||||||
mVideoLogger d;
|
|
||||||
VideoProxy* p;
|
VideoProxy* p;
|
||||||
} m_logger = {{}, this};
|
} m_logger;
|
||||||
|
|
||||||
RingFIFO m_dirtyQueue;
|
RingFIFO m_dirtyQueue;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
|
|
|
@ -1493,6 +1493,20 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}
|
}
|
||||||
m_config->updateOption("fastForwardRatio");
|
m_config->updateOption("fastForwardRatio");
|
||||||
|
|
||||||
|
addGameAction(tr("Increase fast forward speed"), "fastForwardUp", [this] {
|
||||||
|
float newRatio = m_config->getOption("fastForwardRatio", 1.0f).toFloat() + 1.0f;
|
||||||
|
if (newRatio >= 3.0f) {
|
||||||
|
m_config->setOption("fastForwardRatio", QVariant(newRatio));
|
||||||
|
}
|
||||||
|
}, "emu");
|
||||||
|
|
||||||
|
addGameAction(tr("Decrease fast forward speed"), "fastForwardDown", [this] {
|
||||||
|
float newRatio = m_config->getOption("fastForwardRatio").toFloat() - 1.0f;
|
||||||
|
if (newRatio >= 2.0f) {
|
||||||
|
m_config->setOption("fastForwardRatio", QVariant(newRatio));
|
||||||
|
}
|
||||||
|
}, "emu");
|
||||||
|
|
||||||
Action* rewindHeld = m_actions.addHeldAction(tr("Rewind (held)"), "holdRewind", [this](bool held) {
|
Action* rewindHeld = m_actions.addHeldAction(tr("Rewind (held)"), "holdRewind", [this](bool held) {
|
||||||
if (m_controller) {
|
if (m_controller) {
|
||||||
m_controller->setRewinding(held);
|
m_controller->setRewinding(held);
|
||||||
|
@ -1634,7 +1648,8 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
m_actions.addSeparator("av");
|
m_actions.addSeparator("av");
|
||||||
|
|
||||||
ConfigOption* mute = m_config->addOption("mute");
|
ConfigOption* mute = m_config->addOption("mute");
|
||||||
mute->addBoolean(tr("Mute"), &m_actions, "av");
|
Action* muteAction = mute->addBoolean(tr("Mute"), &m_actions, "av");
|
||||||
|
muteAction->setActive(m_config->getOption("mute").toInt());
|
||||||
mute->connect([this](const QVariant& value) {
|
mute->connect([this](const QVariant& value) {
|
||||||
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
|
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
|
@ -1774,7 +1789,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
||||||
void Window::setupOptions() {
|
void Window::setupOptions() {
|
||||||
ConfigOption* videoSync = m_config->addOption("videoSync");
|
ConfigOption* videoSync = m_config->addOption("videoSync");
|
||||||
videoSync->connect([this](const QVariant&) {
|
videoSync->connect([this](const QVariant& variant) {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
@ -1828,6 +1843,11 @@ void Window::setupOptions() {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
ConfigOption* rewindBufferInterval = m_config->addOption("rewindBufferInterval");
|
||||||
|
rewindBufferInterval->connect([this](const QVariant&) {
|
||||||
|
reloadConfig();
|
||||||
|
}, this);
|
||||||
|
|
||||||
ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");
|
ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");
|
||||||
allowOpposingDirections->connect([this](const QVariant&) {
|
allowOpposingDirections->connect([this](const QVariant&) {
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
#include "scripting/ScriptingTextBuffer.h"
|
#include "scripting/ScriptingTextBuffer.h"
|
||||||
#include "scripting/ScriptingTextBufferModel.h"
|
#include "scripting/ScriptingTextBufferModel.h"
|
||||||
|
|
||||||
#include <mgba/script/input.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/storage.h>
|
|
||||||
#include <mgba-util/math.h>
|
#include <mgba-util/math.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
|
|
||||||
#ifdef USE_PNG
|
#ifdef USE_PNG
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ bool mSDLGLCommonLoadBackground(struct VideoBackend* context) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Rectangle dims = {
|
struct mRectangle dims = {
|
||||||
.width = width,
|
.width = width,
|
||||||
.height = height
|
.height = height
|
||||||
};
|
};
|
||||||
|
@ -136,13 +136,13 @@ void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
if (mSDLGLCommonLoadBackground(v)) {
|
if (mSDLGLCommonLoadBackground(v)) {
|
||||||
renderer->player.windowUpdated = true;
|
renderer->player.windowUpdated = true;
|
||||||
|
|
||||||
struct Rectangle frame;
|
struct mRectangle frame;
|
||||||
VideoBackendGetFrame(v, &frame);
|
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &frame);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i <= VIDEO_LAYER_IMAGE; ++i) {
|
for (i = 0; i < VIDEO_LAYER_IMAGE; ++i) {
|
||||||
struct Rectangle dims;
|
struct mRectangle dims;
|
||||||
v->layerDimensions(v, i, &dims);
|
v->layerDimensions(v, i, &dims);
|
||||||
RectangleCenter(&frame, &dims);
|
mRectangleCenter(&frame, &dims);
|
||||||
v->setLayerDimensions(v, i, &dims);
|
v->setLayerDimensions(v, i, &dims);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
|
renderer->core->currentVideoSize(renderer->core, &renderer->width, &renderer->height);
|
||||||
struct Rectangle dims;
|
struct mRectangle dims;
|
||||||
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
|
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
|
||||||
if (renderer->width != dims.width || renderer->height != dims.height) {
|
if (renderer->width != dims.width || renderer->height != dims.height) {
|
||||||
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
|
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
|
||||||
|
|
|
@ -35,7 +35,7 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) {
|
||||||
renderer->gl.d.filter = renderer->filter;
|
renderer->gl.d.filter = renderer->filter;
|
||||||
renderer->gl.d.swap = mSDLGLCommonSwap;
|
renderer->gl.d.swap = mSDLGLCommonSwap;
|
||||||
renderer->gl.d.init(&renderer->gl.d, 0);
|
renderer->gl.d.init(&renderer->gl.d, 0);
|
||||||
struct Rectangle dims = {
|
struct mRectangle dims = {
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
.width = renderer->width,
|
.width = renderer->width,
|
||||||
|
|
|
@ -50,7 +50,7 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
#endif
|
#endif
|
||||||
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
||||||
|
|
||||||
struct Rectangle dims = {
|
struct mRectangle dims = {
|
||||||
.x = 0,
|
.x = 0,
|
||||||
.y = 0,
|
.y = 0,
|
||||||
.width = renderer->width,
|
.width = renderer->width,
|
||||||
|
|
|
@ -61,6 +61,7 @@ int main(int argc, char** argv) {
|
||||||
.useBios = true,
|
.useBios = true,
|
||||||
.rewindEnable = true,
|
.rewindEnable = true,
|
||||||
.rewindBufferCapacity = 600,
|
.rewindBufferCapacity = 600,
|
||||||
|
.rewindBufferInterval = 1,
|
||||||
.audioBuffers = 1024,
|
.audioBuffers = 1024,
|
||||||
.videoSync = false,
|
.videoSync = false,
|
||||||
.audioSync = true,
|
.audioSync = true,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba-util/gui/font.h>
|
#include <mgba-util/gui/font.h>
|
||||||
#include <mgba-util/gui/font-metrics.h>
|
#include <mgba-util/gui/font-metrics.h>
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <mgba/feature/commandline.h>
|
#include <mgba/feature/commandline.h>
|
||||||
#include <mgba/feature/video-logger.h>
|
#include <mgba/feature/video-logger.h>
|
||||||
|
|
||||||
#include <mgba-util/png-io.h>
|
#include <mgba-util/image/png-io.h>
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
#include <mgba-util/table.h>
|
#include <mgba-util/table.h>
|
||||||
#include <mgba-util/threading.h>
|
#include <mgba-util/threading.h>
|
||||||
|
@ -732,8 +732,8 @@ static struct VDir* _makeOutDir(const char* testName) {
|
||||||
|
|
||||||
static void _writeImage(struct VFile* vf, const struct CInemaImage* image) {
|
static void _writeImage(struct VFile* vf, const struct CInemaImage* image) {
|
||||||
png_structp png = PNGWriteOpen(vf);
|
png_structp png = PNGWriteOpen(vf);
|
||||||
png_infop info = PNGWriteHeader(png, image->width, image->height);
|
png_infop info = PNGWriteHeader(png, image->width, image->height, mCOLOR_NATIVE);
|
||||||
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data)) {
|
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data, mCOLOR_NATIVE)) {
|
||||||
CIerr(0, "Could not write output image\n");
|
CIerr(0, "Could not write output image\n");
|
||||||
}
|
}
|
||||||
PNGWriteClose(png, info);
|
PNGWriteClose(png, info);
|
||||||
|
|
|
@ -2,6 +2,7 @@ include(ExportDirectory)
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
context.c
|
context.c
|
||||||
input.c
|
input.c
|
||||||
|
image.c
|
||||||
socket.c
|
socket.c
|
||||||
stdlib.c
|
stdlib.c
|
||||||
types.c)
|
types.c)
|
||||||
|
@ -18,6 +19,7 @@ if(USE_LUA)
|
||||||
list(APPEND SOURCE_FILES engines/lua.c)
|
list(APPEND SOURCE_FILES engines/lua.c)
|
||||||
list(APPEND TEST_FILES
|
list(APPEND TEST_FILES
|
||||||
test/context.c
|
test/context.c
|
||||||
|
test/image.c
|
||||||
test/input.c
|
test/input.c
|
||||||
test/lua.c
|
test/lua.c
|
||||||
test/stdlib.c)
|
test/stdlib.c)
|
||||||
|
|
|
@ -356,6 +356,7 @@ void mScriptEngineExportDocNamespace(struct mScriptEngineContext* ctx, const cha
|
||||||
struct mScriptValue* key = mScriptStringCreateFromUTF8(values[i].key);
|
struct mScriptValue* key = mScriptStringCreateFromUTF8(values[i].key);
|
||||||
mScriptTableInsert(table, key, values[i].value);
|
mScriptTableInsert(table, key, values[i].value);
|
||||||
mScriptValueDeref(key);
|
mScriptValueDeref(key);
|
||||||
|
mScriptValueDeref(values[i].value);
|
||||||
}
|
}
|
||||||
HashTableInsert(&ctx->docroot, nspace, table);
|
HashTableInsert(&ctx->docroot, nspace, table);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
#include <mgba/core/scripting.h>
|
#include <mgba/core/scripting.h>
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
#include <mgba/internal/script/types.h>
|
#include <mgba/internal/script/types.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/input.h>
|
|
||||||
#include <mgba/script/storage.h>
|
|
||||||
#include <mgba-util/string.h>
|
#include <mgba-util/string.h>
|
||||||
|
|
||||||
struct mScriptContext context;
|
struct mScriptContext context;
|
||||||
|
@ -188,6 +186,9 @@ void explainClass(struct mScriptTypeClass* cls, int level) {
|
||||||
}
|
}
|
||||||
docstring = NULL;
|
docstring = NULL;
|
||||||
}
|
}
|
||||||
|
if (details->info.member.readonly) {
|
||||||
|
fprintf(out, "%s readonly: true\n", indent);
|
||||||
|
}
|
||||||
fprintf(out, "%s type: %s\n", indent, details->info.member.type->name);
|
fprintf(out, "%s type: %s\n", indent, details->info.member.type->name);
|
||||||
break;
|
break;
|
||||||
case mSCRIPT_CLASS_INIT_END:
|
case mSCRIPT_CLASS_INIT_END:
|
||||||
|
@ -469,9 +470,10 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
mScriptContextInit(&context);
|
mScriptContextInit(&context);
|
||||||
mScriptContextAttachStdlib(&context);
|
mScriptContextAttachStdlib(&context);
|
||||||
|
mScriptContextAttachImage(&context);
|
||||||
|
mScriptContextAttachInput(&context);
|
||||||
mScriptContextAttachSocket(&context);
|
mScriptContextAttachSocket(&context);
|
||||||
mScriptContextAttachStorage(&context);
|
mScriptContextAttachStorage(&context);
|
||||||
mScriptContextAttachInput(&context);
|
|
||||||
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
||||||
|
|
||||||
initTypes();
|
initTypes();
|
||||||
|
|
|
@ -60,6 +60,7 @@ static int _luaGetList(lua_State* lua);
|
||||||
static int _luaLenList(lua_State* lua);
|
static int _luaLenList(lua_State* lua);
|
||||||
|
|
||||||
static int _luaRequireShim(lua_State* lua);
|
static int _luaRequireShim(lua_State* lua);
|
||||||
|
static int _luaPrintShim(lua_State* lua);
|
||||||
|
|
||||||
static const char* _socketLuaSource =
|
static const char* _socketLuaSource =
|
||||||
"socket = {\n"
|
"socket = {\n"
|
||||||
|
@ -101,7 +102,7 @@ static const char* _socketLuaSource =
|
||||||
" local cbid = self._nextCallback\n"
|
" local cbid = self._nextCallback\n"
|
||||||
" self._nextCallback = cbid + 1\n"
|
" self._nextCallback = cbid + 1\n"
|
||||||
" self._callbacks[event][cbid] = callback\n"
|
" self._callbacks[event][cbid] = callback\n"
|
||||||
" return id\n"
|
" return cbid\n"
|
||||||
" end,\n"
|
" end,\n"
|
||||||
" remove = function(self, cbid)\n"
|
" remove = function(self, cbid)\n"
|
||||||
" for _, group in pairs(self._callbacks) do\n"
|
" for _, group in pairs(self._callbacks) do\n"
|
||||||
|
@ -414,6 +415,14 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS
|
||||||
lua_getglobal(luaContext->lua, "require");
|
lua_getglobal(luaContext->lua, "require");
|
||||||
luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
luaContext->require = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
lua_pushliteral(luaContext->lua, "log");
|
||||||
|
lua_pushcclosure(luaContext->lua, _luaPrintShim, 1);
|
||||||
|
lua_setglobal(luaContext->lua, "print");
|
||||||
|
|
||||||
|
lua_pushliteral(luaContext->lua, "warn");
|
||||||
|
lua_pushcclosure(luaContext->lua, _luaPrintShim, 1);
|
||||||
|
lua_setglobal(luaContext->lua, "warn");
|
||||||
|
|
||||||
HashTableInit(&luaContext->d.docroot, 0, (void (*)(void*)) mScriptValueDeref);
|
HashTableInit(&luaContext->d.docroot, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
|
|
||||||
int status = luaL_dostring(luaContext->lua, _socketLuaSource);
|
int status = luaL_dostring(luaContext->lua, _socketLuaSource);
|
||||||
|
@ -449,7 +458,6 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS
|
||||||
mSCRIPT_KV_PAIR(connect, mSCRIPT_VALUE_DOC_FUNCTION(socket_connect)),
|
mSCRIPT_KV_PAIR(connect, mSCRIPT_VALUE_DOC_FUNCTION(socket_connect)),
|
||||||
mSCRIPT_KV_SENTINEL
|
mSCRIPT_KV_SENTINEL
|
||||||
});
|
});
|
||||||
mScriptValueDeref(errors);
|
|
||||||
mScriptEngineSetDocstring(&luaContext->d, "socket", "A basic TCP socket library");
|
mScriptEngineSetDocstring(&luaContext->d, "socket", "A basic TCP socket library");
|
||||||
mScriptEngineSetDocstring(&luaContext->d, "socket.ERRORS",
|
mScriptEngineSetDocstring(&luaContext->d, "socket.ERRORS",
|
||||||
"Error strings corresponding to the C.SOCKERR error codes, indexed both by name and by value");
|
"Error strings corresponding to the C.SOCKERR error codes, indexed both by name and by value");
|
||||||
|
@ -464,6 +472,16 @@ struct mScriptEngineContext* _luaCreate(struct mScriptEngine2* engine, struct mS
|
||||||
"the connection either succeeds or fails");
|
"the connection either succeeds or fails");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mScriptEngineExportDocNamespace(&luaContext->d, "script", (struct mScriptKVPair[]) {
|
||||||
|
mSCRIPT_KV_PAIR(dir, mScriptStringCreateFromASCII("/")),
|
||||||
|
mSCRIPT_KV_PAIR(path, mScriptStringCreateFromASCII("/lua")),
|
||||||
|
mSCRIPT_KV_SENTINEL
|
||||||
|
});
|
||||||
|
|
||||||
|
mScriptEngineSetDocstring(&luaContext->d, "script", "Information about the currently loaded script");
|
||||||
|
mScriptEngineSetDocstring(&luaContext->d, "script.dir", "The path to the directory containing the script");
|
||||||
|
mScriptEngineSetDocstring(&luaContext->d, "script.path", "The path of the current script file");
|
||||||
|
|
||||||
return &luaContext->d;
|
return &luaContext->d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,6 +639,14 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext,
|
||||||
mScriptValueDeref(list);
|
mScriptValueDeref(list);
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < mScriptListSize(list->value.list); ++i) {
|
||||||
|
struct mScriptValue* value = mScriptListGetPointer(list->value.list, i);
|
||||||
|
if (value->type->base != mSCRIPT_TYPE_WRAPPER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value = mScriptValueUnwrap(value);
|
||||||
|
mScriptValueRef(value);
|
||||||
|
}
|
||||||
mScriptValueDeref(table);
|
mScriptValueDeref(table);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -885,7 +911,9 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
||||||
luaContext->lastError = NULL;
|
luaContext->lastError = NULL;
|
||||||
}
|
}
|
||||||
char name[PATH_MAX + 1];
|
char name[PATH_MAX + 1];
|
||||||
char dirname[PATH_MAX] = {0};
|
char dirname[PATH_MAX];
|
||||||
|
name[0] = '\0';
|
||||||
|
dirname[0] = '\0';
|
||||||
if (filename) {
|
if (filename) {
|
||||||
if (*filename == '*') {
|
if (*filename == '*') {
|
||||||
snprintf(name, sizeof(name), "=%s", filename + 1);
|
snprintf(name, sizeof(name), "=%s", filename + 1);
|
||||||
|
@ -900,7 +928,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
||||||
lastSlash = lastBackslash;
|
lastSlash = lastBackslash;
|
||||||
}
|
}
|
||||||
if (lastSlash) {
|
if (lastSlash) {
|
||||||
strncpy(dirname, filename, lastSlash - filename);
|
size_t len = lastSlash - filename + 1;
|
||||||
|
if (sizeof(dirname) < len) {
|
||||||
|
len = sizeof(dirname);
|
||||||
|
}
|
||||||
|
strlcpy(dirname, filename, len);
|
||||||
}
|
}
|
||||||
snprintf(name, sizeof(name), "@%s", filename);
|
snprintf(name, sizeof(name), "@%s", filename);
|
||||||
}
|
}
|
||||||
|
@ -913,14 +945,43 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
||||||
#endif
|
#endif
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case LUA_OK:
|
case LUA_OK:
|
||||||
|
// Create new _ENV
|
||||||
|
lua_newtable(luaContext->lua);
|
||||||
|
|
||||||
|
// Make the old _ENV the __index in the metatable
|
||||||
|
lua_newtable(luaContext->lua);
|
||||||
|
lua_pushliteral(luaContext->lua, "__index");
|
||||||
|
lua_getupvalue(luaContext->lua, -4, 1);
|
||||||
|
lua_rawset(luaContext->lua, -3);
|
||||||
|
|
||||||
|
lua_pushliteral(luaContext->lua, "__newindex");
|
||||||
|
lua_getupvalue(luaContext->lua, -4, 1);
|
||||||
|
lua_rawset(luaContext->lua, -3);
|
||||||
|
|
||||||
|
lua_setmetatable(luaContext->lua, -2);
|
||||||
|
|
||||||
|
lua_pushliteral(luaContext->lua, "script");
|
||||||
|
lua_newtable(luaContext->lua);
|
||||||
|
|
||||||
if (dirname[0]) {
|
if (dirname[0]) {
|
||||||
lua_getupvalue(luaContext->lua, -1, 1);
|
|
||||||
lua_pushliteral(luaContext->lua, "require");
|
lua_pushliteral(luaContext->lua, "require");
|
||||||
lua_pushstring(luaContext->lua, dirname);
|
lua_pushstring(luaContext->lua, dirname);
|
||||||
lua_pushcclosure(luaContext->lua, _luaRequireShim, 1);
|
lua_pushcclosure(luaContext->lua, _luaRequireShim, 1);
|
||||||
|
lua_rawset(luaContext->lua, -5);
|
||||||
|
|
||||||
|
lua_pushliteral(luaContext->lua, "dir");
|
||||||
|
lua_pushstring(luaContext->lua, dirname);
|
||||||
lua_rawset(luaContext->lua, -3);
|
lua_rawset(luaContext->lua, -3);
|
||||||
lua_pop(luaContext->lua, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name[0] == '@') {
|
||||||
|
lua_pushliteral(luaContext->lua, "path");
|
||||||
|
lua_pushstring(luaContext->lua, &name[1]);
|
||||||
|
lua_rawset(luaContext->lua, -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_rawset(luaContext->lua, -3);
|
||||||
|
lua_setupvalue(luaContext->lua, -2, 1);
|
||||||
luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
|
||||||
return true;
|
return true;
|
||||||
case LUA_ERRSYNTAX:
|
case LUA_ERRSYNTAX:
|
||||||
|
@ -1237,7 +1298,7 @@ int _luaGetTable(lua_State* lua) {
|
||||||
}
|
}
|
||||||
lua_pop(lua, 2);
|
lua_pop(lua, 2);
|
||||||
|
|
||||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||||
if (obj->type->base == mSCRIPT_TYPE_WRAPPER) {
|
if (obj->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||||
obj = mScriptValueUnwrap(obj);
|
obj = mScriptValueUnwrap(obj);
|
||||||
}
|
}
|
||||||
|
@ -1475,3 +1536,37 @@ static int _luaRequireShim(lua_State* lua) {
|
||||||
int newtop = lua_gettop(luaContext->lua);
|
int newtop = lua_gettop(luaContext->lua);
|
||||||
return newtop - oldtop + 1;
|
return newtop - oldtop + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _luaPrintShim(lua_State* lua) {
|
||||||
|
int n = lua_gettop(lua);
|
||||||
|
|
||||||
|
lua_getglobal(lua, "console");
|
||||||
|
lua_insert(lua, 1);
|
||||||
|
|
||||||
|
// The first upvalue is either "log" or "warn"
|
||||||
|
lua_getglobal(lua, "console");
|
||||||
|
lua_pushvalue(lua, lua_upvalueindex(1));
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
lua_insert(lua, 1);
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
// TODO when console:log is variadic and stringifies by itself:
|
||||||
|
// lua_call(lua, n + 1, 0);
|
||||||
|
|
||||||
|
// Until then, stringify and concatenate:
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
luaL_tolstring(lua, i * 2 + 3, NULL);
|
||||||
|
lua_replace(lua, i * 2 + 3);
|
||||||
|
if (i == 0) {
|
||||||
|
lua_pushliteral(lua, "");
|
||||||
|
} else {
|
||||||
|
lua_pushliteral(lua, "\t");
|
||||||
|
}
|
||||||
|
lua_insert(lua, i * 2 + 3);
|
||||||
|
}
|
||||||
|
n = n * 2 - 1;
|
||||||
|
lua_concat(lua, n + 1);
|
||||||
|
lua_call(lua, 2, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* Copyright (c) 2013-2023 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.h>
|
||||||
|
|
||||||
|
static struct mScriptValue* _mImageNew(unsigned width, unsigned height) {
|
||||||
|
// For various reasons, it's probably a good idea to limit the maximum image size scripts can make
|
||||||
|
if (width >= 10000 || height >= 10000) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct mImage* image = mImageCreate(width, height, mCOLOR_ABGR8);
|
||||||
|
if (!image) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
|
||||||
|
result->value.opaque = image;
|
||||||
|
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mScriptValue* _mImageLoad(const char* path) {
|
||||||
|
struct mImage* image = mImageLoad(path);
|
||||||
|
if (!image) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
|
||||||
|
result->value.opaque = image;
|
||||||
|
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
mSCRIPT_DECLARE_STRUCT_C_METHOD(mImage, U32, getPixel, mImageGetPixel, 2, U32, x, U32, y);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mImage, setPixel, mImageSetPixel, 3, U32, x, U32, y, U32, color);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_C_METHOD_WITH_DEFAULTS(mImage, BOOL, save, mImageSave, 2, CHARP, path, CHARP, format);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mImage, _deinit, mImageDestroy, 0);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mImage, drawImageOpaque, mImageBlit, 3, CS(mImage), image, U32, x, U32, y);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mImage, drawImage, mImageCompositeWithAlpha, 4, CS(mImage), image, U32, x, U32, y, F32, alpha);
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mImage, save)
|
||||||
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
mSCRIPT_CHARP("PNG")
|
||||||
|
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mImage, drawImage)
|
||||||
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
mSCRIPT_F32(1.0f)
|
||||||
|
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT(mImage)
|
||||||
|
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||||
|
"A single, static image."
|
||||||
|
)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_DEINIT(mImage)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Save the image to a file. Currently, only `PNG` format is supported")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, save)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Get the ARGB value of the pixel at a given coordinate")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, getPixel)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Set the ARGB value of the pixel at a given coordinate")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, setPixel)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Draw another image onto this image without any alpha blending, overwriting what was already there")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, drawImageOpaque)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Draw another image onto this image with alpha blending as needed, optionally specifying a coefficient for adjusting the opacity")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mImage, drawImage)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("The width of the image, in pixels")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(mImage, U32, width)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("The height of the image, in pixels")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(mImage, U32, height)
|
||||||
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
|
mSCRIPT_BIND_FUNCTION(mImageNew_Binding, W(mImage), _mImageNew, 2, U32, width, U32, height);
|
||||||
|
mSCRIPT_BIND_FUNCTION(mImageLoad_Binding, W(mImage), _mImageLoad, 1, CHARP, path);
|
||||||
|
|
||||||
|
void mScriptContextAttachImage(struct mScriptContext* context) {
|
||||||
|
mScriptContextExportNamespace(context, "image", (struct mScriptKVPair[]) {
|
||||||
|
mSCRIPT_KV_PAIR(new, &mImageNew_Binding),
|
||||||
|
mSCRIPT_KV_PAIR(load, &mImageLoad_Binding),
|
||||||
|
mSCRIPT_KV_SENTINEL
|
||||||
|
});
|
||||||
|
mScriptContextSetDocstring(context, "image", "Methods for creating struct::mImage instances");
|
||||||
|
mScriptContextSetDocstring(context, "image.new", "Create a new image with the given dimensions");
|
||||||
|
mScriptContextSetDocstring(context, "image.load", "Load an image from a path. Currently, only `PNG` format is supported");
|
||||||
|
}
|
|
@ -3,10 +3,12 @@
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/base.h>
|
||||||
|
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/serialize.h>
|
#include <mgba/core/serialize.h>
|
||||||
|
#include <mgba/core/version.h>
|
||||||
|
#include <mgba/script/context.h>
|
||||||
#include <mgba/script/macros.h>
|
#include <mgba/script/macros.h>
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include <mgba/internal/gba/input.h>
|
#include <mgba/internal/gba/input.h>
|
||||||
|
@ -189,4 +191,27 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) {
|
||||||
mScriptContextSetDocstring(context, "util", "Basic utility library");
|
mScriptContextSetDocstring(context, "util", "Basic utility library");
|
||||||
mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask");
|
mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask");
|
||||||
mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices");
|
mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices");
|
||||||
|
|
||||||
|
struct mScriptValue* systemVersion = mScriptStringCreateFromUTF8(projectVersion);
|
||||||
|
struct mScriptValue* systemProgram = mScriptStringCreateFromUTF8(projectName);
|
||||||
|
struct mScriptValue* systemBranch = mScriptStringCreateFromUTF8(gitBranch);
|
||||||
|
struct mScriptValue* systemCommit = mScriptStringCreateFromUTF8(gitCommit);
|
||||||
|
struct mScriptValue* systemRevision = mScriptValueAlloc(mSCRIPT_TYPE_MS_S32);
|
||||||
|
systemRevision->value.s32 = gitRevision;
|
||||||
|
|
||||||
|
mScriptContextExportNamespace(context, "system", (struct mScriptKVPair[]) {
|
||||||
|
mSCRIPT_KV_PAIR(version, systemVersion),
|
||||||
|
mSCRIPT_KV_PAIR(program, systemProgram),
|
||||||
|
mSCRIPT_KV_PAIR(branch, systemBranch),
|
||||||
|
mSCRIPT_KV_PAIR(commit, systemCommit),
|
||||||
|
mSCRIPT_KV_PAIR(revision, systemRevision),
|
||||||
|
mSCRIPT_KV_SENTINEL
|
||||||
|
});
|
||||||
|
|
||||||
|
mScriptContextSetDocstring(context, "system", "Information about the system the script is running under");
|
||||||
|
mScriptContextSetDocstring(context, "system.version", "The current version of this build of the program");
|
||||||
|
mScriptContextSetDocstring(context, "system.program", "The name of the program. Generally this will be \"mGBA\", but forks may change it to differentiate");
|
||||||
|
mScriptContextSetDocstring(context, "system.branch", "The current git branch of this build of the program, if known");
|
||||||
|
mScriptContextSetDocstring(context, "system.commit", "The current git commit hash of this build of the program, if known");
|
||||||
|
mScriptContextSetDocstring(context, "system.revision", "The current git revision number of this build of the program, or -1 if unknown");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/macros.h>
|
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
struct TestA {
|
struct TestA {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
|
@ -54,6 +52,11 @@ struct TestG {
|
||||||
const char* c;
|
const char* c;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TestH {
|
||||||
|
int32_t i;
|
||||||
|
int32_t j;
|
||||||
|
};
|
||||||
|
|
||||||
static int32_t testAi0(struct TestA* a) {
|
static int32_t testAi0(struct TestA* a) {
|
||||||
return a->i;
|
return a->i;
|
||||||
}
|
}
|
||||||
|
@ -198,6 +201,12 @@ mSCRIPT_DEFINE_STRUCT(TestG)
|
||||||
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setC)
|
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setC)
|
||||||
mSCRIPT_DEFINE_END;
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_STRUCT(TestH)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_MEMBER(TestH, S32, i)
|
||||||
|
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(TestH, S32, j)
|
||||||
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
M_TEST_DEFINE(testALayout) {
|
M_TEST_DEFINE(testALayout) {
|
||||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||||
assert_false(cls->init);
|
assert_false(cls->init);
|
||||||
|
@ -1134,6 +1143,47 @@ M_TEST_DEFINE(testGSet) {
|
||||||
assert_false(cls->init);
|
assert_false(cls->init);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(testHSet) {
|
||||||
|
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestH)->details.cls;
|
||||||
|
|
||||||
|
struct TestH s = {
|
||||||
|
.i = 1,
|
||||||
|
.j = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mScriptValue sval = mSCRIPT_MAKE_S(TestH, &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, "j", &val));
|
||||||
|
assert_true(compare.type->equal(&compare, &val));
|
||||||
|
|
||||||
|
val = mSCRIPT_MAKE_S32(3);
|
||||||
|
assert_true(mScriptObjectSet(&sval, "i", &val));
|
||||||
|
assert_int_equal(s.i, 3);
|
||||||
|
|
||||||
|
val = mSCRIPT_MAKE_S32(4);
|
||||||
|
assert_false(mScriptObjectSet(&sval, "j", &val));
|
||||||
|
assert_int_equal(s.j, 2);
|
||||||
|
|
||||||
|
compare = mSCRIPT_MAKE_S32(3);
|
||||||
|
assert_true(mScriptObjectGet(&sval, "i", &val));
|
||||||
|
assert_true(compare.type->equal(&compare, &val));
|
||||||
|
|
||||||
|
compare = mSCRIPT_MAKE_S32(2);
|
||||||
|
assert_true(mScriptObjectGet(&sval, "j", &val));
|
||||||
|
assert_true(compare.type->equal(&compare, &val));
|
||||||
|
|
||||||
|
assert_true(cls->init);
|
||||||
|
mScriptClassDeinit(cls);
|
||||||
|
assert_false(cls->init);
|
||||||
|
}
|
||||||
|
|
||||||
M_TEST_SUITE_DEFINE(mScriptClasses,
|
M_TEST_SUITE_DEFINE(mScriptClasses,
|
||||||
cmocka_unit_test(testALayout),
|
cmocka_unit_test(testALayout),
|
||||||
cmocka_unit_test(testASignatures),
|
cmocka_unit_test(testASignatures),
|
||||||
|
@ -1150,4 +1200,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses,
|
||||||
cmocka_unit_test(testEGet),
|
cmocka_unit_test(testEGet),
|
||||||
cmocka_unit_test(testFDeinit),
|
cmocka_unit_test(testFDeinit),
|
||||||
cmocka_unit_test(testGSet),
|
cmocka_unit_test(testGSet),
|
||||||
|
cmocka_unit_test(testHSet),
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/macros.h>
|
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
M_TEST_DEFINE(weakrefBasic) {
|
M_TEST_DEFINE(weakrefBasic) {
|
||||||
struct mScriptContext context;
|
struct mScriptContext context;
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* Copyright (c) 2013-2023 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.h>
|
||||||
|
|
||||||
|
#include "script/test.h"
|
||||||
|
|
||||||
|
#define SETUP_LUA \
|
||||||
|
struct mScriptContext context; \
|
||||||
|
mScriptContextInit(&context); \
|
||||||
|
struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \
|
||||||
|
mScriptContextAttachStdlib(&context); \
|
||||||
|
mScriptContextAttachImage(&context)
|
||||||
|
|
||||||
|
M_TEST_SUITE_SETUP(mScriptImage) {
|
||||||
|
if (mSCRIPT_ENGINE_LUA->init) {
|
||||||
|
mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_SUITE_TEARDOWN(mScriptImage) {
|
||||||
|
if (mSCRIPT_ENGINE_LUA->deinit) {
|
||||||
|
mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(members) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("assert(image)");
|
||||||
|
TEST_PROGRAM("assert(image.new)");
|
||||||
|
TEST_PROGRAM("assert(image.load)");
|
||||||
|
TEST_PROGRAM("im = image.new(1, 1)");
|
||||||
|
TEST_PROGRAM("assert(im)");
|
||||||
|
TEST_PROGRAM("assert(im.width == 1)");
|
||||||
|
TEST_PROGRAM("assert(im.height == 1)");
|
||||||
|
TEST_PROGRAM("assert(im.save)");
|
||||||
|
TEST_PROGRAM("assert(im.save)");
|
||||||
|
TEST_PROGRAM("assert(im.getPixel)");
|
||||||
|
TEST_PROGRAM("assert(im.setPixel)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(zeroDim) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("im = image.new(0, 0)");
|
||||||
|
TEST_PROGRAM("assert(not im)");
|
||||||
|
TEST_PROGRAM("im = image.new(1, 0)");
|
||||||
|
TEST_PROGRAM("assert(not im)");
|
||||||
|
TEST_PROGRAM("im = image.new(0, 1)");
|
||||||
|
TEST_PROGRAM("assert(not im)");
|
||||||
|
TEST_PROGRAM("im = image.new(1, 1)");
|
||||||
|
TEST_PROGRAM("assert(im)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(pixelColorDefault) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("im = image.new(1, 1)");
|
||||||
|
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(pixelColorRoundTrip) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM("im = image.new(1, 1)");
|
||||||
|
TEST_PROGRAM("im:setPixel(0, 0, 0xFF123456)");
|
||||||
|
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0xFF123456)");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_PNG
|
||||||
|
M_TEST_DEFINE(saveLoadRoundTrip) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
unlink("tmp.png");
|
||||||
|
TEST_PROGRAM("im = image.new(1, 1)");
|
||||||
|
TEST_PROGRAM("im:setPixel(0, 0, 0xFF123456)");
|
||||||
|
TEST_PROGRAM("assert(im:save('tmp.png'))");
|
||||||
|
TEST_PROGRAM("im = image.load('tmp.png')");
|
||||||
|
TEST_PROGRAM("assert(im)");
|
||||||
|
TEST_PROGRAM("assert(im:getPixel(0, 0) == 0xFF123456)");
|
||||||
|
unlink("tmp.png");
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptImage,
|
||||||
|
cmocka_unit_test(members),
|
||||||
|
cmocka_unit_test(zeroDim),
|
||||||
|
cmocka_unit_test(pixelColorDefault),
|
||||||
|
cmocka_unit_test(pixelColorRoundTrip),
|
||||||
|
#ifdef USE_PNG
|
||||||
|
cmocka_unit_test(saveLoadRoundTrip),
|
||||||
|
#endif
|
||||||
|
)
|
|
@ -6,9 +6,7 @@
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/internal/script/lua.h>
|
#include <mgba/internal/script/lua.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/input.h>
|
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
#include "script/test.h"
|
#include "script/test.h"
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/internal/script/lua.h>
|
#include <mgba/internal/script/lua.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/macros.h>
|
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
#include "script/test.h"
|
#include "script/test.h"
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/internal/script/lua.h>
|
#include <mgba/internal/script/lua.h>
|
||||||
#include <mgba/script/storage.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
#include "script/test.h"
|
#include "script/test.h"
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "util/test/suite.h"
|
#include "util/test/suite.h"
|
||||||
|
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script.h>
|
||||||
#include <mgba/script/macros.h>
|
|
||||||
#include <mgba/script/types.h>
|
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
int32_t a;
|
int32_t a;
|
||||||
|
@ -96,6 +94,12 @@ mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str);
|
||||||
mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list);
|
mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list);
|
||||||
mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg);
|
mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg);
|
||||||
mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg);
|
mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg);
|
||||||
|
mSCRIPT_BIND_FUNCTION_WITH_DEFAULTS(boundAddIntWithDefaults, S32, addInts, 2, S32, a, S32, b);
|
||||||
|
|
||||||
|
mSCRIPT_DEFINE_FUNCTION_BINDING_DEFAULTS(boundAddIntWithDefaults)
|
||||||
|
mSCRIPT_NO_DEFAULT,
|
||||||
|
mSCRIPT_S32(0)
|
||||||
|
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||||
|
|
||||||
M_TEST_DEFINE(voidArgs) {
|
M_TEST_DEFINE(voidArgs) {
|
||||||
struct mScriptFrame frame;
|
struct mScriptFrame frame;
|
||||||
|
@ -172,6 +176,30 @@ M_TEST_DEFINE(addS32) {
|
||||||
mScriptFrameDeinit(&frame);
|
mScriptFrameDeinit(&frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(addS32Defaults) {
|
||||||
|
struct mScriptFrame frame;
|
||||||
|
int32_t val;
|
||||||
|
|
||||||
|
mScriptFrameInit(&frame);
|
||||||
|
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||||
|
mSCRIPT_PUSH(&frame.arguments, S32, 2);
|
||||||
|
assert_true(mScriptInvoke(&boundAddIntWithDefaults, &frame));
|
||||||
|
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||||
|
assert_int_equal(val, 3);
|
||||||
|
mScriptFrameDeinit(&frame);
|
||||||
|
|
||||||
|
mScriptFrameInit(&frame);
|
||||||
|
mSCRIPT_PUSH(&frame.arguments, S32, 1);
|
||||||
|
assert_true(mScriptInvoke(&boundAddIntWithDefaults, &frame));
|
||||||
|
assert_true(mScriptPopS32(&frame.returnValues, &val));
|
||||||
|
assert_int_equal(val, 1);
|
||||||
|
mScriptFrameDeinit(&frame);
|
||||||
|
|
||||||
|
mScriptFrameInit(&frame);
|
||||||
|
assert_false(mScriptInvoke(&boundAddIntWithDefaults, &frame));
|
||||||
|
mScriptFrameDeinit(&frame);
|
||||||
|
}
|
||||||
|
|
||||||
M_TEST_DEFINE(subS32) {
|
M_TEST_DEFINE(subS32) {
|
||||||
struct mScriptFrame frame;
|
struct mScriptFrame frame;
|
||||||
mScriptFrameInit(&frame);
|
mScriptFrameInit(&frame);
|
||||||
|
@ -1316,6 +1344,7 @@ M_TEST_SUITE_DEFINE(mScript,
|
||||||
cmocka_unit_test(identityFunctionF32),
|
cmocka_unit_test(identityFunctionF32),
|
||||||
cmocka_unit_test(identityFunctionStruct),
|
cmocka_unit_test(identityFunctionStruct),
|
||||||
cmocka_unit_test(addS32),
|
cmocka_unit_test(addS32),
|
||||||
|
cmocka_unit_test(addS32Defaults),
|
||||||
cmocka_unit_test(subS32),
|
cmocka_unit_test(subS32),
|
||||||
cmocka_unit_test(wrongArgCountLo),
|
cmocka_unit_test(wrongArgCountLo),
|
||||||
cmocka_unit_test(wrongArgCountHi),
|
cmocka_unit_test(wrongArgCountHi),
|
||||||
|
|
|
@ -364,24 +364,6 @@ static uint32_t _hashString(const struct mScriptValue* val) {
|
||||||
return hash32(buffer, size, 0);
|
return hash32(buffer, size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t _hashScalar(const struct mScriptValue* val) {
|
|
||||||
// From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
|
|
||||||
uint32_t x = 0;
|
|
||||||
switch (val->type->base) {
|
|
||||||
case mSCRIPT_TYPE_SINT:
|
|
||||||
x = val->value.s32;
|
|
||||||
break;
|
|
||||||
case mSCRIPT_TYPE_UINT:
|
|
||||||
default:
|
|
||||||
x = val->value.u32;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
x = ((x >> 16) ^ x) * 0x45D9F3B;
|
|
||||||
x = ((x >> 16) ^ x) * 0x45D9F3B;
|
|
||||||
x = (x >> 16) ^ x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _wstrCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) {
|
bool _wstrCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) {
|
||||||
if (input->type->base != mSCRIPT_TYPE_WRAPPER) {
|
if (input->type->base != mSCRIPT_TYPE_WRAPPER) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -519,6 +501,16 @@ bool _castScalar(const struct mScriptValue* input, const struct mScriptType* typ
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t _hashScalar(const struct mScriptValue* val) {
|
||||||
|
// From https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
|
||||||
|
uint32_t x = 0;
|
||||||
|
_asUInt32(val, &x);
|
||||||
|
x = ((x >> 16) ^ x) * 0x45D9F3B;
|
||||||
|
x = ((x >> 16) ^ x) * 0x45D9F3B;
|
||||||
|
x = (x >> 16) ^ x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t _valHash(const void* val, size_t len, uint32_t seed) {
|
uint32_t _valHash(const void* val, size_t len, uint32_t seed) {
|
||||||
UNUSED(len);
|
UNUSED(len);
|
||||||
const struct mScriptValue* value = val;
|
const struct mScriptValue* value = val;
|
||||||
|
@ -1480,6 +1472,10 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m->readonly) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset);
|
void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset);
|
||||||
if (m->type != val->type) {
|
if (m->type != val->type) {
|
||||||
if (!mScriptCast(m->type, val, val)) {
|
if (!mScriptCast(m->type, val, val)) {
|
||||||
|
@ -1572,7 +1568,7 @@ void mScriptObjectFree(struct mScriptValue* value) {
|
||||||
if (value->type->base != mSCRIPT_TYPE_OBJECT) {
|
if (value->type->base != mSCRIPT_TYPE_OBJECT) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) {
|
if (value->flags & (mSCRIPT_VALUE_FLAG_DEINIT | mSCRIPT_VALUE_FLAG_FREE_BUFFER)) {
|
||||||
mScriptClassInit(value->type->details.cls);
|
mScriptClassInit(value->type->details.cls);
|
||||||
if (value->type->details.cls->free) {
|
if (value->type->details.cls->free) {
|
||||||
struct mScriptValue deinitMember;
|
struct mScriptValue deinitMember;
|
||||||
|
@ -1588,6 +1584,8 @@ void mScriptObjectFree(struct mScriptValue* value) {
|
||||||
mScriptFrameDeinit(&frame);
|
mScriptFrameDeinit(&frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (value->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) {
|
||||||
free(value->value.opaque);
|
free(value->value.opaque);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue