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*
|
||||
*~
|
||||
*.swp
|
||||
*.pyc
|
||||
|
||||
# Build directories
|
||||
/build
|
||||
/build-*
|
||||
/.vs
|
||||
|
||||
# Build files
|
||||
*.a
|
||||
*.dylib
|
||||
*.dll
|
||||
|
@ -18,4 +21,9 @@ CMakeCache.txt
|
|||
CMakeFiles
|
||||
CMakeSettings.json
|
||||
cmake_install.cmake
|
||||
hle-bios.bin
|
||||
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
|
||||
- Debugger: Add range watchpoints
|
||||
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
|
||||
- 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: Clear GB audio state when disabled
|
||||
- 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 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 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:
|
||||
- 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: 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: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702)
|
||||
- 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 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 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 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:
|
||||
- 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: Stop eating boolean action key events (fixes mgba.io/i/2636)
|
||||
- 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)
|
||||
Emulation fixes:
|
||||
|
|
|
@ -33,7 +33,7 @@ if(NOT MSVC)
|
|||
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format")
|
||||
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")
|
||||
else()
|
||||
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)
|
||||
set(OPENGL_LIBRARY OpenGL::GL)
|
||||
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()
|
||||
if(BUILD_GL)
|
||||
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")
|
||||
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))
|
||||
message(FATAL_ERROR "Windows requires epoxy module!")
|
||||
endif()
|
||||
|
|
|
@ -10,20 +10,21 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct Size {
|
||||
struct mSize {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct Rectangle {
|
||||
struct mRectangle {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
void RectangleUnion(struct Rectangle* dst, const struct Rectangle* add);
|
||||
void RectangleCenter(const struct Rectangle* ref, struct Rectangle* rect);
|
||||
void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add);
|
||||
bool mRectangleIntersection(struct mRectangle* dst, const struct mRectangle* add);
|
||||
void mRectangleCenter(const struct mRectangle* ref, struct mRectangle* rect);
|
||||
|
||||
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
|
||||
* 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 EXPORT_H
|
||||
#define EXPORT_H
|
||||
#ifndef M_IMAGE_EXPORT_H
|
||||
#define M_IMAGE_EXPORT_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
|
@ -12,8 +12,8 @@ CXX_GUARD_START
|
|||
|
||||
struct VFile;
|
||||
|
||||
bool exportPaletteRIFF(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
bool exportPaletteACT(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
bool mPaletteExportRIFF(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
|
||||
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
|||
|
||||
#ifdef USE_PNG
|
||||
|
||||
#include <mgba-util/image.h>
|
||||
|
||||
// png.h defines its own version of restrict which conflicts with mGBA's.
|
||||
#ifdef restrict
|
||||
#undef restrict
|
||||
|
@ -25,13 +27,10 @@ enum {
|
|||
};
|
||||
|
||||
png_structp PNGWriteOpen(struct VFile* source);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool 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);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat);
|
||||
png_infop PNGWriteHeaderPalette(png_structp png, unsigned width, unsigned height, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat);
|
||||
bool PNGWritePixelsPalette(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||
void PNGWriteClose(png_structp png, png_infop info);
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
#define _mCALL_0(FN, ...)
|
||||
#define _mCALL_1(FN, A) FN(A)
|
||||
#define _mCALL_2(FN, A, B) FN(A), FN(B)
|
||||
#define _mCALL_3(FN, A, ...) FN(A), _mCALL_2(FN, __VA_ARGS__)
|
||||
#define _mCALL_4(FN, A, ...) FN(A), _mCALL_3(FN, __VA_ARGS__)
|
||||
#define _mCALL_5(FN, A, ...) FN(A), _mCALL_4(FN, __VA_ARGS__)
|
||||
#define _mCALL_6(FN, A, ...) FN(A), _mCALL_5(FN, __VA_ARGS__)
|
||||
#define _mCALL_7(FN, A, ...) FN(A), _mCALL_6(FN, __VA_ARGS__)
|
||||
#define _mCALL_8(FN, A, ...) FN(A), _mCALL_7(FN, __VA_ARGS__)
|
||||
#define _mCALL_9(FN, A, ...) FN(A), _mCALL_8(FN, __VA_ARGS__)
|
||||
#define _mCALL_3(FN, A, B, C) FN(A), FN(B), FN(C)
|
||||
#define _mCALL_4(FN, A, B, C, D) FN(A), FN(B), FN(C), FN(D)
|
||||
#define _mCALL_5(FN, A, B, C, D, E) FN(A), FN(B), FN(C), FN(D), FN(E)
|
||||
#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, 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, 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, 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_1(N, ...) N, __VA_ARGS__
|
||||
|
@ -37,25 +37,25 @@
|
|||
|
||||
#define _mEVEN_0(...)
|
||||
#define _mEVEN_1(A, B, ...) A
|
||||
#define _mEVEN_2(A, B, ...) A, _mIDENT(_mEVEN_1(__VA_ARGS__))
|
||||
#define _mEVEN_3(A, B, ...) A, _mIDENT(_mEVEN_2(__VA_ARGS__))
|
||||
#define _mEVEN_4(A, B, ...) A, _mIDENT(_mEVEN_3(__VA_ARGS__))
|
||||
#define _mEVEN_5(A, B, ...) A, _mIDENT(_mEVEN_4(__VA_ARGS__))
|
||||
#define _mEVEN_2(A, B, C, D, ...) A, C
|
||||
#define _mEVEN_3(A, B, C, D, E, F, ...) A, C, E
|
||||
#define _mEVEN_4(A, B, C, D, E, F, G, H, ...) A, C, E, G
|
||||
#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_7(A, B, ...) A, _mIDENT(_mEVEN_6(__VA_ARGS__))
|
||||
#define _mEVEN_8(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
#define _mEVEN_9(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
#define _mEVEN_7(A, B, C, D, ...) A, C, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
#define _mEVEN_8(A, B, C, D, E, F, ...) A, C, E, _mIDENT(_mEVEN_5(__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_1(A, B, ...) B
|
||||
#define _mODD_2(A, B, ...) B, _mIDENT(_mODD_1(__VA_ARGS__))
|
||||
#define _mODD_3(A, B, ...) B, _mIDENT(_mODD_2(__VA_ARGS__))
|
||||
#define _mODD_4(A, B, ...) B, _mIDENT(_mODD_3(__VA_ARGS__))
|
||||
#define _mODD_5(A, B, ...) B, _mIDENT(_mODD_4(__VA_ARGS__))
|
||||
#define _mODD_2(A, B, C, D, ...) B, D
|
||||
#define _mODD_3(A, B, C, D, E, F, ...) B, D, F
|
||||
#define _mODD_4(A, B, C, D, E, F, G, H, ...) B, D, F, H
|
||||
#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_7(A, B, ...) B, _mIDENT(_mODD_6(__VA_ARGS__))
|
||||
#define _mODD_8(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
#define _mODD_9(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
#define _mODD_7(A, B, C, D, ...) B, D, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
#define _mODD_8(A, B, C, D, E, F, ...) B, D, F, _mIDENT(_mODD_5(__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_1(...)
|
||||
|
|
|
@ -33,6 +33,7 @@ struct mCoreOptions {
|
|||
int frameskip;
|
||||
bool rewindEnable;
|
||||
int rewindBufferCapacity;
|
||||
int rewindBufferInterval;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
unsigned sampleRate;
|
||||
|
|
|
@ -10,165 +10,14 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/image.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct mCore;
|
||||
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;
|
||||
|
||||
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 {
|
||||
mCORE_FEATURE_OPENGL = 1,
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ struct mCoreRewindContext {
|
|||
size_t size;
|
||||
struct VFile* previousState;
|
||||
struct VFile* currentState;
|
||||
int rewindFrameCounter;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
bool onThread;
|
||||
|
|
|
@ -30,8 +30,8 @@ enum VideoLayer {
|
|||
struct VideoBackend {
|
||||
void (*init)(struct VideoBackend*, WHandle handle);
|
||||
void (*deinit)(struct VideoBackend*);
|
||||
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct Rectangle*);
|
||||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct Rectangle*);
|
||||
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct mRectangle*);
|
||||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
|
||||
void (*swap)(struct VideoBackend*);
|
||||
void (*clear)(struct VideoBackend*);
|
||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||
|
@ -58,7 +58,7 @@ struct VideoShader {
|
|||
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);
|
||||
|
||||
CXX_GUARD_END
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba-util/socket.h>
|
||||
|
||||
#define GDB_STUB_MAX_LINE 1200
|
||||
#define GDB_STUB_MAX_LINE 1400
|
||||
#define GDB_STUB_INTERVAL 32
|
||||
|
||||
enum GDBStubAckState {
|
||||
|
|
|
@ -419,6 +419,9 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint8_t registersActive;
|
||||
} pocketCam;
|
||||
struct {
|
||||
uint64_t lastLatch;
|
||||
uint8_t reg;
|
||||
|
@ -484,6 +487,7 @@ struct GBSerializedState {
|
|||
|
||||
union {
|
||||
uint8_t huc3Registers[0x80];
|
||||
uint8_t pocketCamRegisters[0x36];
|
||||
struct {
|
||||
uint8_t registers[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 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 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, \
|
||||
.info = { \
|
||||
.member = { \
|
||||
.name = #EXPORTED_NAME, \
|
||||
.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) \
|
||||
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) { \
|
||||
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
||||
.info = { \
|
||||
|
@ -393,6 +403,9 @@ CXX_GUARD_START
|
|||
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__) \
|
||||
|
||||
#define mSCRIPT_DEFINE_FUNCTION_BINDING_DEFAULTS(NAME) \
|
||||
static const struct mScriptValue _bindingDefaults_ ## NAME[mSCRIPT_PARAMS_MAX] = {
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
|
@ -439,7 +452,7 @@ CXX_GUARD_START
|
|||
|
||||
#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 = { \
|
||||
.call = _binding_ ## NAME \
|
||||
}; \
|
||||
|
@ -456,6 +469,7 @@ CXX_GUARD_START
|
|||
.count = NPARAMS, \
|
||||
.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__)) }, \
|
||||
.defaults = DEFAULTS, \
|
||||
}, \
|
||||
.returnType = { \
|
||||
.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) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
|
@ -482,9 +496,9 @@ CXX_GUARD_START
|
|||
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
||||
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) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
|
@ -494,7 +508,21 @@ CXX_GUARD_START
|
|||
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
||||
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, ...) \
|
||||
static const struct mScriptType _mScriptDocType_ ## NAME = { \
|
||||
|
|
|
@ -164,7 +164,8 @@ enum mScriptClassInitType {
|
|||
};
|
||||
|
||||
enum {
|
||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1
|
||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1,
|
||||
mSCRIPT_VALUE_FLAG_DEINIT = 2,
|
||||
};
|
||||
|
||||
struct mScriptType;
|
||||
|
@ -231,6 +232,7 @@ struct mScriptClassMember {
|
|||
const char* docstring;
|
||||
const struct mScriptType* type;
|
||||
size_t offset;
|
||||
bool readonly;
|
||||
};
|
||||
|
||||
struct mScriptClassCastMember {
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Only used when Game Boy Camera is selected and a physical camera is set</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<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
|
||||
MichaelK__
|
||||
Miras Absar
|
||||
Nic Losby
|
||||
Petru-Sebastian Toader
|
||||
Stevoisiak
|
||||
Tyler Jenkins
|
||||
William K. Leung
|
||||
Zach
|
||||
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
|
||||
end
|
||||
|
||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
local mon = {}
|
||||
mon.species = emu:read8(address + 0)
|
||||
mon.item = emu:read8(address + 1)
|
||||
|
|
|
@ -34,12 +34,15 @@ void mCacheSetDeinit(struct mCacheSet* cache) {
|
|||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
|
||||
}
|
||||
mMapCacheSetDeinit(&cache->maps);
|
||||
for (i = 0; i < mBitmapCacheSetSize(&cache->bitmaps); ++i) {
|
||||
mBitmapCacheDeinit(mBitmapCacheSetGetPointer(&cache->bitmaps, i));
|
||||
}
|
||||
mBitmapCacheSetDeinit(&cache->bitmaps);
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
|
||||
}
|
||||
mTileCacheSetDeinit(&cache->tiles);
|
||||
}
|
||||
|
||||
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) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wpath[MAX_PATH];
|
||||
HMODULE hModule = GetModuleHandleW(NULL);
|
||||
GetModuleFileNameW(hModule, wpath, MAX_PATH);
|
||||
GetModuleFileNameW(NULL, wpath, MAX_PATH);
|
||||
PathRemoveFileSpecW(wpath);
|
||||
if (PATH_SEP[0] != '\\') {
|
||||
WCHAR* pathSep;
|
||||
|
@ -407,6 +406,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
|||
_lookupIntValue(config, "frameskip", &opts->frameskip);
|
||||
_lookupIntValue(config, "volume", &opts->volume);
|
||||
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
|
||||
_lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
|
||||
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
|
||||
unsigned 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, "rewindEnable", opts->rewindEnable);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
|
||||
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
|
||||
|
|
|
@ -94,7 +94,7 @@ struct mCore* mCoreCreate(enum mPlatform platform) {
|
|||
}
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
|
||||
#ifdef PSP2
|
||||
#include <psp2/photoexport.h>
|
||||
|
@ -377,8 +377,8 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
|
|||
core->currentVideoSize(core, &width, &height);
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
bool success = PNGWritePixels(png, width, height, stride, pixels);
|
||||
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||
bool success = PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||
PNGWriteClose(png, info);
|
||||
return success;
|
||||
#else
|
||||
|
|
|
@ -30,6 +30,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries,
|
|||
context->previousState = VFileMemChunk(0, 0);
|
||||
context->currentState = VFileMemChunk(0, 0);
|
||||
context->size = 0;
|
||||
context->rewindFrameCounter = 0;
|
||||
#ifndef DISABLE_THREADING
|
||||
context->onThread = onThread;
|
||||
context->ready = false;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/script/base.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -399,6 +400,7 @@ static int _mScriptCoreLoadStateFile(struct mCore* core, const char* path, int f
|
|||
vf->close(vf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) {
|
||||
if (filename) {
|
||||
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
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadFile, mCoreLoadFile, 1, CHARP, path);
|
||||
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
|
||||
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_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_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_DOCSTRING("Get a screenshot in an struct::mImage")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshotToImage)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <png.h>
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
@ -177,13 +177,13 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
|
|||
unsigned width, height;
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||
if (!png || !info) {
|
||||
PNGWriteClose(png, info);
|
||||
free(buffer);
|
||||
return false;
|
||||
}
|
||||
PNGWritePixels(png, width, height, stride, pixels);
|
||||
PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
||||
if (extdata) {
|
||||
uint32_t i;
|
||||
|
@ -453,13 +453,14 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
#endif
|
||||
vf->truncate(vf, stateSize);
|
||||
void* state = vf->map(vf, stateSize, MAP_WRITE);
|
||||
if (!state || !core->saveState(core, state)) {
|
||||
if (!state) {
|
||||
mStateExtdataDeinit(&extdata);
|
||||
if (cheatVf) {
|
||||
cheatVf->close(cheatVf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
core->saveState(core, state);
|
||||
vf->unmap(vf, state, stateSize);
|
||||
vf->seek(vf, stateSize, SEEK_SET);
|
||||
mStateExtdataSerialize(&extdata, vf);
|
||||
|
|
|
@ -310,6 +310,24 @@ M_TEST_DEFINE(logging) {
|
|||
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,
|
||||
cmocka_unit_test(globals),
|
||||
cmocka_unit_test(infoFuncs),
|
||||
|
@ -318,4 +336,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
|||
cmocka_unit_test(memoryRead),
|
||||
cmocka_unit_test(memoryWrite),
|
||||
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->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) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
if (!dv) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
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;
|
||||
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=\"lr\" bitsize=\"32\"/>"
|
||||
"<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>"
|
||||
"</target>";
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ set(SOURCE_FILES
|
|||
commandline.c
|
||||
thread-proxy.c
|
||||
updater.c
|
||||
video-backend.c
|
||||
video-logger.c)
|
||||
|
||||
set(GUI_FILES
|
||||
|
|
|
@ -158,19 +158,35 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
|
|||
}
|
||||
encoder->sampleRate = encoder->isampleRate;
|
||||
if (codec->supported_samplerates) {
|
||||
bool gotSampleRate = false;
|
||||
int highestSampleRate = 0;
|
||||
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) {
|
||||
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];
|
||||
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) {
|
||||
// HACK: FLAC doesn't support > 65535Hz unless it's divisible by 10
|
||||
if (encoder->sampleRate >= 65535) {
|
||||
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) {
|
||||
// HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
|
||||
encoder->sampleRate = 48000;
|
||||
|
@ -890,7 +906,7 @@ void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRa
|
|||
}
|
||||
|
||||
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);
|
||||
#ifdef USE_LIBAVRESAMPLE
|
||||
encoder->resampleContext = avresample_alloc_context();
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#include <mgba-util/gui/file-select.h>
|
||||
#include <mgba-util/gui/font.h>
|
||||
#include <mgba-util/gui/menu.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef PSP2
|
||||
|
@ -456,6 +456,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
mLOG(GUI_RUNNER, DEBUG, "Loading save...");
|
||||
mCoreAutoloadSave(runner->core);
|
||||
mCoreAutoloadCheats(runner->core);
|
||||
mCoreAutoloadPatch(runner->core);
|
||||
if (runner->setup) {
|
||||
mLOG(GUI_RUNNER, DEBUG, "Setting up runner...");
|
||||
runner->setup(runner);
|
||||
|
|
|
@ -3,20 +3,20 @@
|
|||
* 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 "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));
|
||||
int i;
|
||||
for (i = 0; i < VIDEO_LAYER_MAX; ++i) {
|
||||
struct Rectangle dims;
|
||||
struct mRectangle dims;
|
||||
v->layerDimensions(v, i, &dims);
|
||||
RectangleUnion(frame, &dims);
|
||||
mRectangleUnion(frame, &dims);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoBackendGetFrameSize(const struct VideoBackend* v, unsigned* width, unsigned* height) {
|
||||
struct Rectangle frame;
|
||||
struct mRectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
*width = frame.width;
|
||||
*height = frame.height;
|
|
@ -138,6 +138,9 @@ void GBAudioReset(struct GBAudio* audio) {
|
|||
}
|
||||
|
||||
void GBAudioResizeBuffer(struct GBAudio* audio, size_t samples) {
|
||||
if (samples > BLIP_BUFFER_SIZE / 2) {
|
||||
samples = BLIP_BUFFER_SIZE / 2;
|
||||
}
|
||||
mCoreSyncLockAudio(audio->p->sync);
|
||||
audio->samples = samples;
|
||||
blip_clear(audio->left);
|
||||
|
@ -199,6 +202,7 @@ void GBAudioWriteNR14(struct GBAudio* audio, uint8_t value) {
|
|||
--audio->ch1.control.length;
|
||||
}
|
||||
}
|
||||
audio->ch1.lastUpdate = mTimingCurrentTime(audio->timing);
|
||||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
*audio->nr52 &= ~0x0001;
|
||||
|
@ -246,6 +250,7 @@ void GBAudioWriteNR24(struct GBAudio* audio, uint8_t value) {
|
|||
--audio->ch2.control.length;
|
||||
}
|
||||
}
|
||||
audio->ch2.lastUpdate = mTimingCurrentTime(audio->timing);
|
||||
_updateSquareSample(&audio->ch2);
|
||||
}
|
||||
*audio->nr52 &= ~0x0002;
|
||||
|
|
|
@ -227,7 +227,7 @@ void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
mbcState->rumble = !!(value & 0x80);
|
||||
}
|
||||
|
||||
if (mbcState->rumble) {
|
||||
if (mbcState->rumble && memory->rumble) {
|
||||
memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02));
|
||||
}
|
||||
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;
|
||||
}
|
||||
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:
|
||||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
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;
|
||||
}
|
||||
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:
|
||||
memory->mbcState.mmm01.locked = state->memory.mmm01.locked;
|
||||
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) {
|
||||
if (samples > 0x2000) {
|
||||
samples = 0x2000;
|
||||
}
|
||||
mCoreSyncLockAudio(audio->p->sync);
|
||||
audio->samples = samples;
|
||||
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) {
|
||||
GBAAudioSample(audio, mTimingCurrentTime(&audio->p->timing));
|
||||
audio->enable = GBAudioEnableGetEnable(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) {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#ifdef USE_FFMPEG
|
||||
#include <mgba-util/convolve.h>
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#endif
|
||||
|
||||
|
@ -888,7 +888,11 @@ struct EReaderScan* EReaderScanLoadImagePNG(const char* filename) {
|
|||
}
|
||||
png_infop info = 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 width = png_get_image_width(png, info);
|
||||
int type = png_get_color_type(png, info);
|
||||
|
@ -900,19 +904,34 @@ struct EReaderScan* EReaderScanLoadImagePNG(const char* filename) {
|
|||
break;
|
||||
}
|
||||
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;
|
||||
case PNG_COLOR_TYPE_RGBA:
|
||||
if (depth != 8) {
|
||||
break;
|
||||
}
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PNGReadFooter(png, end);
|
||||
out:
|
||||
PNGReadClose(png, info, end);
|
||||
vf->close(vf);
|
||||
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,
|
||||
0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3,
|
||||
0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a,
|
||||
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, 0x04, 0x00, 0x55, 0xe1,
|
||||
0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
|
||||
0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xb0, 0xe8, 0x04, 0x00, 0x51, 0xe1,
|
||||
0x08, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea,
|
||||
0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0,
|
||||
0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0,
|
||||
0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3,
|
||||
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
|
||||
0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
||||
0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x51, 0xe1,
|
||||
0x08, 0x00, 0xb0, 0xb8, 0x08, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
||||
0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1,
|
||||
0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba,
|
||||
0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9,
|
||||
|
|
|
@ -209,10 +209,10 @@ tst r2, #0x04000000
|
|||
beq 1f
|
||||
@ Word
|
||||
add r4, r5, r4, lsr #10
|
||||
ldmia r12!, {r3}
|
||||
ldmia r0!, {r3}
|
||||
2:
|
||||
cmp r5, r4
|
||||
stmltia r5!, {r3}
|
||||
cmp r1, r4
|
||||
stmltia r1!, {r3}
|
||||
blt 2b
|
||||
b 3f
|
||||
@ Halfword
|
||||
|
@ -233,9 +233,9 @@ beq 1f
|
|||
@ Word
|
||||
add r4, r5, r4, lsr #10
|
||||
2:
|
||||
cmp r5, r4
|
||||
ldmltia r12!, {r3}
|
||||
stmltia r5!, {r3}
|
||||
cmp r1, r4
|
||||
ldmltia r0!, {r3}
|
||||
stmltia r1!, {r3}
|
||||
blt 2b
|
||||
b 3f
|
||||
@ Halfword
|
||||
|
|
|
@ -1747,15 +1747,13 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
|||
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
|
||||
int32_t s = cpu->memory.activeSeqCycles16;
|
||||
int32_t stall = s + 1;
|
||||
int32_t loads = 1;
|
||||
|
||||
while (stall < wait && loads < maxLoads) {
|
||||
stall += s;
|
||||
stall += s + 1;
|
||||
++loads;
|
||||
}
|
||||
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.
|
||||
wait -= n2s;
|
||||
wait -= cpu->memory.activeNonseqCycles16 - s;
|
||||
|
||||
// The next |loads|S waitstates disappear entirely, so long as they're all in a row
|
||||
wait -= stall - 1;
|
||||
wait -= stall;
|
||||
|
||||
return wait;
|
||||
}
|
||||
|
|
|
@ -123,10 +123,8 @@ static const struct GBACartridgeOverride _overrides[] = {
|
|||
{ "BPEF", SAVEDATA_FLASH1M, HW_RTC, 0x80008C6, false },
|
||||
|
||||
// Pokemon Mystery Dungeon
|
||||
{ "B24J", 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 },
|
||||
{ "B24U", SAVEDATA_FLASH1M, HW_NONE, IDLE_LOOP_NONE, false },
|
||||
|
||||
// Pokemon FireRed
|
||||
{ "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"
|
||||
" mat[3] = transform[start + 3].xy;\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";
|
||||
|
||||
static const char* const _renderMode2 =
|
||||
|
@ -250,8 +273,7 @@ static const char* const _renderMode2 =
|
|||
"OUT(0) out vec4 color;\n"
|
||||
|
||||
"int fetchTile(ivec2 coord);\n"
|
||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
||||
"ivec2 affineInterpolate();\n"
|
||||
|
||||
"int renderTile(ivec2 coord) {\n"
|
||||
" int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
|
||||
|
@ -278,26 +300,7 @@ static const char* const _renderMode2 =
|
|||
"}\n"
|
||||
|
||||
"void main() {\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 = 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"
|
||||
" int paletteEntry = fetchTile(affineInterpolate());\n"
|
||||
" color = texelFetch(palette, ivec2(paletteEntry, int(texCoord.y)), 0);\n"
|
||||
"}";
|
||||
|
||||
|
@ -325,30 +328,10 @@ static const char* const _renderMode35 =
|
|||
"uniform ivec2 mosaic;\n"
|
||||
"OUT(0) out vec4 color;\n"
|
||||
|
||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
||||
"ivec2 affineInterpolate();\n"
|
||||
|
||||
"void main() {\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 = 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"
|
||||
" ivec2 coord = affineInterpolate();\n"
|
||||
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
||||
" discard;\n"
|
||||
" }\n"
|
||||
|
@ -386,30 +369,10 @@ static const char* const _renderMode4 =
|
|||
"uniform ivec2 mosaic;\n"
|
||||
"OUT(0) out vec4 color;\n"
|
||||
|
||||
"vec2 interpolate(ivec2 arr[4], float x);\n"
|
||||
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
|
||||
"ivec2 affineInterpolate();\n"
|
||||
|
||||
"void main() {\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 = 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"
|
||||
" ivec2 coord = affineInterpolate();\n"
|
||||
" if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
|
||||
" discard;\n"
|
||||
" }\n"
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef PSP2
|
||||
#include <psp2/rtc.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
@ -637,6 +641,9 @@ void GBASavedataRTCRead(struct GBASavedata* savedata) {
|
|||
}
|
||||
LOAD_64LE(savedata->gpio->rtc.lastLatch, 0, &buffer.lastLatch);
|
||||
|
||||
time_t rtcTime;
|
||||
|
||||
#ifndef PSP2
|
||||
struct tm date;
|
||||
date.tm_year = _unBCD(savedata->gpio->rtc.time[0]) + 100;
|
||||
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_sec = _unBCD(savedata->gpio->rtc.time[6]);
|
||||
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) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba-util/gui/font.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 "icons.h"
|
||||
|
||||
|
|
|
@ -873,7 +873,7 @@ int main(int argc, char* argv[]) {
|
|||
u8 model = 0;
|
||||
cfguInit();
|
||||
CFGU_GetSystemModel(&model);
|
||||
if (model != 3 /* o2DS */) {
|
||||
if (model != CFG_MODEL_2DS) {
|
||||
gfxSetWide(true);
|
||||
}
|
||||
cfguExit();
|
||||
|
|
|
@ -63,7 +63,7 @@ static inline void _setTexDims(int width, int height) {
|
|||
#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;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
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;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
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];
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
glScissor(viewport[0] + dims->x * viewport[2] / frameW,
|
||||
viewport[1] + dims->y * viewport[3] / frameH,
|
||||
dims->width * viewport[2] / frameW,
|
||||
dims->height * viewport[3] / frameH);
|
||||
glScissor(viewport[0] + (dims->x - frame->x) * viewport[2] / frame->width,
|
||||
viewport[1] + (dims->y - frame->y) * viewport[3] / frame->height,
|
||||
dims->width * viewport[2] / frame->width,
|
||||
dims->height * viewport[3] / frame->height);
|
||||
glTranslatef(dims->x, dims->y, 0);
|
||||
glScalef(toPow2(dims->width), toPow2(dims->height), 1);
|
||||
}
|
||||
|
@ -162,9 +162,9 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
|
|||
glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
unsigned frameW, frameH;
|
||||
VideoBackendGetFrameSize(v, &frameW, &frameH);
|
||||
glOrtho(0, frameW, frameH, 0, 0, 1);
|
||||
struct mRectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
glOrtho(frame.x, frame.x + frame.width, frame.y + frame.height, frame.y, 0, 1);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
|
@ -178,12 +178,12 @@ void mGLContextDrawFrame(struct VideoBackend* v) {
|
|||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
_setFilter(v);
|
||||
glPushMatrix();
|
||||
_setFrame(&context->layerDims[layer], frameW, frameH);
|
||||
_setFrame(&context->layerDims[layer], &frame);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], frameW, frameH);
|
||||
_setFrame(&context->layerDims[VIDEO_LAYER_IMAGE], &frame);
|
||||
if (v->interframeBlending) {
|
||||
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
|
||||
glBlendColor(1, 1, 1, 0.5);
|
||||
|
|
|
@ -21,7 +21,7 @@ CXX_GUARD_START
|
|||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#include "platform/video-backend.h"
|
||||
#include <mgba/feature/video-backend.h>
|
||||
|
||||
struct mGLContext {
|
||||
struct VideoBackend d;
|
||||
|
@ -29,8 +29,8 @@ struct mGLContext {
|
|||
int activeTex;
|
||||
GLuint tex[2];
|
||||
GLuint layers[VIDEO_LAYER_MAX];
|
||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
||||
struct mRectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct mSize imageSizes[VIDEO_LAYER_MAX];
|
||||
};
|
||||
|
||||
void mGLContextCreate(struct mGLContext*);
|
||||
|
|
|
@ -194,7 +194,7 @@ static inline void _setTexDims(int width, int height) {
|
|||
#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;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
|
@ -212,10 +212,9 @@ static void mGLES2ContextSetLayerDimensions(struct VideoBackend* v, enum VideoLa
|
|||
context->layerDims[layer].x = dims->x;
|
||||
context->layerDims[layer].y = dims->y;
|
||||
|
||||
unsigned newW;
|
||||
unsigned newH;
|
||||
VideoBackendGetFrameSize(v, &newW, &newH);
|
||||
if (newW != context->width || newH != context->height) {
|
||||
struct mRectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
if (frame.width != context->width || frame.height != context->height) {
|
||||
size_t n;
|
||||
for (n = 0; n < context->nShaders; ++n) {
|
||||
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->interframeShader.dirty = true;
|
||||
context->width = newW;
|
||||
context->height = newH;
|
||||
context->width = frame.width;
|
||||
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;
|
||||
if (layer >= VIDEO_LAYER_MAX) {
|
||||
return;
|
||||
|
@ -320,7 +321,7 @@ static void _drawShaderEx(struct mGLES2Context* context, struct mGLES2Shader* sh
|
|||
}
|
||||
|
||||
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 {
|
||||
glViewport(padW, padH, drawW, drawH);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ CXX_GUARD_START
|
|||
#include <GLES2/gl2.h>
|
||||
#endif
|
||||
|
||||
#include "platform/video-backend.h"
|
||||
#include <mgba/feature/video-backend.h>
|
||||
|
||||
union mGLES2UniformValue {
|
||||
GLfloat f;
|
||||
|
@ -82,8 +82,10 @@ struct mGLES2Context {
|
|||
GLuint tex[VIDEO_LAYER_MAX];
|
||||
GLuint vbo;
|
||||
|
||||
struct Rectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct Size imageSizes[VIDEO_LAYER_MAX];
|
||||
struct mRectangle layerDims[VIDEO_LAYER_MAX];
|
||||
struct mSize imageSizes[VIDEO_LAYER_MAX];
|
||||
int x;
|
||||
int y;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
|
|||
-lScePgf_stub
|
||||
-lScePhotoExport_stub
|
||||
-lScePower_stub
|
||||
-lSceRtc_stub
|
||||
-lSceSysmodule_stub
|
||||
-lSceTouch_stub)
|
||||
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
|
||||
|
|
|
@ -53,7 +53,7 @@ void free(void*);
|
|||
#undef PYEXPORT
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/interface.h>
|
||||
|
|
|
@ -42,7 +42,7 @@ ffi.set_source("mgba._pylib", """
|
|||
#include <mgba/internal/sm83/sm83.h>
|
||||
#include <mgba/internal/gb/gb.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>
|
||||
|
||||
#define PYEXPORT
|
||||
|
|
|
@ -21,20 +21,16 @@ class PNG:
|
|||
def write_header(self, image):
|
||||
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
||||
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:
|
||||
self._info = lib.PNGWriteHeaderA(self._png, image.width, image.height)
|
||||
if self.mode == MODE_INDEX:
|
||||
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
||||
self._info = lib.PNGWriteHeader(self._png, image.width, image.height, lib.mCOLOR_ABGR8)
|
||||
return self._info != ffi.NULL
|
||||
|
||||
def write_pixels(self, image):
|
||||
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:
|
||||
return lib.PNGWritePixelsA(self._png, image.width, image.height, image.stride, image.buffer)
|
||||
if self.mode == MODE_INDEX:
|
||||
return lib.PNGWritePixels8(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)
|
||||
return False
|
||||
|
||||
def write_close(self):
|
||||
|
|
|
@ -125,6 +125,7 @@ ConfigController::ConfigController(QObject* parent)
|
|||
m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL;
|
||||
m_opts.rewindEnable = false;
|
||||
m_opts.rewindBufferCapacity = 300;
|
||||
m_opts.rewindBufferInterval = 1;
|
||||
m_opts.useBios = true;
|
||||
m_opts.suspendScreensaver = true;
|
||||
m_opts.lockAspectRatio = true;
|
||||
|
|
|
@ -297,13 +297,6 @@ void CoreController::loadConfig(ConfigController* config) {
|
|||
mCoreConfigCopyValue(&m_threadContext.core->config, config->config(), "mute");
|
||||
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();
|
||||
m_activeBuffer.resize(256 * 224 * sizeof(color_t));
|
||||
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);
|
||||
clearMultiplayerController();
|
||||
GBPrinterCreate(&m_printer.d);
|
||||
GBPrinterCreate(&m_printer);
|
||||
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);
|
||||
QImage image(GB_VIDEO_HORIZONTAL_PIXELS, height, QImage::Format_Indexed8);
|
||||
QVector<QRgb> colors;
|
||||
|
@ -1023,7 +1016,7 @@ void CoreController::attachPrinter() {
|
|||
QMetaObject::invokeMethod(qPrinter->parent, "imagePrinted", Q_ARG(const QImage&, image));
|
||||
};
|
||||
Interrupter interrupter(this);
|
||||
GBSIOSetDriver(&gb->sio, &m_printer.d.d);
|
||||
GBSIOSetDriver(&gb->sio, &m_printer.d);
|
||||
}
|
||||
|
||||
void CoreController::detachPrinter() {
|
||||
|
@ -1032,7 +1025,7 @@ void CoreController::detachPrinter() {
|
|||
}
|
||||
Interrupter interrupter(this);
|
||||
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
||||
GBPrinterDonePrinting(&m_printer.d);
|
||||
GBPrinterDonePrinting(&m_printer);
|
||||
GBSIOSetDriver(&gb->sio, nullptr);
|
||||
}
|
||||
|
||||
|
@ -1041,7 +1034,7 @@ void CoreController::endPrint() {
|
|||
return;
|
||||
}
|
||||
Interrupter interrupter(this);
|
||||
GBPrinterDonePrinting(&m_printer.d);
|
||||
GBPrinterDonePrinting(&m_printer);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1199,7 +1192,12 @@ void CoreController::updatePlayerSave() {
|
|||
int savePlayerId = 0;
|
||||
mCoreConfigGetIntValue(&m_threadContext.core->config, "savePlayerId", &savePlayerId);
|
||||
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;
|
||||
|
|
|
@ -313,8 +313,7 @@ private:
|
|||
VFile* m_vlVf = nullptr;
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
struct QGBPrinter {
|
||||
GBPrinter d;
|
||||
struct QGBPrinter : public GBPrinter {
|
||||
CoreController* parent;
|
||||
} m_printer;
|
||||
#endif
|
||||
|
|
|
@ -19,18 +19,18 @@ using namespace QGBA;
|
|||
DebuggerConsoleController::DebuggerConsoleController(QObject* parent)
|
||||
: DebuggerController(&m_cliDebugger.d, parent)
|
||||
{
|
||||
m_backend.d.printf = printf;
|
||||
m_backend.d.init = init;
|
||||
m_backend.d.deinit = deinit;
|
||||
m_backend.d.readline = readLine;
|
||||
m_backend.d.lineAppend = lineAppend;
|
||||
m_backend.d.historyLast = historyLast;
|
||||
m_backend.d.historyAppend = historyAppend;
|
||||
m_backend.d.interrupt = interrupt;
|
||||
m_backend.printf = printf;
|
||||
m_backend.init = init;
|
||||
m_backend.deinit = deinit;
|
||||
m_backend.readline = readLine;
|
||||
m_backend.lineAppend = lineAppend;
|
||||
m_backend.historyLast = historyLast;
|
||||
m_backend.historyAppend = historyAppend;
|
||||
m_backend.interrupt = interrupt;
|
||||
m_backend.self = this;
|
||||
|
||||
CLIDebuggerCreate(&m_cliDebugger);
|
||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d);
|
||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend);
|
||||
}
|
||||
|
||||
void DebuggerConsoleController::enterLine(const QString& line) {
|
||||
|
@ -60,7 +60,7 @@ void DebuggerConsoleController::attachInternal() {
|
|||
CoreController::Interrupter interrupter(m_gameController);
|
||||
QMutexLocker lock(&m_mutex);
|
||||
mCore* core = m_gameController->thread()->core;
|
||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d);
|
||||
CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend);
|
||||
CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core));
|
||||
}
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ private:
|
|||
QStringList m_lines;
|
||||
QByteArray m_last;
|
||||
|
||||
struct Backend {
|
||||
CLIDebuggerBackend d;
|
||||
struct Backend : public CLIDebuggerBackend {
|
||||
DebuggerConsoleController* self;
|
||||
} m_backend;
|
||||
};
|
||||
|
|
|
@ -129,6 +129,7 @@ void QGBA::Display::configure(ConfigController* config) {
|
|||
filter(opts->resampleVideo);
|
||||
config->updateOption("showOSD");
|
||||
config->updateOption("showFrameCounter");
|
||||
config->updateOption("videoSync");
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
if (opts->shader && supportsShaders()) {
|
||||
struct VDir* shader = VDirOpen(opts->shader);
|
||||
|
|
|
@ -78,6 +78,7 @@ public slots:
|
|||
virtual void showOSDMessages(bool enable);
|
||||
virtual void showFrameCounter(bool enable);
|
||||
virtual void filter(bool filter);
|
||||
virtual void swapInterval(int interval) = 0;
|
||||
virtual void framePosted() = 0;
|
||||
virtual void setShaders(struct VDir*) = 0;
|
||||
virtual void clearShaders() = 0;
|
||||
|
|
|
@ -38,6 +38,21 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
|
|||
#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
|
||||
#define OVERHEAD_NSEC 1000000
|
||||
#else
|
||||
|
@ -48,11 +63,6 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core;
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
enum ThreadStartFrom {
|
||||
START = 1,
|
||||
PROXY = 2,
|
||||
};
|
||||
|
||||
QHash<QSurfaceFormat, bool> DisplayGL::s_supports;
|
||||
|
||||
uint qHash(const QSurfaceFormat& format, uint seed) {
|
||||
|
@ -206,11 +216,6 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
|||
m_drawThread.setObjectName("Painter Thread");
|
||||
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_painter.get(), &PainterGL::started, this, [this] {
|
||||
m_hasStarted = true;
|
||||
|
@ -225,11 +230,6 @@ DisplayGL::~DisplayGL() {
|
|||
QMetaObject::invokeMethod(m_painter.get(), "destroy", Qt::BlockingQueuedConnection);
|
||||
m_drawThread.exit();
|
||||
m_drawThread.wait();
|
||||
|
||||
if (m_proxyThread.isRunning()) {
|
||||
m_proxyThread.exit();
|
||||
m_proxyThread.wait();
|
||||
}
|
||||
}
|
||||
|
||||
bool DisplayGL::supportsShaders() const {
|
||||
|
@ -253,6 +253,9 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
|||
m_painter->setContext(controller);
|
||||
m_painter->setMessagePainter(messagePainter());
|
||||
m_context = controller;
|
||||
if (videoProxy()) {
|
||||
videoProxy()->moveToThread(&m_drawThread);
|
||||
}
|
||||
|
||||
lockAspectRatio(isAspectRatioLocked());
|
||||
lockIntegerScaling(isIntegerScalingLocked());
|
||||
|
@ -267,15 +270,6 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
|||
messagePainter()->resize(size(), devicePixelRatio());
|
||||
#endif
|
||||
|
||||
startThread(ThreadStartFrom::START);
|
||||
}
|
||||
|
||||
void DisplayGL::startThread(int from) {
|
||||
m_threadStartPending |= from;
|
||||
if (m_threadStartPending < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "start");
|
||||
if (!m_gl) {
|
||||
|
@ -353,7 +347,6 @@ void DisplayGL::stopDrawing() {
|
|||
hide();
|
||||
}
|
||||
setUpdatesEnabled(true);
|
||||
m_threadStartPending &= ~1;
|
||||
}
|
||||
m_context.reset();
|
||||
}
|
||||
|
@ -419,6 +412,10 @@ void DisplayGL::filter(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() {
|
||||
m_painter->enqueue(m_context->drawContext());
|
||||
QMetaObject::invokeMethod(m_painter.get(), "draw");
|
||||
|
@ -474,35 +471,11 @@ bool DisplayGL::shouldDisableUpdates() {
|
|||
void DisplayGL::setVideoProxy(std::shared_ptr<VideoProxy> proxy) {
|
||||
Display::setVideoProxy(proxy);
|
||||
if (proxy) {
|
||||
proxy->moveToThread(&m_proxyThread);
|
||||
proxy->moveToThread(&m_drawThread);
|
||||
}
|
||||
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() {
|
||||
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)));
|
||||
mGLES2ContextCreate(gl2Backend);
|
||||
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
|
||||
|
||||
|
@ -650,11 +617,9 @@ void PainterGL::destroy() {
|
|||
}
|
||||
makeCurrent();
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
QOpenGLFunctions* fn = m_gl->functions();
|
||||
if (m_shader.passes) {
|
||||
mGLES2ShaderFree(&m_shader);
|
||||
}
|
||||
fn->glDeleteTextures(m_bridgeTexes.size(), m_bridgeTexes.data());
|
||||
#endif
|
||||
m_backend->deinit(m_backend);
|
||||
m_gl->doneCurrent();
|
||||
|
@ -686,7 +651,7 @@ void PainterGL::resizeContext() {
|
|||
}
|
||||
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);
|
||||
recenterLayers();
|
||||
}
|
||||
|
@ -699,26 +664,20 @@ void PainterGL::recenterLayers() {
|
|||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND, VIDEO_LAYER_IMAGE};
|
||||
Rectangle frame = {0};
|
||||
const static std::initializer_list<VideoLayer> centeredLayers{VIDEO_LAYER_BACKGROUND};
|
||||
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());
|
||||
|
||||
for (VideoLayer l : centeredLayers) {
|
||||
Rectangle dims{};
|
||||
int width, height;
|
||||
mRectangle dims{};
|
||||
m_backend->imageSize(m_backend, l, &width, &height);
|
||||
dims.width = width;
|
||||
dims.height = height;
|
||||
if (l != VIDEO_LAYER_IMAGE) {
|
||||
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);
|
||||
dims.width = width * scale;
|
||||
dims.height = height * scale;
|
||||
mRectangleCenter(&frame, &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
|
||||
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
|
||||
#endif
|
||||
|
@ -785,16 +770,16 @@ void PainterGL::start() {
|
|||
}
|
||||
#endif
|
||||
resizeContext();
|
||||
m_context->addFrameAction(std::bind(&PainterGL::swapTex, this));
|
||||
|
||||
m_buffer = nullptr;
|
||||
m_active = true;
|
||||
m_started = true;
|
||||
swapInterval(1);
|
||||
emit started();
|
||||
}
|
||||
|
||||
void PainterGL::draw() {
|
||||
if (!m_started || (m_queue.isEmpty() && m_queueTex.isEmpty())) {
|
||||
if (!m_started || m_queue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -820,12 +805,16 @@ void PainterGL::draw() {
|
|||
}
|
||||
return;
|
||||
}
|
||||
int wantSwap = sync->audioWait || sync->videoFrameWait;
|
||||
if (m_swapInterval != wantSwap) {
|
||||
swapInterval(wantSwap);
|
||||
}
|
||||
dequeue();
|
||||
bool forceRedraw = true;
|
||||
if (!m_delayTimer.isValid()) {
|
||||
m_delayTimer.start();
|
||||
} else {
|
||||
if (sync->audioWait || sync->videoFrameWait) {
|
||||
if (wantSwap) {
|
||||
while (m_delayTimer.nsecsElapsed() + OVERHEAD_NSEC < 1000000000 / sync->fpsTarget) {
|
||||
QThread::usleep(500);
|
||||
}
|
||||
|
@ -877,6 +866,11 @@ void PainterGL::doStop() {
|
|||
}
|
||||
m_backend->clear(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() {
|
||||
|
@ -904,33 +898,22 @@ void PainterGL::performDraw() {
|
|||
}
|
||||
|
||||
void PainterGL::enqueue(const uint32_t* backing) {
|
||||
if (!backing) {
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&m_mutex);
|
||||
uint32_t* buffer = nullptr;
|
||||
if (m_free.isEmpty()) {
|
||||
buffer = m_queue.dequeue();
|
||||
} else {
|
||||
buffer = m_free.takeLast();
|
||||
}
|
||||
if (buffer) {
|
||||
QSize size = m_context->screenDimensions();
|
||||
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
|
||||
if (backing) {
|
||||
if (m_free.isEmpty()) {
|
||||
buffer = m_queue.dequeue();
|
||||
} else {
|
||||
buffer = m_free.takeLast();
|
||||
}
|
||||
if (buffer) {
|
||||
QSize size = m_context->screenDimensions();
|
||||
memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL);
|
||||
}
|
||||
}
|
||||
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() {
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (!m_queue.isEmpty()) {
|
||||
|
@ -940,19 +923,6 @@ void PainterGL::dequeue() {
|
|||
}
|
||||
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) {
|
||||
|
@ -973,19 +943,6 @@ void PainterGL::dequeueAll(bool keep) {
|
|||
m_free.append(m_buffer);
|
||||
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) {
|
||||
|
@ -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) {
|
||||
if (!m_started) {
|
||||
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
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "CoreController.h"
|
||||
#include "VideoProxy.h"
|
||||
|
||||
#include "platform/video-backend.h"
|
||||
#include <mgba/feature/video-backend.h>
|
||||
|
||||
class QOpenGLPaintDevice;
|
||||
class QOpenGLWidget;
|
||||
|
@ -109,6 +109,7 @@ public slots:
|
|||
void showOSDMessages(bool enable) override;
|
||||
void showFrameCounter(bool enable) override;
|
||||
void filter(bool filter) override;
|
||||
void swapInterval(int interval) override;
|
||||
void framePosted() override;
|
||||
void setShaders(struct VDir*) override;
|
||||
void clearShaders() override;
|
||||
|
@ -121,8 +122,6 @@ protected:
|
|||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
|
||||
private slots:
|
||||
void startThread(int);
|
||||
void setupProxyThread();
|
||||
void updateContentSize();
|
||||
|
||||
private:
|
||||
|
@ -133,14 +132,10 @@ private:
|
|||
|
||||
bool m_isDrawing = false;
|
||||
bool m_hasStarted = false;
|
||||
int m_threadStartPending = 0;
|
||||
std::unique_ptr<PainterGL> m_painter;
|
||||
QThread m_drawThread;
|
||||
QThread m_proxyThread;
|
||||
std::shared_ptr<CoreController> m_context;
|
||||
mGLWidget* m_gl;
|
||||
QOffscreenSurface m_proxySurface;
|
||||
std::unique_ptr<QOpenGLContext> m_proxyContext;
|
||||
QSize m_cachedContentSize;
|
||||
};
|
||||
|
||||
|
@ -155,7 +150,6 @@ public:
|
|||
void setContext(std::shared_ptr<CoreController>);
|
||||
void setMessagePainter(MessagePainter*);
|
||||
void enqueue(const uint32_t* backing);
|
||||
void enqueue(GLuint tex);
|
||||
|
||||
void stop();
|
||||
|
||||
|
@ -167,9 +161,6 @@ public:
|
|||
void setVideoProxy(std::shared_ptr<VideoProxy>);
|
||||
void interrupt();
|
||||
|
||||
// Run on main thread
|
||||
void swapTex();
|
||||
|
||||
public slots:
|
||||
void create();
|
||||
void destroy();
|
||||
|
@ -186,8 +177,8 @@ public slots:
|
|||
void showOSD(bool enable);
|
||||
void showFrameCounter(bool enable);
|
||||
void filter(bool filter);
|
||||
void swapInterval(int interval);
|
||||
void resizeContext();
|
||||
void updateFramebufferHandle();
|
||||
void setBackgroundImage(const QImage&);
|
||||
|
||||
void setShaders(struct VDir*);
|
||||
|
@ -215,13 +206,6 @@ private:
|
|||
QQueue<uint32_t*> m_queue;
|
||||
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;
|
||||
QMutex m_mutex;
|
||||
QWindow* m_window;
|
||||
|
@ -248,6 +232,7 @@ private:
|
|||
MessagePainter* m_messagePainter = nullptr;
|
||||
QElapsedTimer m_delayTimer;
|
||||
std::shared_ptr<VideoProxy> m_videoProxy;
|
||||
int m_swapInterval = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ public slots:
|
|||
void lockAspectRatio(bool lock) override;
|
||||
void lockIntegerScaling(bool lock) override;
|
||||
void interframeBlending(bool enable) override;
|
||||
void swapInterval(int) override {};
|
||||
void filter(bool filter) override;
|
||||
void framePosted() override;
|
||||
void setShaders(struct VDir*) override {}
|
||||
|
|
|
@ -548,6 +548,11 @@ void FrameView::newVl() {
|
|||
m_vl->deinit(m_vl);
|
||||
}
|
||||
m_vl = mCoreFindVF(m_currentFrame);
|
||||
if (!m_vl) {
|
||||
m_currentFrame->close(m_currentFrame);
|
||||
m_currentFrame = nullptr;
|
||||
return;
|
||||
}
|
||||
m_vl->init(m_vl);
|
||||
m_vl->loadROM(m_vl, m_currentFrame);
|
||||
m_currentFrame = nullptr;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "GBAApp.h"
|
||||
#include "LogController.h"
|
||||
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#endif
|
||||
#include <mgba-util/export.h>
|
||||
#include <mgba-util/image/export.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
using namespace QGBA;
|
||||
|
@ -145,9 +145,9 @@ void PaletteView::exportPalette(int start, int length) {
|
|||
return;
|
||||
}
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <mgba/core/cheats.h>
|
||||
#include <mgba/core/serialize.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 "CoreController.h"
|
||||
|
@ -127,6 +127,7 @@ void ReportView::generateReport() {
|
|||
swReport << QString("Build architecture: %1").arg(QSysInfo::buildCpuArchitecture());
|
||||
swReport << QString("Run architecture: %1").arg(QSysInfo::currentCpuArchitecture());
|
||||
swReport << QString("Qt version: %1").arg(QLatin1String(qVersion()));
|
||||
swReport << QString("Qt QPA platform: %1").arg(QGuiApplication::platformName());
|
||||
#ifdef USE_FFMPEG
|
||||
QStringList libavVers;
|
||||
libavVers << QLatin1String(LIBAVCODEC_IDENT);
|
||||
|
|
|
@ -483,6 +483,7 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("fastForwardMute", m_ui.muteFf);
|
||||
saveSetting("rewindEnable", m_ui.rewind);
|
||||
saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
saveSetting("rewindBufferInterval", m_ui.rewindBufferInterval);
|
||||
saveSetting("resampleVideo", m_ui.resampleVideo);
|
||||
saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
saveSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
|
@ -716,6 +717,7 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("fastForwardMute", m_ui.muteFf, m_ui.mute->isChecked());
|
||||
loadSetting("rewindEnable", m_ui.rewind);
|
||||
loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);
|
||||
loadSetting("rewindBufferInterval", m_ui.rewindBufferInterval);
|
||||
loadSetting("resampleVideo", m_ui.resampleVideo);
|
||||
loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);
|
||||
loadSetting("suspendScreensaver", m_ui.suspendScreensaver);
|
||||
|
|
|
@ -1195,21 +1195,51 @@
|
|||
</item>
|
||||
</layout>
|
||||
</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">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Idle loops:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QComboBox" name="idleOptimization">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
@ -1228,21 +1258,21 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="preload">
|
||||
<property name="text">
|
||||
<string>Preload entire ROM into memory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<item row="10" column="1">
|
||||
<widget class="QCheckBox" name="forceGbp">
|
||||
<property name="text">
|
||||
<string>Enable Game Boy Player features by default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<widget class="QCheckBox" name="vbaBugCompat">
|
||||
<property name="text">
|
||||
<string>Enable VBA bug compatibility in ROM hacks</string>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#include <QSpinBox>
|
||||
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/feature/video-backend.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#include "platform/video-backend.h"
|
||||
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||
|
||||
|
|
|
@ -12,39 +12,40 @@
|
|||
using namespace QGBA;
|
||||
|
||||
VideoProxy::VideoProxy() {
|
||||
mVideoLoggerRendererCreate(&m_logger.d, false);
|
||||
m_logger.d.block = true;
|
||||
m_logger.d.waitOnFlush = true;
|
||||
mVideoLoggerRendererCreate(&m_logger, false);
|
||||
m_logger.p = this;
|
||||
m_logger.block = true;
|
||||
m_logger.waitOnFlush = true;
|
||||
|
||||
m_logger.d.init = &cbind<&VideoProxy::init>;
|
||||
m_logger.d.reset = &cbind<&VideoProxy::reset>;
|
||||
m_logger.d.deinit = &cbind<&VideoProxy::deinit>;
|
||||
m_logger.d.lock = &cbind<&VideoProxy::lock>;
|
||||
m_logger.d.unlock = &cbind<&VideoProxy::unlock>;
|
||||
m_logger.d.wait = &cbind<&VideoProxy::wait>;
|
||||
m_logger.d.wake = &callback<void, int>::func<&VideoProxy::wake>;
|
||||
m_logger.init = &cbind<&VideoProxy::init>;
|
||||
m_logger.reset = &cbind<&VideoProxy::reset>;
|
||||
m_logger.deinit = &cbind<&VideoProxy::deinit>;
|
||||
m_logger.lock = &cbind<&VideoProxy::lock>;
|
||||
m_logger.unlock = &cbind<&VideoProxy::unlock>;
|
||||
m_logger.wait = &cbind<&VideoProxy::wait>;
|
||||
m_logger.wake = &callback<void, int>::func<&VideoProxy::wake>;
|
||||
|
||||
m_logger.d.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.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
|
||||
m_logger.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>;
|
||||
m_logger.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>;
|
||||
m_logger.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
|
||||
|
||||
connect(this, &VideoProxy::dataAvailable, this, &VideoProxy::processData);
|
||||
}
|
||||
|
||||
void VideoProxy::attach(CoreController* controller) {
|
||||
CoreController::Interrupter interrupter(controller);
|
||||
controller->thread()->core->videoLogger = &m_logger.d;
|
||||
controller->thread()->core->videoLogger = &m_logger;
|
||||
}
|
||||
|
||||
void VideoProxy::detach(CoreController* 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;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoProxy::processData() {
|
||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
||||
mVideoLoggerRendererRun(&m_logger, false);
|
||||
m_fromThreadCond.wakeAll();
|
||||
}
|
||||
|
||||
|
@ -67,7 +68,7 @@ bool VideoProxy::writeData(const void* data, size_t length) {
|
|||
while (!RingFIFOWrite(&m_dirtyQueue, data, length)) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// We're on the main thread
|
||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
||||
mVideoLoggerRendererRun(&m_logger, false);
|
||||
} else {
|
||||
emit dataAvailable();
|
||||
m_mutex.lock();
|
||||
|
@ -105,7 +106,7 @@ void VideoProxy::postEvent(enum mVideoLoggerEvent event) {
|
|||
|
||||
void VideoProxy::handleEvent(int event) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -122,7 +123,7 @@ void VideoProxy::wait() {
|
|||
while (RingFIFOSize(&m_dirtyQueue)) {
|
||||
if (QThread::currentThread() == thread()) {
|
||||
// We're on the main thread
|
||||
mVideoLoggerRendererRun(&m_logger.d, false);
|
||||
mVideoLoggerRendererRun(&m_logger, false);
|
||||
} else {
|
||||
emit dataAvailable();
|
||||
m_toThreadCond.wakeAll();
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
void attach(CoreController*);
|
||||
void detach(CoreController*);
|
||||
void setBlocking(bool block) { m_logger.d.waitOnFlush = block; }
|
||||
void setBlocking(bool block) { m_logger.waitOnFlush = block; }
|
||||
|
||||
signals:
|
||||
void dataAvailable();
|
||||
|
@ -51,17 +51,16 @@ private:
|
|||
using type = T (VideoProxy::*)(A...);
|
||||
|
||||
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...);
|
||||
}
|
||||
};
|
||||
|
||||
template<void (VideoProxy::*F)()> static void cbind(mVideoLogger* logger) { callback<void>::func<F>(logger); }
|
||||
|
||||
struct Logger {
|
||||
mVideoLogger d;
|
||||
struct Logger : public mVideoLogger {
|
||||
VideoProxy* p;
|
||||
} m_logger = {{}, this};
|
||||
} m_logger;
|
||||
|
||||
RingFIFO m_dirtyQueue;
|
||||
QMutex m_mutex;
|
||||
|
|
|
@ -1493,6 +1493,20 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
}
|
||||
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) {
|
||||
if (m_controller) {
|
||||
m_controller->setRewinding(held);
|
||||
|
@ -1634,7 +1648,8 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_actions.addSeparator("av");
|
||||
|
||||
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) {
|
||||
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
|
||||
reloadConfig();
|
||||
|
@ -1774,7 +1789,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
void Window::setupOptions() {
|
||||
ConfigOption* videoSync = m_config->addOption("videoSync");
|
||||
videoSync->connect([this](const QVariant&) {
|
||||
videoSync->connect([this](const QVariant& variant) {
|
||||
reloadConfig();
|
||||
}, this);
|
||||
|
||||
|
@ -1828,6 +1843,11 @@ void Window::setupOptions() {
|
|||
reloadConfig();
|
||||
}, this);
|
||||
|
||||
ConfigOption* rewindBufferInterval = m_config->addOption("rewindBufferInterval");
|
||||
rewindBufferInterval->connect([this](const QVariant&) {
|
||||
reloadConfig();
|
||||
}, this);
|
||||
|
||||
ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");
|
||||
allowOpposingDirections->connect([this](const QVariant&) {
|
||||
reloadConfig();
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
#include "scripting/ScriptingTextBuffer.h"
|
||||
#include "scripting/ScriptingTextBufferModel.h"
|
||||
|
||||
#include <mgba/script/input.h>
|
||||
#include <mgba/script/storage.h>
|
||||
#include <mgba/script.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <mgba/core/version.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#endif
|
||||
|
||||
|
@ -66,7 +66,7 @@ bool mSDLGLCommonLoadBackground(struct VideoBackend* context) {
|
|||
goto done;
|
||||
}
|
||||
|
||||
struct Rectangle dims = {
|
||||
struct mRectangle dims = {
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
|
@ -136,13 +136,13 @@ void mSDLGLCommonRunloop(struct mSDLRenderer* renderer, void* user) {
|
|||
if (mSDLGLCommonLoadBackground(v)) {
|
||||
renderer->player.windowUpdated = true;
|
||||
|
||||
struct Rectangle frame;
|
||||
VideoBackendGetFrame(v, &frame);
|
||||
struct mRectangle frame;
|
||||
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &frame);
|
||||
int i;
|
||||
for (i = 0; i <= VIDEO_LAYER_IMAGE; ++i) {
|
||||
struct Rectangle dims;
|
||||
for (i = 0; i < VIDEO_LAYER_IMAGE; ++i) {
|
||||
struct mRectangle dims;
|
||||
v->layerDimensions(v, i, &dims);
|
||||
RectangleCenter(&frame, &dims);
|
||||
mRectangleCenter(&frame, &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);
|
||||
struct Rectangle dims;
|
||||
struct mRectangle dims;
|
||||
v->layerDimensions(v, VIDEO_LAYER_IMAGE, &dims);
|
||||
if (renderer->width != dims.width || renderer->height != dims.height) {
|
||||
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.swap = mSDLGLCommonSwap;
|
||||
renderer->gl.d.init(&renderer->gl.d, 0);
|
||||
struct Rectangle dims = {
|
||||
struct mRectangle dims = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = renderer->width,
|
||||
|
|
|
@ -50,7 +50,7 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
|||
#endif
|
||||
renderer->gl2.d.init(&renderer->gl2.d, 0);
|
||||
|
||||
struct Rectangle dims = {
|
||||
struct mRectangle dims = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = renderer->width,
|
||||
|
|
|
@ -61,6 +61,7 @@ int main(int argc, char** argv) {
|
|||
.useBios = true,
|
||||
.rewindEnable = true,
|
||||
.rewindBufferCapacity = 600,
|
||||
.rewindBufferInterval = 1,
|
||||
.audioBuffers = 1024,
|
||||
.videoSync = false,
|
||||
.audioSync = true,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba-util/gui/font.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/vfs.h>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <mgba/feature/commandline.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/table.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) {
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, image->width, image->height);
|
||||
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data)) {
|
||||
png_infop info = PNGWriteHeader(png, image->width, image->height, mCOLOR_NATIVE);
|
||||
if (!PNGWritePixels(png, image->width, image->height, image->stride, image->data, mCOLOR_NATIVE)) {
|
||||
CIerr(0, "Could not write output image\n");
|
||||
}
|
||||
PNGWriteClose(png, info);
|
||||
|
|
|
@ -2,6 +2,7 @@ include(ExportDirectory)
|
|||
set(SOURCE_FILES
|
||||
context.c
|
||||
input.c
|
||||
image.c
|
||||
socket.c
|
||||
stdlib.c
|
||||
types.c)
|
||||
|
@ -18,6 +19,7 @@ if(USE_LUA)
|
|||
list(APPEND SOURCE_FILES engines/lua.c)
|
||||
list(APPEND TEST_FILES
|
||||
test/context.c
|
||||
test/image.c
|
||||
test/input.c
|
||||
test/lua.c
|
||||
test/stdlib.c)
|
||||
|
|
|
@ -356,6 +356,7 @@ void mScriptEngineExportDocNamespace(struct mScriptEngineContext* ctx, const cha
|
|||
struct mScriptValue* key = mScriptStringCreateFromUTF8(values[i].key);
|
||||
mScriptTableInsert(table, key, values[i].value);
|
||||
mScriptValueDeref(key);
|
||||
mScriptValueDeref(values[i].value);
|
||||
}
|
||||
HashTableInsert(&ctx->docroot, nspace, table);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
#include <mgba/core/scripting.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/internal/script/types.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/input.h>
|
||||
#include <mgba/script/storage.h>
|
||||
#include <mgba/script.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
struct mScriptContext context;
|
||||
|
@ -188,6 +186,9 @@ void explainClass(struct mScriptTypeClass* cls, int level) {
|
|||
}
|
||||
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);
|
||||
break;
|
||||
case mSCRIPT_CLASS_INIT_END:
|
||||
|
@ -469,9 +470,10 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
mScriptContextInit(&context);
|
||||
mScriptContextAttachStdlib(&context);
|
||||
mScriptContextAttachImage(&context);
|
||||
mScriptContextAttachInput(&context);
|
||||
mScriptContextAttachSocket(&context);
|
||||
mScriptContextAttachStorage(&context);
|
||||
mScriptContextAttachInput(&context);
|
||||
mScriptContextSetTextBufferFactory(&context, NULL, NULL);
|
||||
|
||||
initTypes();
|
||||
|
|
|
@ -60,6 +60,7 @@ static int _luaGetList(lua_State* lua);
|
|||
static int _luaLenList(lua_State* lua);
|
||||
|
||||
static int _luaRequireShim(lua_State* lua);
|
||||
static int _luaPrintShim(lua_State* lua);
|
||||
|
||||
static const char* _socketLuaSource =
|
||||
"socket = {\n"
|
||||
|
@ -101,7 +102,7 @@ static const char* _socketLuaSource =
|
|||
" local cbid = self._nextCallback\n"
|
||||
" self._nextCallback = cbid + 1\n"
|
||||
" self._callbacks[event][cbid] = callback\n"
|
||||
" return id\n"
|
||||
" return cbid\n"
|
||||
" end,\n"
|
||||
" remove = function(self, cbid)\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");
|
||||
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);
|
||||
|
||||
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_SENTINEL
|
||||
});
|
||||
mScriptValueDeref(errors);
|
||||
mScriptEngineSetDocstring(&luaContext->d, "socket", "A basic TCP socket library");
|
||||
mScriptEngineSetDocstring(&luaContext->d, "socket.ERRORS",
|
||||
"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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -621,6 +639,14 @@ struct mScriptValue* _luaCoerceTable(struct mScriptEngineContextLua* luaContext,
|
|||
mScriptValueDeref(list);
|
||||
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);
|
||||
return list;
|
||||
}
|
||||
|
@ -885,7 +911,9 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
|||
luaContext->lastError = NULL;
|
||||
}
|
||||
char name[PATH_MAX + 1];
|
||||
char dirname[PATH_MAX] = {0};
|
||||
char dirname[PATH_MAX];
|
||||
name[0] = '\0';
|
||||
dirname[0] = '\0';
|
||||
if (filename) {
|
||||
if (*filename == '*') {
|
||||
snprintf(name, sizeof(name), "=%s", filename + 1);
|
||||
|
@ -900,7 +928,11 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
|||
lastSlash = lastBackslash;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -913,14 +945,43 @@ bool _luaLoad(struct mScriptEngineContext* ctx, const char* filename, struct VFi
|
|||
#endif
|
||||
switch (ret) {
|
||||
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]) {
|
||||
lua_getupvalue(luaContext->lua, -1, 1);
|
||||
lua_pushliteral(luaContext->lua, "require");
|
||||
lua_pushstring(luaContext->lua, dirname);
|
||||
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_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);
|
||||
return true;
|
||||
case LUA_ERRSYNTAX:
|
||||
|
@ -1237,7 +1298,7 @@ int _luaGetTable(lua_State* lua) {
|
|||
}
|
||||
lua_pop(lua, 2);
|
||||
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
obj = mScriptContextAccessWeakref(luaContext->d.context, obj);
|
||||
if (obj->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||
obj = mScriptValueUnwrap(obj);
|
||||
}
|
||||
|
@ -1475,3 +1536,37 @@ static int _luaRequireShim(lua_State* lua) {
|
|||
int newtop = lua_gettop(luaContext->lua);
|
||||
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
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/base.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/input.h>
|
||||
|
@ -189,4 +191,27 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) {
|
|||
mScriptContextSetDocstring(context, "util", "Basic utility library");
|
||||
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");
|
||||
|
||||
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/. */
|
||||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
struct TestA {
|
||||
int32_t i;
|
||||
|
@ -54,6 +52,11 @@ struct TestG {
|
|||
const char* c;
|
||||
};
|
||||
|
||||
struct TestH {
|
||||
int32_t i;
|
||||
int32_t j;
|
||||
};
|
||||
|
||||
static int32_t testAi0(struct TestA* a) {
|
||||
return a->i;
|
||||
}
|
||||
|
@ -198,6 +201,12 @@ mSCRIPT_DEFINE_STRUCT(TestG)
|
|||
mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TestG, setC)
|
||||
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) {
|
||||
struct mScriptTypeClass* cls = mSCRIPT_TYPE_MS_S(TestA)->details.cls;
|
||||
assert_false(cls->init);
|
||||
|
@ -1134,6 +1143,47 @@ M_TEST_DEFINE(testGSet) {
|
|||
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,
|
||||
cmocka_unit_test(testALayout),
|
||||
cmocka_unit_test(testASignatures),
|
||||
|
@ -1150,4 +1200,5 @@ M_TEST_SUITE_DEFINE(mScriptClasses,
|
|||
cmocka_unit_test(testEGet),
|
||||
cmocka_unit_test(testFDeinit),
|
||||
cmocka_unit_test(testGSet),
|
||||
cmocka_unit_test(testHSet),
|
||||
)
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
M_TEST_DEFINE(weakrefBasic) {
|
||||
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 <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/input.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
#include "script/test.h"
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
#include "script/test.h"
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/storage.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
#include "script/test.h"
|
||||
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "util/test/suite.h"
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
struct Test {
|
||||
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(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, 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) {
|
||||
struct mScriptFrame frame;
|
||||
|
@ -172,6 +176,30 @@ M_TEST_DEFINE(addS32) {
|
|||
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) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
|
@ -1316,6 +1344,7 @@ M_TEST_SUITE_DEFINE(mScript,
|
|||
cmocka_unit_test(identityFunctionF32),
|
||||
cmocka_unit_test(identityFunctionStruct),
|
||||
cmocka_unit_test(addS32),
|
||||
cmocka_unit_test(addS32Defaults),
|
||||
cmocka_unit_test(subS32),
|
||||
cmocka_unit_test(wrongArgCountLo),
|
||||
cmocka_unit_test(wrongArgCountHi),
|
||||
|
|
|
@ -364,24 +364,6 @@ static uint32_t _hashString(const struct mScriptValue* val) {
|
|||
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) {
|
||||
if (input->type->base != mSCRIPT_TYPE_WRAPPER) {
|
||||
return false;
|
||||
|
@ -519,6 +501,16 @@ bool _castScalar(const struct mScriptValue* input, const struct mScriptType* typ
|
|||
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) {
|
||||
UNUSED(len);
|
||||
const struct mScriptValue* value = val;
|
||||
|
@ -1480,6 +1472,10 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m->readonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* rawMember = (void *)((uintptr_t) obj->value.opaque + m->offset);
|
||||
if (m->type != val->type) {
|
||||
if (!mScriptCast(m->type, val, val)) {
|
||||
|
@ -1572,7 +1568,7 @@ void mScriptObjectFree(struct mScriptValue* value) {
|
|||
if (value->type->base != mSCRIPT_TYPE_OBJECT) {
|
||||
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);
|
||||
if (value->type->details.cls->free) {
|
||||
struct mScriptValue deinitMember;
|
||||
|
@ -1588,6 +1584,8 @@ void mScriptObjectFree(struct mScriptValue* value) {
|
|||
mScriptFrameDeinit(&frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value->flags & mSCRIPT_VALUE_FLAG_FREE_BUFFER) {
|
||||
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