Merge branch 'master' into medusa

This commit is contained in:
Vicki Pfau 2020-07-13 17:37:15 -07:00
commit 42c5ec87c0
65 changed files with 1016 additions and 493 deletions

19
.appveyor.yml Normal file
View File

@ -0,0 +1,19 @@
image:
- Visual Studio 2019
platform:
- x64
configuration:
- Release
cache:
- C:\Tools\vcpkg
install:
- git -C C:\Tools\vcpkg pull --quiet
- C:\Tools\vcpkg\bootstrap-vcpkg
- vcpkg --triplet x64-windows install ffmpeg libepoxy libpng libzip sdl2 sqlite3
- vcpkg --no-dry-run upgrade
- rd /Q /S C:\Tools\vcpkg\buildtrees
before_build:
- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.12\msvc2017_64 -DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake
build:
parallel: true
project: mGBA.sln

2
.gitignore vendored
View File

@ -5,6 +5,7 @@
/build
/build-*
/.vs
*.a
*.dylib
@ -14,4 +15,5 @@
*.so
CMakeCache.txt
CMakeFiles
CMakeSettings.json
version.c

21
CHANGES
View File

@ -22,9 +22,13 @@ Misc:
Features:
- e-Reader card scanning
- Add APNG recording
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
Emulation fixes:
- ARM: Fix ALU reading PC after shifting
- ARM: Fix STR storing PC after address calculation
- ARM: Fix LDM^ writeback to user-mode register
- ARM: Fix LDM^ {pc} differences (fixes mgba.io/i/1698)
- ARM: Fix edge case with Thumb SBC flags (fixes mgba.io/i/1818)
- GB: Partially fix timing for skipped BIOS
- GB Memory: Fix OAM DMA from top 8 kB
- GB MBC: Fix MBC1 mode changing behavior
@ -34,24 +38,41 @@ Emulation fixes:
- GBA: Fix timing advancing too quickly in rare cases
- GBA BIOS: Implement dummy sound driver calls
- GBA BIOS: Improve HLE BIOS timing
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
- GBA Memory: Improve gamepak prefetch timing
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
- GBA SIO: Fix copying Normal mode transfer values
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing
- GBA Video: Fix invalid read in mode 4 mosaic
- SM83: Emulate HALT bug
Other fixes:
- All: Improve export headers (fixes mgba.io/i/1738)
- All: Correct format strings for some numbers on Windows (fixes mgba.io/i/1794)
- All: Correct more format strings on Windows (fixes mgba.io/i/1817)
- CMake: Fix build with libzip 1.7
- Core: Ensure ELF regions can be written before trying
- Debugger: Don't skip undefined instructions when debugger attached
- FFmpeg: Fix some small memory leaks
- GB Core: Fix extracting SRAM when none is present
- GBA Savedata: Fix extracting save when not yet configured in-game
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)
- Qt: Fix file handle leak on opening an invalid ROM
- Qt: Fix a race condition in the frame inspector
- Qt: Fix Italian RTC translation (fixes mgba.io/i/1798)
- Qt: Add missing option for Wisdom Tree in overrides list
- Util: Fix crash if PNG header fails to write
Misc:
- Debugger: Keep track of global cycle count
- FFmpeg: Add looping option for GIF/APNG
- FFmpeg: Use range coder for FFV1 to reduce output size
- Qt: Renderer can be changed while a game is running
- Qt: Add hex index to palette view
- Qt: Add transformation matrix info to sprite view
- Qt: Add per-page scrolling to memory view (fixes mgba.io/i/1795)
- Qt: Add setting to display ROM filename in title (closes mgba.io/i/1784)
0.8.2: (2020-06-14)
Emulation fixes:

View File

@ -20,6 +20,10 @@ if(NOT MSVC)
set(CMAKE_C_EXTENSIONS ON)
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers")
if(WIN32)
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format")
endif()
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146")
endif()
@ -286,6 +290,7 @@ include(CheckFunctionExists)
include(CheckIncludeFiles)
check_function_exists(strdup HAVE_STRDUP)
check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(strlcpy HAVE_STRLCPY)
if(NOT DEFINED PSP2)
check_function_exists(localtime_r HAVE_LOCALTIME_R)
endif()
@ -364,6 +369,10 @@ if(HAVE_STRNDUP)
list(APPEND FUNCTION_DEFINES HAVE_STRNDUP)
endif()
if(HAVE_STRLCPY)
list(APPEND FUNCTION_DEFINES HAVE_STRLCPY)
endif()
if(HAVE_LOCALTIME_R)
list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R)
endif()
@ -470,7 +479,15 @@ set(WANT_PNG ${USE_PNG})
set(WANT_SQLITE3 ${USE_SQLITE3})
set(USE_CMOCKA ${BUILD_SUITE})
find_feature(USE_FFMPEG "libavcodec;libavfilter;libavformat;libavutil;libswscale")
if(DEFINED VCPKG_TARGET_TRIPLET)
find_feature(USE_FFMPEG "FFMPEG")
if(FFMPEG_FOUND)
set(USE_LIBAVRESAMPLE OFF)
set(USE_LIBSWRESAMPLE ON)
endif()
else()
find_feature(USE_FFMPEG "libavcodec;libavfilter;libavformat;libavutil;libswscale")
endif()
find_feature(USE_ZLIB "ZLIB")
find_feature(USE_MINIZIP "minizip")
find_feature(USE_PNG "PNG")
@ -481,7 +498,7 @@ find_feature(USE_SQLITE3 "sqlite3")
find_feature(USE_ELF "libelf")
find_feature(ENABLE_PYTHON "PythonLibs")
if(USE_FFMPEG)
if(USE_FFMPEG AND NOT DEFINED VCPKG_TARGET_TRIPLET)
set(USE_LIBAVRESAMPLE ON)
set(USE_LIBSWRESAMPLE ON)
find_feature(USE_LIBAVRESAMPLE "libavresample")
@ -533,31 +550,33 @@ if(USE_FFMPEG)
list(APPEND FEATURES LIBAVRESAMPLE)
list(APPEND FEATURES LIBAV)
endif()
include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFILTER_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS})
link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFILTER_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS})
include_directories(AFTER ${FFMPEG_INCLUDE_DIRS} ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFILTER_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS})
link_directories(${FFMPEG_LIBRARY_DIRS} ${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFILTER_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS})
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-encoder.c")
string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVFILTER_VERSION_MAJOR ${libavfilter_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVUTIL_VERSION_MAJOR ${libavutil_VERSION})
string(REGEX MATCH "^[0-9]+" LIBSWSCALE_VERSION_MAJOR ${libswscale_VERSION})
list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES})
if(WIN32)
list(APPEND DEPENDENCY_LIB ${FFMPEG_LIBRARIES} ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES})
if(WIN32 AND NOT DEFINED VCPKG_TARGET_TRIPLET)
list(APPEND DEPENDENCY_LIB bcrypt)
endif()
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavcodec${LIBAVCODEC_VERSION_MAJOR}|libavcodec-extra-${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavfilter${LIBAVFILTER_VERSION_MAJOR}|libavfilter-ffmpeg${LIBAVFILTER_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
if(USE_LIBSWRESAMPLE)
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_MAJOR}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_MAJOR}")
else()
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
if(UNIX)
string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVFILTER_VERSION_MAJOR ${libavfilter_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION})
string(REGEX MATCH "^[0-9]+" LIBAVUTIL_VERSION_MAJOR ${libavutil_VERSION})
string(REGEX MATCH "^[0-9]+" LIBSWSCALE_VERSION_MAJOR ${libswscale_VERSION})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavcodec${LIBAVCODEC_VERSION_MAJOR}|libavcodec-extra-${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavfilter${LIBAVFILTER_VERSION_MAJOR}|libavfilter-ffmpeg${LIBAVFILTER_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
if(USE_LIBSWRESAMPLE)
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_MAJOR}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_MAJOR}")
else()
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
endif()
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavutil${LIBAVUTIL_VERSION_MAJOR}|libavutil-ffmpeg${LIBAVUTIL_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswscale${LIBSWSCALE_VERSION_MAJOR}|libswscale-ffmpeg${LIBSWSCALE_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
endif()
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavutil${LIBAVUTIL_VERSION_MAJOR}|libavutil-ffmpeg${LIBAVUTIL_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswscale${LIBSWSCALE_VERSION_MAJOR}|libswscale-ffmpeg${LIBSWSCALE_VERSION_MAJOR}")
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
if(APPLE)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
@ -630,18 +649,22 @@ if(WANT_SQLITE3 AND NOT USE_SQLITE3)
endif()
if(USE_LIBZIP)
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
link_directories(${LIBZIP_LIBRARY_DIRS})
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
if(TARGET libzip::zip)
list(APPEND DEPENDENCY_LIB libzip::zip)
else()
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
link_directories(${LIBZIP_LIBRARY_DIRS})
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
endif()
list(APPEND FEATURES LIBZIP)
list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c)
string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR ${libzip_VERSION})
string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR "${libzip_VERSION}")
if (LIBZIP_VERSION_MAJOR LESS 1)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip2")
elseif(LIBZIP_VERSION_MAJOR EQUAL 1)
elseif(LIBZIP_VERSION_MAJOR EQUAL 1 OR NOT LIBZIP_VERSION_MAJOR)
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip4|libzip5")
else()
message(AUTHOR_WARNING Unknown version of libzip detected: ${libzip_VERSION})
message(AUTHOR_WARNING "Unknown version of libzip detected: ${libzip_VERSION}")
endif()
elseif(USE_MINIZIP)
include_directories(AFTER ${MINIZIP_INCLUDE_DIRS})
@ -956,48 +979,10 @@ if(BUILD_QT)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/qt ${CMAKE_CURRENT_BINARY_DIR}/qt)
endif()
if(BUILD_PERF)
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c)
if(UNIX AND NOT APPLE)
list(APPEND PERF_LIB rt)
endif()
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB} ${OS_LIB})
set_target_properties(${BINARY_NAME}-perf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
install(TARGETS ${BINARY_NAME}-perf DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-perf)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/tools/perf.py DESTINATION "${LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)
endif()
if(BUILD_TEST)
add_executable(${BINARY_NAME}-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/fuzz-main.c)
target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME})
set_target_properties(${BINARY_NAME}-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
add_executable(tbl-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/tbl-fuzz-main.c)
target_link_libraries(tbl-fuzz ${BINARY_NAME})
set_target_properties(tbl-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
install(TARGETS ${BINARY_NAME}-fuzz tbl-fuzz DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-test)
endif()
if(NOT USE_CMOCKA)
set(BUILD_SUITE OFF)
endif()
if(BUILD_SUITE)
enable_testing()
include_directories(AFTER ${CMOCKA_INCLUDE_DIRS})
link_directories(${CMOCKA_LIBRARY_DIRS})
foreach(TEST IN LISTS TEST_SRC)
string(REPLACE "${CMAKE_SOURCE_DIR}/src/" "" TEST_NAME "${TEST}")
string(REPLACE "/" "-" TEST_NAME "${TEST_NAME}")
string(REPLACE "-test" "" TEST_NAME "${TEST_NAME}")
string(REPLACE ".c" "" TEST_NAME "${TEST_NAME}")
add_executable(test-${TEST_NAME} ${TEST})
target_link_libraries(test-${TEST_NAME} ${BINARY_NAME} ${PLATFORM_LIBRARY} cmocka)
set_target_properties(test-${TEST_NAME} PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
add_test(${TEST_NAME} test-${TEST_NAME})
endforeach()
endif()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test ${CMAKE_CURRENT_BINARY_DIR}/test)
if(BUILD_EXAMPLE)
add_executable(${BINARY_NAME}-example-server ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/server.c)
@ -1207,8 +1192,7 @@ endif()
cpack_add_component_group(test PARENT_GROUP dev)
cpack_add_component(${BINARY_NAME}-perf GROUP test)
cpack_add_component(${BINARY_NAME}-fuzz GROUP test)
cpack_add_component(tbl-fuzz GROUP test)
cpack_add_component(${BINARY_NAME}-test GROUP test)
# Summaries
set(SUMMARY_GL_LIST)

View File

@ -52,6 +52,7 @@ The following mappers are fully supported:
- MBC5+Rumble
- MBC7
- Wisdom Tree (unlicensed)
- Pokémon Jade/Diamond (unlicensed)
The following mappers are partially supported:
@ -175,6 +176,8 @@ Note that you should not do a `make install` on macOS, as it will not work prope
#### Windows developer building
##### MSYS2
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 1100MiB of packages, so it will take a long time):
For x86 (32 bit) builds:
@ -199,6 +202,20 @@ Then finally build it by running these commands:
Please note that this build of medusa for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), running `cpack -G ZIP` will prepare a zip file with all of the necessary DLLs.
##### Visual Studio
To build using Visual Studio is a similarly complicated setup. To begin you will need to install [vcpkg](https://github.com/Microsoft/vcpkg). After installing vcpkg you will need to install several additional packages:
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3
Note that this installation won't support hardware accelerated video encoding on Nvidia hardware. If you care about this, you'll need to install CUDA beforehand, and then substitute `ffmpeg[vpx,x264,nvcodec]` into the previous command.
You will also need to install Qt. Unfortunately due to Qt being owned and run by an ailing company as opposed to a reasonable organization there is no longer an offline open source edition installer for the latest version, so you'll need to either fall back to an [old version installer](https://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe) (which wants you to create an otherwise-useless account, but you can bypass temporarily setting an invalid proxy or otherwise disabling networking), use the online installer (which requires an account regardless), or use vcpkg to build it (slowly). None of these are great options. For the installer you'll want to install the applicable MSVC versions. Note that the offline installers do not support MSVC 2019. For vcpkg you'll want to install it as such, which will take quite a while, especially on quad core or less computers:
vcpkg install qt5-base qt5-multimedia
Next, open Visual Studio, select Clone Repository, and enter `https://github.com/mgba-emu/mgba.git`. When Visual Studio is done cloning, go to File > CMake and open the CMakeLists.txt file at the root of the checked out repository. From there, mGBA can be developed in Visual Studio similarly to other Visual Studio CMake projects.
#### Toolchain building
If you have devkitARM (for 3DS), devkitPPC (for Wii), devkitA64 (for Switch), or vitasdk (for PS Vita), you can use the following commands for building:
@ -223,9 +240,8 @@ medusa has no hard dependencies, however, the following optional dependencies ar
- SDL: for a more basic frontend and gamepad support in the Qt frontend. SDL 2 is recommended, but 1.2 is supported.
- zlib and libpng: for screenshot support and savestate-in-PNG support.
- libedit: for command-line debugger support.
- ffmpeg or libav: for video recording.
- ffmpeg or libav: for video and GIF recording.
- libzip or zlib: for loading ROMs stored in zip files.
- ImageMagick: for GIF recording.
- SQLite3: for game databases.
- libelf: for ELF loading.
@ -291,7 +307,7 @@ medusa is Copyright © 2013 2020 Jeffrey Pfau. It is distributed under the [
medusa contains the following third-party libraries:
- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 Ben Hoyt and used under a BSD 3-clause license.
- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 2020 Ben Hoyt and used under a BSD 3-clause license.
- [blip-buf](https://code.google.com/archive/p/blip-buf), which is copyright © 2003 2009 Shay Green and used under a Lesser GNU Public License.
- [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain.
- [MurmurHash3](https://github.com/aappleby/smhasher) implementation by Austin Appleby, which is public domain.

View File

@ -14,6 +14,10 @@
#define CXX_GUARD_END
#endif
#ifdef __MINGW32__
#define __USE_MINGW_ANSI_STDIO 1
#endif
CXX_GUARD_START
#include <ctype.h>
@ -115,10 +119,8 @@ typedef intptr_t ssize_t;
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
// newlib doesn't support %z properly by default
#define PRIz ""
#elif defined(_WIN64)
#define PRIz "I64"
#elif defined(_WIN32)
#define PRIz ""
#elif defined(_MSC_VER)
#define PRIz "I"
#else
#define PRIz "z"
#endif

View File

@ -19,6 +19,10 @@ char* strndup(const char* start, size_t len);
char* strdup(const char* str);
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize);
#endif
char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len);
bool endswith(const char* restrict s1, const char* restrict end);
bool startswith(const char* restrict s1, const char* restrict start);

View File

@ -41,7 +41,7 @@ CXX_GUARD_START
capacity = 4; \
} \
vector->capacity = capacity; \
vector->vector = malloc(sizeof(TYPE) * capacity); \
vector->vector = calloc(capacity, sizeof(TYPE)); \
} \
void NAME ## Deinit(struct NAME* vector) { \
free(vector->vector); \

View File

@ -74,7 +74,7 @@ struct mCore {
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
void (*reloadConfigOption)(struct mCore*, const char* option, const struct mCoreConfig*);
void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height);
void (*desiredVideoDimensions)(const struct mCore*, unsigned* width, unsigned* height);
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
void (*setVideoGLTex)(struct mCore*, unsigned texid);

View File

@ -34,9 +34,10 @@ enum GBMemoryBankControllerType {
GB_HuC3 = 0x12,
GB_POCKETCAM = 0x13,
GB_TAMA5 = 0x14,
GB_UNL_WISDOM_TREE = 0x20,
GB_MBC3_RTC = 0x103,
GB_MBC5_RUMBLE = 0x105
GB_MBC5_RUMBLE = 0x105,
GB_UNL_WISDOM_TREE = 0x200,
GB_UNL_PKJD = 0x203,
};
struct GBSIODriver {

View File

@ -146,6 +146,10 @@ struct GBTAMA5State {
uint8_t registers[GBTAMA5_MAX];
};
struct GBPKJDState {
uint8_t reg[2];
};
union GBMBCState {
struct GBMBC1State mbc1;
struct GBMBC6State mbc6;
@ -153,6 +157,7 @@ union GBMBCState {
struct GBMMM01State mmm01;
struct GBPocketCamState pocketCam;
struct GBTAMA5State tama5;
struct GBPKJDState pkjd;
};
struct mRotationSource;
@ -171,6 +176,7 @@ struct GBMemory {
int wramCurrentBank;
bool sramAccess;
bool directSramAccess;
uint8_t* sram;
uint8_t* sramBank;
int sramCurrentBank;

View File

@ -211,8 +211,8 @@ struct GBAVideo {
struct GBAVideoRenderer* renderer;
struct mTimingEvent event;
// VCOUNT
int vcount;
int shouldStall;
uint16_t palette[512];
uint16_t* vram;

View File

@ -6,6 +6,7 @@
#include <mgba/internal/arm/decoder.h>
#include <mgba/internal/arm/decoder-inlines.h>
#include <mgba-util/string.h>
#define ADVANCE(AMOUNT) \
if (AMOUNT >= blen) { \
@ -45,22 +46,22 @@ static const char* _armConditions[] = {
static int _decodeRegister(int reg, char* buffer, int blen) {
switch (reg) {
case ARM_SP:
strncpy(buffer, "sp", blen - 1);
strlcpy(buffer, "sp", blen);
return 2;
case ARM_LR:
strncpy(buffer, "lr", blen - 1);
strlcpy(buffer, "lr", blen);
return 2;
case ARM_PC:
strncpy(buffer, "pc", blen - 1);
strlcpy(buffer, "pc", blen);
return 2;
case ARM_CPSR:
strncpy(buffer, "cpsr", blen - 1);
strlcpy(buffer, "cpsr", blen);
return 4;
case ARM_SPSR:
strncpy(buffer, "spsr", blen - 1);
strlcpy(buffer, "spsr", blen);
return 4;
default:
return snprintf(buffer, blen - 1, "r%i", reg);
return snprintf(buffer, blen, "r%i", reg);
}
}
@ -69,7 +70,7 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
return 0;
}
int total = 0;
strncpy(buffer, "{", blen - 1);
strlcpy(buffer, "{", blen);
ADVANCE(1);
int i;
int start = -1;
@ -86,12 +87,12 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
if (end > start) {
written = _decodeRegister(start, buffer, blen);
ADVANCE(written);
strncpy(buffer, "-", blen - 1);
strlcpy(buffer, "-", blen);
ADVANCE(1);
}
written = _decodeRegister(end, buffer, blen);
ADVANCE(written);
strncpy(buffer, ",", blen - 1);
strlcpy(buffer, ",", blen);
ADVANCE(1);
start = i;
end = i;
@ -103,13 +104,13 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
if (end > start) {
written = _decodeRegister(start, buffer, blen);
ADVANCE(written);
strncpy(buffer, "-", blen - 1);
strlcpy(buffer, "-", blen);
ADVANCE(1);
}
written = _decodeRegister(end, buffer, blen);
ADVANCE(written);
}
strncpy(buffer, "}", blen - 1);
strlcpy(buffer, "}", blen);
ADVANCE(1);
return total;
}
@ -119,29 +120,29 @@ static int _decodePSR(int psrBits, char* buffer, int blen) {
return 0;
}
int total = 0;
strncpy(buffer, "_", blen - 1);
strlcpy(buffer, "_", blen);
ADVANCE(1);
if (psrBits & ARM_PSR_C) {
strncpy(buffer, "c", blen - 1);
strlcpy(buffer, "c", blen);
ADVANCE(1);
}
if (psrBits & ARM_PSR_X) {
strncpy(buffer, "x", blen - 1);
strlcpy(buffer, "x", blen);
ADVANCE(1);
}
if (psrBits & ARM_PSR_S) {
strncpy(buffer, "s", blen - 1);
strlcpy(buffer, "s", blen);
ADVANCE(1);
}
if (psrBits & ARM_PSR_F) {
strncpy(buffer, "f", blen - 1);
strlcpy(buffer, "f", blen);
ADVANCE(1);
}
return total;
}
static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) {
return snprintf(buffer, blen - 1, "$%08X", address + pc);
return snprintf(buffer, blen, "$%08X", address + pc);
}
static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) {
@ -149,7 +150,7 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
return 0;
}
int total = 0;
strncpy(buffer, "[", blen - 1);
strlcpy(buffer, "[", blen);
ADVANCE(1);
int written;
if (memory.format & ARM_MEMORY_REGISTER_BASE) {
@ -160,26 +161,26 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
written = _decodeRegister(memory.baseReg, buffer, blen);
ADVANCE(written);
if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) {
strncpy(buffer, ", ", blen - 1);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
}
}
}
if (memory.format & ARM_MEMORY_POST_INCREMENT) {
strncpy(buffer, "], ", blen - 1);
strlcpy(buffer, "], ", blen);
ADVANCE(3);
}
if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) {
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
written = snprintf(buffer, blen - 1, "#-%i", memory.offset.immediate);
written = snprintf(buffer, blen, "#-%i", memory.offset.immediate);
ADVANCE(written);
} else {
written = snprintf(buffer, blen - 1, "#%i", memory.offset.immediate);
written = snprintf(buffer, blen, "#%i", memory.offset.immediate);
ADVANCE(written);
}
} else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) {
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
strncpy(buffer, "-", blen - 1);
strlcpy(buffer, "-", blen);
ADVANCE(1);
}
written = _decodeRegister(memory.offset.reg, buffer, blen);
@ -191,11 +192,11 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
}
if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) {
strncpy(buffer, "]", blen - 1);
strlcpy(buffer, "]", blen);
ADVANCE(1);
}
if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) {
strncpy(buffer, "!", blen - 1);
strlcpy(buffer, "!", blen);
ADVANCE(1);
}
return total;
@ -206,33 +207,33 @@ static int _decodeShift(union ARMOperand op, bool reg, char* buffer, int blen) {
return 0;
}
int total = 0;
strncpy(buffer, ", ", blen - 1);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
int written;
switch (op.shifterOp) {
case ARM_SHIFT_LSL:
strncpy(buffer, "lsl ", blen - 1);
strlcpy(buffer, "lsl ", blen);
ADVANCE(4);
break;
case ARM_SHIFT_LSR:
strncpy(buffer, "lsr ", blen - 1);
strlcpy(buffer, "lsr ", blen);
ADVANCE(4);
break;
case ARM_SHIFT_ASR:
strncpy(buffer, "asr ", blen - 1);
strlcpy(buffer, "asr ", blen);
ADVANCE(4);
break;
case ARM_SHIFT_ROR:
strncpy(buffer, "ror ", blen - 1);
strlcpy(buffer, "ror ", blen);
ADVANCE(4);
break;
case ARM_SHIFT_RRX:
strncpy(buffer, "rrx", blen - 1);
strlcpy(buffer, "rrx", blen);
ADVANCE(3);
return total;
}
if (!reg) {
written = snprintf(buffer, blen - 1, "#%i", op.shifterImm);
written = snprintf(buffer, blen, "#%i", op.shifterImm);
} else {
written = _decodeRegister(op.shifterReg, buffer, blen);
}
@ -388,7 +389,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
default:
break;
}
written = snprintf(buffer, blen - 1, "%s%s%s ", mnemonic, cond, flags);
written = snprintf(buffer, blen, "%s%s%s ", mnemonic, cond, flags);
ADVANCE(written);
switch (info->mnemonic) {
@ -397,15 +398,15 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
written = _decodeRegister(info->memory.baseReg, buffer, blen);
ADVANCE(written);
if (info->memory.format & ARM_MEMORY_WRITEBACK) {
strncpy(buffer, "!", blen - 1);
strlcpy(buffer, "!", blen);
ADVANCE(1);
}
strncpy(buffer, ", ", blen - 1);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
written = _decodeRegisterList(info->op1.immediate, buffer, blen);
ADVANCE(written);
if (info->memory.format & ARM_MEMORY_SPSR_SWAP) {
strncpy(buffer, "^", blen - 1);
strlcpy(buffer, "^", blen);
ADVANCE(1);
}
break;
@ -430,7 +431,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written);
}
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
written = snprintf(buffer, blen - 1, "#%i", info->op1.immediate);
written = snprintf(buffer, blen, "#%i", info->op1.immediate);
ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_1) {
written = _decodeMemory(info->memory, pc, buffer, blen);
@ -453,11 +454,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written);
}
if (info->operandFormat & ARM_OPERAND_2) {
strncpy(buffer, ", ", blen);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
}
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) {
written = snprintf(buffer, blen - 1, "#%i", info->op2.immediate);
written = snprintf(buffer, blen, "#%i", info->op2.immediate);
ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_2) {
written = _decodeMemory(info->memory, pc, buffer, blen);
@ -477,11 +478,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written);
}
if (info->operandFormat & ARM_OPERAND_3) {
strncpy(buffer, ", ", blen - 1);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
}
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) {
written = snprintf(buffer, blen - 1, "#%i", info->op3.immediate);
written = snprintf(buffer, blen, "#%i", info->op3.immediate);
ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_3) {
written = _decodeMemory(info->memory, pc, buffer, blen);
@ -501,11 +502,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
ADVANCE(written);
}
if (info->operandFormat & ARM_OPERAND_4) {
strncpy(buffer, ", ", blen - 1);
strlcpy(buffer, ", ", blen);
ADVANCE(2);
}
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) {
written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate);
written = snprintf(buffer, blen, "#%i", info->op4.immediate);
ADVANCE(written);
} else if (info->operandFormat & ARM_OPERAND_MEMORY_4) {
written = _decodeMemory(info->memory, pc, buffer, blen);

View File

@ -499,11 +499,26 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
#define ARM_MS_PRE \
#define ARM_MS_PRE_store \
enum PrivilegeMode privilegeMode = cpu->privilegeMode; \
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
#define ARM_MS_POST ARMSetPrivilegeMode(cpu, privilegeMode);
#define ARM_MS_PRE_load \
enum PrivilegeMode privilegeMode; \
if (!(rs & 0x8000)) { \
privilegeMode = cpu->privilegeMode; \
ARMSetPrivilegeMode(cpu, MODE_SYSTEM); \
}
#define ARM_MS_POST_store ARMSetPrivilegeMode(cpu, privilegeMode);
#define ARM_MS_POST_load \
if ((rs & 0x8000) && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
cpu->cpsr = cpu->spsr; \
_ARMReadCPSR(cpu); \
} else { \
ARMSetPrivilegeMode(cpu, privilegeMode); \
} \
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \
DEFINE_INSTRUCTION_ARM(NAME, \
@ -512,10 +527,9 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
uint32_t address = cpu->gprs[rn]; \
S_PRE; \
address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, &currentCycles); \
WRITEBACK; \
S_POST; \
POST_BODY; \
WRITEBACK;)
POST_BODY;)
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(NAME, LS, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## DA, LS, , , , DA, POST_BODY) \
@ -529,14 +543,14 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(NAME, LS, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(NAME, LS, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE, ARM_MS_POST, DA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, DA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE, ARM_MS_POST, DB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, DB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE, ARM_MS_POST, IA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, IA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE, ARM_MS_POST, IB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, IB, POST_BODY)
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IA, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IB, POST_BODY) \
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IB, POST_BODY)
// Begin ALU definitions
@ -691,7 +705,11 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
load,
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
if ((rs & 0x8000) || !rs) {
currentCycles += ARMWritePC(cpu);
if (cpu->executionMode == MODE_THUMB) {
currentCycles += ThumbWritePC(cpu);
} else {
currentCycles += ARMWritePC(cpu);
}
})
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(LDMv5,

View File

@ -25,6 +25,13 @@
cpu->cpsr.c = ARM_BORROW_FROM(M, N, D); \
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
#define THUMB_SUBTRACTION_CARRY_S(M, N, D, C) \
cpu->cpsr.flags = 0; \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D); \
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
#define THUMB_NEUTRAL_S(M, N, D) \
cpu->cpsr.n = ARM_SIGN(D); \
cpu->cpsr.z = !(D);
@ -203,10 +210,11 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ADC,
THUMB_ADDITION_S(d, n, cpu->gprs[rd]);)
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(SBC,
int n = cpu->gprs[rn] + !cpu->cpsr.c;
int n = cpu->gprs[rn];
int d = cpu->gprs[rd];
cpu->gprs[rd] = d - n;
THUMB_SUBTRACTION_S(d, n, cpu->gprs[rd]);)
cpu->gprs[rd] = d - n - !cpu->cpsr.c;
THUMB_SUBTRACTION_CARRY_S(d, n, cpu->gprs[rd], !cpu->cpsr.c);)
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ROR,
int rs = cpu->gprs[rn] & 0xFF;
if (rs) {

View File

@ -42,7 +42,7 @@ static void _redoCacheSize(struct mBitmapCache* cache) {
cache->cache = anonymousMemoryMap(mBitmapCacheSystemInfoGetWidth(cache->sysConfig) * size * sizeof(color_t));
cache->status = anonymousMemoryMap(size * sizeof(*cache->status));
if (mBitmapCacheSystemInfoIsUsesPalette(cache->sysConfig)) {
cache->palette = malloc((1 << (1 << mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig))) * sizeof(color_t));
cache->palette = calloc((1 << (1 << mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig))), sizeof(color_t));
} else {
cache->palette = NULL;
}

View File

@ -69,7 +69,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
enum mPlatform mCoreIsCompatible(struct VFile* vf) {
if (!vf) {
return false;
return PLATFORM_NONE;
}
const struct mCoreFilter* filter;
for (filter = &_filters[0]; filter->filter; ++filter) {
@ -169,6 +169,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si
}
}
vf->close(vf);
if (read < 0) {
vfm->close(vfm);
return false;
}
bool ret = core->loadROM(core, vfm);
if (!ret) {
vfm->close(vfm);

View File

@ -15,6 +15,10 @@
#cmakedefine BUILD_GLES2
#endif
#ifndef BUILD_GLES3
#cmakedefine BUILD_GLES3
#endif
// Miscellaneous flags
#ifndef COLOR_16_BIT

View File

@ -88,7 +88,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
map->numMaps = 1;
impl = &map->maps[0];
impl->type = type;
impl->map = malloc(map->info->nKeys * sizeof(int));
impl->map = calloc(map->info->nKeys, sizeof(int));
size_t i;
for (i = 0; i < map->info->nKeys; ++i) {
impl->map[i] = -1;
@ -108,7 +108,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
}
if (impl) {
impl->type = type;
impl->map = malloc(map->info->nKeys * sizeof(int));
impl->map = calloc(map->info->nKeys, sizeof(int));
size_t i;
for (i = 0; i < map->info->nKeys; ++i) {
impl->map[i] = -1;
@ -122,7 +122,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
map->numMaps *= 2;
impl = &map->maps[m];
impl->type = type;
impl->map = malloc(map->info->nKeys * sizeof(int));
impl->map = calloc(map->info->nKeys, sizeof(int));
size_t i;
for (i = 0; i < map->info->nKeys; ++i) {
impl->map[i] = -1;

View File

@ -448,7 +448,9 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
if (mStateExtdataGet(&extdata, EXTDATA_SAVEDATA, &item)) {
mLOG(SAVESTATE, INFO, "Loading savedata");
if (item.data) {
core->savedataRestore(core, item.data, item.size, flags & SAVESTATE_SAVEDATA);
if (!core->savedataRestore(core, item.data, item.size, flags & SAVESTATE_SAVEDATA)) {
mLOG(SAVESTATE, WARN, "Failed to load savedata from savestate");
}
}
}
struct mCheatDevice* device;

View File

@ -636,12 +636,6 @@ struct mCoreThread* mCoreThreadGet(void) {
}
#endif
#else
struct mCoreThread* mCoreThreadGet(void) {
return NULL;
}
#endif
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
UNUSED(logger);
UNUSED(level);
@ -650,11 +644,14 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level
printf("\n");
struct mCoreThread* thread = mCoreThreadGet();
if (thread && level == mLOG_FATAL) {
#ifndef DISABLE_THREADING
mCoreThreadMarkCrashed(thread);
#endif
}
}
#else
struct mCoreThread* mCoreThreadGet(void) {
return NULL;
}
#endif
struct mLogger* mCoreThreadLogger(void) {
struct mCoreThread* thread = mCoreThreadGet();

View File

@ -46,8 +46,8 @@ static void _redoCacheSize(struct mTileCache* cache) {
unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles * size);
cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
cache->globalPaletteVersion = malloc(size * sizeof(*cache->globalPaletteVersion));
cache->palette = malloc(size * bpp * sizeof(*cache->palette));
cache->globalPaletteVersion = calloc(size, sizeof(*cache->globalPaletteVersion));
cache->palette = calloc(size * bpp, sizeof(*cache->palette));
}
void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {

View File

@ -174,7 +174,7 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector*
for (accum = dv; accum; accum = accum->next) {
++args;
}
const char** arglist = malloc(sizeof(const char*) * (args + 1));
const char** arglist = calloc(args + 1, sizeof(const char*));
args = 0;
for (accum = dv; accum; accum = accum->next) {
arglist[args] = accum->charValue;

View File

@ -387,6 +387,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
// QuickTime and a few other things require YUV420
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
}
#if LIBAVCODEC_VERSION_MAJOR >= 57
if (encoder->video->codec->id == AV_CODEC_ID_FFV1) {
av_opt_set(encoder->video->priv_data, "coder", "range_tab", 0);
}
#endif
if (strcmp(vcodec->name, "libx264") == 0) {
// Try to adaptively figure out when you can use a slower encoder
@ -545,8 +550,12 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
#endif
}
if (encoder->audio) {
#ifdef FFMPEG_USE_CODECPAR
avcodec_free_context(&encoder->audio);
#else
avcodec_close(encoder->audio);
encoder->audio = NULL;
#endif
}
if (encoder->resampleContext) {
@ -568,6 +577,7 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
}
if (encoder->videoFrame) {
av_freep(encoder->videoFrame->data);
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_free(&encoder->videoFrame);
#else
@ -585,8 +595,12 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
}
if (encoder->video) {
#ifdef FFMPEG_USE_CODECPAR
avcodec_free_context(&encoder->video);
#else
avcodec_close(encoder->video);
encoder->video = NULL;
#endif
}
if (encoder->scaleContext) {

View File

@ -286,8 +286,8 @@ static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, co
}
}
static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
struct GB* gb = core->board;
static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
const struct GB* gb = core->board;
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
*width = GB_VIDEO_HORIZONTAL_PIXELS;
*height = GB_VIDEO_VERTICAL_PIXELS;
@ -845,9 +845,13 @@ static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
vf->seek(vf, 0, SEEK_SET);
return vf->read(vf, *sram, vf->size(vf));
}
*sram = malloc(gb->sramSize);
memcpy(*sram, gb->memory.sram, gb->sramSize);
return gb->sramSize;
if (gb->sramSize) {
*sram = malloc(gb->sramSize);
memcpy(*sram, gb->memory.sram, gb->sramSize);
return gb->sramSize;
}
*sram = NULL;
return 0;
}
static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {

View File

@ -36,6 +36,7 @@ static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
@ -43,6 +44,7 @@ static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
static void _GBPocketCamCapture(struct GBMemory*);
@ -273,6 +275,7 @@ void GBMBCInit(struct GB* gb) {
gb->memory.mbcType = GB_MBC_NONE;
}
gb->memory.mbcRead = NULL;
gb->memory.directSramAccess = true;
switch (gb->memory.mbcType) {
case GB_MBC_NONE:
gb->memory.mbcWrite = _GBMBCNone;
@ -288,6 +291,7 @@ void GBMBCInit(struct GB* gb) {
case GB_MBC2:
gb->memory.mbcWrite = _GBMBC2;
gb->memory.mbcRead = _GBMBC2Read;
gb->memory.directSramAccess = false;
gb->sramSize = 0x100;
break;
case GB_MBC3:
@ -300,9 +304,9 @@ void GBMBCInit(struct GB* gb) {
gb->memory.mbcWrite = _GBMBC5;
break;
case GB_MBC6:
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
gb->memory.mbcWrite = _GBMBC6;
gb->memory.mbcRead = _GBMBC6Read;
gb->memory.directSramAccess = false;
break;
case GB_MBC7:
gb->memory.mbcWrite = _GBMBC7;
@ -342,6 +346,10 @@ void GBMBCInit(struct GB* gb) {
case GB_UNL_WISDOM_TREE:
gb->memory.mbcWrite = _GBWisdomTree;
break;
case GB_UNL_PKJD:
gb->memory.mbcWrite = _GBPKJD;
gb->memory.mbcRead = _GBPKJDRead;
break;
}
gb->memory.currentBank = 1;
@ -626,10 +634,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
case 0:
switch (value) {
case 0:
memory->mbcState.mbc6.sramAccess = false;
memory->sramAccess = false;
break;
case 0xA:
memory->mbcState.mbc6.sramAccess = true;
memory->sramAccess = true;
break;
default:
// TODO
@ -655,7 +663,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
case 0x29:
case 0x2A:
case 0x2B:
if (memory->mbcState.mbc6.sramAccess) {
if (memory->sramAccess) {
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
}
break;
@ -663,7 +671,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
case 0x2D:
case 0x2E:
case 0x2F:
if (memory->mbcState.mbc6.sramAccess) {
if (memory->sramAccess) {
memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
}
break;
@ -674,7 +682,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
}
uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
if (!memory->mbcState.mbc6.sramAccess) {
if (!memory->sramAccess) {
return 0xFF;
}
switch (address >> 12) {
@ -1229,6 +1237,74 @@ void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) {
}
}
void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory;
switch (address >> 13) {
case 0x2:
if (value < 8) {
memory->directSramAccess = true;
memory->activeRtcReg = 0;
} else if (value >= 0xD && value <= 0xF) {
memory->directSramAccess = false;
memory->rtcAccess = false;
memory->activeRtcReg = value - 8;
}
break;
case 0x5:
if (!memory->sramAccess) {
return;
}
switch (memory->activeRtcReg) {
case 0:
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
break;
case 5:
case 6:
memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value;
break;
case 7:
switch (value) {
case 0x11:
memory->mbcState.pkjd.reg[0]--;
break;
case 0x12:
memory->mbcState.pkjd.reg[1]--;
break;
case 0x41:
memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1];
break;
case 0x42:
memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0];
break;
case 0x51:
memory->mbcState.pkjd.reg[0]++;
break;
case 0x52:
memory->mbcState.pkjd.reg[1]--;
break;
}
break;
}
return;
}
_GBMBC3(gb, address, value);
}
static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
if (!memory->sramAccess) {
return 0xFF;
}
switch (memory->activeRtcReg) {
case 0:
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
case 5:
case 6:
return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5];
default:
return 0;
}
}
void GBMBCRTCRead(struct GB* gb) {
struct GBMBCRTCSaveBuffer rtcBuffer;
struct VFile* vf = gb->sramVf;

View File

@ -361,7 +361,7 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) {
case GB_REGION_EXTERNAL_RAM + 1:
if (memory->rtcAccess) {
memory->rtcRegs[memory->activeRtcReg] = value;
} else if (memory->sramAccess && memory->sram && memory->mbcType != GB_MBC2) {
} else if (memory->sramAccess && memory->sram && memory->directSramAccess) {
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
} else {
memory->mbcWrite(gb, address, value);

View File

@ -495,6 +495,7 @@ static const struct GBCartridgeOverride _overrides[] = {
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
{ 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
{ 0x30f8f86c, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
{ 0, 0, 0, { 0 } }
};

View File

@ -104,81 +104,86 @@ static void _RegisterRamReset(struct GBA* gba) {
memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32));
}
if (registers & 0x80) {
cpu->memory.store16(cpu, BASE_IO | 0x04, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x06, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x08, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x0A, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x0C, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x0E, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x10, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x12, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x14, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x16, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x18, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x1A, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x1C, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x1E, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DISPSTAT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_VCOUNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG0CNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG1CNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2CNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3CNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG0HOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG0VOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG1HOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG1VOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2HOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2VOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3HOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3VOFS, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0);
cpu->memory.store32(cpu, BASE_IO | 0x28, 0, 0);
cpu->memory.store32(cpu, BASE_IO | 0x2C, 0, 0);
cpu->memory.store32(cpu, BASE_IO | REG_BG2X_LO, 0, 0);
cpu->memory.store32(cpu, BASE_IO | REG_BG2Y_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0);
cpu->memory.store32(cpu, BASE_IO | 0x38, 0, 0);
cpu->memory.store32(cpu, BASE_IO | 0x3C, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x40, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x42, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x44, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x46, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x48, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x4A, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x4C, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x50, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x52, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x54, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xB0, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xB2, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xB4, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xB6, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xB8, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xBA, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xBC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xBE, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xC0, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xC2, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xC4, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xC6, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xC8, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xCA, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xCC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xCE, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xD0, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xD2, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xD4, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xD6, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xD8, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xDA, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xDC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0xDE, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x100, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x102, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x104, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x106, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x108, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x10A, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x10C, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x10E, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x200, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x202, 0xFFFF, 0);
cpu->memory.store16(cpu, BASE_IO | 0x204, 0, 0);
cpu->memory.store16(cpu, BASE_IO | 0x208, 0, 0);
cpu->memory.store32(cpu, BASE_IO | REG_BG3X_LO, 0, 0);
cpu->memory.store32(cpu, BASE_IO | REG_BG3Y_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WIN0H, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WIN1H, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WIN0V, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WIN1V, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WININ, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WINOUT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_MOSAIC, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BLDCNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BLDALPHA, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_BLDY, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_LO, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_HI, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_IE, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_IF, 0xFFFF, 0);
cpu->memory.store16(cpu, BASE_IO | REG_WAITCNT, 0, 0);
cpu->memory.store16(cpu, BASE_IO | REG_IME, 0, 0);
}
if (registers & 0x9C) {
gba->video.renderer->reset(gba->video.renderer);
gba->video.renderer->writeVideoRegister(gba->video.renderer, REG_DISPCNT, gba->memory.io[REG_DISPCNT >> 1]);
int i;
for (i = REG_BG0CNT; i < REG_SOUND1CNT_LO; i += 2) {
gba->video.renderer->writeVideoRegister(gba->video.renderer, i, gba->memory.io[i >> 1]);
}
}
}

View File

@ -396,9 +396,9 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
}
}
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
struct GBACore* gbacore = (struct GBACore*) core;
const struct GBACore* gbacore = (const struct GBACore*) core;
int scale = gbacore->glRenderer.scale;
#else
UNUSED(core);

View File

@ -288,7 +288,7 @@ static void _eReaderAddress(uint8_t* origin, int a) {
}
static void _eReaderReedSolomon(const uint8_t* input, uint8_t* output) {
uint8_t rsBuffer[64] = {};
uint8_t rsBuffer[64] = { 0 };
int i;
for (i = 0; i < 48; ++i) {
rsBuffer[63 - i] = input[i];

View File

@ -29,6 +29,7 @@ static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait);
static int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra);
static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 };
static const char GBA_BASE_WAITSTATES_32[16] = { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 9 };
@ -337,33 +338,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
}
#define LOAD_BAD \
if (gba->performingDMA || cpu->gprs[ARM_PC] - gba->dmaPC == (gba->cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM)) { \
value = gba->bus; \
} else { \
value = cpu->prefetch[1]; \
if (cpu->executionMode == MODE_THUMB) { \
/* http://ngemu.com/threads/gba-open-bus.170809/ */ \
switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) { \
case REGION_BIOS: \
case REGION_OAM: \
/* This isn't right half the time, but we don't have $+6 handy */ \
value <<= 16; \
value |= cpu->prefetch[0]; \
break; \
case REGION_WORKING_IRAM: \
/* This doesn't handle prefetch clobbering */ \
if (cpu->gprs[ARM_PC] & 2) { \
value <<= 16; \
value |= cpu->prefetch[0]; \
} else { \
value |= cpu->prefetch[0] << 16; \
} \
break; \
default: \
value |= value << 16; \
} \
} \
}
value = GBALoadBad(cpu);
#define LOAD_BIOS \
if (address < SIZE_BIOS) { \
@ -375,7 +350,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
} \
} else { \
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load32: 0x%08X", address); \
LOAD_BAD; \
value = GBALoadBad(cpu); \
}
#define LOAD_WORKING_RAM \
@ -400,7 +375,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
} else { \
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
} \
wait += waitstatesRegion[REGION_VRAM];
++wait; \
if (gba->video.shouldStall) { \
wait += GBAMemoryStallVRAM(gba, wait, 1); \
}
#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw);
@ -427,7 +405,33 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
uint32_t GBALoadBad(struct ARMCore* cpu) {
struct GBA* gba = (struct GBA*) cpu->master;
uint32_t value = 0;
LOAD_BAD;
if (gba->performingDMA || cpu->gprs[ARM_PC] - gba->dmaPC == (gba->cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM)) {
value = gba->bus;
} else {
value = cpu->prefetch[1];
if (cpu->executionMode == MODE_THUMB) {
/* http://ngemu.com/threads/gba-open-bus.170809/ */
switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) {
case REGION_BIOS:
case REGION_OAM:
/* This isn't right half the time, but we don't have $+6 handy */
value <<= 16;
value |= cpu->prefetch[0];
break;
case REGION_WORKING_IRAM:
/* This doesn't handle prefetch clobbering */
if (cpu->gprs[ARM_PC] & 2) {
value <<= 16;
value |= cpu->prefetch[0];
} else {
value |= cpu->prefetch[0] << 16;
}
break;
default:
value |= value << 16;
}
}
}
return value;
}
@ -507,8 +511,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
}
} else {
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load16: 0x%08X", address);
LOAD_BAD;
value = (value >> ((address & 2) * 8)) & 0xFFFF;
value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF;
}
break;
case REGION_WORKING_RAM:
@ -535,6 +538,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
} else {
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
}
if (gba->video.shouldStall) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
case REGION_OAM:
LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
@ -591,8 +597,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
break;
default:
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load16: 0x%08X", address);
LOAD_BAD;
value = (value >> ((address & 2) * 8)) & 0xFFFF;
value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF;
break;
}
@ -625,8 +630,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
}
} else {
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load8: 0x%08x", address);
LOAD_BAD;
value = (value >> ((address & 3) * 8)) & 0xFF;
value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF;
}
break;
case REGION_WORKING_RAM:
@ -653,6 +657,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
} else {
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
}
if (gba->video.shouldStall) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
case REGION_OAM:
value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)];
@ -701,8 +708,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
break;
default:
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load8: 0x%08x", address);
LOAD_BAD;
value = (value >> ((address & 3) * 8)) & 0xFF;
value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF;
break;
}
@ -755,7 +761,10 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
} \
} \
wait += waitstatesRegion[REGION_VRAM];
++wait; \
if (gba->video.shouldStall) { \
wait += GBAMemoryStallVRAM(gba, wait, 1); \
}
#define STORE_OAM \
LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \
@ -880,6 +889,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
}
}
if (gba->video.shouldStall) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
case REGION_OAM:
LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw);
@ -981,6 +993,9 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
gba->video.vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
}
if (gba->video.shouldStall) {
wait += GBAMemoryStallVRAM(gba, wait, 0);
}
break;
case REGION_OAM:
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);
@ -1662,6 +1677,28 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
return wait;
}
int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra) {
UNUSED(extra);
// TODO
uint16_t dispcnt = gba->memory.io[REG_DISPCNT >> 1];
int32_t stall = 0;
switch (GBARegisterDISPCNTGetMode(dispcnt)) {
case 2:
if (GBARegisterDISPCNTIsBg2Enable(dispcnt) && GBARegisterDISPCNTIsBg3Enable(dispcnt)) {
// If both backgrounds are enabled, VRAM access is entirely blocked during hdraw
stall = mTimingUntil(&gba->timing, &gba->video.event);
}
break;
default:
return 0;
}
stall -= wait;
if (stall < 0) {
return 0;
}
return stall;
}
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) {
memcpy(state->wram, memory->wram, SIZE_WORKING_RAM);
memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM);

View File

@ -8,6 +8,17 @@
#include <mgba/core/interface.h>
#include <mgba/internal/gba/gba.h>
#define BACKGROUND_BITMAP_ITERATE(W, H) \
x += background->dx; \
y += background->dy; \
\
if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
continue; \
} else { \
localX = x; \
localY = y; \
}
#define MODE_2_COORD_OVERFLOW \
localX = x & (sizeAdjusted - 1); \
localY = y & (sizeAdjusted - 1); \
@ -123,7 +134,7 @@ void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer
void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
BACKGROUND_BITMAP_INIT;
uint16_t color = renderer->normalPalette[0];
uint16_t color = 0;
uint32_t offset = 0;
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
offset = 0xA000;

View File

@ -222,17 +222,6 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
UNUSED(palette); \
PREPARE_OBJWIN;
#define BACKGROUND_BITMAP_ITERATE(W, H) \
x += background->dx; \
y += background->dy; \
\
if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
continue; \
} else { \
localX = x; \
localY = y; \
}
#define TEST_LAYER_ENABLED(X) \
(softwareRenderer->bg[X].enabled == 3 && \
(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \

View File

@ -139,6 +139,7 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
} else if (savedata->vf) {
off_t read = 0;
uint8_t buffer[2048];
savedata->vf->seek(savedata->vf, 0, SEEK_SET);
do {
read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
out->write(out, buffer, read);

View File

@ -107,6 +107,7 @@ void GBAVideoReset(struct GBAVideo* video) {
video->renderer->vramOBJ[1] = &video->vram[0xA000];
video->renderer->vramOBJ[2] = _zeroes;
video->renderer->vramOBJ[3] = _zeroes;
video->shouldStall = 0;
memset(video->palette, 0, sizeof(video->palette));
memset(video->oam.raw, 0, sizeof(video->oam.raw));
@ -160,6 +161,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
video->shouldStall = 1;
}
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
@ -219,6 +221,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate);
}
video->shouldStall = 0;
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
}
@ -359,6 +362,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
}
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
video->shouldStall = 0;
int32_t flags;
LOAD_32(flags, 0, &state->video.flags);
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
@ -375,6 +379,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
break;
case 2:
video->event.callback = _startHblank;
video->shouldStall = 1;
break;
case 3:
video->event.callback = _midHblank;

View File

@ -31,7 +31,7 @@ struct GUIFont* GUIFontCreate(void) {
guiFont->font = fontGetSystemFont();
TGLP_s* glyphInfo = fontGetGlyphInfo(guiFont->font);
guiFont->size = FONT_SIZE / glyphInfo->cellHeight;
guiFont->sheets = malloc(sizeof(*guiFont->sheets) * glyphInfo->nSheets);
guiFont->sheets = calloc(glyphInfo->nSheets, sizeof(*guiFont->sheets));
int i;
for (i = 0; i < glyphInfo->nSheets; ++i) {

View File

@ -2,14 +2,6 @@
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIRS, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARIES variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
@ -109,21 +101,19 @@ FIND_LIBRARY(SDL2_LIBRARIES_TEMP
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIRS}/../..
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
@ -141,13 +131,6 @@ IF(MINGW)
ENDIF(MINGW)
IF(SDL2_LIBRARIES_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARIES_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARIES_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.

View File

@ -921,7 +921,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
success = false;
}
if (success) {
struct mGLES2Shader* shaderBlock = malloc(sizeof(struct mGLES2Shader) * inShaders);
struct mGLES2Shader* shaderBlock = calloc(inShaders, sizeof(struct mGLES2Shader));
int n;
for (n = 0; n < inShaders; ++n) {
char passName[12];
@ -980,7 +980,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
}
}
u = mGLES2UniformListSize(&uniformVector);
struct mGLES2Uniform* uniformBlock = malloc(sizeof(*uniformBlock) * u);
struct mGLES2Uniform* uniformBlock = calloc(u, sizeof(*uniformBlock));
memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
mGLES2UniformListDeinit(&uniformVector);

View File

@ -46,6 +46,10 @@ if(APPLE)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
endif()
get_target_property(QT_TYPE Qt5::Core TYPE)
if(QT_TYPE STREQUAL STATIC_LIBRARY)
set(QT_STATIC ON)
@ -285,6 +289,10 @@ qt5_wrap_ui(UI_SRC ${UI_FILES})
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}" COMPILE_OPTIONS "${FEATURE_FLAGS}")
if(WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network)
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})

View File

@ -83,6 +83,7 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr
mCore* core = mCoreFindVF(vf);
if (!core) {
vf->close(vf);
LOG(QT, ERROR) << tr("Could not load game. Are you sure it's in the correct format?");
return nullptr;
}

View File

@ -386,12 +386,6 @@ void FrameView::refreshVl() {
m_nextFrame = VFileMemChunk(nullptr, 0);
if (m_currentFrame) {
m_controller->endVideoLog(false);
VFile* currentFrame = VFileMemChunk(nullptr, m_currentFrame->size(m_currentFrame));
void* buffer = currentFrame->map(currentFrame, m_currentFrame->size(m_currentFrame), MAP_WRITE);
m_currentFrame->seek(m_currentFrame, 0, SEEK_SET);
m_currentFrame->read(m_currentFrame, buffer, m_currentFrame->size(m_currentFrame));
currentFrame->unmap(currentFrame, buffer, m_currentFrame->size(m_currentFrame));
m_currentFrame = currentFrame;
QMetaObject::invokeMethod(this, "newVl");
}
m_controller->endVideoLog();
@ -403,12 +397,16 @@ void FrameView::newVl() {
m_glowTimer.start();
}
QMutexLocker locker(&m_mutex);
if (!m_currentFrame) {
return;
}
if (m_vl) {
m_vl->deinit(m_vl);
}
m_vl = mCoreFindVF(m_currentFrame);
m_vl->init(m_vl);
m_vl->loadROM(m_vl, m_currentFrame);
m_currentFrame = nullptr;
mCoreInitConfig(m_vl, nullptr);
unsigned width, height;
m_vl->desiredVideoDimensions(m_vl, &width, &height);

View File

@ -93,6 +93,10 @@ MemoryModel::MemoryModel(QWidget* parent)
++m_top;
} else if (action == QSlider::SliderSingleStepSub) {
--m_top;
} else if (action == QSlider::SliderPageStepAdd) {
m_top += (viewport()->size().height() - m_cellHeight) / m_cellHeight;
} else if (action == QSlider::SliderPageStepSub) {
m_top -= (viewport()->size().height() - m_cellHeight) / m_cellHeight;
} else {
return;
}
@ -590,6 +594,12 @@ void MemoryModel::keyPressEvent(QKeyEvent* event) {
case Qt::Key_Down:
adjustCursor(16, event->modifiers() & Qt::ShiftModifier);
return;
case Qt::Key_PageUp:
adjustCursor(-16 * ((viewport()->size().height() - m_cellHeight) / m_cellHeight), event->modifiers() & Qt::ShiftModifier);
return;
case Qt::Key_PageDown:
adjustCursor(16 * ((viewport()->size().height() - m_cellHeight) / m_cellHeight), event->modifiers() & Qt::ShiftModifier);
return;
default:
return;
}

View File

@ -49,6 +49,8 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
s_mbcList.append(GB_TAMA5);
s_mbcList.append(GB_HuC1);
s_mbcList.append(GB_HuC3);
s_mbcList.append(GB_UNL_WISDOM_TREE);
s_mbcList.append(GB_UNL_PKJD);
}
if (s_gbModelList.isEmpty()) {
// NB: Keep in sync with OverrideView.ui

View File

@ -354,6 +354,16 @@
<string>HuC-3</string>
</property>
</item>
<item>
<property name="text">
<string>Wisdom Tree (Unlicensed)</string>
</property>
</item>
<item>
<property name="text">
<string>Pokémon Jade/Diamond (Unlicensed)</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">

View File

@ -12,6 +12,8 @@ using namespace QGBA;
RotatedHeaderView::RotatedHeaderView(Qt::Orientation orientation, QWidget* parent)
: QHeaderView(orientation, parent)
{
int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
setMinimumSectionSize(fontMetrics().height() + margin);
}
void RotatedHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const {

View File

@ -394,6 +394,7 @@ void SettingsView::updateConfig() {
saveSetting("showFps", m_ui.showFps);
saveSetting("cheatAutoload", m_ui.cheatAutoload);
saveSetting("cheatAutosave", m_ui.cheatAutosave);
saveSetting("showFilename", m_ui.showFilename);
saveSetting("autoload", m_ui.autoload);
saveSetting("autosave", m_ui.autosave);
saveSetting("logToFile", m_ui.logToFile);
@ -573,6 +574,7 @@ void SettingsView::reloadConfig() {
loadSetting("showFps", m_ui.showFps, true);
loadSetting("cheatAutoload", m_ui.cheatAutoload, true);
loadSetting("cheatAutosave", m_ui.cheatAutosave, true);
loadSetting("showFilename", m_ui.showFilename, false);
loadSetting("autoload", m_ui.autoload, true);
loadSetting("autosave", m_ui.autosave, false);
loadSetting("logToFile", m_ui.logToFile);

View File

@ -586,21 +586,21 @@
</property>
</widget>
</item>
<item row="13" column="1">
<item row="14" column="1">
<widget class="QCheckBox" name="useDiscordPresence">
<property name="text">
<string>Enable Discord Rich Presence</string>
</property>
</widget>
</item>
<item row="14" column="0" colspan="2">
<item row="15" column="0" colspan="2">
<widget class="Line" name="line_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="15" column="1">
<item row="16" column="1">
<widget class="QCheckBox" name="autosave">
<property name="text">
<string>Automatically save state</string>
@ -610,7 +610,7 @@
</property>
</widget>
</item>
<item row="16" column="1">
<item row="17" column="1">
<widget class="QCheckBox" name="autoload">
<property name="text">
<string>Automatically load state</string>
@ -620,14 +620,14 @@
</property>
</widget>
</item>
<item row="17" column="0" colspan="2">
<item row="18" column="0" colspan="2">
<widget class="Line" name="line_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="18" column="1">
<item row="19" column="1">
<widget class="QCheckBox" name="cheatAutosave">
<property name="text">
<string>Automatically save cheats</string>
@ -637,7 +637,7 @@
</property>
</widget>
</item>
<item row="19" column="1">
<item row="20" column="1">
<widget class="QCheckBox" name="cheatAutoload">
<property name="text">
<string>Automatically load cheats</string>
@ -647,7 +647,7 @@
</property>
</widget>
</item>
<item row="12" column="1">
<item row="13" column="1">
<widget class="QCheckBox" name="showOSD">
<property name="text">
<string>Show OSD messages</string>
@ -657,6 +657,16 @@
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QCheckBox" name="showFilename">
<property name="text">
<string>Show filename instead of ROM name in title bar</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="emulation">

View File

@ -80,7 +80,7 @@ QModelIndex ShortcutModel::index(int row, int column, const QModelIndex& parent)
pmenu = static_cast<Item*>(parent.internalPointer())->name;
}
QString name = m_controller->name(row, pmenu);
Item* item = &(*const_cast<QHash<QString, Item>*>(&m_cache))[name];
Item* item = &m_cache[name];
item->name = name;
item->shortcut = m_controller->shortcut(name);
return createIndex(row, column, item);
@ -95,7 +95,7 @@ QModelIndex ShortcutModel::parent(const QModelIndex& index) const {
if (parent.isNull()) {
return QModelIndex();
}
Item* pitem = &(*const_cast<QHash<QString, Item>*>(&m_cache))[parent];
Item* pitem = &m_cache[parent];
pitem->name = parent;
pitem->shortcut = m_controller->shortcut(parent);
return createIndex(m_controller->indexIn(parent), 0, pitem);

View File

@ -44,7 +44,7 @@ private:
const Shortcut* shortcut = nullptr;
};
QHash<QString, Item> m_cache;
mutable QHash<QString, Item> m_cache;
};
}

View File

@ -99,12 +99,11 @@ VideoView::VideoView(QWidget* parent)
updatePresets();
setPreset({
.container = "MKV",
.vcodec = "h.264",
.acodec = "FLAC",
.vbr = -1,
.abr = 0,
.dims = QSize(),
"MKV",
"h.264",
"FLAC",
-1,
0,
});
showAdvanced(false);
}
@ -112,96 +111,58 @@ VideoView::VideoView(QWidget* parent)
void VideoView::updatePresets() {
m_presets.clear();
addPreset(m_ui.preset4K, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = maintainAspect(QSize(3840, 2160))
});
addPreset(m_ui.preset1080, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = maintainAspect(QSize(1920, 1080))
});
addPreset(m_ui.preset720, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = maintainAspect(QSize(1280, 720))
});
addPreset(m_ui.preset480, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = maintainAspect(QSize(720, 480))
});
addPreset(m_ui.preset4K, { maintainAspect(QSize(3840, 2160)) });
addPreset(m_ui.preset1080, { maintainAspect(QSize(1920, 1080)) });
addPreset(m_ui.preset720, { maintainAspect(QSize(1280, 720)) });
addPreset(m_ui.preset480, { maintainAspect(QSize(720, 480)) });
if (m_nativeWidth && m_nativeHeight) {
addPreset(m_ui.presetNative, {
.container = QString(),
.vcodec = QString(),
.acodec = QString(),
.vbr = 0,
.abr = 0,
.dims = QSize(m_nativeWidth, m_nativeHeight)
});
addPreset(m_ui.presetNative, { QSize(m_nativeWidth, m_nativeHeight) });
m_ui.presetNative->setEnabled(true);
}
addPreset(m_ui.presetHQ, {
.container = "MP4",
.vcodec = "h.264",
.acodec = "AAC",
.vbr = 8000,
.abr = 384,
.dims = maintainAspect(QSize(1920, 1080))
"MP4",
"h.264",
"AAC",
8000,
384,
maintainAspect({ 1920, 1080 })
});
addPreset(m_ui.presetYoutube, {
.container = "MP4",
.vcodec = "h.264",
.acodec = "AAC",
.vbr = 5000,
.abr = 256,
.dims = maintainAspect(QSize(1280, 720))
"MP4",
"h.264",
"AAC",
5000,
256,
maintainAspect({ 1280, 720 })
});
addPreset(m_ui.presetWebM, {
.container = "WebM",
.vcodec = "VP9",
.acodec = "Opus",
.vbr = 800,
.abr = 128
"WebM",
"VP9",
"Opus",
800,
128
});
addPreset(m_ui.presetMP4, {
.container = "MP4",
.vcodec = "h.264",
.acodec = "AAC",
.vbr = 800,
.abr = 128
"MP4",
"h.264",
"AAC",
800,
128
});
if (m_nativeWidth && m_nativeHeight) {
addPreset(m_ui.presetLossless, {
.container = "MKV",
.vcodec = "h.264",
.acodec = "FLAC",
.vbr = -1,
.abr = 0,
.dims = QSize(m_nativeWidth, m_nativeHeight)
"MKV",
"h.264",
"FLAC",
-1,
0,
{ m_nativeWidth, m_nativeHeight }
});
}
}
@ -455,12 +416,12 @@ void VideoView::updateAspectRatio(int width, int height, bool force) {
void VideoView::uncheckIncompatible() {
Preset current = {
.container = m_container,
.vcodec = m_videoCodec,
.acodec = m_audioCodec,
.vbr = m_vbr / 1000,
.abr = m_abr / 1000,
.dims = QSize(m_width, m_height)
m_container,
m_videoCodec,
m_audioCodec,
m_vbr / 1000,
m_abr / 1000,
{ m_width, m_height }
};
m_ui.presets->setExclusive(false);

View File

@ -71,6 +71,15 @@ private:
int abr;
QSize dims;
Preset() {}
Preset(QString container, QString vcodec, QString acodec, int vbr, int abr, QSize dims = QSize())
: container(container)
, vcodec(vcodec)
, acodec(acodec)
, vbr(vbr)
, abr(abr)
, dims(dims) {}
Preset(QSize dims) : dims(dims) {}
bool compatible(const Preset&) const;
};

View File

@ -1096,18 +1096,25 @@ void Window::updateTitle(float fps) {
const NoIntroDB* db = GBAApp::app()->gameDB();
NoIntroGame game{};
uint32_t crc32 = 0;
m_controller->thread()->core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32);
char gameTitle[17] = { '\0' };
mCore* core = m_controller->thread()->core;
core->getGameTitle(core, gameTitle);
title = gameTitle;
core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32);
QString filePath = windowFilePath();
if (m_config->getOption("showFilename").toInt() && !filePath.isNull()) {
QFileInfo fileInfo(filePath);
title = fileInfo.fileName();
} else {
char gameTitle[17] = { '\0' };
core->getGameTitle(core, gameTitle);
title = gameTitle;
#ifdef USE_SQLITE3
if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) {
title = QLatin1String(game.name);
}
if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) {
title = QLatin1String(game.name);
}
#endif
}
MultiplayerController* multiplayer = m_controller->multiplayerController();
if (multiplayer && multiplayer->attached() > 1) {
title += tr(" - Player %1 of %2").arg(multiplayer->playerId(m_controller.get()) + 1).arg(multiplayer->attached());

View File

@ -4495,7 +4495,7 @@ Game Boy Advance è un marchio registrato di Nintendo Co., Ltd.</translation>
<message>
<location filename="../SensorView.ui" line="97"/>
<source>MM/dd/yy hh:mm:ss AP</source>
<translation>gg/MM/aa OO:mm:ss</translation>
<translation>dd/MM/yy HH:mm:ss</translation>
</message>
<message>
<location filename="../SensorView.ui" line="107"/>

View File

@ -114,6 +114,8 @@ set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATU
target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
if(NOT WIN32)
set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME})
else()
set_target_properties(${BINARY_NAME}-sdl PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
if (WIN32 AND MSVC)
set_target_properties(${BINARY_NAME}-sdl PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE")

View File

@ -0,0 +1,39 @@
if(BUILD_PERF)
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/perf-main.c)
if(UNIX AND NOT APPLE)
list(APPEND PERF_LIB rt)
endif()
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB} ${OS_LIB})
set_target_properties(${BINARY_NAME}-perf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
install(TARGETS ${BINARY_NAME}-perf DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-perf)
install(FILES "${CMAKE_SOURCE_DIR}/tools/perf.py" DESTINATION "${LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)
endif()
if(BUILD_TEST)
add_executable(${BINARY_NAME}-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/fuzz-main.c)
target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME})
set_target_properties(${BINARY_NAME}-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
add_executable(tbl-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/tbl-fuzz-main.c)
target_link_libraries(tbl-fuzz ${BINARY_NAME})
set_target_properties(tbl-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
install(TARGETS ${BINARY_NAME}-fuzz tbl-fuzz DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-test)
endif()
if(BUILD_SUITE)
enable_testing()
include_directories(AFTER ${CMOCKA_INCLUDE_DIRS})
link_directories(${CMOCKA_LIBRARY_DIRS})
foreach(TEST IN LISTS TEST_SRC)
string(REPLACE "${CMAKE_SOURCE_DIR}/src/" "" TEST_NAME "${TEST}")
string(REPLACE "/" "-" TEST_NAME "${TEST_NAME}")
string(REPLACE "-test" "" TEST_NAME "${TEST_NAME}")
string(REPLACE ".c" "" TEST_NAME "${TEST_NAME}")
add_executable(test-${TEST_NAME} ${TEST})
target_link_libraries(test-${TEST_NAME} ${BINARY_NAME} ${PLATFORM_LIBRARY} cmocka)
set_target_properties(test-${TEST_NAME} PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
add_test(${TEST_NAME} test-${TEST_NAME})
endforeach()
endif()

View File

@ -78,8 +78,8 @@ Name: "sgbfileassoc"; Description: "{cm:AssocFileExtension,{#AppName},Super Game
Name: "gbafileassoc"; Description: "{cm:AssocFileExtension,{#AppName},Game Boy Advance}"; GroupDescription: "{cm:FileAssoc}"
[Files]
Source: "{#BinDir}\qt\{#AppName}.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BinDir}\sdl\{#AppName2}-sdl.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BinDir}\{#AppName}.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BinDir}\{#AppName2}-sdl.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BinDir}\CHANGES.txt"; DestDir: "{app}\"; Flags: ignoreversion isreadme
Source: "{#BinDir}\LICENSE.txt"; DestDir: "{app}\"; Flags: ignoreversion
Source: "{#ResDir}\nointro.dat"; DestDir: "{app}\"; Flags: ignoreversion

View File

@ -7,6 +7,7 @@
#include <mgba/internal/sm83/emitter-sm83.h>
#include <mgba/internal/sm83/sm83.h>
#include <mgba-util/string.h>
typedef size_t (*SM83Decoder)(uint8_t opcode, struct SM83InstructionInfo* info);
@ -504,39 +505,39 @@ static int _decodeOperand(struct SM83Operand op, uint16_t pc, char* buffer, int
return 0;
}
strncpy(buffer, " ", blen - 1);
strlcpy(buffer, " ", blen);
ADVANCE(1);
if (op.flags & SM83_OP_FLAG_MEMORY) {
strncpy(buffer, "[", blen - 1);
strlcpy(buffer, "[", blen);
ADVANCE(1);
}
if (op.reg) {
int written = snprintf(buffer, blen - 1, "%s", _sm83Registers[op.reg]);
int written = snprintf(buffer, blen, "%s", _sm83Registers[op.reg]);
ADVANCE(written);
} else {
int written;
if (op.flags & SM83_OP_FLAG_RELATIVE) {
written = snprintf(buffer, blen - 1, "$%04X", pc + (int8_t) op.immediate);
written = snprintf(buffer, blen, "$%04X", pc + (int8_t) op.immediate);
} else {
written = snprintf(buffer, blen - 1, "$%02X", op.immediate);
written = snprintf(buffer, blen, "$%02X", op.immediate);
}
ADVANCE(written);
if (op.reg) {
strncpy(buffer, "+", blen - 1);
strlcpy(buffer, "+", blen);
ADVANCE(1);
}
}
if (op.flags & SM83_OP_FLAG_INCREMENT) {
strncpy(buffer, "+", blen - 1);
strlcpy(buffer, "+", blen);
ADVANCE(1);
}
if (op.flags & SM83_OP_FLAG_DECREMENT) {
strncpy(buffer, "-", blen - 1);
strlcpy(buffer, "-", blen);
ADVANCE(1);
}
if (op.flags & SM83_OP_FLAG_MEMORY) {
strncpy(buffer, "]", blen - 1);
strlcpy(buffer, "]", blen);
ADVANCE(1);
}
return total;
@ -548,15 +549,15 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
int total = 0;
const char* cond = _sm83Conditions[info->condition];
written = snprintf(buffer, blen - 1, "%s", mnemonic);
written = snprintf(buffer, blen, "%s", mnemonic);
ADVANCE(written);
if (cond) {
written = snprintf(buffer, blen - 1, " %s", cond);
written = snprintf(buffer, blen, " %s", cond);
ADVANCE(written);
if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) {
strncpy(buffer, ",", blen - 1);
strlcpy(buffer, ",", blen);
ADVANCE(1);
}
}
@ -568,7 +569,7 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) {
if (written) {
strncpy(buffer, ",", blen - 1);
strlcpy(buffer, ",", blen);
ADVANCE(1);
}
written = _decodeOperand(info->op2, pc, buffer, blen);

0
src/third-party/inih/LICENSE.txt vendored Executable file → Normal file
View File

61
src/third-party/inih/README.md vendored Executable file → Normal file
View File

@ -1,20 +1,40 @@
# inih (INI Not Invented Here)
[![TravisCI Build](https://travis-ci.org/benhoyt/inih.svg)](https://travis-ci.org/benhoyt/inih)
**inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries.
To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation.
You can also call `ini_parse_file()` to parse directly from a `FILE*` object, or `ini_parse_stream()` to parse using a custom reader to implement string-based or other custom I/O ([see example code](https://github.com/benhoyt/inih/blob/master/examples/ini_buffer.c)).
You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O.
Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros.
## Compile-time options ##
You can control various aspects of inih using preprocessor defines:
### Syntax options ###
* **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`.
* **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`.
* **Stack vs heap:** By default, inih allocates its line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`.
* **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`.
* **Maximum line length:** The default maximum line length is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`.
* **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`.
* **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`.
* **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL.
### Parsing options ###
* **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`.
* **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`.
* **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL.
### Memory options ###
* **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`.
* **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL).
* **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes.
* **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`.
## Simple example in C ##
@ -102,3 +122,36 @@ Some differences between inih and Python's [ConfigParser](http://docs.python.org
* INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error.
* Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser).
## Platform-specific notes ##
* Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling.
## Meson notes ##
* The `meson.build` file is not required to use or compile inih, its main purpose is for distributions.
* By default Meson only creates a static library for inih, but Meson can be used to configure this behavior:
* with `-Ddefault_library=shared` a shared library is build.
* with `-Ddistro_install=true` the library will be installed with the header and a pkg-config entry, you may want to set `-Ddefault_library=shared` when using this.
* with `-Dwith_INIReader` you can build (and install if selected) the C++ library.
* all compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`.
* If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`.
* In case you use inih as a subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables.
## Building from vcpkg ##
You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install inih
The inih port in vcpkg is kept up to date by microsoft team members and community contributors.
If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Related links ##
* [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager)

157
src/third-party/inih/ini.c vendored Executable file → Normal file
View File

@ -1,5 +1,9 @@
/* inih -- simple .INI file parser
SPDX-License-Identifier: BSD-3-Clause
Copyright (C) 2009-2020, Ben Hoyt
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
@ -7,7 +11,7 @@ https://github.com/benhoyt/inih
*/
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
@ -24,6 +28,12 @@ https://github.com/benhoyt/inih
#define MAX_SECTION 128
#define MAX_NAME 128
/* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct {
const char* ptr;
size_t num_left;
} ini_parse_string_ctx;
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
@ -41,24 +51,35 @@ static char* lskip(const char* s)
return (char*)s;
}
/* Return pointer to first char c or ';' comment in given string, or pointer to
null at end of string if neither found. ';' must be prefixed by a whitespace
character to register as a comment. */
static char* find_char_or_comment(const char* s, char c)
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to NUL at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
int was_whitespace = 0;
while (*s && *s != c && !(was_whitespace && *s == ';')) {
was_whitespace = isspace((unsigned char)(*s));
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
/* Similar to strncpy, but ensures dest (size bytes) is
NUL-terminated, and doesn't pad with NULs. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
size_t i;
for (i = 0; i < size - 1 && src[i]; i++)
dest[i] = src[i];
dest[i] = '\0';
return dest;
}
@ -69,8 +90,14 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
#else
char* line;
size_t max_line = INI_INITIAL_ALLOC;
#endif
#if INI_ALLOW_REALLOC && !INI_USE_STACK
char* new_line;
size_t offset;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
@ -83,14 +110,40 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_MAX_LINE);
line = (char*)malloc(INI_INITIAL_ALLOC);
if (!line) {
return -2;
}
#endif
#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif
/* Scan through stream line by line */
while (reader(line, INI_MAX_LINE, stream) != NULL) {
while (reader(line, (int)max_line, stream) != NULL) {
#if INI_ALLOW_REALLOC && !INI_USE_STACK
offset = strlen(line);
while (offset == max_line - 1 && line[offset - 1] != '\n') {
max_line *= 2;
if (max_line > INI_MAX_LINE)
max_line = INI_MAX_LINE;
new_line = realloc(line, max_line);
if (!new_line) {
free(line);
return -2;
}
line = new_line;
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
break;
if (max_line >= INI_MAX_LINE)
break;
offset += strlen(line + offset);
}
#endif
lineno++;
start = line;
@ -103,53 +156,64 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
#endif
start = lskip(rstrip(start));
if (*start == ';' || *start == '#') {
/* Per Python ConfigParser, allow '#' comments at start of line */
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
/* Start-of-line comment */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-black line with leading whitespace, treat as continuation
of previous name's value (as per Python ConfigParser). */
if (!handler(user, section, prev_name, start) && !error)
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!HANDLER(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_char_or_comment(start + 1, ']');
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
#if INI_CALL_HANDLER_ON_NEW_SECTION
if (!HANDLER(user, section, NULL, NULL) && !error)
error = lineno;
#endif
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start && *start != ';') {
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_char_or_comment(start, '=');
if (*end != '=') {
end = find_char_or_comment(start, ':');
}
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = lskip(end + 1);
end = find_char_or_comment(value, '\0');
if (*end == ';')
value = end + 1;
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
value = lskip(value);
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
#if INI_ALLOW_NO_VALUE
*end = '\0';
name = rstrip(start);
if (!HANDLER(user, section, name, NULL) && !error)
error = lineno;
#else
error = lineno;
#endif
}
}
@ -185,3 +249,40 @@ int ini_parse(const char* filename, ini_handler handler, void* user)
fclose(file);
return error;
}
/* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) {
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
const char* ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left;
char* strp = str;
char c;
if (ctx_num_left == 0 || num < 2)
return NULL;
while (num > 1 && ctx_num_left != 0) {
c = *ctx_ptr++;
ctx_num_left--;
*strp++ = c;
if (c == '\n')
break;
num--;
}
*strp = '\0';
ctx->ptr = ctx_ptr;
ctx->num_left = ctx_num_left;
return str;
}
/* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) {
ini_parse_string_ctx ctx;
ctx.ptr = string;
ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
user);
}

81
src/third-party/inih/ini.h vendored Executable file → Normal file
View File

@ -1,5 +1,9 @@
/* inih -- simple .INI file parser
SPDX-License-Identifier: BSD-3-Clause
Copyright (C) 2009-2020, Ben Hoyt
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
@ -17,9 +21,20 @@ extern "C" {
#include <stdio.h>
/* Nonzero if ini_handler callback should accept lineno parameter. */
#ifndef INI_HANDLER_LINENO
#define INI_HANDLER_LINENO 0
#endif
/* Typedef for prototype of handler function. */
#if INI_HANDLER_LINENO
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value,
int lineno);
#else
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
#endif
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
@ -27,7 +42,7 @@ typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's ConfigParser.
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
@ -44,36 +59,86 @@ int ini_parse(const char* filename, ini_handler handler, void* user);
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O. */
filename. Used for implementing custom or string-based I/O (see also
ini_parse_string). */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
instead of a file. Useful for parsing INI data from a network socket or
already in memory. */
int ini_parse_string(const char* string, ini_handler handler, void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
ConfigParser. If allowed, ini_parse() will call the handler with the same
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
the file. See https://github.com/benhoyt/inih/issues/21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Nonzero to use stack, zero to use heap (malloc/free). */
/* Chars that begin a start-of-line comment. Per Python configparser, allow
both ; and # comments at the start of a line by default. */
#ifndef INI_START_COMMENT_PREFIXES
#define INI_START_COMMENT_PREFIXES ";#"
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file (stack or heap). Note that
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
zero. */
#ifndef INI_ALLOW_REALLOC
#define INI_ALLOW_REALLOC 0
#endif
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
is zero. */
#ifndef INI_INITIAL_ALLOC
#define INI_INITIAL_ALLOC 200
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
/* Maximum line length for any line in INI file. */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
/* Nonzero to call the handler at the start of each new section (with
name and value NULL). Default is to only call the handler on
each name=value pair. */
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
#endif
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
call the handler with value NULL in this case. Default is to treat
no-value lines as an error. */
#ifndef INI_ALLOW_NO_VALUE
#define INI_ALLOW_NO_VALUE 0
#endif
#ifdef __cplusplus

View File

@ -7,6 +7,7 @@
#include <mgba-util/gui/font.h>
#include <mgba-util/gui/menu.h>
#include <mgba-util/string.h>
#include <mgba-util/vfs.h>
#include <stdlib.h>
@ -200,7 +201,7 @@ bool GUISelectFile(struct GUIParams* params, char* outPath, size_t outLen, bool
_cleanFiles(&menu.items);
GUIMenuItemListDeinit(&menu.items);
menu.items = newFiles;
strncpy(params->currentPath, outPath, PATH_MAX);
strlcpy(params->currentPath, outPath, PATH_MAX);
}
}
params->fileIndex = 0;

View File

@ -47,19 +47,16 @@ static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned heigh
return 0;
}
png_set_IHDR(png, info, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png, info);
return info;
}
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
png_write_info(png, info);
return info;
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
}
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) {
png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
png_write_info(png, info);
return info;
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
}
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
@ -273,6 +270,10 @@ bool PNGIgnorePixels(png_structp png, png_infop info) {
}
bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
if (png_get_channels(png, info) != 3) {
return false;
}
if (setjmp(png_jmpbuf(png))) {
return false;
}
@ -324,6 +325,10 @@ bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width
}
bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
if (png_get_channels(png, info) != 4) {
return false;
}
if (setjmp(png_jmpbuf(png))) {
return false;
}
@ -375,6 +380,10 @@ bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned widt
}
bool PNGReadPixels8(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
if (png_get_channels(png, info) != 1) {
return false;
}
if (setjmp(png_jmpbuf(png))) {
return false;
}

View File

@ -31,6 +31,23 @@ char* strdup(const char* str) {
}
#endif
#ifndef HAVE_STRLCPY
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize) {
size_t i = 0;
for (; src[i] && dstsize > 1; ++i) {
dst[i] = src[i];
--dstsize;
}
if (dstsize) {
dst[i] = '\0';
}
while (src[i]) {
++i;
}
return i;
}
#endif
char* strnrstr(const char* restrict haystack, const char* restrict needle, size_t len) {
char* last = 0;
const char* next = haystack;