From 5e0e250a6d10d63ef0c6bd6215f2c6fc17976ff3 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 16:30:21 -0400 Subject: [PATCH 01/23] Make newstate a public header. Since it is included in gambatte.h it needs to be installed. --- libgambatte/include/gambatte.h | 2 +- libgambatte/{src => include}/newstate.h | 0 libgambatte/src/video/mstat_irq.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename libgambatte/{src => include}/newstate.h (100%) diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 6a19a432..282f32d8 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -23,7 +23,7 @@ #include "inputgetter.h" #include "loadres.h" #include "pakinfo.h" -#include "../src/newstate.h" +#include "newstate.h" #include #include diff --git a/libgambatte/src/newstate.h b/libgambatte/include/newstate.h similarity index 100% rename from libgambatte/src/newstate.h rename to libgambatte/include/newstate.h diff --git a/libgambatte/src/video/mstat_irq.h b/libgambatte/src/video/mstat_irq.h index 9ee5adb3..f9ba675d 100644 --- a/libgambatte/src/video/mstat_irq.h +++ b/libgambatte/src/video/mstat_irq.h @@ -3,7 +3,7 @@ #include "lcddef.h" #include "../savestate.h" -#include "../newstate.h" +#include "newstate.h" #include From 0b6e3e9009d4752071291ebe04aed31c501d41ce Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 17:00:49 -0400 Subject: [PATCH 02/23] Add common CMake build directories to gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ed392daf..e1b54bd4 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ gambatte_qt/src/platforms.pri *.dylib .DS_Store + +build/ +out/ From 327d33998e0d3be51ab4d00c91e1c06b7aa613f8 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 17:17:42 -0400 Subject: [PATCH 03/23] Add CMake build script. The current implementation provides: - Options to build both static and shared library at the same time - A toggle for enabling warnings as errors - Support for enabling zlib - Generating pkg-config and CMake targets - Exporting CMake targets to consume from the build directory --- CMakeLists.txt | 296 +++++++++++++++++++++++++++ cmake/GetAvailableCompileFlags.cmake | 38 ++++ cmake/libgambatte-config.cmake.in | 29 +++ cmake/libgambatte.pc.in | 12 ++ 4 files changed, 375 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/GetAvailableCompileFlags.cmake create mode 100644 cmake/libgambatte-config.cmake.in create mode 100644 cmake/libgambatte.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..6376580d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,296 @@ +cmake_minimum_required(VERSION 3.4) +project(libgambatte CXX) + +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(LIBGAMBATTE_IS_SUBPROJECT FALSE) +else() + set(LIBGAMBATTE_IS_SUBPROJECT TRUE) +endif() + +include(GNUInstallDirs) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +option(LIBGAMBATTE_BUILD_SHARED "Build the shared version of libgambatte" ON) +option(LIBGAMBATTE_BUILD_STATIC "Build the static version of libgambatte" ON) +option(LIBGAMBATTE_ENABLE_ZIP "Build zip support using zlib" ON) +set(LIBGAMBATTE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/libgambatte" CACHE STRING "Directory to install the CMake package files to") +set(LIBGAMBATTE_INSTALL_PKGCONFDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING "Directory to install the pkg-config file to") + +if(NOT LIBGAMBATTE_BUILD_SHARED AND NOT LIBGAMBATTE_BUILD_STATIC) + message(FATAL_ERROR "Both shared and static libraries have been disabled. Turn on at least one.") +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) + message(STATUS "No build type was specified, defaulting to: ${CMAKE_BUILD_TYPE}") + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;MinSizeRel;RelWithDebInfo") +endif() + +include(GetAvailableCompileFlags) +get_available_compile_flags(LIBGAMBATTE_COMPILE_FLAGS) + +set(LIBGAMBATTE_SOURCES + "libgambatte/src/bitmap_font.cpp" + "libgambatte/src/cpu.cpp" + "libgambatte/src/gambatte.cpp" + "libgambatte/src/initstate.cpp" + "libgambatte/src/interrupter.cpp" + "libgambatte/src/interruptrequester.cpp" + "libgambatte/src/loadres.cpp" + "libgambatte/src/memory.cpp" + "libgambatte/src/newstate.cpp" + "libgambatte/src/sound.cpp" + "libgambatte/src/state_osd_elements.cpp" + "libgambatte/src/statesaver.cpp" + "libgambatte/src/tima.cpp" + "libgambatte/src/video.cpp" + "libgambatte/src/mem/camera.cpp" + "libgambatte/src/mem/cartridge.cpp" + "libgambatte/src/mem/huc3_chip.cpp" + "libgambatte/src/mem/infrared.cpp" + "libgambatte/src/mem/memptrs.cpp" + "libgambatte/src/mem/pakinfo.cpp" + "libgambatte/src/mem/remote.cpp" + "libgambatte/src/mem/rtc.cpp" + "libgambatte/src/mem/sgb.cpp" + "libgambatte/src/mem/time.cpp" + "libgambatte/src/mem/mbc/huc1.cpp" + "libgambatte/src/mem/mbc/huc3.cpp" + "libgambatte/src/mem/mbc/m161.cpp" + "libgambatte/src/mem/mbc/mbc0.cpp" + "libgambatte/src/mem/mbc/mbc1.cpp" + "libgambatte/src/mem/mbc/mbc2.cpp" + "libgambatte/src/mem/mbc/mbc3.cpp" + "libgambatte/src/mem/mbc/mbc5.cpp" + "libgambatte/src/mem/mbc/mmm01.cpp" + "libgambatte/src/mem/mbc/pocket_camera.cpp" + "libgambatte/src/mem/mbc/wisdom_tree.cpp" + "libgambatte/src/mem/snes_spc/dsp.cpp" + "libgambatte/src/mem/snes_spc/SNES_SPC.cpp" + "libgambatte/src/mem/snes_spc/SNES_SPC_misc.cpp" + "libgambatte/src/mem/snes_spc/SNES_SPC_state.cpp" + "libgambatte/src/mem/snes_spc/spc.cpp" + "libgambatte/src/mem/snes_spc/SPC_DSP.cpp" + "libgambatte/src/mem/snes_spc/SPC_Filter.cpp" + "libgambatte/src/sound/channel1.cpp" + "libgambatte/src/sound/channel2.cpp" + "libgambatte/src/sound/channel3.cpp" + "libgambatte/src/sound/channel4.cpp" + "libgambatte/src/sound/duty_unit.cpp" + "libgambatte/src/sound/envelope_unit.cpp" + "libgambatte/src/sound/length_counter.cpp" + "libgambatte/src/video/ly_counter.cpp" + "libgambatte/src/video/lyc_irq.cpp" + "libgambatte/src/video/next_m0_time.cpp" + "libgambatte/src/video/ppu.cpp" + "libgambatte/src/video/sprite_mapper.cpp" +) + +set(LIBGAMBATTE_COMMON_PRIVATE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/common") + +set(LIBGAMBATTE_SUPPORT_ZIP FALSE) + +if(LIBGAMBATTE_ENABLE_ZIP) + find_package(ZLIB) + set(LIBGAMBATTE_SUPPORT_ZIP ${ZLIB_FOUND}) +endif() + +if(LIBGAMBATTE_SUPPORT_ZIP) + enable_language(C) + list(APPEND LIBGAMBATTE_SOURCES + "libgambatte/src/file/unzip/unzip.c" + "libgambatte/src/file/unzip/ioapi.c" + "libgambatte/src/file/file_zip.cpp" + ) +else() + list(APPEND LIBGAMBATTE_SOURCES + "libgambatte/src/file/file.cpp" + "libgambatte/src/file/crc32.cpp" + ) +endif() + +include(CheckIncludeFileCXX) + +foreach(_header "cstdint" "stdint.h") + string(TOUPPER "${_header}" _header_upper_name) + string(MAKE_C_IDENTIFIER "${_header_upper_name}" _header_c_name) + check_include_file_cxx("${_header}" "HAVE_${_header_c_name}") + + if(HAVE_${_header_c_name}) + list(APPEND LIBGAMBATTE_DEFINITIONS "HAVE_${_header_c_name}") + endif() +endforeach() + +find_package(Git) +set(LIBGAMBATTE_REVISION "-1") + +if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") + execute_process(COMMAND "${GIT_EXECUTABLE}" rev-list HEAD --count + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" + OUTPUT_VARIABLE GIT_COMMIT_COUNT) + string(REPLACE "\n" "" GIT_COMMIT_COUNT "${GIT_COMMIT_COUNT}") + + if(GIT_COMMIT_COUNT MATCHES "^[0-9]*$") + set(LIBGAMBATTE_REVISION "${GIT_COMMIT_COUNT}") + endif() +endif() + +if(LIBGAMBATTE_BUILD_SHARED) + add_library(libgambatte-shared SHARED ${LIBGAMBATTE_SOURCES} "libgambatte/src/cinterface.cpp") + add_library(libgambatte::libgambatte-shared ALIAS libgambatte-shared) + list(APPEND LIBGAMBATTE_TARGETS libgambatte-shared) +endif() + +if(LIBGAMBATTE_BUILD_STATIC) + add_library(libgambatte-static STATIC ${LIBGAMBATTE_SOURCES}) + add_library(libgambatte::libgambatte-static ALIAS libgambatte-static) + list(APPEND LIBGAMBATTE_TARGETS libgambatte-static) +endif() + +set_target_properties(${LIBGAMBATTE_TARGETS} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED TRUE + CXX_EXTENSIONS OFF + OUTPUT_NAME "gambatte" +) + +# MSVC and non-mingw Clang toolchain use .lib for the shared implib and the static library +# Make sure the names don't clash since both are installed to lib/ +if(WIN32 AND NOT MINGW AND LIBGAMBATTE_BUILD_STATIC) + set_target_properties(libgambatte-static PROPERTIES OUTPUT_NAME "gambatte-static") +endif() + +foreach(_target IN LISTS LIBGAMBATTE_TARGETS) + target_compile_definitions(${_target} PRIVATE + "REVISION=${LIBGAMBATTE_REVISION}" + ${LIBGAMBATTE_DEFINITIONS} + ) + + target_compile_options(${_target} PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) + + target_include_directories(${_target} + PUBLIC + $ + $ + PRIVATE + "${LIBGAMBATTE_COMMON_PRIVATE_HEADERS}" + "libgambatte/src" + ) + + if(LIBGAMBATTE_SUPPORT_ZIP) + target_compile_definitions(${_target} PRIVATE "HAVE_ZLIB_H") + target_link_libraries(${_target} PRIVATE ZLIB::ZLIB) + endif() + + install(TARGETS ${_target} + EXPORT "${_target}-target" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ) + install(EXPORT "${_target}-target" + FILE "${_target}-target.cmake" + NAMESPACE libgambatte:: + DESTINATION "${LIBGAMBATTE_INSTALL_CMAKEDIR}" + ) + + # Export the targets for consuming from the build directory + export(EXPORT "${_target}-target" + FILE "${libgambatte_BINARY_DIR}/${_target}-target.cmake" + NAMESPACE libgambatte:: + ) +endforeach() + +include(CMakePackageConfigHelpers) +configure_package_config_file( + "${CMAKE_CURRENT_LIST_DIR}/cmake/libgambatte-config.cmake.in" + "${libgambatte_BINARY_DIR}/install-config/libgambatte-config.cmake" + INSTALL_DESTINATION "${LIBGAMBATTE_INSTALL_CMAKEDIR}" + NO_SET_AND_CHECK_MACRO + NO_CHECK_REQUIRED_COMPONENTS_MACRO +) + +write_basic_package_version_file( + "libgambatte-config-version.cmake" + VERSION "${LIBGAMBATTE_REVISION}" + COMPATIBILITY ExactVersion +) + +install(FILES + "${libgambatte_BINARY_DIR}/install-config/libgambatte-config.cmake" + "${libgambatte_BINARY_DIR}/libgambatte-config-version.cmake" + DESTINATION "${LIBGAMBATTE_INSTALL_CMAKEDIR}" +) + +install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/libgambatte/include/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gambatte" +) + +# Write a special config for consuming from the build directory +set(PACKAGE_INIT "get_filename_component(PACKAGE_PREFIX \"\${CMAKE_CURRENT_LIST_DIR}\" ABSOLUTE)") +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/cmake/libgambatte-config.cmake.in" + "${libgambatte_BINARY_DIR}/libgambatte-config.cmake" + @ONLY +) +unset(PACKAGE_INIT) + +# Create and install a pkg-config file +set(LIBGAMBATTE_PC_REQUIRES) +set(LIBGAMBATTE_PC_REQUIRES_PRIVATE) + +if(LIBGAMBATTE_SUPPORT_ZIP) + # Make it a public dependency if only static is built + if(NOT LIBGAMBATTE_BUILD_SHARED) + # For macOS, the zlib pc file is provided by Homebrew + list(APPEND LIBGAMBATTE_PC_REQUIRES "zlib") + else() + list(APPEND LIBGAMBATTE_PC_REQUIRES_PRIVATE "zlib") + endif() +endif() + +# Ensure the paths written in the pkg-config are relative to the prefix +if(IS_ABSOLUTE "${LIBGAMBATTE_INSTALL_PKGCONFDIR}") + file(RELATIVE_PATH + LIBGAMBATTE_PC_TO_PREFIX + "${LIBGAMBATTE_INSTALL_PKGCONFDIR}" + "${CMAKE_INSTALL_PREFIX}" + ) +else() + file(RELATIVE_PATH + LIBGAMBATTE_PC_TO_PREFIX + "${CMAKE_INSTALL_PREFIX}/${LIBGAMBATTE_INSTALL_PKGCONFDIR}" + "${CMAKE_INSTALL_PREFIX}" + ) +endif() + +if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + file(RELATIVE_PATH + LIBGAMBATTE_PC_INCLUDEDIR + "${CMAKE_INSTALL_PREFIX}" + "${CMAKE_INSTALL_INCLUDEDIR}" + ) +else() + set(LIBGAMBATTE_PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") +endif() + +if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + file(RELATIVE_PATH + LIBGAMBATTE_PC_LIBDIR + "${CMAKE_INSTALL_PREFIX}" + "${CMAKE_INSTALL_LIBDIR}" + ) +else() + set(LIBGAMBATTE_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") +endif() + +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/cmake/libgambatte.pc.in" + "${libgambatte_BINARY_DIR}/libgambatte.pc" + @ONLY +) + +install(FILES "${libgambatte_BINARY_DIR}/libgambatte.pc" + DESTINATION "${LIBGAMBATTE_INSTALL_PKGCONFDIR}" +) diff --git a/cmake/GetAvailableCompileFlags.cmake b/cmake/GetAvailableCompileFlags.cmake new file mode 100644 index 00000000..92dbdcba --- /dev/null +++ b/cmake/GetAvailableCompileFlags.cmake @@ -0,0 +1,38 @@ +function(get_available_compile_flags OUT_VAR) + if(MSVC) + set(FLAGS_TO_CHECK "/W4" "/GR-") + set(WARNINGS_AS_ERROR "/WX") + else() + set(FLAGS_TO_CHECK "-Wall" "-Wextra" "-fno-rtti" "-fno-exceptions") + set(CXX_ONLY_FLAGS "-fno-rtti" "-fno-exceptions") + set(WARNINGS_AS_ERROR "-Werror") + endif() + + include(CheckCXXCompilerFlag) + + foreach(FLAG IN LISTS FLAGS_TO_CHECK) + string(TOUPPER "${FLAG}" UPPER_FLAG) + string(MAKE_C_IDENTIFIER "${UPPER_FLAG}" FLAG_NAME) + check_cxx_compiler_flag("${FLAG}" "HAVE${FLAG_NAME}") + + if(HAVE${FLAG_NAME}) + if(FLAG IN_LIST CXX_ONLY_FLAGS) + list(APPEND SUPPORTED_FLAGS $<$:${FLAG}>) + else() + list(APPEND SUPPORTED_FLAGS "${FLAG}") + endif() + endif() + endforeach() + + check_cxx_compiler_flag("${WARNINGS_AS_ERROR}" HAVE_WARNINGS_AS_ERROR) + include(CMakeDependentOption) + + # Hide the option altogether if this is included as a subproject + cmake_dependent_option(LIBGAMBATTE_WARNINGS_AS_ERROR "Treat all warnings as error" OFF "HAVE_WARNINGS_AS_ERROR;NOT LIBGAMBATTE_IS_SUBPROJECT" OFF) + + if(LIBGAMBATTE_WARNINGS_AS_ERROR) + list(APPEND SUPPORTED_FLAGS "${WARNINGS_AS_ERROR}") + endif() + + set(${OUT_VAR} ${SUPPORTED_FLAGS} PARENT_SCOPE) +endfunction() diff --git a/cmake/libgambatte-config.cmake.in b/cmake/libgambatte-config.cmake.in new file mode 100644 index 00000000..4ca3b604 --- /dev/null +++ b/cmake/libgambatte-config.cmake.in @@ -0,0 +1,29 @@ +@PACKAGE_INIT@ + +include(FeatureSummary) +set_package_properties(libgambatte + PROPERTIES + URL "https://github.com/pokemon-speedrunning/gambatte-core" + DESCRIPTION "Core library for the Gambatte emulator" +) + +set(LIBGAMBATTE_SUPPORT_ZIP @LIBGAMBATTE_SUPPORT_ZIP@) + +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/libgambatte-shared-target.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/libgambatte-shared-target.cmake") +endif() + +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/libgambatte-static-target.cmake") + if(LIBGAMBATTE_SUPPORT_ZIP) + include(CMakeFindDependencyMacro) + find_dependency(ZLIB) + endif() + + include("${CMAKE_CURRENT_LIST_DIR}/libgambatte-static-target.cmake") +endif() + +if(TARGET libgambatte::libgambatte-shared) + add_library(libgambatte::libgambatte ALIAS libgambatte::libgambatte-shared) +else() + add_library(libgambatte::libgambatte ALIAS libgambatte::libgambatte-static) +endif() diff --git a/cmake/libgambatte.pc.in b/cmake/libgambatte.pc.in new file mode 100644 index 00000000..63d30ac1 --- /dev/null +++ b/cmake/libgambatte.pc.in @@ -0,0 +1,12 @@ +prefix=${pcfiledir}/@LIBGAMBATTE_PC_TO_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@LIBGAMBATTE_PC_LIBDIR@ +includedir=${prefix}/@LIBGAMBATTE_PC_INCLUDEDIR@ + +Name: libgambatte +Description: The gambatte emulator core library +Version: @LIBGAMBATTE_REVISION@ +Cflags: -I${includedir}/gambatte +Libs: -L${libdir} -lgambatte +Requires: @LIBGAMBATTE_PC_REQUIRES@ +Requires.private: @LIBGAMBATTE_PC_REQUIRES_PRIVATE@ From f17fbc3a1bd22007334e6956d8bc973e726ba9d9 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 22:17:43 -0400 Subject: [PATCH 04/23] Replace defines with a generated config.h. This is required as public header rely on knowing the value of HAVE_STDINT_H and HAVE_CSTDINT. This was also backported to scons since it edits code. --- .gitignore | 1 + CMakeLists.txt | 28 ++++++++------- cmake/config.h.in | 5 +++ libgambatte/SConstruct | 36 ++++++++++++++------ libgambatte/include/gbint.h | 2 ++ libgambatte/src/cinterface.cpp | 2 +- libgambatte/src/file/crc32.cpp | 2 +- libgambatte/src/file/crc32.h | 4 ++- libgambatte/src/mem/snes_spc/blargg_common.h | 2 ++ scripts/clean.sh | 3 ++ 10 files changed, 59 insertions(+), 26 deletions(-) create mode 100644 cmake/config.h.in diff --git a/.gitignore b/.gitignore index e1b54bd4..7a36de8d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ gambatte_qt/src/release/ gambatte_qt/src/debug/ gambatte_qt/bin/ test/testrunner +libgambatte/include/config.h *.cpp~ *.h~ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6376580d..07fdc56b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,6 +87,13 @@ set(LIBGAMBATTE_SOURCES "libgambatte/src/video/sprite_mapper.cpp" ) +file(GLOB LIBGAMBATTE_PUBLIC_HEADERS "libgambatte/include/*.h") +file( + COPY ${LIBGAMBATTE_PUBLIC_HEADERS} + DESTINATION "${libgambatte_BINARY_DIR}/include/gambatte" + PATTERN "config.h" EXCLUDE +) + set(LIBGAMBATTE_COMMON_PRIVATE_HEADERS "${CMAKE_CURRENT_LIST_DIR}/common") set(LIBGAMBATTE_SUPPORT_ZIP FALSE) @@ -116,10 +123,6 @@ foreach(_header "cstdint" "stdint.h") string(TOUPPER "${_header}" _header_upper_name) string(MAKE_C_IDENTIFIER "${_header_upper_name}" _header_c_name) check_include_file_cxx("${_header}" "HAVE_${_header_c_name}") - - if(HAVE_${_header_c_name}) - list(APPEND LIBGAMBATTE_DEFINITIONS "HAVE_${_header_c_name}") - endif() endforeach() find_package(Git) @@ -136,6 +139,11 @@ if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") endif() endif() +configure_file( + "${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in" + "${libgambatte_BINARY_DIR}/include/gambatte/config.h" +) + if(LIBGAMBATTE_BUILD_SHARED) add_library(libgambatte-shared SHARED ${LIBGAMBATTE_SOURCES} "libgambatte/src/cinterface.cpp") add_library(libgambatte::libgambatte-shared ALIAS libgambatte-shared) @@ -162,16 +170,11 @@ if(WIN32 AND NOT MINGW AND LIBGAMBATTE_BUILD_STATIC) endif() foreach(_target IN LISTS LIBGAMBATTE_TARGETS) - target_compile_definitions(${_target} PRIVATE - "REVISION=${LIBGAMBATTE_REVISION}" - ${LIBGAMBATTE_DEFINITIONS} - ) - target_compile_options(${_target} PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) target_include_directories(${_target} PUBLIC - $ + $ $ PRIVATE "${LIBGAMBATTE_COMMON_PRIVATE_HEADERS}" @@ -179,7 +182,6 @@ foreach(_target IN LISTS LIBGAMBATTE_TARGETS) ) if(LIBGAMBATTE_SUPPORT_ZIP) - target_compile_definitions(${_target} PRIVATE "HAVE_ZLIB_H") target_link_libraries(${_target} PRIVATE ZLIB::ZLIB) endif() @@ -223,8 +225,8 @@ install(FILES DESTINATION "${LIBGAMBATTE_INSTALL_CMAKEDIR}" ) -install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/libgambatte/include/" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/gambatte" +install(DIRECTORY "${libgambatte_BINARY_DIR}/include/gambatte" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) # Write a special config for consuming from the build directory diff --git a/cmake/config.h.in b/cmake/config.h.in new file mode 100644 index 00000000..ddd04749 --- /dev/null +++ b/cmake/config.h.in @@ -0,0 +1,5 @@ +#cmakedefine LIBGAMBATTE_REVISION @LIBGAMBATTE_REVISION@ + +#cmakedefine HAVE_CSTDINT +#cmakedefine HAVE_STDINT_H +#cmakedefine LIBGAMBATTE_SUPPORT_ZIP diff --git a/libgambatte/SConstruct b/libgambatte/SConstruct index 3faeeb04..3a8a61e6 100644 --- a/libgambatte/SConstruct +++ b/libgambatte/SConstruct @@ -1,6 +1,5 @@ global_cflags = ARGUMENTS.get('CFLAGS', '-Wall -Wextra -O2 -fomit-frame-pointer') global_cxxflags = ARGUMENTS.get('CXXFLAGS', global_cflags + ' -fno-exceptions -fno-rtti -std=c++11') -global_defines = ' -DHAVE_STDINT_H' vars = Variables() vars.Add('CC') vars.Add('CXX') @@ -8,8 +7,8 @@ vars.Add('CXX') import os env = Environment(ENV = os.environ, CPPPATH = ['src', 'include', '../common'], - CFLAGS = global_cflags + global_defines, - CXXFLAGS = global_cxxflags + global_defines, + CFLAGS = global_cflags, + CXXFLAGS = global_cxxflags, variables = vars) sourceFiles = Split(''' @@ -75,31 +74,48 @@ if not bool(ARGUMENTS.get('NO_ZIP', 0)) and conf.CheckHeader('zlib.h'): sourceFiles.append('src/file/unzip/unzip.c') sourceFiles.append('src/file/unzip/ioapi.c') sourceFiles.append('src/file/file_zip.cpp') - env.Append(CCFLAGS = ' -DHAVE_ZLIB_H') sys_libs = ['z'] else: sourceFiles.append('src/file/file.cpp') sourceFiles.append('src/file/crc32.cpp') sys_libs = [] -conf.Finish() +have_cstdint = conf.CheckHeader('cstdint') +have_stdint_h = conf.CheckHeader('stdint.h') -lib = env.Library('gambatte', sourceFiles) +conf.Finish() -def rev(): +def write_config_h(): + defines = [] try: from subprocess import check_output, CalledProcessError stdout = check_output(['git', 'rev-list', 'HEAD', '--count']) - return ' -DREVISION=' + stdout.decode().strip() + defines.append('#define LIBGAMBATTE_REVISION ' + stdout.decode().strip()) except (OSError, CalledProcessError): - return ' -DREVISION=-1' + defines.append('#define LIBGAMBATTE_REVISION -1') + + if 'z' in sys_libs: + defines.append('#define LIBGAMBATTE_SUPPORT_ZIP') + + if have_cstdint: + defines.append('#define HAVE_CSTDINT') + + if have_stdint_h: + defines.append('#define HAVE_STDINT_H') + + with open("include/config.h", "w") as config_h: + config_h.write('\n'.join(defines)) + +write_config_h() + +lib = env.Library('gambatte', sourceFiles) import sys if sys.platform == 'darwin': sys_libs.append('System') shlib = env.SharedLibrary('gambatte', sourceFiles + ['src/cinterface.cpp'], - CXXFLAGS = env['CXXFLAGS'] + rev(), + CXXFLAGS = env['CXXFLAGS'], LINKFLAGS = env['LINKFLAGS'] + ' -s', LIBS = sys_libs, SHLIBPREFIX = "lib") diff --git a/libgambatte/include/gbint.h b/libgambatte/include/gbint.h index 5b1706ad..3bf16325 100644 --- a/libgambatte/include/gbint.h +++ b/libgambatte/include/gbint.h @@ -19,6 +19,8 @@ #ifndef GAMBATTE_INT_H #define GAMBATTE_INT_H +#include "config.h" + #ifdef HAVE_CSTDINT #include diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 4f56fc57..64e33db2 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -44,7 +44,7 @@ namespace { using namespace gambatte; GBEXPORT int gambatte_revision() { - return REVISION; + return LIBGAMBATTE_REVISION; } GBEXPORT GB * gambatte_create() { diff --git a/libgambatte/src/file/crc32.cpp b/libgambatte/src/file/crc32.cpp index cab7e55e..4561e0eb 100644 --- a/libgambatte/src/file/crc32.cpp +++ b/libgambatte/src/file/crc32.cpp @@ -42,7 +42,7 @@ #include "crc32.h" -#ifndef HAVE_ZLIB_H +#ifndef LIBGAMBATTE_SUPPORT_ZIP namespace gambatte { diff --git a/libgambatte/src/file/crc32.h b/libgambatte/src/file/crc32.h index 8065e1ef..693d6e2a 100644 --- a/libgambatte/src/file/crc32.h +++ b/libgambatte/src/file/crc32.h @@ -1,7 +1,9 @@ #ifndef CRC32_H #define CRC32_H -#ifdef HAVE_ZLIB_H +#include "config.h" + +#ifdef LIBGAMBATTE_SUPPORT_ZIP #include #else diff --git a/libgambatte/src/mem/snes_spc/blargg_common.h b/libgambatte/src/mem/snes_spc/blargg_common.h index 75edff39..40f8d63b 100644 --- a/libgambatte/src/mem/snes_spc/blargg_common.h +++ b/libgambatte/src/mem/snes_spc/blargg_common.h @@ -5,6 +5,8 @@ #ifndef BLARGG_COMMON_H #define BLARGG_COMMON_H +#include "config.h" + #include #include #include diff --git a/scripts/clean.sh b/scripts/clean.sh index ad576c11..b32c397f 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -6,6 +6,9 @@ echo "cd libgambatte && scons -c ." echo "cd test && scons -c" (cd test && scons -c) +echo "rm -f libgambatte/include/config.h" +rm -f libgambatte/include/config.h + echo "rm -f *gambatte*/config.log" rm -f *gambatte*/config.log From 680dfad42b63c05ba07d6ebe6b8f0ef4a89dc1be Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 22:38:38 -0400 Subject: [PATCH 05/23] CMake: Add support for tests. --- CMakeLists.txt | 12 +++++++++++- test/CMakeLists.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 07fdc56b..10b878f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.12) project(libgambatte CXX) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) @@ -14,6 +14,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") option(LIBGAMBATTE_BUILD_SHARED "Build the shared version of libgambatte" ON) option(LIBGAMBATTE_BUILD_STATIC "Build the static version of libgambatte" ON) option(LIBGAMBATTE_ENABLE_ZIP "Build zip support using zlib" ON) +option(LIBGAMBATTE_ENABLE_TESTING "Build tests" ON) set(LIBGAMBATTE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/libgambatte" CACHE STRING "Directory to install the CMake package files to") set(LIBGAMBATTE_INSTALL_PKGCONFDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING "Directory to install the pkg-config file to") @@ -296,3 +297,12 @@ configure_file( install(FILES "${libgambatte_BINARY_DIR}/libgambatte.pc" DESTINATION "${LIBGAMBATTE_INSTALL_PKGCONFDIR}" ) + +if(LIBGAMBATTE_ENABLE_TESTING) + if(NOT LIBGAMBATTE_BUILD_STATIC) + message(FATAL_ERROR "Building the static library is required to run tests") + endif() + + enable_testing() + add_subdirectory(test) +endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..ba0b9b4c --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,41 @@ +find_package(Python3 COMPONENTS Interpreter) +find_package(PNG) + +if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/bios.gb") + message(STATUS "bios.gb could NOT be found") +endif() + +if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/bios.gbc") + message(STATUS "bios.gbc could NOT be found") +endif() + +if(NOT Python3_Interpreter_FOUND OR NOT PNG_FOUND) + message(FATAL_ERROR "Python3 and libpng are required to build tests") +endif() + +file(GLOB_RECURSE GB_ASM_SOURCES "hwtests/**/*.asm") +message(STATUS "Processing assembly files...") +execute_process( + COMMAND "${Python3_EXECUTABLE}" "qdgbas.py" ${GB_ASM_SOURCES} + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" + OUTPUT_QUIET +) +message(STATUS "Done processing assembly files") +file(GLOB_RECURSE GB_ASM_BIN "hwtests/**/*.gb") + + +add_executable(testrunner testrunner.cpp) +target_compile_options(testrunner PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) +target_include_directories(testrunner PRIVATE ${LIBGAMBATTE_COMMON_PRIVATE_HEADERS}) +target_link_libraries(testrunner PRIVATE libgambatte::libgambatte-static PNG::PNG) +set_target_properties(testrunner PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED TRUE + CXX_EXTENSIONS OFF +) + +add_test( + NAME testrunner + COMMAND testrunner ${GB_ASM_BIN} + WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" +) From 01bd8917569aa015b8997cd93be7534851c950a1 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 22:39:31 -0400 Subject: [PATCH 06/23] Update build docs to use CMake. --- INSTALL.md | 153 ++++++++++++++++++++++++++++++----------------------- README.md | 107 +++++++++++++++++++++++-------------- 2 files changed, 154 insertions(+), 106 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 0d3dec09..d0a2366e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,120 +1,139 @@ -# Setting up the build environment +# Setting Up the Build Environment -These instructions explain how to set up an environment for building Gambatte-Speedrun from source on different operating systems. Once you've set up your build environment, see the main [README.md](README.md) for build instructions and information on running the test suite. +These instructions explain how to set up an environment for building the core libgambatte library from source on different operating systems. Once you've set up your build environment, see the main [README.md](README.md) for build instructions and information on running the test suite. -If all you want to do is build Gambatte-Speedrun's `libgambatte` shared library (for use in scripting, "botting", TASing, and other programmatic use of the emulation core), you do not need to perform the Qt-specific steps or the Testrunner-specific steps. You'll still need to follow the "basic steps" for setting up the build environment. - -Running the hwtests suite requires installing an additional dependency (`libpng`); this can be skipped if you have no intention of using the testrunner (we primarily use the test suite to confirm no regressions in accuracy were introduced in our changes). Likewise, the Qt-specific steps can be skipped if you have no intention of building the full Qt UI application. +Running the hwtests suite requires installing two additional dependencies (`python3`, and `libpng`); this can be skipped if you have no intention of using the testrunner (we primarily use the test suite to confirm no regressions in accuracy were introduced in our changes). --- -*Platform-specific instructions:* -* [Windows](#windows) -* [macOS](#macos) -* [Debian/Ubuntu](#debianubuntu) + +Platform-specific instructions: + +- [Windows](#windows) +- [macOS](#macos) +- [Linux](#linux) --- + ## Windows -***\*NOTE:*** The instructions below assume you're installing 64-bit MSYS2 and using the 32-bit MinGW toolchain (they work with other configurations, but the commands won't be exactly the same). -* Choice of 32-bit vs 64-bit for MSYS2 doesn't really matter; either is fine -* There's currently no benefit to building a 64-bit version of Gambatte-Speedrun on Windows, so 32-bit is used for the release binaries +**NOTE:** The instructions below assume you're installing 64-bit MSYS2 and using the 32-bit MinGW toolchain (they work with other configurations, but the commands won't be exactly the same). + +- Choice of 32-bit vs 64-bit for MSYS2 doesn't really matter; either is fine +- There's currently no benefit to building a 64-bit version of libgambatte on Windows, so 32-bit is used for the release binaries -***\*NOTE:*** At the end of these steps, to do any build tasks related to Gambatte-Speedrun, open the MSYS2 MinGW 32-bit shell (under the MSYS2 folder in the Start menu; ***\*must be\**** this specific shell in order for building to work). +**NOTE:** At the end of these steps, to do any build tasks related to libgambatte, open the MSYS2 MinGW 32-bit shell (under the MSYS2 folder in the Start menu; **\*\*must be\*\*** this specific shell in order for building to work). ### Basic steps -*Do the following:* +Do the following: -\- Install [MSYS2](https://www.msys2.org/) by selecting the one-click installer exe for x86_64 +- Install [MSYS2](https://www.msys2.org/) by selecting the one-click installer exe for x86_64 -\- Run MSYS2 shell and update MSYS2 core components and packages *(copied from [here](https://www.msys2.org/wiki/MSYS2-installation/#iii-updating-packages))*: +- Run MSYS2 shell and update MSYS2 core components and packages _(copied from [here](https://www.msys2.org/wiki/MSYS2-installation/#iii-updating-packages))_: + +``` +pacman -Syuu ``` -$ pacman -Syuu Follow the instructions. Repeat this step until it says there are no packages to update. See the above link if you have an older installation of MSYS2 and/or pacman. -``` -\- Install MSYS2 packages for general build development environment: -``` -$ pacman -S base-devel git mingw-w64-i686-zlib mingw-w64-i686-toolchain -``` -### Qt-specific steps +- Install MSYS2 packages for general build development environment: -\- Acquire and install the qt5-static library environment: -``` -$ pacman -S mingw-w64-i686-qt5-static ``` -\- Modify `.bash_profile` to add qt5-static binaries to `PATH`: -``` -$ echo 'if [[ "${MSYSTEM}" == "MINGW"* ]]; then PATH="/${MSYSTEM}/qt5-static/bin:${PATH}"; fi' >> ~/.bash_profile +pacman -S base-devel \ + git \ + mingw-w64-i686-toolchain \ + mingw-w64-i686-cmake \ + mingw-w64-i686-ninja \ + mingw-w64-i686-zlib ``` ### Testrunner-specific steps -\- Install the `libpng` package: +- Install the `libpng` and `python3` packages: + ``` -$ pacman -S mingw-w64-i686-libpng +pacman -S mingw-w64-i686-libpng mingw-w64-i686-python3 ``` +### Visual Studio users + +As there is no standard package manager for Visual Studio, you will need to either manually build zlib (and libpng for test), or use a package manager such as [vcpkg](https://github.com/microsoft/vcpkg). + +You will also need to manually install [CMake](https://cmake.org/) and to make sure to add it to your `PATH` during installation. + ## macOS ### Basic steps -*Open a terminal and do the following:* +_Open a terminal and do the following:_ + +- Install Xcode Command Line Tools and accept the license (if not already installed): -\- Install Homebrew if not already installed *(copied from [here](https://brew.sh/))*: -``` -$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" -``` -\- Install the `scons` and `zlib` packages: ``` -$ brew install scons zlib +xcode-select --install +sudo xcodebuild -license accept ``` -### Qt-specific steps +- Install Homebrew if not already installed _(copied from [here](https://brew.sh/))_: -\- Install Xcode Command Line Tools (if not already installed; newer Homebrew versions should install them by default): -``` -$ xcode-select --install -``` -\- Install Qt5 package through Homebrew *(NOTE: currently tested to work with XCode 11 and Qt 5.14.1, which supports macOS 10.13+; builds for older macOS versions should also be possible, but you would need to dig up the Homebrew formulae for older versions of Qt5)*: ``` -$ brew install qt -``` -***\*NOTE:*** After building and creating "Gambatte-Speedrun.app", you can run the following command to create a deployable standalone macOS app, which can be used as a release build that other macOS users can run: -``` -$ macdeployqt gambatte_qt/bin/Gambatte-Speedrun.app -dmg +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` -### Testrunner-specific steps +- Install the `cmake`, `ninja`, and `zlib` packages: -\- Install the `libpng` package: ``` -$ brew install libpng +brew install cmake ninja zlib ``` -## Debian/Ubuntu - -### Basic steps +### Testrunner-specific steps -*Open a terminal and do the following:* +- Install the `libpng` and `python3` packages: -\- Install build dependencies: ``` -$ sudo apt install build-essential git scons zlib1g-dev +brew install libpng python3 ``` -### Qt-specific steps +## Linux -\- Install Qt5 development packages, plus Gambatte-Speedrun dependencies that aren't bundled with Qt5: -``` -$ sudo apt install qt5-default libqt5x11extras5-dev libxrandr-dev libxv-dev libasound2-dev -``` +### Basic steps + +_Open a terminal and do the following:_ + +- Install build dependencies: + +- Ubuntu/Debian + ``` + sduo apt update && sudo apt upgrade -y + sudo apt install build-essential git cmake ninja-build libz-dev + ``` +- Fedora/REHL + ``` + sudo dnf update -y + sudo dnf groupinstall 'Development Tools' + sudo dnf install git cmake ninja-build zlib-devel + ``` +- Arch Linux + ``` + sudo pacman -Syu + sudo pacman -S base-devel git cmake ninja zlib + ``` ### Testrunner-specific steps -\- Install the `libpng-dev` package: -``` -$ sudo apt install libpng-dev -``` +Install the `libpng` and `python3` packages: + +- Ubuntu/Debian + ``` + sudo apt install libpng-dev python3 + ``` +- Fedora/REHL + ``` + sudo dnf install libpng-devel python3 + ``` +- Arch Linux + ``` + sudo pacman -S libpng python3 + ``` diff --git a/README.md b/README.md index 5f48c168..4ba9b27f 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,94 @@ -# Gambatte-Speedrun +# Gambatte Core Fork of [Gambatte](https://github.com/sinamas/gambatte) (authored by sinamas), with local changes for Pokémon Speedruns, as well as other speedrunning communities. Under GPLv2. -Below is a brief list of the major differences between Gambatte-Speedrun and the upstream version of Gambatte. +This is the core library, for the Qt frontend, check out [Gambatte-Speedrun](https://github.com/pokemon-speedrunning/gambatte-speedrun). --- -***Emulation core*** -* Original Game Boy games playable in Game Boy Color mode (emulated properly) -* Super Game Boy emulation (to match the SGB2 platform/framerate) -* Cycle-based RTC option (for *"proper"* RTC behavior when using speedups/pauses/savestates) -* Other various emulation fixes, improvements, and configuration options - -***Speedrun features*** - -* Timing parity with official consoles (bootroms required; GBP/SGB2 hard reset fades emulated) -* Emulator version and ROM info displayed in the title bar, and in on-screen display after hard resets -* Disabling of undesired functionality for speedruns (e.g. cheats, turbo, framerate changes, etc.) +## Building from source -***Software updates*** +The library can be built on Windows using [MSYS2](https://www.msys2.org/), on macOS using [Homebrew](https://brew.sh/), and on Linux using your system's package manager. See [INSTALL.md](INSTALL.md) for detailed information on how to set up the build environment. -* Updated to build using Qt5 (instead of Qt4) -* Buildable as a shared library, for programmatic use of the emulation core +The amount of setup you need to do depends on what parts of the project you are planning to use. ---- -## Building from source +Open a terminal and do the following: + +- Clone the project + ``` + git clone https://github.com/pokemon-speedrunning/gambatte-core.git + cd gambatte-core + ``` +- Configure with CMake + ``` + cmake -S . -B build + ``` + Here you can provide additional options such as: + - `-G Ninja` to use ninja over the default generator + - `-DCMAKE_BUILD_TYPE=` to select between `Debug`, `Release`, etc. (default: `Release`) + - `-DCMAKE_INSTALL_PREFIX=/path/to/install` to select where the libraries should be installed + - `-DLIBGAMBATTE_SHARED=ON/OFF` to enable/disable building the shared library (default: `ON`) + - `-DLIBGAMBATTE_STATIC=ON/OFF` to enable/disable building the static library (default: `ON`) + - `-DLIBGAMBATTE_ENABLE_ZIP=ON/OFF` to enable/disable zip support (default: `ON`) + - `-DLIBGAMBATTE_ENABLE_TESTING=ON/OFF` to enable/disable the test suite (default: `ON`) +- Build the library + ``` + cmake --build build + ``` +- Install the library + ``` + cmake --install build + ``` + +Note: When using a multi-config generator (`Visual Studio ...`, `Ninja Multi-Config`), you must specify `--config ` for the `--build` and `--install` steps. -Distributable binaries can be built on Windows using [MSYS2](https://www.msys2.org/) and the qt5-static package, or on macOS using [Homebrew](https://brew.sh/) and the qt package with its `macdeployqt` tool. See [INSTALL.md](INSTALL.md) for detailed information on how to set up the build environment. +### Testrunner -The amount of setup you need to do depends on what parts of the project you are planning to use. +To be able to run the upstream hwtests suite, you must acquire the DMG and CGB bootroms. Name the DMG bootrom `bios.gb`, the CGB bootrom `bios.gbc`, and move both into the `test` directory. -### Shared Library +To run the test suite, you must configure the project with `LIBGAMBATTE_ENABLE_TESTING` set to `ON` and build it. You can then run the following command: -If you only wish to build Gambatte-Speedrun's `libgambatte` shared library (for use in scripting, "botting", TASing, or other programming projects), and have the basic build environment set up, you can run the following from the project's root directory, regardless of platform: ``` -$ sh scripts/build_shlib.sh +ctest --test-dir build ``` -### Gambatte-Speedrun *(i.e. the full-blown emulator)* +## Consuming the library + +You can consume the library either through CMake's `find_package` or pkg-config. + +### CMake package + +To find and link the library, add the following to your CMakeLists: -After completing all the Qt-specific build steps in [INSTALL.md](INSTALL.md), running the following in the project's root directory should build the "PSR" version of Gambatte-Speedrun: ``` -$ sh scripts/build_qt.sh +find_package(libgambatte CONFIG REQUIRED) +target_link_libraries(my_program PRIVATE libgambatte::libgambatte) ``` -To instead build the "non-PSR" version, with additional selectable platforms (GB, GBC, GBA, SGB2), create `gambatte_qt/src/platforms.pri` with the following (before running `build_qt.sh`): + +The package provides the following targets: + +- `libgambatte::libgambatte` +- `libgambatte::libgambatte-shared` +- `libgambatte::libgambatte-static` + +The `libgambatte::libgambatte` target is an alias of the shared library, if the shared library was not built the target instead aliases the static library. + +It is not necessary to install the library to use it through CMake, you can set `libgambatte_DIR` to the build directory when configuring your own project: + ``` -# platform support -# GBP is hardcoded -DEFINES += SHOW_PLATFORM_GB -DEFINES += SHOW_PLATFORM_GBC -DEFINES += SHOW_PLATFORM_GBA -DEFINES += SHOW_PLATFORM_SGB +cd my_project +cmake -S . -B build -Dlibgambatte_DIR=/path/to/gambatte-core/build ``` -### Testrunner +### pkg-config + +For non-CMake projects, a pkg-config file is provided. + +By default, it is installed to `/lib/pkgconfig/libgambatte.pc`, this can be controlled with the `LIBGAMBATTE_INSTALL_PKGCONFDIR` option. -To be able to run the upstream hwtests suite on Gambatte-Speedrun, you must acquire the DMG and CGB bootroms. Name the DMG bootrom `bios.gb`, the CGB bootrom `bios.gbc`, and move both into the `test` directory. +You can then read from it using pkg-config, make sure to set your search patch accordingly: -Run the following in a terminal from the project's root directory to assemble and run all hwtests: ``` -$ (cd test && sh scripts/assemble_tests.sh) -$ sh scripts/test.sh +export PKG_CONFIG_PATH="/path/to/libgambatte/lib/pkgconfig:$PKG_CONFIG_PATH" +pkg-config --cflags --libs libgambatte ``` -Note that the first line (with `assemble_tests.sh`) only needs to be run one time, or until the contents of the hwtests directory change. \ No newline at end of file From 278edca6516aee6b5b6306901658c474290158fa Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 27 May 2023 23:58:11 -0400 Subject: [PATCH 07/23] CI: Update workflow to use CMake. Changes include: - building on multiple platforms - run ctest tests - test consuming from the build directory, from an installation, and from pkg-config. --- .github/workflows/test.yml | 165 ++++++++++++++++++++++++++++++++++--- cmake/test/CMakeLists.txt | 8 ++ cmake/test/main.cpp | 6 ++ 3 files changed, 166 insertions(+), 13 deletions(-) create mode 100644 cmake/test/CMakeLists.txt create mode 100644 cmake/test/main.cpp diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f89fa62..b31d553d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,19 +1,158 @@ -name: Check GSR testROM accuracy +name: Build and run tests + on: [push, pull_request] + jobs: - Check-TestROMs: - runs-on: ubuntu-20.04 + build-and-test: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.runner }} + defaults: + run: + shell: ${{ matrix.config.shell }} + strategy: + fail-fast: false + matrix: + config: + - { + name: "MSYS2 UCRT64", + runner: windows-latest, + shell: "msys2 {0}", + msystem: ucrt64, + msys-env: mingw-w64-ucrt-x86_64, + cmake_flags: "-G Ninja", + } + - { + name: "MSYS2 i686", + runner: windows-latest, + shell: "msys2 {0}", + msystem: mingw32, + msys-env: mingw-w64-i686, + cmake_flags: "-G Ninja", + } + - { + name: "Windows (Clang-cl)", + runner: windows-latest, + shell: bash, + cmake_flags: "-T ClangCL -A x64", + vcpkg_triplet: x64-windows, + } + - { + name: "Windows (Clang GNU)", + runner: windows-latest, + shell: bash, + cmake_flags: "-G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++", + vcpkg_triplet: x64-windows, + } + - { + name: "macOS", + runner: macos-latest, + shell: bash, + cmake_flags: '-G Ninja -DPNG_LIBRARY="$(brew --prefix libpng)/lib/libpng.a" -DPNG_PNG_INCLUDE_DIR="$(brew --prefix libpng)/include"', + } + - { + name: "Linux", + runner: ubuntu-latest, + shell: bash, + cmake_flags: "-G Ninja", + } + steps: - - name: Install dependencies - run: "sudo apt install scons" + - uses: actions/checkout@v3 - - name: Checkout repository code - uses: actions/checkout@v2 + - name: Install dependencies (MSYS) + if: matrix.config.shell == 'msys2 {0}' + uses: msys2/setup-msys2@v2 with: - path: gambatte-speedrun - - - name: Assemble TestROMs - run: "cd gambatte-speedrun/test && sh scripts/assemble_tests.sh" + update: true + msystem: ${{ matrix.config.msystem }} + install: >- + ${{ matrix.config.msys-env }}-gcc + ${{ matrix.config.msys-env }}-cmake + ${{ matrix.config.msys-env }}-ninja + ${{ matrix.config.msys-env }}-pkgconf + ${{ matrix.config.msys-env }}-libpng + ${{ matrix.config.msys-env }}-python3 + ${{ matrix.config.msys-env }}-zlib + + - name: Install dependencies (macOS) + if: runner.os == 'macOS' + run: | + brew update + brew unlink pkg-config + brew install \ + cmake \ + ninja \ + pkgconf \ + libpng \ + python3 \ + zlib + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get upgrade -y + sudo apt-get install -y \ + build-essential \ + cmake \ + ninja-build \ + pkgconf \ + libpng-dev \ + libz-dev \ + python3 + + - name: Install dependencies (Windows) + if: runner.os == 'Windows' && matrix.config.shell != 'msys2 {0}' + run: | + vcpkg install --triplet ${{ matrix.config.vcpkg_triplet }} libpng pkgconf zlib + echo "$VCPKG_INSTALLATION_ROOT/installed/${{ matrix.config.vcpkg_triplet }}/tools/pkgconf" >> $GITHUB_PATH + echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=$VCPKG_INSTALLATION_ROOT/installed/${{ matrix.config.vcpkg_triplet }}/lib/pkgconfig" >> $GITHUB_ENV + + - name: Configure + run: | + cmake -S.\ + -Bbuild \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=install-prefix \ + -DLIBGAMBATTE_BUILD_SHARED=ON \ + -DLIBGAMBATTE_BUILD_STATIC=ON \ + -DLIBGAMBATTE_ENABLE_ZIP=ON \ + -DLIBGAMBATTE_ENABLE_TESTING=ON \ + ${{ matrix.config.cmake_flags }} + + - name: Build + run: | + cmake --build build --config Release + + - name: Run tests + run: | + ctest --extra-verbose --test-dir build -C Release + + - name: Install + run: | + cmake --install build --config Release + + - name: Test consuming from build directory + run: | + cmake -Scmake/test \ + -Bconsumer-from-build \ + -DCMAKE_BUILD_TYPE=Release \ + -Dlibgambatte_DIR="$(pwd)/build" \ + ${{ matrix.config.cmake_flags }} + cmake --build consumer-from-build --config Release + + - name: Test consuming from installation + run: | + cmake -Scmake/test \ + -Bconsumer-from-install \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$(pwd)/install-prefix" \ + ${{ matrix.config.cmake_flags }} + cmake --build consumer-from-install --config Release - - name: Build libgambatte and run tests - run: "cd gambatte-speedrun && sh scripts/test.sh" + - name: Test consuming from pkg-config + run: | + set -eo pipefail + pkgconf --with-path="$(pwd)/install-prefix/lib/pkgconfig" --cflags --libs --static libgambatte + pkgconf --with-path="$(pwd)/install-prefix/lib/pkgconfig" --validate libgambatte diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt new file mode 100644 index 00000000..cd91ab73 --- /dev/null +++ b/cmake/test/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0) +project(libgambatte-consumer-test LANGUAGES CXX) + +add_executable(example main.cpp) + +find_package(libgambatte CONFIG REQUIRED) +target_link_libraries(example PRIVATE libgambatte::libgambatte-static) +set_target_properties(example PROPERTIES CXX_STANDARD 11) diff --git a/cmake/test/main.cpp b/cmake/test/main.cpp new file mode 100644 index 00000000..cf9565c5 --- /dev/null +++ b/cmake/test/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int, char**) { + gambatte::GB foo{}; + return 0; +} From 53b10f7c6cb97eb21e6347dad2e18eab0807bbd6 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 28 May 2023 11:42:38 -0400 Subject: [PATCH 08/23] Add vcpkg config for compatibility with VS. --- .github/workflows/test.yml | 5 +++-- CMakeLists.txt | 22 ++++++++++++++++------ vcpkg-configuration.json | 7 +++++++ vcpkg.json | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 vcpkg-configuration.json create mode 100644 vcpkg.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b31d553d..e9dfde47 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -57,8 +57,6 @@ jobs: } steps: - - uses: actions/checkout@v3 - - name: Install dependencies (MSYS) if: matrix.config.shell == 'msys2 {0}' uses: msys2/setup-msys2@v2 @@ -109,6 +107,8 @@ jobs: echo "CMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$VCPKG_INSTALLATION_ROOT/installed/${{ matrix.config.vcpkg_triplet }}/lib/pkgconfig" >> $GITHUB_ENV + - uses: actions/checkout@v3 + - name: Configure run: | cmake -S.\ @@ -119,6 +119,7 @@ jobs: -DLIBGAMBATTE_BUILD_STATIC=ON \ -DLIBGAMBATTE_ENABLE_ZIP=ON \ -DLIBGAMBATTE_ENABLE_TESTING=ON \ + -DVCPKG_MANIFEST_MODE=OFF \ ${{ matrix.config.cmake_flags }} - name: Build diff --git a/CMakeLists.txt b/CMakeLists.txt index 10b878f9..4562ec2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,18 @@ cmake_minimum_required(VERSION 3.12) + +option(LIBGAMBATTE_BUILD_SHARED "Build the shared version of libgambatte" ON) +option(LIBGAMBATTE_BUILD_STATIC "Build the static version of libgambatte" ON) +option(LIBGAMBATTE_ENABLE_ZIP "Build zip support using zlib" ON) +option(LIBGAMBATTE_ENABLE_TESTING "Build tests" ON) + +if(LIBGAMBATTE_ENABLE_ZIP) + list(APPEND VCPKG_MANIFEST_FEATURES "zip") +endif() + +if(LIBGAMBATTE_ENABLE_TESTING) + list(APPEND VCPKG_MANIFEST_FEATURES "testing") +endif() + project(libgambatte CXX) if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) @@ -9,15 +23,11 @@ endif() include(GNUInstallDirs) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") - -option(LIBGAMBATTE_BUILD_SHARED "Build the shared version of libgambatte" ON) -option(LIBGAMBATTE_BUILD_STATIC "Build the static version of libgambatte" ON) -option(LIBGAMBATTE_ENABLE_ZIP "Build zip support using zlib" ON) -option(LIBGAMBATTE_ENABLE_TESTING "Build tests" ON) set(LIBGAMBATTE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/libgambatte" CACHE STRING "Directory to install the CMake package files to") set(LIBGAMBATTE_INSTALL_PKGCONFDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig" CACHE STRING "Directory to install the pkg-config file to") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + if(NOT LIBGAMBATTE_BUILD_SHARED AND NOT LIBGAMBATTE_BUILD_STATIC) message(FATAL_ERROR "Both shared and static libraries have been disabled. Turn on at least one.") endif() diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 00000000..530f0d1e --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,7 @@ +{ + "default-registry": { + "kind": "git", + "baseline": "afbb37cfd648335fd980f6fb7dbc2d0c84c009cf", + "repository": "https://github.com/microsoft/vcpkg" + } +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..1e21cc9e --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", + "name": "libgambatte", + "features": { + "testing": { + "description": "Build tests", + "dependencies": [ + "libpng", + "python3" + ] + }, + "zip": { + "description": "Build with zip support", + "dependencies": [ + "zlib" + ] + } + } +} From d3a7e1117dfcbab9211abd0c027ff6f723bdec29 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 28 May 2023 12:02:25 -0400 Subject: [PATCH 09/23] Update documentation to cover Visual Studio. --- INSTALL.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index d0a2366e..6aaf5486 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -16,6 +16,8 @@ Platform-specific instructions: ## Windows +### Msys2 + **NOTE:** The instructions below assume you're installing 64-bit MSYS2 and using the 32-bit MinGW toolchain (they work with other configurations, but the commands won't be exactly the same). - Choice of 32-bit vs 64-bit for MSYS2 doesn't really matter; either is fine @@ -23,8 +25,6 @@ Platform-specific instructions: **NOTE:** At the end of these steps, to do any build tasks related to libgambatte, open the MSYS2 MinGW 32-bit shell (under the MSYS2 folder in the Start menu; **\*\*must be\*\*** this specific shell in order for building to work). -### Basic steps - Do the following: - Install [MSYS2](https://www.msys2.org/) by selecting the one-click installer exe for x86_64 @@ -49,19 +49,36 @@ pacman -S base-devel \ mingw-w64-i686-zlib ``` -### Testrunner-specific steps - -- Install the `libpng` and `python3` packages: +- To build the testrunner, install the `libpng` and `python3` packages: ``` pacman -S mingw-w64-i686-libpng mingw-w64-i686-python3 ``` -### Visual Studio users +### MSVC + +**NOTE:** At the end of these steps, to do any build tasks related to libgambatte, open "Developer PowerShell for VS 2022" (**\*\*must be\*\*** this specific shell in order for building to work). + +The Microsoft VisualC toolchain is distributed with Visual Studio, moreover, it provides options to install CMake and vcpkg. -As there is no standard package manager for Visual Studio, you will need to either manually build zlib (and libpng for test), or use a package manager such as [vcpkg](https://github.com/microsoft/vcpkg). +- Download the Community Edition of [Visual Studio](https://visualstudio.microsoft.com/) +- In the installer, select the "Desktop development with C++" workflow +- In the right panel, ensure the "C++ CMake tools for Windows" and "vcpkg package manager" are ticked +- Proceed with the installation -You will also need to manually install [CMake](https://cmake.org/) and to make sure to add it to your `PATH` during installation. +While Visual Studio has a git integration, you are not able to use it from a terminal. Install it from [git-scm](https://git-scm.com/) and make sure you allow using it from outside of Git Bash. + +Lastly, open a Developer PowerShell and run the following command: + +``` +vcpkg integrate install +``` + +Take note of the following line, you will need to pass this to the CMake configure commands: + +``` +CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake" +``` ## macOS From 4ea626ad66ba8134aef373587ce0ffe5625663a1 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 18:19:30 -0400 Subject: [PATCH 10/23] Properly set symbols' visibility. - Classes and functions in public headers should be exported. - Internal symbols should hidden. - The "cinterface.cpp" module was left unmodified for compatibility. - This reduces binary size for non-Windows platforms. - For Windows, it allows using a DLL with the public API. --- CMakeLists.txt | 8 +++++++ cmake/GetAvailableCompileFlags.cmake | 11 ++++++++-- cmake/config.h.in | 1 + libgambatte/include/gambatte.h | 3 ++- libgambatte/include/gbexport.h | 32 ++++++++++++++++++++++++++++ libgambatte/include/loadres.h | 4 +++- libgambatte/include/newstate.h | 12 ++++++----- libgambatte/include/pakinfo.h | 4 +++- libgambatte/src/cinterface.cpp | 6 ++++-- 9 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 libgambatte/include/gbexport.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4562ec2c..bd1c0785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,14 @@ if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") endif() endif() +include(CheckCXXSourceCompiles) +check_cxx_source_compiles( + "#include + static void f(void) __attribute__ ((visibility(\"default\"))); + int main(void) {return 0;}" + LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT +) + configure_file( "${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in" "${libgambatte_BINARY_DIR}/include/gambatte/config.h" diff --git a/cmake/GetAvailableCompileFlags.cmake b/cmake/GetAvailableCompileFlags.cmake index 92dbdcba..9cd01dd4 100644 --- a/cmake/GetAvailableCompileFlags.cmake +++ b/cmake/GetAvailableCompileFlags.cmake @@ -3,8 +3,15 @@ function(get_available_compile_flags OUT_VAR) set(FLAGS_TO_CHECK "/W4" "/GR-") set(WARNINGS_AS_ERROR "/WX") else() - set(FLAGS_TO_CHECK "-Wall" "-Wextra" "-fno-rtti" "-fno-exceptions") - set(CXX_ONLY_FLAGS "-fno-rtti" "-fno-exceptions") + set(FLAGS_TO_CHECK + "-Wall" + "-Wextra" + "-fno-rtti" + "-fno-exceptions" + "-fvisibility=hidden" + "-fvisibility-inlines-hidden" + ) + set(CXX_ONLY_FLAGS "-fno-rtti" "-fno-exceptions" "-fvisibility-inlines-hidden") set(WARNINGS_AS_ERROR "-Werror") endif() diff --git a/cmake/config.h.in b/cmake/config.h.in index ddd04749..637b0db4 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -3,3 +3,4 @@ #cmakedefine HAVE_CSTDINT #cmakedefine HAVE_STDINT_H #cmakedefine LIBGAMBATTE_SUPPORT_ZIP +#cmakedefine LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 282f32d8..c4915490 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -19,6 +19,7 @@ #ifndef GAMBATTE_H #define GAMBATTE_H +#include "gbexport.h" #include "gbint.h" #include "inputgetter.h" #include "loadres.h" @@ -49,7 +50,7 @@ enum eCDLog_Flags { eCDLog_Flags_Data = 4, }; -class GB { +class GBEXPORT GB { public: GB(); ~GB(); diff --git a/libgambatte/include/gbexport.h b/libgambatte/include/gbexport.h new file mode 100644 index 00000000..ada1ef09 --- /dev/null +++ b/libgambatte/include/gbexport.h @@ -0,0 +1,32 @@ +// +// Copyright (C) 2023 by Pierre Wendling +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef GBEXPORT_H +#define GBEXPORT_H + +#include "config.h" + +#if defined(_WIN32) || defined(__CYGWIN__) +#define GBEXPORT __declspec(dllexport) +#elif defined(LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) +#define GBEXPORT __attribute__((visibility("default"))) +#else +#define GBEXPORT +#endif + +#endif diff --git a/libgambatte/include/loadres.h b/libgambatte/include/loadres.h index f72c1331..1b9e815a 100644 --- a/libgambatte/include/loadres.h +++ b/libgambatte/include/loadres.h @@ -1,6 +1,8 @@ #ifndef GAMBATTE_LOADRES_H #define GAMBATTE_LOADRES_H +#include "gbexport.h" + #include namespace gambatte { @@ -16,7 +18,7 @@ enum LoadRes { LOADRES_OK = 0 }; -std::string const to_string(LoadRes); +std::string const GBEXPORT to_string(LoadRes); } diff --git a/libgambatte/include/newstate.h b/libgambatte/include/newstate.h index 548e5103..c779285b 100644 --- a/libgambatte/include/newstate.h +++ b/libgambatte/include/newstate.h @@ -1,12 +1,14 @@ #ifndef NEWSTATE_H #define NEWSTATE_H +#include "gbexport.h" + #include #include namespace gambatte { -class NewState { +class GBEXPORT NewState { public: virtual void Save(void const *ptr, std::size_t size, char const *name) = 0; virtual void Load(void *ptr, std::size_t size, char const *name) = 0; @@ -14,7 +16,7 @@ class NewState { virtual void ExitSection(char const */*name*/) { } }; -class NewStateDummy : public NewState { +class GBEXPORT NewStateDummy : public NewState { public: NewStateDummy(); long GetLength() { return length; } @@ -26,7 +28,7 @@ class NewStateDummy : public NewState { long length; }; -class NewStateExternalBuffer : public NewState { +class GBEXPORT NewStateExternalBuffer : public NewState { public: NewStateExternalBuffer(char *buffer, long maxlength); long GetLength() { return length; } @@ -41,14 +43,14 @@ class NewStateExternalBuffer : public NewState { const long maxlength; }; -struct FPtrs { +struct GBEXPORT FPtrs { void (*Save_)(void const *ptr, std::size_t size, char const *name); void (*Load_)(void *ptr, std::size_t size, char const *name); void (*EnterSection_)(char const *name); void (*ExitSection_)(char const *name); }; -class NewStateExternalFunctions : public NewState { +class GBEXPORT NewStateExternalFunctions : public NewState { public: NewStateExternalFunctions(const FPtrs *ff); virtual void Save(void const *ptr, std::size_t size, char const *name); diff --git a/libgambatte/include/pakinfo.h b/libgambatte/include/pakinfo.h index ace947cc..bd37c8dc 100644 --- a/libgambatte/include/pakinfo.h +++ b/libgambatte/include/pakinfo.h @@ -1,11 +1,13 @@ #ifndef GAMBATTE_PAKINFO_H #define GAMBATTE_PAKINFO_H +#include "gbexport.h" + #include namespace gambatte { -class PakInfo { +class GBEXPORT PakInfo { public: PakInfo(); PakInfo(bool mbc1m, bool m161, bool mmm01, bool wisdomtree, unsigned rombanks, unsigned crc, unsigned char const romheader[]); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 64e33db2..a6de9fd8 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -16,7 +16,7 @@ // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // -#include "cinterface.h" +#include "gbexport.h" #include "gambatte.h" #include "newstate.h" @@ -43,6 +43,8 @@ namespace { using namespace gambatte; +extern "C" { + GBEXPORT int gambatte_revision() { return LIBGAMBATTE_REVISION; } @@ -301,5 +303,5 @@ GBEXPORT int gambatte_getdivstate(GB *g) { GBEXPORT void gambatte_setspeedupflags(GB *g, unsigned flags) { g->setSpeedupFlags(flags); } - +} } From ac92f8ecbb89ce7426700a61a72d23ecc61fa474 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 18:54:27 -0400 Subject: [PATCH 11/23] Add the libgambatte::libgambatte alias. This allows using the same target when adding the project as a subproject using `add_subdirectory`. --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd1c0785..809654f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,13 @@ if(LIBGAMBATTE_BUILD_STATIC) list(APPEND LIBGAMBATTE_TARGETS libgambatte-static) endif() +# Create the same alias as the exported CMake config +if(LIBGAMBATTE_BUILD_SHARED) + add_library(libgambatte::libgambatte ALIAS libgambatte-shared) +else() + add_library(libgambatte::libgambatte ALIAS libgambatte-static) +endif() + set_target_properties(${LIBGAMBATTE_TARGETS} PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED TRUE From 93d5f8fbe9c1c7462b8b8f708cc88c48cee52c59 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 19:17:45 -0400 Subject: [PATCH 12/23] Test building with pkg-config more thoroughly. --- .github/workflows/test.yml | 9 +++++++-- cmake/test/CMakeLists.txt | 18 ++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e9dfde47..060a70b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -154,6 +154,11 @@ jobs: - name: Test consuming from pkg-config run: | - set -eo pipefail - pkgconf --with-path="$(pwd)/install-prefix/lib/pkgconfig" --cflags --libs --static libgambatte pkgconf --with-path="$(pwd)/install-prefix/lib/pkgconfig" --validate libgambatte + cmake -Scmake/test \ + -Bconsumer-from-pkg-config \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$(pwd)/install-prefix" \ + -DUSE_PKG_CONFIG=TRUE \ + ${{ matrix.config.cmake_flags }} + cmake --build consumer-from-pkg-config --config Release diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index cd91ab73..60c0072b 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -1,8 +1,18 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.6) project(libgambatte-consumer-test LANGUAGES CXX) -add_executable(example main.cpp) +option(USE_PKG_CONFIG "Use pkg-config instead of find_package" OFF) -find_package(libgambatte CONFIG REQUIRED) -target_link_libraries(example PRIVATE libgambatte::libgambatte-static) +add_executable(example main.cpp) set_target_properties(example PROPERTIES CXX_STANDARD 11) + +if(USE_PKG_CONFIG) + find_package(PkgConfig REQUIRED) + pkg_check_modules(libgambatte REQUIRED IMPORTED_TARGET libgambatte) + set(LIBGAMBATTE_TARGET PkgConfig::libgambatte) +else() + find_package(libgambatte CONFIG REQUIRED) + set(LIBGAMBATTE_TARGET libgambatte::libgambatte-static) +endif() + +target_link_libraries(example PRIVATE ${LIBGAMBATTE_TARGET}) From 98e01d22a4e8e5fd894afd047c5dc1383889689c Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 20:04:18 -0400 Subject: [PATCH 13/23] Move misplaced GBEXPORT. --- libgambatte/include/loadres.h | 2 +- libgambatte/src/loadres.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libgambatte/include/loadres.h b/libgambatte/include/loadres.h index 1b9e815a..b286f590 100644 --- a/libgambatte/include/loadres.h +++ b/libgambatte/include/loadres.h @@ -18,7 +18,7 @@ enum LoadRes { LOADRES_OK = 0 }; -std::string const GBEXPORT to_string(LoadRes); +GBEXPORT std::string const to_string(LoadRes); } diff --git a/libgambatte/src/loadres.cpp b/libgambatte/src/loadres.cpp index 0da2bb19..d4a9686a 100644 --- a/libgambatte/src/loadres.cpp +++ b/libgambatte/src/loadres.cpp @@ -17,6 +17,6 @@ static char const * to_cstr(LoadRes const loadres) { return ""; } -std::string const to_string(LoadRes loadres) { return to_cstr(loadres); } +GBEXPORT std::string const to_string(LoadRes loadres) { return to_cstr(loadres); } } From 1f5459048df569d3d5cbd0f8ed52715b9ab22a69 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 22:29:28 -0400 Subject: [PATCH 14/23] Also include cinterface.cpp in static builds. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 809654f7..88c4aeb7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ get_available_compile_flags(LIBGAMBATTE_COMPILE_FLAGS) set(LIBGAMBATTE_SOURCES "libgambatte/src/bitmap_font.cpp" + "libgambatte/src/cinterface.cpp" "libgambatte/src/cpu.cpp" "libgambatte/src/gambatte.cpp" "libgambatte/src/initstate.cpp" @@ -164,7 +165,7 @@ configure_file( ) if(LIBGAMBATTE_BUILD_SHARED) - add_library(libgambatte-shared SHARED ${LIBGAMBATTE_SOURCES} "libgambatte/src/cinterface.cpp") + add_library(libgambatte-shared SHARED ${LIBGAMBATTE_SOURCES}) add_library(libgambatte::libgambatte-shared ALIAS libgambatte-shared) list(APPEND LIBGAMBATTE_TARGETS libgambatte-shared) endif() From 93b78c0e8ed759cbb4d52a49353178fb307c535e Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 22:33:46 -0400 Subject: [PATCH 15/23] Partially revert 4ea626ad66ba8134aef373587ce0ffe5625663a1 The C++ API should *not* be exported in the shared library. --- libgambatte/include/gambatte.h | 3 +-- libgambatte/include/gbexport.h | 32 -------------------------------- libgambatte/include/loadres.h | 4 +--- libgambatte/include/newstate.h | 12 +++++------- libgambatte/include/pakinfo.h | 4 +--- libgambatte/src/loadres.cpp | 2 +- 6 files changed, 9 insertions(+), 48 deletions(-) delete mode 100644 libgambatte/include/gbexport.h diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index c4915490..282f32d8 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -19,7 +19,6 @@ #ifndef GAMBATTE_H #define GAMBATTE_H -#include "gbexport.h" #include "gbint.h" #include "inputgetter.h" #include "loadres.h" @@ -50,7 +49,7 @@ enum eCDLog_Flags { eCDLog_Flags_Data = 4, }; -class GBEXPORT GB { +class GB { public: GB(); ~GB(); diff --git a/libgambatte/include/gbexport.h b/libgambatte/include/gbexport.h deleted file mode 100644 index ada1ef09..00000000 --- a/libgambatte/include/gbexport.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright (C) 2023 by Pierre Wendling -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License version 2 for more details. -// -// You should have received a copy of the GNU General Public License -// version 2 along with this program; if not, write to the -// Free Software Foundation, Inc., -// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -// - -#ifndef GBEXPORT_H -#define GBEXPORT_H - -#include "config.h" - -#if defined(_WIN32) || defined(__CYGWIN__) -#define GBEXPORT __declspec(dllexport) -#elif defined(LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) -#define GBEXPORT __attribute__((visibility("default"))) -#else -#define GBEXPORT -#endif - -#endif diff --git a/libgambatte/include/loadres.h b/libgambatte/include/loadres.h index b286f590..edcfcbd9 100644 --- a/libgambatte/include/loadres.h +++ b/libgambatte/include/loadres.h @@ -1,8 +1,6 @@ #ifndef GAMBATTE_LOADRES_H #define GAMBATTE_LOADRES_H -#include "gbexport.h" - #include namespace gambatte { @@ -18,7 +16,7 @@ enum LoadRes { LOADRES_OK = 0 }; -GBEXPORT std::string const to_string(LoadRes); + std::string const to_string(LoadRes); } diff --git a/libgambatte/include/newstate.h b/libgambatte/include/newstate.h index c779285b..2e20c90d 100644 --- a/libgambatte/include/newstate.h +++ b/libgambatte/include/newstate.h @@ -1,14 +1,12 @@ #ifndef NEWSTATE_H #define NEWSTATE_H -#include "gbexport.h" - #include #include namespace gambatte { -class GBEXPORT NewState { +class NewState { public: virtual void Save(void const *ptr, std::size_t size, char const *name) = 0; virtual void Load(void *ptr, std::size_t size, char const *name) = 0; @@ -16,7 +14,7 @@ class GBEXPORT NewState { virtual void ExitSection(char const */*name*/) { } }; -class GBEXPORT NewStateDummy : public NewState { +class NewStateDummy : public NewState { public: NewStateDummy(); long GetLength() { return length; } @@ -28,7 +26,7 @@ class GBEXPORT NewStateDummy : public NewState { long length; }; -class GBEXPORT NewStateExternalBuffer : public NewState { +class NewStateExternalBuffer : public NewState { public: NewStateExternalBuffer(char *buffer, long maxlength); long GetLength() { return length; } @@ -43,14 +41,14 @@ class GBEXPORT NewStateExternalBuffer : public NewState { const long maxlength; }; -struct GBEXPORT FPtrs { +struct FPtrs { void (*Save_)(void const *ptr, std::size_t size, char const *name); void (*Load_)(void *ptr, std::size_t size, char const *name); void (*EnterSection_)(char const *name); void (*ExitSection_)(char const *name); }; -class GBEXPORT NewStateExternalFunctions : public NewState { +class NewStateExternalFunctions : public NewState { public: NewStateExternalFunctions(const FPtrs *ff); virtual void Save(void const *ptr, std::size_t size, char const *name); diff --git a/libgambatte/include/pakinfo.h b/libgambatte/include/pakinfo.h index bd37c8dc..e8fcf980 100644 --- a/libgambatte/include/pakinfo.h +++ b/libgambatte/include/pakinfo.h @@ -1,13 +1,11 @@ #ifndef GAMBATTE_PAKINFO_H #define GAMBATTE_PAKINFO_H -#include "gbexport.h" - #include namespace gambatte { -class GBEXPORT PakInfo { +class PakInfo { public: PakInfo(); PakInfo(bool mbc1m, bool m161, bool mmm01, bool wisdomtree, unsigned rombanks, unsigned crc, unsigned char const romheader[]); diff --git a/libgambatte/src/loadres.cpp b/libgambatte/src/loadres.cpp index d4a9686a..e867cae9 100644 --- a/libgambatte/src/loadres.cpp +++ b/libgambatte/src/loadres.cpp @@ -17,6 +17,6 @@ static char const * to_cstr(LoadRes const loadres) { return ""; } -GBEXPORT std::string const to_string(LoadRes loadres) { return to_cstr(loadres); } + std::string const to_string(LoadRes loadres) { return to_cstr(loadres); } } From 78791e7ca580774a3d79523d0c2d801a27b0aba0 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sat, 3 Jun 2023 22:34:41 -0400 Subject: [PATCH 16/23] Expose the cinterface functions in gambatte-c.h --- cmake/test/main.cpp | 4 +- libgambatte/include/gambatte-c.h | 160 +++++++++++++++++++++++++++++++ libgambatte/src/cinterface.cpp | 2 +- libgambatte/src/cinterface.h | 28 ------ 4 files changed, 163 insertions(+), 31 deletions(-) create mode 100644 libgambatte/include/gambatte-c.h delete mode 100644 libgambatte/src/cinterface.h diff --git a/cmake/test/main.cpp b/cmake/test/main.cpp index cf9565c5..9bd8a74c 100644 --- a/cmake/test/main.cpp +++ b/cmake/test/main.cpp @@ -1,6 +1,6 @@ -#include +#include int main(int, char**) { - gambatte::GB foo{}; + (void)gambatte_revision(); return 0; } diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h new file mode 100644 index 00000000..e7f13fb7 --- /dev/null +++ b/libgambatte/include/gambatte-c.h @@ -0,0 +1,160 @@ +// +// Copyright (C) 2023 by Pierre Wendling +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License version 2 for more details. +// +// You should have received a copy of the GNU General Public License +// version 2 along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// + +/* C interface for using a shared libgambatte */ + +#ifndef GAMBATTE_C_H +#define GAMBATTE_C_H + +#include "config.h" + +#include "gambatte.h" +#include "newstate.h" + +#if defined(_WIN32) || defined(__CYGWIN__) +#define GBEXPORT __declspec(dllexport) +#elif defined(LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) +#define GBEXPORT __attribute__((visibility("default"))) +#else +#define GBEXPORT +#endif + +extern "C" { + +GBEXPORT int gambatte_revision(); + +GBEXPORT gambatte::GB *gambatte_create(); + +GBEXPORT void gambatte_destroy(gambatte::GB *g); + +GBEXPORT int gambatte_load(gambatte::GB *g, char const *romfile, unsigned flags); + +GBEXPORT int gambatte_loadbuf(gambatte::GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags); + +GBEXPORT int gambatte_loadbios(gambatte::GB *g, char const *biosfile, unsigned size, unsigned crc); + +GBEXPORT int gambatte_loadbiosbuf(gambatte::GB *g, char const *biosfiledata, unsigned size); + +GBEXPORT int gambatte_runfor(gambatte::GB *g, unsigned *videoBuf, int pitch, unsigned *audioBuf, unsigned *samples); + +GBEXPORT int gambatte_updatescreenborder(gambatte::GB *g, unsigned *videoBuf, int pitch); + +GBEXPORT int gambatte_generatesgbsamples(gambatte::GB *g, short *audioBuf, unsigned *samples); + +GBEXPORT int gambatte_generatembcsamples(gambatte::GB *g, short *audioBuf); + +GBEXPORT void gambatte_setlayers(gambatte::GB *g, unsigned mask); + +GBEXPORT void gambatte_settimemode(gambatte::GB *g, bool useCycles); + +GBEXPORT void gambatte_setrtcdivisoroffset(gambatte::GB *g, int rtcDivisorOffset); + +GBEXPORT void gambatte_reset(gambatte::GB *g, unsigned samplesToStall); + +GBEXPORT void gambatte_setdmgpalettecolor(gambatte::GB *g, unsigned palnum, unsigned colornum, unsigned rgb32); + +GBEXPORT void gambatte_setcgbpalette(gambatte::GB *g, unsigned *lut); + +GBEXPORT void gambatte_setinputgetter(gambatte::GB *g, gambatte::InputGetter *getInput, void *p); + +GBEXPORT int gambatte_getjoypadindex(gambatte::GB *g); + +GBEXPORT void gambatte_setreadcallback(gambatte::GB *g, gambatte::MemoryCallback callback); + +GBEXPORT void gambatte_setwritecallback(gambatte::GB *g, gambatte::MemoryCallback callback); + +GBEXPORT void gambatte_setexeccallback(gambatte::GB *g, gambatte::MemoryCallback callback); + +GBEXPORT void gambatte_setcdcallback(gambatte::GB *g, gambatte::CDCallback cdc); + +GBEXPORT void gambatte_settracecallback(gambatte::GB *g, void (*callback)(void *)); + +GBEXPORT void gambatte_setscanlinecallback(gambatte::GB *g, void (*callback)(), int sl); + +GBEXPORT void gambatte_setlinkcallback(gambatte::GB *g, void (*callback)()); + +GBEXPORT void gambatte_setcameracallback(gambatte::GB *g, void (*callback)(int *cameraBuf)); + +GBEXPORT void gambatte_setremotecallback(gambatte::GB *g, unsigned char (*callback)()); + +GBEXPORT void gambatte_setcartbuspulluptime(gambatte::GB *g, unsigned cartBusPullUpTime); + +GBEXPORT int gambatte_iscgb(gambatte::GB *g); + +GBEXPORT int gambatte_iscgbdmg(gambatte::GB *g); + +GBEXPORT int gambatte_isloaded(gambatte::GB *g); + +GBEXPORT void gambatte_savesavedata(gambatte::GB *g, char *dest); + +GBEXPORT void gambatte_loadsavedata(gambatte::GB *g, char const *data); + +GBEXPORT int gambatte_getsavedatalength(gambatte::GB *g); + +GBEXPORT int gambatte_newstatelen(gambatte::GB *g); + +GBEXPORT int gambatte_newstatesave(gambatte::GB *g, char *data, int len); + +GBEXPORT int gambatte_newstateload(gambatte::GB *g, char const *data, int len); + +GBEXPORT void gambatte_newstatesave_ex(gambatte::GB *g, gambatte::FPtrs *ff); + +GBEXPORT void gambatte_newstateload_ex(gambatte::GB *g, gambatte::FPtrs *ff); + +GBEXPORT void gambatte_romtitle(gambatte::GB *g, char *dest); + +GBEXPORT void gambatte_pakinfo(gambatte::GB *g, char *mbc, unsigned *rambanks, unsigned *rombanks, unsigned *crc, unsigned *headerChecksumOk); + +GBEXPORT int gambatte_getmemoryarea(gambatte::GB *g, int which, unsigned char **data, int *length); + +GBEXPORT unsigned gambatte_savestate(gambatte::GB *g, unsigned const *videoBuf, int pitch, char *stateBuf); + +GBEXPORT bool gambatte_loadstate(gambatte::GB *g, char const *stateBuf, unsigned size); + +GBEXPORT unsigned char gambatte_cpuread(gambatte::GB *g, unsigned short addr); + +GBEXPORT void gambatte_cpuwrite(gambatte::GB *g, unsigned short addr, unsigned char val); + +GBEXPORT int gambatte_linkstatus(gambatte::GB *g, int which); + +GBEXPORT unsigned gambatte_getbank(gambatte::GB *g, unsigned type); + +GBEXPORT unsigned gambatte_getaddrbank(gambatte::GB *g, unsigned short addr); + +GBEXPORT void gambatte_setbank(gambatte::GB *g, unsigned type, unsigned bank); + +GBEXPORT void gambatte_setaddrbank(gambatte::GB *g, unsigned short addr, unsigned bank); + +GBEXPORT void gambatte_getregs(gambatte::GB *g, int *dest); + +GBEXPORT void gambatte_setregs(gambatte::GB *g, int *src); + +GBEXPORT void gambatte_setinterruptaddresses(gambatte::GB *g, int *addrs, int numAddrs); + +GBEXPORT int gambatte_gethitinterruptaddress(gambatte::GB *g); + +GBEXPORT unsigned long long gambatte_timenow(gambatte::GB *g); + +GBEXPORT void gambatte_settime(gambatte::GB *g, unsigned long long dividers); + +GBEXPORT int gambatte_getdivstate(gambatte::GB *g); + +GBEXPORT void gambatte_setspeedupflags(gambatte::GB *g, unsigned flags); +} + +#endif /* GAMBATTE_C_H */ diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index a6de9fd8..552228e5 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -16,7 +16,7 @@ // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // -#include "gbexport.h" +#include "gambatte-c.h" #include "gambatte.h" #include "newstate.h" diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h deleted file mode 100644 index 2e87945c..00000000 --- a/libgambatte/src/cinterface.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (C) 2007 by sinamas -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License version 2 for more details. -// -// You should have received a copy of the GNU General Public License -// version 2 along with this program; if not, write to the -// Free Software Foundation, Inc., -// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. -// - -#ifndef CINTERFACE_H -#define CINTERFACE_H - -#ifdef _WIN32 -# define GBEXPORT extern "C" __declspec(dllexport) -#else -# define GBEXPORT extern "C" -#endif - -#endif From a1f208b5e6a741406b3eb29e96d8acdb9107f6e6 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 00:07:07 -0400 Subject: [PATCH 17/23] Make gambatte-c usable in C. --- libgambatte/include/gambatte-c.h | 142 ++++++++++++++++++------------- 1 file changed, 82 insertions(+), 60 deletions(-) diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h index e7f13fb7..023cf418 100644 --- a/libgambatte/include/gambatte-c.h +++ b/libgambatte/include/gambatte-c.h @@ -16,16 +16,33 @@ // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // -/* C interface for using a shared libgambatte */ +/* C interface for using libgambatte */ #ifndef GAMBATTE_C_H #define GAMBATTE_C_H #include "config.h" +#ifndef __cplusplus +#include +#include + +typedef struct GB GB; +typedef struct FPtrs FPtrs; +typedef unsigned(InputGetter)(void *); +typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); +typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); +#else #include "gambatte.h" #include "newstate.h" +using GB = gambatte::GB; +using FPtrs = gambatte::FPtrs; +using InputGetter = gambatte::InputGetter; +using MemoryCallback = gambatte::MemoryCallback; +using CDCallback = gambatte::CDCallback; +#endif + #if defined(_WIN32) || defined(__CYGWIN__) #define GBEXPORT __declspec(dllexport) #elif defined(LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) @@ -34,127 +51,132 @@ #define GBEXPORT #endif +#ifdef __cplusplus extern "C" { +#endif GBEXPORT int gambatte_revision(); -GBEXPORT gambatte::GB *gambatte_create(); +GBEXPORT GB *gambatte_create(); + +GBEXPORT void gambatte_destroy(GB *g); -GBEXPORT void gambatte_destroy(gambatte::GB *g); +GBEXPORT int gambatte_load(GB *g, char const *romfile, unsigned flags); -GBEXPORT int gambatte_load(gambatte::GB *g, char const *romfile, unsigned flags); +GBEXPORT int gambatte_loadbuf(GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags); -GBEXPORT int gambatte_loadbuf(gambatte::GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags); +GBEXPORT int gambatte_loadbios(GB *g, char const *biosfile, unsigned size, unsigned crc); -GBEXPORT int gambatte_loadbios(gambatte::GB *g, char const *biosfile, unsigned size, unsigned crc); +GBEXPORT int gambatte_loadbiosbuf(GB *g, char const *biosfiledata, unsigned size); -GBEXPORT int gambatte_loadbiosbuf(gambatte::GB *g, char const *biosfiledata, unsigned size); +GBEXPORT int gambatte_runfor(GB *g, unsigned *videoBuf, int pitch, unsigned *audioBuf, unsigned *samples); -GBEXPORT int gambatte_runfor(gambatte::GB *g, unsigned *videoBuf, int pitch, unsigned *audioBuf, unsigned *samples); +GBEXPORT int gambatte_updatescreenborder(GB *g, unsigned *videoBuf, int pitch); -GBEXPORT int gambatte_updatescreenborder(gambatte::GB *g, unsigned *videoBuf, int pitch); +GBEXPORT int gambatte_generatesgbsamples(GB *g, short *audioBuf, unsigned *samples); -GBEXPORT int gambatte_generatesgbsamples(gambatte::GB *g, short *audioBuf, unsigned *samples); +GBEXPORT int gambatte_generatembcsamples(GB *g, short *audioBuf); -GBEXPORT int gambatte_generatembcsamples(gambatte::GB *g, short *audioBuf); +GBEXPORT void gambatte_setlayers(GB *g, unsigned mask); -GBEXPORT void gambatte_setlayers(gambatte::GB *g, unsigned mask); +GBEXPORT void gambatte_settimemode(GB *g, bool useCycles); -GBEXPORT void gambatte_settimemode(gambatte::GB *g, bool useCycles); +GBEXPORT void gambatte_setrtcdivisoroffset(GB *g, int rtcDivisorOffset); -GBEXPORT void gambatte_setrtcdivisoroffset(gambatte::GB *g, int rtcDivisorOffset); +GBEXPORT void gambatte_reset(GB *g, unsigned samplesToStall); -GBEXPORT void gambatte_reset(gambatte::GB *g, unsigned samplesToStall); +GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32); -GBEXPORT void gambatte_setdmgpalettecolor(gambatte::GB *g, unsigned palnum, unsigned colornum, unsigned rgb32); +GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut); -GBEXPORT void gambatte_setcgbpalette(gambatte::GB *g, unsigned *lut); +GBEXPORT void gambatte_setinputgetter(GB *g, InputGetter *getInput, void *p); -GBEXPORT void gambatte_setinputgetter(gambatte::GB *g, gambatte::InputGetter *getInput, void *p); +GBEXPORT int gambatte_getjoypadindex(GB *g); -GBEXPORT int gambatte_getjoypadindex(gambatte::GB *g); +GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback); -GBEXPORT void gambatte_setreadcallback(gambatte::GB *g, gambatte::MemoryCallback callback); +GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback); -GBEXPORT void gambatte_setwritecallback(gambatte::GB *g, gambatte::MemoryCallback callback); +GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback); -GBEXPORT void gambatte_setexeccallback(gambatte::GB *g, gambatte::MemoryCallback callback); +GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc); -GBEXPORT void gambatte_setcdcallback(gambatte::GB *g, gambatte::CDCallback cdc); +GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)); -GBEXPORT void gambatte_settracecallback(gambatte::GB *g, void (*callback)(void *)); +GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl); -GBEXPORT void gambatte_setscanlinecallback(gambatte::GB *g, void (*callback)(), int sl); +GBEXPORT void gambatte_setlinkcallback(GB *g, void (*callback)()); -GBEXPORT void gambatte_setlinkcallback(gambatte::GB *g, void (*callback)()); +GBEXPORT void gambatte_setcameracallback(GB *g, void (*callback)(int *cameraBuf)); -GBEXPORT void gambatte_setcameracallback(gambatte::GB *g, void (*callback)(int *cameraBuf)); +GBEXPORT void gambatte_setremotecallback(GB *g, unsigned char (*callback)()); -GBEXPORT void gambatte_setremotecallback(gambatte::GB *g, unsigned char (*callback)()); +GBEXPORT void gambatte_setcartbuspulluptime(GB *g, unsigned cartBusPullUpTime); -GBEXPORT void gambatte_setcartbuspulluptime(gambatte::GB *g, unsigned cartBusPullUpTime); +GBEXPORT int gambatte_iscgb(GB *g); -GBEXPORT int gambatte_iscgb(gambatte::GB *g); +GBEXPORT int gambatte_iscgbdmg(GB *g); -GBEXPORT int gambatte_iscgbdmg(gambatte::GB *g); +GBEXPORT int gambatte_isloaded(GB *g); -GBEXPORT int gambatte_isloaded(gambatte::GB *g); +GBEXPORT void gambatte_savesavedata(GB *g, char *dest); -GBEXPORT void gambatte_savesavedata(gambatte::GB *g, char *dest); +GBEXPORT void gambatte_loadsavedata(GB *g, char const *data); -GBEXPORT void gambatte_loadsavedata(gambatte::GB *g, char const *data); +GBEXPORT int gambatte_getsavedatalength(GB *g); -GBEXPORT int gambatte_getsavedatalength(gambatte::GB *g); +GBEXPORT int gambatte_newstatelen(GB *g); -GBEXPORT int gambatte_newstatelen(gambatte::GB *g); +GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len); -GBEXPORT int gambatte_newstatesave(gambatte::GB *g, char *data, int len); +GBEXPORT int gambatte_newstateload(GB *g, char const *data, int len); -GBEXPORT int gambatte_newstateload(gambatte::GB *g, char const *data, int len); +GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff); -GBEXPORT void gambatte_newstatesave_ex(gambatte::GB *g, gambatte::FPtrs *ff); +GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff); -GBEXPORT void gambatte_newstateload_ex(gambatte::GB *g, gambatte::FPtrs *ff); +GBEXPORT void gambatte_romtitle(GB *g, char *dest); -GBEXPORT void gambatte_romtitle(gambatte::GB *g, char *dest); +GBEXPORT void gambatte_pakinfo(GB *g, char *mbc, unsigned *rambanks, unsigned *rombanks, unsigned *crc, unsigned *headerChecksumOk); -GBEXPORT void gambatte_pakinfo(gambatte::GB *g, char *mbc, unsigned *rambanks, unsigned *rombanks, unsigned *crc, unsigned *headerChecksumOk); +GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length); -GBEXPORT int gambatte_getmemoryarea(gambatte::GB *g, int which, unsigned char **data, int *length); +GBEXPORT unsigned gambatte_savestate(GB *g, unsigned const *videoBuf, int pitch, char *stateBuf); -GBEXPORT unsigned gambatte_savestate(gambatte::GB *g, unsigned const *videoBuf, int pitch, char *stateBuf); +GBEXPORT bool gambatte_loadstate(GB *g, char const *stateBuf, unsigned size); -GBEXPORT bool gambatte_loadstate(gambatte::GB *g, char const *stateBuf, unsigned size); +GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr); -GBEXPORT unsigned char gambatte_cpuread(gambatte::GB *g, unsigned short addr); +GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val); -GBEXPORT void gambatte_cpuwrite(gambatte::GB *g, unsigned short addr, unsigned char val); +GBEXPORT int gambatte_linkstatus(GB *g, int which); -GBEXPORT int gambatte_linkstatus(gambatte::GB *g, int which); +GBEXPORT unsigned gambatte_getbank(GB *g, unsigned type); -GBEXPORT unsigned gambatte_getbank(gambatte::GB *g, unsigned type); +GBEXPORT unsigned gambatte_getaddrbank(GB *g, unsigned short addr); -GBEXPORT unsigned gambatte_getaddrbank(gambatte::GB *g, unsigned short addr); +GBEXPORT void gambatte_setbank(GB *g, unsigned type, unsigned bank); -GBEXPORT void gambatte_setbank(gambatte::GB *g, unsigned type, unsigned bank); +GBEXPORT void gambatte_setaddrbank(GB *g, unsigned short addr, unsigned bank); -GBEXPORT void gambatte_setaddrbank(gambatte::GB *g, unsigned short addr, unsigned bank); +GBEXPORT void gambatte_getregs(GB *g, int *dest); -GBEXPORT void gambatte_getregs(gambatte::GB *g, int *dest); +GBEXPORT void gambatte_setregs(GB *g, int *src); -GBEXPORT void gambatte_setregs(gambatte::GB *g, int *src); +GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs); -GBEXPORT void gambatte_setinterruptaddresses(gambatte::GB *g, int *addrs, int numAddrs); +GBEXPORT int gambatte_gethitinterruptaddress(GB *g); -GBEXPORT int gambatte_gethitinterruptaddress(gambatte::GB *g); +GBEXPORT unsigned long long gambatte_timenow(GB *g); -GBEXPORT unsigned long long gambatte_timenow(gambatte::GB *g); +GBEXPORT void gambatte_settime(GB *g, unsigned long long dividers); -GBEXPORT void gambatte_settime(gambatte::GB *g, unsigned long long dividers); +GBEXPORT int gambatte_getdivstate(GB *g); -GBEXPORT int gambatte_getdivstate(gambatte::GB *g); +GBEXPORT void gambatte_setspeedupflags(GB *g, unsigned flags); -GBEXPORT void gambatte_setspeedupflags(gambatte::GB *g, unsigned flags); +#ifdef __cplusplus } +#endif #endif /* GAMBATTE_C_H */ From d1ae9ec4a2bb31f66ae7b75f2287b607aa8a7004 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 00:09:10 -0400 Subject: [PATCH 18/23] Use dllexport/import properly. dllexport is only meant to be used when building the library. When using the API, dllimport should be used. --- CMakeLists.txt | 1 + libgambatte/include/gambatte-c.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88c4aeb7..c35bf28d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ if(WIN32 AND NOT MINGW AND LIBGAMBATTE_BUILD_STATIC) endif() foreach(_target IN LISTS LIBGAMBATTE_TARGETS) + target_compile_definitions(${_target} PRIVATE "LIBGAMBATTE_DLL_EXPORT") target_compile_options(${_target} PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) target_include_directories(${_target} diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h index 023cf418..94a98d28 100644 --- a/libgambatte/include/gambatte-c.h +++ b/libgambatte/include/gambatte-c.h @@ -44,7 +44,11 @@ using CDCallback = gambatte::CDCallback; #endif #if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBGAMBATTE_DLL_EXPORT) #define GBEXPORT __declspec(dllexport) +#else +#define GBEXPORT __declspec(dllimport) +#endif #elif defined(LIBGAMBATTE_HAVE_ATTRIBUTE_VISIBILITY_DEFAULT) #define GBEXPORT __attribute__((visibility("default"))) #else From fc605a8351ea998ea3762579f02665b679294772 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 00:34:40 -0400 Subject: [PATCH 19/23] Move FPtrs to a C-compatible header. --- libgambatte/include/fptrs.h | 21 +++++++++++++++++++++ libgambatte/include/gambatte-c.h | 4 +++- libgambatte/include/newstate.h | 8 +------- 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 libgambatte/include/fptrs.h diff --git a/libgambatte/include/fptrs.h b/libgambatte/include/fptrs.h new file mode 100644 index 00000000..bb246a74 --- /dev/null +++ b/libgambatte/include/fptrs.h @@ -0,0 +1,21 @@ +#ifndef FPTRS_H +#define FPTRS_H + +#include /* size_t */ + +#ifdef __cplusplus +namespace gambatte { +extern "C" { +#endif +struct FPtrs { + void (*Save_)(void const *ptr, size_t size, char const *name); + void (*Load_)(void *ptr, size_t size, char const *name); + void (*EnterSection_)(char const *name); + void (*ExitSection_)(char const *name); +}; +#ifdef __cplusplus +} +} +#endif + +#endif /* FPTRS_H */ diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h index 94a98d28..c13291b4 100644 --- a/libgambatte/include/gambatte-c.h +++ b/libgambatte/include/gambatte-c.h @@ -27,12 +27,14 @@ #include #include +#include "fptrs.h" + typedef struct GB GB; -typedef struct FPtrs FPtrs; typedef unsigned(InputGetter)(void *); typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); #else +#include "fptrs.h" #include "gambatte.h" #include "newstate.h" diff --git a/libgambatte/include/newstate.h b/libgambatte/include/newstate.h index 2e20c90d..343264d6 100644 --- a/libgambatte/include/newstate.h +++ b/libgambatte/include/newstate.h @@ -4,6 +4,7 @@ #include #include +#include "fptrs.h" namespace gambatte { class NewState { @@ -41,13 +42,6 @@ class NewStateExternalBuffer : public NewState { const long maxlength; }; -struct FPtrs { - void (*Save_)(void const *ptr, std::size_t size, char const *name); - void (*Load_)(void *ptr, std::size_t size, char const *name); - void (*EnterSection_)(char const *name); - void (*ExitSection_)(char const *name); -}; - class NewStateExternalFunctions : public NewState { public: NewStateExternalFunctions(const FPtrs *ff); From 472e68a14873ce86b354b60a3b90570eabadbcd7 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 00:47:38 -0400 Subject: [PATCH 20/23] Only define dllexport/import with the shared lib. --- CMakeLists.txt | 7 +++++++ cmake/libgambatte.pc.in | 2 +- libgambatte/include/gambatte-c.h | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c35bf28d..1553035c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,7 @@ if(LIBGAMBATTE_BUILD_SHARED) add_library(libgambatte-shared SHARED ${LIBGAMBATTE_SOURCES}) add_library(libgambatte::libgambatte-shared ALIAS libgambatte-shared) list(APPEND LIBGAMBATTE_TARGETS libgambatte-shared) + target_compile_definitions(libgambatte-shared PUBLIC "LIBGAMBATTE_SHARED") endif() if(LIBGAMBATTE_BUILD_STATIC) @@ -315,6 +316,12 @@ else() set(LIBGAMBATTE_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") endif() +set(LIBGAMBATTE_PC_CFLAGS) + +if(LIBGAMBATTE_BUILD_SHARED) + set(LIBGAMBATTE_PC_CFLAGS "-DLIBGAMBATTE_SHARED") +endif() + configure_file( "${CMAKE_CURRENT_LIST_DIR}/cmake/libgambatte.pc.in" "${libgambatte_BINARY_DIR}/libgambatte.pc" diff --git a/cmake/libgambatte.pc.in b/cmake/libgambatte.pc.in index 63d30ac1..83f0cfc6 100644 --- a/cmake/libgambatte.pc.in +++ b/cmake/libgambatte.pc.in @@ -6,7 +6,7 @@ includedir=${prefix}/@LIBGAMBATTE_PC_INCLUDEDIR@ Name: libgambatte Description: The gambatte emulator core library Version: @LIBGAMBATTE_REVISION@ -Cflags: -I${includedir}/gambatte +Cflags: -I${includedir}/gambatte @LIBGAMBATTE_PC_CFLAGS@ Libs: -L${libdir} -lgambatte Requires: @LIBGAMBATTE_PC_REQUIRES@ Requires.private: @LIBGAMBATTE_PC_REQUIRES_PRIVATE@ diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h index c13291b4..d3c4e08c 100644 --- a/libgambatte/include/gambatte-c.h +++ b/libgambatte/include/gambatte-c.h @@ -45,6 +45,7 @@ using MemoryCallback = gambatte::MemoryCallback; using CDCallback = gambatte::CDCallback; #endif +#if defined(LIBGAMBATTE_SHARED) #if defined(_WIN32) || defined(__CYGWIN__) #if defined(LIBGAMBATTE_DLL_EXPORT) #define GBEXPORT __declspec(dllexport) @@ -56,6 +57,9 @@ using CDCallback = gambatte::CDCallback; #else #define GBEXPORT #endif +#else /* !defined(LIBGAMBATTE_SHARED) */ +#define GBEXPORT +#endif #ifdef __cplusplus extern "C" { From c75fe837cbcbe7b857fd74783525538a287e8cc0 Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 15:54:55 -0400 Subject: [PATCH 21/23] Add a simple test for the C API. This ensures the header is usable from a C source. --- test/CMakeLists.txt | 16 +++++++++++++++- test/testcapi.c | 14 ++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 test/testcapi.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ba0b9b4c..e4c1651b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,7 +23,6 @@ execute_process( message(STATUS "Done processing assembly files") file(GLOB_RECURSE GB_ASM_BIN "hwtests/**/*.gb") - add_executable(testrunner testrunner.cpp) target_compile_options(testrunner PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) target_include_directories(testrunner PRIVATE ${LIBGAMBATTE_COMMON_PRIVATE_HEADERS}) @@ -39,3 +38,18 @@ add_test( COMMAND testrunner ${GB_ASM_BIN} WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" ) + +enable_language(C) +add_executable(testcapi testcapi.c) +target_compile_options(testcapi PRIVATE ${LIBGAMBATTE_COMPILE_FLAGS}) +target_link_libraries(testcapi PRIVATE libgambatte::libgambatte-static) +set_target_properties(testcapi PROPERTIES + C_STANDARD 99 + C_STANDARD_REQUIRED TRUE + C_EXTENSIONS OFF +) + +add_test( + NAME test_c_api + COMMAND testcapi +) diff --git a/test/testcapi.c b/test/testcapi.c new file mode 100644 index 00000000..5d6a726e --- /dev/null +++ b/test/testcapi.c @@ -0,0 +1,14 @@ +#include "gambatte-c.h" + +#include + +/* Simple test to ensure the C api can be used from C */ + +int main() { + GB *test_gb = gambatte_create(); + if (test_gb == NULL) { + return EXIT_FAILURE; + } + gambatte_destroy(test_gb); + return EXIT_SUCCESS; +} From 0c8ffeda084121c1672da6d666c6b074b22f46ae Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 15:55:18 -0400 Subject: [PATCH 22/23] Typedef FPtrs for when it's used from C. --- libgambatte/include/fptrs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libgambatte/include/fptrs.h b/libgambatte/include/fptrs.h index bb246a74..f841e613 100644 --- a/libgambatte/include/fptrs.h +++ b/libgambatte/include/fptrs.h @@ -7,12 +7,12 @@ namespace gambatte { extern "C" { #endif -struct FPtrs { +typedef struct FPtrs { void (*Save_)(void const *ptr, size_t size, char const *name); void (*Load_)(void *ptr, size_t size, char const *name); void (*EnterSection_)(char const *name); void (*ExitSection_)(char const *name); -}; +} FPtrs; #ifdef __cplusplus } } From 79d984112f60b853ca5c80dc7174871cb6e419dc Mon Sep 17 00:00:00 2001 From: Pierre Wendling Date: Sun, 4 Jun 2023 16:01:05 -0400 Subject: [PATCH 23/23] Define callback functions with C linkage. This is mainly for correctness, but ensures compatibility when __stdcall is the default. --- libgambatte/include/gambatte-c.h | 12 ++++-------- libgambatte/include/gambatte.h | 4 +--- libgambatte/include/gbcallback.h | 19 +++++++++++++++++++ libgambatte/include/inputgetter.h | 17 +++++------------ 4 files changed, 29 insertions(+), 23 deletions(-) create mode 100644 libgambatte/include/gbcallback.h diff --git a/libgambatte/include/gambatte-c.h b/libgambatte/include/gambatte-c.h index d3c4e08c..98728d08 100644 --- a/libgambatte/include/gambatte-c.h +++ b/libgambatte/include/gambatte-c.h @@ -23,20 +23,16 @@ #include "config.h" +#include "fptrs.h" +#include "gbcallback.h" +#include "inputgetter.h" + #ifndef __cplusplus -#include #include -#include "fptrs.h" - typedef struct GB GB; -typedef unsigned(InputGetter)(void *); -typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); -typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); #else -#include "fptrs.h" #include "gambatte.h" -#include "newstate.h" using GB = gambatte::GB; using FPtrs = gambatte::FPtrs; diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 282f32d8..cfefc5d0 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -20,6 +20,7 @@ #define GAMBATTE_H #include "gbint.h" +#include "gbcallback.h" #include "inputgetter.h" #include "loadres.h" #include "pakinfo.h" @@ -32,9 +33,6 @@ namespace gambatte { enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 }; -typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); -typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); - enum eCDLog_AddrType { eCDLog_AddrType_ROM, eCDLog_AddrType_HRAM, diff --git a/libgambatte/include/gbcallback.h b/libgambatte/include/gbcallback.h new file mode 100644 index 00000000..0c01ffa7 --- /dev/null +++ b/libgambatte/include/gbcallback.h @@ -0,0 +1,19 @@ +#ifndef GBCALLBACK_H +#define GBCALLBACK_H + +#include + +#ifdef __cplusplus +namespace gambatte { +extern "C" { +#endif + +typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); +typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); + +#ifdef __cplusplus +} +} +#endif + +#endif /* GBCALLBACK_H */ diff --git a/libgambatte/include/inputgetter.h b/libgambatte/include/inputgetter.h index c6a1ac57..dfb78c43 100755 --- a/libgambatte/include/inputgetter.h +++ b/libgambatte/include/inputgetter.h @@ -19,23 +19,16 @@ #ifndef GAMBATTE_INPUTGETTER_H #define GAMBATTE_INPUTGETTER_H +#ifdef __cplusplus namespace gambatte { - -#if 0 -class InputGetter { -public: - enum Button { A = 0x01, B = 0x02, SELECT = 0x04, START = 0x08, - RIGHT = 0x10, LEFT = 0x20, UP = 0x40, DOWN = 0x80 }; - - virtual ~InputGetter() {} - - /** @return A|B|SELECT|START|RIGHT|LEFT|UP|DOWN if those buttons are pressed. */ - virtual unsigned operator()() = 0; -}; +extern "C" { #endif typedef unsigned (InputGetter)(void *); +#ifdef __cplusplus } +} +#endif #endif