mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
42c5ec87c0
|
@ -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 @@
|
|||
|
||||
/build
|
||||
/build-*
|
||||
/.vs
|
||||
|
||||
*.a
|
||||
*.dylib
|
||||
|
@ -14,4 +15,5 @@
|
|||
*.so
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeSettings.json
|
||||
version.c
|
||||
|
|
21
CHANGES
21
CHANGES
|
@ -22,9 +22,13 @@ Misc:
|
|||
Features:
|
||||
- e-Reader card scanning
|
||||
- Add APNG recording
|
||||
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
|
||||
Emulation fixes:
|
||||
- ARM: Fix ALU reading PC after shifting
|
||||
- ARM: Fix STR storing PC after address calculation
|
||||
- ARM: Fix LDM^ writeback to user-mode register
|
||||
- ARM: Fix LDM^ {pc} differences (fixes mgba.io/i/1698)
|
||||
- ARM: Fix edge case with Thumb SBC flags (fixes mgba.io/i/1818)
|
||||
- GB: Partially fix timing for skipped BIOS
|
||||
- GB Memory: Fix OAM DMA from top 8 kB
|
||||
- GB MBC: Fix MBC1 mode changing behavior
|
||||
|
@ -34,24 +38,41 @@ Emulation fixes:
|
|||
- GBA: Fix timing advancing too quickly in rare cases
|
||||
- GBA BIOS: Implement dummy sound driver calls
|
||||
- GBA BIOS: Improve HLE BIOS timing
|
||||
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
|
||||
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
|
||||
- GBA Memory: Improve gamepak prefetch timing
|
||||
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
|
||||
- GBA SIO: Fix copying Normal mode transfer values
|
||||
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
|
||||
- GBA Video: Fix Hblank timing
|
||||
- GBA Video: Fix invalid read in mode 4 mosaic
|
||||
- SM83: Emulate HALT bug
|
||||
Other fixes:
|
||||
- All: Improve export headers (fixes mgba.io/i/1738)
|
||||
- All: Correct format strings for some numbers on Windows (fixes mgba.io/i/1794)
|
||||
- All: Correct more format strings on Windows (fixes mgba.io/i/1817)
|
||||
- CMake: Fix build with libzip 1.7
|
||||
- Core: Ensure ELF regions can be written before trying
|
||||
- Debugger: Don't skip undefined instructions when debugger attached
|
||||
- FFmpeg: Fix some small memory leaks
|
||||
- GB Core: Fix extracting SRAM when none is present
|
||||
- GBA Savedata: Fix extracting save when not yet configured in-game
|
||||
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
|
||||
- Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769)
|
||||
- Qt: Fix file handle leak on opening an invalid ROM
|
||||
- Qt: Fix a race condition in the frame inspector
|
||||
- Qt: Fix Italian RTC translation (fixes mgba.io/i/1798)
|
||||
- Qt: Add missing option for Wisdom Tree in overrides list
|
||||
- Util: Fix crash if PNG header fails to write
|
||||
Misc:
|
||||
- Debugger: Keep track of global cycle count
|
||||
- FFmpeg: Add looping option for GIF/APNG
|
||||
- FFmpeg: Use range coder for FFV1 to reduce output size
|
||||
- Qt: Renderer can be changed while a game is running
|
||||
- Qt: Add hex index to palette view
|
||||
- Qt: Add transformation matrix info to sprite view
|
||||
- Qt: Add per-page scrolling to memory view (fixes mgba.io/i/1795)
|
||||
- Qt: Add setting to display ROM filename in title (closes mgba.io/i/1784)
|
||||
|
||||
0.8.2: (2020-06-14)
|
||||
Emulation fixes:
|
||||
|
|
124
CMakeLists.txt
124
CMakeLists.txt
|
@ -20,6 +20,10 @@ if(NOT MSVC)
|
|||
set(CMAKE_C_EXTENSIONS ON)
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers")
|
||||
if(WIN32)
|
||||
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146")
|
||||
endif()
|
||||
|
@ -286,6 +290,7 @@ include(CheckFunctionExists)
|
|||
include(CheckIncludeFiles)
|
||||
check_function_exists(strdup HAVE_STRDUP)
|
||||
check_function_exists(strndup HAVE_STRNDUP)
|
||||
check_function_exists(strlcpy HAVE_STRLCPY)
|
||||
if(NOT DEFINED PSP2)
|
||||
check_function_exists(localtime_r HAVE_LOCALTIME_R)
|
||||
endif()
|
||||
|
@ -364,6 +369,10 @@ if(HAVE_STRNDUP)
|
|||
list(APPEND FUNCTION_DEFINES HAVE_STRNDUP)
|
||||
endif()
|
||||
|
||||
if(HAVE_STRLCPY)
|
||||
list(APPEND FUNCTION_DEFINES HAVE_STRLCPY)
|
||||
endif()
|
||||
|
||||
if(HAVE_LOCALTIME_R)
|
||||
list(APPEND FUNCTION_DEFINES HAVE_LOCALTIME_R)
|
||||
endif()
|
||||
|
@ -470,7 +479,15 @@ set(WANT_PNG ${USE_PNG})
|
|||
set(WANT_SQLITE3 ${USE_SQLITE3})
|
||||
set(USE_CMOCKA ${BUILD_SUITE})
|
||||
|
||||
find_feature(USE_FFMPEG "libavcodec;libavfilter;libavformat;libavutil;libswscale")
|
||||
if(DEFINED VCPKG_TARGET_TRIPLET)
|
||||
find_feature(USE_FFMPEG "FFMPEG")
|
||||
if(FFMPEG_FOUND)
|
||||
set(USE_LIBAVRESAMPLE OFF)
|
||||
set(USE_LIBSWRESAMPLE ON)
|
||||
endif()
|
||||
else()
|
||||
find_feature(USE_FFMPEG "libavcodec;libavfilter;libavformat;libavutil;libswscale")
|
||||
endif()
|
||||
find_feature(USE_ZLIB "ZLIB")
|
||||
find_feature(USE_MINIZIP "minizip")
|
||||
find_feature(USE_PNG "PNG")
|
||||
|
@ -481,7 +498,7 @@ find_feature(USE_SQLITE3 "sqlite3")
|
|||
find_feature(USE_ELF "libelf")
|
||||
find_feature(ENABLE_PYTHON "PythonLibs")
|
||||
|
||||
if(USE_FFMPEG)
|
||||
if(USE_FFMPEG AND NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||
set(USE_LIBAVRESAMPLE ON)
|
||||
set(USE_LIBSWRESAMPLE ON)
|
||||
find_feature(USE_LIBAVRESAMPLE "libavresample")
|
||||
|
@ -533,31 +550,33 @@ if(USE_FFMPEG)
|
|||
list(APPEND FEATURES LIBAVRESAMPLE)
|
||||
list(APPEND FEATURES LIBAV)
|
||||
endif()
|
||||
include_directories(AFTER ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFILTER_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS})
|
||||
link_directories(${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFILTER_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS})
|
||||
include_directories(AFTER ${FFMPEG_INCLUDE_DIRS} ${LIBAVCODEC_INCLUDE_DIRS} ${LIBAVFILTER_INCLUDE_DIRS} ${LIBAVFORMAT_INCLUDE_DIRS} ${LIBAVRESAMPLE_INCLUDE_DIRS} ${LIBAVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIRS} ${LIBSWSCALE_INCLUDE_DIRS})
|
||||
link_directories(${FFMPEG_LIBRARY_DIRS} ${LIBAVCODEC_LIBRARY_DIRS} ${LIBAVFILTER_LIBRARY_DIRS} ${LIBAVFORMAT_LIBRARY_DIRS} ${LIBAVRESAMPLE_LIBRARY_DIRS} ${LIBAVUTIL_LIBRARY_DIRS} ${LIBSWRESAMPLE_LIBRARY_DIRS} ${LIBSWSCALE_LIBRARY_DIRS})
|
||||
list(APPEND FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/feature/ffmpeg/ffmpeg-encoder.c")
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVFILTER_VERSION_MAJOR ${libavfilter_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVUTIL_VERSION_MAJOR ${libavutil_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBSWSCALE_VERSION_MAJOR ${libswscale_VERSION})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES})
|
||||
if(WIN32)
|
||||
list(APPEND DEPENDENCY_LIB ${FFMPEG_LIBRARIES} ${LIBAVCODEC_LIBRARIES} ${LIBAVFILTER_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVRESAMPLE_LIBRARIES} ${LIBAVUTIL_LIBRARIES} ${LIBSWSCALE_LIBRARIES} ${LIBSWRESAMPLE_LIBRARIES})
|
||||
if(WIN32 AND NOT DEFINED VCPKG_TARGET_TRIPLET)
|
||||
list(APPEND DEPENDENCY_LIB bcrypt)
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavcodec${LIBAVCODEC_VERSION_MAJOR}|libavcodec-extra-${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavfilter${LIBAVFILTER_VERSION_MAJOR}|libavfilter-ffmpeg${LIBAVFILTER_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
|
||||
if(USE_LIBSWRESAMPLE)
|
||||
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_MAJOR}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_MAJOR}")
|
||||
else()
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
|
||||
if(UNIX)
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVCODEC_VERSION_MAJOR ${libavcodec_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVFILTER_VERSION_MAJOR ${libavfilter_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVFORMAT_VERSION_MAJOR ${libavformat_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVUTIL_VERSION_MAJOR ${libavutil_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBSWSCALE_VERSION_MAJOR ${libswscale_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavcodec${LIBAVCODEC_VERSION_MAJOR}|libavcodec-extra-${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg${LIBAVCODEC_VERSION_MAJOR}|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavfilter${LIBAVFILTER_VERSION_MAJOR}|libavfilter-ffmpeg${LIBAVFILTER_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
|
||||
if(USE_LIBSWRESAMPLE)
|
||||
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_MAJOR}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_MAJOR}")
|
||||
else()
|
||||
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavutil${LIBAVUTIL_VERSION_MAJOR}|libavutil-ffmpeg${LIBAVUTIL_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswscale${LIBSWSCALE_VERSION_MAJOR}|libswscale-ffmpeg${LIBSWSCALE_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavutil${LIBAVUTIL_VERSION_MAJOR}|libavutil-ffmpeg${LIBAVUTIL_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswscale${LIBSWSCALE_VERSION_MAJOR}|libswscale-ffmpeg${LIBSWSCALE_VERSION_MAJOR}")
|
||||
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "libavcodec-extra|libavcodec-ffmpeg-extra${LIBAVCODEC_VERSION_MAJOR}")
|
||||
if(APPLE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework VideoDecodeAcceleration -framework CoreVideo")
|
||||
|
@ -630,18 +649,22 @@ if(WANT_SQLITE3 AND NOT USE_SQLITE3)
|
|||
endif()
|
||||
|
||||
if(USE_LIBZIP)
|
||||
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
|
||||
link_directories(${LIBZIP_LIBRARY_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
|
||||
if(TARGET libzip::zip)
|
||||
list(APPEND DEPENDENCY_LIB libzip::zip)
|
||||
else()
|
||||
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
|
||||
link_directories(${LIBZIP_LIBRARY_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND FEATURES LIBZIP)
|
||||
list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c)
|
||||
string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR ${libzip_VERSION})
|
||||
string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR "${libzip_VERSION}")
|
||||
if (LIBZIP_VERSION_MAJOR LESS 1)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip2")
|
||||
elseif(LIBZIP_VERSION_MAJOR EQUAL 1)
|
||||
elseif(LIBZIP_VERSION_MAJOR EQUAL 1 OR NOT LIBZIP_VERSION_MAJOR)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libzip4|libzip5")
|
||||
else()
|
||||
message(AUTHOR_WARNING Unknown version of libzip detected: ${libzip_VERSION})
|
||||
message(AUTHOR_WARNING "Unknown version of libzip detected: ${libzip_VERSION}")
|
||||
endif()
|
||||
elseif(USE_MINIZIP)
|
||||
include_directories(AFTER ${MINIZIP_INCLUDE_DIRS})
|
||||
|
@ -956,48 +979,10 @@ if(BUILD_QT)
|
|||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/qt ${CMAKE_CURRENT_BINARY_DIR}/qt)
|
||||
endif()
|
||||
|
||||
if(BUILD_PERF)
|
||||
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c)
|
||||
if(UNIX AND NOT APPLE)
|
||||
list(APPEND PERF_LIB rt)
|
||||
endif()
|
||||
|
||||
add_executable(${BINARY_NAME}-perf ${PERF_SRC})
|
||||
target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB} ${OS_LIB})
|
||||
set_target_properties(${BINARY_NAME}-perf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
install(TARGETS ${BINARY_NAME}-perf DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-perf)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/tools/perf.py DESTINATION "${LIBDIR}/${BINARY_NAME}" COMPONENT ${BINARY_NAME}-perf)
|
||||
endif()
|
||||
|
||||
if(BUILD_TEST)
|
||||
add_executable(${BINARY_NAME}-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/fuzz-main.c)
|
||||
target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME})
|
||||
set_target_properties(${BINARY_NAME}-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
add_executable(tbl-fuzz ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/tbl-fuzz-main.c)
|
||||
target_link_libraries(tbl-fuzz ${BINARY_NAME})
|
||||
set_target_properties(tbl-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
install(TARGETS ${BINARY_NAME}-fuzz tbl-fuzz DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-test)
|
||||
endif()
|
||||
|
||||
if(NOT USE_CMOCKA)
|
||||
set(BUILD_SUITE OFF)
|
||||
endif()
|
||||
if(BUILD_SUITE)
|
||||
enable_testing()
|
||||
include_directories(AFTER ${CMOCKA_INCLUDE_DIRS})
|
||||
link_directories(${CMOCKA_LIBRARY_DIRS})
|
||||
|
||||
foreach(TEST IN LISTS TEST_SRC)
|
||||
string(REPLACE "${CMAKE_SOURCE_DIR}/src/" "" TEST_NAME "${TEST}")
|
||||
string(REPLACE "/" "-" TEST_NAME "${TEST_NAME}")
|
||||
string(REPLACE "-test" "" TEST_NAME "${TEST_NAME}")
|
||||
string(REPLACE ".c" "" TEST_NAME "${TEST_NAME}")
|
||||
add_executable(test-${TEST_NAME} ${TEST})
|
||||
target_link_libraries(test-${TEST_NAME} ${BINARY_NAME} ${PLATFORM_LIBRARY} cmocka)
|
||||
set_target_properties(test-${TEST_NAME} PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
add_test(${TEST_NAME} test-${TEST_NAME})
|
||||
endforeach()
|
||||
endif()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test ${CMAKE_CURRENT_BINARY_DIR}/test)
|
||||
|
||||
if(BUILD_EXAMPLE)
|
||||
add_executable(${BINARY_NAME}-example-server ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/server.c)
|
||||
|
@ -1207,8 +1192,7 @@ endif()
|
|||
|
||||
cpack_add_component_group(test PARENT_GROUP dev)
|
||||
cpack_add_component(${BINARY_NAME}-perf GROUP test)
|
||||
cpack_add_component(${BINARY_NAME}-fuzz GROUP test)
|
||||
cpack_add_component(tbl-fuzz GROUP test)
|
||||
cpack_add_component(${BINARY_NAME}-test GROUP test)
|
||||
|
||||
# Summaries
|
||||
set(SUMMARY_GL_LIST)
|
||||
|
|
22
README.md
22
README.md
|
@ -52,6 +52,7 @@ The following mappers are fully supported:
|
|||
- MBC5+Rumble
|
||||
- MBC7
|
||||
- Wisdom Tree (unlicensed)
|
||||
- Pokémon Jade/Diamond (unlicensed)
|
||||
|
||||
The following mappers are partially supported:
|
||||
|
||||
|
@ -175,6 +176,8 @@ Note that you should not do a `make install` on macOS, as it will not work prope
|
|||
|
||||
#### Windows developer building
|
||||
|
||||
##### MSYS2
|
||||
|
||||
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 1100MiB of packages, so it will take a long time):
|
||||
|
||||
For x86 (32 bit) builds:
|
||||
|
@ -199,6 +202,20 @@ Then finally build it by running these commands:
|
|||
|
||||
Please note that this build of medusa for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), running `cpack -G ZIP` will prepare a zip file with all of the necessary DLLs.
|
||||
|
||||
##### Visual Studio
|
||||
|
||||
To build using Visual Studio is a similarly complicated setup. To begin you will need to install [vcpkg](https://github.com/Microsoft/vcpkg). After installing vcpkg you will need to install several additional packages:
|
||||
|
||||
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3
|
||||
|
||||
Note that this installation won't support hardware accelerated video encoding on Nvidia hardware. If you care about this, you'll need to install CUDA beforehand, and then substitute `ffmpeg[vpx,x264,nvcodec]` into the previous command.
|
||||
|
||||
You will also need to install Qt. Unfortunately due to Qt being owned and run by an ailing company as opposed to a reasonable organization there is no longer an offline open source edition installer for the latest version, so you'll need to either fall back to an [old version installer](https://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe) (which wants you to create an otherwise-useless account, but you can bypass temporarily setting an invalid proxy or otherwise disabling networking), use the online installer (which requires an account regardless), or use vcpkg to build it (slowly). None of these are great options. For the installer you'll want to install the applicable MSVC versions. Note that the offline installers do not support MSVC 2019. For vcpkg you'll want to install it as such, which will take quite a while, especially on quad core or less computers:
|
||||
|
||||
vcpkg install qt5-base qt5-multimedia
|
||||
|
||||
Next, open Visual Studio, select Clone Repository, and enter `https://github.com/mgba-emu/mgba.git`. When Visual Studio is done cloning, go to File > CMake and open the CMakeLists.txt file at the root of the checked out repository. From there, mGBA can be developed in Visual Studio similarly to other Visual Studio CMake projects.
|
||||
|
||||
#### Toolchain building
|
||||
|
||||
If you have devkitARM (for 3DS), devkitPPC (for Wii), devkitA64 (for Switch), or vitasdk (for PS Vita), you can use the following commands for building:
|
||||
|
@ -223,9 +240,8 @@ medusa has no hard dependencies, however, the following optional dependencies ar
|
|||
- SDL: for a more basic frontend and gamepad support in the Qt frontend. SDL 2 is recommended, but 1.2 is supported.
|
||||
- zlib and libpng: for screenshot support and savestate-in-PNG support.
|
||||
- libedit: for command-line debugger support.
|
||||
- ffmpeg or libav: for video recording.
|
||||
- ffmpeg or libav: for video and GIF recording.
|
||||
- libzip or zlib: for loading ROMs stored in zip files.
|
||||
- ImageMagick: for GIF recording.
|
||||
- SQLite3: for game databases.
|
||||
- libelf: for ELF loading.
|
||||
|
||||
|
@ -291,7 +307,7 @@ medusa is Copyright © 2013 – 2020 Jeffrey Pfau. It is distributed under the [
|
|||
|
||||
medusa contains the following third-party libraries:
|
||||
|
||||
- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 Ben Hoyt and used under a BSD 3-clause license.
|
||||
- [inih](https://github.com/benhoyt/inih), which is copyright © 2009 – 2020 Ben Hoyt and used under a BSD 3-clause license.
|
||||
- [blip-buf](https://code.google.com/archive/p/blip-buf), which is copyright © 2003 – 2009 Shay Green and used under a Lesser GNU Public License.
|
||||
- [LZMA SDK](http://www.7-zip.org/sdk.html), which is public domain.
|
||||
- [MurmurHash3](https://github.com/aappleby/smhasher) implementation by Austin Appleby, which is public domain.
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#define CXX_GUARD_END
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define __USE_MINGW_ANSI_STDIO 1
|
||||
#endif
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <ctype.h>
|
||||
|
@ -115,10 +119,8 @@ typedef intptr_t ssize_t;
|
|||
#if defined(_3DS) || defined(GEKKO) || defined(PSP2)
|
||||
// newlib doesn't support %z properly by default
|
||||
#define PRIz ""
|
||||
#elif defined(_WIN64)
|
||||
#define PRIz "I64"
|
||||
#elif defined(_WIN32)
|
||||
#define PRIz ""
|
||||
#elif defined(_MSC_VER)
|
||||
#define PRIz "I"
|
||||
#else
|
||||
#define PRIz "z"
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,10 @@ char* strndup(const char* start, size_t len);
|
|||
char* strdup(const char* str);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize);
|
||||
#endif
|
||||
|
||||
char* strnrstr(const char* restrict s1, const char* restrict s2, size_t len);
|
||||
bool endswith(const char* restrict s1, const char* restrict end);
|
||||
bool startswith(const char* restrict s1, const char* restrict start);
|
||||
|
|
|
@ -41,7 +41,7 @@ CXX_GUARD_START
|
|||
capacity = 4; \
|
||||
} \
|
||||
vector->capacity = capacity; \
|
||||
vector->vector = malloc(sizeof(TYPE) * capacity); \
|
||||
vector->vector = calloc(capacity, sizeof(TYPE)); \
|
||||
} \
|
||||
void NAME ## Deinit(struct NAME* vector) { \
|
||||
free(vector->vector); \
|
||||
|
|
|
@ -74,7 +74,7 @@ struct mCore {
|
|||
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
|
||||
void (*reloadConfigOption)(struct mCore*, const char* option, const struct mCoreConfig*);
|
||||
|
||||
void (*desiredVideoDimensions)(struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*desiredVideoDimensions)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
|
||||
void (*setVideoGLTex)(struct mCore*, unsigned texid);
|
||||
|
||||
|
|
|
@ -34,9 +34,10 @@ enum GBMemoryBankControllerType {
|
|||
GB_HuC3 = 0x12,
|
||||
GB_POCKETCAM = 0x13,
|
||||
GB_TAMA5 = 0x14,
|
||||
GB_UNL_WISDOM_TREE = 0x20,
|
||||
GB_MBC3_RTC = 0x103,
|
||||
GB_MBC5_RUMBLE = 0x105
|
||||
GB_MBC5_RUMBLE = 0x105,
|
||||
GB_UNL_WISDOM_TREE = 0x200,
|
||||
GB_UNL_PKJD = 0x203,
|
||||
};
|
||||
|
||||
struct GBSIODriver {
|
||||
|
|
|
@ -146,6 +146,10 @@ struct GBTAMA5State {
|
|||
uint8_t registers[GBTAMA5_MAX];
|
||||
};
|
||||
|
||||
struct GBPKJDState {
|
||||
uint8_t reg[2];
|
||||
};
|
||||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC6State mbc6;
|
||||
|
@ -153,6 +157,7 @@ union GBMBCState {
|
|||
struct GBMMM01State mmm01;
|
||||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBPKJDState pkjd;
|
||||
};
|
||||
|
||||
struct mRotationSource;
|
||||
|
@ -171,6 +176,7 @@ struct GBMemory {
|
|||
int wramCurrentBank;
|
||||
|
||||
bool sramAccess;
|
||||
bool directSramAccess;
|
||||
uint8_t* sram;
|
||||
uint8_t* sramBank;
|
||||
int sramCurrentBank;
|
||||
|
|
|
@ -211,8 +211,8 @@ struct GBAVideo {
|
|||
struct GBAVideoRenderer* renderer;
|
||||
struct mTimingEvent event;
|
||||
|
||||
// VCOUNT
|
||||
int vcount;
|
||||
int shouldStall;
|
||||
|
||||
uint16_t palette[512];
|
||||
uint16_t* vram;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/internal/arm/decoder.h>
|
||||
|
||||
#include <mgba/internal/arm/decoder-inlines.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#define ADVANCE(AMOUNT) \
|
||||
if (AMOUNT >= blen) { \
|
||||
|
@ -45,22 +46,22 @@ static const char* _armConditions[] = {
|
|||
static int _decodeRegister(int reg, char* buffer, int blen) {
|
||||
switch (reg) {
|
||||
case ARM_SP:
|
||||
strncpy(buffer, "sp", blen - 1);
|
||||
strlcpy(buffer, "sp", blen);
|
||||
return 2;
|
||||
case ARM_LR:
|
||||
strncpy(buffer, "lr", blen - 1);
|
||||
strlcpy(buffer, "lr", blen);
|
||||
return 2;
|
||||
case ARM_PC:
|
||||
strncpy(buffer, "pc", blen - 1);
|
||||
strlcpy(buffer, "pc", blen);
|
||||
return 2;
|
||||
case ARM_CPSR:
|
||||
strncpy(buffer, "cpsr", blen - 1);
|
||||
strlcpy(buffer, "cpsr", blen);
|
||||
return 4;
|
||||
case ARM_SPSR:
|
||||
strncpy(buffer, "spsr", blen - 1);
|
||||
strlcpy(buffer, "spsr", blen);
|
||||
return 4;
|
||||
default:
|
||||
return snprintf(buffer, blen - 1, "r%i", reg);
|
||||
return snprintf(buffer, blen, "r%i", reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +70,7 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
|
|||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
strncpy(buffer, "{", blen - 1);
|
||||
strlcpy(buffer, "{", blen);
|
||||
ADVANCE(1);
|
||||
int i;
|
||||
int start = -1;
|
||||
|
@ -86,12 +87,12 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
|
|||
if (end > start) {
|
||||
written = _decodeRegister(start, buffer, blen);
|
||||
ADVANCE(written);
|
||||
strncpy(buffer, "-", blen - 1);
|
||||
strlcpy(buffer, "-", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
written = _decodeRegister(end, buffer, blen);
|
||||
ADVANCE(written);
|
||||
strncpy(buffer, ",", blen - 1);
|
||||
strlcpy(buffer, ",", blen);
|
||||
ADVANCE(1);
|
||||
start = i;
|
||||
end = i;
|
||||
|
@ -103,13 +104,13 @@ static int _decodeRegisterList(int list, char* buffer, int blen) {
|
|||
if (end > start) {
|
||||
written = _decodeRegister(start, buffer, blen);
|
||||
ADVANCE(written);
|
||||
strncpy(buffer, "-", blen - 1);
|
||||
strlcpy(buffer, "-", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
written = _decodeRegister(end, buffer, blen);
|
||||
ADVANCE(written);
|
||||
}
|
||||
strncpy(buffer, "}", blen - 1);
|
||||
strlcpy(buffer, "}", blen);
|
||||
ADVANCE(1);
|
||||
return total;
|
||||
}
|
||||
|
@ -119,29 +120,29 @@ static int _decodePSR(int psrBits, char* buffer, int blen) {
|
|||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
strncpy(buffer, "_", blen - 1);
|
||||
strlcpy(buffer, "_", blen);
|
||||
ADVANCE(1);
|
||||
if (psrBits & ARM_PSR_C) {
|
||||
strncpy(buffer, "c", blen - 1);
|
||||
strlcpy(buffer, "c", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (psrBits & ARM_PSR_X) {
|
||||
strncpy(buffer, "x", blen - 1);
|
||||
strlcpy(buffer, "x", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (psrBits & ARM_PSR_S) {
|
||||
strncpy(buffer, "s", blen - 1);
|
||||
strlcpy(buffer, "s", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (psrBits & ARM_PSR_F) {
|
||||
strncpy(buffer, "f", blen - 1);
|
||||
strlcpy(buffer, "f", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
static int _decodePCRelative(uint32_t address, uint32_t pc, char* buffer, int blen) {
|
||||
return snprintf(buffer, blen - 1, "$%08X", address + pc);
|
||||
return snprintf(buffer, blen, "$%08X", address + pc);
|
||||
}
|
||||
|
||||
static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, int blen) {
|
||||
|
@ -149,7 +150,7 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
|
|||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
strncpy(buffer, "[", blen - 1);
|
||||
strlcpy(buffer, "[", blen);
|
||||
ADVANCE(1);
|
||||
int written;
|
||||
if (memory.format & ARM_MEMORY_REGISTER_BASE) {
|
||||
|
@ -160,26 +161,26 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
|
|||
written = _decodeRegister(memory.baseReg, buffer, blen);
|
||||
ADVANCE(written);
|
||||
if (memory.format & (ARM_MEMORY_REGISTER_OFFSET | ARM_MEMORY_IMMEDIATE_OFFSET) && !(memory.format & ARM_MEMORY_POST_INCREMENT)) {
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory.format & ARM_MEMORY_POST_INCREMENT) {
|
||||
strncpy(buffer, "], ", blen - 1);
|
||||
strlcpy(buffer, "], ", blen);
|
||||
ADVANCE(3);
|
||||
}
|
||||
if (memory.format & ARM_MEMORY_IMMEDIATE_OFFSET && memory.baseReg != ARM_PC) {
|
||||
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
|
||||
written = snprintf(buffer, blen - 1, "#-%i", memory.offset.immediate);
|
||||
written = snprintf(buffer, blen, "#-%i", memory.offset.immediate);
|
||||
ADVANCE(written);
|
||||
} else {
|
||||
written = snprintf(buffer, blen - 1, "#%i", memory.offset.immediate);
|
||||
written = snprintf(buffer, blen, "#%i", memory.offset.immediate);
|
||||
ADVANCE(written);
|
||||
}
|
||||
} else if (memory.format & ARM_MEMORY_REGISTER_OFFSET) {
|
||||
if (memory.format & ARM_MEMORY_OFFSET_SUBTRACT) {
|
||||
strncpy(buffer, "-", blen - 1);
|
||||
strlcpy(buffer, "-", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
written = _decodeRegister(memory.offset.reg, buffer, blen);
|
||||
|
@ -191,11 +192,11 @@ static int _decodeMemory(struct ARMMemoryAccess memory, int pc, char* buffer, in
|
|||
}
|
||||
|
||||
if (!(memory.format & ARM_MEMORY_POST_INCREMENT)) {
|
||||
strncpy(buffer, "]", blen - 1);
|
||||
strlcpy(buffer, "]", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if ((memory.format & (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) == (ARM_MEMORY_PRE_INCREMENT | ARM_MEMORY_WRITEBACK)) {
|
||||
strncpy(buffer, "!", blen - 1);
|
||||
strlcpy(buffer, "!", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
return total;
|
||||
|
@ -206,33 +207,33 @@ static int _decodeShift(union ARMOperand op, bool reg, char* buffer, int blen) {
|
|||
return 0;
|
||||
}
|
||||
int total = 0;
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
int written;
|
||||
switch (op.shifterOp) {
|
||||
case ARM_SHIFT_LSL:
|
||||
strncpy(buffer, "lsl ", blen - 1);
|
||||
strlcpy(buffer, "lsl ", blen);
|
||||
ADVANCE(4);
|
||||
break;
|
||||
case ARM_SHIFT_LSR:
|
||||
strncpy(buffer, "lsr ", blen - 1);
|
||||
strlcpy(buffer, "lsr ", blen);
|
||||
ADVANCE(4);
|
||||
break;
|
||||
case ARM_SHIFT_ASR:
|
||||
strncpy(buffer, "asr ", blen - 1);
|
||||
strlcpy(buffer, "asr ", blen);
|
||||
ADVANCE(4);
|
||||
break;
|
||||
case ARM_SHIFT_ROR:
|
||||
strncpy(buffer, "ror ", blen - 1);
|
||||
strlcpy(buffer, "ror ", blen);
|
||||
ADVANCE(4);
|
||||
break;
|
||||
case ARM_SHIFT_RRX:
|
||||
strncpy(buffer, "rrx", blen - 1);
|
||||
strlcpy(buffer, "rrx", blen);
|
||||
ADVANCE(3);
|
||||
return total;
|
||||
}
|
||||
if (!reg) {
|
||||
written = snprintf(buffer, blen - 1, "#%i", op.shifterImm);
|
||||
written = snprintf(buffer, blen, "#%i", op.shifterImm);
|
||||
} else {
|
||||
written = _decodeRegister(op.shifterReg, buffer, blen);
|
||||
}
|
||||
|
@ -388,7 +389,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
default:
|
||||
break;
|
||||
}
|
||||
written = snprintf(buffer, blen - 1, "%s%s%s ", mnemonic, cond, flags);
|
||||
written = snprintf(buffer, blen, "%s%s%s ", mnemonic, cond, flags);
|
||||
ADVANCE(written);
|
||||
|
||||
switch (info->mnemonic) {
|
||||
|
@ -397,15 +398,15 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
written = _decodeRegister(info->memory.baseReg, buffer, blen);
|
||||
ADVANCE(written);
|
||||
if (info->memory.format & ARM_MEMORY_WRITEBACK) {
|
||||
strncpy(buffer, "!", blen - 1);
|
||||
strlcpy(buffer, "!", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
written = _decodeRegisterList(info->op1.immediate, buffer, blen);
|
||||
ADVANCE(written);
|
||||
if (info->memory.format & ARM_MEMORY_SPSR_SWAP) {
|
||||
strncpy(buffer, "^", blen - 1);
|
||||
strlcpy(buffer, "^", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
break;
|
||||
|
@ -430,7 +431,7 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
ADVANCE(written);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||
written = snprintf(buffer, blen - 1, "#%i", info->op1.immediate);
|
||||
written = snprintf(buffer, blen, "#%i", info->op1.immediate);
|
||||
ADVANCE(written);
|
||||
} else if (info->operandFormat & ARM_OPERAND_MEMORY_1) {
|
||||
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||
|
@ -453,11 +454,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
ADVANCE(written);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_2) {
|
||||
strncpy(buffer, ", ", blen);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_2) {
|
||||
written = snprintf(buffer, blen - 1, "#%i", info->op2.immediate);
|
||||
written = snprintf(buffer, blen, "#%i", info->op2.immediate);
|
||||
ADVANCE(written);
|
||||
} else if (info->operandFormat & ARM_OPERAND_MEMORY_2) {
|
||||
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||
|
@ -477,11 +478,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
ADVANCE(written);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_3) {
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_3) {
|
||||
written = snprintf(buffer, blen - 1, "#%i", info->op3.immediate);
|
||||
written = snprintf(buffer, blen, "#%i", info->op3.immediate);
|
||||
ADVANCE(written);
|
||||
} else if (info->operandFormat & ARM_OPERAND_MEMORY_3) {
|
||||
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||
|
@ -501,11 +502,11 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i
|
|||
ADVANCE(written);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_4) {
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
strlcpy(buffer, ", ", blen);
|
||||
ADVANCE(2);
|
||||
}
|
||||
if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) {
|
||||
written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate);
|
||||
written = snprintf(buffer, blen, "#%i", info->op4.immediate);
|
||||
ADVANCE(written);
|
||||
} else if (info->operandFormat & ARM_OPERAND_MEMORY_4) {
|
||||
written = _decodeMemory(info->memory, pc, buffer, blen);
|
||||
|
|
|
@ -499,11 +499,26 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## I, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(-, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||
DEFINE_LOAD_STORE_INSTRUCTION_EX_ARM(NAME ## IU, ADDR_MODE_2_RN, ADDR_MODE_2_WRITEBACK(ADDR_MODE_2_INDEX(+, ADDR_MODE_2_IMMEDIATE)), LS, BODY) \
|
||||
|
||||
#define ARM_MS_PRE \
|
||||
#define ARM_MS_PRE_store \
|
||||
enum PrivilegeMode privilegeMode = cpu->privilegeMode; \
|
||||
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
|
||||
|
||||
#define ARM_MS_POST ARMSetPrivilegeMode(cpu, privilegeMode);
|
||||
#define ARM_MS_PRE_load \
|
||||
enum PrivilegeMode privilegeMode; \
|
||||
if (!(rs & 0x8000)) { \
|
||||
privilegeMode = cpu->privilegeMode; \
|
||||
ARMSetPrivilegeMode(cpu, MODE_SYSTEM); \
|
||||
}
|
||||
|
||||
#define ARM_MS_POST_store ARMSetPrivilegeMode(cpu, privilegeMode);
|
||||
|
||||
#define ARM_MS_POST_load \
|
||||
if ((rs & 0x8000) && _ARMModeHasSPSR(cpu->cpsr.priv)) { \
|
||||
cpu->cpsr = cpu->spsr; \
|
||||
_ARMReadCPSR(cpu); \
|
||||
} else { \
|
||||
ARMSetPrivilegeMode(cpu, privilegeMode); \
|
||||
} \
|
||||
|
||||
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME, LS, WRITEBACK, S_PRE, S_POST, DIRECTION, POST_BODY) \
|
||||
DEFINE_INSTRUCTION_ARM(NAME, \
|
||||
|
@ -512,10 +527,9 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
uint32_t address = cpu->gprs[rn]; \
|
||||
S_PRE; \
|
||||
address = cpu->memory. LS ## Multiple(cpu, address, rs, LSM_ ## DIRECTION, ¤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 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
|||
|
||||
#define DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(NAME, LS, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(NAME, LS, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE, ARM_MS_POST, DA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, DA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE, ARM_MS_POST, DB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, DB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE, ARM_MS_POST, IA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, IA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE, ARM_MS_POST, IB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE, ARM_MS_POST, IB, POST_BODY)
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDA, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDB, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SDBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, DB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIA, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIAW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IA, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIB, LS, , ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IB, POST_BODY) \
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_EX_ARM(NAME ## SIBW, LS, ADDR_MODE_4_WRITEBACK_ ## NAME, ARM_MS_PRE_ ## LS, ARM_MS_POST_ ## LS, IB, POST_BODY)
|
||||
|
||||
// Begin ALU definitions
|
||||
|
||||
|
@ -691,7 +705,11 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
|
|||
load,
|
||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
||||
if ((rs & 0x8000) || !rs) {
|
||||
currentCycles += ARMWritePC(cpu);
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
currentCycles += ThumbWritePC(cpu);
|
||||
} else {
|
||||
currentCycles += ARMWritePC(cpu);
|
||||
}
|
||||
})
|
||||
|
||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(LDMv5,
|
||||
|
|
|
@ -25,6 +25,13 @@
|
|||
cpu->cpsr.c = ARM_BORROW_FROM(M, N, D); \
|
||||
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
|
||||
|
||||
#define THUMB_SUBTRACTION_CARRY_S(M, N, D, C) \
|
||||
cpu->cpsr.flags = 0; \
|
||||
cpu->cpsr.n = ARM_SIGN(D); \
|
||||
cpu->cpsr.z = !(D); \
|
||||
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
|
||||
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
|
||||
|
||||
#define THUMB_NEUTRAL_S(M, N, D) \
|
||||
cpu->cpsr.n = ARM_SIGN(D); \
|
||||
cpu->cpsr.z = !(D);
|
||||
|
@ -203,10 +210,11 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ADC,
|
|||
THUMB_ADDITION_S(d, n, cpu->gprs[rd]);)
|
||||
|
||||
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(SBC,
|
||||
int n = cpu->gprs[rn] + !cpu->cpsr.c;
|
||||
int n = cpu->gprs[rn];
|
||||
int d = cpu->gprs[rd];
|
||||
cpu->gprs[rd] = d - n;
|
||||
THUMB_SUBTRACTION_S(d, n, cpu->gprs[rd]);)
|
||||
cpu->gprs[rd] = d - n - !cpu->cpsr.c;
|
||||
THUMB_SUBTRACTION_CARRY_S(d, n, cpu->gprs[rd], !cpu->cpsr.c);)
|
||||
|
||||
DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(ROR,
|
||||
int rs = cpu->gprs[rn] & 0xFF;
|
||||
if (rs) {
|
||||
|
|
|
@ -42,7 +42,7 @@ static void _redoCacheSize(struct mBitmapCache* cache) {
|
|||
cache->cache = anonymousMemoryMap(mBitmapCacheSystemInfoGetWidth(cache->sysConfig) * size * sizeof(color_t));
|
||||
cache->status = anonymousMemoryMap(size * sizeof(*cache->status));
|
||||
if (mBitmapCacheSystemInfoIsUsesPalette(cache->sysConfig)) {
|
||||
cache->palette = malloc((1 << (1 << mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig))) * sizeof(color_t));
|
||||
cache->palette = calloc((1 << (1 << mBitmapCacheSystemInfoGetEntryBPP(cache->sysConfig))), sizeof(color_t));
|
||||
} else {
|
||||
cache->palette = NULL;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
|
|||
|
||||
enum mPlatform mCoreIsCompatible(struct VFile* vf) {
|
||||
if (!vf) {
|
||||
return false;
|
||||
return PLATFORM_NONE;
|
||||
}
|
||||
const struct mCoreFilter* filter;
|
||||
for (filter = &_filters[0]; filter->filter; ++filter) {
|
||||
|
@ -169,6 +169,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si
|
|||
}
|
||||
}
|
||||
vf->close(vf);
|
||||
if (read < 0) {
|
||||
vfm->close(vfm);
|
||||
return false;
|
||||
}
|
||||
bool ret = core->loadROM(core, vfm);
|
||||
if (!ret) {
|
||||
vfm->close(vfm);
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#cmakedefine BUILD_GLES2
|
||||
#endif
|
||||
|
||||
#ifndef BUILD_GLES3
|
||||
#cmakedefine BUILD_GLES3
|
||||
#endif
|
||||
|
||||
// Miscellaneous flags
|
||||
|
||||
#ifndef COLOR_16_BIT
|
||||
|
|
|
@ -88,7 +88,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
|
|||
map->numMaps = 1;
|
||||
impl = &map->maps[0];
|
||||
impl->type = type;
|
||||
impl->map = malloc(map->info->nKeys * sizeof(int));
|
||||
impl->map = calloc(map->info->nKeys, sizeof(int));
|
||||
size_t i;
|
||||
for (i = 0; i < map->info->nKeys; ++i) {
|
||||
impl->map[i] = -1;
|
||||
|
@ -108,7 +108,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
|
|||
}
|
||||
if (impl) {
|
||||
impl->type = type;
|
||||
impl->map = malloc(map->info->nKeys * sizeof(int));
|
||||
impl->map = calloc(map->info->nKeys, sizeof(int));
|
||||
size_t i;
|
||||
for (i = 0; i < map->info->nKeys; ++i) {
|
||||
impl->map[i] = -1;
|
||||
|
@ -122,7 +122,7 @@ static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type)
|
|||
map->numMaps *= 2;
|
||||
impl = &map->maps[m];
|
||||
impl->type = type;
|
||||
impl->map = malloc(map->info->nKeys * sizeof(int));
|
||||
impl->map = calloc(map->info->nKeys, sizeof(int));
|
||||
size_t i;
|
||||
for (i = 0; i < map->info->nKeys; ++i) {
|
||||
impl->map[i] = -1;
|
||||
|
|
|
@ -448,7 +448,9 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
if (mStateExtdataGet(&extdata, EXTDATA_SAVEDATA, &item)) {
|
||||
mLOG(SAVESTATE, INFO, "Loading savedata");
|
||||
if (item.data) {
|
||||
core->savedataRestore(core, item.data, item.size, flags & SAVESTATE_SAVEDATA);
|
||||
if (!core->savedataRestore(core, item.data, item.size, flags & SAVESTATE_SAVEDATA)) {
|
||||
mLOG(SAVESTATE, WARN, "Failed to load savedata from savestate");
|
||||
}
|
||||
}
|
||||
}
|
||||
struct mCheatDevice* device;
|
||||
|
|
|
@ -636,12 +636,6 @@ struct mCoreThread* mCoreThreadGet(void) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(logger);
|
||||
UNUSED(level);
|
||||
|
@ -650,11 +644,14 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level
|
|||
printf("\n");
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
if (thread && level == mLOG_FATAL) {
|
||||
#ifndef DISABLE_THREADING
|
||||
mCoreThreadMarkCrashed(thread);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct mLogger* mCoreThreadLogger(void) {
|
||||
struct mCoreThread* thread = mCoreThreadGet();
|
||||
|
|
|
@ -46,8 +46,8 @@ static void _redoCacheSize(struct mTileCache* cache) {
|
|||
unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
|
||||
cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles * size);
|
||||
cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
|
||||
cache->globalPaletteVersion = malloc(size * sizeof(*cache->globalPaletteVersion));
|
||||
cache->palette = malloc(size * bpp * sizeof(*cache->palette));
|
||||
cache->globalPaletteVersion = calloc(size, sizeof(*cache->globalPaletteVersion));
|
||||
cache->palette = calloc(size * bpp, sizeof(*cache->palette));
|
||||
}
|
||||
|
||||
void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
|
||||
|
|
|
@ -174,7 +174,7 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector*
|
|||
for (accum = dv; accum; accum = accum->next) {
|
||||
++args;
|
||||
}
|
||||
const char** arglist = malloc(sizeof(const char*) * (args + 1));
|
||||
const char** arglist = calloc(args + 1, sizeof(const char*));
|
||||
args = 0;
|
||||
for (accum = dv; accum; accum = accum->next) {
|
||||
arglist[args] = accum->charValue;
|
||||
|
|
|
@ -387,6 +387,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
|||
// QuickTime and a few other things require YUV420
|
||||
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
}
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 57
|
||||
if (encoder->video->codec->id == AV_CODEC_ID_FFV1) {
|
||||
av_opt_set(encoder->video->priv_data, "coder", "range_tab", 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strcmp(vcodec->name, "libx264") == 0) {
|
||||
// Try to adaptively figure out when you can use a slower encoder
|
||||
|
@ -545,8 +550,12 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
|||
#endif
|
||||
}
|
||||
if (encoder->audio) {
|
||||
#ifdef FFMPEG_USE_CODECPAR
|
||||
avcodec_free_context(&encoder->audio);
|
||||
#else
|
||||
avcodec_close(encoder->audio);
|
||||
encoder->audio = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (encoder->resampleContext) {
|
||||
|
@ -568,6 +577,7 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
|||
}
|
||||
|
||||
if (encoder->videoFrame) {
|
||||
av_freep(encoder->videoFrame->data);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
av_frame_free(&encoder->videoFrame);
|
||||
#else
|
||||
|
@ -585,8 +595,12 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
|||
}
|
||||
|
||||
if (encoder->video) {
|
||||
#ifdef FFMPEG_USE_CODECPAR
|
||||
avcodec_free_context(&encoder->video);
|
||||
#else
|
||||
avcodec_close(encoder->video);
|
||||
encoder->video = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (encoder->scaleContext) {
|
||||
|
|
|
@ -286,8 +286,8 @@ static void _GBCoreReloadConfigOption(struct mCore* core, const char* option, co
|
|||
}
|
||||
}
|
||||
|
||||
static void _GBCoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
||||
struct GB* gb = core->board;
|
||||
static void _GBCoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
const struct GB* gb = core->board;
|
||||
if (gb && (!(gb->model & GB_MODEL_SGB) || !gb->video.sgbBorders)) {
|
||||
*width = GB_VIDEO_HORIZONTAL_PIXELS;
|
||||
*height = GB_VIDEO_VERTICAL_PIXELS;
|
||||
|
@ -845,9 +845,13 @@ static size_t _GBCoreSavedataClone(struct mCore* core, void** sram) {
|
|||
vf->seek(vf, 0, SEEK_SET);
|
||||
return vf->read(vf, *sram, vf->size(vf));
|
||||
}
|
||||
*sram = malloc(gb->sramSize);
|
||||
memcpy(*sram, gb->memory.sram, gb->sramSize);
|
||||
return gb->sramSize;
|
||||
if (gb->sramSize) {
|
||||
*sram = malloc(gb->sramSize);
|
||||
memcpy(*sram, gb->memory.sram, gb->sramSize);
|
||||
return gb->sramSize;
|
||||
}
|
||||
*sram = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t size, bool writeback) {
|
||||
|
|
88
src/gb/mbc.c
88
src/gb/mbc.c
|
@ -36,6 +36,7 @@ static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
|
|||
static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
|
||||
|
@ -43,6 +44,7 @@ static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
|
|||
static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
|
||||
|
||||
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||
static void _GBPocketCamCapture(struct GBMemory*);
|
||||
|
@ -273,6 +275,7 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
}
|
||||
gb->memory.mbcRead = NULL;
|
||||
gb->memory.directSramAccess = true;
|
||||
switch (gb->memory.mbcType) {
|
||||
case GB_MBC_NONE:
|
||||
gb->memory.mbcWrite = _GBMBCNone;
|
||||
|
@ -288,6 +291,7 @@ void GBMBCInit(struct GB* gb) {
|
|||
case GB_MBC2:
|
||||
gb->memory.mbcWrite = _GBMBC2;
|
||||
gb->memory.mbcRead = _GBMBC2Read;
|
||||
gb->memory.directSramAccess = false;
|
||||
gb->sramSize = 0x100;
|
||||
break;
|
||||
case GB_MBC3:
|
||||
|
@ -300,9 +304,9 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->memory.mbcWrite = _GBMBC5;
|
||||
break;
|
||||
case GB_MBC6:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
|
||||
gb->memory.mbcWrite = _GBMBC6;
|
||||
gb->memory.mbcRead = _GBMBC6Read;
|
||||
gb->memory.directSramAccess = false;
|
||||
break;
|
||||
case GB_MBC7:
|
||||
gb->memory.mbcWrite = _GBMBC7;
|
||||
|
@ -342,6 +346,10 @@ void GBMBCInit(struct GB* gb) {
|
|||
case GB_UNL_WISDOM_TREE:
|
||||
gb->memory.mbcWrite = _GBWisdomTree;
|
||||
break;
|
||||
case GB_UNL_PKJD:
|
||||
gb->memory.mbcWrite = _GBPKJD;
|
||||
gb->memory.mbcRead = _GBPKJDRead;
|
||||
break;
|
||||
}
|
||||
|
||||
gb->memory.currentBank = 1;
|
||||
|
@ -626,10 +634,10 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->mbcState.mbc6.sramAccess = false;
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->mbcState.mbc6.sramAccess = true;
|
||||
memory->sramAccess = true;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
|
@ -655,7 +663,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
if (memory->mbcState.mbc6.sramAccess) {
|
||||
if (memory->sramAccess) {
|
||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
}
|
||||
break;
|
||||
|
@ -663,7 +671,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
case 0x2D:
|
||||
case 0x2E:
|
||||
case 0x2F:
|
||||
if (memory->mbcState.mbc6.sramAccess) {
|
||||
if (memory->sramAccess) {
|
||||
memory->mbcState.mbc6.sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
}
|
||||
break;
|
||||
|
@ -674,7 +682,7 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
|
||||
uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
|
||||
if (!memory->mbcState.mbc6.sramAccess) {
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
switch (address >> 12) {
|
||||
|
@ -1229,6 +1237,74 @@ void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address >> 13) {
|
||||
case 0x2:
|
||||
if (value < 8) {
|
||||
memory->directSramAccess = true;
|
||||
memory->activeRtcReg = 0;
|
||||
} else if (value >= 0xD && value <= 0xF) {
|
||||
memory->directSramAccess = false;
|
||||
memory->rtcAccess = false;
|
||||
memory->activeRtcReg = value - 8;
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
if (!memory->sramAccess) {
|
||||
return;
|
||||
}
|
||||
switch (memory->activeRtcReg) {
|
||||
case 0:
|
||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value;
|
||||
break;
|
||||
case 7:
|
||||
switch (value) {
|
||||
case 0x11:
|
||||
memory->mbcState.pkjd.reg[0]--;
|
||||
break;
|
||||
case 0x12:
|
||||
memory->mbcState.pkjd.reg[1]--;
|
||||
break;
|
||||
case 0x41:
|
||||
memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1];
|
||||
break;
|
||||
case 0x42:
|
||||
memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0];
|
||||
break;
|
||||
case 0x51:
|
||||
memory->mbcState.pkjd.reg[0]++;
|
||||
break;
|
||||
case 0x52:
|
||||
memory->mbcState.pkjd.reg[1]--;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_GBMBC3(gb, address, value);
|
||||
}
|
||||
|
||||
static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
switch (memory->activeRtcReg) {
|
||||
case 0:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
case 5:
|
||||
case 6:
|
||||
return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb) {
|
||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
|
|
|
@ -361,7 +361,7 @@ void GBStore8(struct SM83Core* cpu, uint16_t address, int8_t value) {
|
|||
case GB_REGION_EXTERNAL_RAM + 1:
|
||||
if (memory->rtcAccess) {
|
||||
memory->rtcRegs[memory->activeRtcReg] = value;
|
||||
} else if (memory->sramAccess && memory->sram && memory->mbcType != GB_MBC2) {
|
||||
} else if (memory->sramAccess && memory->sram && memory->directSramAccess) {
|
||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
|
||||
} else {
|
||||
memory->mbcWrite(gb, address, value);
|
||||
|
|
|
@ -495,6 +495,7 @@ static const struct GBCartridgeOverride _overrides[] = {
|
|||
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||
{ 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
|
||||
{ 0x30f8f86c, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
|
||||
|
||||
{ 0, 0, 0, { 0 } }
|
||||
};
|
||||
|
|
133
src/gba/bios.c
133
src/gba/bios.c
|
@ -104,81 +104,86 @@ static void _RegisterRamReset(struct GBA* gba) {
|
|||
memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32));
|
||||
}
|
||||
if (registers & 0x80) {
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x04, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x06, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x08, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x0E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x12, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x14, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x16, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x18, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x1E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DISPSTAT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_VCOUNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x28, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x2C, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG2X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG2Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x38, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | 0x3C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x40, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x42, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x44, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x46, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x48, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x4A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x4C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x50, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x52, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x54, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xB8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xBE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xC8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xCE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD0, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD2, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD4, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD6, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xD8, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0xDE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x100, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x102, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x104, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x106, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x108, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10A, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10C, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x10E, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x200, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x202, 0xFFFF, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x204, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | 0x208, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG3X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG3Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN0H, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN1H, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN0V, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN1V, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WININ, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WINOUT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_MOSAIC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDALPHA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDY, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IF, 0xFFFF, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WAITCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IME, 0, 0);
|
||||
}
|
||||
if (registers & 0x9C) {
|
||||
gba->video.renderer->reset(gba->video.renderer);
|
||||
gba->video.renderer->writeVideoRegister(gba->video.renderer, REG_DISPCNT, gba->memory.io[REG_DISPCNT >> 1]);
|
||||
int i;
|
||||
for (i = REG_BG0CNT; i < REG_SOUND1CNT_LO; i += 2) {
|
||||
gba->video.renderer->writeVideoRegister(gba->video.renderer, i, gba->memory.io[i >> 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -396,9 +396,9 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
|
|||
}
|
||||
}
|
||||
|
||||
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
|
||||
static void _GBACoreDesiredVideoDimensions(const struct mCore* core, unsigned* width, unsigned* height) {
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
const struct GBACore* gbacore = (const struct GBACore*) core;
|
||||
int scale = gbacore->glRenderer.scale;
|
||||
#else
|
||||
UNUSED(core);
|
||||
|
|
|
@ -288,7 +288,7 @@ static void _eReaderAddress(uint8_t* origin, int a) {
|
|||
}
|
||||
|
||||
static void _eReaderReedSolomon(const uint8_t* input, uint8_t* output) {
|
||||
uint8_t rsBuffer[64] = {};
|
||||
uint8_t rsBuffer[64] = { 0 };
|
||||
int i;
|
||||
for (i = 0; i < 48; ++i) {
|
||||
rsBuffer[63 - i] = input[i];
|
||||
|
|
115
src/gba/memory.c
115
src/gba/memory.c
|
@ -29,6 +29,7 @@ static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx
|
|||
|
||||
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
||||
static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait);
|
||||
static int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra);
|
||||
|
||||
static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 };
|
||||
static const char GBA_BASE_WAITSTATES_32[16] = { 0, 0, 5, 0, 0, 1, 1, 0, 7, 7, 9, 9, 13, 13, 9 };
|
||||
|
@ -337,33 +338,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
}
|
||||
|
||||
#define LOAD_BAD \
|
||||
if (gba->performingDMA || cpu->gprs[ARM_PC] - gba->dmaPC == (gba->cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM)) { \
|
||||
value = gba->bus; \
|
||||
} else { \
|
||||
value = cpu->prefetch[1]; \
|
||||
if (cpu->executionMode == MODE_THUMB) { \
|
||||
/* http://ngemu.com/threads/gba-open-bus.170809/ */ \
|
||||
switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) { \
|
||||
case REGION_BIOS: \
|
||||
case REGION_OAM: \
|
||||
/* This isn't right half the time, but we don't have $+6 handy */ \
|
||||
value <<= 16; \
|
||||
value |= cpu->prefetch[0]; \
|
||||
break; \
|
||||
case REGION_WORKING_IRAM: \
|
||||
/* This doesn't handle prefetch clobbering */ \
|
||||
if (cpu->gprs[ARM_PC] & 2) { \
|
||||
value <<= 16; \
|
||||
value |= cpu->prefetch[0]; \
|
||||
} else { \
|
||||
value |= cpu->prefetch[0] << 16; \
|
||||
} \
|
||||
break; \
|
||||
default: \
|
||||
value |= value << 16; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
value = GBALoadBad(cpu);
|
||||
|
||||
#define LOAD_BIOS \
|
||||
if (address < SIZE_BIOS) { \
|
||||
|
@ -375,7 +350,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
} \
|
||||
} else { \
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load32: 0x%08X", address); \
|
||||
LOAD_BAD; \
|
||||
value = GBALoadBad(cpu); \
|
||||
}
|
||||
|
||||
#define LOAD_WORKING_RAM \
|
||||
|
@ -400,7 +375,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
} else { \
|
||||
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
} \
|
||||
wait += waitstatesRegion[REGION_VRAM];
|
||||
++wait; \
|
||||
if (gba->video.shouldStall) { \
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 1); \
|
||||
}
|
||||
|
||||
#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw);
|
||||
|
||||
|
@ -427,7 +405,33 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
uint32_t GBALoadBad(struct ARMCore* cpu) {
|
||||
struct GBA* gba = (struct GBA*) cpu->master;
|
||||
uint32_t value = 0;
|
||||
LOAD_BAD;
|
||||
if (gba->performingDMA || cpu->gprs[ARM_PC] - gba->dmaPC == (gba->cpu->executionMode == MODE_THUMB ? WORD_SIZE_THUMB : WORD_SIZE_ARM)) {
|
||||
value = gba->bus;
|
||||
} else {
|
||||
value = cpu->prefetch[1];
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
/* http://ngemu.com/threads/gba-open-bus.170809/ */
|
||||
switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
case REGION_OAM:
|
||||
/* This isn't right half the time, but we don't have $+6 handy */
|
||||
value <<= 16;
|
||||
value |= cpu->prefetch[0];
|
||||
break;
|
||||
case REGION_WORKING_IRAM:
|
||||
/* This doesn't handle prefetch clobbering */
|
||||
if (cpu->gprs[ARM_PC] & 2) {
|
||||
value <<= 16;
|
||||
value |= cpu->prefetch[0];
|
||||
} else {
|
||||
value |= cpu->prefetch[0] << 16;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value |= value << 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -507,8 +511,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
}
|
||||
} else {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load16: 0x%08X", address);
|
||||
LOAD_BAD;
|
||||
value = (value >> ((address & 2) * 8)) & 0xFFFF;
|
||||
value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF;
|
||||
}
|
||||
break;
|
||||
case REGION_WORKING_RAM:
|
||||
|
@ -535,6 +538,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
} else {
|
||||
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
}
|
||||
if (gba->video.shouldStall) {
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 0);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||
|
@ -591,8 +597,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
break;
|
||||
default:
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load16: 0x%08X", address);
|
||||
LOAD_BAD;
|
||||
value = (value >> ((address & 2) * 8)) & 0xFFFF;
|
||||
value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -625,8 +630,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
}
|
||||
} else {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load8: 0x%08x", address);
|
||||
LOAD_BAD;
|
||||
value = (value >> ((address & 3) * 8)) & 0xFF;
|
||||
value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF;
|
||||
}
|
||||
break;
|
||||
case REGION_WORKING_RAM:
|
||||
|
@ -653,6 +657,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
} else {
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
||||
}
|
||||
if (gba->video.shouldStall) {
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 0);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)];
|
||||
|
@ -701,8 +708,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
break;
|
||||
default:
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Load8: 0x%08x", address);
|
||||
LOAD_BAD;
|
||||
value = (value >> ((address & 3) * 8)) & 0xFF;
|
||||
value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -755,7 +761,10 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||
} \
|
||||
} \
|
||||
wait += waitstatesRegion[REGION_VRAM];
|
||||
++wait; \
|
||||
if (gba->video.shouldStall) { \
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 1); \
|
||||
}
|
||||
|
||||
#define STORE_OAM \
|
||||
LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \
|
||||
|
@ -880,6 +889,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
}
|
||||
}
|
||||
if (gba->video.shouldStall) {
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 0);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw);
|
||||
|
@ -981,6 +993,9 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
|
|||
gba->video.vram[(address & 0x1FFFE) >> 1] = ((uint8_t) value) | (value << 8);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
}
|
||||
if (gba->video.shouldStall) {
|
||||
wait += GBAMemoryStallVRAM(gba, wait, 0);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address);
|
||||
|
@ -1662,6 +1677,28 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
|||
return wait;
|
||||
}
|
||||
|
||||
int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra) {
|
||||
UNUSED(extra);
|
||||
// TODO
|
||||
uint16_t dispcnt = gba->memory.io[REG_DISPCNT >> 1];
|
||||
int32_t stall = 0;
|
||||
switch (GBARegisterDISPCNTGetMode(dispcnt)) {
|
||||
case 2:
|
||||
if (GBARegisterDISPCNTIsBg2Enable(dispcnt) && GBARegisterDISPCNTIsBg3Enable(dispcnt)) {
|
||||
// If both backgrounds are enabled, VRAM access is entirely blocked during hdraw
|
||||
stall = mTimingUntil(&gba->timing, &gba->video.event);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
stall -= wait;
|
||||
if (stall < 0) {
|
||||
return 0;
|
||||
}
|
||||
return stall;
|
||||
}
|
||||
|
||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) {
|
||||
memcpy(state->wram, memory->wram, SIZE_WORKING_RAM);
|
||||
memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM);
|
||||
|
|
|
@ -8,6 +8,17 @@
|
|||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
||||
#define BACKGROUND_BITMAP_ITERATE(W, H) \
|
||||
x += background->dx; \
|
||||
y += background->dy; \
|
||||
\
|
||||
if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
|
||||
continue; \
|
||||
} else { \
|
||||
localX = x; \
|
||||
localY = y; \
|
||||
}
|
||||
|
||||
#define MODE_2_COORD_OVERFLOW \
|
||||
localX = x & (sizeAdjusted - 1); \
|
||||
localY = y & (sizeAdjusted - 1); \
|
||||
|
@ -123,7 +134,7 @@ void GBAVideoSoftwareRendererDrawBackgroundMode3(struct GBAVideoSoftwareRenderer
|
|||
void GBAVideoSoftwareRendererDrawBackgroundMode4(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
|
||||
BACKGROUND_BITMAP_INIT;
|
||||
|
||||
uint16_t color = renderer->normalPalette[0];
|
||||
uint16_t color = 0;
|
||||
uint32_t offset = 0;
|
||||
if (GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt)) {
|
||||
offset = 0xA000;
|
||||
|
|
|
@ -222,17 +222,6 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
UNUSED(palette); \
|
||||
PREPARE_OBJWIN;
|
||||
|
||||
#define BACKGROUND_BITMAP_ITERATE(W, H) \
|
||||
x += background->dx; \
|
||||
y += background->dy; \
|
||||
\
|
||||
if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
|
||||
continue; \
|
||||
} else { \
|
||||
localX = x; \
|
||||
localY = y; \
|
||||
}
|
||||
|
||||
#define TEST_LAYER_ENABLED(X) \
|
||||
(softwareRenderer->bg[X].enabled == 3 && \
|
||||
(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
|
||||
|
|
|
@ -139,6 +139,7 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
|||
} else if (savedata->vf) {
|
||||
off_t read = 0;
|
||||
uint8_t buffer[2048];
|
||||
savedata->vf->seek(savedata->vf, 0, SEEK_SET);
|
||||
do {
|
||||
read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer));
|
||||
out->write(out, buffer, read);
|
||||
|
|
|
@ -107,6 +107,7 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
video->renderer->vramOBJ[1] = &video->vram[0xA000];
|
||||
video->renderer->vramOBJ[2] = _zeroes;
|
||||
video->renderer->vramOBJ[3] = _zeroes;
|
||||
video->shouldStall = 0;
|
||||
|
||||
memset(video->palette, 0, sizeof(video->palette));
|
||||
memset(video->oam.raw, 0, sizeof(video->oam.raw));
|
||||
|
@ -160,6 +161,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
|
||||
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
|
||||
video->renderer->drawScanline(video->renderer, video->vcount);
|
||||
video->shouldStall = 1;
|
||||
}
|
||||
|
||||
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
|
||||
|
@ -219,6 +221,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
|
||||
GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate);
|
||||
}
|
||||
video->shouldStall = 0;
|
||||
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
|
||||
}
|
||||
|
||||
|
@ -359,6 +362,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
|||
}
|
||||
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
|
||||
|
||||
video->shouldStall = 0;
|
||||
int32_t flags;
|
||||
LOAD_32(flags, 0, &state->video.flags);
|
||||
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
|
||||
|
@ -375,6 +379,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
|
|||
break;
|
||||
case 2:
|
||||
video->event.callback = _startHblank;
|
||||
video->shouldStall = 1;
|
||||
break;
|
||||
case 3:
|
||||
video->event.callback = _midHblank;
|
||||
|
|
|
@ -31,7 +31,7 @@ struct GUIFont* GUIFontCreate(void) {
|
|||
guiFont->font = fontGetSystemFont();
|
||||
TGLP_s* glyphInfo = fontGetGlyphInfo(guiFont->font);
|
||||
guiFont->size = FONT_SIZE / glyphInfo->cellHeight;
|
||||
guiFont->sheets = malloc(sizeof(*guiFont->sheets) * glyphInfo->nSheets);
|
||||
guiFont->sheets = calloc(glyphInfo->nSheets, sizeof(*guiFont->sheets));
|
||||
|
||||
int i;
|
||||
for (i = 0; i < glyphInfo->nSheets; ++i) {
|
||||
|
|
|
@ -2,14 +2,6 @@
|
|||
# SDL2_FOUND, if false, do not try to link to SDL2
|
||||
# SDL2_INCLUDE_DIRS, where to find SDL.h
|
||||
#
|
||||
# This module responds to the the flag:
|
||||
# SDL2_BUILDING_LIBRARY
|
||||
# If this is defined, then no SDL2main will be linked in because
|
||||
# only applications need main().
|
||||
# Otherwise, it is assumed you are building an application and this
|
||||
# module will attempt to locate and set the the proper link flags
|
||||
# as part of the returned SDL2_LIBRARIES variable.
|
||||
#
|
||||
# Don't forget to include SDLmain.h and SDLmain.m your project for the
|
||||
# OS X framework based version. (Other versions link to -lSDL2main which
|
||||
# this module will try to find on your behalf.) Also for OS X, this
|
||||
|
@ -109,21 +101,19 @@ FIND_LIBRARY(SDL2_LIBRARIES_TEMP
|
|||
PATHS ${SDL2_SEARCH_PATHS} ${SDL2_INCLUDE_DIRS}/../..
|
||||
)
|
||||
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
|
||||
# Non-OS X framework versions expect you to also dynamically link to
|
||||
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
|
||||
# seem to provide SDL2main for compatibility even though they don't
|
||||
# necessarily need it.
|
||||
FIND_LIBRARY(SDL2MAIN_LIBRARY
|
||||
NAMES SDL2main
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib64 lib
|
||||
PATHS ${SDL2_SEARCH_PATHS}
|
||||
)
|
||||
ENDIF(NOT ${SDL2_INCLUDE_DIRS} MATCHES ".framework")
|
||||
|
||||
# SDL2 may require threads on your system.
|
||||
# The Apple build may not need an explicit flag because one of the
|
||||
|
@ -141,13 +131,6 @@ IF(MINGW)
|
|||
ENDIF(MINGW)
|
||||
|
||||
IF(SDL2_LIBRARIES_TEMP)
|
||||
# For SDL2main
|
||||
IF(NOT SDL2_BUILDING_LIBRARY)
|
||||
IF(SDL2MAIN_LIBRARY)
|
||||
SET(SDL2_LIBRARIES_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARIES_TEMP})
|
||||
ENDIF(SDL2MAIN_LIBRARY)
|
||||
ENDIF(NOT SDL2_BUILDING_LIBRARY)
|
||||
|
||||
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
|
||||
# CMake doesn't display the -framework Cocoa string in the UI even
|
||||
# though it actually is there if I modify a pre-used variable.
|
||||
|
|
|
@ -921,7 +921,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
|||
success = false;
|
||||
}
|
||||
if (success) {
|
||||
struct mGLES2Shader* shaderBlock = malloc(sizeof(struct mGLES2Shader) * inShaders);
|
||||
struct mGLES2Shader* shaderBlock = calloc(inShaders, sizeof(struct mGLES2Shader));
|
||||
int n;
|
||||
for (n = 0; n < inShaders; ++n) {
|
||||
char passName[12];
|
||||
|
@ -980,7 +980,7 @@ bool mGLES2ShaderLoad(struct VideoShader* shader, struct VDir* dir) {
|
|||
}
|
||||
}
|
||||
u = mGLES2UniformListSize(&uniformVector);
|
||||
struct mGLES2Uniform* uniformBlock = malloc(sizeof(*uniformBlock) * u);
|
||||
struct mGLES2Uniform* uniformBlock = calloc(u, sizeof(*uniformBlock));
|
||||
memcpy(uniformBlock, mGLES2UniformListGetPointer(&uniformVector, 0), sizeof(*uniformBlock) * u);
|
||||
mGLES2UniformListDeinit(&uniformVector);
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ if(APPLE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
|
||||
endif()
|
||||
|
||||
get_target_property(QT_TYPE Qt5::Core TYPE)
|
||||
if(QT_TYPE STREQUAL STATIC_LIBRARY)
|
||||
set(QT_STATIC ON)
|
||||
|
@ -285,6 +289,10 @@ qt5_wrap_ui(UI_SRC ${UI_FILES})
|
|||
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
|
||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}" COMPILE_OPTIONS "${FEATURE_FLAGS}")
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::Network)
|
||||
if(BUILD_GL OR BUILD_GLES2 OR BUILD_EPOXY)
|
||||
list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
|
|
|
@ -83,6 +83,7 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr
|
|||
|
||||
mCore* core = mCoreFindVF(vf);
|
||||
if (!core) {
|
||||
vf->close(vf);
|
||||
LOG(QT, ERROR) << tr("Could not load game. Are you sure it's in the correct format?");
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -386,12 +386,6 @@ void FrameView::refreshVl() {
|
|||
m_nextFrame = VFileMemChunk(nullptr, 0);
|
||||
if (m_currentFrame) {
|
||||
m_controller->endVideoLog(false);
|
||||
VFile* currentFrame = VFileMemChunk(nullptr, m_currentFrame->size(m_currentFrame));
|
||||
void* buffer = currentFrame->map(currentFrame, m_currentFrame->size(m_currentFrame), MAP_WRITE);
|
||||
m_currentFrame->seek(m_currentFrame, 0, SEEK_SET);
|
||||
m_currentFrame->read(m_currentFrame, buffer, m_currentFrame->size(m_currentFrame));
|
||||
currentFrame->unmap(currentFrame, buffer, m_currentFrame->size(m_currentFrame));
|
||||
m_currentFrame = currentFrame;
|
||||
QMetaObject::invokeMethod(this, "newVl");
|
||||
}
|
||||
m_controller->endVideoLog();
|
||||
|
@ -403,12 +397,16 @@ void FrameView::newVl() {
|
|||
m_glowTimer.start();
|
||||
}
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (!m_currentFrame) {
|
||||
return;
|
||||
}
|
||||
if (m_vl) {
|
||||
m_vl->deinit(m_vl);
|
||||
}
|
||||
m_vl = mCoreFindVF(m_currentFrame);
|
||||
m_vl->init(m_vl);
|
||||
m_vl->loadROM(m_vl, m_currentFrame);
|
||||
m_currentFrame = nullptr;
|
||||
mCoreInitConfig(m_vl, nullptr);
|
||||
unsigned width, height;
|
||||
m_vl->desiredVideoDimensions(m_vl, &width, &height);
|
||||
|
|
|
@ -93,6 +93,10 @@ MemoryModel::MemoryModel(QWidget* parent)
|
|||
++m_top;
|
||||
} else if (action == QSlider::SliderSingleStepSub) {
|
||||
--m_top;
|
||||
} else if (action == QSlider::SliderPageStepAdd) {
|
||||
m_top += (viewport()->size().height() - m_cellHeight) / m_cellHeight;
|
||||
} else if (action == QSlider::SliderPageStepSub) {
|
||||
m_top -= (viewport()->size().height() - m_cellHeight) / m_cellHeight;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -590,6 +594,12 @@ void MemoryModel::keyPressEvent(QKeyEvent* event) {
|
|||
case Qt::Key_Down:
|
||||
adjustCursor(16, event->modifiers() & Qt::ShiftModifier);
|
||||
return;
|
||||
case Qt::Key_PageUp:
|
||||
adjustCursor(-16 * ((viewport()->size().height() - m_cellHeight) / m_cellHeight), event->modifiers() & Qt::ShiftModifier);
|
||||
return;
|
||||
case Qt::Key_PageDown:
|
||||
adjustCursor(16 * ((viewport()->size().height() - m_cellHeight) / m_cellHeight), event->modifiers() & Qt::ShiftModifier);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
|
|||
s_mbcList.append(GB_TAMA5);
|
||||
s_mbcList.append(GB_HuC1);
|
||||
s_mbcList.append(GB_HuC3);
|
||||
s_mbcList.append(GB_UNL_WISDOM_TREE);
|
||||
s_mbcList.append(GB_UNL_PKJD);
|
||||
}
|
||||
if (s_gbModelList.isEmpty()) {
|
||||
// NB: Keep in sync with OverrideView.ui
|
||||
|
|
|
@ -354,6 +354,16 @@
|
|||
<string>HuC-3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Wisdom Tree (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pokémon Jade/Diamond (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
|
|
|
@ -12,6 +12,8 @@ using namespace QGBA;
|
|||
RotatedHeaderView::RotatedHeaderView(Qt::Orientation orientation, QWidget* parent)
|
||||
: QHeaderView(orientation, parent)
|
||||
{
|
||||
int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
|
||||
setMinimumSectionSize(fontMetrics().height() + margin);
|
||||
}
|
||||
|
||||
void RotatedHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const {
|
||||
|
|
|
@ -394,6 +394,7 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("showFps", m_ui.showFps);
|
||||
saveSetting("cheatAutoload", m_ui.cheatAutoload);
|
||||
saveSetting("cheatAutosave", m_ui.cheatAutosave);
|
||||
saveSetting("showFilename", m_ui.showFilename);
|
||||
saveSetting("autoload", m_ui.autoload);
|
||||
saveSetting("autosave", m_ui.autosave);
|
||||
saveSetting("logToFile", m_ui.logToFile);
|
||||
|
@ -573,6 +574,7 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("showFps", m_ui.showFps, true);
|
||||
loadSetting("cheatAutoload", m_ui.cheatAutoload, true);
|
||||
loadSetting("cheatAutosave", m_ui.cheatAutosave, true);
|
||||
loadSetting("showFilename", m_ui.showFilename, false);
|
||||
loadSetting("autoload", m_ui.autoload, true);
|
||||
loadSetting("autosave", m_ui.autosave, false);
|
||||
loadSetting("logToFile", m_ui.logToFile);
|
||||
|
|
|
@ -586,21 +586,21 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<item row="14" column="1">
|
||||
<widget class="QCheckBox" name="useDiscordPresence">
|
||||
<property name="text">
|
||||
<string>Enable Discord Rich Presence</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="0" colspan="2">
|
||||
<item row="15" column="0" colspan="2">
|
||||
<widget class="Line" name="line_13">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<item row="16" column="1">
|
||||
<widget class="QCheckBox" name="autosave">
|
||||
<property name="text">
|
||||
<string>Automatically save state</string>
|
||||
|
@ -610,7 +610,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="QCheckBox" name="autoload">
|
||||
<property name="text">
|
||||
<string>Automatically load state</string>
|
||||
|
@ -620,14 +620,14 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0" colspan="2">
|
||||
<item row="18" column="0" colspan="2">
|
||||
<widget class="Line" name="line_16">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<item row="19" column="1">
|
||||
<widget class="QCheckBox" name="cheatAutosave">
|
||||
<property name="text">
|
||||
<string>Automatically save cheats</string>
|
||||
|
@ -637,7 +637,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="19" column="1">
|
||||
<item row="20" column="1">
|
||||
<widget class="QCheckBox" name="cheatAutoload">
|
||||
<property name="text">
|
||||
<string>Automatically load cheats</string>
|
||||
|
@ -647,7 +647,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="showOSD">
|
||||
<property name="text">
|
||||
<string>Show OSD messages</string>
|
||||
|
@ -657,6 +657,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="showFilename">
|
||||
<property name="text">
|
||||
<string>Show filename instead of ROM name in title bar</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="emulation">
|
||||
|
|
|
@ -80,7 +80,7 @@ QModelIndex ShortcutModel::index(int row, int column, const QModelIndex& parent)
|
|||
pmenu = static_cast<Item*>(parent.internalPointer())->name;
|
||||
}
|
||||
QString name = m_controller->name(row, pmenu);
|
||||
Item* item = &(*const_cast<QHash<QString, Item>*>(&m_cache))[name];
|
||||
Item* item = &m_cache[name];
|
||||
item->name = name;
|
||||
item->shortcut = m_controller->shortcut(name);
|
||||
return createIndex(row, column, item);
|
||||
|
@ -95,7 +95,7 @@ QModelIndex ShortcutModel::parent(const QModelIndex& index) const {
|
|||
if (parent.isNull()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
Item* pitem = &(*const_cast<QHash<QString, Item>*>(&m_cache))[parent];
|
||||
Item* pitem = &m_cache[parent];
|
||||
pitem->name = parent;
|
||||
pitem->shortcut = m_controller->shortcut(parent);
|
||||
return createIndex(m_controller->indexIn(parent), 0, pitem);
|
||||
|
|
|
@ -44,7 +44,7 @@ private:
|
|||
const Shortcut* shortcut = nullptr;
|
||||
};
|
||||
|
||||
QHash<QString, Item> m_cache;
|
||||
mutable QHash<QString, Item> m_cache;
|
||||
};
|
||||
|
||||
}
|
|
@ -99,12 +99,11 @@ VideoView::VideoView(QWidget* parent)
|
|||
updatePresets();
|
||||
|
||||
setPreset({
|
||||
.container = "MKV",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "FLAC",
|
||||
.vbr = -1,
|
||||
.abr = 0,
|
||||
.dims = QSize(),
|
||||
"MKV",
|
||||
"h.264",
|
||||
"FLAC",
|
||||
-1,
|
||||
0,
|
||||
});
|
||||
showAdvanced(false);
|
||||
}
|
||||
|
@ -112,96 +111,58 @@ VideoView::VideoView(QWidget* parent)
|
|||
void VideoView::updatePresets() {
|
||||
m_presets.clear();
|
||||
|
||||
addPreset(m_ui.preset4K, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = maintainAspect(QSize(3840, 2160))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset1080, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = maintainAspect(QSize(1920, 1080))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset720, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = maintainAspect(QSize(1280, 720))
|
||||
});
|
||||
|
||||
addPreset(m_ui.preset480, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = maintainAspect(QSize(720, 480))
|
||||
});
|
||||
addPreset(m_ui.preset4K, { maintainAspect(QSize(3840, 2160)) });
|
||||
addPreset(m_ui.preset1080, { maintainAspect(QSize(1920, 1080)) });
|
||||
addPreset(m_ui.preset720, { maintainAspect(QSize(1280, 720)) });
|
||||
addPreset(m_ui.preset480, { maintainAspect(QSize(720, 480)) });
|
||||
|
||||
if (m_nativeWidth && m_nativeHeight) {
|
||||
addPreset(m_ui.presetNative, {
|
||||
.container = QString(),
|
||||
.vcodec = QString(),
|
||||
.acodec = QString(),
|
||||
.vbr = 0,
|
||||
.abr = 0,
|
||||
.dims = QSize(m_nativeWidth, m_nativeHeight)
|
||||
});
|
||||
addPreset(m_ui.presetNative, { QSize(m_nativeWidth, m_nativeHeight) });
|
||||
m_ui.presetNative->setEnabled(true);
|
||||
}
|
||||
|
||||
addPreset(m_ui.presetHQ, {
|
||||
.container = "MP4",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "AAC",
|
||||
.vbr = 8000,
|
||||
.abr = 384,
|
||||
.dims = maintainAspect(QSize(1920, 1080))
|
||||
"MP4",
|
||||
"h.264",
|
||||
"AAC",
|
||||
8000,
|
||||
384,
|
||||
maintainAspect({ 1920, 1080 })
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetYoutube, {
|
||||
.container = "MP4",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "AAC",
|
||||
.vbr = 5000,
|
||||
.abr = 256,
|
||||
.dims = maintainAspect(QSize(1280, 720))
|
||||
"MP4",
|
||||
"h.264",
|
||||
"AAC",
|
||||
5000,
|
||||
256,
|
||||
maintainAspect({ 1280, 720 })
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetWebM, {
|
||||
.container = "WebM",
|
||||
.vcodec = "VP9",
|
||||
.acodec = "Opus",
|
||||
.vbr = 800,
|
||||
.abr = 128
|
||||
"WebM",
|
||||
"VP9",
|
||||
"Opus",
|
||||
800,
|
||||
128
|
||||
});
|
||||
|
||||
addPreset(m_ui.presetMP4, {
|
||||
.container = "MP4",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "AAC",
|
||||
.vbr = 800,
|
||||
.abr = 128
|
||||
"MP4",
|
||||
"h.264",
|
||||
"AAC",
|
||||
800,
|
||||
128
|
||||
});
|
||||
|
||||
if (m_nativeWidth && m_nativeHeight) {
|
||||
addPreset(m_ui.presetLossless, {
|
||||
.container = "MKV",
|
||||
.vcodec = "h.264",
|
||||
.acodec = "FLAC",
|
||||
.vbr = -1,
|
||||
.abr = 0,
|
||||
.dims = QSize(m_nativeWidth, m_nativeHeight)
|
||||
"MKV",
|
||||
"h.264",
|
||||
"FLAC",
|
||||
-1,
|
||||
0,
|
||||
{ m_nativeWidth, m_nativeHeight }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -455,12 +416,12 @@ void VideoView::updateAspectRatio(int width, int height, bool force) {
|
|||
|
||||
void VideoView::uncheckIncompatible() {
|
||||
Preset current = {
|
||||
.container = m_container,
|
||||
.vcodec = m_videoCodec,
|
||||
.acodec = m_audioCodec,
|
||||
.vbr = m_vbr / 1000,
|
||||
.abr = m_abr / 1000,
|
||||
.dims = QSize(m_width, m_height)
|
||||
m_container,
|
||||
m_videoCodec,
|
||||
m_audioCodec,
|
||||
m_vbr / 1000,
|
||||
m_abr / 1000,
|
||||
{ m_width, m_height }
|
||||
};
|
||||
|
||||
m_ui.presets->setExclusive(false);
|
||||
|
|
|
@ -71,6 +71,15 @@ private:
|
|||
int abr;
|
||||
QSize dims;
|
||||
|
||||
Preset() {}
|
||||
Preset(QString container, QString vcodec, QString acodec, int vbr, int abr, QSize dims = QSize())
|
||||
: container(container)
|
||||
, vcodec(vcodec)
|
||||
, acodec(acodec)
|
||||
, vbr(vbr)
|
||||
, abr(abr)
|
||||
, dims(dims) {}
|
||||
Preset(QSize dims) : dims(dims) {}
|
||||
bool compatible(const Preset&) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -1096,18 +1096,25 @@ void Window::updateTitle(float fps) {
|
|||
const NoIntroDB* db = GBAApp::app()->gameDB();
|
||||
NoIntroGame game{};
|
||||
uint32_t crc32 = 0;
|
||||
m_controller->thread()->core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32);
|
||||
|
||||
char gameTitle[17] = { '\0' };
|
||||
mCore* core = m_controller->thread()->core;
|
||||
core->getGameTitle(core, gameTitle);
|
||||
title = gameTitle;
|
||||
core->checksum(m_controller->thread()->core, &crc32, CHECKSUM_CRC32);
|
||||
QString filePath = windowFilePath();
|
||||
|
||||
if (m_config->getOption("showFilename").toInt() && !filePath.isNull()) {
|
||||
QFileInfo fileInfo(filePath);
|
||||
title = fileInfo.fileName();
|
||||
} else {
|
||||
char gameTitle[17] = { '\0' };
|
||||
core->getGameTitle(core, gameTitle);
|
||||
title = gameTitle;
|
||||
|
||||
#ifdef USE_SQLITE3
|
||||
if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) {
|
||||
title = QLatin1String(game.name);
|
||||
}
|
||||
if (db && crc32 && NoIntroDBLookupGameByCRC(db, crc32, &game)) {
|
||||
title = QLatin1String(game.name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MultiplayerController* multiplayer = m_controller->multiplayerController();
|
||||
if (multiplayer && multiplayer->attached() > 1) {
|
||||
title += tr(" - Player %1 of %2").arg(multiplayer->playerId(m_controller.get()) + 1).arg(multiplayer->attached());
|
||||
|
|
|
@ -4495,7 +4495,7 @@ Game Boy Advance è un marchio registrato di Nintendo Co., Ltd.</translation>
|
|||
<message>
|
||||
<location filename="../SensorView.ui" line="97"/>
|
||||
<source>MM/dd/yy hh:mm:ss AP</source>
|
||||
<translation>gg/MM/aa OO:mm:ss</translation>
|
||||
<translation>dd/MM/yy HH:mm:ss</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SensorView.ui" line="107"/>
|
||||
|
|
|
@ -114,6 +114,8 @@ set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATU
|
|||
target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
if(NOT WIN32)
|
||||
set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME})
|
||||
else()
|
||||
set_target_properties(${BINARY_NAME}-sdl PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
endif()
|
||||
if (WIN32 AND MSVC)
|
||||
set_target_properties(${BINARY_NAME}-sdl PROPERTIES LINK_FLAGS "/SUBSYSTEM:CONSOLE")
|
||||
|
|
|
@ -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
|
|||
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/emitter-sm83.h>
|
||||
#include <mgba/internal/sm83/sm83.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
typedef size_t (*SM83Decoder)(uint8_t opcode, struct SM83InstructionInfo* info);
|
||||
|
||||
|
@ -504,39 +505,39 @@ static int _decodeOperand(struct SM83Operand op, uint16_t pc, char* buffer, int
|
|||
return 0;
|
||||
}
|
||||
|
||||
strncpy(buffer, " ", blen - 1);
|
||||
strlcpy(buffer, " ", blen);
|
||||
ADVANCE(1);
|
||||
|
||||
if (op.flags & SM83_OP_FLAG_MEMORY) {
|
||||
strncpy(buffer, "[", blen - 1);
|
||||
strlcpy(buffer, "[", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (op.reg) {
|
||||
int written = snprintf(buffer, blen - 1, "%s", _sm83Registers[op.reg]);
|
||||
int written = snprintf(buffer, blen, "%s", _sm83Registers[op.reg]);
|
||||
ADVANCE(written);
|
||||
} else {
|
||||
int written;
|
||||
if (op.flags & SM83_OP_FLAG_RELATIVE) {
|
||||
written = snprintf(buffer, blen - 1, "$%04X", pc + (int8_t) op.immediate);
|
||||
written = snprintf(buffer, blen, "$%04X", pc + (int8_t) op.immediate);
|
||||
} else {
|
||||
written = snprintf(buffer, blen - 1, "$%02X", op.immediate);
|
||||
written = snprintf(buffer, blen, "$%02X", op.immediate);
|
||||
}
|
||||
ADVANCE(written);
|
||||
if (op.reg) {
|
||||
strncpy(buffer, "+", blen - 1);
|
||||
strlcpy(buffer, "+", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
}
|
||||
if (op.flags & SM83_OP_FLAG_INCREMENT) {
|
||||
strncpy(buffer, "+", blen - 1);
|
||||
strlcpy(buffer, "+", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (op.flags & SM83_OP_FLAG_DECREMENT) {
|
||||
strncpy(buffer, "-", blen - 1);
|
||||
strlcpy(buffer, "-", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (op.flags & SM83_OP_FLAG_MEMORY) {
|
||||
strncpy(buffer, "]", blen - 1);
|
||||
strlcpy(buffer, "]", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
return total;
|
||||
|
@ -548,15 +549,15 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
|
|||
int total = 0;
|
||||
const char* cond = _sm83Conditions[info->condition];
|
||||
|
||||
written = snprintf(buffer, blen - 1, "%s", mnemonic);
|
||||
written = snprintf(buffer, blen, "%s", mnemonic);
|
||||
ADVANCE(written);
|
||||
|
||||
if (cond) {
|
||||
written = snprintf(buffer, blen - 1, " %s", cond);
|
||||
written = snprintf(buffer, blen, " %s", cond);
|
||||
ADVANCE(written);
|
||||
|
||||
if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) {
|
||||
strncpy(buffer, ",", blen - 1);
|
||||
strlcpy(buffer, ",", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
}
|
||||
|
@ -568,7 +569,7 @@ int SM83Disassemble(struct SM83InstructionInfo* info, uint16_t pc, char* buffer,
|
|||
|
||||
if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) {
|
||||
if (written) {
|
||||
strncpy(buffer, ",", blen - 1);
|
||||
strlcpy(buffer, ",", blen);
|
||||
ADVANCE(1);
|
||||
}
|
||||
written = _decodeOperand(info->op2, pc, buffer, blen);
|
||||
|
|
|
@ -1,20 +1,40 @@
|
|||
# inih (INI Not Invented Here)
|
||||
|
||||
[](https://travis-ci.org/benhoyt/inih)
|
||||
|
||||
**inih (INI Not Invented Here)** is a simple [.INI file](http://en.wikipedia.org/wiki/INI_file) parser written in C. It's only a couple of pages of code, and it was designed to be _small and simple_, so it's good for embedded systems. It's also more or less compatible with Python's [ConfigParser](http://docs.python.org/library/configparser.html) style of .INI files, including RFC 822-style multi-line syntax and `name: value` entries.
|
||||
|
||||
To use it, just give `ini_parse()` an INI file, and it will call a callback for every `name=value` pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation.
|
||||
|
||||
You can also call `ini_parse_file()` to parse directly from a `FILE*` object, or `ini_parse_stream()` to parse using a custom reader to implement string-based or other custom I/O ([see example code](https://github.com/benhoyt/inih/blob/master/examples/ini_buffer.c)).
|
||||
You can also call `ini_parse_file()` to parse directly from a `FILE*` object, `ini_parse_string()` to parse data from a string, or `ini_parse_stream()` to parse using a custom fgets-style reader function for custom I/O.
|
||||
|
||||
Download a release, browse the source, or read about [how to use inih in a DRY style](http://blog.brush.co.nz/2009/08/xmacros/) with X-Macros.
|
||||
|
||||
|
||||
## Compile-time options ##
|
||||
|
||||
You can control various aspects of inih using preprocessor defines:
|
||||
|
||||
### Syntax options ###
|
||||
|
||||
* **Multi-line entries:** By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add `-DINI_ALLOW_MULTILINE=0`.
|
||||
* **UTF-8 BOM:** By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add `-DINI_ALLOW_BOM=0`.
|
||||
* **Stack vs heap:** By default, inih allocates its line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`.
|
||||
* **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`.
|
||||
* **Maximum line length:** The default maximum line length is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`.
|
||||
* **Inline comments:** By default, inih allows inline comments with the `;` character. To disable, add `-DINI_ALLOW_INLINE_COMMENTS=0`. You can also specify which character(s) start an inline comment using `INI_INLINE_COMMENT_PREFIXES`.
|
||||
* **Start-of-line comments:** By default, inih allows both `;` and `#` to start a comment at the beginning of a line. You can override this by changing `INI_START_COMMENT_PREFIXES`.
|
||||
* **Allow no value:** By default, inih treats a name with no value (no `=` or `:` on the line) as an error. To allow names with no values, add `-DINI_ALLOW_NO_VALUE=1`, and inih will call your handler function with value set to NULL.
|
||||
|
||||
### Parsing options ###
|
||||
|
||||
* **Stop on first error:** By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add `-DINI_STOP_ON_FIRST_ERROR=1`.
|
||||
* **Report line numbers:** By default, the `ini_handler` callback doesn't receive the line number as a parameter. If you need that, add `-DINI_HANDLER_LINENO=1`.
|
||||
* **Call handler on new section:** By default, inih only calls the handler on each `name=value` pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add `-DINI_CALL_HANDLER_ON_NEW_SECTION=1`. Your handler function will then be called each time a new section is encountered, with `section` set to the new section name but `name` and `value` set to NULL.
|
||||
|
||||
### Memory options ###
|
||||
|
||||
* **Stack vs heap:** By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using `malloc` instead, specify `-DINI_USE_STACK=0`.
|
||||
* **Maximum line length:** The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like `-DINI_MAX_LINE=1000`. Note that `INI_MAX_LINE` must be 3 more than the longest line (due to `\r`, `\n`, and the NUL).
|
||||
* **Initial malloc size:** `INI_INITIAL_ALLOC` specifies the initial malloc size when using the heap. It defaults to 200 bytes.
|
||||
* **Allow realloc:** By default when using the heap (`-DINI_USE_STACK=0`), inih allocates a fixed-sized buffer of `INI_INITIAL_ALLOC` bytes. To allow this to grow to `INI_MAX_LINE` bytes, doubling if needed, set `-DINI_ALLOW_REALLOC=1`.
|
||||
|
||||
## Simple example in C ##
|
||||
|
||||
|
@ -102,3 +122,36 @@ Some differences between inih and Python's [ConfigParser](http://docs.python.org
|
|||
|
||||
* INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error.
|
||||
* Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser).
|
||||
|
||||
|
||||
## Platform-specific notes ##
|
||||
|
||||
* Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call `_wfopen()` to open a file and then `ini_parse_file()` to parse it; inih does not include `wchar_t` or Unicode handling.
|
||||
|
||||
## Meson notes ##
|
||||
|
||||
* The `meson.build` file is not required to use or compile inih, its main purpose is for distributions.
|
||||
* By default Meson only creates a static library for inih, but Meson can be used to configure this behavior:
|
||||
* with `-Ddefault_library=shared` a shared library is build.
|
||||
* with `-Ddistro_install=true` the library will be installed with the header and a pkg-config entry, you may want to set `-Ddefault_library=shared` when using this.
|
||||
* with `-Dwith_INIReader` you can build (and install if selected) the C++ library.
|
||||
* all compile-time options are implemented in Meson as well, you can take a look at [meson_options.txt](https://github.com/benhoyt/inih/blob/master/meson_options.txt) for their definition. These won't work if `distro_install` is set to `true`.
|
||||
* If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are `inih` and `INIReader`.
|
||||
* In case you use inih as a subproject, you can use the `inih_dep` and `INIReader_dep` dependency variables.
|
||||
|
||||
## Building from vcpkg ##
|
||||
|
||||
You can build and install inih using [vcpkg](https://github.com/microsoft/vcpkg/) dependency manager:
|
||||
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install inih
|
||||
|
||||
The inih port in vcpkg is kept up to date by microsoft team members and community contributors.
|
||||
If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
|
||||
|
||||
## Related links ##
|
||||
|
||||
* [Conan package for inih](https://github.com/mohamedghita/conan-inih) (Conan is a C/C++ package manager)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
|
@ -7,7 +11,7 @@ https://github.com/benhoyt/inih
|
|||
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
|
@ -24,6 +28,12 @@ https://github.com/benhoyt/inih
|
|||
#define MAX_SECTION 128
|
||||
#define MAX_NAME 128
|
||||
|
||||
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||
typedef struct {
|
||||
const char* ptr;
|
||||
size_t num_left;
|
||||
} ini_parse_string_ctx;
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||
static char* rstrip(char* s)
|
||||
{
|
||||
|
@ -41,24 +51,35 @@ static char* lskip(const char* s)
|
|||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char c or ';' comment in given string, or pointer to
|
||||
null at end of string if neither found. ';' must be prefixed by a whitespace
|
||||
character to register as a comment. */
|
||||
static char* find_char_or_comment(const char* s, char c)
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to NUL at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
int was_whitespace = 0;
|
||||
while (*s && *s != c && !(was_whitespace && *s == ';')) {
|
||||
was_whitespace = isspace((unsigned char)(*s));
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||
/* Similar to strncpy, but ensures dest (size bytes) is
|
||||
NUL-terminated, and doesn't pad with NULs. */
|
||||
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
strncpy(dest, src, size);
|
||||
dest[size - 1] = '\0';
|
||||
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
|
||||
size_t i;
|
||||
for (i = 0; i < size - 1 && src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
@ -69,8 +90,14 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
|||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
int max_line = INI_MAX_LINE;
|
||||
#else
|
||||
char* line;
|
||||
size_t max_line = INI_INITIAL_ALLOC;
|
||||
#endif
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
char* new_line;
|
||||
size_t offset;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
char prev_name[MAX_NAME] = "";
|
||||
|
@ -83,14 +110,40 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
|||
int error = 0;
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)malloc(INI_MAX_LINE);
|
||||
line = (char*)malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, INI_MAX_LINE, stream) != NULL) {
|
||||
while (reader(line, (int)max_line, stream) != NULL) {
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
offset = strlen(line);
|
||||
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE)
|
||||
max_line = INI_MAX_LINE;
|
||||
new_line = realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||
break;
|
||||
if (max_line >= INI_MAX_LINE)
|
||||
break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
lineno++;
|
||||
|
||||
start = line;
|
||||
|
@ -103,53 +156,64 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
|||
#endif
|
||||
start = lskip(rstrip(start));
|
||||
|
||||
if (*start == ';' || *start == '#') {
|
||||
/* Per Python ConfigParser, allow '#' comments at start of line */
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
/* Non-black line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python ConfigParser). */
|
||||
if (!handler(user, section, prev_name, start) && !error)
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = find_char_or_comment(start + 1, ']');
|
||||
end = find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
strncpy0(section, start + 1, sizeof(section));
|
||||
*prev_name = '\0';
|
||||
#if INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
if (!HANDLER(user, section, NULL, NULL) && !error)
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start && *start != ';') {
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = find_char_or_comment(start, '=');
|
||||
if (*end != '=') {
|
||||
end = find_char_or_comment(start, ':');
|
||||
}
|
||||
end = find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
value = lskip(end + 1);
|
||||
end = find_char_or_comment(value, '\0');
|
||||
if (*end == ';')
|
||||
value = end + 1;
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = find_chars_or_comment(value, NULL);
|
||||
if (*end)
|
||||
*end = '\0';
|
||||
#endif
|
||||
value = lskip(value);
|
||||
rstrip(value);
|
||||
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
strncpy0(prev_name, name, sizeof(prev_name));
|
||||
if (!handler(user, section, name, value) && !error)
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else if (!error) {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
#if INI_ALLOW_NO_VALUE
|
||||
*end = '\0';
|
||||
name = rstrip(start);
|
||||
if (!HANDLER(user, section, name, NULL) && !error)
|
||||
error = lineno;
|
||||
#else
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,3 +249,40 @@ int ini_parse(const char* filename, ini_handler handler, void* user)
|
|||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* An ini_reader function to read the next line from a string buffer. This
|
||||
is the fgets() equivalent used by ini_parse_string(). */
|
||||
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||
const char* ctx_ptr = ctx->ptr;
|
||||
size_t ctx_num_left = ctx->num_left;
|
||||
char* strp = str;
|
||||
char c;
|
||||
|
||||
if (ctx_num_left == 0 || num < 2)
|
||||
return NULL;
|
||||
|
||||
while (num > 1 && ctx_num_left != 0) {
|
||||
c = *ctx_ptr++;
|
||||
ctx_num_left--;
|
||||
*strp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
num--;
|
||||
}
|
||||
|
||||
*strp = '\0';
|
||||
ctx->ptr = ctx_ptr;
|
||||
ctx->num_left = ctx_num_left;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||
ini_parse_string_ctx ctx;
|
||||
|
||||
ctx.ptr = string;
|
||||
ctx.num_left = strlen(string);
|
||||
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||
user);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2020, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
|
@ -17,9 +21,20 @@ extern "C" {
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||
#ifndef INI_HANDLER_LINENO
|
||||
#define INI_HANDLER_LINENO 0
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of handler function. */
|
||||
#if INI_HANDLER_LINENO
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value,
|
||||
int lineno);
|
||||
#else
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
@ -27,7 +42,7 @@ typedef char* (*ini_reader)(char* str, int num, void* stream);
|
|||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's ConfigParser.
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
|
@ -44,36 +59,86 @@ int ini_parse(const char* filename, ini_handler handler, void* user);
|
|||
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O. */
|
||||
filename. Used for implementing custom or string-based I/O (see also
|
||||
ini_parse_string). */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||
instead of a file. Useful for parsing INI data from a network socket or
|
||||
already in memory. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
ConfigParser. If allowed, ini_parse() will call the handler with the same
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
||||
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack, zero to use heap (malloc/free). */
|
||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||
both ; and # comments at the start of a line by default. */
|
||||
#ifndef INI_START_COMMENT_PREFIXES
|
||||
#define INI_START_COMMENT_PREFIXES ";#"
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||
zero. */
|
||||
#ifndef INI_ALLOW_REALLOC
|
||||
#define INI_ALLOW_REALLOC 0
|
||||
#endif
|
||||
|
||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||
is zero. */
|
||||
#ifndef INI_INITIAL_ALLOC
|
||||
#define INI_INITIAL_ALLOC 200
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file. */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
/* Nonzero to call the handler at the start of each new section (with
|
||||
name and value NULL). Default is to only call the handler on
|
||||
each name=value pair. */
|
||||
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||
call the handler with value NULL in this case. Default is to treat
|
||||
no-value lines as an error. */
|
||||
#ifndef INI_ALLOW_NO_VALUE
|
||||
#define INI_ALLOW_NO_VALUE 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mgba-util/gui/font.h>
|
||||
#include <mgba-util/gui/menu.h>
|
||||
#include <mgba-util/string.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -200,7 +201,7 @@ bool GUISelectFile(struct GUIParams* params, char* outPath, size_t outLen, bool
|
|||
_cleanFiles(&menu.items);
|
||||
GUIMenuItemListDeinit(&menu.items);
|
||||
menu.items = newFiles;
|
||||
strncpy(params->currentPath, outPath, PATH_MAX);
|
||||
strlcpy(params->currentPath, outPath, PATH_MAX);
|
||||
}
|
||||
}
|
||||
params->fileIndex = 0;
|
||||
|
|
|
@ -47,19 +47,16 @@ static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned heigh
|
|||
return 0;
|
||||
}
|
||||
png_set_IHDR(png, info, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
||||
png_write_info(png, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) {
|
||||
png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
|
||||
png_write_info(png, info);
|
||||
return info;
|
||||
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB);
|
||||
}
|
||||
|
||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) {
|
||||
png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
|
||||
png_write_info(png, info);
|
||||
return info;
|
||||
return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA);
|
||||
}
|
||||
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) {
|
||||
|
@ -273,6 +270,10 @@ bool PNGIgnorePixels(png_structp png, png_infop info) {
|
|||
}
|
||||
|
||||
bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
|
||||
if (png_get_channels(png, info) != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
|
@ -324,6 +325,10 @@ bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width
|
|||
}
|
||||
|
||||
bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
|
||||
if (png_get_channels(png, info) != 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
|
@ -375,6 +380,10 @@ bool PNGReadPixelsA(png_structp png, png_infop info, void* pixels, unsigned widt
|
|||
}
|
||||
|
||||
bool PNGReadPixels8(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) {
|
||||
if (png_get_channels(png, info) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,23 @@ char* strdup(const char* str) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
size_t strlcpy(char* restrict dst, const char* restrict src, size_t dstsize) {
|
||||
size_t i = 0;
|
||||
for (; src[i] && dstsize > 1; ++i) {
|
||||
dst[i] = src[i];
|
||||
--dstsize;
|
||||
}
|
||||
if (dstsize) {
|
||||
dst[i] = '\0';
|
||||
}
|
||||
while (src[i]) {
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
#endif
|
||||
|
||||
char* strnrstr(const char* restrict haystack, const char* restrict needle, size_t len) {
|
||||
char* last = 0;
|
||||
const char* next = haystack;
|
||||
|
|
Loading…
Reference in New Issue