Merge branch 'master' into medusa
jump to
@@ -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
@@ -5,6 +5,7 @@ *.pyc
/build /build-* +/.vs *.a *.dylib@@ -14,4 +15,5 @@ *.o
*.so CMakeCache.txt CMakeFiles +CMakeSettings.json version.c
@@ -22,9 +22,13 @@ 0.9.0: (Future)
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 @@ - GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716)
- 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:
@@ -20,6 +20,10 @@ elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "4.3")
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()@@ -362,6 +367,10 @@ endif()
if(HAVE_STRNDUP) list(APPEND FUNCTION_DEFINES HAVE_STRNDUP) +endif() + +if(HAVE_STRLCPY) + list(APPEND FUNCTION_DEFINES HAVE_STRLCPY) endif() if(HAVE_LOCALTIME_R)@@ -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 @@ else()
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 @@ set(USE_SQLITE3 ON)
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)
@@ -52,6 +52,7 @@ - MBC5
- 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 properly.
#### 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 @@ make
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 @@ - Qt 5: for the GUI frontend. Qt Multimedia or SDL are required for audio.
- 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 [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
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.
@@ -14,6 +14,10 @@ #define CXX_GUARD_START
#define CXX_GUARD_END #endif +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#endif + CXX_GUARD_START #include <ctype.h>@@ -115,10 +119,8 @@
#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
@@ -19,6 +19,10 @@ #ifndef HAVE_STRDUP
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);
@@ -41,7 +41,7 @@ if (capacity == 0) { \
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); \
@@ -74,7 +74,7 @@ void (*setSync)(struct mCore*, struct mCoreSync*);
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);
@@ -34,9 +34,10 @@ GB_HuC1 = 0x11,
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 {
@@ -146,6 +146,10 @@ uint8_t reg;
uint8_t registers[GBTAMA5_MAX]; }; +struct GBPKJDState { + uint8_t reg[2]; +}; + union GBMBCState { struct GBMBC1State mbc1; struct GBMBC6State mbc6;@@ -153,6 +157,7 @@ struct GBMBC7State mbc7;
struct GBMMM01State mmm01; struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; + struct GBPKJDState pkjd; }; struct mRotationSource;@@ -171,6 +176,7 @@ uint8_t* wramBank;
int wramCurrentBank; bool sramAccess; + bool directSramAccess; uint8_t* sram; uint8_t* sramBank; int sramCurrentBank;
@@ -211,8 +211,8 @@ struct GBA* p;
struct GBAVideoRenderer* renderer; struct mTimingEvent event; - // VCOUNT int vcount; + int shouldStall; uint16_t palette[512]; uint16_t* vram;
@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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 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 @@ if (blen <= 0) {
return 0; } int total = 0; - strncpy(buffer, "{", blen - 1); + strlcpy(buffer, "{", blen); ADVANCE(1); int i; int start = -1;@@ -86,12 +87,12 @@ } else {
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 @@ if (start >= 0) {
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 @@ if (!psrBits) {
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 @@ if (blen <= 1) {
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 @@ } else {
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 @@ ADVANCE(written);
} 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 @@ if (blen <= 1) {
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 @@ break;
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 @@ case ARM_MN_STM:
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 @@ written = snprintf(buffer, blen - 1, "p%i, %i, ", info->cp.cp, info->cp.op1);
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 @@ written = _decodeShift(info->op1, false, buffer, blen);
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 @@ written = _decodeShift(info->op2, false, buffer, blen);
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 @@ written = _decodeShift(info->op3, false, buffer, blen);
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);
@@ -499,11 +499,26 @@ DEFINE_LOAD_STORE_T_INSTRUCTION_SHIFTER_ARM(NAME ## _ROR_, ADDR_MODE_2_ROR, LS, BODY) \
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 @@ int rs = opcode & 0x0000FFFF; \
uint32_t address = cpu->gprs[rn]; \ S_PRE; \ address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤tCycles); \ + 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 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## IBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, , , IB, POST_BODY) \
#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,
@@ -25,6 +25,13 @@ cpu->cpsr.z = !(D); \
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 @@ cpu->gprs[rd] = d + n + cpu->cpsr.c;
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) {
@@ -42,7 +42,7 @@ size_t size = mBitmapCacheSystemInfoGetHeight(cache->sysConfig) * mBitmapCacheSystemInfoGetBuffers(cache->sysConfig);
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; }
@@ -69,7 +69,7 @@ }
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 @@ cb(total, size, context);
} } vf->close(vf); + if (read < 0) { + vfm->close(vfm); + return false; + } bool ret = core->loadROM(core, vfm); if (!ret) { vfm->close(vfm);
@@ -15,6 +15,10 @@ #ifndef BUILD_GLES2
#cmakedefine BUILD_GLES2 #endif +#ifndef BUILD_GLES3 +#cmakedefine BUILD_GLES3 +#endif + // Miscellaneous flags #ifndef COLOR_16_BIT
@@ -88,7 +88,7 @@ map->maps = malloc(sizeof(*map->maps));
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 @@ }
} 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 @@ }
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;
@@ -448,7 +448,9 @@ }
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;
@@ -636,12 +636,6 @@ return NULL;
} #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 @@ vprintf(format, args);
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();
@@ -46,8 +46,8 @@ cache->entriesPerTile = size;
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) {
@@ -174,7 +174,7 @@ struct CLIDebugVector* accum;
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;
@@ -387,6 +387,11 @@ strcasecmp(encoder->containerFormat, "mov"))) {
// 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 @@ avcodec_free_frame(&encoder->audioFrame);
#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 @@ #endif
} if (encoder->videoFrame) { + av_freep(encoder->videoFrame->data); #if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_free(&encoder->videoFrame); #else@@ -585,8 +595,12 @@ encoder->sinkFrame = NULL;
} if (encoder->video) { +#ifdef FFMPEG_USE_CODECPAR + avcodec_free_context(&encoder->video); +#else avcodec_close(encoder->video); encoder->video = NULL; +#endif } if (encoder->scaleContext) {
@@ -286,8 +286,8 @@ return;
} } -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 @@ *sram = malloc(vf->size(vf));
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) {
@@ -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 @@ } else {
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 @@ break;
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 @@ case GB_MBC5:
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 @@ break;
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 @@ switch (address >> 10) {
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 @@ case 0x28:
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 @@ case 0x2C:
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 @@ }
} uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { - if (!memory->mbcState.mbc6.sramAccess) { + if (!memory->sramAccess) { return 0xFF; } switch (address >> 12) {@@ -1226,6 +1234,74 @@ default:
// TODO mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value); break; + } +} + +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; } }
@@ -361,7 +361,7 @@ case GB_REGION_EXTERNAL_RAM:
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);
@@ -495,6 +495,7 @@ { 0x232a067d, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
{ 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 } } };
@@ -104,81 +104,86 @@ cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0);
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]); + } } }
@@ -396,9 +396,9 @@ }
} } -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);
@@ -288,7 +288,7 @@ }
} 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];
@@ -29,6 +29,7 @@ static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx lr */ };
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 @@ cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion];
} #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 @@ value = memory->biosPrefetch; \
} \ } 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 @@ } \
} 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 @@
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 @@ value = (memory->biosPrefetch >> ((address & 2) * 8)) & 0xFFFF;
} } 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 @@ LOAD_16(value, address & 0x00017FFE, gba->video.vram);
} 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 @@ value |= value << 8;
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 @@ value = (memory->biosPrefetch >> ((address & 3) * 8)) & 0xFF;
} } 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 @@ value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF];
} 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 @@ value &= 0xFF;
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 @@ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
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 @@ STORE_16(value, address & 0x0001FFFE, gba->video.vram);
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);@@ -980,6 +992,9 @@ oldValue = gba->video.vram[(address & 0x1FFFE) >> 1];
if (oldValue != (((uint8_t) value) | (value << 8))) { 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:@@ -1660,6 +1675,28 @@ // The next |loads|S waitstates disappear entirely, so long as they're all in a row
wait -= stall - 1; 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) {
@@ -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 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;
@@ -222,17 +222,6 @@ } \
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) || \
@@ -139,6 +139,7 @@ }
} 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);
@@ -107,6 +107,7 @@ video->renderer->vramOBJ[0] = &video->vram[0x8000];
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 @@ video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
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 @@ }
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate); } + video->shouldStall = 0; video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; }@@ -359,6 +362,7 @@ GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
} 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 @@ video->event.callback = _startHdraw;
break; case 2: video->event.callback = _startHblank; + video->shouldStall = 1; break; case 3: video->event.callback = _midHblank;
@@ -31,7 +31,7 @@
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) {
@@ -2,14 +2,6 @@ # SDL2_LIBRARIES, the name of the library to link against
# 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 @@ PATH_SUFFIXES lib64 lib
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 @@ SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for 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.
@@ -921,7 +921,7 @@ if (inShaders > MAX_PASSES || inShaders < 1) {
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 @@ --u;
} } 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);
@@ -46,6 +46,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
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)@@ -284,6 +288,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)
@@ -83,6 +83,7 @@ }
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; }
@@ -386,12 +386,6 @@ m_currentFrame = m_nextFrame;
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 @@ if (!m_glowTimer.isActive()) {
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);
@@ -93,6 +93,10 @@ if (action == QSlider::SliderSingleStepAdd) {
++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; }@@ -589,6 +593,12 @@ adjustCursor(-16, event->modifiers() & Qt::ShiftModifier);
return; 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;
@@ -49,6 +49,8 @@ s_mbcList.append(GB_POCKETCAM);
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
@@ -354,6 +354,16 @@ <property name="text">
<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">
@@ -12,6 +12,8 @@
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 {
@@ -394,6 +394,7 @@ saveSetting("preload", m_ui.preload);
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 @@ loadSetting("preload", m_ui.preload);
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);
@@ -586,21 +586,21 @@ <bool>true</bool>
</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 @@ <bool>true</bool>
</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 @@ <bool>true</bool>
</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 @@ <bool>true</bool>
</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,10 +647,20 @@ <bool>true</bool>
</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> + </property> + <property name="checked"> + <bool>true</bool> + </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>
@@ -80,7 +80,7 @@ if (parent.isValid()) {
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 @@ QString parent = m_controller->parent(item->name);
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);
@@ -44,7 +44,7 @@ QString name;
const Shortcut* shortcut = nullptr; }; - QHash<QString, Item> m_cache; + mutable QHash<QString, Item> m_cache; }; }
@@ -99,12 +99,11 @@
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 @@
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::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);
@@ -71,6 +71,15 @@ int vbr;
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; };
@@ -1096,18 +1096,25 @@ CoreController::Interrupter interrupter(m_controller);
const NoIntroDB* db = GBAApp::app()->gameDB(); NoIntroGame game{}; uint32_t crc32 = 0; - m_controller->thread()->core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32); + mCore* core = m_controller->thread()->core; + core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32); + QString filePath = windowFilePath(); - char gameTitle[17] = { '\0' }; - mCore* core = m_controller->thread()->core; - core->getGameTitle(core, gameTitle); - title = gameTitle; + 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 } -#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());
@@ -4495,7 +4495,7 @@ </message>
<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"/>
@@ -114,6 +114,8 @@ set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES}")
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")
@@ -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()
@@ -78,8 +78,8 @@ Name: "sgbfileassoc"; Description: "{cm:AssocFileExtension,{#AppName},Super Game Boy}"; GroupDescription: "{cm:FileAssoc}"
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
@@ -7,6 +7,7 @@ #include <mgba/internal/sm83/decoder.h>
#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 @@ if (op.flags & SM83_OP_FLAG_IMPLICIT) {
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 written;
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 @@ }
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);
@@ -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`. + * **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`. - * **Maximum line length:** The default maximum line length is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. + * **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/library/configparser.html) standard library module:
* 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)
@@ -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 @@
#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 @@ 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 @@ {
/* 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 lineno = 0;
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 @@ }
#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 @@ error = ini_parse_file(file, handler, 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); +}
@@ -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 @@ #endif
#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 @@
/* 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 @@ close the file when it's finished -- the caller must do that. */
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
@@ -7,6 +7,7 @@ #include <mgba-util/gui/file-select.h>
#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 @@ } else {
_cleanFiles(&menu.items); GUIMenuItemListDeinit(&menu.items); menu.items = newFiles; - strncpy(params->currentPath, outPath, PATH_MAX); + strlcpy(params->currentPath, outPath, PATH_MAX); } } params->fileIndex = 0;
@@ -47,19 +47,16 @@ if (setjmp(png_jmpbuf(png))) {
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 @@ return true;
} 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 @@ return true;
} 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 @@ return true;
} 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; }
@@ -31,6 +31,23 @@ return out;
} #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;