game-music-emu: cmake+android: fix toolchains for inclusion by SDL_mixer

From d5ae855592c01ee2e253073c0afc2d89361e50bf Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Fri, 6 Jan 2023 16:12:17 +0100
Subject: [PATCH] cmake+android: fix toolchains for inclusion by SDL_mixer

---
 Android.mk         |  75 ++++++++++++++++++
 CMakeLists.txt     | 119 +++++++++++-----------------
 gme/CMakeLists.txt | 188 +++++++++++++++++++++++++++++++--------------
 gme/Hes_Cpu.cpp    |   6 +-
 gme/gme.cpp        |   2 +-
 gme/gme.exports    |  60 +++++++++++++++
 6 files changed, 315 insertions(+), 135 deletions(-)
 create mode 100644 Android.mk
 create mode 100644 gme/gme.exports

diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..ae6a6db
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,75 @@
+# Android build of game-music-emu for SDL_mixer
+
+# (Don't forgot to set APP_STL=...)
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libgme
+
+LOCAL_CPP_FEATURES := exceptions
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/gme
+
+LOCAL_CFLAGS := -DVGM_YM2612_NUKED \
+	-DBLARGG_BUILD_DLL \
+	-DLIBGME_VISIBILITY \
+	-fvisibility=hidden
+
+LOCAL_CXXFLAGS := -std=c++11 \
+	-Wno-inconsistent-missing-override
+
+LOCAL_LDFLAGS := -Wl,-no-undefined
+
+LOCAL_SRC_FILES := \
+	gme/Ay_Apu.cpp \
+	gme/Ay_Cpu.cpp \
+	gme/Ay_Emu.cpp \
+	gme/Blip_Buffer.cpp \
+	gme/Classic_Emu.cpp \
+	gme/Data_Reader.cpp \
+	gme/Dual_Resampler.cpp \
+	gme/Effects_Buffer.cpp \
+	gme/Fir_Resampler.cpp \
+	gme/Gb_Apu.cpp \
+	gme/Gb_Cpu.cpp \
+	gme/Gb_Oscs.cpp \
+	gme/Gbs_Emu.cpp \
+	gme/Gme_File.cpp \
+	gme/Gym_Emu.cpp \
+	gme/Hes_Apu.cpp \
+	gme/Hes_Cpu.cpp \
+	gme/Hes_Emu.cpp \
+	gme/Kss_Cpu.cpp \
+	gme/Kss_Emu.cpp \
+	gme/Kss_Scc_Apu.cpp \
+	gme/M3u_Playlist.cpp \
+	gme/Multi_Buffer.cpp \
+	gme/Music_Emu.cpp \
+	gme/Nes_Apu.cpp \
+	gme/Nes_Cpu.cpp \
+	gme/Nes_Fme7_Apu.cpp \
+	gme/Nes_Namco_Apu.cpp \
+	gme/Nes_Oscs.cpp \
+	gme/Nes_Vrc6_Apu.cpp \
+	gme/Nsf_Emu.cpp \
+	gme/Nsfe_Emu.cpp \
+	gme/Sap_Apu.cpp \
+	gme/Sap_Cpu.cpp \
+	gme/Sap_Emu.cpp \
+	gme/Sms_Apu.cpp \
+	gme/Snes_Spc.cpp \
+	gme/Spc_Cpu.cpp \
+	gme/Spc_Dsp.cpp \
+	gme/Spc_Emu.cpp \
+	gme/Spc_Filter.cpp \
+	gme/Vgm_Emu.cpp \
+	gme/Vgm_Emu_Impl.cpp \
+	gme/Ym2413_Emu.cpp \
+	gme/Ym2612_Nuked.cpp \
+	gme/gme.cpp
+
+LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b35210..9710529 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,102 +1,75 @@
+cmake_minimum_required(VERSION 3.0)
 # CMake project definition file.
 project(libgme)
 
-include (CheckCXXCompilerFlag)
+include(CheckCSourceCompiles)
+include(CheckCXXCompilerFlag)
+include(CMakePushCheckState)
+include(GNUInstallDirs)
 
-# When version is changed, also change the one in gme/gme.h to match
-set(GME_VERSION 0.6.3 CACHE INTERNAL "libgme Version")
-
-# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
-# Of course, 2.4 might work, in which case you're welcome to drop
-# down the requirement, but I can't test that.
-cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
-
-# Default emulators to build (all of them! ;)
-if (NOT DEFINED USE_GME_AY)
-    SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_GBS)
-    SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_GYM)
-    SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation")
+file(READ "gme/gme.h" GME_GME_H)
+string(REGEX MATCH "/\\* Game_Music_Emu ([0-9]+)\\.([0-9]+).([0-9]+) \\*/" RE_GME_VERSION "${GME_GME_H}")
+if(NOT RE_GME_VERSION)
+    message(FATAL_ERROR "Unable to extract gme version from gme/gme.h")
 endif()
+set(GME_VERSION_MAJOR "${CMAKE_MATCH_1}")
+set(GME_VERSION_MINOR "${CMAKE_MATCH_2}")
+set(GME_VERSION_PATCH "${CMAKE_MATCH_3}")
+set(GME_VERSION "${GME_VERSION_MAJOR}.${GME_VERSION_MINOR}.${GME_VERSION_PATCH}")
 
-if (NOT DEFINED USE_GME_HES)
-    SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_KSS)
-    SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_NSF)
-    SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_NSFE)
-    SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_SAP)
-    SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation")
-endif()
-
-if (NOT DEFINED USE_GME_SPC)
-    SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation")
-endif()
-
-if (NOT DEFINED GME_SPC_ISOLATED_ECHO_BUFFER)
-    option(GME_SPC_ISOLATED_ECHO_BUFFER "Enable isolated echo buffer on SPC emulator to allow correct playing of \"dodgy\" SPC files made for various ROM hacks ran on ZSNES" OFF)
-endif()
-
-if (NOT DEFINED USE_GME_VGM)
-    SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation")
-endif()
-
-if (NOT DEFINED GME_YM2612_EMU)
-    SET(GME_YM2612_EMU "Nuked" CACHE STRING "Which YM2612 emulator to use: \"Nuked\" (LGPLv2.1+), \"MAME\" (GPLv2+), or \"GENS\" (LGPLv2.1+)")
-endif()
-
-if (USE_GME_NSFE AND NOT USE_GME_NSF)
-    MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
-    SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
+# Default emulators to build (all of them! ;)
+option(USE_GME_AY "Enable support for Spectrum ZX music emulation" ON)
+option(USE_GME_GBS "Enable support for Game Boy music emulation" ON)
+option(USE_GME_GYM "Enable Sega MegaDrive/Genesis music emulation" ON)
+option(USE_GME_HES "Enable PC Engine/TurboGrafx-16 music emulation" ON)
+option(USE_GME_KSS "Enable MSX or other Z80 systems music emulation" ON)
+option(USE_GME_NSF "Enable NES NSF music emulation" ON)
+option(USE_GME_NSFE "Enable NES NSFE and NSF music emulation" ON)
+option(USE_GME_SAP "Enable Atari SAP music emulation" ON)
+option(USE_GME_SPC "Enable SNES SPC music emulation" ON)
+option(USE_GME_VGM "Enable Sega VGM/VGZ music emulation" ON)
+
+option(GME_SPC_ISOLATED_ECHO_BUFFER "Enable isolated echo buffer on SPC emulator to allow correct playing of \"dodgy\" SPC files made for various ROM hacks ran on ZSNES" OFF)
+
+set(GME_YM2612_EMU "Nuked" CACHE STRING "Which YM2612 emulator to use: \"Nuked\" (LGPLv2.1+), \"MAME\" (GPLv2+), or \"GENS\" (LGPLv2.1+)")
+set(GME_YM2612_EMU_CHOICES "Nuked;MAME;GENS")
+set_property(CACHE GME_YM2612_EMU PROPERTY STRINGS "${GME_YM2612_EMU_CHOICES}")
+
+if(USE_GME_NSFE AND NOT USE_GME_NSF)
+    message(STATUS "NSFE support requires NSF, enabling NSF support.")
+    set(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
 endif()
 
 option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
 
-option(ENABLE_UBSAN "Enable Undefined Behavior Sanitizer error-checking" ON)
+option(ENABLE_UBSAN "Enable Undefined Behavior Sanitizer error-checking" OFF)
 
 option(BUILD_FRAMEWORK "Build framework instead of dylib (on macOS)" OFF)
 
 # Check for GCC/Clang "visibility" support.
-if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
-    OR
-    CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
 
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra")
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
 
     # Assume we have visibility support on any compiler that supports C++11
-    add_definitions (-DLIBGME_VISIBILITY)
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden")
+    add_definitions(-DLIBGME_VISIBILITY)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden")
 
     # Try to protect against undefined behavior from signed integer overflow
     # This has caused miscompilation of code already and there are other
     # potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/
-    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwrapv")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwrapv")
 
-    if (ENABLE_UBSAN)
+    if(ENABLE_UBSAN)
         # GCC needs -static-libubsan
-        if (NOT BUILD_SHARED_LIBS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-            set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -static-libubsan")
+        if(NOT BUILD_SHARED_LIBS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -static-libubsan")
         else()
-            set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
+            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
         endif()
     endif()
-endif ()
+endif()
 
 # Shared library defined here
 add_subdirectory(gme)
diff --git a/gme/CMakeLists.txt b/gme/CMakeLists.txt
index b1b2bf0..cc09f5b 100644
--- a/gme/CMakeLists.txt
+++ b/gme/CMakeLists.txt
@@ -18,58 +18,65 @@ set(libgme_SRCS Blip_Buffer.cpp
 # libraries.  Ensure CMake looks only for static libs if we're doing a static
 # build.  See https://stackoverflow.com/a/44738756
 if(NOT BUILD_SHARED_LIBS)
-    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+    if(MSVC)
+        set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
+    else()
+        set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+    endif()
 endif()
 
-find_package(ZLIB QUIET)
+option(GME_ZLIB "GME supports compressed sound formats" ON)
+if(GME_ZLIB)
+    find_package(ZLIB REQUIRED)
+endif()
 
 # Ay_Apu is very popular around here
-if (USE_GME_AY OR USE_GME_KSS)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_AY OR USE_GME_KSS)
+    list(APPEND libgme_SRCS
                 Ay_Apu.cpp
         )
 endif()
 
 # so is Ym2612_Emu
-if (USE_GME_VGM OR USE_GME_GYM)
+if(USE_GME_VGM OR USE_GME_GYM)
     if(GME_YM2612_EMU STREQUAL "Nuked")
         add_definitions(-DVGM_YM2612_NUKED)
-        set(libgme_SRCS ${libgme_SRCS}
+        list(APPEND libgme_SRCS
                     Ym2612_Nuked.cpp
             )
-        message("VGM/GYM: Nuked OPN2 emulator will be used")
+        message(STATUS "VGM/GYM: Nuked OPN2 emulator will be used")
     elseif(GME_YM2612_EMU STREQUAL "MAME")
         add_definitions(-DVGM_YM2612_MAME)
-        set(libgme_SRCS ${libgme_SRCS}
+        list(APPEND libgme_SRCS
                     Ym2612_MAME.cpp
             )
-        message("VGM/GYM: MAME YM2612 emulator will be used")
+        message(STATUS "VGM/GYM: MAME YM2612 emulator will be used")
     else()
         add_definitions(-DVGM_YM2612_GENS)
-        set(libgme_SRCS ${libgme_SRCS}
+        list(APPEND libgme_SRCS
                     Ym2612_GENS.cpp
             )
-        message("VGM/GYM: GENS 2.10 emulator will be used")
+        message(STATUS "VGM/GYM: GENS 2.10 emulator will be used")
     endif()
 endif()
 
 # But none are as popular as Sms_Apu
-if (USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS)
+    list(APPEND libgme_SRCS
                 Sms_Apu.cpp
         )
 endif()
 
-if (USE_GME_AY)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_AY)
+    list(APPEND libgme_SRCS
               # Ay_Apu.cpp included earlier
                 Ay_Cpu.cpp
                 Ay_Emu.cpp
         )
 endif()
 
-if (USE_GME_GBS)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_GBS)
+    list(APPEND libgme_SRCS
                 Gb_Apu.cpp
                 Gb_Cpu.cpp
                 Gb_Oscs.cpp
@@ -77,24 +84,24 @@ if (USE_GME_GBS)
         )
 endif()
 
-if (USE_GME_GYM)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_GYM)
+    list(APPEND libgme_SRCS
               # Sms_Apu.cpp included earlier
               # Ym2612_Emu.cpp included earlier
                 Gym_Emu.cpp
         )
 endif()
 
-if (USE_GME_HES)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_HES)
+    list(APPEND libgme_SRCS
                 Hes_Apu.cpp
                 Hes_Cpu.cpp
                 Hes_Emu.cpp
         )
 endif()
 
-if (USE_GME_KSS)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_KSS)
+    list(APPEND libgme_SRCS
               # Ay_Apu.cpp included earlier
               # Sms_Apu.cpp included earlier
                 Kss_Cpu.cpp
@@ -103,8 +110,8 @@ if (USE_GME_KSS)
         )
 endif()
 
-if (USE_GME_NSF OR USE_GME_NSFE)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_NSF OR USE_GME_NSFE)
+    list(APPEND libgme_SRCS
                 Nes_Apu.cpp
                 Nes_Cpu.cpp
                 Nes_Fme7_Apu.cpp
@@ -115,35 +122,35 @@ if (USE_GME_NSF OR USE_GME_NSFE)
         )
 endif()
 
-if (USE_GME_NSFE)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_NSFE)
+    list(APPEND libgme_SRCS
                 Nsfe_Emu.cpp
         )
 endif()
 
-if (USE_GME_SAP)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_SAP)
+    list(APPEND libgme_SRCS
                 Sap_Apu.cpp
                 Sap_Cpu.cpp
                 Sap_Emu.cpp
         )
 endif()
 
-if (USE_GME_SPC)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_SPC)
+    list(APPEND libgme_SRCS
                 Snes_Spc.cpp
                 Spc_Cpu.cpp
                 Spc_Dsp.cpp
                 Spc_Emu.cpp
                 Spc_Filter.cpp
         )
-    if (GME_SPC_ISOLATED_ECHO_BUFFER)
+    if(GME_SPC_ISOLATED_ECHO_BUFFER)
         add_definitions(-DSPC_ISOLATED_ECHO_BUFFER)
     endif()
 endif()
 
-if (USE_GME_VGM)
-    set(libgme_SRCS ${libgme_SRCS}
+if(USE_GME_VGM)
+    list(APPEND libgme_SRCS
               # Sms_Apu.cpp included earlier
               # Ym2612_Emu.cpp included earlier
                 Vgm_Emu.cpp
@@ -157,31 +164,98 @@ set (EXPORTED_HEADERS gme.h blargg_source.h)
 
 # while building a macOS framework, exported headers must be in the source
 # list, or the header files aren't copied to the bundle.
-if (BUILD_FRAMEWORK)
+if(BUILD_FRAMEWORK)
     set(libgme_SRCS ${libgme_SRCS} ${EXPORTED_HEADERS})
 endif()
 
+# Add library to be compiled.
+add_library(gme ${libgme_SRCS})
+add_library(gme::gme ALIAS gme)
+target_include_directories(gme
+    PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"  # For gme_types.h
+    INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>"
+    INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+)
+
 # On some platforms we may need to change headers or whatnot based on whether
 # we're building the library or merely using the library. The following is
 # only defined when building the library to allow us to tell which is which.
-add_definitions(-DBLARGG_BUILD_DLL)
+set_property(TARGET gme PROPERTY DEFINE_SYMBOL BLARGG_BUILD_DLL)
 
-# For the gme_types.h
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
+if(GME_ZLIB)
+    message(STATUS "ZLib library located, compressed file formats will be supported")
+    target_compile_definitions(gme PRIVATE HAVE_ZLIB_H)
+    target_link_libraries(gme ZLIB::ZLIB)
+    # Is not to be installed though
+else()
+    message(STATUS "ZLib library not found, disabling support for compressed formats such as VGZ")
+endif()
 
-# Add library to be compiled.
-add_library(gme ${libgme_SRCS})
+if(NOT MSVC)
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_FLAGS "-Wl,-no-undefined")
+    check_c_source_compiles("int main() { return 0;}" COMPILER_SUPPORTS_WL_NO_UNDEFINED)
+    cmake_pop_check_state()
+    if(COMPILER_SUPPORTS_WL_NO_UNDEFINED)
+        set_property(TARGET gme APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-no-undefined")
+    endif()
+endif()
 
-if(ZLIB_FOUND)
-    message(" ** ZLib library located, compressed file formats will be supported")
-    target_compile_definitions(gme PRIVATE -DHAVE_ZLIB_H)
-    target_include_directories(gme PRIVATE ${ZLIB_INCLUDE_DIRS})
-    target_link_libraries(gme ${ZLIB_LIBRARIES})
-    # Is not to be installed though
+# Extract symbols from gme.exports
+file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/gme.exports" gme_exports_strings)
+set(gme_exports)
+foreach(gme_export_string ${gme_exports_strings})
+    string(STRIP "${gme_export_string}" gme_export_string)
+    string(SUBSTRING "${gme_export_string}" 0 1 gme_export_first_char)
+    if(gme_export_string AND NOT gme_export_first_char STREQUAL "#")
+        list(APPEND gme_exports ${gme_export_string})
+    endif()
+endforeach()
 
-    set(PKG_CONFIG_ZLIB -lz) # evaluated in libgme.pc.in
-else()
-    message("ZLib library not found, disabling support for compressed formats such as VGZ")
+# Add a version script to hide the c++ STL
+if(APPLE)
+    set(gme_syms "")
+    foreach(gme_export ${gme_exports})
+        set(gme_syms "${gme_syms}_${gme_export}\n")
+    endforeach()
+
+    # Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs
+    set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym")
+    set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym")
+    file(WRITE "${temporary_sym_file}" "${gme_syms}")
+    execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}")
+
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_FLAGS "-Wl,-exported_symbols_list,'${CMAKE_CURRENT_SOURCE_DIR}/gme.exports'")
+    check_c_source_compiles("int main() { return 0;}" COMPILER_SUPPORTS_EXPORTED_SYMBOLS_LIST)
+    cmake_pop_check_state()
+
+    if(COMPILER_SUPPORTS_EXPORTED_SYMBOLS_LIST)
+        set_property(TARGET gme APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-exported_symbols_list,'${generated_sym_file}'")
+        set_property(TARGET gme APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}")
+    endif()
+elseif(UNIX)
+    set(gme_syms "{\n  global:\n")
+    foreach(gme_export ${gme_exports})
+        set(gme_syms "${gme_syms}    ${gme_export};\n")
+    endforeach()
+    set(gme_syms "${gme_syms}  local: *;\n};\n")
+
+    # Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs
+    set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym")
+    set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym")
+    file(WRITE "${temporary_sym_file}" "${gme_syms}")
+    execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}")
+
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_FLAGS "-Wl,-version-script='${generated_sym_file}'")
+    check_c_source_compiles("int main() { return 0;}" COMPILER_SUPPORTS_VERSION_SCRIPT)
+    cmake_pop_check_state()
+
+    if(COMPILER_SUPPORTS_VERSION_SCRIPT)
+        set_property(TARGET gme APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-version-script='${generated_sym_file}'")
+        set_property(TARGET gme APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}")
+    endif()
 endif()
 
 # The version is the release.  The "soversion" is the API version.  As long
@@ -204,18 +278,16 @@ if(BUILD_FRAMEWORK)
                    PUBLIC_HEADER "${EXPORTED_HEADERS}")
 endif()
 
-install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
-                    RUNTIME DESTINATION bin  # DLL platforms
-                    ARCHIVE DESTINATION lib # DLL platforms
-                    FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework
+install(TARGETS gme LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+                    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}  # DLL platforms
+                    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}  # DLL platforms
+                    FRAMEWORK DESTINATION /Library/Frameworks)   # macOS framework
 
 
 # Run during cmake phase, so this is available during make
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in
-    ${CMAKE_CURRENT_BINARY_DIR}/gme_types.h)
+configure_file(gme_types.h.in gme_types.h @ONLY)
 
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in
-    ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY)
+configure_file(libgme.pc.in libgme.pc @ONLY)
 
 install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
diff --git a/gme/Hes_Cpu.cpp b/gme/Hes_Cpu.cpp
index 569b73b..7827a2b 100644
--- a/gme/Hes_Cpu.cpp
+++ b/gme/Hes_Cpu.cpp
@@ -875,7 +875,7 @@ bool Hes_Cpu::run( hes_time_t end_time )
 	case 0xD6: // DEC zp,x
 		data = uint8_t (data + x);/*FALLTHRU*/
 	case 0xC6: // DEC zp
-		nz = (unsigned) -1;
+		nz = (uint_fast16_t) -1;
 	add_nz_zp:
 		nz += READ_LOW( data );
 	write_nz_zp:
@@ -900,7 +900,7 @@ bool Hes_Cpu::run( hes_time_t end_time )
 	case 0xCE: // DEC abs
 		data = GET_ADDR();
 	dec_ptr:
-		nz = (unsigned) -1;
+		nz = (uint_fast16_t) -1;
 	inc_common:
 		FLUSH_TIME();
 		nz += READ( data );
@@ -1037,7 +1037,7 @@ bool Hes_Cpu::run( hes_time_t end_time )
 // Flags
 
 	case 0x38: // SEC
-		c = (unsigned) ~0;
+		c = (uint_fast16_t) ~0;
 		goto loop;
 
 	case 0x18: // CLC
diff --git a/gme/gme.cpp b/gme/gme.cpp
index 25f378d..9d81b89 100644
--- a/gme/gme.cpp
+++ b/gme/gme.cpp
@@ -2,7 +2,7 @@
 
 #include "Music_Emu.h"
 
-#include "gme_types.h"
+#include <gme_types.h>
 #if !GME_DISABLE_STEREO_DEPTH
 #include "Effects_Buffer.h"
 #endif
diff --git a/gme/gme.exports b/gme/gme.exports
new file mode 100644
index 0000000..f2e2b4d
--- /dev/null
+++ b/gme/gme.exports
@@ -0,0 +1,60 @@
+# List of all exported symbols
+gme_autoload_playback_limit
+gme_ay_type
+gme_clear_playlist
+gme_delete
+gme_enable_accuracy
+gme_equalizer
+gme_free_info
+gme_gbs_type
+gme_gym_type
+gme_hes_type
+gme_identify_extension
+gme_identify_file
+gme_identify_header
+gme_ignore_silence
+gme_kss_type
+gme_load_custom
+gme_load_data
+gme_load_file
+gme_load_m3u
+gme_load_m3u_data
+gme_multi_channel
+gme_mute_voice
+gme_mute_voices
+gme_new_emu
+gme_new_emu_multi_channel
+gme_nsf_type
+gme_nsfe_type
+gme_open_data
+gme_open_file
+gme_play
+gme_sap_type
+gme_seek
+gme_seek_samples
+gme_set_autoload_playback_limit
+gme_set_equalizer
+gme_set_fade
+gme_set_stereo_depth
+gme_set_tempo
+gme_set_user_cleanup
+gme_set_user_data
+gme_spc_type
+gme_start_track
+gme_tell
+gme_tell_samples
+gme_track_count
+gme_track_ended
+gme_track_info
+gme_type
+gme_type_extension
+gme_type_list
+gme_type_multitrack
+gme_type_system
+gme_user_data
+gme_vgm_type
+gme_vgz_type
+gme_voice_count
+gme_voice_name
+gme_warning
+gme_wrong_file_type