Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2022-05-29 22:13:05 -07:00
commit f83733518e
32 changed files with 444 additions and 205 deletions

View File

@ -49,6 +49,7 @@ Features:
- Discord Rich Presence now supports time elapsed
- Additional scaling shaders
Emulation fixes:
- GB I/O: Fix incrementing SGB controller when P14 is low (fixes mgba.io/i/2202)
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
- GB Video: Render SGB border when unmasking with ATTR/PAL_SET (fixes mgba.io/i/2261)
- GBA: Improve timing when not booting from BIOS
@ -56,6 +57,7 @@ Emulation fixes:
- GBA Video: Fix backdrop color if DISPCNT is first set to 0 (fixes mgba.io/i/2260)
Other fixes:
- Core: Don't attempt to restore rewind diffs past start of rewind
- FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084)
- GB Video: Fix memory leak when reseting SGB games
- GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB
- Libretro: Fix crash when using Game Boy codes (fixes mgba.io/i/2281)

View File

@ -79,7 +79,7 @@ if(NOT LIBMGBA_ONLY)
set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)")
set(BUILD_GL ON CACHE BOOL "Build with OpenGL")
set(BUILD_GLES2 ON CACHE BOOL "Build with OpenGL|ES 2")
set(BUILD_GLES3 OFF CACHE BOOL "Build with OpenGL|ES 3")
set(BUILD_GLES3 ON CACHE BOOL "Build with OpenGL|ES 3")
set(USE_EPOXY ON CACHE STRING "Build with libepoxy")
set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies")
set(DISTBUILD OFF CACHE BOOL "Build distribution packages")
@ -123,6 +123,10 @@ else()
set(CMAKE_INSTALL_INCLUDEDIR "include")
endif()
if(APPLE AND DISTBUILD)
set(CMAKE_INSTALL_DOCDIR ".")
endif()
if(NOT DEFINED LIBDIR)
set(LIBDIR "${CMAKE_INSTALL_LIBDIR}")
endif()
@ -212,8 +216,7 @@ if(WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
add_definitions(-D_UNICODE -DUNICODE)
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -municode")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
add_definitions(-D_GNU_SOURCE)
endif()
list(APPEND OS_LIB ws2_32 shlwapi)
list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/windows/vfs-w32.c)
@ -331,7 +334,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_function(localtime_r)
# The strtof_l on Linux not actually exposed nor actually strtof_l
set(HAVE_STRTOF_L OFF)
elseif(NOT DEFINED PSP2)
else()
find_function(localtime_r)
find_function(strtof_l)
endif()
@ -398,6 +401,7 @@ if(CMAKE_SYSTEM_NAME MATCHES ".*BSD|DragonFly")
else()
find_feature(USE_EDITLINE "libedit")
endif()
if(BUILD_GL)
find_package(OpenGL QUIET)
if(NOT OPENGL_FOUND)
@ -406,39 +410,49 @@ if(BUILD_GL)
set(OPENGL_LIBRARY OpenGL::GL)
endif()
endif()
if(NOT BUILD_GL AND NOT LIBMGBA_ONLY)
set(OPENGL_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(BUILD_GLES2 AND NOT BUILD_RASPI AND NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|Darwin|Linux|.*BSD|DragonFly|Haiku)$")
find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h)
find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM)
if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY)
set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE)
endif()
endif()
if(NOT BUILD_GLES2 AND NOT LIBMGBA_ONLY)
set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(BUILD_GL)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
list(APPEND FEATURE_DEFINES BUILD_GL)
list(APPEND DEPENDENCY_LIB ${OPENGL_LIBRARY})
include_directories(${OPENGL_INCLUDE_DIR})
endif()
if(NOT BUILD_GL AND NOT LIBMGBA_ONLY)
set(OPENGL_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(BUILD_GLES2 AND NOT BUILD_GL)
find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h)
find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM)
if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY)
set(BUILD_GLES2 OFF CACHE BOOL "OpenGL|ES 2 not found" FORCE)
endif()
endif()
if(BUILD_GLES2)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
list(APPEND FEATURE_DEFINES BUILD_GLES2)
list(APPEND DEPENDENCY_LIB ${OPENGLES2_LIBRARY})
include_directories(${OPENGLES2_INCLUDE_DIR})
endif()
if(BUILD_GLES3)
if(BUILD_GLES3 AND NOT BUILD_GL)
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
list(APPEND FEATURE_DEFINES BUILD_GLES3)
if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY)
set(BUILD_GLES3 OFF CACHE BOOL "OpenGL|ES 3 not found" FORCE)
endif()
endif()
if(BUILD_GLES3)
list(APPEND FEATURE_DEFINES BUILD_GLES3)
list(APPEND DEPENDENCY_LIB ${OPENGLES3_LIBRARY})
include_directories(${OPENGLES3_INCLUDE_DIR})
endif()
if(BUILD_GLES2 OR BUILD_GLES3)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
endif()
if(NOT BUILD_GLES2 AND NOT BUILD_GLES3 AND NOT LIBMGBA_ONLY)
set(OPENGLES2_LIBRARY "" CACHE PATH "" FORCE)
endif()
if(DISABLE_DEPS)
set(USE_GDB_STUB OFF)
@ -692,7 +706,7 @@ endif()
if(USE_EPOXY)
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2)
list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3)
list(APPEND FEATURES EPOXY)
include_directories(AFTER ${EPOXY_INCLUDE_DIRS})
link_directories(${EPOXY_LIBRARY_DIRS})
@ -937,14 +951,16 @@ if(BUILD_OPENEMU)
install(TARGETS ${BINARY_NAME}-openemu LIBRARY DESTINATION ${OE_LIBDIR} COMPONENT ${BINARY_NAME}.oecoreplugin NAMELINK_SKIP)
endif()
if(BUILD_QT AND WIN32)
if(BUILD_QT AND (WIN32 OR APPLE))
set(BUILD_UPDATER ON)
endif()
if(BUILD_UPDATER)
add_executable(updater-stub WIN32 ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c)
target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY} ${BINARY_NAME})
if(NOT MSVC)
if(MSVC)
set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup)
else()
set_target_properties(updater-stub PROPERTIES LINK_FLAGS_RELEASE -s)
set_target_properties(updater-stub PROPERTIES LINK_FLAGS_RELWITHDEBINFO -s)
endif()
@ -1091,6 +1107,9 @@ endif()
if(DISTBUILD)
set(CPACK_ARCHIVE_COMPONENT_INSTALL ON)
set(CPACK_DMG_FILESYSTEM "HFS+")
set(CPACK_DMG_FORMAT "UDBZ")
set(CPACK_DMG_VOLUME_NAME "${PROJECT_NAME} ${VERSION_STRING}")
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND BUILD_SHARED)
if(NOT APPLE)
add_custom_command(TARGET ${BINARY_NAME} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$<TARGET_FILE:${BINARY_NAME}>" "$<TARGET_FILE:${BINARY_NAME}>.debug")
@ -1101,7 +1120,8 @@ if(DISTBUILD)
endif()
if(APPLE)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/cmake/DMGOverrides.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake @ONLY)
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/DMGOverrides.cmake)
elseif(WIN32)
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-qt ${BINARY_NAME}-sdl ${BINARY_NAME}-qt-dbg ${BINARY_NAME}-sdl-dbg ${BINARY_NAME}-perf installer)
elseif(3DS)

View File

@ -253,9 +253,9 @@ typedef intptr_t ssize_t;
#define ATTRIBUTE_NOINLINE
// Adapted from https://stackoverflow.com/a/2390626
#define _CONSTRUCTOR(FN, PRE) \
static void FN(void); \
__declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \
static void FN(void)
static void FN(void); \
__declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \
static void FN(void)
#ifdef _WIN64
#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "")
#else

View File

@ -59,6 +59,31 @@ enum GUIIcon {
GUI_ICON_UP,
GUI_ICON_RIGHT,
GUI_ICON_DOWN,
GUI_ICON_9SLICE_EMPTY_NW,
GUI_ICON_9SLICE_EMPTY_N,
GUI_ICON_9SLICE_EMPTY_NE,
GUI_ICON_9SLICE_EMPTY_W,
GUI_ICON_9SLICE_EMPTY_E,
GUI_ICON_9SLICE_EMPTY_SW,
GUI_ICON_9SLICE_EMPTY_S,
GUI_ICON_9SLICE_EMPTY_SE,
GUI_ICON_9SLICE_FILLED_NW,
GUI_ICON_9SLICE_FILLED_N,
GUI_ICON_9SLICE_FILLED_NE,
GUI_ICON_9SLICE_FILLED_W,
GUI_ICON_9SLICE_FILLED_C,
GUI_ICON_9SLICE_FILLED_E,
GUI_ICON_9SLICE_FILLED_SW,
GUI_ICON_9SLICE_FILLED_S,
GUI_ICON_9SLICE_FILLED_SE,
GUI_ICON_9SLICE_CAP_NNW,
GUI_ICON_9SLICE_CAP_NWW,
GUI_ICON_9SLICE_CAP_NNE,
GUI_ICON_9SLICE_CAP_NEE,
GUI_ICON_9SLICE_CAP_SSW,
GUI_ICON_9SLICE_CAP_SWW,
GUI_ICON_9SLICE_CAP_SSE,
GUI_ICON_9SLICE_CAP_SEE,
GUI_ICON_MAX,
};
@ -80,6 +105,12 @@ struct GUIIconMetric {
int height;
};
enum GUI9SliceStyle {
GUI_9SLICE_FILLED,
GUI_9SLICE_EMPTY,
GUI_9SLICE_EMPTY_CAPPED,
};
unsigned GUIFontHeight(const struct GUIFont*);
unsigned GUIFontGlyphWidth(const struct GUIFont*, uint32_t glyph);
unsigned GUIFontSpanWidth(const struct GUIFont*, const char* text);
@ -96,6 +127,8 @@ void GUIFontDrawIconSize(struct GUIFont* font, int x, int y, int w, int h, uint3
void GUIFontDrawSubmit(struct GUIFont* font);
#endif
void GUIFontDraw9Slice(struct GUIFont*, int x, int y, int width, int height, uint32_t color, enum GUI9SliceStyle style);
CXX_GUARD_END
#endif

View File

@ -17,16 +17,16 @@ struct StringList;
struct Table;
struct mUpdaterContext {
struct Configuration manifest;
struct Configuration manifest;
};
struct mUpdate {
const char* path;
size_t size;
int rev;
const char* version;
const char* commit;
const char* sha256;
const char* path;
size_t size;
int rev;
const char* version;
const char* commit;
const char* sha256;
};
bool mUpdaterInit(struct mUpdaterContext*, const char* manifest);

View File

@ -16,7 +16,7 @@ CXX_GUARD_START
#include <mgba/internal/gba/renderers/common.h>
#include <mgba/internal/gba/video.h>
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
#ifdef USE_EPOXY
#include <epoxy/gl.h>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 B

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -662,7 +662,7 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
break;
case 'X':
_writeMemoryBinary(stub, message);
break;
break;
case 'Z':
_setBreakpoint(stub, message);
break;

View File

@ -81,20 +81,12 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) {
decoder->videoStream = i;
decoder->width = -1;
decoder->height = -1;
#if LIBAVCODEC_VERSION_MAJOR >= 55
decoder->videoFrame = av_frame_alloc();
#else
decoder->videoFrame = avcodec_alloc_frame();
#endif
}
if (type == AVMEDIA_TYPE_AUDIO) {
decoder->audioStream = i;
#if LIBAVCODEC_VERSION_MAJOR >= 55
decoder->audioFrame = av_frame_alloc();
#else
decoder->audioFrame = avcodec_alloc_frame();
#endif
}
}
return true;
@ -102,11 +94,7 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) {
void FFmpegDecoderClose(struct FFmpegDecoder* decoder) {
if (decoder->audioFrame) {
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&decoder->audioFrame);
#else
avcodec_free_frame(&decoder->audioFrame);
#endif
}
if (decoder->audio) {
@ -124,11 +112,7 @@ void FFmpegDecoderClose(struct FFmpegDecoder* decoder) {
}
if (decoder->videoFrame) {
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&decoder->videoFrame);
#else
avcodec_free_frame(&decoder->videoFrame);
#endif
}
if (decoder->pixels) {
@ -216,4 +200,4 @@ bool FFmpegDecoderRead(struct FFmpegDecoder* decoder) {
#endif
}
return readPacket;
}
}

View File

@ -90,7 +90,6 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
encoder->audioStream = NULL;
encoder->audioFrame = NULL;
encoder->audioBuffer = NULL;
encoder->postaudioBuffer = NULL;
encoder->video = NULL;
encoder->videoStream = NULL;
encoder->videoFrame = NULL;
@ -315,17 +314,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
FFmpegEncoderClose(encoder);
return false;
}
#if LIBAVCODEC_VERSION_MAJOR >= 55
encoder->audioFrame = av_frame_alloc();
#else
encoder->audioFrame = avcodec_alloc_frame();
#endif
if (!encoder->audio->frame_size) {
encoder->audio->frame_size = 1;
}
encoder->audioFrame->nb_samples = encoder->audio->frame_size;
encoder->audioFrame->format = encoder->audio->sample_fmt;
encoder->audioFrame->pts = 0;
encoder->audioFrame->channel_layout = AV_CH_LAYOUT_STEREO;
#ifdef USE_LIBAVRESAMPLE
encoder->resampleContext = avresample_alloc_context();
av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
@ -342,14 +338,12 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
#endif
encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4;
encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0);
encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize);
avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0);
av_frame_get_buffer(encoder->audioFrame, 0);
if (encoder->audio->codec->id == AV_CODEC_ID_AAC &&
(strcasecmp(encoder->containerFormat, "mp4") ||
strcasecmp(encoder->containerFormat, "m4v") ||
strcasecmp(encoder->containerFormat, "mov"))) {
(strcasecmp(encoder->containerFormat, "mp4") == 0||
strcasecmp(encoder->containerFormat, "m4v") == 0 ||
strcasecmp(encoder->containerFormat, "mov") == 0)) {
// MP4 container doesn't support the raw ADTS AAC format that the encoder spits out
#ifdef FFMPEG_USE_NEW_BSF
av_bsf_alloc(av_bsf_get_by_name("aac_adtstoasc"), &encoder->absf);
@ -391,9 +385,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
}
if (encoder->video->codec->id == AV_CODEC_ID_H264 &&
(strcasecmp(encoder->containerFormat, "mp4") ||
strcasecmp(encoder->containerFormat, "m4v") ||
strcasecmp(encoder->containerFormat, "mov"))) {
(strcasecmp(encoder->containerFormat, "mp4") == 0 ||
strcasecmp(encoder->containerFormat, "m4v") == 0 ||
strcasecmp(encoder->containerFormat, "mov") == 0)) {
// QuickTime and a few other things require YUV420
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
}
@ -497,11 +491,7 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
return false;
}
#if LIBAVCODEC_VERSION_MAJOR >= 55
encoder->sinkFrame = av_frame_alloc();
#else
encoder->sinkFrame = avcodec_alloc_frame();
#endif
}
AVDictionary* opts = 0;
av_dict_set(&opts, "strict", "-2", 0);
@ -511,17 +501,13 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
FFmpegEncoderClose(encoder);
return false;
}
#if LIBAVCODEC_VERSION_MAJOR >= 55
encoder->videoFrame = av_frame_alloc();
#else
encoder->videoFrame = avcodec_alloc_frame();
#endif
encoder->videoFrame->format = encoder->video->pix_fmt != AV_PIX_FMT_PAL8 ? encoder->video->pix_fmt : encoder->ipixFormat;
encoder->videoFrame->width = encoder->video->width;
encoder->videoFrame->height = encoder->video->height;
encoder->videoFrame->pts = 0;
_ffmpegSetVideoDimensions(&encoder->d, encoder->iwidth, encoder->iheight);
av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->videoFrame->width, encoder->videoFrame->height, encoder->videoFrame->format, 32);
av_frame_get_buffer(encoder->videoFrame, 32);
#ifdef FFMPEG_USE_CODECPAR
avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
#endif
@ -579,21 +565,13 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
avio_close(encoder->context->pb);
}
if (encoder->postaudioBuffer) {
av_free(encoder->postaudioBuffer);
encoder->postaudioBuffer = NULL;
}
if (encoder->audioBuffer) {
av_free(encoder->audioBuffer);
encoder->audioBuffer = NULL;
}
if (encoder->audioFrame) {
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&encoder->audioFrame);
#else
avcodec_free_frame(&encoder->audioFrame);
#endif
}
if (encoder->audio) {
#ifdef FFMPEG_USE_CODECPAR
@ -623,20 +601,11 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
}
if (encoder->videoFrame) {
av_freep(encoder->videoFrame->data);
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&encoder->videoFrame);
#else
avcodec_free_frame(&encoder->videoFrame);
#endif
}
if (encoder->sinkFrame) {
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&encoder->sinkFrame);
#else
avcodec_free_frame(&encoder->sinkFrame);
#endif
encoder->sinkFrame = NULL;
}
@ -696,7 +665,6 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
return;
}
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
encoder->currentAudioSample = 0;
#ifdef USE_LIBAVRESAMPLE
avresample_convert(encoder->resampleContext, 0, 0, 0,
@ -705,19 +673,15 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
return;
}
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->audioFrame);
#endif
int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples);
#else
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->audioFrame);
#endif
if (swr_get_out_samples(encoder->resampleContext, 1) < encoder->audioFrame->nb_samples) {
swr_convert(encoder->resampleContext, NULL, 0, (const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4);
return;
}
int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize,
int samples = swr_convert(encoder->resampleContext, encoder->audioFrame->data, encoder->audioFrame->nb_samples,
(const uint8_t**) &encoder->audioBuffer, encoder->audioBufferSize / 4);
#endif
@ -728,61 +692,76 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
}
bool _ffmpegWriteAudioFrame(struct FFmpegEncoder* encoder, struct AVFrame* audioFrame) {
AVPacket packet;
av_init_packet(&packet);
packet.data = 0;
packet.size = 0;
AVPacket* packet;
#ifdef FFMPEG_USE_PACKET_UNREF
packet = av_packet_alloc();
#else
packet = av_malloc(sizeof(*packet));
av_init_packet(packet);
#endif
packet->data = 0;
packet->size = 0;
int gotData;
#ifdef FFMPEG_USE_PACKETS
avcodec_send_frame(encoder->audio, audioFrame);
gotData = avcodec_receive_packet(encoder->audio, &packet);
gotData = (gotData == 0) && packet.size;
gotData = avcodec_receive_packet(encoder->audio, packet);
gotData = (gotData == 0) && packet->size;
#else
avcodec_encode_audio2(encoder->audio, &packet, audioFrame, &gotData);
avcodec_encode_audio2(encoder->audio, packet, audioFrame, &gotData);
#endif
packet.pts = av_rescale_q(packet.pts, encoder->audio->time_base, encoder->audioStream->time_base);
packet.dts = packet.pts;
packet->pts = av_rescale_q(packet->pts, encoder->audio->time_base, encoder->audioStream->time_base);
packet->dts = packet->pts;
if (gotData) {
if (encoder->absf) {
AVPacket tempPacket;
AVPacket* tempPacket;
#ifdef FFMPEG_USE_PACKETS
tempPacket = av_packet_alloc();
#else
tempPacket = av_malloc(sizeof(*tempPacket));
av_init_packet(tempPacket);
#endif
#ifdef FFMPEG_USE_NEW_BSF
int success = av_bsf_send_packet(encoder->absf, &packet);
int success = av_bsf_send_packet(encoder->absf, packet);
if (success >= 0) {
success = av_bsf_receive_packet(encoder->absf, &tempPacket);
success = av_bsf_receive_packet(encoder->absf, tempPacket);
}
#else
int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0,
&tempPacket.data, &tempPacket.size,
packet.data, packet.size, 0);
&tempPacket->data, &tempPacket->size,
packet->data, packet->size, 0);
#endif
if (success >= 0) {
#if LIBAVUTIL_VERSION_MAJOR >= 53
tempPacket.buf = av_buffer_create(tempPacket.data, tempPacket.size, av_buffer_default_free, 0, 0);
tempPacket->buf = av_buffer_create(tempPacket->data, tempPacket->size, av_buffer_default_free, 0, 0);
#endif
#ifdef FFMPEG_USE_PACKET_UNREF
av_packet_move_ref(&packet, &tempPacket);
av_packet_move_ref(packet, tempPacket);
av_packet_free(&packet);
#else
av_free_packet(&packet);
av_free_packet(packet);
av_freep(&packet);
packet = tempPacket;
#endif
packet.stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, &packet);
packet->stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, packet);
}
} else {
packet.stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, &packet);
packet->stream_index = encoder->audioStream->index;
av_interleaved_write_frame(encoder->context, packet);
}
}
#ifdef FFMPEG_USE_PACKET_UNREF
av_packet_unref(&packet);
av_packet_unref(packet);
av_packet_free(&packet);
#else
av_free_packet(&packet);
av_free_packet(packet);
av_freep(&packet);
#endif
return gotData;
}
@ -798,9 +777,7 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size
}
stride *= BYTES_PER_PIXEL;
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->videoFrame);
#endif
if (encoder->video->codec->id == AV_CODEC_ID_WEBP) {
// TODO: Figure out why WebP is rescaling internally (should video frames not be rescaled externally?)
encoder->videoFrame->pts = encoder->currentVideoFrame;
@ -829,32 +806,39 @@ void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size
}
bool _ffmpegWriteVideoFrame(struct FFmpegEncoder* encoder, struct AVFrame* videoFrame) {
AVPacket packet;
AVPacket* packet;
av_init_packet(&packet);
packet.data = 0;
packet.size = 0;
#ifdef FFMPEG_USE_PACKET_UNREF
packet = av_packet_alloc();
#else
packet = av_malloc(sizeof(*packet));
av_init_packet(packet);
#endif
packet->data = 0;
packet->size = 0;
int gotData;
#ifdef FFMPEG_USE_PACKETS
avcodec_send_frame(encoder->video, videoFrame);
gotData = avcodec_receive_packet(encoder->video, &packet) == 0;
gotData = avcodec_receive_packet(encoder->video, packet) == 0;
#else
avcodec_encode_video2(encoder->video, &packet, videoFrame, &gotData);
avcodec_encode_video2(encoder->video, packet, videoFrame, &gotData);
#endif
if (gotData) {
#ifndef FFMPEG_USE_PACKET_UNREF
if (encoder->video->coded_frame->key_frame) {
packet.flags |= AV_PKT_FLAG_KEY;
packet->flags |= AV_PKT_FLAG_KEY;
}
#endif
packet.stream_index = encoder->videoStream->index;
av_interleaved_write_frame(encoder->context, &packet);
packet->stream_index = encoder->videoStream->index;
av_interleaved_write_frame(encoder->context, packet);
}
#ifdef FFMPEG_USE_PACKET_UNREF
av_packet_unref(&packet);
av_packet_unref(packet);
av_packet_free(&packet);
#else
av_free_packet(&packet);
av_free_packet(packet);
av_freep(&packet);
#endif
return gotData;

View File

@ -33,8 +33,6 @@ struct FFmpegEncoder {
int sampleRate;
uint16_t* audioBuffer;
size_t audioBufferSize;
uint16_t* postaudioBuffer;
size_t postaudioBufferSize;
AVFrame* audioFrame;
size_t currentAudioSample;
int64_t currentAudioFrame;

View File

@ -4,7 +4,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/config.h>
#include <mgba/core/version.h>
#include <mgba/feature/updater.h>
#include <mgba-util/string.h>
#include <mgba-util/vfs.h>
#include <errno.h>
@ -16,13 +18,18 @@
#include <direct.h>
#include <io.h>
#include <process.h>
#include <synchapi.h>
#define mkdir(X, Y) _mkdir(X)
#elif defined(_POSIX_C_SOURCE)
#include <unistd.h>
#endif
bool extractArchive(struct VDir* archive, const char* root) {
#ifndef W_OK
#define W_OK 02
#endif
bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
char path[PATH_MAX] = {0};
struct VDirEntry* vde;
uint8_t block[8192];
@ -30,17 +37,37 @@ bool extractArchive(struct VDir* archive, const char* root) {
while ((vde = archive->listNext(archive))) {
struct VFile* vfIn;
struct VFile* vfOut;
const char* fname = strchr(vde->name(vde), '/');
if (!fname) {
const char* fname;
if (prefix) {
fname = strchr(vde->name(vde), '/');
if (!fname) {
continue;
}
snprintf(path, sizeof(path), "%s/%s", root, &fname[1]);
} else {
fname = vde->name(vde);
snprintf(path, sizeof(path), "%s/%s", root, fname);
}
if (fname[0] == '.') {
continue;
}
snprintf(path, sizeof(path), "%s/%s", root, &fname[1]);
switch (vde->type(vde)) {
case VFS_DIRECTORY:
printf("mkdir %s\n", fname);
if (mkdir(path, 0755) < 0 && errno != EEXIST) {
return false;
}
if (!prefix) {
struct VDir* subdir = archive->openDir(archive, fname);
if (!subdir) {
return false;
}
if (!extractArchive(subdir, path, false)) {
subdir->close(subdir);
return false;
}
subdir->close(subdir);
}
break;
case VFS_FILE:
printf("extract %s\n", fname);
@ -48,7 +75,11 @@ bool extractArchive(struct VDir* archive, const char* root) {
errno = 0;
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
if (!vfOut && errno == EACCES) {
#ifdef _WIN32
Sleep(1000);
#else
sleep(1);
#endif
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
}
if (!vfOut) {
@ -76,6 +107,7 @@ int main(int argc, char* argv[]) {
UNUSED(argv);
struct mCoreConfig config;
char updateArchive[PATH_MAX] = {0};
char bin[PATH_MAX] = {0};
const char* root;
int ok = 1;
@ -87,14 +119,71 @@ int main(int argc, char* argv[]) {
} else if (access(root, W_OK)) {
puts("Cannot write to update path");
} else {
#ifdef __APPLE__
char subdir[PATH_MAX];
char devpath[PATH_MAX] = {0};
bool needsUnmount = false;
#endif
bool isPortable = mCoreConfigIsPortable();
struct VDir* archive = VDirOpenArchive(updateArchive);
const char* extension = mUpdateGetArchiveExtension(&config);
struct VDir* archive = NULL;
bool prefix = true;
if (strcmp(extension, "dmg") == 0) {
#ifdef __APPLE__
char mountpoint[PATH_MAX];
// Make a slightly random directory name for the updater mountpoint
struct timeval t;
gettimeofday(&t, NULL);
int printed = snprintf(mountpoint, sizeof(mountpoint), "/Volumes/%s Updater %04X", projectName, (t.tv_usec >> 2) & 0xFFFF);
// Fork hdiutil to mount it
char* args[] = {"hdiutil", "attach", "-nobrowse", "-mountpoint", mountpoint, updateArchive, NULL};
int fds[2];
pipe(fds);
pid_t pid = fork();
if (pid == 0) {
dup2(fds[1], STDOUT_FILENO);
execvp("hdiutil", args);
_exit(1);
} else {
// Parse out the disk ID so we can detach it when we're done
char buffer[1024] = {0};
ssize_t size;
while ((size = read(fds[0], buffer, sizeof(buffer) - 1)) > 0) { // Leave the last byte null
char* devinfo = strnstr(buffer, "\n/dev/disk", size);
if (!devinfo) {
continue;
}
char* devend = strpbrk(&devinfo[9], "s \t");
if (!devend) {
continue;
}
off_t diff = devend - devinfo - 1;
memcpy(devpath, &devinfo[1], diff);
puts(devpath);
break;
}
int retstat;
wait4(pid, &retstat, 0, NULL);
}
snprintf(&mountpoint[printed], sizeof(mountpoint) - printed, "/%s.app", projectName);
snprintf(subdir, sizeof(subdir), "%s/%s.app", root, projectName);
root = subdir;
archive = VDirOpen(mountpoint);
prefix = false;
needsUnmount = true;
#endif
} else {
archive = VDirOpenArchive(updateArchive);
}
if (!archive) {
puts("Cannot open update archive");
} else {
puts("Extracting update");
if (extractArchive(archive, root)) {
if (extractArchive(archive, root, prefix)) {
puts("Complete");
const char* command = mUpdateGetCommand(&config);
strlcpy(bin, command, sizeof(bin));
ok = 0;
mUpdateDeregister(&config);
} else {
@ -103,19 +192,31 @@ int main(int argc, char* argv[]) {
archive->close(archive);
unlink(updateArchive);
}
#ifdef __APPLE__
if (needsUnmount) {
char* args[] = {"hdiutil", "detach", devpath, NULL};
pid_t pid = vfork();
if (pid == 0) {
execvp("hdiutil", args);
_exit(0);
} else {
int retstat;
wait4(pid, &retstat, 0, NULL);
}
}
#endif
if (!isPortable) {
char portableIni[PATH_MAX] = {0};
snprintf(portableIni, sizeof(portableIni), "%s/portable.ini", root);
unlink(portableIni);
}
}
const char* bin = mUpdateGetCommand(&config);
mCoreConfigDeinit(&config);
if (ok == 0) {
const char* argv[] = { bin, NULL };
#ifdef _WIN32
_execv(bin, argv);
#elif defined(_POSIX_C_SOURCE)
#elif defined(_POSIX_C_SOURCE) || defined(__APPLE__)
execv(bin, argv);
#endif
}

View File

@ -185,6 +185,16 @@ void mUpdateRegister(struct mCoreConfig* config, const char* arg0, const char* u
#endif
if (last) {
last[0] = '\0';
#ifdef __APPLE__
ssize_t len = strlen(filename);
if (len > 19 && strcmp(&filename[len - 19], ".app/Contents/MacOS") == 0) {
filename[len - 19] = '\0';
last = strrchr(filename, '/');
if (last) {
last[0] = '\0';
}
}
#endif
}
ConfigurationSetValue(cfg, UPDATE_SECTION, "bin", arg0);
ConfigurationSetValue(cfg, UPDATE_SECTION, "root", filename);

View File

@ -119,19 +119,15 @@ static void _writeSGBBits(struct GB* gb, int bits) {
if (bits == gb->currentSgbBits) {
return;
}
switch (bits) {
case 0:
case 1:
if (gb->currentSgbBits & 2) {
gb->sgbIncrement = !gb->sgbIncrement;
}
break;
case 3:
if (bits & 2) {
if (gb->sgbIncrement) {
gb->sgbIncrement = false;
gb->sgbCurrentController = (gb->sgbCurrentController + 1) & gb->sgbControllers;
}
break;
} else {
if (gb->currentSgbBits & 2) {
gb->sgbIncrement = !gb->sgbIncrement;
}
}
gb->currentSgbBits = bits;
if (gb->sgbBit == 128 && bits == 2) {

View File

@ -18,7 +18,7 @@
#ifndef DISABLE_THREADING
#include <mgba/feature/thread-proxy.h>
#endif
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
#include <mgba/internal/gba/renderers/gl.h>
#endif
#include <mgba/internal/gba/renderers/proxy.h>
@ -138,7 +138,7 @@ struct GBACore {
struct mCore d;
struct GBAVideoRenderer dummyRenderer;
struct GBAVideoSoftwareRenderer renderer;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
struct GBAVideoGLRenderer glRenderer;
#endif
#ifndef MINIMAL_CORE
@ -196,7 +196,7 @@ static bool _GBACoreInit(struct mCore* core) {
GBAVideoSoftwareRendererCreate(&gbacore->renderer);
gbacore->renderer.outputBuffer = NULL;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
GBAVideoGLRendererCreate(&gbacore->glRenderer);
gbacore->glRenderer.outputTex = -1;
#endif
@ -256,7 +256,7 @@ static bool _GBACoreSupportsFeature(const struct mCore* core, enum mCoreFeature
UNUSED(core);
switch (feature) {
case mCORE_FEATURE_OPENGL:
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
return true;
#else
return false;
@ -370,7 +370,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
}
struct GBACore* gbacore = (struct GBACore*) core;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
if (strcmp("videoScale", option) == 0) {
if (config != &core->config) {
mCoreConfigCopyValue(&core->config, config, "videoScale");
@ -388,7 +388,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
if (gbacore->renderer.outputBuffer) {
renderer = &gbacore->renderer.d;
}
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
renderer = &gbacore->glRenderer.d;
@ -410,7 +410,7 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
}
static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
const struct GBACore* gbacore = (const struct GBACore*) core;
int scale = gbacore->glRenderer.scale;
#else
@ -430,7 +430,7 @@ static void _GBACoreSetVideoBuffer(struct mCore* core, color_t* buffer, size_t s
}
static void _GBACoreSetVideoGLTex(struct mCore* core, unsigned texid) {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->glRenderer.outputTex = texid;
#else
@ -573,7 +573,7 @@ static void _GBACoreReset(struct mCore* core) {
struct GBA* gba = (struct GBA*) core->board;
int fakeBool;
if (gbacore->renderer.outputBuffer
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
|| gbacore->glRenderer.outputTex != (unsigned) -1
#endif
) {
@ -581,7 +581,7 @@ static void _GBACoreReset(struct mCore* core) {
if (gbacore->renderer.outputBuffer) {
renderer = &gbacore->renderer.d;
}
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
renderer = &gbacore->glRenderer.d;

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/renderers/gl.h>
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#ifdef BUILD_GLES3
#include <mgba/core/cache-set.h>
#include <mgba/internal/arm/macros.h>

View File

@ -0,0 +1,6 @@
message(FATAL ${CPACK_GENERATOR})
if(CPACK_GENERATOR STREQUAL "DragNDrop")
set(CPACK_COMPONENTS_ALL @BINARY_NAME@ @BINARY_NAME@-qt @BINARY_NAME@-qt-dbg)
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
unset(CPACK_RESOURCE_FILE_LICENSE)
endif()

View File

@ -159,6 +159,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
mGLES2ShaderInit(&context->finalShader, 0, 0, 0, 0, false, 0, 0);
mGLES2ShaderInit(&context->interframeShader, 0, _interframeFragmentShader, -1, -1, false, 0, 0);
#ifdef BUILD_GLES3
if (context->initialShader.vao != (GLuint) -1) {
glBindVertexArray(context->initialShader.vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
@ -168,6 +169,7 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glBindVertexArray(0);
}
#endif
glDeleteFramebuffers(1, &context->finalShader.fbo);
glDeleteTextures(1, &context->finalShader.tex);
@ -297,9 +299,12 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
glUseProgram(shader->program);
glUniform1i(shader->texLocation, 0);
glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH);
#ifdef BUILD_GLES3
if (shader->vao != (GLuint) -1) {
glBindVertexArray(shader->vao);
} else {
} else
#endif
{
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glEnableVertexAttribArray(shader->positionLocation);
glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
@ -391,9 +396,11 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0);
#ifdef BUILD_GLES3
if (context->finalShader.vao != (GLuint) -1) {
glBindVertexArray(0);
}
#endif
}
void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
@ -508,6 +515,7 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
shader->uniforms[i].location = glGetUniformLocation(shader->program, shader->uniforms[i].name);
}
#ifdef BUILD_GLES3
const GLubyte* extensions = glGetString(GL_EXTENSIONS);
if (shaderBuffer[0] == _gles2Header || version[0] >= '3' || (extensions && strstr((const char*) extensions, "_vertex_array_object") != NULL)) {
glGenVertexArrays(1, &shader->vao);
@ -515,7 +523,9 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
glEnableVertexAttribArray(shader->positionLocation);
glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(0);
} else {
} else
#endif
{
shader->vao = -1;
}
@ -527,9 +537,11 @@ void mGLES2ShaderDeinit(struct mGLES2Shader* shader) {
glDeleteShader(shader->fragmentShader);
glDeleteProgram(shader->program);
glDeleteFramebuffers(1, &shader->fbo);
#ifdef BUILD_GLES3
if (shader->vao != (GLuint) -1) {
glDeleteVertexArrays(1, &shader->vao);
}
#endif
}
void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shaders, size_t nShaders) {
@ -547,16 +559,20 @@ void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shad
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
#ifdef BUILD_GLES3
if (context->shaders[i].vao != (GLuint) -1) {
glBindVertexArray(context->shaders[i].vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glEnableVertexAttribArray(context->shaders[i].positionLocation);
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
}
#endif
}
#ifdef BUILD_GLES3
if (context->initialShader.vao != (GLuint) -1) {
glBindVertexArray(0);
}
#endif
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

View File

@ -20,6 +20,8 @@ CXX_GUARD_START
#include <GL/gl.h>
#include <GL/glext.h>
#endif
#elif defined(BUILD_GLES3)
#include <GLES3/gl3.h>
#else
#include <GLES2/gl2.h>
#endif

View File

@ -3,7 +3,7 @@ include("${VITASDK}/share/vita.cmake" REQUIRED)
find_program(OBJCOPY ${cross_prefix}objcopy)
find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share)
set(OS_DEFINES IOAPI_NO_64)
set(OS_DEFINES IOAPI_NO_64 _GNU_SOURCE)
set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/psp2-*.c)

View File

@ -90,7 +90,7 @@ struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) {
bool _vfsceClose(struct VFile* vf) {
struct VFileSce* vfsce = (struct VFileSce*) vf;
sceIoSyncByFd(vfsce->fd);
sceIoSyncByFd(vfsce->fd, 0);
return sceIoClose(vfsce->fd) >= 0;
}
@ -128,7 +128,7 @@ static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) {
sceIoLseek(vfsce->fd, 0, SEEK_SET);
sceIoWrite(vfsce->fd, memory, size);
sceIoLseek(vfsce->fd, cur, SEEK_SET);
sceIoSyncByFd(vfsce->fd);
sceIoSyncByFd(vfsce->fd, 0);
mappedMemoryFree(memory, size);
}
@ -155,7 +155,7 @@ bool _vfsceSync(struct VFile* vf, void* buffer, size_t size) {
sceIoLseek(vfsce->fd, cur, SEEK_SET);
return res == size;
}
return sceIoSyncByFd(vfsce->fd) >= 0;
return sceIoSyncByFd(vfsce->fd, 0) >= 0;
}
struct VDir* VDirOpen(const char* path) {

View File

@ -44,8 +44,7 @@ ApplicationUpdater::ApplicationUpdater(ConfigController* config, QObject* parent
config->setQtOption("lastUpdateCheck", m_lastCheck);
if (available && currentVersion() < updateInfo()) {
#ifdef Q_OS_WIN
// Only works on Windows at the moment
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
ApplicationUpdatePrompt* prompt = new ApplicationUpdatePrompt;
connect(prompt, &QDialog::accepted, GBAApp::app(), &GBAApp::restartForUpdate);
prompt->setAttribute(Qt::WA_DeleteOnClose);

View File

@ -25,7 +25,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/input)
find_package(Qt5 COMPONENTS Core Widgets Network Multimedia)
if(NOT BUILD_GL AND NOT BUILD_GLES2)
if(NOT BUILD_GL AND NOT BUILD_GLES2 AND NOT BUILD_GLES3)
message(WARNING "OpenGL is recommended to build the Qt port")
endif()
@ -244,6 +244,11 @@ if(APPLE)
set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME})
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.endrift.${BINARY_NAME}-qt)
set_source_files_properties(${CMAKE_SOURCE_DIR}/res/icon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
if(DISTBUILD)
set(APPDIR ".")
else()
set(APPDIR "Applications")
endif()
endif()
if(WIN32)
configure_file(${CMAKE_SOURCE_DIR}/res/win.rc.in ${CMAKE_BINARY_DIR}/res/${BINARY_NAME}.rc)
@ -252,7 +257,7 @@ if(WIN32)
endif()
if(NOT DEFINED DATADIR)
if(APPLE)
set(DATADIR Applications/${PROJECT_NAME}.app/Contents/Resources)
set(DATADIR ${APPDIR}/${PROJECT_NAME}.app/Contents/Resources)
elseif(WIN32 AND NOT WIN32_UNIX_PATHS)
set(DATADIR ".")
else()
@ -308,6 +313,9 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CM
if(WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
if(NOT MSVC)
target_link_libraries(${BINARY_NAME}-qt -municode)
endif()
endif()
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network)
@ -339,11 +347,11 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE)
install(TARGETS ${BINARY_NAME}-qt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt
BUNDLE DESTINATION Applications COMPONENT ${BINARY_NAME}-qt)
BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt)
if(UNIX AND NOT APPLE)
install(FILES ${CMAKE_SOURCE_DIR}/res/qt.desktop DESTINATION share/applications RENAME ${BINARY_NAME}-qt.desktop COMPONENT ${BINARY_NAME}-qt)
endif()
if(UNIX)
if(UNIX AND NOT (APPLE AND DISTBUILD))
install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt)
endif()
if(APPLE OR WIN32)
@ -376,7 +384,7 @@ if(APPLE)
if(DEFINED CROSS_ROOT)
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}")
endif()
install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")")
install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${APPDIR}/${PROJECT_NAME}.app\")")
endif()
elseif(WIN32)
if(CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION EQUAL 8)

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Display.h"
#include "CoreController.h"
#include "ConfigController.h"
#include "DisplayGL.h"
#include "DisplayQt.h"
@ -14,23 +15,23 @@
using namespace QGBA;
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
Display::Driver Display::s_driver = Display::Driver::OPENGL;
#else
Display::Driver Display::s_driver = Display::Driver::QT;
#endif
Display* Display::create(QWidget* parent) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
QSurfaceFormat format;
format.setSwapInterval(1);
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
#endif
switch (s_driver) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
case Driver::OPENGL:
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
format.setVersion(2, 0);
} else {
@ -65,7 +66,7 @@ Display* Display::create(QWidget* parent) {
return new DisplayQt(parent);
default:
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
return new DisplayGL(format, parent);
#else
return new DisplayQt(parent);
@ -118,7 +119,7 @@ void Display::configure(ConfigController* config) {
interframeBlending(opts->interframeBlending);
filter(opts->resampleVideo);
config->updateOption("showOSD");
#if defined(BUILD_GL) || defined(BUILD_GLES2)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (opts->shader) {
struct VDir* shader = VDirOpen(opts->shader);
if (shader && supportsShaders()) {

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DisplayGL.h"
#if defined(BUILD_GL) || defined(BUILD_GLES2)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
#include <QApplication>
#include <QMutexLocker>
@ -24,7 +24,7 @@
#ifdef BUILD_GL
#include "platform/opengl/gl.h"
#endif
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
#include "platform/opengl/gles2.h"
#ifdef _WIN32
#include <epoxy/wgl.h>
@ -297,13 +297,13 @@ void PainterGL::create() {
#ifdef BUILD_GL
mGLContext* glBackend;
#endif
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
mGLES2Context* gl2Backend;
#endif
m_window = std::make_unique<QOpenGLPaintDevice>();
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
auto version = m_format.version();
if (version >= qMakePair(2, 0)) {
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
@ -329,7 +329,7 @@ void PainterGL::create() {
};
m_backend->init(m_backend, 0);
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_supportsShaders) {
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
}
@ -346,7 +346,7 @@ void PainterGL::destroy() {
return;
}
makeCurrent();
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_shader.passes) {
mGLES2ShaderFree(&m_shader);
}
@ -423,7 +423,7 @@ void PainterGL::filter(bool filter) {
void PainterGL::start() {
makeCurrent();
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_supportsShaders && m_shader.passes) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
}
@ -524,19 +524,17 @@ void PainterGL::unpause() {
}
void PainterGL::performDraw() {
m_painter.begin(m_window.get());
m_painter.beginNativePainting();
float r = m_surface->devicePixelRatio();
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
if (m_buffer) {
m_backend->postFrame(m_backend, m_buffer);
}
m_backend->drawFrame(m_backend);
m_painter.endNativePainting();
if (m_showOSD && m_messagePainter) {
m_painter.begin(m_window.get());
m_messagePainter->paint(&m_painter);
m_painter.end();
}
m_painter.end();
}
void PainterGL::enqueue(const uint32_t* backing) {
@ -601,7 +599,7 @@ void PainterGL::setShaders(struct VDir* dir) {
if (!supportsShaders()) {
return;
}
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
@ -615,7 +613,7 @@ void PainterGL::clearShaders() {
if (!supportsShaders()) {
return;
}
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader);
@ -628,7 +626,7 @@ VideoShader* PainterGL::shaders() {
}
int PainterGL::glTex() {
#ifdef BUILD_GLES2
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (supportsShaders()) {
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(m_backend);
return gl2Backend->tex;

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
#if defined(BUILD_GL) || defined(BUILD_GLES2)
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
#include "Display.h"

View File

@ -29,7 +29,7 @@ CXX_GUARD_START
#pragma GCC diagnostic pop
#endif
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
#include "gl-common.h"
#include "platform/opengl/gles2.h"
#endif
@ -72,7 +72,7 @@ struct mSDLRenderer {
#ifdef BUILD_GL
struct mGLContext gl;
#endif
#if defined(BUILD_GLES2) || defined(USE_EPOXY)
#if defined(BUILD_GLES2) || defined(BUILD_GLES3) || defined(USE_EPOXY)
struct mGLES2Context gl2;
#endif

View File

@ -716,7 +716,11 @@ static struct VDir* _makeOutDir(const char* testName) {
strncpy(pathEnd, testName, len);
pathEnd += len;
#ifndef _WIN32
mkdir(path, 0777);
#else
mkdir(path);
#endif
if (!pos) {
break;

View File

@ -83,8 +83,8 @@ static Socket _server = INVALID_SOCKET;
int main(int argc, char** argv) {
#ifdef _3DS
UNUSED(_mPerfShutdown);
gfxInitDefault();
osSetSpeedupEnable(true);
gfxInitDefault();
osSetSpeedupEnable(true);
consoleInit(GFX_BOTTOM, NULL);
#elif defined(__SWITCH__)
UNUSED(_mPerfShutdown);

View File

@ -102,7 +102,7 @@ const struct GUIFontGlyphMetric defaultFontMetrics[128] = {
{ 6, 11, { 2, 5, 3, 5 }}, // 0x5D "]"
{ 8, 7, { 2, 4, 7, 4 }}, // 0x5E "^"
{ 10, 3, { 10, 3, 3, 3 }}, // 0x5F "_"
{ 6, 5, { 8, 6, 3, 4 }}, // 0x60 "`"
{ 6, 5, { 3, 4, 8, 6 }}, // 0x60 "`"
{ 8, 7, { 6, 4, 3, 4 }}, // 0x61 "a"
{ 8, 11, { 2, 4, 3, 4 }}, // 0x62 "b"
{ 8, 7, { 6, 4, 3, 4 }}, // 0x63 "c"
@ -158,4 +158,29 @@ const struct GUIIconMetric defaultIconMetrics[] = {
[GUI_ICON_DOWN] = { 130, 34, 12, 12 },
[GUI_ICON_STATUS_FAST_FORWARD] = { 2, 50, 12, 12 },
[GUI_ICON_STATUS_MUTE] = { 17, 50, 14, 12 },
[GUI_ICON_9SLICE_EMPTY_NW] = { 162, 1, 8, 8 },
[GUI_ICON_9SLICE_EMPTY_N] = { 170, 1, 12, 8 },
[GUI_ICON_9SLICE_EMPTY_NE] = { 182, 1, 8, 8 },
[GUI_ICON_9SLICE_EMPTY_W] = { 162, 9, 8, 12 },
[GUI_ICON_9SLICE_EMPTY_E] = { 182, 9, 8, 12 },
[GUI_ICON_9SLICE_EMPTY_SW] = { 162, 23, 8, 8 },
[GUI_ICON_9SLICE_EMPTY_S] = { 170, 23, 12, 8 },
[GUI_ICON_9SLICE_EMPTY_SE] = { 182, 23, 8, 8 },
[GUI_ICON_9SLICE_FILLED_NW] = { 194, 1, 8, 8 },
[GUI_ICON_9SLICE_FILLED_N] = { 202, 1, 12, 8 },
[GUI_ICON_9SLICE_FILLED_NE] = { 214, 1, 8, 8 },
[GUI_ICON_9SLICE_FILLED_W] = { 194, 9, 8, 12 },
[GUI_ICON_9SLICE_FILLED_C] = { 202, 9, 12, 12 },
[GUI_ICON_9SLICE_FILLED_E] = { 214, 9, 8, 12 },
[GUI_ICON_9SLICE_FILLED_SW] = { 194, 23, 8, 8 },
[GUI_ICON_9SLICE_FILLED_S] = { 202, 23, 12, 8 },
[GUI_ICON_9SLICE_FILLED_SE] = { 214, 23, 8, 8 },
[GUI_ICON_9SLICE_CAP_NNW] = { 240, 1, 8, 7 },
[GUI_ICON_9SLICE_CAP_NWW] = { 226, 16, 6, 8 },
[GUI_ICON_9SLICE_CAP_NNE] = { 232, 1, 8, 7 },
[GUI_ICON_9SLICE_CAP_NEE] = { 248, 16, 6, 8 },
[GUI_ICON_9SLICE_CAP_SSW] = { 240, 24, 8, 7 },
[GUI_ICON_9SLICE_CAP_SWW] = { 226, 8, 6, 8 },
[GUI_ICON_9SLICE_CAP_SSE] = { 232, 24, 8, 7 },
[GUI_ICON_9SLICE_CAP_SEE] = { 248, 8, 6, 8 },
};

View File

@ -86,3 +86,55 @@ void GUIFontPrintf(struct GUIFont* font, int x, int y, enum GUIAlignment align,
va_end(args);
GUIFontPrint(font, x, y, align, color, buffer);
}
void GUIFontDraw9Slice(struct GUIFont* font, int x, int y, int width, int height, uint32_t color, enum GUI9SliceStyle style) {
switch (style) {
case GUI_9SLICE_EMPTY:
case GUI_9SLICE_EMPTY_CAPPED:
GUIFontDrawIcon(font, x , y , GUI_ALIGN_LEFT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_NW);
GUIFontDrawIcon(font, x + width, y , GUI_ALIGN_RIGHT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_NE);
GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_SW);
GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_EMPTY_SE);
break;
case GUI_9SLICE_FILLED:
GUIFontDrawIcon(font, x , y , GUI_ALIGN_LEFT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_NW);
GUIFontDrawIcon(font, x + width, y , GUI_ALIGN_RIGHT | GUI_ALIGN_TOP , GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_NE);
GUIFontDrawIcon(font, x , y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SW);
GUIFontDrawIcon(font, x + width, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_FILLED_SE);
break;
}
unsigned offX, offY;
unsigned endX, endY;
GUIFontIconMetrics(font, GUI_ICON_9SLICE_EMPTY_NW, &offX, &offY);
GUIFontIconMetrics(font, GUI_ICON_9SLICE_EMPTY_SE, &endX, &endY);
switch (style) {
case GUI_9SLICE_EMPTY:
GUIFontDrawIconSize(font, x + offX, y, width - offX - endX, offY, color, GUI_ICON_9SLICE_EMPTY_N);
GUIFontDrawIconSize(font, x, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_EMPTY_W);
GUIFontDrawIconSize(font, x + offX, y + height - endY, width - offX - endX, offY, color, GUI_ICON_9SLICE_EMPTY_S);
GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_EMPTY_E);
break;
case GUI_9SLICE_FILLED:
GUIFontDrawIconSize(font, x + offX, y, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILLED_N);
GUIFontDrawIconSize(font, x, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_W);
GUIFontDrawIconSize(font, x + offX, y + height - endY, width - offX - endX, offY, color, GUI_ICON_9SLICE_FILLED_S);
GUIFontDrawIconSize(font, x + width - endX, y + offY, offX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_E);
GUIFontDrawIconSize(font, x + offX, y + offY, width - offX - endX, height - offY - endY, color, GUI_ICON_9SLICE_FILLED_C);
break;
case GUI_9SLICE_EMPTY_CAPPED:
GUIFontDrawIcon(font, x + offX, y, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NNW);
GUIFontDrawIcon(font, x, y + offY, GUI_ALIGN_LEFT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NWW);
GUIFontDrawIcon(font, x + width - endX, y, GUI_ALIGN_RIGHT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NNE);
GUIFontDrawIcon(font, x + width, y + offY, GUI_ALIGN_RIGHT | GUI_ALIGN_TOP, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_NEE);
GUIFontDrawIcon(font, x + offX, y + height, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SSW);
GUIFontDrawIcon(font, x, y + height - endY, GUI_ALIGN_LEFT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SWW);
GUIFontDrawIcon(font, x + width - endX, y + height, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SSE);
GUIFontDrawIcon(font, x + width, y + height - endY, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, GUI_ICON_9SLICE_CAP_SEE);
break;
}
}