Update to spice2x-25-04-25 (pre-apply)

> broken commit
This commit is contained in:
[ ]
2025-05-07 00:25:31 +09:00
parent 04dee88276
commit c94de456b5
543 changed files with 78491 additions and 91698 deletions
+1
View File
@@ -2,3 +2,4 @@ bin/**
dist/** dist/**
docker/** docker/**
cmake-build* cmake-build*
.ccache/**
+2
View File
@@ -16,3 +16,5 @@ bin/*
dist/* dist/*
external/cv2pdb/* external/cv2pdb/*
.ccache/*
+158 -67
View File
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.12)
cmake_policy(SET CMP0069 NEW) cmake_policy(SET CMP0069 NEW)
project(spicetools) project(spicetools)
include(CheckIPOSupported) include(CheckIPOSupported)
@@ -14,7 +14,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
add_compile_definitions(RAPIDJSON_HAS_STDSTRING) add_compile_definitions(RAPIDJSON_HAS_STDSTRING)
if(MSVC) if(MSVC)
# disable intermediate manifest # disable intermediate manifest
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
@@ -34,6 +33,9 @@ if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DYNAMICBASE:NO") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DYNAMICBASE:NO")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DYNAMICBASE:NO") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DYNAMICBASE:NO")
set(USE_STATIC_MSVCRT ON CACHE BOOL "If enabled, will force the use of static crt instead of dynamic")
if(USE_STATIC_MSVCRT)
# use statically linked runtime # use statically linked runtime
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(CompilerFlags set(CompilerFlags
@@ -44,17 +46,40 @@ if(MSVC)
CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELEASE
) )
foreach(CompilerFlag ${CompilerFlags}) foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
endforeach() endforeach()
# enable PDB generation with Release builds
if(CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
endif() endif()
# disable C4996 "The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name" warning
add_compile_options("/wd4996")
# cleanup windows.h includes
add_compile_options("/DNOMINMAX")
# cleanup weird types
add_compile_options("/DWINBOOL=BOOL")
# add support for the deprecated std::result_of in c++20
add_compile_options("/D_HAS_DEPRECATED_RESULT_OF")
# enable build paralellization
add_compile_options("/MP")
# enable edit and continue in Debug
add_compile_options("$<$<CONFIG:DEBUG>:/ZI>")
add_link_options("$<$<CONFIG:DEBUG>:/SAFESEH:NO>")
# enable fast pdb generation in debug
add_link_options("$<$<CONFIG:DEBUG>:/DEBUG:FASTLINK>")
# enable pdb generation for release builds
add_compile_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/Zi>")
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/DEBUG:FULL>")
# enable COMDAT folding for even smaller release builds
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/OPT:ICF>")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# disable warnings about using non _s variants like strncpy # disable warnings about using non _s variants like strncpy
add_compile_definitions(_CRT_SECURE_NO_WARNINGS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
@@ -94,8 +119,13 @@ else()
# hide ident strings # hide ident strings
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-ident -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections") # a change in the linker caused the executable to be loaded above 4GB base virtual address
# https://github.com/msys2/MINGW-packages/pull/6880
# some games crash if some DLLS load above 4GB VA, so manually set base address to standard 32-bit VA,
# and might as well double make sure ASLR is disabled here
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
# set visibility to hidden # set visibility to hidden
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fvisibility=hidden")
@@ -106,8 +136,8 @@ else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
# performance # performance
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -pipe") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -pipe")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -pipe") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -pipe")
# ensure frame pointers are enabled # ensure frame pointers are enabled
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-omit-frame-pointer") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-omit-frame-pointer")
@@ -128,8 +158,10 @@ else()
# hide ident strings # hide ident strings
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-fno-ident -ffunction-sections -fdata-sections")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections") # linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections,--disable-dynamicbase,--image-base=0x400000")
# set visibility to hidden # set visibility to hidden
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
@@ -151,6 +183,10 @@ else()
set(CMAKE_C_FLAGS_DEBUG "-gdwarf") set(CMAKE_C_FLAGS_DEBUG "-gdwarf")
set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf") set(CMAKE_CXX_FLAGS_DEBUG "-gdwarf")
# linker fix to load below 4GB
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--disable-dynamicbase,--image-base=0x400000")
# enable debug symbols on level 3 and keep frame pointers # enable debug symbols on level 3 and keep frame pointers
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3 -fno-omit-frame-pointer")
@@ -169,14 +205,13 @@ endif()
add_compile_definitions( add_compile_definitions(
WIN32_LEAN_AND_MEAN WIN32_LEAN_AND_MEAN
_WIN32_IE=0x0400 _WIN32_IE=0x0400
OPENVR_BUILD_STATIC
) )
# acioemu log # acioemu log
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG")
# add project directory to include path so we can comfortably import # add project directory to include path so we can comfortably import
include_directories(${spicetools_SOURCE_DIR}) include_directories(${spicetools_SOURCE_DIR} ${spicetools_SOURCE_DIR}/external/imgui)
# add external libraries # add external libraries
add_subdirectory(external/fmt EXCLUDE_FROM_ALL) add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
@@ -184,18 +219,16 @@ add_subdirectory(external/discord-rpc EXCLUDE_FROM_ALL)
add_subdirectory(external/hash-library EXCLUDE_FROM_ALL) add_subdirectory(external/hash-library EXCLUDE_FROM_ALL)
add_subdirectory(external/imgui EXCLUDE_FROM_ALL) add_subdirectory(external/imgui EXCLUDE_FROM_ALL)
add_subdirectory(external/minhook EXCLUDE_FROM_ALL) add_subdirectory(external/minhook EXCLUDE_FROM_ALL)
add_subdirectory(external/openvr EXCLUDE_FROM_ALL) add_subdirectory(external/cpu_features EXCLUDE_FROM_ALL)
add_subdirectory(external/lua EXCLUDE_FROM_ALL)
# set link time optimizations (disabled for Debug builds for speed, disabled # set link time optimizations (disabled for Debug builds for speed, disabled
# for RelWithDebInfo builds due to "lto1: error: two or more sections for" # for RelWithDebInfo builds due to "lto1: error: two or more sections for"
# errors) # errors)
check_ipo_supported() check_ipo_supported()
if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" OR CMAKE_BUILD_TYPE MATCHES "Debug") set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)
else() set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
endif()
# resources # resources
########### ###########
@@ -266,6 +299,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
api/serial.cpp api/serial.cpp
api/modules/drs.cpp api/modules/drs.cpp
api/modules/lcd.cpp api/modules/lcd.cpp
api/modules/ddr.cpp
api/modules/resize.cpp
# avs # avs
avs/core.cpp avs/core.cpp
@@ -297,17 +332,6 @@ set(SOURCE_FILES ${SOURCE_FILES}
# external asio # external asio
external/asio/asiolist.cpp external/asio/asiolist.cpp
# external layeredfs
external/layeredfs/config.cpp
external/layeredfs/hook.cpp
external/layeredfs/modpath_handler.cpp
external/layeredfs/texture_packer.cpp
external/layeredfs/utils.cpp
external/layeredfs/3rd_party/GuillotineBinPack.cpp
external/layeredfs/3rd_party/lodepng.cpp
external/layeredfs/3rd_party/Rect.cpp
external/layeredfs/3rd_party/stb_dxt.cpp
# external cardio # external cardio
external/cardio/cardio_hid.cpp external/cardio/cardio_hid.cpp
external/cardio/cardio_window.cpp external/cardio/cardio_window.cpp
@@ -335,10 +359,15 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/hpm/io.cpp games/hpm/io.cpp
games/iidx/iidx.cpp games/iidx/iidx.cpp
games/iidx/io.cpp games/iidx/io.cpp
games/iidx/poke.cpp
games/iidx/bi2a.cpp games/iidx/bi2a.cpp
games/iidx/bi2x.cpp games/iidx/bi2x.cpp
games/iidx/bi2x_hook.cpp games/iidx/bi2x_hook.cpp
games/iidx/ezusb.cpp games/iidx/ezusb.cpp
games/iidx/legacy_camera.cpp
games/iidx/local_camera.cpp
games/iidx/camera.cpp
games/iidx/mf_wrappers.cpp
games/sdvx/bi2x_hook.cpp games/sdvx/bi2x_hook.cpp
games/sdvx/sdvx.cpp games/sdvx/sdvx.cpp
games/sdvx/io.cpp games/sdvx/io.cpp
@@ -347,6 +376,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/jb/io.cpp games/jb/io.cpp
games/nost/nost.cpp games/nost/nost.cpp
games/nost/io.cpp games/nost/io.cpp
games/nost/poke.cpp
games/gitadora/gitadora.cpp games/gitadora/gitadora.cpp
games/gitadora/io.cpp games/gitadora/io.cpp
games/mga/mga.cpp games/mga/mga.cpp
@@ -362,6 +392,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/rf3d/rf3d.cpp games/rf3d/rf3d.cpp
games/rf3d/io.cpp games/rf3d/io.cpp
games/museca/io.cpp games/museca/io.cpp
games/museca/museca.cpp
games/dea/dea.cpp games/dea/dea.cpp
games/dea/io.cpp games/dea/io.cpp
games/qma/qma.cpp games/qma/qma.cpp
@@ -372,6 +403,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/ddr/p3io/p3io.cpp games/ddr/p3io/p3io.cpp
games/ddr/p3io/sate.cpp games/ddr/p3io/sate.cpp
games/ddr/p3io/usbmem.cpp games/ddr/p3io/usbmem.cpp
games/ddr/p4io/p4io.cpp
games/ddr/p4io/p4io.h
games/mfc/mfc.cpp games/mfc/mfc.cpp
games/mfc/io.cpp games/mfc/io.cpp
games/ftt/ftt.cpp games/ftt/ftt.cpp
@@ -400,12 +433,23 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/onpara/touchpanel.cpp games/onpara/touchpanel.cpp
games/bc/bc.cpp games/bc/bc.cpp
games/bc/io.cpp games/bc/io.cpp
games/ccj/ccj.cpp
games/ccj/io.cpp
games/ccj/bi2x_hook.cpp
games/ccj/trackball.cpp
games/qks/qks.cpp
games/qks/io.cpp
games/qks/bi2x_hook.cpp
games/mfg/mfg.cpp
games/mfg/io.cpp
games/mfg/bi2a_hook.cpp
# hooks # hooks
hooks/audio/audio.cpp hooks/audio/audio.cpp
hooks/audio/buffer.cpp hooks/audio/buffer.cpp
hooks/audio/util.cpp hooks/audio/util.cpp
hooks/audio/backends/dsound/dsound_backend.cpp hooks/audio/backends/dsound/dsound_backend.cpp
hooks/audio/backends/mmdevice/audio_endpoint_volume.cpp
hooks/audio/backends/mmdevice/device.cpp hooks/audio/backends/mmdevice/device.cpp
hooks/audio/backends/mmdevice/device_enumerator.cpp hooks/audio/backends/mmdevice/device_enumerator.cpp
hooks/audio/backends/wasapi/audio_client.cpp hooks/audio/backends/wasapi/audio_client.cpp
@@ -414,6 +458,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
hooks/audio/backends/wasapi/dummy_audio_clock.cpp hooks/audio/backends/wasapi/dummy_audio_clock.cpp
hooks/audio/backends/wasapi/dummy_audio_render_client.cpp hooks/audio/backends/wasapi/dummy_audio_render_client.cpp
hooks/audio/backends/wasapi/dummy_audio_session_control.cpp hooks/audio/backends/wasapi/dummy_audio_session_control.cpp
hooks/audio/backends/wasapi/low_latency_client.cpp
hooks/audio/backends/wasapi/util.cpp hooks/audio/backends/wasapi/util.cpp
hooks/audio/implementations/asio.cpp hooks/audio/implementations/asio.cpp
hooks/audio/implementations/wave_out.cpp hooks/audio/implementations/wave_out.cpp
@@ -423,6 +468,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
hooks/devicehook.cpp hooks/devicehook.cpp
hooks/graphics/graphics.cpp hooks/graphics/graphics.cpp
hooks/graphics/graphics_windowed.cpp hooks/graphics/graphics_windowed.cpp
hooks/graphics/nvapi_hook.cpp
hooks/graphics/nvenc_hook.cpp
hooks/graphics/backends/d3d9/d3d9_backend.cpp hooks/graphics/backends/d3d9/d3d9_backend.cpp
hooks/graphics/backends/d3d9/d3d9_device.cpp hooks/graphics/backends/d3d9/d3d9_device.cpp
hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp
@@ -439,6 +486,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
hooks/setupapihook.cpp hooks/setupapihook.cpp
hooks/sleephook.cpp hooks/sleephook.cpp
hooks/unisintrhook.cpp hooks/unisintrhook.cpp
hooks/winuser.cpp
# launcher # launcher
launcher/launcher.cpp launcher/launcher.cpp
@@ -457,18 +505,21 @@ set(SOURCE_FILES ${SOURCE_FILES}
misc/extdev.cpp misc/extdev.cpp
misc/sciunit.cpp misc/sciunit.cpp
misc/sde.cpp misc/sde.cpp
misc/vrutil.cpp
misc/wintouchemu.cpp misc/wintouchemu.cpp
# nvapi
nvapi/nvapi.cpp
# overlay # overlay
overlay/overlay.cpp overlay/overlay.cpp
overlay/window.cpp overlay/window.cpp
overlay/imgui/extensions.cpp overlay/imgui/extensions.cpp
overlay/imgui/impl_dx9.cpp
overlay/imgui/impl_spice.cpp overlay/imgui/impl_spice.cpp
overlay/imgui/impl_sw.cpp overlay/imgui/impl_sw.cpp
overlay/windows/acio_status_buffers.cpp overlay/windows/acio_status_buffers.cpp
overlay/windows/camera_control.cpp
overlay/windows/card_manager.cpp overlay/windows/card_manager.cpp
overlay/windows/drs_dancefloor.cpp
overlay/windows/screen_resize.cpp overlay/windows/screen_resize.cpp
overlay/windows/sdvx_sub.cpp overlay/windows/sdvx_sub.cpp
overlay/windows/config.cpp overlay/windows/config.cpp
@@ -483,11 +534,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
overlay/windows/iopanel_gfdm.cpp overlay/windows/iopanel_gfdm.cpp
overlay/windows/iopanel_iidx.cpp overlay/windows/iopanel_iidx.cpp
overlay/windows/keypad.cpp overlay/windows/keypad.cpp
overlay/windows/kfcontrol.cpp
overlay/windows/log.cpp overlay/windows/log.cpp
overlay/windows/midi.cpp overlay/windows/midi.cpp
overlay/windows/patch_manager.cpp overlay/windows/patch_manager.cpp
overlay/windows/vr.cpp
overlay/windows/wnd_manager.cpp overlay/windows/wnd_manager.cpp
# rawinput # rawinput
@@ -496,6 +545,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
rawinput/piuio.cpp rawinput/piuio.cpp
rawinput/touch.cpp rawinput/touch.cpp
rawinput/hotplug.cpp rawinput/hotplug.cpp
rawinput/smx.cpp
rawinput/smx.h
rawinput/smxstage.cpp
rawinput/smxstage.h
rawinput/smxdedicab.cpp
rawinput/smxdedicab.h
# reader # reader
reader/reader.cpp reader/reader.cpp
@@ -503,30 +558,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
reader/structuredmessage.cpp reader/structuredmessage.cpp
reader/crypt.cpp reader/crypt.cpp
# script
script/api/analogs.cpp
script/api/buttons.cpp
script/api/capture.cpp
script/api/card.cpp
script/api/coin.cpp
script/api/control.cpp
script/api/drs.cpp
script/api/iidx.cpp
script/api/info.cpp
script/api/keypads.cpp
script/api/lcd.cpp
script/api/lights.cpp
script/api/memory.cpp
script/api/touch.cpp
script/instance.cpp
script/lib.cpp
script/manager.cpp
# stubs # stubs
stubs/stubs.cpp stubs/stubs.cpp
# touch # touch
touch/touch.cpp touch/touch.cpp
touch/touch_indicators.cpp
touch/win7.cpp touch/win7.cpp
touch/win8.cpp touch/win8.cpp
@@ -539,6 +576,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
util/libutils.cpp util/libutils.cpp
util/fileutils.cpp util/fileutils.cpp
util/resutils.cpp util/resutils.cpp
util/unity_player.cpp
util/utils.cpp util/utils.cpp
util/memutils.cpp util/memutils.cpp
util/rc4.cpp util/rc4.cpp
@@ -546,17 +584,22 @@ set(SOURCE_FILES ${SOURCE_FILES}
util/time.cpp util/time.cpp
util/cpuutils.cpp util/cpuutils.cpp
util/netutils.cpp util/netutils.cpp
util/sysutils.cpp
util/lz77.cpp util/lz77.cpp
util/tapeled.cpp
util/execexe.cpp
) )
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "Source Files" FILES ${SOURCE_FILES})
# spice.exe # spice.exe
########### ###########
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice target_link_libraries(spicetools_spice
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice PROPERTIES PREFIX "") set_target_properties(spicetools_spice PROPERTIES PREFIX "")
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice") set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
@@ -564,14 +607,33 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
endif() endif()
# spice_laa.exe
###########
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice_laa ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice_laa
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice_laa PROPERTIES PREFIX "")
set_target_properties(spicetools_spice_laa PROPERTIES OUTPUT_NAME "spice_laa")
target_compile_definitions(spicetools_spice_laa PRIVATE SPICE32_LARGE_ADDRESS_AWARE=1)
IF(NOT MSVC)
set_target_properties(spicetools_spice_laa PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32 -Wl,--large-address-aware")
endif()
# spice64.exe # spice64.exe
############# #############
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice64 ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_spice64 ${SOURCE_FILES} ${RESOURCE_FILES})
# do NOT link against: mf, mfplat, mfreadwrite; otherwise unity games will break
target_link_libraries(spicetools_spice64 target_link_libraries(spicetools_spice64
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp mfuuid strmiids dxva2
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api64 lua_static imm32 dwmapi) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "") set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64") set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1) target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
@@ -580,7 +642,6 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64") set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif() endif()
# spicecfg.exe # spicecfg.exe
############## ##############
@@ -588,8 +649,8 @@ set(SOURCE_FILES ${SOURCE_FILES} launcher/options.h launcher/options.cpp)
set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc) set(RESOURCE_FILES cfg/manifest.manifest cfg/manifest.rc cfg/icon.rc cfg/Win32D.rc)
add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES}) add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_cfg target_link_libraries(spicetools_cfg
PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp strmiids
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi) PRIVATE fmt-header-only discord-rpc imgui hash-library minhook imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_cfg PROPERTIES PREFIX "") set_target_properties(spicetools_cfg PROPERTIES PREFIX "")
set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg") set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg")
target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1) target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
@@ -647,6 +708,36 @@ if(NOT MSVC)
set_target_properties(spicetools_stubs_kld64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64") set_target_properties(spicetools_stubs_kld64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif() endif()
# nvcuda.dll
set(SOURCE_FILES stubs/nvcuda.cpp)
add_library(spicetools_stubs_nvcuda SHARED ${SOURCE_FILES} stubs/nvcuda.def)
set_target_properties(spicetools_stubs_nvcuda PROPERTIES PREFIX "")
set_target_properties(spicetools_stubs_nvcuda PROPERTIES OUTPUT_NAME "nvcuda")
if(NOT MSVC)
set_target_properties(spicetools_stubs_nvcuda PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# nvcuvid.dll
set(SOURCE_FILES stubs/nvcuvid.cpp)
add_library(spicetools_stubs_nvcuvid SHARED ${SOURCE_FILES} stubs/nvcuvid.def)
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES PREFIX "")
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES OUTPUT_NAME "nvcuvid")
if(NOT MSVC)
set_target_properties(spicetools_stubs_nvcuvid PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# nvEncodeAPI64.dll
set(SOURCE_FILES stubs/nvEncodeAPI64.cpp)
add_library(spicetools_stubs_nvEncodeAPI64 SHARED ${SOURCE_FILES} stubs/nvEncodeAPI64.def)
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES PREFIX "")
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES OUTPUT_NAME "nvEncodeAPI64")
if(NOT MSVC)
set_target_properties(spicetools_stubs_nvEncodeAPI64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
# output directories # output directories
#################### ####################
@@ -656,14 +747,14 @@ set_target_properties(spicetools_cfg
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools") RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
# output 32bit # output 32bit
set_target_properties(spicetools_spice spicetools_stubs_kbt spicetools_stubs_kld set_target_properties(spicetools_spice spicetools_spice_laa spicetools_stubs_kbt spicetools_stubs_kld
PROPERTIES PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32") RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
# output 64bit # output 64bit
set_target_properties(spicetools_spice64 spicetools_stubs_kbt64 spicetools_stubs_kld64 set_target_properties(spicetools_spice64 spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvcuda spicetools_stubs_nvcuvid spicetools_stubs_nvEncodeAPI64
PROPERTIES PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
+5 -4
View File
@@ -1,6 +1,7 @@
FROM spicetools/deps FROM spicetools/deps
WORKDIR /src WORKDIR /src
RUN chown user:user /src
USER user COPY --from=gitroot . /src/.git
COPY --chown=user:user . /src COPY . /src/src/spice2x
CMD ./build_all.sh WORKDIR /src/src/spice2x
ENTRYPOINT ["./build_all.sh"]
+19 -1
View File
@@ -95,7 +95,7 @@ If you need more examples, you can check the example code.
"id": 1, "id": 1,
"module": "card", "module": "card",
"function": "insert", "function": "insert",
"params": [0, "E004000000000000"] "params": [0, "E004010000000000"]
} }
``` ```
#### Response #### Response
@@ -181,6 +181,12 @@ All of those three modules have equally named methods for you to call.
- removes the override value from the objects specified by name - removes the override value from the objects specified by name
- if no names were passed, all overrides will be removed - if no names were passed, all overrides will be removed
##### Additional API for lights
- read(name: string, ...)
- same as read(), but you can specify light names
- write_reset(name: str, ...)
- same as write_reset(), but it accepts a flat list of strings
#### Touch #### Touch
- read() - read()
- returns an array of state objects containing id, x and y - returns an array of state objects containing id, x and y
@@ -246,6 +252,18 @@ which also means that your hex edits are applicable directly.
- info() - info()
- returns information about the serial LCD controller some games use - returns information about the serial LCD controller some games use
#### Resize
- image_resize_enable(enable: bool)
- enables or disables image resize state
- image_resize_set_scene(scene: int)
- sets the active scene for image resize state; set to 0 to disable resize
## Native wrapper libraries
Spicetools provides wrapper libraries in: Arduino, C++, Dart, and Python.
Python is the only one that is fully spec compliant.
Other libraries may be missing features and contain bugs; please feel free to
contribute code to fill the gaps if you work on a project using these libraries.
## License ## License
Unless otherwise noted, all files are licensed under the GPLv3. Unless otherwise noted, all files are licensed under the GPLv3.
See the LICENSE file for the full license text. See the LICENSE file for the full license text.
+3
View File
@@ -70,6 +70,9 @@ void acio::attach() {
libraryhook_hook_library("libacioex.dll", acio::DLL_INSTANCE); libraryhook_hook_library("libacioex.dll", acio::DLL_INSTANCE);
libraryhook_hook_library("libacio_ex.dll", acio::DLL_INSTANCE); libraryhook_hook_library("libacio_ex.dll", acio::DLL_INSTANCE);
libraryhook_hook_library("libacio_old.dll", acio::DLL_INSTANCE); libraryhook_hook_library("libacio_old.dll", acio::DLL_INSTANCE);
// libacioEx.dll for Road Fighters 3D
// needed as comparisons in LoadLibrary hooks are case-sensitive
libraryhook_hook_library("libacioEx.dll", acio::DLL_INSTANCE);
libraryhook_enable(avs::game::DLL_INSTANCE); libraryhook_enable(avs::game::DLL_INSTANCE);
// get hook mode // get hook mode
+288 -2
View File
@@ -2,6 +2,7 @@
#include "avs/game.h" #include "avs/game.h"
#include "games/ddr/io.h" #include "games/ddr/io.h"
#include "games/ddr/ddr.h"
#include "games/sdvx/sdvx.h" #include "games/sdvx/sdvx.h"
#include "games/sdvx/io.h" #include "games/sdvx/io.h"
#include "games/drs/io.h" #include "games/drs/io.h"
@@ -9,6 +10,7 @@
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "util/logging.h" #include "util/logging.h"
#include "util/utils.h" #include "util/utils.h"
#include "util/tapeled.h"
using namespace GameAPI; using namespace GameAPI;
@@ -399,6 +401,148 @@ static long __cdecl ac_io_bi2a_control_led_bright(size_t index, uint8_t brightne
if (light.light2 >= 0) { if (light.light2 >= 0) {
Lights::writeLight(RI_MGR, lights[light.light2], brightness / light.max); Lights::writeLight(RI_MGR, lights[light.light2], brightness / light.max);
} }
// DANCERUSH
} else if (avs::game::is_model("REC")) {
/*
* Control R G B
* ==============================
* CARD UNIT 13 14 15
* TITLE PANEL 28 29 30
* MONITOR SIDE LEFT (tape LED - see ac_io_bi2a_control_tapeled_bright)
* MONITOR SIDE RIGHT (tape LED - see ac_io_bi2a_control_tapeled_bright)
*
* Values go up to 127.
*
* Control Index
* ==================
* 1P LEFT 11
* 1P RIGHT 12
* 1P UP 9
* 1P DOWN 10
* 1P START 8
* 2P LEFT 19
* 2P RIGHT 20
* 2P UP 17
* 2P DOWN 18
* 2P START 16
*
* Values go up to 127.
*/
static const struct {
int light;
float max;
} mapping[] = {
{ -1, 0 }, // 0
{ -1, 0 }, // 1
{ -1, 0 }, // 2
{ -1, 0 }, // 3
{ -1, 0 }, // 4
{ -1, 0 }, // 5
{ -1, 0 }, // 6
{ -1, 0 }, // 7
{ games::drs::Lights::P1_START, 127 }, // 8
{ games::drs::Lights::P1_MENU_UP, 127 }, // 9
{ games::drs::Lights::P1_MENU_DOWN, 127 }, // 10
{ games::drs::Lights::P1_MENU_LEFT, 127 }, // 11
{ games::drs::Lights::P1_MENU_RIGHT, 127 }, // 12
{ games::drs::Lights::CARD_READER_R, 127 }, // 13
{ games::drs::Lights::CARD_READER_G, 127 }, // 14
{ games::drs::Lights::CARD_READER_B, 127 }, // 15
{ games::drs::Lights::P2_START, 127 }, // 16
{ games::drs::Lights::P2_MENU_UP, 127 }, // 17
{ games::drs::Lights::P2_MENU_DOWN, 127 }, // 18
{ games::drs::Lights::P2_MENU_LEFT, 127 }, // 19
{ games::drs::Lights::P2_MENU_RIGHT, 127 }, // 20
{ -1, 0 }, // 21
{ -1, 0 }, // 22
{ -1, 0 }, // 23
{ -1, 0 }, // 24
{ -1, 0 }, // 25
{ -1, 0 }, // 26
{ -1, 0 }, // 27
{ games::drs::Lights::TITLE_PANEL_R, 127 }, // 28
{ games::drs::Lights::TITLE_PANEL_G, 127 }, // 29
{ games::drs::Lights::TITLE_PANEL_B, 127 }, // 30
};
// ignore index out of range
if (index > std::size(mapping)) {
return true;
}
// get lights
auto &lights = games::drs::get_lights();
// get light from mapping
auto light = mapping[index];
// write lights
if (light.light >= 0) {
Lights::writeLight(RI_MGR, lights[light.light], brightness / light.max);
} else {
log_warning("drs", "light unset {} {}", index, (int) brightness);
}
// DanceDanceRevolution
} else if (avs::game::is_model("MDX")) {
static const struct {
int light;
float max;
} mapping[] = {
{ -1, 0 }, // 0
{ -1, 0 }, // 1
{ -1, 0 }, // 2
{ -1, 0 }, // 3
{ -1, 0 }, // 4
{ -1, 0 }, // 5
{ -1, 0 }, // 6
{ -1, 0 }, // 7
{ games::ddr::Lights::GOLD_P1_MENU_START, 127 }, // 8
{ games::ddr::Lights::GOLD_P1_MENU_UP, 127 }, // 9
{ games::ddr::Lights::GOLD_P1_MENU_DOWN, 127 }, // 10
{ games::ddr::Lights::GOLD_P1_MENU_LEFT, 127 }, // 11
{ games::ddr::Lights::GOLD_P1_MENU_RIGHT, 127 }, // 12
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_R, 127 }, // 13
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_G, 127 }, // 14
{ games::ddr::Lights::GOLD_P1_CARD_UNIT_B, 127 }, // 15
{ games::ddr::Lights::GOLD_P2_MENU_START, 127 }, // 16
{ games::ddr::Lights::GOLD_P2_MENU_UP, 127 }, // 17
{ games::ddr::Lights::GOLD_P2_MENU_DOWN, 127 }, // 18
{ games::ddr::Lights::GOLD_P2_MENU_LEFT, 127 }, // 19
{ games::ddr::Lights::GOLD_P2_MENU_RIGHT, 127 }, // 20
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_R, 0 }, // 21
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_G, 0 }, // 22
{ games::ddr::Lights::GOLD_P2_CARD_UNIT_B, 0 }, // 23
{ -1, 0 }, // 24
{ -1, 0 }, // 25
{ -1, 0 }, // 26
{ -1, 0 }, // 27
{ games::ddr::Lights::GOLD_TITLE_PANEL_LEFT, 0 }, // 28
{ games::ddr::Lights::GOLD_TITLE_PANEL_CENTER, 0 }, // 29
{ games::ddr::Lights::GOLD_TITLE_PANEL_RIGHT, 0 }, // 30
{ games::ddr::Lights::GOLD_P1_WOOFER_CORNER, 0 }, // 31
{ games::ddr::Lights::GOLD_P2_WOOFER_CORNER, 0 } // 32
};
// ignore index out of range
if (index > std::size(mapping)) {
return true;
}
// get lights
auto &lights = games::ddr::get_lights();
// get light from mapping
auto light = mapping[index];
// write lights
if (light.light >= 0) {
Lights::writeLight(RI_MGR, lights[light.light], brightness / light.max);
}
} }
// return success // return success
@@ -443,7 +587,7 @@ static bool __cdecl ac_io_bi2a_tapeled_init_is_finished() {
static bool __cdecl ac_io_bi2a_control_tapeled_rec_set(uint8_t* data, size_t x_sz, size_t y_sz) { static bool __cdecl ac_io_bi2a_control_tapeled_rec_set(uint8_t* data, size_t x_sz, size_t y_sz) {
// check dimensions // check dimensions
if (x_sz != 38 || y_sz != 49) { if (x_sz != DRS_TAPELED_COLS || y_sz != DRS_TAPELED_ROWS) {
log_fatal("drs", "DRS tapeled wrong dimensions"); log_fatal("drs", "DRS tapeled wrong dimensions");
} }
@@ -458,9 +602,151 @@ static bool __cdecl ac_io_bi2a_control_tapeled_rec_set(uint8_t* data, size_t x_s
return true; return true;
} }
// TODO: this controls the upright RGB bars on the sides // TODO: DRS tape lights
static bool __cdecl ac_io_bi2a_control_tapeled_bright(size_t off1, size_t off2, static bool __cdecl ac_io_bi2a_control_tapeled_bright(size_t off1, size_t off2,
uint8_t r, uint8_t g, uint8_t b, uint8_t bank) { uint8_t r, uint8_t g, uint8_t b, uint8_t bank) {
if (!tapeledutils::is_enabled()) {
return true;
}
if (avs::game::is_model("MDX")) {
/*
* r, g, b values range from [0-255]
* bank always seems to be [0]
*
* [off1.off2] [LEDs] [tape name]
* 0.0 25 P1 Foot Up (0.0 to 0.24, inclusive)
* 0.25 25 P1 Foot Right
* 1.0 25 P1 Foot Left
* 1.25 25 P1 Foot Down
*
* 2.0 25 P2 Foot Up
* 2.25 25 P2 Foot Right
* 3.0 25 P2 Foot Left
* 3.25 25 P2 Foot Down
*
* 5.0 50 Top Panel
* 6.0 50 Monitor side left
* 7.0 50 Monitor side right
*/
// In order to set the data that can be output via Spice API, we first
// need to figure out which device in our buffers this data belongs to
int device = -1;
if (off1 == 0) {
if (off2 < 25) device = 0; // P1 Foot Up
else device = 1; // P1 Foot Right
} else if (off1 == 1) {
if (off2 < 25) device = 2; // P1 Foot Left
else device = 3; // P1 Foot Down
} else if (off1 == 2) {
if (off2 < 25) device = 4; // P2 Foot Up
else device = 5; // P2 Foot Right
} else if (off1 == 3) {
if (off2 < 25) device = 6; // P2 Foot Left
else device = 7; // P2 Foot Down
} else if (off1 >= 5 && off1 <= 7) {
device = off1 + 3; // Top Panel / Monitor Side Left / Monitor Side Right
}
if (device != -1) {
// We subtract 25 from off2 to get the device's LED index, if it's for one of the
// arrow panels that is on the latter half of the logical strip
size_t subtractor = 0;
if (off1 <= 3 && off2 >= 25) {
subtractor = 25;
}
size_t led_index = off2 - subtractor;
games::ddr::DDR_TAPELEDS[device][led_index][0] = r;
games::ddr::DDR_TAPELEDS[device][led_index][1] = g;
games::ddr::DDR_TAPELEDS[device][led_index][2] = b;
}
static struct TapeLedMapping {
bool split; // true == 50 LEDs for one light, false == 25 for two lights
uint8_t index_r0, index_g0, index_b0;
uint8_t index_r1, index_g1, index_b1;
size_t index_for_avg0 = UINT8_MAX;
size_t index_for_avg1 = UINT8_MAX;
TapeLedMapping(
uint8_t index_r0, uint8_t index_g0, uint8_t index_b0,
uint8_t index_r1, uint8_t index_g1, uint8_t index_b1)
: index_r0(index_r0), index_g0(index_g0), index_b0(index_b0),
index_r1(index_r1), index_g1(index_g1), index_b1(index_b1) {
split = (index_r1 != UINT8_MAX);
if (split) {
index_for_avg0 = tapeledutils::get_led_index_using_avg_algo(25);
index_for_avg1 = index_for_avg0 + 25;
} else {
index_for_avg0 = tapeledutils::get_led_index_using_avg_algo(50);
index_for_avg1 = -1;
}
}
} mapping[] = {
{
games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_UP_AVG_B,
games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_RIGHT_AVG_B
},
{
games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_LEFT_AVG_B,
games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_R, games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_G, games::ddr::Lights::GOLD_P1_FOOT_DOWN_AVG_B
},
{
games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_UP_AVG_B,
games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_RIGHT_AVG_B
},
{
games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_LEFT_AVG_B,
games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_R, games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_G, games::ddr::Lights::GOLD_P2_FOOT_DOWN_AVG_B
},
{
games::ddr::Lights::GOLD_TOP_PANEL_AVG_R, games::ddr::Lights::GOLD_TOP_PANEL_AVG_G, games::ddr::Lights::GOLD_TOP_PANEL_AVG_B,
UINT8_MAX, UINT8_MAX, UINT8_MAX
},
{
UINT8_MAX, UINT8_MAX, UINT8_MAX,
UINT8_MAX, UINT8_MAX, UINT8_MAX
},
{
games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_R, games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_G, games::ddr::Lights::GOLD_MONITOR_SIDE_LEFT_AVG_B,
UINT8_MAX, UINT8_MAX, UINT8_MAX
},
{
games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_R, games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_G, games::ddr::Lights::GOLD_MONITOR_SIDE_RIGHT_AVG_B,
UINT8_MAX, UINT8_MAX, UINT8_MAX
},
};
if (off1 < std::size(mapping)) {
auto &map = mapping[off1];
size_t off2_match = -1;
if (!map.split || off2 < 25) {
off2_match = map.index_for_avg0;
} else {
off2_match = map.index_for_avg1;
}
if (off2_match == off2 && map.index_r0 != UINT8_MAX) {
auto &lights = games::ddr::get_lights();
if (!map.split || off2 < 25) {
Lights::writeLight(RI_MGR, lights[map.index_r0], r / 255.f);
Lights::writeLight(RI_MGR, lights[map.index_g0], g / 255.f);
Lights::writeLight(RI_MGR, lights[map.index_b0], b / 255.f);
} else {
Lights::writeLight(RI_MGR, lights[map.index_r1], r / 255.f);
Lights::writeLight(RI_MGR, lights[map.index_g1], g / 255.f);
Lights::writeLight(RI_MGR, lights[map.index_b1], b / 255.f);
}
}
}
}
return true; return true;
} }
+20
View File
@@ -33,6 +33,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
if (avs::game::is_model("KDM")) { if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights(); auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f); Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 0.f);
} }
return 1; return 1;
@@ -44,6 +49,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
if (avs::game::is_model("KDM")) { if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights(); auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f); Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Start), 1.f);
} }
return 1; return 1;
@@ -55,6 +65,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
if (avs::game::is_model("KDM")) { if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights(); auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f); Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 0.f);
} }
return 1; return 1;
@@ -66,6 +81,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
if (avs::game::is_model("KDM")) { if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights(); auto &lights = games::dea::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f); Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f);
// MUSECA
} else if (avs::game::is_model("PIX")) {
auto &lights = games::museca::get_lights();
Lights::writeLight(RI_MGR, lights.at(games::museca::Lights::Keypad), 1.f);
} }
return 1; return 1;
+38 -3
View File
@@ -25,6 +25,13 @@ struct ICCA_STATUS {
uint32_t key_edge; uint32_t key_edge;
uint32_t key_level; uint32_t key_level;
}; };
struct ICCA_STATUS_LA9 {
uint8_t status_code;
uint8_t card_in;
uint8_t uid[8];
uint8_t error;
uint8_t uid2[8];
};
static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes"); static_assert(sizeof(struct ICCA_STATUS) == 24, "ICCA_STATUS must be 24 bytes");
enum ICCA_WORKFLOW { enum ICCA_WORKFLOW {
@@ -52,6 +59,7 @@ struct ICCA_UNIT {
char key_serial = 0; char key_serial = 0;
bool uid_skip = false; bool uid_skip = false;
bool initialized = false; bool initialized = false;
int felica_retries = 0;
}; };
static ICCA_UNIT ICCA_UNITS[2] {}; static ICCA_UNIT ICCA_UNITS[2] {};
static bool IS_LAST_CARD_FELICA = false; static bool IS_LAST_CARD_FELICA = false;
@@ -88,14 +96,25 @@ static inline void update_card(int unit_id) {
// eamio keypress // eamio keypress
int index = unit_id > 0 && icca_get_active_count() > 1 ? 1 : 0; int index = unit_id > 0 && icca_get_active_count() > 1 ? 1 : 0;
bool kb_insert_press = (eamuse_get_keypad_state(index) & (1 << EAM_IO_INSERT)) > 0; bool kb_insert_press = (eamuse_get_keypad_state(index) & (1 << EAM_IO_INSERT)) > 0;
static bool kb_insert_press_old[2] = {false, false};
// get unit // get unit
ICCA_UNIT *unit = &ICCA_UNITS[unit_id]; ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
const bool card_presented = eamuse_card_insert_consume(icca_get_active_count(), unit_id);
const bool key_pressed = (kb_insert_press && !kb_insert_press_old[unit_id]);
// beatstream and nostalgia have logic that requires ac_io_icca_get_uid_felica to return the
// exact same card number multiple times in a row in order for the card number to be read...
// for whatever reason setting this to 3-4 doesn't work the very first time the game boots up
// so we use 10 just to be safe
const bool need_felica_retries = avs::game::is_model({"NBT", "PAN"});
if (need_felica_retries && (card_presented || key_pressed)) {
unit->felica_retries = 10;
}
// check for card insert // check for card insert
static bool kb_insert_press_old[2] = {false, false}; if (card_presented || key_pressed || (0 < unit->felica_retries)) {
if (eamuse_card_insert_consume(icca_get_active_count(), unit_id) ||
(kb_insert_press && !kb_insert_press_old[unit_id])) {
if (!unit->card_cmd_pressed) { if (!unit->card_cmd_pressed) {
unit->card_cmd_pressed = true; unit->card_cmd_pressed = true;
if (unit->state == GET_USERID || unit->state == CLOSE_EJECT || unit->state == STEP) { if (unit->state == GET_USERID || unit->state == CLOSE_EJECT || unit->state == STEP) {
@@ -288,7 +307,18 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
// funny workaround // funny workaround
if (acio::ICCA_COMPAT_ACTIVE) { if (acio::ICCA_COMPAT_ACTIVE) {
if (avs::game::is_model("LA9")) {
auto p = (ICCA_STATUS*) status;
ICCA_STATUS_LA9 p_la9;
p_la9.status_code = unit->state;
p_la9.card_in = unit->card_in;
memcpy(p_la9.uid, p->uid, sizeof(p_la9.uid));
p_la9.error = p->error;
memcpy(p_la9.uid2, p->uid, sizeof(p_la9.uid));
memcpy(status, &p_la9, sizeof(ICCA_STATUS_LA9));
} else {
// the struct is different (28 bytes instead of 24) but nobody ain't got time for that // the struct is different (28 bytes instead of 24) but nobody ain't got time for that
auto p = (ICCA_STATUS*) status; auto p = (ICCA_STATUS*) status;
p->error = p->key_level << 16; p->error = p->key_level << 16;
@@ -300,6 +330,7 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
p->uid[sizeof(p->uid) - 2] = 0; p->uid[sizeof(p->uid) - 2] = 0;
p->uid[sizeof(p->uid) - 1] = 0; p->uid[sizeof(p->uid) - 1] = 0;
} }
}
return 1; return 1;
} }
@@ -330,6 +361,10 @@ static char __cdecl ac_io_icca_get_uid_felica(int unit_id, char *card) {
card[8] = (char) (felica ? 1 : 0); card[8] = (char) (felica ? 1 : 0);
IS_LAST_CARD_FELICA = felica; IS_LAST_CARD_FELICA = felica;
if (0 < unit->felica_retries) {
unit->felica_retries--;
}
// check for error // check for error
return unit->state != ERR_GETUID; return unit->state != ERR_GETUID;
} }
+68 -16
View File
@@ -16,13 +16,26 @@ static uint8_t COUNTER = 0;
// buffers // buffers
#pragma pack(push, 1) #pragma pack(push, 1)
static struct { static struct {
uint8_t STATUS_BUFFER_17[7][STATUS_BUFFER_SIZE] {}; uint8_t STATUS_BUFFER_P1[7][STATUS_BUFFER_SIZE] {};
uint8_t STATUS_BUFFER_18[7][STATUS_BUFFER_SIZE] {}; uint8_t STATUS_BUFFER_P2[7][STATUS_BUFFER_SIZE] {};
} BUFFERS {}; } BUFFERS {};
#pragma pack(pop) #pragma pack(pop)
static bool STATUS_BUFFER_FREEZE = false; static bool STATUS_BUFFER_FREEZE = false;
typedef uint64_t (__cdecl *ARK_GET_TICK_TIME64_T)();
static uint64_t arkGetTickTime64() {
static ARK_GET_TICK_TIME64_T getTickTime64 =
(ARK_GET_TICK_TIME64_T)GetProcAddress(avs::game::DLL_INSTANCE, "arkGetTickTime64");
if (getTickTime64 == nullptr) {
// this works on 32-bit versions of avs, but not on 64.
// it's better than nothing though.
return timeGetTime();
}
return getTickTime64();
}
/* /*
* Implementations * Implementations
*/ */
@@ -33,13 +46,13 @@ static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer,
if (avs::game::is_model("MDX")) { if (avs::game::is_model("MDX")) {
// get buffer index // get buffer index
auto i = (COUNTER + a3) % std::size(BUFFERS.STATUS_BUFFER_17); auto i = (COUNTER + a3) % std::size(BUFFERS.STATUS_BUFFER_P1);
// copy buffer // copy buffer
if (node == 17) { if (node == 17 || node == 25) {
memcpy(buffer, BUFFERS.STATUS_BUFFER_17[i], STATUS_BUFFER_SIZE); memcpy(buffer, BUFFERS.STATUS_BUFFER_P1[i], STATUS_BUFFER_SIZE);
} else if (node == 18) { } else if (node == 18 || node == 26) {
memcpy(buffer, BUFFERS.STATUS_BUFFER_18[i], STATUS_BUFFER_SIZE); memcpy(buffer, BUFFERS.STATUS_BUFFER_P2[i], STATUS_BUFFER_SIZE);
} else { } else {
// fill with zeros on unknown node // fill with zeros on unknown node
@@ -52,8 +65,41 @@ static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer,
return true; return true;
} }
static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t a3) { static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t value) {
// TODO(felix): DDR BIO2 lights if (avs::game::is_model("MDX")) {
static const struct {
int a2[4];
} mapping[] = {
{
// a1 == 17
{
games::ddr::Lights::GOLD_P1_STAGE_UP_RIGHT,
games::ddr::Lights::GOLD_P1_STAGE_DOWN_LEFT,
games::ddr::Lights::GOLD_P1_STAGE_UP_LEFT,
games::ddr::Lights::GOLD_P1_STAGE_DOWN_RIGHT
}
},
{
// a1 == 18
{
games::ddr::Lights::GOLD_P2_STAGE_UP_RIGHT,
games::ddr::Lights::GOLD_P2_STAGE_DOWN_LEFT,
games::ddr::Lights::GOLD_P2_STAGE_UP_LEFT,
games::ddr::Lights::GOLD_P2_STAGE_DOWN_RIGHT
}
}
};
if ((a1 == 17 || a1 == 18) && (a2 < 4)) {
// get light from mapping
const auto light = mapping[a1 - 17].a2[a2];
// get lights
auto &lights = games::ddr::get_lights();
// write lights
GameAPI::Lights::writeLight(RI_MGR, lights[light], value / 128.f);
}
}
return true; return true;
} }
@@ -61,7 +107,7 @@ static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2
static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) { static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
// increase counter // increase counter
COUNTER = (COUNTER + 1) % std::size(BUFFERS.STATUS_BUFFER_17); COUNTER = (COUNTER + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
// check freeze // check freeze
if (STATUS_BUFFER_FREEZE) { if (STATUS_BUFFER_FREEZE) {
@@ -72,10 +118,12 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
uint8_t *buffer = nullptr; uint8_t *buffer = nullptr;
switch (node) { switch (node) {
case 17: case 17:
buffer = BUFFERS.STATUS_BUFFER_17[COUNTER]; case 25:
buffer = BUFFERS.STATUS_BUFFER_P1[COUNTER];
break; break;
case 18: case 18:
buffer = BUFFERS.STATUS_BUFFER_18[COUNTER]; case 26:
buffer = BUFFERS.STATUS_BUFFER_P2[COUNTER];
break; break;
default: default:
@@ -92,13 +140,13 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
// FOOT UP = bit 36-39 = byte 4, bit 4-7 // FOOT UP = bit 36-39 = byte 4, bit 4-7
// FOOT RIGHT = bit 40-43 = byte 5, bit 0-3 // FOOT RIGHT = bit 40-43 = byte 5, bit 0-3
// FOOT LEFT = bit 44-47 = byte 5, bit 4-7 // FOOT LEFT = bit 44-47 = byte 5, bit 4-7
static const size_t buttons_17[] = { static const size_t buttons_p1[] = {
games::ddr::Buttons::P1_PANEL_UP, games::ddr::Buttons::P1_PANEL_UP,
games::ddr::Buttons::P1_PANEL_DOWN, games::ddr::Buttons::P1_PANEL_DOWN,
games::ddr::Buttons::P1_PANEL_LEFT, games::ddr::Buttons::P1_PANEL_LEFT,
games::ddr::Buttons::P1_PANEL_RIGHT, games::ddr::Buttons::P1_PANEL_RIGHT,
}; };
static const size_t buttons_18[] = { static const size_t buttons_p2[] = {
games::ddr::Buttons::P2_PANEL_UP, games::ddr::Buttons::P2_PANEL_UP,
games::ddr::Buttons::P2_PANEL_DOWN, games::ddr::Buttons::P2_PANEL_DOWN,
games::ddr::Buttons::P2_PANEL_LEFT, games::ddr::Buttons::P2_PANEL_LEFT,
@@ -109,13 +157,17 @@ static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
const size_t *button_map = nullptr; const size_t *button_map = nullptr;
switch (node) { switch (node) {
case 17: case 17:
button_map = &buttons_17[0]; case 25:
button_map = &buttons_p1[0];
break; break;
case 18: case 18:
button_map = &buttons_18[0]; case 26:
button_map = &buttons_p2[0];
break; break;
} }
*(uint64_t*)&buffer[0x18] = arkGetTickTime64();
// get buttons // get buttons
auto &buttons = games::ddr::get_buttons(); auto &buttons = games::ddr::get_buttons();
+1
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include <windows.h> #include <windows.h>
+110 -9
View File
@@ -6,6 +6,11 @@
#include "util/logging.h" #include "util/logging.h"
#include "avs/game.h" #include "avs/game.h"
// std::min
#ifdef min
#undef min
#endif
using namespace GameAPI; using namespace GameAPI;
// static stuff // static stuff
@@ -79,6 +84,32 @@ static bool __cdecl ac_io_panb_start_auto_input() {
return true; return true;
} }
static uint8_t panb_get_button_velocity(Button button, Button button_soft, Button button_medium, Button button_hard) {
const auto velocity = Buttons::getVelocity(RI_MGR, button);
const auto velocity_soft = Buttons::getVelocity(RI_MGR, button_soft);
const auto velocity_medium = Buttons::getVelocity(RI_MGR, button_medium);
const auto velocity_hard = Buttons::getVelocity(RI_MGR, button_hard);
// note that the digital values have been obtained via trial-and-error in recital mode
// based on Op3:
// * soft presses glow blue, should trigger Elegant in blue sections
// * hard presses glow red/orange, should trigger Elegant in yellow sections
// * default (medium) presses glow green, triggers Elegant in both blue and yellow sections
// digital-only values
if (velocity_hard > 0.f) {
// do NOT use 15 here!! 14 properly registers as a hard press, but 15 does not
return 14;
} else if (velocity_medium > 0.f) {
return 11;
} else if (velocity_soft > 0.f) {
return 1;
}
// digital or midi (velocity-sensitive) values
return std::min((uint8_t)(velocity * 15.999f), (uint8_t)14);
}
static bool __cdecl ac_io_panb_update_control_status_buffer() { static bool __cdecl ac_io_panb_update_control_status_buffer() {
// check freeze // check freeze
@@ -107,6 +138,7 @@ static bool __cdecl ac_io_panb_update_control_status_buffer() {
auto &analogs = games::nost::get_analogs(); auto &analogs = games::nost::get_analogs();
// mappings // mappings
// "normal" buttons - these are velocity sensitive (digital or MIDI)
static const size_t button_mapping[] = { static const size_t button_mapping[] = {
games::nost::Buttons::Key1, games::nost::Buttons::Key2, games::nost::Buttons::Key1, games::nost::Buttons::Key2,
games::nost::Buttons::Key3, games::nost::Buttons::Key4, games::nost::Buttons::Key3, games::nost::Buttons::Key4,
@@ -123,6 +155,60 @@ static bool __cdecl ac_io_panb_update_control_status_buffer() {
games::nost::Buttons::Key25, games::nost::Buttons::Key26, games::nost::Buttons::Key25, games::nost::Buttons::Key26,
games::nost::Buttons::Key27, games::nost::Buttons::Key28, games::nost::Buttons::Key27, games::nost::Buttons::Key28,
}; };
// soft (digital) button - always registers as soft press
static const size_t soft_button_mapping[] = {
games::nost::Buttons::Key1Soft, games::nost::Buttons::Key2Soft,
games::nost::Buttons::Key3Soft, games::nost::Buttons::Key4Soft,
games::nost::Buttons::Key5Soft, games::nost::Buttons::Key6Soft,
games::nost::Buttons::Key7Soft, games::nost::Buttons::Key8Soft,
games::nost::Buttons::Key9Soft, games::nost::Buttons::Key10Soft,
games::nost::Buttons::Key11Soft, games::nost::Buttons::Key12Soft,
games::nost::Buttons::Key13Soft, games::nost::Buttons::Key14Soft,
games::nost::Buttons::Key15Soft, games::nost::Buttons::Key16Soft,
games::nost::Buttons::Key17Soft, games::nost::Buttons::Key18Soft,
games::nost::Buttons::Key19Soft, games::nost::Buttons::Key20Soft,
games::nost::Buttons::Key21Soft, games::nost::Buttons::Key22Soft,
games::nost::Buttons::Key23Soft, games::nost::Buttons::Key24Soft,
games::nost::Buttons::Key25Soft, games::nost::Buttons::Key26Soft,
games::nost::Buttons::Key27Soft, games::nost::Buttons::Key28Soft,
};
// medium (digital) button - always registers as medium press
static const size_t medium_button_mapping[] = {
games::nost::Buttons::Key1Medium, games::nost::Buttons::Key2Medium,
games::nost::Buttons::Key3Medium, games::nost::Buttons::Key4Medium,
games::nost::Buttons::Key5Medium, games::nost::Buttons::Key6Medium,
games::nost::Buttons::Key7Medium, games::nost::Buttons::Key8Medium,
games::nost::Buttons::Key9Medium, games::nost::Buttons::Key10Medium,
games::nost::Buttons::Key11Medium, games::nost::Buttons::Key12Medium,
games::nost::Buttons::Key13Medium, games::nost::Buttons::Key14Medium,
games::nost::Buttons::Key15Medium, games::nost::Buttons::Key16Medium,
games::nost::Buttons::Key17Medium, games::nost::Buttons::Key18Medium,
games::nost::Buttons::Key19Medium, games::nost::Buttons::Key20Medium,
games::nost::Buttons::Key21Medium, games::nost::Buttons::Key22Medium,
games::nost::Buttons::Key23Medium, games::nost::Buttons::Key24Medium,
games::nost::Buttons::Key25Medium, games::nost::Buttons::Key26Medium,
games::nost::Buttons::Key27Medium, games::nost::Buttons::Key28Medium,
};
// hard (digital) button - always registers as hard press
static const size_t hard_button_mapping[] = {
games::nost::Buttons::Key1Hard, games::nost::Buttons::Key2Hard,
games::nost::Buttons::Key3Hard, games::nost::Buttons::Key4Hard,
games::nost::Buttons::Key5Hard, games::nost::Buttons::Key6Hard,
games::nost::Buttons::Key7Hard, games::nost::Buttons::Key8Hard,
games::nost::Buttons::Key9Hard, games::nost::Buttons::Key10Hard,
games::nost::Buttons::Key11Hard, games::nost::Buttons::Key12Hard,
games::nost::Buttons::Key13Hard, games::nost::Buttons::Key14Hard,
games::nost::Buttons::Key15Hard, games::nost::Buttons::Key16Hard,
games::nost::Buttons::Key17Hard, games::nost::Buttons::Key18Hard,
games::nost::Buttons::Key19Hard, games::nost::Buttons::Key20Hard,
games::nost::Buttons::Key21Hard, games::nost::Buttons::Key22Hard,
games::nost::Buttons::Key23Hard, games::nost::Buttons::Key24Hard,
games::nost::Buttons::Key25Hard, games::nost::Buttons::Key26Hard,
games::nost::Buttons::Key27Hard, games::nost::Buttons::Key28Hard,
};
static const size_t analog_mapping[] = { static const size_t analog_mapping[] = {
games::nost::Analogs::Key1, games::nost::Analogs::Key2, games::nost::Analogs::Key1, games::nost::Analogs::Key2,
games::nost::Analogs::Key3, games::nost::Analogs::Key4, games::nost::Analogs::Key3, games::nost::Analogs::Key4,
@@ -148,23 +234,38 @@ static bool __cdecl ac_io_panb_update_control_status_buffer() {
uint8_t state1 = 0; uint8_t state1 = 0;
// check analogs // check analogs
//
// while 15 is technically allowed by the I/O board & is recognized correctly in test
// menu, when you play recital mode, 15 fails to register Elegant in yellow sections.
// therefore, cap the value at 14. tested with Nostroller.
auto &analog0 = analogs.at(analog_mapping[key_pair * 2 + 0]); auto &analog0 = analogs.at(analog_mapping[key_pair * 2 + 0]);
auto &analog1 = analogs.at(analog_mapping[key_pair * 2 + 1]); auto &analog1 = analogs.at(analog_mapping[key_pair * 2 + 1]);
if (analog0.isSet()) { if (analog0.isSet()) {
state0 = (uint8_t) (Analogs::getState(RI_MGR, analog0) * 15.999f); state0 = std::min((uint8_t)(Analogs::getState(RI_MGR, analog0) * 15.999f), (uint8_t)14);
} }
if (analog1.isSet()) { if (analog1.isSet()) {
state1 = (uint8_t) (Analogs::getState(RI_MGR, analog1) * 15.999f); state1 = std::min((uint8_t)(Analogs::getState(RI_MGR, analog1) * 15.999f), (uint8_t)14);
} }
// check buttons // check digital buttons
auto velocity0 = Buttons::getVelocity(RI_MGR, buttons.at(button_mapping[key_pair * 2 + 0])); const auto button0 = panb_get_button_velocity(
auto velocity1 = Buttons::getVelocity(RI_MGR, buttons.at(button_mapping[key_pair * 2 + 1])); buttons.at(button_mapping[key_pair * 2 + 0]),
if (velocity0 > 0.f) { buttons.at(soft_button_mapping[key_pair * 2 + 0]),
state0 = (uint8_t) (velocity0 * 15.999f); buttons.at(medium_button_mapping[key_pair * 2 + 0]),
buttons.at(hard_button_mapping[key_pair * 2 + 0])
);
if (button0 > 0) {
state0 = button0;
} }
if (velocity1 > 0.f) {
state1 = (uint8_t) (velocity1 * 15.999f); const auto button1 = panb_get_button_velocity(
buttons.at(button_mapping[key_pair * 2 + 1]),
buttons.at(soft_button_mapping[key_pair * 2 + 1]),
buttons.at(medium_button_mapping[key_pair * 2 + 1]),
buttons.at(hard_button_mapping[key_pair * 2 + 1])
);
if (button1 > 0) {
state1 = button1;
} }
// build value // build value
+1 -1
View File
@@ -44,7 +44,7 @@ namespace acio2emu::firmware {
break; break;
case 785: { // write output case 785: { // write output
auto count = write_output(std::span{cur.base(), static_cast<size_t>(in.payload.end() - cur)}); auto count = write_output(std::span{&*cur, static_cast<size_t>(in.payload.end() - cur)});
if (count < 0) { if (count < 0) {
return false; return false;
} }
+4
View File
@@ -108,6 +108,10 @@ std::optional<uint8_t> ACIOEmu::read() {
return this->response_buffer->get(); return this->response_buffer->get();
} }
size_t ACIOEmu::bytes_available() {
return this->response_buffer->size();
}
void ACIOEmu::msg_parse() { void ACIOEmu::msg_parse() {
#ifdef ACIOEMU_LOG #ifdef ACIOEMU_LOG
+1
View File
@@ -28,5 +28,6 @@ namespace acioemu {
void write(uint8_t byte); void write(uint8_t byte);
std::optional<uint8_t> read(); std::optional<uint8_t> read();
size_t bytes_available();
}; };
} }
+7 -2
View File
@@ -4,8 +4,9 @@
#include "rawinput/rawinput.h" #include "rawinput/rawinput.h"
#include "util/utils.h" #include "util/utils.h"
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) { acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
this->com_port = lpCOMPort; this->com_port = lpCOMPort;
this->icca_node_count = iccaNodeCount;
} }
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) { bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
@@ -16,7 +17,7 @@ bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port)); log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
// ACIO device // ACIO device
acio_emu.add_device(new acioemu::ICCADevice(false, true, 2)); acio_emu.add_device(new acioemu::ICCADevice(false, true, icca_node_count));
return true; return true;
} }
@@ -62,6 +63,10 @@ int acioemu::ACIOHandle::device_io(
return -1; return -1;
} }
size_t acioemu::ACIOHandle::bytes_available() {
return acio_emu.bytes_available();
}
bool acioemu::ACIOHandle::close() { bool acioemu::ACIOHandle::close() {
log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port)); log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port));
+5 -1
View File
@@ -9,10 +9,12 @@ namespace acioemu {
private: private:
LPCWSTR com_port; LPCWSTR com_port;
uint8_t icca_node_count;
acioemu::ACIOEmu acio_emu; acioemu::ACIOEmu acio_emu;
public: public:
ACIOHandle(LPCWSTR lpCOMPort); ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
bool open(LPCWSTR lpFileName) override; bool open(LPCWSTR lpFileName) override;
@@ -23,6 +25,8 @@ namespace acioemu {
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
DWORD nOutBufferSize) override; DWORD nOutBufferSize) override;
size_t bytes_available() override;
bool close() override; bool close() override;
}; };
} }
+57 -13
View File
@@ -8,6 +8,10 @@
using namespace acioemu; using namespace acioemu;
namespace acioemu {
bool ICCA_DEVICE_HACK = false;
}
ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count) { ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count) {
// init defaults // init defaults
@@ -98,11 +102,13 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
// send version data // send version data
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE); auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
if ( if (
avs::game::is_model({"LDJ", "TBS"}) || avs::game::is_model({"LDJ", "TBS", "UJK"}) ||
// SDVX Valkyrie cabinet mode // SDVX Valkyrie cabinet mode
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H')) (avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))
) { ) {
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA"); this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCA");
} else if (avs::game::is_model({"VFG"})) {
this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
} else { } else {
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA"); this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
} }
@@ -327,6 +333,9 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
} }
case ACIO_CMD_STARTUP: case ACIO_CMD_STARTUP:
case ACIO_CMD_CLEAR: case ACIO_CMD_CLEAR:
case 0x30: // GetBoardProductNumber
case 0x31: // GetMicomInfo
case 0x3A: // ???
case 0x0116: // ??? case 0x0116: // ???
case 0x0120: // ??? case 0x0120: // ???
case 0xFF: // BROADCAST case 0xFF: // BROADCAST
@@ -385,19 +394,48 @@ void ICCADevice::update_card(int unit) {
static int KEYPAD_EAMUSE_MAPPING[] = { static int KEYPAD_EAMUSE_MAPPING[] = {
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4 0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
}; };
// map for KEYPAD_KEY_CODES:
// 7 8 9 | 800 8000 8
// 4 5 6 | 400 4000 4
// 1 2 3 | 200 2000 2
// 0 00 . | 100 1000 1
static int KEYPAD_KEY_CODES[]{ static int KEYPAD_KEY_CODES[]{
0x100, 0x100, // 0
0x200, 0x200, // 1
0x2000, 0x2000, // 2
2, 2, // 3
0x400, 0x400, // 4
0x4000, 0x4000, // 5
4, 4, // 6
0x800, 0x800, // 7
0x8000, 0x8000, // 8
8, 8, // 9
1, 1, // .
0x1000 0x1000 // 00
};
// map for KEYPAD_KEY_CODES_ALT:
// 7 8 9 | 8 80 800
// 4 5 6 | 4 40 400
// 1 2 3 | 2 20 200
// 0 00 . | 1 10 100
//
// note that the only game that needs this (SDVX VM) does not accept decimal,
// so that key is untested
static int KEYPAD_KEY_CODES_ALT[]{
1, // 0
2, // 1
0x20, // 2
0x200, // 3
4, // 4
0x40, // 5
0x400, // 6
8, // 7
0x80, // 8
0x800, // 9
0x100, // .
0x10 // 00
}; };
static uint8_t KEYPAD_KEY_CODE_NUMS[]{ static uint8_t KEYPAD_KEY_CODE_NUMS[]{
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4 0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
@@ -421,7 +459,13 @@ void ICCADevice::update_keypad(int unit, bool update_edge) {
// check if pressed // check if pressed
if (eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])) { if (eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])) {
if (ICCA_DEVICE_HACK) {
this->keypad[unit] |= KEYPAD_KEY_CODES_ALT[i];
} else {
this->keypad[unit] |= KEYPAD_KEY_CODES[i]; this->keypad[unit] |= KEYPAD_KEY_CODES[i];
}
if (!this->keypad_last[unit][i] && update_edge) { if (!this->keypad_last[unit][i] && update_edge) {
this->keydown[unit] = (this->keypad_capture[unit] << 4) | KEYPAD_KEY_CODE_NUMS[n]; this->keydown[unit] = (this->keypad_capture[unit] << 4) | KEYPAD_KEY_CODE_NUMS[n];
this->keypad_last[unit][i] = true; this->keypad_last[unit][i] = true;
+2
View File
@@ -11,6 +11,8 @@
#include "reader/crypt.h" #include "reader/crypt.h"
namespace acioemu { namespace acioemu {
extern bool ICCA_DEVICE_HACK;
class ICCADevice : public ACIODeviceEmu { class ICCADevice : public ACIODeviceEmu {
private: private:
bool type_new; bool type_new;
+4
View File
@@ -18,6 +18,7 @@
#include "modules/capture.h" #include "modules/capture.h"
#include "modules/coin.h" #include "modules/coin.h"
#include "modules/control.h" #include "modules/control.h"
#include "modules/ddr.h"
#include "modules/drs.h" #include "modules/drs.h"
#include "modules/iidx.h" #include "modules/iidx.h"
#include "modules/info.h" #include "modules/info.h"
@@ -26,6 +27,7 @@
#include "modules/lights.h" #include "modules/lights.h"
#include "modules/memory.h" #include "modules/memory.h"
#include "modules/touch.h" #include "modules/touch.h"
#include "modules/resize.h"
#include "request.h" #include "request.h"
#include "response.h" #include "response.h"
@@ -393,6 +395,7 @@ void Controller::init_state(api::ClientState *state) {
state->modules.push_back(new modules::Capture()); state->modules.push_back(new modules::Capture());
state->modules.push_back(new modules::Coin()); state->modules.push_back(new modules::Coin());
state->modules.push_back(new modules::Control()); state->modules.push_back(new modules::Control());
state->modules.push_back(new modules::DDR());
state->modules.push_back(new modules::DRS()); state->modules.push_back(new modules::DRS());
state->modules.push_back(new modules::IIDX()); state->modules.push_back(new modules::IIDX());
state->modules.push_back(new modules::Info()); state->modules.push_back(new modules::Info());
@@ -401,6 +404,7 @@ void Controller::init_state(api::ClientState *state) {
state->modules.push_back(new modules::Lights()); state->modules.push_back(new modules::Lights());
state->modules.push_back(new modules::Memory()); state->modules.push_back(new modules::Memory());
state->modules.push_back(new modules::Touch()); state->modules.push_back(new modules::Touch());
state->modules.push_back(new modules::Resize());
} }
void Controller::free_state(api::ClientState *state) { void Controller::free_state(api::ClientState *state) {
+1 -29
View File
@@ -27,34 +27,6 @@ namespace api::modules {
{ SIGTERM, "SIGTERM" }, { SIGTERM, "SIGTERM" },
}; };
static inline bool acquire_shutdown_privs() {
// check if already acquired
static bool acquired = false;
if (acquired)
return true;
// get process token
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
// get the LUID for the shutdown privilege
TOKEN_PRIVILEGES tkp;
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// get the shutdown privilege for this process
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
// check for error
bool success = GetLastError() == ERROR_SUCCESS;
if (success)
acquired = true;
return success;
}
Control::Control() : Module("control", true) { Control::Control() : Module("control", true) {
functions["raise"] = std::bind(&Control::raise, this, _1, _2); functions["raise"] = std::bind(&Control::raise, this, _1, _2);
functions["exit"] = std::bind(&Control::exit, this, _1, _2); functions["exit"] = std::bind(&Control::exit, this, _1, _2);
@@ -150,7 +122,7 @@ namespace api::modules {
return error(res, "Unable to acquire shutdown privileges"); return error(res, "Unable to acquire shutdown privileges");
// exit windows // exit windows
if (!ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN | EWX_FORCE,
SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MAJOR_APPLICATION |
SHTDN_REASON_MINOR_MAINTENANCE)) SHTDN_REASON_MINOR_MAINTENANCE))
return error(res, "Unable to shutdown system"); return error(res, "Unable to shutdown system");
+54
View File
@@ -0,0 +1,54 @@
#include "ddr.h"
#include <functional>
#include "external/rapidjson/document.h"
#include "games/ddr/ddr.h"
using namespace std::placeholders;
using namespace rapidjson;
namespace api::modules {
DDR::DDR() : Module("ddr") {
functions["tapeled_get"] = std::bind(&DDR::tapeled_get, this, _1, _2);
}
/**
* Allows fetching of the RGB LED strips that are gold cabinets, via SpiceAPI
*/
void DDR::tapeled_get(Request &req, Response &res) {
static const char* device_names[11] = {
"p1_foot_up",
"p1_foot_right",
"p1_foot_left",
"p1_foot_down",
"p2_foot_up",
"p2_foot_right",
"p2_foot_left",
"p2_foot_down",
"top_panel",
"monitor_left",
"monitor_right"
};
Value response_object(kObjectType);
// Iterate through each device and dump its lights data into the response
for (size_t device = 0; device < 11; device++) {
size_t num_leds = 25;
if (device > 7)
num_leds = 50;
Value light_state(kArrayType);
light_state.Reserve(num_leds * 3, res.doc()->GetAllocator());
for (size_t led = 0; led < num_leds; led++) {
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][0], res.doc()->GetAllocator());
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][1], res.doc()->GetAllocator());
light_state.PushBack(games::ddr::DDR_TAPELEDS[device][led][2], res.doc()->GetAllocator());
}
response_object.AddMember(StringRef(device_names[device]), light_state, res.doc()->GetAllocator());
}
res.add_data(response_object);
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <vector>
#include "api/module.h"
#include "api/request.h"
namespace api::modules {
class DDR : public Module {
public:
DDR();
private:
// function definitions
void tapeled_get(Request &req, Response &res);
};
}
+58 -18
View File
@@ -1,5 +1,7 @@
#include "lights.h" #include "lights.h"
#include <functional> #include <functional>
#include <cfg/configurator.h>
#include "external/rapidjson/document.h" #include "external/rapidjson/document.h"
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "cfg/light.h" #include "cfg/light.h"
@@ -17,11 +19,16 @@ namespace api::modules {
functions["read"] = std::bind(&Lights::read, this, _1, _2); functions["read"] = std::bind(&Lights::read, this, _1, _2);
functions["write"] = std::bind(&Lights::write, this, _1, _2); functions["write"] = std::bind(&Lights::write, this, _1, _2);
functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2); functions["write_reset"] = std::bind(&Lights::write_reset, this, _1, _2);
lights = games::get_lights(eamuse_get_game());
this->lights = games::get_lights(eamuse_get_game());
for (auto &light : *this->lights) {
this->lights_by_names.emplace(light.getName(), light);
}
} }
/** /**
* read() * read()
* read(name: str, ...)
*/ */
void Lights::read(api::Request &req, Response &res) { void Lights::read(api::Request &req, Response &res) {
@@ -30,8 +37,31 @@ namespace api::modules {
return; return;
} }
// all lights for this game
if (req.params.Size() == 0) {
// add state for each light // add state for each light
for (auto &light : *this->lights) { for (auto &light : *this->lights) {
get_light(light, res);
}
return;
}
// specified light names
for (Value &param : req.params.GetArray()) {
// check params
if (!param.IsString()) {
error_type(res, "name", "string");
return;
}
const auto name = param.GetString();
if (this->lights_by_names.contains(name)) {
get_light(this->lights_by_names.at(name).get(), res);
}
}
}
void Lights::get_light(Light &light, Response &res) {
Value state(kArrayType); Value state(kArrayType);
Value light_name(light.getName().c_str(), res.doc()->GetAllocator()); Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
Value light_state(GameAPI::Lights::readLight(RI_MGR, light)); Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
@@ -41,7 +71,6 @@ namespace api::modules {
state.PushBack(light_enabled, res.doc()->GetAllocator()); state.PushBack(light_enabled, res.doc()->GetAllocator());
res.add_data(state); res.add_data(state);
} }
}
/** /**
* write([name: str, state: float], ...) * write([name: str, state: float], ...)
@@ -88,6 +117,7 @@ namespace api::modules {
/** /**
* write_reset() * write_reset()
* write_reset(name: str, ...)
* write_reset([name: str], ...) * write_reset([name: str], ...)
*/ */
void Lights::write_reset(Request &req, Response &res) { void Lights::write_reset(Request &req, Response &res) {
@@ -104,20 +134,23 @@ namespace api::modules {
if (params.Size() == 0) { if (params.Size() == 0) {
if (lights != nullptr) { if (lights != nullptr) {
for (auto &light : *this->lights) { for (auto &light : *this->lights) {
if (light.override_enabled) {
if (cfg::CONFIGURATOR_STANDALONE) {
GameAPI::Lights::writeLight(RI_MGR, light, light.last_state);
}
light.override_enabled = false; light.override_enabled = false;
} }
} }
}
return; return;
} }
// loop parameters // loop parameters
for (Value &param : req.params.GetArray()) { for (Value &param : req.params.GetArray()) {
const char* light_name = nullptr;
// check params // check params
if (!param.IsArray()) { if (param.IsArray()) {
error(res, "parameters must be arrays");
return;
}
if (param.Size() < 1) { if (param.Size() < 1) {
error_params_insufficient(res); error_params_insufficient(res);
continue; continue;
@@ -126,12 +159,16 @@ namespace api::modules {
error_type(res, "name", "string"); error_type(res, "name", "string");
continue; continue;
} }
// get params // get params
auto light_name = param[0].GetString(); light_name = param[0].GetString();
} else if (param.IsString()) {
light_name = param.GetString();
} else {
error(res, "parameters must be arrays or strings");
}
// write analog state // write analog state
if (!this->write_light_reset(light_name)) { if (light_name && !this->write_light_reset(light_name)) {
error_unknown(res, "analog", light_name); error_unknown(res, "analog", light_name);
continue; continue;
} }
@@ -146,17 +183,21 @@ namespace api::modules {
} }
// find light // find light
for (auto &light : *this->lights) { if (this->lights_by_names.contains(name)) {
if (light.getName() == name) { auto &light = this->lights_by_names.at(name).get();
light.override_state = CLAMP(state, 0.f, 1.f); light.override_state = CLAMP(state, 0.f, 1.f);
light.override_enabled = true; light.override_enabled = true;
return true;
} if (cfg::CONFIGURATOR_STANDALONE) {
GameAPI::Lights::writeLight(RI_MGR, light, state);
} }
return true;
} else {
// unknown light // unknown light
return false; return false;
} }
}
bool Lights::write_light_reset(std::string name) { bool Lights::write_light_reset(std::string name) {
@@ -166,14 +207,13 @@ namespace api::modules {
} }
// find light // find light
for (auto &light : *this->lights) { if (this->lights_by_names.contains(name)) {
if (light.getName() == name) { auto &light = this->lights_by_names.at(name).get();
light.override_enabled = false; light.override_enabled = false;
return true; return true;
} } else {
}
// unknown light // unknown light
return false; return false;
} }
}
} }
+4
View File
@@ -1,6 +1,8 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <external/robin_hood.h>
#include "api/module.h" #include "api/module.h"
#include "api/request.h" #include "api/request.h"
#include "cfg/api.h" #include "cfg/api.h"
@@ -15,6 +17,7 @@ namespace api::modules {
// state // state
std::vector<Light> *lights; std::vector<Light> *lights;
robin_hood::unordered_map<std::string, std::reference_wrapper<Light>> lights_by_names;
// function definitions // function definitions
void read(Request &req, Response &res); void read(Request &req, Response &res);
@@ -22,6 +25,7 @@ namespace api::modules {
void write_reset(Request &req, Response &res); void write_reset(Request &req, Response &res);
// helper // helper
void get_light(Light &light, Response &res);
bool write_light(std::string name, float state); bool write_light(std::string name, float state);
bool write_light_reset(std::string name); bool write_light_reset(std::string name);
}; };
+53
View File
@@ -0,0 +1,53 @@
#include "resize.h"
#include "external/rapidjson/document.h"
#include "cfg/screen_resize.h"
using namespace std::placeholders;
using namespace rapidjson;
namespace api::modules {
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
Resize::Resize() : Module("resize") {
functions["image_resize_enable"] = std::bind(&Resize::image_resize_enable, this, _1, _2);
functions["image_resize_set_scene"] = std::bind(&Resize::image_resize_set_scene, this, _1, _2);
}
/**
* image_resize_enable(enable: bool)
*/
void Resize::image_resize_enable(Request &req, Response &res) {
if (req.params.Size() < 1) {
return error_params_insufficient(res);
}
if (!req.params[0].IsBool()) {
return error_type(res, "enable", "bool");
}
cfg::SCREENRESIZE->enable_screen_resize = req.params[0].GetBool();
}
/**
* image_resize_set_scene(scene: int)
*/
void Resize::image_resize_set_scene(Request &req, Response &res) {
if (req.params.Size() < 1) {
return error_params_insufficient(res);
}
if (!req.params[0].IsInt()) {
return error_type(res, "scene", "int");
}
const auto scene = req.params[0].GetInt();
if (scene < 0 || (int)std::size(cfg::SCREENRESIZE->scene_settings) < scene) {
return error(res, "invalid scene number");
}
if (scene == 0) {
cfg::SCREENRESIZE->enable_screen_resize = false;
} else {
cfg::SCREENRESIZE->enable_screen_resize = true;
cfg::SCREENRESIZE->screen_resize_current_scene = scene - 1;
}
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "api/module.h"
#include "api/request.h"
namespace api::modules {
class Resize : public Module {
public:
Resize();
private:
// function definitions
void image_resize_enable(Request &req, Response &res);
void image_resize_set_scene(Request &req, Response &res);
};
}
+6
View File
@@ -4,6 +4,7 @@
#include "external/rapidjson/document.h" #include "external/rapidjson/document.h"
#include "avs/game.h" #include "avs/game.h"
#include "hooks/graphics/graphics.h"
#include "misc/eamuse.h" #include "misc/eamuse.h"
#include "launcher/launcher.h" #include "launcher/launcher.h"
#include "touch/touch.h" #include "touch/touch.h"
@@ -18,7 +19,12 @@ namespace api::modules {
Touch::Touch() : Module("touch") { Touch::Touch() : Module("touch") {
is_sdvx = avs::game::is_model("KFC"); is_sdvx = avs::game::is_model("KFC");
is_tdj_fhd = (avs::game::is_model("LDJ") && games::iidx::is_tdj_fhd()); is_tdj_fhd = (avs::game::is_model("LDJ") && games::iidx::is_tdj_fhd());
// special case: when windowed subscreen is in use, use the original coords
if (GRAPHICS_IIDX_WSUB) {
is_tdj_fhd = false;
}
functions["read"] = std::bind(&Touch::read, this, _1, _2); functions["read"] = std::bind(&Touch::read, this, _1, _2);
functions["write"] = std::bind(&Touch::write, this, _1, _2); functions["write"] = std::bind(&Touch::write, this, _1, _2);
-15
View File
@@ -1,15 +0,0 @@
# Lua Scripting
Supported version: Lua 5.4.3
No proper documentation yet. Check the example scripts if you need this!
For undocumented functions you can find the definitions in the source code (script/api/*.cpp).
They are very similar to what the network API provides.
# Automatic Execution
Create a "scripts" folder next to spice and put your scripts in there (subfolders allowed).
The prefix specifies when the script will be called:
- `boot_*`: executed on game boot
- `shutdown_*`: executed on game end
- `config_*`: executed when you start spicecfg (mostly for debugging/tests)
Example: "scripts/boot_patch.py" would be called on game boot.
-113
View File
@@ -1,113 +0,0 @@
-- example script for light effects on IIDX TT stab movement
-- create a folder called "script" next to spice and put me in there
--------------------------------------------------------------------------------
-- settings
tt_duration = 0.25
zero_duration = 0.3141592
loop_delta = 1 / 240
curve_pow = 1 / 4
col_r = 1.0
col_g = 0.0
col_b = 0.0
light_p1_r = "Side Panel Left Avg R"
light_p1_g = "Side Panel Left Avg G"
light_p1_b = "Side Panel Left Avg B"
light_p2_r = "Side Panel Right Avg R"
light_p2_g = "Side Panel Right Avg G"
light_p2_b = "Side Panel Right Avg B"
-- wait for game
while not analogs.read()["Turntable P1"] do yield() end
-- initial state
tt1_last = tonumber(analogs.read()["Turntable P1"].state)
tt2_last = tonumber(analogs.read()["Turntable P2"].state)
tt1_diff_last = 0
tt2_diff_last = 0
tt1_trigger = 0
tt2_trigger = 0
tt1_zero_elapsed = 0
tt2_zero_elapsed = 0
-- main loop
while true do
-- read state
tt1 = tonumber(analogs.read()["Turntable P1"].state)
tt2 = tonumber(analogs.read()["Turntable P2"].state)
time_cur = time()
-- calculate difference
tt1_diff = tt1 - tt1_last
tt2_diff = tt2 - tt2_last
-- fix wrap around
if math.abs(tt1_diff) > 0.5 then tt1_diff = 0 end
if math.abs(tt2_diff) > 0.5 then tt2_diff = 0 end
-- trigger on movement start and direction changes
if (tt1_diff_last == 0 and tt1_diff ~= 0)
or (tt1_diff_last > 0 and tt1_diff < 0)
or (tt1_diff_last < 0 and tt1_diff > 0) then
tt1_trigger = time_cur
end
if (tt2_diff_last == 0 and tt2_diff ~= 0)
or (tt2_diff_last > 0 and tt2_diff < 0)
or (tt2_diff_last < 0 and tt2_diff > 0) then
tt2_trigger = time_cur
end
-- light effects when last trigger is still active
if time_cur - tt1_trigger < tt_duration then
brightness = 1 - ((time_cur - tt1_trigger) / tt_duration) ^ curve_pow
lights.write({[light_p1_r]={state=brightness*col_r}})
lights.write({[light_p1_g]={state=brightness*col_g}})
lights.write({[light_p1_b]={state=brightness*col_b}})
else
lights.write_reset(light_p1_r)
lights.write_reset(light_p1_g)
lights.write_reset(light_p1_b)
end
if time_cur - tt2_trigger < tt_duration then
brightness = 1 - ((time_cur - tt2_trigger) / tt_duration) ^ curve_pow
lights.write({[light_p2_r]={state=brightness*col_r}})
lights.write({[light_p2_g]={state=brightness*col_g}})
lights.write({[light_p2_b]={state=brightness*col_b}})
else
lights.write_reset(light_p2_r)
lights.write_reset(light_p2_g)
lights.write_reset(light_p2_b)
end
-- flush HID light output
lights.update()
-- turntable movement detection
-- doesn't set the diff back to zero unless enough time has passed
if tt1_diff == 0 then
tt1_zero_elapsed = tt1_zero_elapsed + loop_delta
if tt1_zero_elapsed >= zero_duration then
tt1_diff_last = tt1_diff
end
else
tt1_zero_elapsed = 0
tt1_diff_last = tt1_diff
end
if tt2_diff == 0 then
tt2_zero_elapsed = tt2_zero_elapsed + loop_delta
if tt2_zero_elapsed >= zero_duration then
tt2_diff_last = tt2_diff
end
else
tt2_zero_elapsed = 0
tt2_diff_last = tt2_diff
end
-- remember state
tt1_last = tt1
tt2_last = tt2
-- loop end
sleep(loop_delta)
end
-57
View File
@@ -1,57 +0,0 @@
-- script examples
-- no proper documentation yet
-- create a folder called "script" next to spice and put me in there
-- then open the config and if needed select IIDX for the demo
--------------------------------------------------------------------------------
-- sleep for 0.2 seconds
sleep(0.2)
-- log functions
log_misc("example misc")
log_info("example info")
log_warning("example warning")
--log_fatal("this would terminate")
-- print time
log_info(time())
-- show message box
msgbox("You are running the example script! Select IIDX if not already done.")
-- wait until analog is available
while not analogs.read()["Turntable P1"] do yield() end
-- write button state
buttons.write({["P1 Start"]={state=1}})
-- write analog state
analogs.write({["Turntable P1"]={state=0.33}})
-- write light state
lights.write({["P2 Start"]={state=0.8}})
-- import other libraries in "script" folder
--local example = require('script.example')
-- demo
while true do
-- analog animation
analogs.write({["Turntable P2"]={state=math.abs(math.sin(time()))}})
-- button blink
if math.cos(time() * 10) > 0 then
buttons.write({["P1 1"]={state=1}})
else
buttons.write({["P1 1"]={state=0}})
end
-- flush HID light output
lights.update()
-- check for keyboard press
if GetAsyncKeyState(0x20) > 0 then
msgbox("You pressed space!")
end
end
@@ -12,3 +12,4 @@ from .keypads import *
from .lights import * from .lights import *
from .memory import * from .memory import *
from .touch import * from .touch import *
from .resize import *
+8 -2
View File
@@ -2,8 +2,14 @@ from .connection import Connection
from .request import Request from .request import Request
def lights_read(con: Connection): def lights_read(con: Connection, light_names=None):
res = con.request(Request("lights", "read")) req = Request("lights", "read")
if light_names:
for light_name in light_names:
req.add_param(light_name)
res = con.request(req)
return res.get_data() return res.get_data()
+12
View File
@@ -0,0 +1,12 @@
from .connection import Connection
from .request import Request
def image_resize_enable(con: Connection, enable: bool):
req = Request("resize", "image_resize_enable")
req.add_param(enable)
con.request(req)
def image_resize_set_scene(con: Connection, scene: int):
req = Request("resize", "image_resize_set_scene")
req.add_param(scene)
con.request(req)
+62 -1
View File
@@ -179,7 +179,7 @@ class ControlTab(ttk.Frame):
self.card_lbl = ttk.Label(self.card, text="Card") self.card_lbl = ttk.Label(self.card, text="Card")
self.card_lbl.grid(row=0, columnspan=2) self.card_lbl.grid(row=0, columnspan=2)
self.card_entry = ttk.Entry(self.card) self.card_entry = ttk.Entry(self.card)
self.card_entry.insert(tk.END, "E004000000000000") self.card_entry.insert(tk.END, "E004010000000000")
self.card_entry.grid(row=1, columnspan=2, sticky=NSEW, padx=2, pady=2) self.card_entry.grid(row=1, columnspan=2, sticky=NSEW, padx=2, pady=2)
self.card_insert_p1 = ttk.Button(self.card, text="Insert P1", command=self.action_insert_p1) self.card_insert_p1 = ttk.Button(self.card, text="Insert P1", command=self.action_insert_p1)
self.card_insert_p1.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2) self.card_insert_p1.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2)
@@ -397,6 +397,65 @@ class LightsTab(ttk.Frame):
# set text # set text
self.txt_lights.set_text(txt) self.txt_lights.set_text(txt)
class ResizeTab(ttk.Frame):
"""Resize tab."""
def __init__(self, app, parent, **kwargs):
# init frame
ttk.Frame.__init__(self, parent, **kwargs)
self.app = app
self.parent = parent
# scale grid
self.columnconfigure(0, weight=1)
# image resize
self.resize = ttk.Frame(self, padding=(8, 8, 8, 8))
self.resize.grid(row=0, column=0, sticky=tk.E+tk.W)
self.resize.columnconfigure(0, weight=1)
self.resize.columnconfigure(1, weight=1)
self.resize_lbl = ttk.Label(self.resize, text="Image Resize")
self.resize_lbl.grid(row=0, columnspan=2)
self.resize_off = ttk.Button(self.resize, text="Disable", command=self.action_resize_false)
self.resize_off.grid(row=2, column=0, sticky=NSEW, padx=2, pady=2)
self.resize_on = ttk.Button(self.resize, text="Enable", command=self.action_resize_on)
self.resize_on.grid(row=2, column=1, sticky=NSEW, padx=2, pady=2)
self.resize_scene_1 = ttk.Button(self.resize, text="Scene 1", command=self.action_resize_scene_1)
self.resize_scene_1.grid(row=3, column=0, sticky=NSEW, padx=2, pady=2)
self.resize_scene_2 = ttk.Button(self.resize, text="Scene 2", command=self.action_resize_scene_2)
self.resize_scene_2.grid(row=3, column=1, sticky=NSEW, padx=2, pady=2)
self.resize_scene_3 = ttk.Button(self.resize, text="Scene 3", command=self.action_resize_scene_3)
self.resize_scene_3.grid(row=4, column=0, sticky=NSEW, padx=2, pady=2)
self.resize_scene_4 = ttk.Button(self.resize, text="Scene 4", command=self.action_resize_scene_4)
self.resize_scene_4.grid(row=4, column=1, sticky=NSEW, padx=2, pady=2)
@api_action
def action_resize_on(self):
spiceapi.image_resize_enable(self.app.connection, True)
@api_action
def action_resize_false(self):
spiceapi.image_resize_enable(self.app.connection, False)
@api_action
def action_resize_scene_1(self):
spiceapi.image_resize_set_scene(self.app.connection, 1)
@api_action
def action_resize_scene_2(self):
spiceapi.image_resize_set_scene(self.app.connection, 2)
@api_action
def action_resize_scene_3(self):
spiceapi.image_resize_set_scene(self.app.connection, 3)
@api_action
def action_resize_scene_4(self):
spiceapi.image_resize_set_scene(self.app.connection, 4)
class MainApp(ttk.Frame): class MainApp(ttk.Frame):
"""The main application frame.""" """The main application frame."""
@@ -419,6 +478,8 @@ class MainApp(ttk.Frame):
self.tabs.add(self.tab_analogs, text="Analogs") self.tabs.add(self.tab_analogs, text="Analogs")
self.tab_lights = LightsTab(self, self.tabs) self.tab_lights = LightsTab(self, self.tabs)
self.tabs.add(self.tab_lights, text="Lights") self.tabs.add(self.tab_lights, text="Lights")
self.tab_resize = ResizeTab(self, self.tabs)
self.tabs.add(self.tab_resize, text="Resize")
self.tab_manual = ManualTab(self, self.tabs) self.tab_manual = ManualTab(self, self.tabs)
self.tabs.add(self.tab_manual, text="Manual") self.tabs.add(self.tab_manual, text="Manual")
self.tabs.pack(expand=True, fill=tk.BOTH) self.tabs.pack(expand=True, fill=tk.BOTH)
+1 -1
View File
@@ -471,7 +471,7 @@ namespace avs::automap {
ENABLED = true; ENABLED = true;
// check if optional imports are supported for this avs version // check if optional imports are supported for this avs version
if (!avs::core::property_node_read) { if (!avs::core::property_node_read || !avs::core::property_get_error) {
log_fatal("automap", "missing optional avs imports which are required for this module to work"); log_fatal("automap", "missing optional avs imports which are required for this module to work");
} }
+28 -4
View File
@@ -334,8 +334,12 @@ namespace avs {
.avs_fs_close = "XC0bbe97000119", .avs_fs_close = "XC0bbe97000119",
.avs_fs_dump_mountpoint = "XC0bbe970000c5", .avs_fs_dump_mountpoint = "XC0bbe970000c5",
.avs_fs_mount = "XC0bbe97000099", .avs_fs_mount = "XC0bbe97000099",
.avs_fs_fstat = "XC0bbe970000ce",
.avs_fs_lstat = "XC0bbe9700005f", .avs_fs_lstat = "XC0bbe9700005f",
.avs_fs_read = "XC0bbe97000134",
.avs_fs_opendir = "XC0bbe970000db", .avs_fs_opendir = "XC0bbe970000db",
.property_file_write = "property_file_write", // not found
.property_get_error = "property_get_error", // not found
.avs_net_add_protocol = "XC0bbe97000124", .avs_net_add_protocol = "XC0bbe97000124",
.avs_net_del_protocol = "XC0bbe97000026", .avs_net_del_protocol = "XC0bbe97000026",
.avs_net_addrinfobyaddr = "XC0bbe970000a4", .avs_net_addrinfobyaddr = "XC0bbe970000a4",
@@ -1360,10 +1364,12 @@ namespace avs {
fclose(file); fclose(file);
// error checking // error checking
if (avs::core::property_get_error) {
auto err = avs::core::property_get_error(property); auto err = avs::core::property_get_error(property);
if (err > 0) { if (err > 0) {
log_fatal("avs-core", "failed to read config file ({}): {}", filename, error_str(err)); log_fatal("avs-core", "failed to read config file ({}): {}", filename, error_str(err));
} }
}
// return value // return value
return property; return property;
@@ -1467,7 +1473,7 @@ namespace avs {
} }
} }
bool load_dll() { void load_dll() {
log_info("avs-core", "loading DLL"); log_info("avs-core", "loading DLL");
// detect DLL name // detect DLL name
@@ -1479,12 +1485,30 @@ namespace avs {
#else #else
DLL_NAME = "libavs-win32.dll"; DLL_NAME = "libavs-win32.dll";
#endif #endif
if (!fileutils::file_exists(MODULE_PATH / DLL_NAME)) {
std::string info_str { fmt::format(
"\n\n"
"Failed to find critical avs DLL on disk (avs2-core.dll OR {})\n"
"Looked in the following directory: {}\n"
"\n"
"One of these is required to boot the game. Spice found neither of them. You do not need both, just one, next to your game DLL.\n"
"\n"
"HOW TO FIX:\n"
" * Avoid manually specifying DLL path (-exec) and module directory (-modules); let spice2x auto-detect unless you have a good reason not to\n"
" * Ensure you do NOT have multiple copies of the game DLLs (e.g., in contents and in contents\\modules)\n"
" * It's also possible that you have incomplete game data\n"
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
"\n"
, DLL_NAME, MODULE_PATH.string()) };
log_fatal("avs-ea3", "{}", info_str);
}
} }
// load library // load library
DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME, false); DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME);
if (!DLL_INSTANCE) { if (!DLL_INSTANCE) {
return false; return;
} }
// check by version string if obtained // check by version string if obtained
@@ -1709,7 +1733,7 @@ namespace avs {
} }
// success // success
return true; return;
} }
static void create_dir( static void create_dir(
+2 -1
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <windows.h> #include <windows.h>
@@ -448,7 +449,7 @@ namespace avs {
// functions // functions
void set_default_heap_size(const std::string &dll_name); void set_default_heap_size(const std::string &dll_name);
void create_log(); void create_log();
bool load_dll(); void load_dll();
void boot(); void boot();
void copy_defaults(); void copy_defaults();
void shutdown(); void shutdown();
+18
View File
@@ -133,6 +133,24 @@ namespace avs {
#else #else
DLL_NAME = "libavs-win32-ea3.dll"; DLL_NAME = "libavs-win32-ea3.dll";
#endif #endif
if (!fileutils::file_exists(MODULE_PATH / DLL_NAME)) {
std::string info_str { fmt::format(
"\n\n"
"Failed to find critical ea3 DLL on disk (avs2-ea3.dll OR {})\n"
"Looked in the following directory: {}\n"
"\n"
"One of these is required to boot the game. Spice found neither of them. You do not need both, just one, next to your game DLL.\n"
"\n"
"HOW TO FIX:\n"
" * Avoid manually specifying DLL path (-exec) and module directory (-modules); let spice2x auto-detect unless you have a good reason not to\n"
" * Ensure you do NOT have multiple copies of the game DLLs (e.g., in contents and in contents\\modules)\n"
" * It's also possible that you have incomplete game data\n"
" * Do NOT copy over random DLLs from another game installation; DLL must match game version\n"
"\n"
, DLL_NAME, MODULE_PATH.string()) };
log_fatal("avs-ea3", "{}", info_str);
}
} }
// load library // load library
+29 -2
View File
@@ -72,8 +72,35 @@ namespace avs {
log_info("avs-game", "loading DLL '{}'", DLL_NAME); log_info("avs-game", "loading DLL '{}'", DLL_NAME);
// load game instance // load game instance
if (fileutils::verify_header_pe(MODULE_PATH / DLL_NAME)) { const auto dll_path = MODULE_PATH / DLL_NAME;
DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME); const auto dll_path_s = dll_path.string();
log_info("avs-game", "DLL path: {}", dll_path_s.c_str());
// MAX_PATH is 260
if (130 <= dll_path_s.length()) {
log_warning(
"avs-game",
"PATH TOO LONG WARNING\n\n\n"
"-------------------------------------------------------------------\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
" PATH TOO LONG \n"
"WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING\n"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
"The path '{}'\n"
" has a length of {}\n"
"Most of these games may behave unexpectedly when the path is too\n"
"long, often resulting in random crashes. Move the game contents to\n"
"a directory with shorter path.\n"
"-------------------------------------------------------------------\n\n",
dll_path_s, dll_path_s.length());
}
if (!fileutils::file_exists(dll_path)) {
log_warning("avs-game", "game DLL could not be found on disk: {}", dll_path.string().c_str());
log_warning("avs-game", "double check -exec and -modules parameters; unless you know what you're doing, leave them blank");
}
if (fileutils::verify_header_pe(dll_path)) {
DLL_INSTANCE = libutils::load_library(dll_path);
} }
// load entry points // load entry points
Regular → Executable
+66 -41
View File
@@ -19,6 +19,25 @@ function trap_error_exit {
trap trap_error_dbg DEBUG trap trap_error_dbg DEBUG
trap trap_error_exit EXIT trap trap_error_exit EXIT
IGNORE_CACHE=0
# Parse options
while getopts "ih" opt; do
case $opt in
i) IGNORE_CACHE=1 ;;
h)
echo "Usage: $0 [-i]"
echo " -i: Ignore build cache"
echo " -h: Show this help"
exit 0
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
# settings # settings
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none") GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
GIT_HEAD=$(git rev-parse HEAD || echo "none") GIT_HEAD=$(git rev-parse HEAD || echo "none")
@@ -41,8 +60,8 @@ DIST_ENABLE=1
DIST_FOLDER="./dist" DIST_FOLDER="./dist"
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip" DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.' DIST_COMMENT=${DIST_NAME}$'\n'"$GIT_BRANCH - $GIT_HEAD"$'\nThank you for playing.'
TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice" TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa"
TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_spice64" TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
# determine build type # determine build type
BUILD_TYPE="Release" BUILD_TYPE="Release"
@@ -57,7 +76,7 @@ then
fi fi
# determine number of cores # determine number of cores
CORES=$(awk '/^processor\t/ {cores[$NF]++} END {print length(cores)}' /proc/cpuinfo) CORES=$(nproc)
# print information # print information
echo "" echo ""
@@ -74,34 +93,45 @@ echo "Build Type: $BUILD_TYPE"
echo "Cores: $CORES" echo "Cores: $CORES"
echo "" echo ""
# 32 bit if ((IGNORE_CACHE > 0))
echo "Building 32bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
then then
echo "Ignoring build cache..."
else
export CCACHE_DIR="$(pwd)/.ccache"
export CMAKE_CXX_COMPILER_LAUNCHER=ccache
export CMAKE_C_COMPILER_LAUNCHER=ccache
fi
time (
# 32 bit
echo "Building 32bit targets..."
echo "========================="
if ((CLEAN_BUILD > 0))
then
rm -rf ${BUILDDIR_32} rm -rf ${BUILDDIR_32}
fi fi
mkdir -p ${BUILDDIR_32} mkdir -p ${BUILDDIR_32}
pushd ${BUILDDIR_32} > /dev/null pushd ${BUILDDIR_32} > /dev/null
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_32} cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
popd > /dev/null popd > /dev/null
# 64 bit # 64 bit
echo "" echo ""
echo "Building 64bit targets..." echo "Building 64bit targets..."
echo "=========================" echo "========================="
if ((CLEAN_BUILD > 0)) if ((CLEAN_BUILD > 0))
then then
rm -rf ${BUILDDIR_64} rm -rf ${BUILDDIR_64}
fi fi
mkdir -p ${BUILDDIR_64} mkdir -p ${BUILDDIR_64}
pushd ${BUILDDIR_64} > /dev/null pushd ${BUILDDIR_64} > /dev/null
cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_64} cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
popd > /dev/null popd > /dev/null
echo "" echo ""
echo "Compilation process done :)" echo "Compilation process done :)"
echo "===========================" echo "==========================="
)
# generate PDBs # generate PDBs
if false # ((DEBUG > 0)) if false # ((DEBUG > 0))
@@ -152,7 +182,8 @@ echo "Copy files to output directory..."
rm -rf ${OUTDIR} rm -rf ${OUTDIR}
mkdir -p ${OUTDIR} mkdir -p ${OUTDIR}
#mkdir -p ${OUTDIR}/stubs/32 #mkdir -p ${OUTDIR}/stubs/32
#mkdir -p ${OUTDIR}/stubs/64 mkdir -p ${OUTDIR}/stubs/64
mkdir -p ${OUTDIR}/extras/largeaddressaware
if false # ((DEBUG > 0)) if false # ((DEBUG > 0))
then then
# debug files # debug files
@@ -166,28 +197,22 @@ then
cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/spice64-pdb.pdb ${OUTDIR} 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
else else
# release files # release files
cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null cp ${BUILDDIR_32}/spicetools/spicecfg.exe ${OUTDIR} 2>/dev/null
cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null cp ${BUILDDIR_32}/spicetools/32/spice.exe ${OUTDIR} 2>/dev/null
cp ${BUILDDIR_32}/spicetools/32/spice_laa.exe ${OUTDIR}/extras/largeaddressaware/spice.exe 2>/dev/null
#cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null #cp ${BUILDDIR_32}/spicetools/32/kbt.dll ${OUTDIR}/stubs/32 2>/dev/null
#cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null #cp ${BUILDDIR_32}/spicetools/32/kld.dll ${OUTDIR}/stubs/32 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null cp ${BUILDDIR_64}/spicetools/64/spice64.exe ${OUTDIR} 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/kbt.dll ${OUTDIR}/stubs/64 2>/dev/null
#cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null #cp ${BUILDDIR_64}/spicetools/64/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
fi cp ${BUILDDIR_64}/spicetools/64/nvEncodeAPI64.dll ${OUTDIR}/stubs/64 2>/dev/null
cp ${BUILDDIR_64}/spicetools/64/nvcuda.dll ${OUTDIR}/stubs/64 2>/dev/null
# compress using UPX cp ${BUILDDIR_64}/spicetools/64/nvcuvid.dll ${OUTDIR}/stubs/64 2>/dev/null
if ((UPX_ENABLE > 0 && DEBUG == 0))
then
echo "Processing files with UPX..."
upx ${UPX_FLAGS} ${OUTDIR}/spicecfg.exe > /dev/null
upx ${UPX_FLAGS} ${OUTDIR}/spice.exe > /dev/null
#upx ${UPX_FLAGS} ${OUTDIR}/stubs/32/kbt.dll > /dev/null
#upx ${UPX_FLAGS} ${OUTDIR}/stubs/32/kld.dll > /dev/null
upx ${UPX_FLAGS} ${OUTDIR}/spice64.exe > /dev/null
#upx ${UPX_FLAGS} ${OUTDIR}/stubs/64/kbt.dll > /dev/null
#upx ${UPX_FLAGS} ${OUTDIR}/stubs/64/kld.dll > /dev/null
fi fi
# pack source files to output directory # pack source files to output directory
@@ -196,7 +221,7 @@ mkdir -p ${OUTDIR}/src
if ((INCLUDE_SRC > 0)) if ((INCLUDE_SRC > 0))
then then
echo "Generating source file archive..." echo "Generating source file archive..."
git archive --format tar.gz --prefix=spice2x/ HEAD > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \ git archive --format tar.gz --prefix=spice2x/ HEAD ./ > ${OUTDIR}/src/spice2x-${GIT_BRANCH}.tar.gz 2>/dev/null || \
echo "WARNING: Couldn't get git to create the archive. Is this a git repository?" echo "WARNING: Couldn't get git to create the archive. Is this a git repository?"
fi fi
+2 -2
View File
@@ -1,6 +1,6 @@
docker build --pull external/docker -t spicetools/deps docker build --pull external/docker -t spicetools/deps
docker build . -t spicetools/spice --no-cache docker build --build-context gitroot=%cd%/../../.git . -t spicetools/spice
docker run --rm -it -v %cd%/dist:/src/dist -v %cd%/bin:/src/bin spicetools/spice docker run --rm -it -v %cd%/dist:/src/src/spice2x/dist -v %cd%/bin:/src/src/spice2x/bin -v %cd%/.ccache:/src/src/spice2x/.ccache spicetools/spice %*
@REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below @REM to generate PDBs, set DEBUG to 1 in build_all.sh, place cv2pdb in external\cv2pdb, and run below
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb @REM external\cv2pdb\cv2pdb.exe bin\spice2x\spicecfg.exe bin\spice2x\spicecfg-pdb.exe bin\spice2x\spicecfg-pdb.pdb
@REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb @REM external\cv2pdb\cv2pdb.exe bin\spice2x\spice.exe bin\spice2x\spice-pdb.exe bin\spice2x\spice-pdb.pdb
Regular → Executable
+2 -4
View File
@@ -1,6 +1,4 @@
#!/bin/bash #!/bin/bash
export DOCKER_BUILDKIT=0
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64 docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
docker build . -t spicetools/spice:latest --no-cache docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
docker run --rm -it -v $PWD/dist:/src/dist -v $PWD/bin:/src/bin spicetools/spice docker run --rm -v $PWD/dist:/src/src/spice2x/dist -v $PWD/bin:/src/src/spice2x/bin -v $PWD/.ccache:/src/src/spice2x/.ccache spicetools/spice "$@"
-85
View File
@@ -1,85 +0,0 @@
from typing import Dict, List
import argparse
import os
import subprocess
def get_vs_installation_path() -> str:
program_files_x86 = os.environ["ProgramFiles(x86)"]
process = subprocess.run(
[f"{program_files_x86}\\Microsoft Visual Studio\\Installer\\vswhere.exe", "-prerelease", "-latest", "-property",
"installationPath"],
capture_output=True, check=True, encoding="utf-8")
return process.stdout.strip()
def source_bat(bat_file: str, arch: str) -> Dict[str, str]:
interesting = {"INCLUDE", "LIB", "LIBPATH", "PATH"}
result = {}
process = subprocess.Popen(f"\"{bat_file}\" {arch} & set", stdout=subprocess.PIPE, shell=True, encoding="utf-8")
(out, err) = process.communicate()
if err is not None:
raise Exception(err)
for line in out.split("\n"):
if '=' not in line:
continue
key, value = line.strip().split('=', 1)
key = key.upper()
if key in interesting:
result[key] = value
return result
def run_build(build_dir: str, build_type: str, build_targets: List[str], build_env: Dict[str, str]):
os.makedirs(build_dir, exist_ok=True)
subprocess.check_call(["cmake.exe", f"-DCMAKE_BUILD_TYPE={build_type}", "-GNinja", ".."], cwd=build_dir,
env=build_env, shell=True)
subprocess.check_call(["ninja.exe"] + build_targets, cwd=build_dir, env=build_env, shell=True)
def main():
parser = argparse.ArgumentParser(description='Build SpiceTools with MSVC')
parser.add_argument('--build-type', type=str, default='Release', help='CMake build type')
args = parser.parse_args()
build_dir = args.build_type.lower()
parent_env = {key: os.environ[key] for key in os.environ}
vs_installation_path = get_vs_installation_path()
bat_file = f"{vs_installation_path}\\VC\\Auxiliary\\Build\\vcvarsall.bat"
print(bat_file)
# parent_env["CC"] = "clang"
# parent_env["CXX"] = "clang++"
print("Building spice64")
env_64 = source_bat(bat_file, "x64")
env_64_merged = parent_env.copy()
env_64_merged.update(env_64)
run_build(f"cmake-build-{build_dir}-64", args.build_type,
["spicetools_stubs_kbt64", "spicetools_stubs_kld64", "spicetools_spice64"], env_64_merged)
print("Building spice")
env_32 = source_bat(bat_file, "x86")
env_32_merged = parent_env.copy()
env_32_merged.update(env_32)
run_build(f"cmake-build-{build_dir}-32", args.build_type,
["spicetools_stubs_kbt", "spicetools_stubs_kld", "spicetools_cfg", "spicetools_spice"], env_32_merged)
if __name__ == "__main__":
main()
+2
View File
@@ -0,0 +1,2 @@
del /s /q .ccache
call build_docker.bat
+121 -7
View File
@@ -55,20 +55,39 @@ std::string Analog::getDisplayString(rawinput::RawInputManager *manager) {
} }
case rawinput::MIDI: { case rawinput::MIDI: {
auto midi = device->midiInfo; auto midi = device->midiInfo;
// update strings in button.cpp as well
if (index < midi->controls_precision.size()) { if (index < midi->controls_precision.size()) {
return "MIDI PREC " + indexString + " (" + device->desc + ")"; const int channel = (index / 32) + 1;
const int cc_index = (index % 32);
return fmt::format("MIDI Prec Ctrl Ch.{} CC#{} ({})", channel, cc_index, device->desc);
} else if (index < midi->controls_precision.size() + midi->controls_single.size()) { } else if (index < midi->controls_precision.size() + midi->controls_single.size()) {
return "MIDI CTRL " + indexString + " (" + device->desc + ")"; const int index_rel = index - midi->controls_precision.size();
const int channel = (index_rel / 44) + 1;
int cc_index = (index_rel % 44);
if (cc_index < 26) {
cc_index += 0x46; // single byte range
} else {
cc_index = cc_index - 26 + 0x66; // undefined single byte range
}
return fmt::format("MIDI Ctrl Ch.{} CC#{} ({})", channel, cc_index, device->desc);
} else if (index < midi->controls_precision.size() + midi->controls_single.size() } else if (index < midi->controls_precision.size() + midi->controls_single.size()
+ midi->controls_onoff.size()) + midi->controls_onoff.size())
{ {
return "MIDI ONOFF " + indexString + " (" + device->desc + ")"; const int index_rel = index - midi->controls_precision.size() - midi->controls_single.size();
} else if (index == midi->controls_precision.size() + midi->controls_single.size() const int channel = (index_rel / 6) + 1;
+ midi->controls_onoff.size()) const int cc_index = (index_rel % 6) + 0x40;
return fmt::format("MIDI OnOff Ch.{} CC#{} ({})", channel, cc_index, device->desc);
} else if (index <
midi->controls_precision.size() + midi->controls_single.size() + midi->controls_onoff.size() + midi->pitch_bend.size())
{ {
return "MIDI Pitch Bend (" + device->desc + ")"; const int index_rel =
index -
midi->controls_precision.size() -
midi->controls_single.size() -
midi->controls_onoff.size();
return fmt::format("MIDI Pitch Ch.{} ({})", index_rel + 1, device->desc);
} else { } else {
return "MIDI Unknown " + indexString + " (" + device->desc + ")"; return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
} }
} }
case rawinput::DESTROYED: case rawinput::DESTROYED:
@@ -158,3 +177,98 @@ float Analog::normalizeAngle(float rads) {
} }
return angle; return angle;
} }
float Analog::applyMultiplier(float value) {
if (1 < this->multiplier) {
// multiplier - just multiply the value and take the decimal part
return normalizeAnalogValue(value * this->multiplier);
} else if (this->multiplier < -1) {
const unsigned short number_of_divisions = -this->multiplier;
// divisor - need to take care of over/underflow
if (0.75f < this->divisor_previous_value && value < 0.25f) {
this->divisor_region = (this->divisor_region + 1) % number_of_divisions;
} else if (this->divisor_previous_value < 0.25f && 0.75f < value) {
if (1 <= this->divisor_region) {
this->divisor_region -= 1;
} else {
this->divisor_region = number_of_divisions - 1;
}
}
this->divisor_previous_value = value;
return ((float)this->divisor_region + value) / (float)number_of_divisions;
} else {
// multiplier in [-1, 1] range is just treated as 1
return value;
}
}
float Analog::normalizeAnalogValue(float value) {
// effectively the same as fmodf(value, 1.f)
// for small values, this is MUCH faster than fmodf.
float new_value = value;
while (new_value > 1.f) {
new_value -= 1.f;
}
while (new_value < 0.f) {
new_value += 1.f;
}
return new_value;
}
float Analog::applyDeadzone(float raw_value) {
float value = raw_value;
const auto deadzone = this->getDeadzone();
if (deadzone > 0) {
// calculate values
const auto delta = value - 0.5f;
const auto dtlen = 1.f - deadzone;
// check mirror
if (this->getDeadzoneMirror()) {
// deadzone on the edges
if (dtlen != 0.f) {
value = std::max(0.f, std::min(1.f, 0.5f + (delta / dtlen)));
} else {
value = 0.5f;
}
} else {
// deadzone around the middle
const auto limit = deadzone * 0.5f;
if (dtlen != 0.f) {
if (delta > limit) {
value = std::min(1.f, 0.5f + std::max(0.f, (delta - limit) / dtlen));
} else if (delta < -limit) {
value = std::max(0.f, 0.5f + std::min(0.f, (delta + limit) / dtlen));
} else {
value = 0.5f;
}
} else {
value = 0.5f;
}
}
} else if (deadzone < 0) {
// invert for mirror
if (this->getDeadzoneMirror()) {
value = 1.f - value;
}
// deadzone from minimum value
if (deadzone > -1 && value > -deadzone) {
value = std::min(1.f, (value + deadzone) / (1.f + deadzone));
} else {
value = 0.f;
}
// revert value for mirror
if (this->getDeadzoneMirror()) {
value = 1.f - value;
}
}
return value;
}
+58 -1
View File
@@ -3,6 +3,7 @@
#include <array> #include <array>
#include <string> #include <string>
#include <cmath> #include <cmath>
#include <queue>
#define ANALOG_HISTORY_CNT 10 #define ANALOG_HISTORY_CNT 10
#define M_TAU (2 * M_PI) #define M_TAU (2 * M_PI)
@@ -22,7 +23,7 @@ class Analog {
private: private:
std::string name; std::string name;
std::string device_identifier = ""; std::string device_identifier = "";
unsigned short index = 0xFF; unsigned short index = USHRT_MAX;
float sensitivity = 1.f; float sensitivity = 1.f;
float deadzone = 0.f; float deadzone = 0.f;
bool deadzone_mirror = false; bool deadzone_mirror = false;
@@ -41,8 +42,22 @@ private:
float previous_raw_rads = 0.f; float previous_raw_rads = 0.f;
float adjusted_rads = 0.f; float adjusted_rads = 0.f;
// multiplier/divisor
int multiplier = 1;
float divisor_previous_value = 0.5f;
unsigned short divisor_region = 0;
// relative input mode
float absolute_value_for_rel_mode = 0.5f;
bool relative_mode = false;
// circular buffer (delayed input)
int delay_buffer_depth = 0;
std::queue<float> delay_buffer;
float calculateAngularDifference(float old_rads, float new_rads); float calculateAngularDifference(float old_rads, float new_rads);
float normalizeAngle(float rads); float normalizeAngle(float rads);
float normalizeAnalogValue(float value);
public: public:
@@ -57,6 +72,8 @@ public:
std::string getDisplayString(rawinput::RawInputManager* manager); std::string getDisplayString(rawinput::RawInputManager* manager);
float getSmoothedValue(float raw_rads); float getSmoothedValue(float raw_rads);
float applyAngularSensitivity(float raw_rads); float applyAngularSensitivity(float raw_rads);
float applyMultiplier(float raw_value);
float applyDeadzone(float raw_value);
inline bool isSet() { inline bool isSet() {
if (this->override_enabled) { if (this->override_enabled) {
@@ -72,6 +89,9 @@ public:
setDeadzone(0.f); setDeadzone(0.f);
invert = false; invert = false;
smoothing = false; smoothing = false;
setMultiplier(1);
setRelativeMode(false);
setDelayBufferDepth(0);
} }
inline const std::string &getName() const { inline const std::string &getName() const {
@@ -144,6 +164,16 @@ public:
this->smoothing = smoothing; this->smoothing = smoothing;
} }
inline int getMultiplier() const {
return this->multiplier;
}
inline void setMultiplier(int multiplier) {
this->multiplier = multiplier;
this->divisor_region = 0;
this->divisor_previous_value = 0.5f;
}
inline float getLastState() const { inline float getLastState() const {
return this->last_state; return this->last_state;
} }
@@ -151,4 +181,31 @@ public:
inline void setLastState(float last_state) { inline void setLastState(float last_state) {
this->last_state = last_state; this->last_state = last_state;
} }
inline bool isRelativeMode() const {
return this->relative_mode;
}
inline void setRelativeMode(bool relative_mode) {
this->relative_mode = relative_mode;
this->absolute_value_for_rel_mode = 0.5f;
}
inline float getAbsoluteValue(float relative_delta) {
this->absolute_value_for_rel_mode =
normalizeAnalogValue(this->absolute_value_for_rel_mode + relative_delta);
return this->absolute_value_for_rel_mode;
}
inline int getDelayBufferDepth() const {
return this->delay_buffer_depth;
}
inline void setDelayBufferDepth(int depth) {
this->delay_buffer_depth = depth;
}
inline std::queue<float> &getDelayBuffer() {
return this->delay_buffer;
}
}; };
+256 -90
View File
@@ -17,6 +17,8 @@ std::vector<Button> GameAPI::Buttons::getButtons(Game *game) {
return Config::getInstance().getButtons(game); return Config::getInstance().getButtons(game);
} }
static Buttons::State getMidiV2ButtonState(float last_on_time, float last_off_time);
std::vector<Button> GameAPI::Buttons::sortButtons( std::vector<Button> GameAPI::Buttons::sortButtons(
const std::vector<Button> &buttons, const std::vector<Button> &buttons,
const std::vector<std::string> &button_names, const std::vector<std::string> &button_names,
@@ -161,14 +163,17 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
break; break;
} }
case BAT_NEGATIVE: case BAT_NEGATIVE:
case BAT_POSITIVE: { case BAT_POSITIVE:
case BAT_ANY: {
auto value_states = &hid->value_states; auto value_states = &hid->value_states;
if (vKey < value_states->size()) { if (vKey < value_states->size()) {
auto value = value_states->at(vKey); auto value = value_states->at(vKey);
if (current_button->getAnalogType() == BAT_POSITIVE) { if (current_button->getAnalogType() == BAT_POSITIVE) {
state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = value > 0.6f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else { } else if (current_button->getAnalogType() == BAT_NEGATIVE) {
state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = value < 0.4f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else {
state = value > 0.01f ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} }
} else { } else {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
@@ -216,8 +221,21 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
auto midi = device->midiInfo; auto midi = device->midiInfo;
switch (bat) { switch (bat) {
case BAT_NONE: { case BAT_NONE: {
if (vKey < 16 * 128) { if (rawinput::get_midi_algorithm() == rawinput::MidiNoteAlgorithm::LEGACY) {
// spicetools legacy midi logic: use event log
//
// drums send NOTE_ON and NOTE_OFF in rapid succession, before game engine has a chance
// to poll for it - to address this, keep a counter (states_events array) and the last
// state (states array), incrementing the states_events on rising edges (NOTE_ON)
// and popping events off the queue every time it's checked.
//
// if the same drum pad is mapped to multiple buttons, multiple issues arise:
// 1. we run through this logic for each button, which consumes an event every time;
// therefore, the first button may see the ON event, but subsequent mappings may
// completely miss it as it already has been drained
// 2. it is impossible to implement velocity threshold with this logic since the
// velocity is a per-note value that goes away as soon as NOTE_OFF is detected
if (vKey < midi->states_events.size()) {
// check for event // check for event
auto midi_event = midi->states_events[vKey]; auto midi_event = midi->states_events[vKey];
if (midi_event) { if (midi_event) {
@@ -226,40 +244,112 @@ GameAPI::Buttons::State GameAPI::Buttons::getState(rawinput::RawInputManager *ma
state = (midi_event % 2) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = (midi_event % 2) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
// update event // update event
if (!midi->states[vKey] || midi_event > 1) if (!midi->states[vKey] || midi_event > 1) {
midi->states_events[vKey]--; midi->states_events[vKey]--;
}
} else } else {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
} }
}
} else {
// spice2x midi logic (new!)
//
// for every MIDI NOTE ON message, latch the "on" for a certain time, even if NOTE
// OFF message is seen immediately afterwards.
//
// each ON event is held long enough for the game's input poll to see it (e.g., gitadora
// polls every 16ms or so, rawinput holds it for 20ms by default)
//
// this is much simpler and does not have the issues mentioned above for the legacy
// logic, however the downside is that there is a risk of coalescing rapid inputs into
// one.
//
// that being said:
// * default value of 20ms should be reasonable; humans can't realistically hit the
// same note faster than this; in fact it's likely to be a misfire
// * we can tweak it per-game if needed to suit the game's polling period (in the
// future)
// * as a last resort the user can always override it via the option (MidiNoteSustain)
if (vKey < midi->v2_last_on_time.size()) {
// take the velocity threshold from first button binding we encounter here
// this hardware key may be mapped to multiple bindings, but the UI should keep them
// the same value, as only one threshold value can be set per MIDI key
// (otherwise it makes the sustain logic too complicated)
const auto sw_threshold = current_button->getVelocityThreshold();
if (0 < sw_threshold && !midi->v2_velocity_threshold_set_on_device[vKey]) {
midi->v2_velocity_threshold_set_on_device[vKey] = true;
midi->v2_velocity_threshold[vKey] = sw_threshold;
}
state = getMidiV2ButtonState(
midi->v2_last_on_time[vKey],
midi->v2_last_off_time[vKey]);
} else {
state = BUTTON_NOT_PRESSED;
}
}
break; break;
} }
case BAT_MIDI_CTRL_PRECISION: { case BAT_MIDI_CTRL_PRECISION: {
if (vKey < 16 * 32) if (vKey < midi->controls_precision.size()) {
if (rawinput::get_midi_algorithm() == rawinput::MidiNoteAlgorithm::LEGACY) {
state = midi->controls_precision[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = midi->controls_precision[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else } else {
// not using getVelocityHelper here to avoid locking and other checks
const auto v = device->midiInfo->controls_precision[vKey];
// velocity threshold ranges from [0, 127], so do some math for double precision
const auto threshold = (current_button->getVelocityThreshold() << 7u) | 0x7f;
state = (threshold < v) ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
} else {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
}
break; break;
} }
case BAT_MIDI_CTRL_SINGLE: { case BAT_MIDI_CTRL_SINGLE: {
if (vKey < 16 * 44) if (vKey < midi->controls_single.size()) {
if (rawinput::get_midi_algorithm() == rawinput::MidiNoteAlgorithm::LEGACY) {
state = midi->controls_single[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = midi->controls_single[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else } else {
// not using getVelocityHelper here to avoid locking and other checks
const auto v = device->midiInfo->controls_single[vKey];
state = (current_button->getVelocityThreshold() < v) ?
BUTTON_PRESSED : BUTTON_NOT_PRESSED;
}
} else {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
}
break; break;
} }
case BAT_MIDI_CTRL_ONOFF: { case BAT_MIDI_CTRL_ONOFF: {
if (vKey < 16 * 6) if (vKey < midi->controls_onoff.size()) {
if (rawinput::get_midi_algorithm() == rawinput::MidiNoteAlgorithm::LEGACY) {
state = midi->controls_onoff[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; state = midi->controls_onoff[vKey] ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
else } else {
state = getMidiV2ButtonState(
midi->v2_controls_onoff_last_on_time[vKey],
midi->v2_controls_onoff_last_off_time[vKey]);
}
} else {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
}
break; break;
} }
case BAT_MIDI_PITCH_DOWN: case BAT_MIDI_PITCH_DOWN:
state = midi->pitch_bend < 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; if (vKey < midi->pitch_bend.size()) {
state = midi->pitch_bend[vKey] < 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else {
state = BUTTON_NOT_PRESSED;
}
break; break;
case BAT_MIDI_PITCH_UP: case BAT_MIDI_PITCH_UP:
state = midi->pitch_bend > 0x2000 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED; if (vKey < midi->pitch_bend.size()) {
state = midi->pitch_bend[vKey] > 0 ? BUTTON_PRESSED : BUTTON_NOT_PRESSED;
} else {
state = BUTTON_NOT_PRESSED;
}
break; break;
default: { default: {
state = BUTTON_NOT_PRESSED; state = BUTTON_NOT_PRESSED;
@@ -372,24 +462,64 @@ static float getVelocityHelper(rawinput::RawInputManager *manager, Button &butto
device->mutex->lock(); device->mutex->lock();
// determine velocity based on device type // determine velocity based on device type
switch (device->type) { if (device->type == rawinput::MIDI) {
case rawinput::MIDI: { switch (button.getAnalogType()) {
case ButtonAnalogType::BAT_MIDI_CTRL_PRECISION:
if (vKey < device->midiInfo->controls_precision.size()) {
velocity = device->midiInfo->controls_precision[vKey] / 16383.f;
} else {
velocity = 0.f;
}
break;
// read control case ButtonAnalogType::BAT_MIDI_CTRL_SINGLE:
if (vKey < 16 * 128) { if (vKey < device->midiInfo->controls_single.size()) {
velocity = device->midiInfo->controls_single[vKey] / 127.f;
} else {
velocity = 0.f;
}
break;
case ButtonAnalogType::BAT_MIDI_CTRL_ONOFF:
if (vKey < device->midiInfo->controls_onoff.size()) {
velocity = device->midiInfo->controls_onoff[vKey] ? 1.f : 0.f;
} else {
velocity = 0.f;
}
break;
case ButtonAnalogType::BAT_MIDI_PITCH_DOWN:
if (vKey < device->midiInfo->pitch_bend.size()) {
velocity = device->midiInfo->pitch_bend[vKey] < 0 ? 1.f : 0.f;
} else {
velocity = 0.f;
}
break;
case ButtonAnalogType::BAT_MIDI_PITCH_UP:
if (vKey < device->midiInfo->pitch_bend.size()) {
// pitch range is [-8192, 8191]
velocity = (device->midiInfo->pitch_bend[vKey]) > 0 ? 1.f : 0.f;
} else {
velocity = 0.f;
}
break;
case ButtonAnalogType::BAT_NONE:
default:
// velocity sensitive
if (vKey < device->midiInfo->velocity.size()) {
velocity = (float) device->midiInfo->velocity[vKey] / 127.f; velocity = (float) device->midiInfo->velocity[vKey] / 127.f;
} else { } else {
velocity = 0.f; velocity = 0.f;
} }
break;
}
// invert // invert
if (button.getInvert()) { if (button.getInvert()) {
velocity = 1.f - velocity; velocity = 1.f - velocity;
} }
break;
}
default:
break;
} }
// unlock device // unlock device
@@ -490,6 +620,36 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
value = device->hidInfo->value_states[index]; value = device->hidInfo->value_states[index];
} }
// deadzone
if (analog.isDeadzoneSet()) {
value = analog.applyDeadzone(value);
}
if (analog.isRelativeMode()) {
float relative_delta = value - 0.5f;
// built-in scaling to make values reasonable
relative_delta /= 80.f;
// integer multiplier/divisor
const auto mult = analog.getMultiplier();
if (mult < -1) {
relative_delta /= -mult;
} else if (1 < mult) {
relative_delta *= mult;
}
// sensitivity (ranges from 0.0 to 4.0)
if (analog.isSensitivitySet()) {
relative_delta *= analog.getSensitivity();
}
// translate relative movement to absolute value
value = analog.getAbsoluteValue(relative_delta);
} else {
// integer multiplier
value = analog.applyMultiplier(value);
// smoothing/sensitivity // smoothing/sensitivity
if (analog.getSmoothing() || analog.isSensitivitySet()) { if (analog.getSmoothing() || analog.isSensitivitySet()) {
float rads = value * (float) M_TAU; float rads = value * (float) M_TAU;
@@ -514,6 +674,29 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
// apply to value // apply to value
value = rads * (float) M_1_TAU; value = rads * (float) M_1_TAU;
} }
}
// delay
if (0 < analog.getDelayBufferDepth()) {
auto& queue = analog.getDelayBuffer();
// ensure the queue isn't too long; drop old values
while (analog.getDelayBufferDepth() <= (int)queue.size()) {
queue.pop();
}
// always push new value
queue.push(value);
// get a new value to return
if ((int)queue.size() < analog.getDelayBufferDepth()) {
// not enough in the queue, stall for now, shouldn't happen often
value = analog.getLastState();
} else {
value = queue.front();
queue.pop();
}
}
break; break;
} }
@@ -524,6 +707,7 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
auto prec_count = (int) midi->controls_precision.size(); auto prec_count = (int) midi->controls_precision.size();
auto single_count = (int) midi->controls_single.size(); auto single_count = (int) midi->controls_single.size();
auto onoff_count = (int) midi->controls_onoff.size(); auto onoff_count = (int) midi->controls_onoff.size();
auto pitch_count = (int) midi->pitch_bend.size();
// decide on value // decide on value
if (index < prec_count) if (index < prec_count)
@@ -532,81 +716,19 @@ float GameAPI::Analogs::getState(rawinput::Device *device, Analog &analog) {
value = midi->controls_single[index - prec_count] / 127.f; value = midi->controls_single[index - prec_count] / 127.f;
else if (index < prec_count + single_count + onoff_count) else if (index < prec_count + single_count + onoff_count)
value = midi->controls_onoff[index - prec_count - single_count] ? 1.f : 0.f; value = midi->controls_onoff[index - prec_count - single_count] ? 1.f : 0.f;
else if (index == prec_count + single_count + onoff_count) else if (index < prec_count + single_count + onoff_count + pitch_count)
value = midi->pitch_bend / 16383.f; value = (midi->pitch_bend[index - prec_count - single_count - onoff_count] + 0x2000) / 16383.f;
// invert value // invert value
if (inverted) { if (inverted) {
value = 1.f - value; value = 1.f - value;
} }
}
default:
break;
}
// deadzone logic // deadzone
switch (device->type) {
case rawinput::HID:
case rawinput::MIDI: {
// check if set
if (analog.isDeadzoneSet()) { if (analog.isDeadzoneSet()) {
value = analog.applyDeadzone(value);
// check sign
auto deadzone = analog.getDeadzone();
if (deadzone > 0) {
// calculate values
auto delta = value - 0.5f;
auto dtlen = 1.f - deadzone;
// check mirror
if (analog.getDeadzoneMirror()) {
// deadzone on the edges
if (dtlen != 0.f) {
value = std::max(0.f, std::min(1.f, 0.5f + (delta / dtlen)));
} else {
value = 0.5f;
}
} else {
// deadzone around the middle
auto limit = deadzone * 0.5f;
if (dtlen != 0.f) {
if (delta > limit) {
value = std::min(1.f, 0.5f + std::max(0.f, (delta - limit) / dtlen));
} else if (delta < -limit) {
value = std::max(0.f, 0.5f + std::min(0.f, (delta + limit) / dtlen));
} else {
value = 0.5f;
}
} else {
value = 0.5f;
}
}
} else if (deadzone < 0) {
// invert for mirror
if (analog.getDeadzoneMirror()) {
value = 1.f - value;
}
// deadzone from minimum value
if (deadzone > -1 && value > -deadzone) {
value = std::min(1.f, (value + deadzone) / (1.f + deadzone));
} else {
value = 0.f;
}
// revert value for mirror
if (analog.getDeadzoneMirror()) {
value = 1.f - value;
}
}
} }
break;
} }
default: default:
break; break;
@@ -761,6 +883,7 @@ void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float valu
if (index < rawinput::SextetDevice::LIGHT_COUNT) { if (index < rawinput::SextetDevice::LIGHT_COUNT) {
device->sextetInfo->light_state[index] = value > 0; device->sextetInfo->light_state[index] = value > 0;
device->sextetInfo->push_light_state(); device->sextetInfo->push_light_state();
device->output_pending = true;
} else { } else {
log_warning("api", "invalid sextet light index: {}", index); log_warning("api", "invalid sextet light index: {}", index);
} }
@@ -769,11 +892,30 @@ void GameAPI::Lights::writeLight(rawinput::Device *device, int index, float valu
case rawinput::PIUIO_DEVICE: { case rawinput::PIUIO_DEVICE: {
if (index < rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS) { if (index < rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS) {
device->piuioDev->SetLight(index, value > 0); device->piuioDev->SetLight(index, value > 0);
device->output_pending = true;
} else { } else {
log_warning("api", "invalid piuio light index: {}", index); log_warning("api", "invalid piuio light index: {}", index);
} }
break; break;
} }
case rawinput::SMX_STAGE: {
if (index < rawinput::SmxStageDevice::TOTAL_LIGHT_COUNT) {
device->smxstageInfo->SetLightByIndex(index, static_cast<uint8_t>(value*255.f));
device->output_pending = true;
} else {
log_warning("api", "invalid smx stage light index: {}", index);
}
break;
}
case rawinput::SMX_DEDICAB: {
if (index < rawinput::SmxDedicabDevice::LIGHTS_COUNT) {
device->smxdedicabInfo->SetLightByIndex(index, static_cast<uint8_t>(value * 255.f));
device->output_pending = true;
} else {
log_warning("api", "invalid SMX dedicab light index: {}", index);
}
break;
}
default: default:
break; break;
} }
@@ -922,3 +1064,27 @@ void GameAPI::Options::sortOptions(std::vector<Option> &options, const std::vect
options = std::move(sorted); options = std::move(sorted);
} }
static Buttons::State getMidiV2ButtonState(float on, float off) {
if (on == 0.0) {
return Buttons::State::BUTTON_NOT_PRESSED;
} else if (off < on) {
// if OFF was not observed strictly after ON, we can confidently say that the note
// remains ON; in case of a tie (rarely in v2, all the time in v2_drum), prefer to keep note
// off since that's better than a note stuck on
return Buttons::State::BUTTON_PRESSED;
} else {
// otherwise, this is an ON-OFF sequence
// check for time the most recent ON message
//
// if recent, consider the button to be on - even if there were OFF messages following it
// this is needed to detect things like MIDI drums which send a quick ON-OFF sequence
// between the game's polling period
const auto now = get_performance_milliseconds();
if ((now - on) < (double)rawinput::MIDI_NOTE_SUSTAIN) {
return Buttons::State::BUTTON_PRESSED;
} else {
return Buttons::State::BUTTON_NOT_PRESSED;
}
}
}
+151 -13
View File
@@ -22,6 +22,7 @@ const char *ButtonAnalogTypeStr[] = {
"MIDI Control On/Off", "MIDI Control On/Off",
"MIDI Pitch Down", "MIDI Pitch Down",
"MIDI Pitch Up", "MIDI Pitch Up",
"Any Direction",
}; };
std::string Button::getVKeyString() { std::string Button::getVKeyString() {
@@ -267,6 +268,15 @@ std::string Button::getVKeyString() {
} }
} }
std::string Button::getMidiNoteString() {
static const std::string note_names[] = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
int channel;
int index;
this->getMidiVKey(channel, index);
return fmt::format("{}{}", note_names[index % 12], ((index / 12) - 1));
}
std::string Button::getDisplayString(rawinput::RawInputManager* manager) { std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
// get VKey string // get VKey string
@@ -317,8 +327,16 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
else else
return "Invalid button (" + device->desc + ")"; return "Invalid button (" + device->desc + ")";
case BAT_NEGATIVE: case BAT_NEGATIVE:
case BAT_POSITIVE: { case BAT_POSITIVE:
const char *sign = this->analog_type == BAT_NEGATIVE ? "-" : "+"; case BAT_ANY: {
const char *sign;
if (this->analog_type == BAT_NEGATIVE) {
sign = "-";
} else if (this->analog_type == BAT_POSITIVE) {
sign = "+";
} else {
sign = "*";
}
if (vKey < hid->value_caps_names.size()) { if (vKey < hid->value_caps_names.size()) {
return hid->value_caps_names[vKey] + sign + " (" + device->desc + ")"; return hid->value_caps_names[vKey] + sign + " (" + device->desc + ")";
} else { } else {
@@ -347,23 +365,34 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
return "Unknown analog type (" + device->desc + ")"; return "Unknown analog type (" + device->desc + ")";
} }
} }
case rawinput::MIDI: case rawinput::MIDI: {
int channel = 0;
int ctrl = 0;
this->getMidiVKey(channel, ctrl);
switch (this->analog_type) { switch (this->analog_type) {
case BAT_NONE: // update strings in analog.cpp as well
return "MIDI " + vKeyString + " (" + device->desc + ")"; case BAT_NONE: {
case BAT_MIDI_CTRL_PRECISION: const auto note = this->getMidiNoteString();
return "MIDI PREC " + vKeyString + " (" + device->desc + ")"; return fmt::format("MIDI Note Ch.{} #{} {} ({})", channel, ctrl, note, device->desc);
case BAT_MIDI_CTRL_SINGLE: }
return "MIDI CTRL " + vKeyString + " (" + device->desc + ")"; case BAT_MIDI_CTRL_PRECISION: {
case BAT_MIDI_CTRL_ONOFF: return fmt::format("MIDI Prec Ctrl Ch.{} CC#{} ({})", channel, ctrl, device->desc);
return "MIDI ONOFF " + vKeyString + " (" + device->desc + ")"; }
case BAT_MIDI_CTRL_SINGLE: {
return fmt::format("MIDI Ctrl Ch.{} CC#{} ({})", channel, ctrl, device->desc);
}
case BAT_MIDI_CTRL_ONOFF: {
return fmt::format("MIDI OnOff Ch.{} CC#{} ({})", channel, ctrl, device->desc);
}
case BAT_MIDI_PITCH_DOWN: case BAT_MIDI_PITCH_DOWN:
return "MIDI Pitch Down (" + device->desc + ")"; return fmt::format("MIDI Pitch Down Ch.{} ({})", channel, device->desc);
case BAT_MIDI_PITCH_UP: case BAT_MIDI_PITCH_UP:
return "MIDI Pitch Up (" + device->desc + ")"; return fmt::format("MIDI Pitch Up Ch.{} ({})", channel, device->desc);
default: default:
return "MIDI Unknown " + vKeyString + " (" + device->desc + ")"; return "MIDI Unknown " + vKeyString + " (" + device->desc + ")";
} }
return "";
}
case rawinput::PIUIO_DEVICE: case rawinput::PIUIO_DEVICE:
return "PIUIO " + vKeyString; return "PIUIO " + vKeyString;
case rawinput::DESTROYED: case rawinput::DESTROYED:
@@ -374,6 +403,115 @@ std::string Button::getDisplayString(rawinput::RawInputManager* manager) {
} }
} }
void Button::getMidiVKey(int& channel, int& index) {
switch (this->analog_type) {
// update strings in analog.cpp as well
case BAT_NONE:
channel = (vKey / 0x80) + 1;
index = vKey & 0x7f;
break;
case BAT_MIDI_CTRL_PRECISION:
channel = (vKey / 32) + 1;
index = (vKey % 32);
break;
case BAT_MIDI_CTRL_SINGLE:
channel = (vKey / 44) + 1;
index = (vKey % 44);
if (index <= 25) {
index += 0x46; // single byte range
} else {
index = index - 26 + 0x66; // undefined single byte range
}
break;
case BAT_MIDI_CTRL_ONOFF:
channel = (vKey / 6) + 1;
index = (vKey % 6) + 0x40;
break;
case BAT_MIDI_PITCH_DOWN:
case BAT_MIDI_PITCH_UP:
channel = vKey + 1;
index = 0;
break;
default:
channel = 0;
index = 0;
break;
}
}
void Button::setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index) {
int vKey = 0;
if (is_note) {
vKey = (channel - 1) * 0x80 + index;
this->setVKey(vKey);
this->setAnalogType(BAT_NONE);
// ensure that velocity threshold is read back from what rawinput has for other bindings
if (manager && !this->device_identifier.empty()) {
auto device = manager->devices_get(this->device_identifier);
if (device &&
device->midiInfo &&
(size_t)vKey < device->midiInfo->v2_velocity_threshold.size()) {
this->setVelocityThreshold(device->midiInfo->v2_velocity_threshold[vKey]);
}
}
return;
}
if (channel < 1 || 16 < channel) {
this->setVKey(0);
this->setAnalogType(BAT_NONE);
return;
}
if (index < 0 || 127 < index) {
this->setVKey(0);
this->setAnalogType(BAT_NONE);
return;
}
// continuous controller MSB
if (0x00 <= index && index <= 0x1F) {
vKey = (channel - 1) * 32 + index;
this->setVKey(vKey);
this->setAnalogType(BAT_MIDI_CTRL_PRECISION);
return;
}
// continuous controller LSB
if (0x20 <= index && index <= 0x3F) {
vKey = (channel - 1) * 32 + index - 0x20;
this->setVKey(vKey);
this->setAnalogType(BAT_MIDI_CTRL_PRECISION);
return;
}
// on/off controls
if (0x40 <= index && index <= 0x45) {
vKey = (channel - 1) * 6 + (index - 0x40);
this->setVKey(vKey);
this->setAnalogType(BAT_MIDI_CTRL_ONOFF);
return;
}
// single byte controllers
if (0x46 <= index && index <= 0x5F) {
vKey = (channel - 1) * 44;
vKey += index - 0x46; // single byte range
this->setVKey(vKey);
this->setAnalogType(BAT_MIDI_CTRL_SINGLE);
return;
}
// undefined single byte controllers
if (0x66 <= index && index <= 0x77) {
vKey = (channel - 1) * 44;
vKey += index - 0x66 + (0x5F - 0x46 + 1) ; // undefined single byte range
this->setVKey(vKey);
this->setAnalogType(BAT_MIDI_CTRL_SINGLE);
return;
}
}
#define HAT_SWITCH_INCREMENT (1.f / 7) #define HAT_SWITCH_INCREMENT (1.f / 7)
void Button::getHatSwitchValues(float analog_state, ButtonAnalogType* buffer) { void Button::getHatSwitchValues(float analog_state, ButtonAnalogType* buffer) {
+14
View File
@@ -28,6 +28,7 @@ enum ButtonAnalogType {
BAT_MIDI_CTRL_ONOFF = 14, BAT_MIDI_CTRL_ONOFF = 14,
BAT_MIDI_PITCH_DOWN = 15, BAT_MIDI_PITCH_DOWN = 15,
BAT_MIDI_PITCH_UP = 16, BAT_MIDI_PITCH_UP = 16,
BAT_ANY = 17,
}; };
extern const char *ButtonAnalogTypeStr[]; extern const char *ButtonAnalogTypeStr[];
@@ -45,8 +46,10 @@ private:
GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED; GameAPI::Buttons::State last_state = GameAPI::Buttons::BUTTON_NOT_PRESSED;
float last_velocity = 0.f; float last_velocity = 0.f;
unsigned short velocity_threshold = 0;
std::string getVKeyString(); std::string getVKeyString();
std::string getMidiNoteString();
public: public:
@@ -159,6 +162,17 @@ public:
this->last_velocity = last_velocity; this->last_velocity = last_velocity;
} }
inline unsigned short getVelocityThreshold() const {
return this->velocity_threshold;
}
inline void setVelocityThreshold(unsigned short velocity_threshold) {
this->velocity_threshold = velocity_threshold;
}
void getMidiVKey(int& channel, int& index);
void setMidiVKey(rawinput::RawInputManager* manager, bool is_note, int channel, int index);
/* /*
* Map hat switch float value from [0-1] to directions. * Map hat switch float value from [0-1] to directions.
* Buffer must be sized 3 or bigger. * Buffer must be sized 3 or bigger.
+106 -13
View File
@@ -19,10 +19,16 @@ Config::Config() {
this->status = false; this->status = false;
if (CONFIG_PATH_OVERRIDE.length() > 0) { if (CONFIG_PATH_OVERRIDE.length() > 0) {
this->configLocation = CONFIG_PATH_OVERRIDE; this->configLocation = CONFIG_PATH_OVERRIDE;
log_info("cfg", "using custom config file: {}", this->configLocation.string());
} else { } else {
this->configLocation = std::string(getenv("APPDATA")) + "\\spicetools.xml"; this->configLocation = std::filesystem::path(_wgetenv(L"APPDATA")) / L"spicetools.xml";
// avoids logging the expanded appdata path as it contains user name
log_info("cfg", "using global config file: %appdata%\\spicetools.xml");
} }
this->configLocationTemp = this->configLocation;
this->configLocationTemp.replace_extension(L"tmp");
tinyxml2::XMLError configLoadError, *previousConfigLoadError = nullptr; tinyxml2::XMLError configLoadError, *previousConfigLoadError = nullptr;
do { do {
@@ -42,7 +48,7 @@ Config::Config() {
this->firstFillConfigFile(); this->firstFillConfigFile();
break; break;
case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED: case tinyxml2::XMLError::XML_ERROR_FILE_COULD_NOT_BE_OPENED:
log_fatal("cfg", "could not open config file: {}", this->configLocation); log_fatal("cfg", "could not open config file: {}", this->configLocation.string());
break; break;
case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND: case tinyxml2::XMLError::XML_ERROR_FILE_NOT_FOUND:
this->createConfigFile(); this->createConfigFile();
@@ -57,7 +63,7 @@ Config::Config() {
case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN: case tinyxml2::XMLError::XML_ERROR_PARSING_UNKNOWN:
case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT: case tinyxml2::XMLError::XML_ERROR_MISMATCHED_ELEMENT:
case tinyxml2::XMLError::XML_ERROR_PARSING: case tinyxml2::XMLError::XML_ERROR_PARSING:
log_warning("cfg", "Couldn't read config file: {}", this->configLocation); log_warning("cfg", "Couldn't read config file: {}", this->configLocation.string());
this->createConfigFile(); this->createConfigFile();
this->firstFillConfigFile(); this->firstFillConfigFile();
break; break;
@@ -147,12 +153,14 @@ bool Config::addGame(Game &game) {
auto analogType = (int) BAT_NONE; auto analogType = (int) BAT_NONE;
double debounce_up = 0.0; double debounce_up = 0.0;
double debounce_down = 0.0; double debounce_down = 0.0;
int velocity_threshold = 0;
bool invert = false; bool invert = false;
tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey); tinyxml2::XMLError attrError = gameButtonNode->QueryIntAttribute("vkey", &vKey);
const char *devid = gameButtonNode->Attribute("devid"); const char *devid = gameButtonNode->Attribute("devid");
gameButtonNode->QueryIntAttribute("analogtype", &analogType); gameButtonNode->QueryIntAttribute("analogtype", &analogType);
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up); gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down); gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
gameButtonNode->QueryBoolAttribute("invert", &invert); gameButtonNode->QueryBoolAttribute("invert", &invert);
if (attrError != tinyxml2::XMLError::XML_SUCCESS) { if (attrError != tinyxml2::XMLError::XML_SUCCESS) {
gameButtonsNode->DeleteChild(gameButtonNode); gameButtonsNode->DeleteChild(gameButtonNode);
@@ -163,6 +171,7 @@ bool Config::addGame(Game &game) {
gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str()); gameButtonNode->SetAttribute("devid", button->getDeviceIdentifier().c_str());
gameButtonNode->SetAttribute("debounce_up", debounce_up); gameButtonNode->SetAttribute("debounce_up", debounce_up);
gameButtonNode->SetAttribute("debounce_down", debounce_down); gameButtonNode->SetAttribute("debounce_down", debounce_down);
gameButtonNode->SetAttribute("velocity_threshold", velocity_threshold);
gameButtonNode->SetAttribute("invert", invert); gameButtonNode->SetAttribute("invert", invert);
gameButtonsNode->InsertEndChild(gameButtonNode); gameButtonsNode->InsertEndChild(gameButtonNode);
} else { } else {
@@ -170,6 +179,7 @@ bool Config::addGame(Game &game) {
button->setAnalogType((ButtonAnalogType) analogType); button->setAnalogType((ButtonAnalogType) analogType);
button->setDebounceUp(debounce_up); button->setDebounceUp(debounce_up);
button->setDebounceDown(debounce_down); button->setDebounceDown(debounce_down);
button->setVelocityThreshold(velocity_threshold);
button->setInvert(invert); button->setInvert(invert);
if (devid) { if (devid) {
button->setDeviceIdentifier(devid); button->setDeviceIdentifier(devid);
@@ -188,6 +198,7 @@ bool Config::addGame(Game &game) {
gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType()); gameButtonNode->SetAttribute("analogtype", (int) it.getAnalogType());
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp()); gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown()); gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
gameButtonNode->SetAttribute("invert", it.getInvert()); gameButtonNode->SetAttribute("invert", it.getInvert());
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str()); gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
gameButtonsNode->InsertEndChild(gameButtonNode); gameButtonsNode->InsertEndChild(gameButtonNode);
@@ -226,12 +237,18 @@ bool Config::addGame(Game &game) {
bool deadzone_mirror = false; bool deadzone_mirror = false;
bool invert = false; bool invert = false;
bool smoothing = false; bool smoothing = false;
int multiplier = 1;
bool relative_mode = false;
int delay_buffer_depth = 0;
tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index); tinyxml2::XMLError err1 = gameAnalogNode->QueryIntAttribute("index", &index);
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity); gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone); gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
gameAnalogNode->QueryBoolAttribute("deadzone_mirror", &deadzone_mirror); gameAnalogNode->QueryBoolAttribute("deadzone_mirror", &deadzone_mirror);
gameAnalogNode->QueryBoolAttribute("invert", &invert); gameAnalogNode->QueryBoolAttribute("invert", &invert);
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing); gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
const char *devid = gameAnalogNode->Attribute("devid"); const char *devid = gameAnalogNode->Attribute("devid");
if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) { if (err1 != tinyxml2::XMLError::XML_SUCCESS || !devid) {
@@ -245,6 +262,9 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror()); gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror());
gameAnalogNode->SetAttribute("invert", it.getInvert()); gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing()); gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
gameAnalogsNode->InsertEndChild(gameAnalogNode); gameAnalogsNode->InsertEndChild(gameAnalogNode);
} else { } else {
it.setIndex(static_cast<unsigned short int>(index)); it.setIndex(static_cast<unsigned short int>(index));
@@ -254,6 +274,9 @@ bool Config::addGame(Game &game) {
it.setDeadzoneMirror(deadzone_mirror); it.setDeadzoneMirror(deadzone_mirror);
it.setInvert(invert); it.setInvert(invert);
it.setSmoothing(smoothing); it.setSmoothing(smoothing);
it.setMultiplier(multiplier);
it.setRelativeMode(relative_mode);
it.setDelayBufferDepth(delay_buffer_depth);
} }
} else { } else {
gameAnalogNode = this->configFile.NewElement("analog"); gameAnalogNode = this->configFile.NewElement("analog");
@@ -264,6 +287,9 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror()); gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror());
gameAnalogNode->SetAttribute("invert", it.getInvert()); gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing()); gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str()); gameAnalogNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
gameAnalogsNode->InsertEndChild(gameAnalogNode); gameAnalogsNode->InsertEndChild(gameAnalogNode);
} }
@@ -397,6 +423,7 @@ bool Config::addGame(Game &game) {
gameButtonNode->SetAttribute("analogtype", it.getAnalogType()); gameButtonNode->SetAttribute("analogtype", it.getAnalogType());
gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp()); gameButtonNode->SetAttribute("debounce_up", it.getDebounceUp());
gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown()); gameButtonNode->SetAttribute("debounce_down", it.getDebounceDown());
gameButtonNode->SetAttribute("velocity_threshold", it.getVelocityThreshold());
gameButtonNode->SetAttribute("invert", it.getInvert()); gameButtonNode->SetAttribute("invert", it.getInvert());
gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str()); gameButtonNode->SetAttribute("devid", it.getDeviceIdentifier().c_str());
gameButtonsNode->InsertEndChild(gameButtonNode); gameButtonsNode->InsertEndChild(gameButtonNode);
@@ -414,6 +441,9 @@ bool Config::addGame(Game &game) {
gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror()); gameAnalogNode->SetAttribute("deadzone_mirror", it.getDeadzoneMirror());
gameAnalogNode->SetAttribute("invert", it.getInvert()); gameAnalogNode->SetAttribute("invert", it.getInvert());
gameAnalogNode->SetAttribute("smoothing", it.getSmoothing()); gameAnalogNode->SetAttribute("smoothing", it.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", it.getMultiplier());
gameAnalogNode->SetAttribute("relative", it.isRelativeMode());
gameAnalogNode->SetAttribute("delay", it.getDelayBufferDepth());
gameAnalogsNode->InsertEndChild(gameAnalogNode); gameAnalogsNode->InsertEndChild(gameAnalogNode);
} }
@@ -442,7 +472,7 @@ bool Config::addGame(Game &game) {
} }
// save config // save config
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
// return success // return success
return true; return true;
@@ -494,6 +524,7 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType()); gameButtonNode->SetAttribute("analogtype", (int) button.getAnalogType());
gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp()); gameButtonNode->SetAttribute("debounce_up", button.getDebounceUp());
gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown()); gameButtonNode->SetAttribute("debounce_down", button.getDebounceDown());
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
gameButtonNode->SetAttribute("invert", button.getInvert()); gameButtonNode->SetAttribute("invert", button.getInvert());
gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str()); gameButtonNode->SetAttribute("devid", button.getDeviceIdentifier().c_str());
break; break;
@@ -509,19 +540,47 @@ bool Config::updateBinding(const Game &game, const Button &button, int alternati
gameButtonNode->SetAttribute("analogtype", 0); gameButtonNode->SetAttribute("analogtype", 0);
gameButtonNode->SetAttribute("debounce_up", 0.0); gameButtonNode->SetAttribute("debounce_up", 0.0);
gameButtonNode->SetAttribute("debounce_down", 0.0); gameButtonNode->SetAttribute("debounce_down", 0.0);
gameButtonNode->SetAttribute("velocity_threshold", 0);
gameButtonNode->SetAttribute("invert", false); gameButtonNode->SetAttribute("invert", false);
gameButtonNode->SetAttribute("devid", ""); gameButtonNode->SetAttribute("devid", "");
gameButtonsNode->InsertEndChild(gameButtonNode); gameButtonsNode->InsertEndChild(gameButtonNode);
} }
} }
// for MIDI notes, need to keep velocity threshold consistent for all bindings
// ;MIDI; is a unique prefix that we use at rawinput layer to identify MIDI devices
const bool fixup_other_buttons =
(button.getAnalogType() == (int)BAT_NONE &&
!button.getDeviceIdentifier().empty() &&
button.getDeviceIdentifier().find(";MIDI;", 0) == 0);
if (fixup_other_buttons) {
gameButtonNode = gameButtonsNode->FirstChildElement("button");
while (gameButtonNode != nullptr) {
const char *devid = gameButtonNode->Attribute("devid");
if (button.getDeviceIdentifier() == devid) {
int other_vKey = 0;
int other_vel = 0;
int other_type = 0;
gameButtonNode->QueryIntAttribute("velocity_threshold", &other_vel);
gameButtonNode->QueryIntAttribute("vkey", &other_vKey);
gameButtonNode->QueryIntAttribute("analogtype", &other_type);
if (other_vKey == button.getVKey() &&
other_type == button.getAnalogType() &&
other_vel != button.getVelocityThreshold()) {
gameButtonNode->SetAttribute("velocity_threshold", button.getVelocityThreshold());
}
}
gameButtonNode = gameButtonNode->NextSiblingElement("button");
}
}
// check if button was not found // check if button was not found
if (button_count == 0) { if (button_count == 0) {
return false; return false;
} }
// save config // save config
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
// return success // return success
return true; return true;
@@ -579,6 +638,9 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
gameAnalogNode->SetAttribute("deadzone_mirror", analog.getDeadzoneMirror()); gameAnalogNode->SetAttribute("deadzone_mirror", analog.getDeadzoneMirror());
gameAnalogNode->SetAttribute("invert", analog.getInvert()); gameAnalogNode->SetAttribute("invert", analog.getInvert());
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing()); gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str()); gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
} else { } else {
gameAnalogNode = this->configFile.NewElement("analog"); gameAnalogNode = this->configFile.NewElement("analog");
@@ -588,11 +650,14 @@ bool Config::updateBinding(const Game &game, const Analog &analog) {
gameAnalogNode->SetAttribute("deadzone_mirror", analog.getDeadzoneMirror()); gameAnalogNode->SetAttribute("deadzone_mirror", analog.getDeadzoneMirror());
gameAnalogNode->SetAttribute("invert", analog.getInvert()); gameAnalogNode->SetAttribute("invert", analog.getInvert());
gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing()); gameAnalogNode->SetAttribute("smoothing", analog.getSmoothing());
gameAnalogNode->SetAttribute("multiplier", analog.getMultiplier());
gameAnalogNode->SetAttribute("relative", analog.isRelativeMode());
gameAnalogNode->SetAttribute("delay", analog.getDelayBufferDepth());
gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str()); gameAnalogNode->SetAttribute("devid", analog.getDeviceIdentifier().c_str());
gameAnalogsNode->InsertEndChild(gameAnalogNode); gameAnalogsNode->InsertEndChild(gameAnalogNode);
} }
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
return true; return true;
} }
@@ -633,7 +698,7 @@ bool Config::updateBinding(const Game &game, ConfigKeypadBindings &keypads) {
gameKeypadNode->SetAttribute("cardpath1", reinterpret_cast<const char *>(keypads.card_paths[0].u8string().c_str())); gameKeypadNode->SetAttribute("cardpath1", reinterpret_cast<const char *>(keypads.card_paths[0].u8string().c_str()));
gameKeypadNode->SetAttribute("cardpath2", reinterpret_cast<const char *>(keypads.card_paths[1].u8string().c_str())); gameKeypadNode->SetAttribute("cardpath2", reinterpret_cast<const char *>(keypads.card_paths[1].u8string().c_str()));
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
return true; return true;
} }
@@ -701,7 +766,7 @@ bool Config::updateBinding(const Game &game, const Light &light, int alternative
} }
// save config // save config
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
// return success // return success
return true; return true;
@@ -760,7 +825,7 @@ bool Config::updateBinding(const Game &game, const Option &option) {
gameOptionsNode->InsertEndChild(gameOptionNode); gameOptionsNode->InsertEndChild(gameOptionNode);
} }
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
return true; return true;
} }
@@ -806,11 +871,13 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
auto analogType = (int) BAT_NONE; auto analogType = (int) BAT_NONE;
double debounce_up = 0.0; double debounce_up = 0.0;
double debounce_down = 0.0; double debounce_down = 0.0;
int velocity_threshold = 0;
bool invert = false; bool invert = false;
gameButtonNode->QueryIntAttribute("vkey", &vKey); gameButtonNode->QueryIntAttribute("vkey", &vKey);
gameButtonNode->QueryIntAttribute("analogtype", &analogType); gameButtonNode->QueryIntAttribute("analogtype", &analogType);
gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up); gameButtonNode->QueryDoubleAttribute("debounce_up", &debounce_up);
gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down); gameButtonNode->QueryDoubleAttribute("debounce_down", &debounce_down);
gameButtonNode->QueryIntAttribute("velocity_threshold", &velocity_threshold);
gameButtonNode->QueryBoolAttribute("invert", &invert); gameButtonNode->QueryBoolAttribute("invert", &invert);
const char *devid = gameButtonNode->Attribute("devid"); const char *devid = gameButtonNode->Attribute("devid");
@@ -824,6 +891,7 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
alt.setAnalogType((ButtonAnalogType) analogType); alt.setAnalogType((ButtonAnalogType) analogType);
alt.setDebounceUp(debounce_up); alt.setDebounceUp(debounce_up);
alt.setDebounceDown(debounce_down); alt.setDebounceDown(debounce_down);
alt.setVelocityThreshold(velocity_threshold);
alt.setInvert(invert); alt.setInvert(invert);
if (devid) { if (devid) {
alt.setDeviceIdentifier(std::string(devid)); alt.setDeviceIdentifier(std::string(devid));
@@ -835,13 +903,13 @@ std::vector<Button> Config::getButtons(const std::string &gameName) {
// if no alternative was found // if no alternative was found
if (!alternative_found) { if (!alternative_found) {
// create button and add to list // create button and add to list
auto &button = buttons.emplace_back(buttonNodeName); auto &button = buttons.emplace_back(buttonNodeName);
button.setVKey((unsigned short) vKey); button.setVKey((unsigned short) vKey);
button.setAnalogType((ButtonAnalogType) analogType); button.setAnalogType((ButtonAnalogType) analogType);
button.setDebounceUp(debounce_up); button.setDebounceUp(debounce_up);
button.setDebounceDown(debounce_down); button.setDebounceDown(debounce_down);
button.setVelocityThreshold(velocity_threshold);
button.setInvert(invert); button.setInvert(invert);
if (devid) { if (devid) {
button.setDeviceIdentifier(devid); button.setDeviceIdentifier(devid);
@@ -985,12 +1053,18 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
bool deadzone_mirror = false; bool deadzone_mirror = false;
bool invert = false; bool invert = false;
bool smoothing = false; bool smoothing = false;
int multiplier = 1;
bool relative_mode = false;
int delay_buffer_depth = 0;
gameAnalogNode->QueryIntAttribute("index", &index); gameAnalogNode->QueryIntAttribute("index", &index);
gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity); gameAnalogNode->QueryFloatAttribute("sensivity", &sensitivity);
gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone); gameAnalogNode->QueryFloatAttribute("deadzone", &deadzone);
gameAnalogNode->QueryBoolAttribute("deadzone_mirror", &deadzone_mirror); gameAnalogNode->QueryBoolAttribute("deadzone_mirror", &deadzone_mirror);
gameAnalogNode->QueryBoolAttribute("invert", &invert); gameAnalogNode->QueryBoolAttribute("invert", &invert);
gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing); gameAnalogNode->QueryBoolAttribute("smoothing", &smoothing);
gameAnalogNode->QueryIntAttribute("multiplier", &multiplier);
gameAnalogNode->QueryBoolAttribute("relative", &relative_mode);
gameAnalogNode->QueryIntAttribute("delay", &delay_buffer_depth);
const char *devid = gameAnalogNode->Attribute("devid"); const char *devid = gameAnalogNode->Attribute("devid");
// create analog and add to list // create analog and add to list
@@ -1001,6 +1075,9 @@ std::vector<Analog> Config::getAnalogs(const std::string &gameName) {
analog.setDeadzoneMirror(deadzone_mirror); analog.setDeadzoneMirror(deadzone_mirror);
analog.setInvert(invert); analog.setInvert(invert);
analog.setSmoothing(smoothing); analog.setSmoothing(smoothing);
analog.setMultiplier(multiplier);
analog.setRelativeMode(relative_mode);
analog.setDelayBufferDepth(delay_buffer_depth);
if (devid) { if (devid) {
analog.setDeviceIdentifier(devid); analog.setDeviceIdentifier(devid);
} }
@@ -1136,7 +1213,7 @@ std::vector<Option> Config::getOptions(Game *game) {
bool Config::createConfigFile() { bool Config::createConfigFile() {
std::ofstream ofsConfig; std::ofstream ofsConfig;
ofsConfig.open(this->configLocation); ofsConfig.open(this->configLocationTemp);
if (!ofsConfig.is_open() || ofsConfig.fail() || ofsConfig.bad()) { if (!ofsConfig.is_open() || ofsConfig.fail() || ofsConfig.bad()) {
this->status = false; this->status = false;
return false; return false;
@@ -1146,7 +1223,7 @@ bool Config::createConfigFile() {
} }
bool Config::firstFillConfigFile() { bool Config::firstFillConfigFile() {
this->configFile.LoadFile(this->configLocation.c_str()); this->configFile.LoadFile(this->configLocationTemp.c_str());
this->configFile.Clear(); this->configFile.Clear();
tinyxml2::XMLNode *declarationNode = this->configFile.NewDeclaration(); tinyxml2::XMLNode *declarationNode = this->configFile.NewDeclaration();
@@ -1155,6 +1232,22 @@ bool Config::firstFillConfigFile() {
tinyxml2::XMLNode *rootNode = this->configFile.NewElement("games"); tinyxml2::XMLNode *rootNode = this->configFile.NewElement("games");
this->configFile.InsertEndChild(rootNode); this->configFile.InsertEndChild(rootNode);
this->configFile.SaveFile(this->configLocation.c_str(), false); this->saveConfigFile();
return true; return true;
} }
void Config::saveConfigFile() {
// create a .tmp file and write to it...
const auto xml_result = this->configFile.SaveFile(this->configLocationTemp.c_str(), false);
if (xml_result != tinyxml2::XMLError::XML_SUCCESS) {
log_info("cfg", "failed to write file: {}", this->configLocationTemp.string());
return;
}
// copy the .tmp file to the main file...
if (CopyFileW(this->configLocationTemp.c_str(), this->configLocation.c_str(), false) == 0) {
log_warning("cfg", "CopyFileA failed: 0x{:08x}", GetLastError());
return;
}
// delete the .tmp file (not critical if this fails)
DeleteFileW(this->configLocationTemp.c_str());
}
+3 -1
View File
@@ -54,7 +54,9 @@ private:
tinyxml2::XMLDocument configFile; tinyxml2::XMLDocument configFile;
bool status; bool status;
std::string configLocation; std::filesystem::path configLocation;
std::filesystem::path configLocationTemp;
bool firstFillConfigFile(); bool firstFillConfigFile();
void saveConfigFile();
}; };
-8
View File
@@ -1,7 +1,6 @@
#include "configurator.h" #include "configurator.h"
#include "overlay/overlay.h" #include "overlay/overlay.h"
#include "script/manager.h"
namespace cfg { namespace cfg {
@@ -26,14 +25,7 @@ namespace cfg {
overlay::OVERLAY->hotkeys_enable = false; overlay::OVERLAY->hotkeys_enable = false;
ImGui::GetIO().MouseDrawCursor = false; ImGui::GetIO().MouseDrawCursor = false;
// scripts
script::manager_scan();
script::manager_config();
// run window // run window
this->wnd.run(); this->wnd.run();
// clean up
script::manager_shutdown();
} }
} }
+1 -2
View File
@@ -5,8 +5,7 @@
namespace cfg { namespace cfg {
enum class ConfigType { enum class ConfigType {
Config, Config
KFControl,
}; };
// globals // globals
+1 -5
View File
@@ -29,13 +29,9 @@ cfg::ConfiguratorWindow::ConfiguratorWindow() {
// determine window title // determine window title
if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) { if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::Config) {
WINDOW_TITLE = "spice2x config - a fork of SpiceTools (" + to_string(VERSION_STRING_CFG) + ")"; WINDOW_TITLE = "spice2x config (" + to_string(VERSION_STRING_CFG) + ")";
WINDOW_SIZE_X = 800; WINDOW_SIZE_X = 800;
WINDOW_SIZE_Y = 600; WINDOW_SIZE_Y = 600;
} else if (cfg::CONFIGURATOR_TYPE == cfg::ConfigType::KFControl) {
WINDOW_TITLE = "KFControl (" + to_string(VERSION_STRING_CFG) + ")";
WINDOW_SIZE_X = 400;
WINDOW_SIZE_Y = 316;
} }
// open window // open window
+20
View File
@@ -5,6 +5,8 @@
#include "rawinput/piuio.h" #include "rawinput/piuio.h"
#include "rawinput/rawinput.h" #include "rawinput/rawinput.h"
#include "rawinput/sextet.h" #include "rawinput/sextet.h"
#include "rawinput/smxdedicab.h"
#include "rawinput/smxstage.h"
#include "util/logging.h" #include "util/logging.h"
std::string Light::getDisplayString(rawinput::RawInputManager* manager) { std::string Light::getDisplayString(rawinput::RawInputManager* manager) {
@@ -65,6 +67,24 @@ std::string Light::getDisplayString(rawinput::RawInputManager* manager) {
return "Invalid PIUIO Light (" + index_string + ")"; return "Invalid PIUIO Light (" + index_string + ")";
} }
case rawinput::SMX_STAGE: {
// get light name of SMX Stage device
if (index < rawinput::SmxStageDevice::TOTAL_LIGHT_COUNT) {
return rawinput::SmxStageDevice::GetLightNameByIndex(index) + " (" + index_string + ")";
}
return "Invalid SMX Stage Light (" + index_string + ")";
}
case rawinput::SMX_DEDICAB: {
// get light name of SMX Dedicab device
if (index < rawinput::SmxDedicabDevice::LIGHTS_COUNT) {
return rawinput::SmxDedicabDevice::GetLightNameByIndex(index) + " (" + index_string + ")";
}
return "Invalid SMX Dedicab Light (" + index_string + ")";
}
case rawinput::DESTROYED: case rawinput::DESTROYED:
return "Unplugged device (" + index_string + ")"; return "Unplugged device (" + index_string + ")";
default: default:
+13
View File
@@ -1,6 +1,7 @@
#include "option.h" #include "option.h"
#include "util/logging.h" #include "util/logging.h"
#include "util/utils.h"
void Option::value_add(std::string new_value) { void Option::value_add(std::string new_value) {
@@ -97,3 +98,15 @@ uint64_t Option::value_hex64() const {
} }
return affinity; return affinity;
} }
bool Option::search_match(const std::string &query_in_lower_case) {
if (this->search_string.empty()) {
const auto &param =
this->definition.display_name.empty() ?
this->definition.name : this->definition.display_name;
const auto s = this->definition.title + " -" + param;
this->search_string = strtolower(s);
}
return this->search_string.find(query_in_lower_case) != std::string::npos;
}
+13 -1
View File
@@ -3,6 +3,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <cstdint>
enum class OptionType { enum class OptionType {
Bool, Bool,
@@ -14,7 +15,14 @@ enum class OptionType {
struct OptionDefinition { struct OptionDefinition {
std::string title; std::string title;
// unique identifier used for flag matching but also stored in config files
// (should not be changed once published for compat)
std::string name; std::string name;
// what's displayed in the UI/logs as the flag name
std::string display_name = "";
// slash-delimited list of strings that also work as flag
std::string aliases = "";
// what's displayed in the UI/logs as the tooltip
std::string desc; std::string desc;
OptionType type; OptionType type;
bool hidden = false; bool hidden = false;
@@ -23,11 +31,13 @@ struct OptionDefinition {
std::string category = "Development"; std::string category = "Development";
bool sensitive = false; bool sensitive = false;
std::vector<std::pair<std::string, std::string>> elements = {}; std::vector<std::pair<std::string, std::string>> elements = {};
bool disabled = false;
}; };
class Option { class Option {
private: private:
OptionDefinition definition; OptionDefinition definition;
std::string search_string;
public: public:
std::string value; std::string value;
@@ -35,7 +45,8 @@ public:
bool disabled = false; bool disabled = false;
explicit Option(OptionDefinition definition, std::string value = "") : explicit Option(OptionDefinition definition, std::string value = "") :
definition(std::move(definition)), value(std::move(value)) {}; definition(std::move(definition)), value(std::move(value)) {
};
inline const OptionDefinition &get_definition() const { inline const OptionDefinition &get_definition() const {
return this->definition; return this->definition;
@@ -57,4 +68,5 @@ public:
std::vector<std::string> values_text() const; std::vector<std::string> values_text() const;
uint32_t value_uint32() const; uint32_t value_uint32() const;
uint64_t value_hex64() const; uint64_t value_hex64() const;
bool search_match(const std::string &query_in_lower_case);
}; };
+38 -23
View File
@@ -12,10 +12,21 @@ namespace cfg {
// globals // globals
std::unique_ptr<cfg::ScreenResize> SCREENRESIZE; std::unique_ptr<cfg::ScreenResize> SCREENRESIZE;
std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
ScreenResize::ScreenResize() { ScreenResize::ScreenResize() {
this->config_path = std::string(getenv("APPDATA")) + "\\spicetools_screen_resize.json"; bool file_exists = false;
if (SCREEN_RESIZE_CFG_PATH_OVERRIDE.has_value()) {
this->config_path = SCREEN_RESIZE_CFG_PATH_OVERRIDE.value();
if (fileutils::file_exists(this->config_path)) { if (fileutils::file_exists(this->config_path)) {
log_info("ScreenResize", "loading config from: {}", this->config_path.string());
file_exists = true;
}
} else {
this->config_path =
fileutils::get_config_file_path("ScreenResize", "spicetools_screen_resize.json", &file_exists);
}
if (file_exists) {
this->config_load(); this->config_load();
} }
} }
@@ -24,10 +35,9 @@ namespace cfg {
} }
void ScreenResize::config_load() { void ScreenResize::config_load() {
log_info("ScreenResize", "loading config");
std::string config = fileutils::text_read(this->config_path); std::string config = fileutils::text_read(this->config_path);
if (config.empty()) { if (config.empty()) {
log_info("ScreenResize", "config is empty");
return; return;
} }
@@ -66,14 +76,18 @@ namespace cfg {
eamuse_get_game(), eamuse_get_game(),
use_game_setting, use_game_setting,
root); root);
load_int_value(doc, root + "offset_x", this->offset_x);
load_int_value(doc, root + "offset_y", this->offset_y);
load_float_value(doc, root + "scale_x", this->scale_x);
load_float_value(doc, root + "scale_y", this->scale_y);
load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize); load_bool_value(doc, root + "enable_screen_resize", this->enable_screen_resize);
load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter); load_bool_value(doc, root + "enable_linear_filter", this->enable_linear_filter);
load_bool_value(doc, root + "keep_aspect_ratio", this->keep_aspect_ratio); for (size_t i = 0; i < std::size(this->scene_settings); i++) {
load_bool_value(doc, root + "centered", this->centered); auto& scene = this->scene_settings[i];
const std::string prefix = fmt::format("scenes/{}/", i);
load_int_value(doc, root + prefix + "offset_x", scene.offset_x);
load_int_value(doc, root + prefix + "offset_y", scene.offset_y);
load_float_value(doc, root + prefix + "scale_x", scene.scale_x);
load_float_value(doc, root + prefix + "scale_y", scene.scale_y);
load_bool_value(doc, root + prefix + "keep_aspect_ratio", scene.keep_aspect_ratio);
}
// windowed settings are always under game settings // windowed settings are always under game settings
root = "/sp2x_games/" + eamuse_get_game() + "/"; root = "/sp2x_games/" + eamuse_get_game() + "/";
@@ -95,7 +109,7 @@ namespace cfg {
bool ScreenResize::load_bool_value(rapidjson::Document& doc, std::string path, bool& value) { bool ScreenResize::load_bool_value(rapidjson::Document& doc, std::string path, bool& value) {
const auto v = rapidjson::Pointer(path).Get(doc); const auto v = rapidjson::Pointer(path).Get(doc);
if (!v) { if (!v) {
log_warning("ScreenResize", "{} not found", path); log_misc("ScreenResize", "{} not found", path);
return false; return false;
} }
if (!v->IsBool()) { if (!v->IsBool()) {
@@ -109,7 +123,7 @@ namespace cfg {
bool ScreenResize::load_int_value(rapidjson::Document& doc, std::string path, int& value) { bool ScreenResize::load_int_value(rapidjson::Document& doc, std::string path, int& value) {
const auto v = rapidjson::Pointer(path).Get(doc); const auto v = rapidjson::Pointer(path).Get(doc);
if (!v) { if (!v) {
log_warning("ScreenResize", "{} not found", path); log_misc("ScreenResize", "{} not found", path);
return false; return false;
} }
if (!v->IsInt()) { if (!v->IsInt()) {
@@ -123,7 +137,7 @@ namespace cfg {
bool ScreenResize::load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value) { bool ScreenResize::load_uint32_value(rapidjson::Document& doc, std::string path, uint32_t& value) {
const auto v = rapidjson::Pointer(path).Get(doc); const auto v = rapidjson::Pointer(path).Get(doc);
if (!v) { if (!v) {
log_warning("ScreenResize", "{} not found", path); log_misc("ScreenResize", "{} not found", path);
return false; return false;
} }
if (!v->IsUint()) { if (!v->IsUint()) {
@@ -137,7 +151,7 @@ namespace cfg {
bool ScreenResize::load_float_value(rapidjson::Document& doc, std::string path, float& value) { bool ScreenResize::load_float_value(rapidjson::Document& doc, std::string path, float& value) {
const auto v = rapidjson::Pointer(path).Get(doc); const auto v = rapidjson::Pointer(path).Get(doc);
if (!v) { if (!v) {
log_warning("ScreenResize", "{} not found", path); log_misc("ScreenResize", "{} not found", path);
return false; return false;
} }
if (v->IsInt()) { if (v->IsInt()) {
@@ -156,8 +170,6 @@ namespace cfg {
} }
void ScreenResize::config_save() { void ScreenResize::config_save() {
log_info("ScreenResize", "saving config");
rapidjson::Document doc; rapidjson::Document doc;
std::string config = fileutils::text_read(this->config_path); std::string config = fileutils::text_read(this->config_path);
if (!config.empty()) { if (!config.empty()) {
@@ -179,14 +191,17 @@ namespace cfg {
root); root);
// full screen image settings // full screen image settings
rapidjson::Pointer(root + "offset_x").Set(doc, this->offset_x);
rapidjson::Pointer(root + "offset_y").Set(doc, this->offset_y);
rapidjson::Pointer(root + "scale_x").Set(doc, this->scale_x);
rapidjson::Pointer(root + "scale_y").Set(doc, this->scale_y);
rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize); rapidjson::Pointer(root + "enable_screen_resize").Set(doc, this->enable_screen_resize);
rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter); rapidjson::Pointer(root + "enable_linear_filter").Set(doc, this->enable_linear_filter);
rapidjson::Pointer(root + "keep_aspect_ratio").Set(doc, this->keep_aspect_ratio); for (size_t i = 0; i < std::size(this->scene_settings); i++) {
rapidjson::Pointer(root + "centered").Set(doc, this->centered); auto& scene = this->scene_settings[i];
const std::string prefix = fmt::format("scenes/{}/", i);
rapidjson::Pointer(root + prefix + "offset_x").Set(doc, scene.offset_x);
rapidjson::Pointer(root + prefix + "offset_y").Set(doc, scene.offset_y);
rapidjson::Pointer(root + prefix + "scale_x").Set(doc, scene.scale_x);
rapidjson::Pointer(root + prefix + "scale_y").Set(doc, scene.scale_y);
rapidjson::Pointer(root + prefix + "keep_aspect_ratio").Set(doc, scene.keep_aspect_ratio);
}
// windowed mode settings // windowed mode settings
rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top); rapidjson::Pointer(root + "w_always_on_top").Set(doc, this->window_always_on_top);
@@ -204,10 +219,10 @@ namespace cfg {
doc.Accept(writer); doc.Accept(writer);
// save to file // save to file
if (fileutils::text_write(this->config_path, buffer.GetString())) { if (fileutils::write_config_file("ScreenResize", this->config_path, buffer.GetString())) {
// this->config_dirty = false; // this->config_dirty = false;
} else { } else {
log_warning("ScreenResize", "unable to save config file to {}", this->config_path); log_warning("ScreenResize", "unable to save config file");
} }
} }
} }
+15 -7
View File
@@ -2,6 +2,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <optional>
#include <filesystem>
#include "external/rapidjson/document.h" #include "external/rapidjson/document.h"
namespace cfg { namespace cfg {
@@ -12,9 +14,19 @@ namespace cfg {
ResizableFrame = 2 ResizableFrame = 2
}; };
struct fullscreen_setting {
int offset_x = 0;
int offset_y = 0;
float scale_x = 1.0;
float scale_y = 1.0;
bool keep_aspect_ratio = true;
};
extern std::optional<std::string> SCREEN_RESIZE_CFG_PATH_OVERRIDE;
class ScreenResize { class ScreenResize {
private: private:
std::string config_path; std::filesystem::path config_path;
// bool config_dirty = false; // bool config_dirty = false;
bool load_bool_value(rapidjson::Document& doc, std::string path, bool& value); bool load_bool_value(rapidjson::Document& doc, std::string path, bool& value);
@@ -27,14 +39,10 @@ namespace cfg {
~ScreenResize(); ~ScreenResize();
// full screen (directx) image settings // full screen (directx) image settings
int offset_x = 0;
int offset_y = 0;
float scale_x = 1.0;
float scale_y = 1.0;
bool enable_screen_resize = false; bool enable_screen_resize = false;
int8_t screen_resize_current_scene = 0;
bool enable_linear_filter = true; bool enable_linear_filter = true;
bool keep_aspect_ratio = true; fullscreen_setting scene_settings[4];
bool centered = true;
// windowed mode sizing // windowed mode sizing
// Windows terminology: // Windows terminology:
+393 -3
View File
@@ -1,3 +1,393 @@
04/25/2025 [spice2x]
LargeAddressAware variant of spice.exe for Metal Gear
Save new JSON config files to %appdata%/spice2x
Various bug fixes
04/08/2025 [spice2x]
Ensure rawinput touch is default for all games (-wintouch to override)
Various overlay UI fixes and improvements, update ImGui library
Add missing lights in Museca
03/29/2025 [spice2x]
Add SDVX landscape mode, -forceresswap option
Fix bugs in image scaler
Configurator UI tweaks
03/25/2025 [spice2x]
Add Mahjong Fight Girl support
Add option for custom full screen resolution (-forceres)
03/24/2025 [spice2x]
Auto PIN entry
Multiple scenes for image resize
API: add resize function, lights.read can now filter lights by name
Fix DLL hooks not receiving command-line arguments
03/16/2025 [spice2x]
Fix clipboard copy function truncating last character of string
03/03/2025 [spice2x]
IIDX: add workaround for REVERB EX filter missing in Windows 11 update
02/25/2025 [spice2x]
Add -vsyncbuffer option for forcing double/triple buffering
02/22/2025 [spice2x]
Add -windowscale option for resizing DX9 backbuffer dimensions
01/26/2025 [spice2x]
Expanded main menu in overlay (default bind: esc key)
Nostalgia: touch and swipe gestures (-nostpoke)
Jubeat: detect root directory boot failure
Add "Any Direction" analog type for button binding
01/16/2025 [spice2x]
More MIDI improvements:
use v2_drum algorithm instead of v2 by default for drummania & FTT
threshold setting for CC when mapped as a button (hi-hat pedals)
sustain duration applied to CC OnOff controls (sustain pedals)
Nostalgia, Beatstream: fix EA card insertion issue
CCJ, QKS: translate some windowed mode settings to Unity engine
Fix DEBUG build
01/08/2025 [spice2x]
Better MIDI support; significant improvements for drums
Fix various UI bugs in Buttons / Analog tabs
Nostalgia: fix velocity handling for MIDI input
Show camera names in camera control overlay
Game exit dialog (default: Escape key)
Button layout help text in Buttons tab
12/31/2024 [spice2x]
Fix videos not playing in Unity-based games
Dump audio device info in wrapped WASAPI handler
Update DLL load failure message
12/30/2024 [spice2x]
Busou Shinki: Show Cursor option now properly shows cursor in game
IIDX: add IIDX NVENC Quality option (-iidxreccqp)
IIDX: always enable NVENC hook, not just when -iidx is set
IIDX: add help text to keypad overlay in TDJ mode
Busou Shinki, QKS, CCJ: Only show cursor by default if no touchscreen detected
Ignore invalid HID devices with bad device path
12/13/2024 [spice2x]
Improve error messages for DLL load failures
Load cardio module in spicecfg
12/06/2024 [spice2x]
Clean up seldom used features (VR, Lua scripting, layeredfs, KFControl)
New card scanner section in Cards tab
Add -scardfix option for converting NFC cards into E00401 format
Add system information logging, controlled by -sysdump option
11/20/2024 [spice2x]
DRS: add subscreen overlay window (dance floor tape LED display)
IIDX: TDJ cam selection options for users with 3+ cameras
(-iidxtdjcamhooktop /-iidxtdjcamhookfront)
IIDX/SDVX: move some options to Advanced tab
Update -nvprofile to set V-Sync settings as Application Controlled
10/29/2024 [spice2x]
(IIDX31+) apply signature patch to force WASAPI by default; use -iidxsounddevice to override
Fix SDVX EG song search when using Japanese touch keyboard (when OS is not ja-JP)
Nostalgia I/O improvements
Expose DDR Gold cabinet tape LEDs via Spice API
10/14/2024 [spice2x]
Apply patches earlier, on DLL load notification
SMX dedicab lights improvements
10/12/2024 [spice2x]
SMX dedicab lights support (requires forked SMX.dll - see Issue #228)
Option to show FPS overlay on top left (-fpsflip)
Allow TDJ camhook to load without -iidx (for cabs)
09/21/2024 [spice2x]
Add Inject Early DLL Hooks option (-z)
09/14/2024 [spice2x]
Add Patch Manager Config Path (-patchcfgpath) option
Vertical/horizontal flip for TDJ cam hook
Fix mouse button behavior when left/right buttons are swapped
08/24/2024 [spice2x]
Fix Patch Manager failing to import patches when modules path is Unicode
08/22/2024 [spice2x]
UI update for Card Manager in overlay
08/12/2024 [spice2x]
IIDX TDJ camera - bug fixes
Prevent users from accidentally enabling -cfg and -kfcontrol in spicecfg
08/07/2024 [spice2x]
IIDX TDJ camera improvements - draw modes (stretch/crop), bug fixes
08/06/2024 [spice2x]
IIDX TDJ camera improvements
Automatic cropping for 16:9 resolutions
Toggle to keep or override camera parameters
Fix Unicode path handling for config files
08/03/2024 [spice2x]
IIDX TDJ camera improvements
Camera control overlay (check Overlay tab)
Hardware acceleration for camera rendering
IIDX TDJ play record improvements
NVENC hooks have been added to fix crash on song start; -nod3d9devhook
is no longer required
07/29/2024 [spice2x]
Fix IIDX TDJ not launching in fullscreen with two monitors
07/28/2024 [spice2x]
Add more DLL support for -iidxtdjcamhook
Add -iidxtdjcamhookratio and -iidxtdjcamhookoffset
07/25/2024 [spice2x]
Add -iidxtdjcamhook option for webcam support in TDJ
07/17/2024 [spice2x]
Disable touch feedback indicators
Bug fixes
07/15/2024 [spice2x]
Fix touch and touch emulation in wintouch-based games (Nostalgia, BeatStream...)
Fix misaligned SpiceCompanion touches on windowed TDJ subscreen
Improvements to TDJ poke feature
07/13/2024 [spice2x]
Fix windowed TDJ subscreen not accepting mouse clicks after card in
Allow windowed TDJ/UFC subscreen to minimize, but not close
07/10/2024 [spice2x]
Better gfdm XG2 and XG3 support
IIDX TDJ windowed mode rendering improvements
IIDX TDJ subscreen poke feature
CardIO NumLock toggle option
Patch manager UI tweaks
07/06/2024 [spice2x]
Reliability improvements for config file saving
Experimental and incomplete support for gfdm XG3
Option to specify path for screen resize config
Reflec Beat: fix being unable to type into overlay
07/03/2024 [spice2x]
Fix SMX stage compatibility
Patch manager: log header in patches JSON, better error handling
06/26/2024 [spice2x]
Fix MDX-003 not retrieving patches from online source
06/22/2024 [spice2x]
Reliability & performance improvements for auto-card-insert
Update -sdvxnosub to prevent creation of subscreen in windowed mode
Bug fixes
06/20/2024 [spice2x]
Improve experience for windowed SDVX UFC (always launch subscreen window)
Add Delay option in analog binds
Fix pop'n soft-lock with auto card insert
Fix -graphics-single-adapter not working in Beatstream/Nostalgia
06/14/2024 [spice2x]
Improve experience for touch-enabled games in windowed mode (SpiceTouch)
06/08/2024 [spice2x]
Patch manager bug fixes
06/03/2024 [spice2x]
Fix patch status not being saved properly
Search tab for finding options
06/02/2024 [spice2x]
Fix BBC and Museca hang on boot when COM ports are present
Deprecate -iidxtdjw (use -iidxtdj and -w together instead)
05/31/2024 [spice2x]
Redesign patches tab
Bug fixes for patches.json parsing
05/29/2024 [spice2x]
Add support for "number" patch type
History for remote patch URLs
05/28/2024 [spice2x]
Update -iidxtdjw to hide useless second window
05/27/2024 [spice2x]
Fix -apiserial crashing when -apiserialbaud is not provided
Suppress "failed to acquire subscreen" error when not relevant
05/22/2024 [spice2x]
Add caution string to patches.json format
Paste from clipboard button for URL patches import
05/12/2024 [spice2x]
Disable URL patch importing in-game
UI tweaks for importing patches
05/05/2024 [spice2x]
UI tweaks for importing patches
Add aliases for arguments with -sp2x prefix
05/04/2024 [spice2x]
New patch JSON format, importing patches from URL
Add Process Efficiency Class option for hetero CPUs
04/29/2024 [spice2x]
Move -vr to be DANCERUSH only option
Fix being unable to type into overlay when -nolegacy is on
04/08/2024 [spice2x]
Fix Discord rich presence app IDs
03/31/2024 [spice2x]
Fix IC CARD UNIT ERROR in UFC when COM ports are present
03/24/2024 [spice2x]
Fix Ongaku Paradise crash on boot due to VFS redirection
Address all compiler warnings
03/17/2024 [spice2x]
SMX pad lights output
Fix DDR pad input for P4IO / BIO2
Option aliases for IIDX/SDVX subscreen disable
03/16/2024 [spice2x]
I/O for DDR white cabinet type (P4IO)
Remove background animation from configurator
Compiler updates
03/06/2024 [spice2x]
Lights output for DDR gold cabinet type
02/13/2024 [spice2x]
Fix lights not updating over API in configurator
CCJ mouse trackball improvements
Make IIDX/SDVX native touch the default when -sp2x-nod3d9devhook is on
Remove log spam in IIDX
02/02/2024 [spice2x]
Add missing NVCUDA stubs for cuStreamCreate and cuStreamDestroy_v2
02/01/2024 [spice2x]
QuizKnock STADIUM support
Busou Shinki analog joystick support
01/30/2024 [spice2x]
Add woofer lights to popn HD mode
Prevent crash when both -graphics-force-single-adapter and
-sp2x-nod3d9devhook are enabled
01/27/2024 [spice2x]
Add Disable D3D9 Device Hook option (-sp2x-nod3d9devhook)
01/20/2024 [spice2x]
Force Exit overlay hotkey
Improve Low Latency Audio to work with more games
Charge Machine I/O fixes
UI tweaks (API tab, Development tab)
01/07/2024 [spice2x]
Chase Chase Jokers improvements (trackball and vsync fix)
01/06/2024 [spice2x]
Chase Chase Jokers improvements (cmd line args, trackball sensitivity)
01/05/2024 [spice2x]
Chase Chase Jokers support
01/01/2024 [spice2x]
UI tweaks - menu bar in configurator window
IIDX: scan for SOUND_OUTPUT_DEVICE in binary and log message
Switch from -Ofast to -O2
Remove ImGui demo and debug files from release (reduced binary size)
12/30/2023 [spice2x]
Add NVAPI Block option (-sp2x-nonvapi)
SDVX: Auto Card Insert disables itself after a timeout to prevent soft lock
12/27/2023 [spice2x]
Prevent SDVX from dumping PATH variable to log
Stubs for NVIDIA DLLs (nvcuda.dll, nvcuvid.dll, nvEncodeAPI64.dll)
UI tweaks
12/25/2023 [spice2x]
Fix Lock Cursor option not updating capture area in windowed mode
Update TDJ rom file hooks
12/23/2023 [spice2x]
Remove the need for hex edits to fix note scroll speed in SDVX VM on NVIDIA
Improvements to -graphics-force-refresh
12/22/2023 [spice2x]
HID analog relative axis mode
Automatic process affinity fix for Gitadora
Fix -cfgpath option not working
Buttons tab UI fix for Bind Many
12/18/2023 [spice2x]
Fix Road Fighters 3D I/O error on boot
12/17/2023 [spice2x]
Integer multiplier / divisor for HID analog
(alternative to existing sensitivity option)
12/15/2023 [spice2x]
Add hooks to prevent display scaling changes when SDVX launches
12/12/2023 [spice2x]
Add support for keypad in SDVX UFC/VM mode
New DX9on12 flag with more options (-sp2x-dx9on12, replaces -9on12)
12/11/2023 [spice2x]
Fix crash at launch on AMD/Intel GPUs when nvapi DLL is present
12/09/2023 [spice2x]
Lights output for Gitadora
Lights output for DRS
Tape LED averaging algorithm option (-sp2x-tapeledalgo)
Fix occasional crash on shutdown
11/23/2023 [spice2x]
Low latency shared audio option (-sp2x-lowlatencysharedaudio)
Automatic dev/raw/* folder creation for popn19-21
Move overlay bindings to Overlay tab
11/17/2023 [spice2x]
Use E00401 prefix for card number generation
Volume API hooks to prevent audio volume change (enabled by default)
Small UI tweaks
11/15/2023 [spice2x]
Automatic card insert option (-sp2x-autocard)
Dump CPU features to log on startup
11/12/2023 [spice2x]
Fix crash in some games caused by linker changes in 2022
(Gitadora Exchain and older, Bone Eater, etc)
Add IIDX native touch option (-sp2x-iidxnativetouch)
Fix V-Sync issues with auto-screen-orientation
11/04/2023 [spice2x]
Option for NVIDIA GPU optimization (-sp2x-nvprofile)
SmartCard fixes (for -scardflip and -scardtoggle)
Small reorganization of options in configurator
10/30/2023 [spice2x]
Add touch input support for DRS
Add TDJ Windowed mode for IIDX (-sp2x-iidxtdjw)
Fix -iidxasio not working in some versions of IIDX
10/22/2023 [spice2x]
Add E-spec I/O emulation for IIDX 30+
Fix IIDX TDJ launching at 60Hz for some users
Add workaround for buggy ASIO drivers that lock up on close
Hide Insert Card overlay by default, add option to show it again
09/29/2023 [spice2x] 09/29/2023 [spice2x]
Fix subscreen not updating in certain versions of EG. Fix subscreen not updating in certain versions of EG.
Detect long paths and log a warning message. Detect long paths and log a warning message.
@@ -15,19 +405,19 @@
Update font for IIDX segment display Update font for IIDX segment display
New option: auto show FPS window New option: auto show FPS window
04/16/2023 [spice2x] (beta) 04/16/2023 [spice2x]
New feature: window resize. Added new options to change window size and New feature: window resize. Added new options to change window size and
position on launch. Screen Resize window (F11) also updated with new position on launch. Screen Resize window (F11) also updated with new
controls controls
Screen Resize settings are now per-game Screen Resize settings are now per-game
Misc bug fixes Misc bug fixes
04/09/2023 [spice2x] (beta) 04/09/2023 [spice2x]
Add IIDX LED ticker (segment display) as subscreen overlay for LDJ Add IIDX LED ticker (segment display) as subscreen overlay for LDJ
Add I/O panel window for all games, special support for IIDX, DDR, GFDM Add I/O panel window for all games, special support for IIDX, DDR, GFDM
Add options to automatically show certain windows on game launch Add options to automatically show certain windows on game launch
04/03/2023 [spice2x] (beta) 04/03/2023 [spice2x]
Move/resize for IIDX/SDVX subscreen overlay window Move/resize for IIDX/SDVX subscreen overlay window
New options to control subscreen overlay window New options to control subscreen overlay window
Turn common config mistakes into fatal error messages Turn common config mistakes into fatal error messages
+3
View File
@@ -388,6 +388,9 @@ static void easrv_worker() {
} }
void easrv_start(unsigned short port, bool maintenance, int backlog, int thread_count) { void easrv_start(unsigned short port, bool maintenance, int backlog, int thread_count) {
if (avs::game::is_model("UJK")) {
log_fatal("easrv", "easrv is currently non-functional for Chase Chase Jokers; turn off -ea");
}
// WSA startup // WSA startup
WSADATA wsa_data; WSADATA wsa_data;
-8614
View File
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -7,6 +7,7 @@
#include "cardio_window.h" #include "cardio_window.h"
bool CARDIO_RUNNER_FLIP = false; bool CARDIO_RUNNER_FLIP = false;
bool CARDIO_RUNNER_TOGGLE = false;
static bool CARDIO_RUNNER_INITIALIZED = false; static bool CARDIO_RUNNER_INITIALIZED = false;
static std::thread* CARDIO_RUNNER_THREAD = nullptr; static std::thread* CARDIO_RUNNER_THREAD = nullptr;
static HWND CARDIO_RUNNER_HWND = NULL; static HWND CARDIO_RUNNER_HWND = NULL;
@@ -71,8 +72,17 @@ void cardio_runner_start(bool scan_hid) {
// if card not empty // if card not empty
if (*((uint64_t*) &device->usage_value[0]) > 0) { if (*((uint64_t*) &device->usage_value[0]) > 0) {
bool flip_order = CARDIO_RUNNER_FLIP;
if (CARDIO_RUNNER_FLIP) {
log_info("cardio", "Flip order of readers since flip option is set");
}
if (CARDIO_RUNNER_TOGGLE && (GetKeyState(VK_NUMLOCK) & 1) > 0) {
log_info("cardio", "Flip order of readers since Num Lock is on");
flip_order = !flip_order;
}
// insert card // insert card
if (CARDIO_RUNNER_FLIP) if (flip_order)
eamuse_card_insert((int) (device_no + 1) & 1, &device->usage_value[0]); eamuse_card_insert((int) (device_no + 1) & 1, &device->usage_value[0]);
else else
eamuse_card_insert((int) device_no & 1, &device->usage_value[0]); eamuse_card_insert((int) device_no & 1, &device->usage_value[0]);
+1
View File
@@ -2,6 +2,7 @@
#define SPICETOOLS_CARDIO_RUNNER_H #define SPICETOOLS_CARDIO_RUNNER_H
extern bool CARDIO_RUNNER_FLIP; extern bool CARDIO_RUNNER_FLIP;
extern bool CARDIO_RUNNER_TOGGLE;
void cardio_runner_start(bool scan_hid); void cardio_runner_start(bool scan_hid);
void cardio_runner_stop(); void cardio_runner_stop();
+4
View File
@@ -0,0 +1,4 @@
---
Language: Cpp
BasedOnStyle: Google
...
+36
View File
@@ -0,0 +1,36 @@
# Project Files unneeded by docker
.git
.gitignore
.github
.dockerignore
.clang-format
appveyor.yml
.travis.yml
AUTHORS
CONTRIBUTING.md
CONTRIBUTORS
LICENSE
README.md
bazel/ci/Makefile
bazel/ci/docker
bazel/ci/doc
cmake/ci/Makefile
cmake/ci/docker
cmake/ci/doc
cmake/ci/cache
build/
cmake_build/
build_cross/
cmake-build-*/
out/
# Editor directories and files
.idea/
.vagrant/
.vscode/
.vs/
*.user
*.swp
+19
View File
@@ -0,0 +1,19 @@
# Build folders
build/
cmake_build/
build_cross/
cmake-build-*/
out/
# IDEs / CI temp files
.idea/
.vagrant/
.vscode/
.vs/
*.swp
# Bazel artifacts
**/bazel-*
# Per-user bazelrc files
user.bazelrc
+21
View File
@@ -0,0 +1,21 @@
---
dataSource: "prs"
ignoreLabels:
- "Apple M1"
- "duplicate"
- "help wanted"
- "invalid"
- "question"
- "wontfix"
onlyMilestones: false
groupBy:
"API Change":
- "API Change"
"New features / Enhancements":
- "enhancement"
- "internal"
"Bug Fixes":
- "bug"
"Misc":
- "misc"
changelogFilename: "CHANGELOG.md"
+378
View File
@@ -0,0 +1,378 @@
# cpu_features, a cross platform C99 library to get cpu features at runtime.
load("@bazel_skylib//lib:selects.bzl", "selects")
load("//:bazel/platforms.bzl", "PLATFORM_CPU_ARM", "PLATFORM_CPU_ARM64", "PLATFORM_CPU_MIPS", "PLATFORM_CPU_PPC", "PLATFORM_CPU_RISCV32", "PLATFORM_CPU_RISCV64", "PLATFORM_CPU_X86_64")
load("//:bazel/platforms.bzl", "PLATFORM_OS_MACOS")
package(
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
exports_files(["LICENSE"])
INCLUDES = ["include"]
C99_FLAGS = [
"-std=c99",
"-Wall",
"-Wextra",
"-Wmissing-declarations",
"-Wmissing-prototypes",
"-Wno-implicit-fallthrough",
"-Wno-unused-function",
"-Wold-style-definition",
"-Wshadow",
"-Wsign-compare",
"-Wstrict-prototypes",
]
cc_library(
name = "cpu_features_macros",
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = ["include/cpu_features_macros.h"],
)
cc_library(
name = "cpu_features_cache_info",
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = ["include/cpu_features_cache_info.h"],
deps = [":cpu_features_macros"],
)
cc_library(
name = "bit_utils",
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = ["include/internal/bit_utils.h"],
deps = [":cpu_features_macros"],
)
cc_test(
name = "bit_utils_test",
srcs = ["test/bit_utils_test.cc"],
includes = INCLUDES,
deps = [
":bit_utils",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "memory_utils",
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = [
"src/copy.inl",
"src/equals.inl",
],
)
cc_library(
name = "string_view",
srcs = [
"src/string_view.c",
],
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = ["include/internal/string_view.h"],
deps = [
":cpu_features_macros",
":memory_utils",
],
)
cc_test(
name = "string_view_test",
srcs = ["test/string_view_test.cc"],
includes = INCLUDES,
deps = [
":string_view",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "filesystem",
srcs = ["src/filesystem.c"],
copts = C99_FLAGS,
includes = INCLUDES,
textual_hdrs = ["include/internal/filesystem.h"],
deps = [":cpu_features_macros"],
)
cc_library(
name = "filesystem_for_testing",
testonly = 1,
srcs = [
"src/filesystem.c",
"test/filesystem_for_testing.cc",
],
hdrs = [
"include/internal/filesystem.h",
"test/filesystem_for_testing.h",
],
defines = ["CPU_FEATURES_MOCK_FILESYSTEM"],
includes = INCLUDES,
deps = [
":cpu_features_macros",
],
)
cc_library(
name = "stack_line_reader",
srcs = ["src/stack_line_reader.c"],
copts = C99_FLAGS,
defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
includes = INCLUDES,
textual_hdrs = ["include/internal/stack_line_reader.h"],
deps = [
":cpu_features_macros",
":filesystem",
":string_view",
],
)
cc_test(
name = "stack_line_reader_test",
srcs = [
"include/internal/stack_line_reader.h",
"src/stack_line_reader.c",
"test/stack_line_reader_test.cc",
],
defines = ["STACK_LINE_READER_BUFFER_SIZE=16"],
includes = INCLUDES,
deps = [
":cpu_features_macros",
":filesystem_for_testing",
":string_view",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "stack_line_reader_to_use_with_filesystem_for_testing",
testonly = 1,
srcs = ["src/stack_line_reader.c"],
hdrs = ["include/internal/stack_line_reader.h"],
copts = C99_FLAGS,
defines = ["STACK_LINE_READER_BUFFER_SIZE=1024"],
includes = INCLUDES,
deps = [
":cpu_features_macros",
":filesystem_for_testing",
":string_view",
],
)
cc_library(
name = "hwcaps",
srcs = ["src/hwcaps.c"],
copts = C99_FLAGS,
defines = selects.with_or({
PLATFORM_OS_MACOS: ["HAVE_DLFCN_H"],
"//conditions:default": ["HAVE_STRONG_GETAUXVAL"],
}),
includes = INCLUDES,
textual_hdrs = ["include/internal/hwcaps.h"],
deps = [
":cpu_features_macros",
":filesystem",
":string_view",
],
)
cc_library(
name = "hwcaps_for_testing",
testonly = 1,
srcs = [
"src/hwcaps.c",
"test/hwcaps_for_testing.cc",
],
hdrs = [
"include/internal/hwcaps.h",
"test/hwcaps_for_testing.h",
],
defines = [
"CPU_FEATURES_MOCK_GET_ELF_HWCAP_FROM_GETAUXVAL",
"CPU_FEATURES_TEST",
],
includes = INCLUDES,
deps = [
":cpu_features_macros",
":filesystem_for_testing",
":string_view",
],
)
cc_library(
name = "cpuinfo",
srcs = selects.with_or({
PLATFORM_CPU_X86_64: [
"src/impl_x86_freebsd.c",
"src/impl_x86_linux_or_android.c",
"src/impl_x86_macos.c",
"src/impl_x86_windows.c",
],
PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
PLATFORM_CPU_ARM64: [
"src/impl_aarch64_linux_or_android.c",
"src/impl_aarch64_macos_or_iphone.c",
"src/impl_aarch64_windows.c",
],
PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
PLATFORM_CPU_RISCV32: ["src/impl_riscv_linux.c"],
PLATFORM_CPU_RISCV64: ["src/impl_riscv_linux.c"],
}),
hdrs = selects.with_or({
PLATFORM_CPU_X86_64: [
"include/cpuinfo_x86.h",
"include/internal/cpuid_x86.h",
"include/internal/windows_utils.h",
],
PLATFORM_CPU_ARM: ["include/cpuinfo_arm.h"],
PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
PLATFORM_CPU_RISCV32: ["include/cpuinfo_riscv.h"],
PLATFORM_CPU_RISCV64: ["include/cpuinfo_riscv.h"],
}),
copts = C99_FLAGS,
defines = selects.with_or({
PLATFORM_OS_MACOS: ["HAVE_SYSCTLBYNAME"],
"//conditions:default": [],
}),
includes = INCLUDES,
textual_hdrs = selects.with_or({
PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
PLATFORM_CPU_ARM64: ["src/impl_aarch64__base_implementation.inl"],
"//conditions:default": [],
}) + [
"src/define_introspection.inl",
"src/define_introspection_and_hwcaps.inl",
],
deps = [
":bit_utils",
":cpu_features_cache_info",
":cpu_features_macros",
":filesystem",
":hwcaps",
":memory_utils",
":stack_line_reader",
":string_view",
],
)
cc_library(
name = "cpuinfo_for_testing",
testonly = 1,
srcs = selects.with_or({
PLATFORM_CPU_X86_64: [
"src/impl_x86_freebsd.c",
"src/impl_x86_linux_or_android.c",
"src/impl_x86_macos.c",
"src/impl_x86_windows.c",
],
PLATFORM_CPU_ARM: ["src/impl_arm_linux_or_android.c"],
PLATFORM_CPU_ARM64: [
"src/impl_aarch64_linux_or_android.c",
"src/impl_aarch64_macos_or_iphone.c",
"src/impl_aarch64_windows.c",
],
PLATFORM_CPU_MIPS: ["src/impl_mips_linux_or_android.c"],
PLATFORM_CPU_PPC: ["src/impl_ppc_linux.c"],
PLATFORM_CPU_RISCV32: ["src/impl_riscv_linux.c"],
PLATFORM_CPU_RISCV64: ["src/impl_riscv_linux.c"],
}),
hdrs = selects.with_or({
PLATFORM_CPU_X86_64: [
"include/cpuinfo_x86.h",
"include/internal/cpuid_x86.h",
"include/internal/windows_utils.h",
],
PLATFORM_CPU_ARM: ["include/cpuinfo_arm.h"],
PLATFORM_CPU_ARM64: ["include/cpuinfo_aarch64.h"],
PLATFORM_CPU_MIPS: ["include/cpuinfo_mips.h"],
PLATFORM_CPU_PPC: ["include/cpuinfo_ppc.h"],
PLATFORM_CPU_RISCV32: ["include/cpuinfo_riscv.h"],
PLATFORM_CPU_RISCV64: ["include/cpuinfo_riscv.h"],
}),
copts = C99_FLAGS,
defines = selects.with_or({
PLATFORM_CPU_X86_64: ["CPU_FEATURES_MOCK_CPUID_X86"],
"//conditions:default": [],
}) + selects.with_or({
PLATFORM_OS_MACOS: ["HAVE_SYSCTLBYNAME"],
"//conditions:default": [],
}),
includes = INCLUDES,
textual_hdrs = selects.with_or({
PLATFORM_CPU_X86_64: ["src/impl_x86__base_implementation.inl"],
PLATFORM_CPU_ARM64: ["src/impl_aarch64__base_implementation.inl"],
"//conditions:default": [],
}) + [
"src/define_introspection.inl",
"src/define_introspection_and_hwcaps.inl",
],
deps = [
":bit_utils",
":cpu_features_cache_info",
":cpu_features_macros",
":filesystem_for_testing",
":hwcaps_for_testing",
":memory_utils",
":stack_line_reader_to_use_with_filesystem_for_testing",
":string_view",
],
)
cc_test(
name = "cpuinfo_test",
srcs = selects.with_or({
PLATFORM_CPU_ARM64: ["test/cpuinfo_aarch64_test.cc"],
PLATFORM_CPU_ARM: ["test/cpuinfo_arm_test.cc"],
PLATFORM_CPU_MIPS: ["test/cpuinfo_mips_test.cc"],
PLATFORM_CPU_PPC: ["test/cpuinfo_ppc_test.cc"],
PLATFORM_CPU_RISCV32: ["test/cpuinfo_riscv_test.cc"],
PLATFORM_CPU_RISCV64: ["test/cpuinfo_riscv_test.cc"],
PLATFORM_CPU_X86_64: ["test/cpuinfo_x86_test.cc"],
}),
includes = INCLUDES,
deps = [
":cpuinfo_for_testing",
":filesystem_for_testing",
":hwcaps_for_testing",
":string_view",
"@com_google_googletest//:gtest_main",
],
)
cc_binary(
name = "list_cpu_features",
srcs = ["src/utils/list_cpu_features.c"],
copts = C99_FLAGS,
includes = INCLUDES,
deps = [
":bit_utils",
":cpu_features_macros",
":cpuinfo",
],
)
cc_library(
name = "ndk_compat",
srcs = ["ndk_compat/cpu-features.c"],
copts = C99_FLAGS,
includes = INCLUDES + ["ndk_compat"],
textual_hdrs = ["ndk_compat/cpu-features.h"],
deps = [
":cpu_features_macros",
":cpuinfo",
":filesystem",
":stack_line_reader",
":string_view",
],
)
+302
View File
@@ -0,0 +1,302 @@
cmake_minimum_required(VERSION 3.13)
# option() honors normal variables.
# see: https://cmake.org/cmake/help/git-stage/policy/CMP0077.html
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()
project(CpuFeatures VERSION 0.9.0 LANGUAGES C)
set(CMAKE_C_STANDARD 99)
# when cpu_features is included as subproject (i.e. using add_subdirectory(cpu_features))
# in the source tree of a project that uses it, test rules are disabled.
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_TESTING "Enable test rule" OFF)
else()
option(BUILD_TESTING "Enable test rule" ON)
endif()
# Default Build Type to be Release
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel."
FORCE)
endif(NOT CMAKE_BUILD_TYPE)
# An option to enable/disable the executable target list_cpu_features.
# Disable it by default if the project is included as a subproject.
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_EXECUTABLE "Build list_cpu_features executable." OFF)
else()
option(BUILD_EXECUTABLE "Build list_cpu_features executable." ON)
endif()
# An option which allows to switch off install steps. Useful for embedding.
# Disable it by default if the project is included as a subproject.
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(ENABLE_INSTALL "Enable install targets" OFF)
else()
option(ENABLE_INSTALL "Enable install targets" ON)
endif()
# BUILD_SHARED_LIBS is a standard CMake variable, but we declare it here to make
# it prominent in the GUI.
# cpu_features uses bit-fields which are - to some extends - implementation-defined (see https://en.cppreference.com/w/c/language/bit_field).
# As a consequence it is discouraged to use cpu_features as a shared library because different compilers may interpret the code in different ways.
# Prefer static linking from source whenever possible.
option(BUILD_SHARED_LIBS "Build library as shared." OFF)
# Force PIC on unix when building shared libs
# see: https://en.wikipedia.org/wiki/Position-independent_code
if(BUILD_SHARED_LIBS AND UNIX)
option(CMAKE_POSITION_INDEPENDENT_CODE "Build with Position Independant Code." ON)
endif()
include(CheckIncludeFile)
include(CheckSymbolExists)
include(GNUInstallDirs)
macro(setup_include_and_definitions TARGET_NAME)
target_include_directories(${TARGET_NAME}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include/internal>
)
target_compile_definitions(${TARGET_NAME}
PUBLIC STACK_LINE_READER_BUFFER_SIZE=1024
)
endmacro()
set(PROCESSOR_IS_MIPS FALSE)
set(PROCESSOR_IS_ARM FALSE)
set(PROCESSOR_IS_AARCH64 FALSE)
set(PROCESSOR_IS_X86 FALSE)
set(PROCESSOR_IS_POWER FALSE)
set(PROCESSOR_IS_S390X FALSE)
set(PROCESSOR_IS_RISCV FALSE)
set(PROCESSOR_IS_LOONGARCH FALSE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
set(PROCESSOR_IS_MIPS TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(^aarch64)|(^arm64)|(^ARM64)")
set(PROCESSOR_IS_AARCH64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
set(PROCESSOR_IS_ARM TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(x86_64)|(AMD64|amd64)|(^i.86$)")
set(PROCESSOR_IS_X86 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
set(PROCESSOR_IS_POWER TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(s390x)")
set(PROCESSOR_IS_S390X TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv")
set(PROCESSOR_IS_RISCV TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^loongarch")
set(PROCESSOR_IS_LOONGARCH TRUE)
endif()
macro(add_cpu_features_headers_and_sources HDRS_LIST_NAME SRCS_LIST_NAME)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpu_features_macros.h)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpu_features_cache_info.h)
file(GLOB IMPL_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/impl_*.c")
list(APPEND ${SRCS_LIST_NAME} ${IMPL_SOURCES})
if(PROCESSOR_IS_MIPS)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_mips.h)
elseif(PROCESSOR_IS_ARM)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_arm.h)
elseif(PROCESSOR_IS_AARCH64)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_aarch64.h)
list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/internal/windows_utils.h)
elseif(PROCESSOR_IS_X86)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_x86.h)
list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/internal/cpuid_x86.h)
list(APPEND ${SRCS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/internal/windows_utils.h)
elseif(PROCESSOR_IS_POWER)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_ppc.h)
elseif(PROCESSOR_IS_S390X)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_s390x.h)
elseif(PROCESSOR_IS_RISCV)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_riscv.h)
elseif(PROCESSOR_IS_LOONGARCH)
list(APPEND ${HDRS_LIST_NAME} ${PROJECT_SOURCE_DIR}/include/cpuinfo_loongarch.h)
else()
message(FATAL_ERROR "Unsupported architectures ${CMAKE_SYSTEM_PROCESSOR}")
endif()
endmacro()
#
# library : utils
#
add_library(utils OBJECT
${PROJECT_SOURCE_DIR}/include/internal/bit_utils.h
${PROJECT_SOURCE_DIR}/include/internal/filesystem.h
${PROJECT_SOURCE_DIR}/include/internal/stack_line_reader.h
${PROJECT_SOURCE_DIR}/include/internal/string_view.h
${PROJECT_SOURCE_DIR}/src/filesystem.c
${PROJECT_SOURCE_DIR}/src/stack_line_reader.c
${PROJECT_SOURCE_DIR}/src/string_view.c
)
setup_include_and_definitions(utils)
#
# library : unix_based_hardware_detection
#
if(UNIX)
add_library(unix_based_hardware_detection OBJECT
${PROJECT_SOURCE_DIR}/include/internal/hwcaps.h
${PROJECT_SOURCE_DIR}/src/hwcaps.c
)
setup_include_and_definitions(unix_based_hardware_detection)
check_include_file(dlfcn.h HAVE_DLFCN_H)
if(HAVE_DLFCN_H)
target_compile_definitions(unix_based_hardware_detection PRIVATE HAVE_DLFCN_H)
endif()
check_symbol_exists(getauxval "sys/auxv.h" HAVE_STRONG_GETAUXVAL)
if(HAVE_STRONG_GETAUXVAL)
target_compile_definitions(unix_based_hardware_detection PRIVATE HAVE_STRONG_GETAUXVAL)
endif()
endif()
#
# library : cpu_features
#
set (CPU_FEATURES_HDRS)
set (CPU_FEATURES_SRCS)
add_cpu_features_headers_and_sources(CPU_FEATURES_HDRS CPU_FEATURES_SRCS)
list(APPEND CPU_FEATURES_SRCS $<TARGET_OBJECTS:utils>)
if(NOT PROCESSOR_IS_X86 AND UNIX)
list(APPEND CPU_FEATURES_SRCS $<TARGET_OBJECTS:unix_based_hardware_detection>)
endif()
add_library(cpu_features ${CPU_FEATURES_HDRS} ${CPU_FEATURES_SRCS})
set_target_properties(cpu_features PROPERTIES PUBLIC_HEADER "${CPU_FEATURES_HDRS}")
setup_include_and_definitions(cpu_features)
target_link_libraries(cpu_features PUBLIC ${CMAKE_DL_LIBS})
target_include_directories(cpu_features
PUBLIC $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/cpu_features>
)
if(APPLE)
target_compile_definitions(cpu_features PRIVATE HAVE_SYSCTLBYNAME)
endif()
add_library(CpuFeatures::cpu_features ALIAS cpu_features)
#
# program : list_cpu_features
#
if(BUILD_EXECUTABLE)
add_executable(list_cpu_features ${PROJECT_SOURCE_DIR}/src/utils/list_cpu_features.c)
target_link_libraries(list_cpu_features PRIVATE cpu_features)
add_executable(CpuFeatures::list_cpu_features ALIAS list_cpu_features)
endif()
#
# ndk_compat
#
if(ANDROID)
add_subdirectory(ndk_compat)
endif()
#
# tests
#
include(CTest)
if(BUILD_TESTING)
# Automatically incorporate googletest into the CMake Project if target not
# found.
enable_language(CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # prefer use of -std14 instead of -gnustd14
if(NOT TARGET gtest OR NOT TARGET gmock_main)
# Download and unpack googletest at configure time.
configure_file(
cmake/googletest.CMakeLists.txt.in
googletest-download/CMakeLists.txt
)
execute_process(
COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download)
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker settings on
# Windows.
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This defines the gtest and
# gtest_main targets.
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
endif()
add_subdirectory(test)
endif()
#
# Install cpu_features and list_cpu_features
#
if(ENABLE_INSTALL)
include(GNUInstallDirs)
install(TARGETS cpu_features
EXPORT CpuFeaturesTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(BUILD_EXECUTABLE)
install(TARGETS list_cpu_features
EXPORT CpuFeaturesTargets
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cpu_features
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
install(EXPORT CpuFeaturesTargets
NAMESPACE CpuFeatures::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures
COMPONENT Devel
)
include(CMakePackageConfigHelpers)
configure_package_config_file(cmake/CpuFeaturesConfig.cmake.in
"${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
COMPATIBILITY SameMajorVersion
)
install(
FILES
"${PROJECT_BINARY_DIR}/CpuFeaturesConfig.cmake"
"${PROJECT_BINARY_DIR}/CpuFeaturesConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CpuFeatures"
COMPONENT Devel
)
endif()
+23
View File
@@ -0,0 +1,23 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
+230
View File
@@ -0,0 +1,230 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--------------------------------------------------------------------------------
For files in the `ndk_compat` folder:
--------------------------------------------------------------------------------
Copyright (C) 2010 The Android Open Source Project
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
+282
View File
@@ -0,0 +1,282 @@
# cpu_features
A cross-platform C library to retrieve CPU features (such as available
instructions) at runtime.
# GitHub-CI Status
[comment]: <> (The following lines are generated by "scripts/generate_badges.d" that you can run online https://run.dlang.io/)
| | Linux | FreeBSD | MacOS | Windows |
| :-- | --: | --: | --: | --: |
| amd64 | [![CMake][i1a0]][l1a0]<br/>[![Bazel][i1a1]][l1a1] | [![CMake][i2a0]][l2a0]<br/>![Bazel][d1] | [![CMake][i3a0]][l3a0]<br/>[![Bazel][i3a1]][l3a1] | [![CMake][i4a0]][l4a0]<br/>![Bazel][d1] |
| AArch64 | [![CMake][i1b0]][l1b0]<br/>[![Bazel][i1b1]][l1b1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| ARM | [![CMake][i1c0]][l1c0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| MIPS | [![CMake][i1d0]][l1d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| POWER | [![CMake][i1e0]][l1e0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| RISCV | [![CMake][i1f0]][l1f0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| LOONGARCH | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
| s390x | [![CMake][i1h0]][l1h0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] | ![CMake][d0]<br/>![Bazel][d1] |
[d0]: https://img.shields.io/badge/n%2Fa-lightgrey?&logo=cmake
[d1]: https://img.shields.io/badge/n%2Fa-lightgrey?&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
[i1a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1a1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_linux_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
[i1b0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/aarch64_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1b1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/aarch64_linux_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
[i1c0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/arm_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1d0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/mips_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1e0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/power_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1f0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/riscv_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i1h0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/s390x_linux_cmake.yml?branch=main&event=push&label=&logo=cmake
[i2a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_freebsd_cmake.yml?branch=main&event=push&label=&logo=cmake
[i3a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_macos_cmake.yml?branch=main&event=push&label=&logo=cmake
[i3a1]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_macos_bazel.yml?branch=main&event=push&label=&logo=data:image/svg%2bxml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNiAuMTZsNS43ODYgNS43ODZMNiAxMS43MzIuMjE0IDUuOTQ2IDYgLjE2MXpNMCA2LjIxNFYxMmw1Ljc4NiA1Ljc4NlYxMkwwIDYuMjE0ek0xOCAuMTZsNS43ODYgNS43ODZMMTggMTEuNzMybC01Ljc4Ni01Ljc4NkwxOCAuMTYxek0yNCA2LjIxNFYxMmwtNS43ODYgNS43ODZWMTJMMjQgNi4yMTR6TTEyIDYuMTZsNS43ODYgNS43ODZMMTIgMTcuNzMybC01Ljc4Ni01Ljc4NkwxMiA2LjE2MXpNMTEuODQgMTguMDU0djUuNzg1bC01Ljc4Ni01Ljc4NXYtNS43ODZsNS43ODUgNS43ODZ6TTEyLjE2IDE4LjA1NGw1Ljc4Ni01Ljc4NnY1Ljc4NmwtNS43ODUgNS43ODV2LTUuNzg1eiIgc3Ryb2tlPSJ0cmFuc3BhcmVudCIgZmlsbD0id2hpdGUiLz48L3N2Zz4=
[i4a0]: https://img.shields.io/github/actions/workflow/status/google/cpu_features/amd64_windows_cmake.yml?branch=main&event=push&label=&logo=cmake
[l1a0]: https://github.com/google/cpu_features/actions/workflows/amd64_linux_cmake.yml
[l1a1]: https://github.com/google/cpu_features/actions/workflows/amd64_linux_bazel.yml
[l1b0]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux_cmake.yml
[l1b1]: https://github.com/google/cpu_features/actions/workflows/aarch64_linux_bazel.yml
[l1c0]: https://github.com/google/cpu_features/actions/workflows/arm_linux_cmake.yml
[l1d0]: https://github.com/google/cpu_features/actions/workflows/mips_linux_cmake.yml
[l1e0]: https://github.com/google/cpu_features/actions/workflows/power_linux_cmake.yml
[l1f0]: https://github.com/google/cpu_features/actions/workflows/riscv_linux_cmake.yml
[l1h0]: https://github.com/google/cpu_features/actions/workflows/s390x_linux_cmake.yml
[l2a0]: https://github.com/google/cpu_features/actions/workflows/amd64_freebsd_cmake.yml
[l3a0]: https://github.com/google/cpu_features/actions/workflows/amd64_macos_cmake.yml
[l3a1]: https://github.com/google/cpu_features/actions/workflows/amd64_macos_bazel.yml
[l4a0]: https://github.com/google/cpu_features/actions/workflows/amd64_windows_cmake.yml
## Table of Contents
- [Design Rationale](#rationale)
- [Code samples](#codesample)
- [Running sample code](#usagesample)
- [What's supported](#support)
- [Android NDK's drop in replacement](#ndk)
- [License](#license)
- [Build with cmake](#cmake)
- [Community Bindings](#bindings)
<a name="rationale"></a>
## Design Rationale
- **Simple to use.** See the snippets below for examples.
- **Extensible.** Easy to add missing features or architectures.
- **Compatible with old compilers** and available on many architectures so it
can be used widely. To ensure that cpu_features works on as many platforms
as possible, we implemented it in a highly portable version of C: C99.
- **Sandbox-compatible.** The library uses a variety of strategies to cope
with sandboxed environments or when `cpuid` is unavailable. This is useful
when running integration tests in hermetic environments.
- **Thread safe, no memory allocation, and raises no exceptions.**
cpu_features is suitable for implementing fundamental libc functions like
`malloc`, `memcpy`, and `memcmp`.
- **Unit tested.**
<a name="codesample"></a>
## Code samples
**Note:** For C++ code, the library functions are defined in the `cpu_features` namespace.
### Checking features at runtime
Here's a simple example that executes a codepath if the CPU supports both the
AES and the SSE4.2 instruction sets:
```c
#include "cpuinfo_x86.h"
// For C++, add `using namespace cpu_features;`
static const X86Features features = GetX86Info().features;
void Compute(void) {
if (features.aes && features.sse4_2) {
// Run optimized code.
} else {
// Run standard code.
}
}
```
### Caching for faster evaluation of complex checks
If you wish, you can read all the features at once into a global variable, and
then query for the specific features you care about. Below, we store all the ARM
features and then check whether AES and NEON are supported.
```c
#include <stdbool.h>
#include "cpuinfo_arm.h"
// For C++, add `using namespace cpu_features;`
static const ArmFeatures features = GetArmInfo().features;
static const bool has_aes_and_neon = features.aes && features.neon;
// use has_aes_and_neon.
```
This is a good approach to take if you're checking for combinations of features
when using a compiler that is slow to extract individual bits from bit-packed
structures.
### Checking compile time flags
The following code determines whether the compiler was told to use the AVX
instruction set (e.g., `g++ -mavx`) and sets `has_avx` accordingly.
```c
#include <stdbool.h>
#include "cpuinfo_x86.h"
// For C++, add `using namespace cpu_features;`
static const X86Features features = GetX86Info().features;
static const bool has_avx = CPU_FEATURES_COMPILED_X86_AVX || features.avx;
// use has_avx.
```
`CPU_FEATURES_COMPILED_X86_AVX` is set to 1 if the compiler was instructed to
use AVX and 0 otherwise, combining compile time and runtime knowledge.
### Rejecting poor hardware implementations based on microarchitecture
On x86, the first incarnation of a feature in a microarchitecture might not be
the most efficient (e.g. AVX on Sandy Bridge). We provide a function to retrieve
the underlying microarchitecture so you can decide whether to use it.
Below, `has_fast_avx` is set to 1 if the CPU supports the AVX instruction
set&mdash;but only if it's not Sandy Bridge.
```c
#include <stdbool.h>
#include "cpuinfo_x86.h"
// For C++, add `using namespace cpu_features;`
static const X86Info info = GetX86Info();
static const X86Microarchitecture uarch = GetX86Microarchitecture(&info);
static const bool has_fast_avx = info.features.avx && uarch != INTEL_SNB;
// use has_fast_avx.
```
This feature is currently available only for x86 microarchitectures.
<a name="usagesample"></a>
### Running sample code
Building `cpu_features` (check [quickstart](#quickstart) below) brings a small executable to test the library.
```shell
% ./build/list_cpu_features
arch : x86
brand : Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz
family : 6 (0x06)
model : 45 (0x2D)
stepping : 7 (0x07)
uarch : INTEL_SNB
flags : aes,avx,cx16,smx,sse4_1,sse4_2,ssse3
```
```shell
% ./build/list_cpu_features --json
{"arch":"x86","brand":" Intel(R) Xeon(R) CPU E5-1650 0 @ 3.20GHz","family":6,"model":45,"stepping":7,"uarch":"INTEL_SNB","flags":["aes","avx","cx16","smx","sse4_1","sse4_2","ssse3"]}
```
<a name="support"></a>
## What's supported
| | x86³ | AArch64 | ARM | MIPS⁴ | POWER | RISCV | Loongarch | s390x |
|---------|:----:|:-------:|:-------:|:-------:|:-------:|:-------:|:---------:|:-------:|
| Linux | yes² | yes¹ | yes¹ | yes¹ | yes¹ | yes¹ | yes¹ | yes¹ |
| FreeBSD | yes² | not yet | not yet | not yet | not yet | N/A | not yet | not yet |
| MacOs | yes² | yes⁵ | N/A | N/A | N/A | N/A | N/A | N/A |
| Windows | yes² | not yet | not yet | N/A | N/A | N/A | N/A | N/A |
| Android | yes² | yes¹ | yes¹ | yes¹ | N/A | N/A | N/A | N/A |
| iOS | N/A | not yet | not yet | N/A | N/A | N/A | N/A | N/A |
1. **Features revealed from Linux.** We gather data from several sources
depending on availability:
+ from glibc's
[getauxval](https://www.gnu.org/software/libc/manual/html_node/Auxiliary-Vector.html)
+ by parsing `/proc/self/auxv`
+ by parsing `/proc/cpuinfo`
2. **Features revealed from CPU.** features are retrieved by using the `cpuid`
instruction.
3. **Microarchitecture detection.** On x86 some features are not always
implemented efficiently in hardware (e.g. AVX on Sandybridge). Exposing the
microarchitecture allows the client to reject particular microarchitectures.
4. All flavors of Mips are supported, little and big endian as well as 32/64
bits.
5. **Features revealed from sysctl.** features are retrieved by the `sysctl`
instruction.
<a name="ndk"></a>
## Android NDK's drop in replacement
[cpu_features](https://github.com/google/cpu_features) is now officially
supporting Android and offers a drop in replacement of for the NDK's [cpu-features.h](https://android.googlesource.com/platform/ndk/+/main/sources/android/cpufeatures/cpu-features.h)
, see [ndk_compat](ndk_compat) folder for details.
<a name="license"></a>
## License
The cpu_features library is licensed under the terms of the Apache license.
See [LICENSE](LICENSE) for more information.
<a name="cmake"></a>
## Build with CMake
Please check the [CMake build instructions](cmake/README.md).
<a name="quickstart"></a>
### Quickstart
- Run `list_cpu_features`
```sh
cmake -S. -Bbuild -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j
./build/list_cpu_features --json
```
_Note_: Use `--target ALL_BUILD` on the second line for `Visual Studio` and `XCode`.
- run tests
```sh
cmake -S. -Bbuild -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug -j
cmake --build build --config Debug --target test
```
_Note_: Use `--target RUN_TESTS` on the last line for `Visual Studio` and `--target RUN_TEST` for `XCode`.
- install `cpu_features`
```sh
cmake --build build --config Release --target install -v
```
_Note_: Use `--target INSTALL` for `Visual Studio`.
_Note_: When using `Makefile` or `XCode` generator, you can use
[`DESTDIR`](https://www.gnu.org/software/make/manual/html_node/DESTDIR.html)
to install on a local repository.<br>
e.g.
```sh
cmake --build build --config Release --target install -v -- DESTDIR=install
```
<a name="bindings"></a>
## Community bindings
Links provided here are not affiliated with Google but are kindly provided by the OSS Community.
- .Net
- https://github.com/toor1245/cpu_features.NET
- Python
- https://github.com/Narasimha1997/py_cpu
- Java
- https://github.com/aecsocket/cpu-features-java
_Send PR to showcase your wrapper here_
+19
View File
@@ -0,0 +1,19 @@
workspace(name = "com_google_cpufeatures")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "com_google_googletest",
tag = "release-1.11.0",
remote = "https://github.com/google/googletest.git",
)
git_repository(
name = "bazel_skylib",
tag = "1.2.0",
remote = "https://github.com/bazelbuild/bazel-skylib.git",
)
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()
@@ -0,0 +1,3 @@
# CpuFeatures CMake configuration file
include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesTargets.cmake")
@@ -0,0 +1,3 @@
# CpuFeaturesNdkCompat CMake configuration file
include("${CMAKE_CURRENT_LIST_DIR}/CpuFeaturesNdkCompatTargets.cmake")
+30
View File
@@ -0,0 +1,30 @@
# CMake build instructions
## Recommended usage : Incorporating cpu_features into a CMake project
For API / ABI compatibility reasons, it is recommended to build and use
cpu_features in a subdirectory of your project or as an embedded dependency.
This is similar to the recommended usage of the googletest framework
( https://github.com/google/googletest/blob/main/googletest/README.md )
Build and use step-by-step
1- Download cpu_features and copy it in a sub-directory in your project.
or add cpu_features as a git-submodule in your project
2- You can then use the cmake command `add_subdirectory()` to include
cpu_features directly and use the `cpu_features` target in your project.
3- Add the `CpuFeatures::cpu_features` target to the `target_link_libraries()` section of
your executable or of your library.
## Disabling tests
CMake default options for cpu_features is `Release` built type with tests
enabled. To disable testing set cmake `BUILD_TESTING` variable to `OFF`.
e.g.
```sh
cmake -S. -Bbuild -DBUILD_TESTING=OFF
```
+252
View File
@@ -0,0 +1,252 @@
PROJECT := cpu_features
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
SHA1 := $(shell git rev-parse --verify HEAD)
# General commands
.PHONY: help
BOLD=\e[1m
RESET=\e[0m
help:
@echo -e "${BOLD}SYNOPSIS${RESET}"
@echo -e "\tmake <target> [NOCACHE=1]"
@echo
@echo -e "${BOLD}DESCRIPTION${RESET}"
@echo -e "\ttest build inside docker container to have a reproductible build."
@echo
@echo -e "${BOLD}MAKE TARGETS${RESET}"
@echo -e "\t${BOLD}help${RESET}: display this help and exit."
@echo
@echo -e "\t${BOLD}amd64_<stage>${RESET}: build <stage> docker image using an Ubuntu:latest x86_64 base image."
@echo -e "\t${BOLD}save_amd64_<stage>${RESET}: Save the <stage> docker image."
@echo -e "\t${BOLD}sh_amd64_<stage>${RESET}: run a container using the <stage> docker image (debug purpose)."
@echo -e "\t${BOLD}clean_amd64_<stage>${RESET}: Remove cache and docker image."
@echo
@echo -e "\tWith ${BOLD}<stage>${RESET}:"
@echo -e "\t\t${BOLD}env${RESET}"
@echo -e "\t\t${BOLD}devel${RESET}"
@echo -e "\t\t${BOLD}build${RESET}"
@echo -e "\t\t${BOLD}test${RESET}"
@echo -e "\t\t${BOLD}install_env${RESET}"
@echo -e "\t\t${BOLD}install_devel${RESET}"
@echo -e "\t\t${BOLD}install_build${RESET}"
@echo -e "\t\t${BOLD}install_test${RESET}"
@echo -e "\te.g. 'make amd64_build'"
@echo
@echo -e "\t${BOLD}<target>_<toolchain_stage>${RESET}: build <stage> docker image for a specific toolchain target."
@echo -e "\t${BOLD}save_<target>_<toolchain_stage>${RESET}: Save the <stage> docker image for a specific platform."
@echo -e "\t${BOLD}sh_<target>_<toolchain_stage>${RESET}: run a container using the <stage> docker image specified (debug purpose)."
@echo -e "\t${BOLD}clean_<target>_<toolchain_stage>${RESET}: Remove cache and docker image."
@echo
@echo -e "\tWith ${BOLD}<target>${RESET}:"
@echo -e "\t\t${BOLD}arm-linux-gnueabihf${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}armv8l-linux-gnueabihf${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}arm-linux-gnueabi${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}armeb-linux-gnueabihf${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}armeb-linux-gnueabi${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}aarch64-linux-gnu${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}aarch64${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}aarch64_be-linux-gnu${RESET} (linaro toolchain)"
@echo -e "\t\t${BOLD}aarch64be${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}mips32${RESET} (codespace toolchain)"
@echo -e "\t\t${BOLD}mips64${RESET} (codespace toolchain)"
@echo -e "\t\t${BOLD}mips32el${RESET} (codespace toolchain)"
@echo -e "\t\t${BOLD}mips64el${RESET} (codespace toolchain)"
@echo -e "\t\t${BOLD}ppc${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}ppc64${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}ppc64le${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}riscv32${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}riscv64${RESET} (bootlin toolchain)"
@echo -e "\t\t${BOLD}s390x${RESET} (bootlin toolchain)"
@echo
@echo -e "\tWith ${BOLD}<toolchain_stage>${RESET}:"
@echo -e "\t\t${BOLD}env${RESET}"
@echo -e "\t\t${BOLD}devel${RESET}"
@echo -e "\t\t${BOLD}build${RESET}"
@echo -e "\t\t${BOLD}test${RESET}"
@echo -e "\te.g. 'make aarch64_test'"
@echo
@echo -e "\t${BOLD}<VM>${RESET}: build the vagrant <VM> virtual machine."
@echo -e "\t${BOLD}clean_<VM>${RESET}: Remove virtual machine for the specified vm."
@echo
@echo -e "\t${BOLD}<VM>${RESET}:"
@echo -e "\t\t${BOLD}freebsd${RESET} (FreeBSD)"
@echo
@echo -e "\t${BOLD}clean${RESET}: Remove cache and ALL docker images."
@echo
@echo -e "\t${BOLD}NOCACHE=1${RESET}: use 'docker build --no-cache' when building container (default use cache)."
@echo
@echo -e "branch: $(BRANCH)"
@echo -e "sha1: $(SHA1)"
# Need to add cmd_platform to PHONY otherwise target are ignored since they do not
# contain recipe (using FORCE do not work here)
.PHONY: all
all: build
# Delete all implicit rules to speed up makefile
MAKEFLAGS += --no-builtin-rules
.SUFFIXES:
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
# Keep all intermediate files
# ToDo: try to remove it later
.SECONDARY:
# Docker image name prefix.
IMAGE := ${PROJECT}
ifdef NOCACHE
DOCKER_BUILD_CMD := docker build --no-cache
else
DOCKER_BUILD_CMD := docker build
endif
DOCKER_RUN_CMD := docker run --rm --init --net=host
# $* stem
# $< first prerequist
# $@ target name
############
## NATIVE ##
############
STAGES = env devel build test install_env install_devel install_build install_test
targets_amd64 = $(addprefix amd64_, $(STAGES))
.PHONY: $(targets_amd64)
$(targets_amd64): amd64_%: docker/amd64/Dockerfile
#@docker image rm -f ${IMAGE}:amd64_$* 2>/dev/null
${DOCKER_BUILD_CMD} \
--tag ${IMAGE}:amd64_$* \
--target=$* \
-f $< \
../..
#$(info Create targets: save_amd64 $(addprefix save_amd64_, $(STAGES)) (debug).)
save_targets_amd64 = $(addprefix save_amd64_, $(STAGES))
.PHONY: $(save_targets_amd64)
$(save_targets_amd64): save_amd64_%: cache/amd64/docker_%.tar
cache/amd64/docker_%.tar: amd64_%
@rm -f $@
mkdir -p cache/amd64
docker save ${IMAGE}:amd64_$* -o $@
#$(info Create targets: $(addprefix sh_amd64_, $(STAGES)) (debug).)
sh_targets_amd64 = $(addprefix sh_amd64_, $(STAGES))
.PHONY: $(sh_targets_amd64)
$(sh_targets_amd64): sh_amd64_%: amd64_%
${DOCKER_RUN_CMD} -it --name ${IMAGE}_amd64_$* ${IMAGE}:amd64_$*
#$(info Create targets: $(addprefix clean_amd64_, $(STAGES)).)
clean_targets_amd64 = $(addprefix clean_amd64_, $(STAGES))
.PHONY: clean_amd64 $(clean_targets_amd64)
clean_amd64: $(clean_targets_amd64)
$(clean_targets_amd64): clean_amd64_%:
docker image rm -f ${IMAGE}:amd64_$* 2>/dev/null
rm -f cache/amd64/docker_$*.tar
###############
## TOOLCHAIN ##
###############
TOOLCHAIN_TARGETS = \
aarch64 aarch64be \
arm-linux-gnueabihf armv8l-linux-gnueabihf arm-linux-gnueabi armeb-linux-gnueabihf armeb-linux-gnueabi \
aarch64-linux-gnu aarch64_be-linux-gnu \
mips32 mips32el mips64 mips64el \
ppc ppc64 ppc64le \
riscv32 riscv64 \
s390x
TOOLCHAIN_STAGES = env devel build test
define toolchain-stage-target =
#$$(info STAGE: $1)
#$$(info Create targets: toolchain_$1 $(addsuffix _$1, $(TOOLCHAIN_TARGETS)).)
targets_toolchain_$1 = $(addsuffix _$1, $(TOOLCHAIN_TARGETS))
.PHONY: toolchain_$1 $$(targets_toolchain_$1)
toolchain_$1: $$(targets_toolchain_$1)
$$(targets_toolchain_$1): %_$1: docker/toolchain/Dockerfile
#@docker image rm -f ${IMAGE}:$$*_$1 2>/dev/null
${DOCKER_BUILD_CMD} \
--tag ${IMAGE}:$$*_$1 \
--build-arg TARGET=$$* \
--target=$1 \
-f $$< \
../..
#$$(info Create targets: save_toolchain_$1 $(addprefix save_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).)
save_targets_toolchain_$1 = $(addprefix save_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
.PHONY: save_toolchain_$1 $$(save_targets_toolchain_$1)
save_toolchain_$1: $$(save_targets_toolchain_$1)
$$(save_targets_toolchain_$1): save_%_$1: cache/%/docker_$1.tar
cache/%/docker_$1.tar: %_$1
@rm -f $$@
mkdir -p cache/$$*
docker save ${IMAGE}:$$*_$1 -o $$@
#$$(info Create targets: $(addprefix sh_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).)
sh_targets_toolchain_$1 = $(addprefix sh_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
.PHONY: $$(sh_targets_toolchain_$1)
$$(sh_targets_toolchain_$1): sh_%_$1: %_$1
${DOCKER_RUN_CMD} -it --name ${IMAGE}_$$*_$1 ${IMAGE}:$$*_$1
#$$(info Create targets: clean_toolchain_$1 $(addprefix clean_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))).)
clean_targets_toolchain_$1 = $(addprefix clean_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS)))
.PHONY: clean_toolchain_$1 $$(clean_targets_toolchain_$1)
clean_toolchain_$1: $$(clean_targets_toolchain_$1)
$$(clean_targets_toolchain_$1): clean_%_$1:
docker image rm -f ${IMAGE}:$$*_$1 2>/dev/null
rm -f cache/$$*/docker_$1.tar
endef
$(foreach stage,$(TOOLCHAIN_STAGES),$(eval $(call toolchain-stage-target,$(stage))))
## MERGE ##
.PHONY: clean_toolchain
clean_toolchain: $(addprefix clean_toolchain_, $(TOOLCHAIN_STAGES))
-rmdir $(addprefix cache/, $(TOOLCHAIN_TARGETS))
.PHONY: env devel build test
env: amd64_env toolchain_env
devel: amd64_devel toolchain_devel
build: amd64_build toolchain_build
test: amd64_test toolchain_test
.PHONY: install_env install_devel install_build install_test
install_env: amd64_install_env
install_devel: amd64_install_devel
install_build: amd64_install_build
install_test: amd64_install_test
#############
## VAGRANT ##
#############
VMS = freebsd
vms_targets = $(addsuffix _build, $(VMS))
.PHONY: $(vms_targets)
$(vms_targets): %_build: vagrant/%/Vagrantfile
@cd vagrant/$* && vagrant destroy -f
cd vagrant/$* && vagrant up
clean_vms_targets = $(addprefix clean_, $(VMS))
.PHONY: clean_vms $(clean_vms_targets)
clean_vms: $(clean_vms_targets)
$(clean_vms_targets): clean_%:
cd vagrant/$* && vagrant destroy -f
-rm -rf vagrant/$*/.vagrant
###########
## CLEAN ##
###########
.PHONY: clean
clean: clean_amd64 clean_toolchain clean_vms
docker container prune -f
docker image prune -f
-rmdir cache
.PHONY: distclean
distclean: clean
-docker container rm -f $$(docker container ls -aq)
-docker image rm -f $$(docker image ls -aq)
-vagrant box remove -f generic/freebsd12
+40
View File
@@ -0,0 +1,40 @@
## Makefile/Docker testing
To test the build on various distro, we are using docker containers and a Makefile for orchestration.
pros:
* You are independent of third party CI runner config
(e.g. [github action virtual-environnments](https://github.com/actions/virtual-environments)).
* You can run it locally on your linux system.
* Most CI provide runners with docker and Makefile installed.
cons:
* Only GNU/Linux distro supported.
### Usage
To get the help simply type:
```sh
make
```
note: you can also use from top directory
```sh
make --directory=cmake/ci
```
### Example
For example to test mips32 inside an container:
```sh
make mips32_test
```
### Docker layers
Dockerfile is splitted in several stages.
![docker](doc/docker.svg)
## Makefile/Vagrant testing
To test build for FreeBSD we are using Vagrant and VirtualBox box.
This is similar to the docker stuff but use `vagrant` as `docker` cli and
VirtuaBox to replace the docker engine daemon.
+64
View File
@@ -0,0 +1,64 @@
@startdot
digraph DockerDeps {
//rankdir=BT;
rankdir=TD;
node [shape=cylinder, style="rounded,filled", color=black, fillcolor=royalblue];
DISTRO_IMG [label="ubuntu:latest"];
PKG [label="packages\ne.g. cmake, g++", shape=box3d];
SRC [label="git repo", shape=folder];
SPL [label="sample", shape=folder];
subgraph clusterDockerfile {
ENV_IMG [label="cpu_features:amd64_env\nenv"];
DEVEL_IMG [label="cpu_features:amd64_devel\ndevel"];
BUILD_IMG [label="cpu_features:amd64_build\nbuild"];
TEST_IMG [label="cpu_features:amd64_test\ntest"];
INSTALL_ENV_IMG [label="cpu_features:amd64_install_env\ninstall_env"];
INSTALL_DEVEL_IMG [label="cpu_features:amd64_install_devel\ninstall_devel"];
INSTALL_BUILD_IMG [label="cpu_features:amd64_install_build\ninstall_build"];
INSTALL_TEST_IMG [label="cpu_features:amd64_install_test\ninstall_test"];
ENV_IMG -> DEVEL_IMG;
DEVEL_IMG -> BUILD_IMG;
BUILD_IMG -> TEST_IMG;
ENV_IMG -> INSTALL_ENV_IMG;
BUILD_IMG -> INSTALL_ENV_IMG [label="copy install", style="dashed"];
INSTALL_ENV_IMG -> INSTALL_DEVEL_IMG;
SPL -> INSTALL_DEVEL_IMG [label="copy", style="dashed"];
INSTALL_DEVEL_IMG -> INSTALL_BUILD_IMG;
INSTALL_BUILD_IMG -> INSTALL_TEST_IMG;
color=royalblue;
label = "docker/amd64/Dockerfile";
}
DISTRO_IMG -> ENV_IMG;
PKG -> ENV_IMG [label="install", style="dashed"];
SRC -> DEVEL_IMG [label="copy", style="dashed"];
subgraph clusterCache {
node [shape=note, style="rounded,filled", color=black, fillcolor=royalblue];
ENV_TAR [label="docker_amd64_env.tar"];
DEVEL_TAR [label="docker_amd64_devel.tar"];
BUILD_TAR [label="docker_amd64_build.tar"];
TEST_TAR [label="docker_amd64_test.tar"];
INSTALL_ENV_TAR [label="docker_amd64_install_env.tar"];
INSTALL_DEVEL_TAR [label="docker_amd64_install_devel.tar"];
INSTALL_BUILD_TAR [label="docker_amd64_install_build.tar"];
INSTALL_TEST_TAR [label="docker_amd64_install_test.tar"];
edge [color=red];
ENV_IMG -> ENV_TAR [label="make save_amd64_env"];
DEVEL_IMG -> DEVEL_TAR [label="make save_amd64_devel"];
BUILD_IMG -> BUILD_TAR [label="make save_amd64_build"];
TEST_IMG -> TEST_TAR [label="make save_amd64_test"];
INSTALL_ENV_IMG -> INSTALL_ENV_TAR [label="make save_amd64_install_env"];
INSTALL_DEVEL_IMG -> INSTALL_DEVEL_TAR [label="make save_amd64_install_devel"];
INSTALL_BUILD_IMG -> INSTALL_BUILD_TAR [label="make save_amd64_install_build"];
INSTALL_TEST_IMG -> INSTALL_TEST_TAR [label="make save_amd64_install_test"];
color=royalblue;
label = "cache/amd64/";
}
}
@enddot
+312
View File
@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.49.2 (0)
-->
<!-- Title: DockerDeps Pages: 1 -->
<svg width="1904pt" height="900pt"
viewBox="0.00 0.00 1904.00 899.75" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 895.75)">
<title>DockerDeps</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-895.75 1900,-895.75 1900,4 -4,4"/>
<g id="clust1" class="cluster">
<title>clusterDockerfile</title>
<polygon fill="none" stroke="royalblue" points="691,-116 691,-812.75 1253,-812.75 1253,-116 691,-116"/>
<text text-anchor="middle" x="972" y="-797.55" font-family="Times,serif" font-size="14.00">docker/amd64/Dockerfile</text>
</g>
<g id="clust2" class="cluster">
<title>clusterCache</title>
<polygon fill="none" stroke="royalblue" points="8,-8 8,-83 1826,-83 1826,-8 8,-8"/>
<text text-anchor="middle" x="917" y="-67.8" font-family="Times,serif" font-size="14.00">cache/amd64/</text>
</g>
<!-- DISTRO_IMG -->
<g id="node1" class="node">
<title>DISTRO_IMG</title>
<path fill="royalblue" stroke="black" d="M893.5,-887.48C893.5,-889.28 868.18,-890.75 837,-890.75 805.82,-890.75 780.5,-889.28 780.5,-887.48 780.5,-887.48 780.5,-858.02 780.5,-858.02 780.5,-856.22 805.82,-854.75 837,-854.75 868.18,-854.75 893.5,-856.22 893.5,-858.02 893.5,-858.02 893.5,-887.48 893.5,-887.48"/>
<path fill="none" stroke="black" d="M893.5,-887.48C893.5,-885.67 868.18,-884.2 837,-884.2 805.82,-884.2 780.5,-885.67 780.5,-887.48"/>
<text text-anchor="middle" x="837" y="-869.05" font-family="Times,serif" font-size="14.00">ubuntu:latest</text>
</g>
<!-- ENV_IMG -->
<g id="node5" class="node">
<title>ENV_IMG</title>
<path fill="royalblue" stroke="black" d="M1005,-777.1C1005,-779.74 961.52,-781.88 908,-781.88 854.48,-781.88 811,-779.74 811,-777.1 811,-777.1 811,-734.15 811,-734.15 811,-731.51 854.48,-729.37 908,-729.37 961.52,-729.37 1005,-731.51 1005,-734.15 1005,-734.15 1005,-777.1 1005,-777.1"/>
<path fill="none" stroke="black" d="M1005,-777.1C1005,-774.47 961.52,-772.33 908,-772.33 854.48,-772.33 811,-774.47 811,-777.1"/>
<text text-anchor="middle" x="908" y="-759.42" font-family="Times,serif" font-size="14.00">cpu_features:amd64_env</text>
<text text-anchor="middle" x="908" y="-744.42" font-family="Times,serif" font-size="14.00">env</text>
</g>
<!-- DISTRO_IMG&#45;&gt;ENV_IMG -->
<g id="edge10" class="edge">
<title>DISTRO_IMG&#45;&gt;ENV_IMG</title>
<path fill="none" stroke="black" d="M847.78,-854.26C858.17,-837.42 874.16,-811.49 887.05,-790.59"/>
<polygon fill="black" stroke="black" points="890.09,-792.33 892.37,-781.98 884.14,-788.65 890.09,-792.33"/>
</g>
<!-- PKG -->
<g id="node2" class="node">
<title>PKG</title>
<polygon fill="royalblue" stroke="black" points="1046.5,-891.75 915.5,-891.75 911.5,-887.75 911.5,-853.75 1042.5,-853.75 1046.5,-857.75 1046.5,-891.75"/>
<polyline fill="none" stroke="black" points="1042.5,-887.75 911.5,-887.75 "/>
<polyline fill="none" stroke="black" points="1042.5,-887.75 1042.5,-853.75 "/>
<polyline fill="none" stroke="black" points="1042.5,-887.75 1046.5,-891.75 "/>
<text text-anchor="middle" x="979" y="-876.55" font-family="Times,serif" font-size="14.00">packages</text>
<text text-anchor="middle" x="979" y="-861.55" font-family="Times,serif" font-size="14.00">e.g. cmake, g++</text>
</g>
<!-- PKG&#45;&gt;ENV_IMG -->
<g id="edge11" class="edge">
<title>PKG&#45;&gt;ENV_IMG</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M967.75,-853.51C957.4,-836.72 941.77,-811.38 929.09,-790.83"/>
<polygon fill="black" stroke="black" points="931.91,-788.73 923.69,-782.06 925.96,-792.41 931.91,-788.73"/>
<text text-anchor="middle" x="978.5" y="-824.55" font-family="Times,serif" font-size="14.00">install</text>
</g>
<!-- SRC -->
<g id="node3" class="node">
<title>SRC</title>
<polygon fill="royalblue" stroke="black" points="1334.5,-773.62 1331.5,-777.62 1310.5,-777.62 1307.5,-773.62 1261.5,-773.62 1261.5,-737.62 1334.5,-737.62 1334.5,-773.62"/>
<text text-anchor="middle" x="1298" y="-751.92" font-family="Times,serif" font-size="14.00">git repo</text>
</g>
<!-- DEVEL_IMG -->
<g id="node6" class="node">
<title>DEVEL_IMG</title>
<path fill="royalblue" stroke="black" d="M1189,-673.85C1189,-676.49 1142.83,-678.63 1086,-678.63 1029.17,-678.63 983,-676.49 983,-673.85 983,-673.85 983,-630.9 983,-630.9 983,-628.26 1029.17,-626.12 1086,-626.12 1142.83,-626.12 1189,-628.26 1189,-630.9 1189,-630.9 1189,-673.85 1189,-673.85"/>
<path fill="none" stroke="black" d="M1189,-673.85C1189,-671.22 1142.83,-669.08 1086,-669.08 1029.17,-669.08 983,-671.22 983,-673.85"/>
<text text-anchor="middle" x="1086" y="-656.17" font-family="Times,serif" font-size="14.00">cpu_features:amd64_devel</text>
<text text-anchor="middle" x="1086" y="-641.17" font-family="Times,serif" font-size="14.00">devel</text>
</g>
<!-- SRC&#45;&gt;DEVEL_IMG -->
<g id="edge12" class="edge">
<title>SRC&#45;&gt;DEVEL_IMG</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1271.39,-737.62C1266.66,-734.8 1261.73,-731.99 1257,-729.5 1224.81,-712.57 1188.08,-695.89 1156.94,-682.48"/>
<polygon fill="black" stroke="black" points="1158.03,-679.14 1147.46,-678.43 1155.28,-685.58 1158.03,-679.14"/>
<text text-anchor="middle" x="1237" y="-700.3" font-family="Times,serif" font-size="14.00">copy</text>
</g>
<!-- SPL -->
<g id="node4" class="node">
<title>SPL</title>
<polygon fill="royalblue" stroke="black" points="767,-477.88 764,-481.88 743,-481.88 740,-477.88 699,-477.88 699,-441.88 767,-441.88 767,-477.88"/>
<text text-anchor="middle" x="733" y="-456.18" font-family="Times,serif" font-size="14.00">sample</text>
</g>
<!-- INSTALL_DEVEL_IMG -->
<g id="node10" class="node">
<title>INSTALL_DEVEL_IMG</title>
<path fill="royalblue" stroke="black" d="M956.5,-378.1C956.5,-380.74 898.9,-382.88 828,-382.88 757.1,-382.88 699.5,-380.74 699.5,-378.1 699.5,-378.1 699.5,-335.15 699.5,-335.15 699.5,-332.51 757.1,-330.37 828,-330.37 898.9,-330.37 956.5,-332.51 956.5,-335.15 956.5,-335.15 956.5,-378.1 956.5,-378.1"/>
<path fill="none" stroke="black" d="M956.5,-378.1C956.5,-375.47 898.9,-373.33 828,-373.33 757.1,-373.33 699.5,-375.47 699.5,-378.1"/>
<text text-anchor="middle" x="828" y="-360.43" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_devel</text>
<text text-anchor="middle" x="828" y="-345.43" font-family="Times,serif" font-size="14.00">install_devel</text>
</g>
<!-- SPL&#45;&gt;INSTALL_DEVEL_IMG -->
<g id="edge7" class="edge">
<title>SPL&#45;&gt;INSTALL_DEVEL_IMG</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M749.12,-441.7C762.24,-427.71 781.16,-407.54 797.18,-390.47"/>
<polygon fill="black" stroke="black" points="800.02,-392.56 804.31,-382.87 794.91,-387.77 800.02,-392.56"/>
<text text-anchor="middle" x="803" y="-404.55" font-family="Times,serif" font-size="14.00">copy</text>
</g>
<!-- ENV_IMG&#45;&gt;DEVEL_IMG -->
<g id="edge1" class="edge">
<title>ENV_IMG&#45;&gt;DEVEL_IMG</title>
<path fill="none" stroke="black" d="M952.46,-729.34C976.87,-715.45 1007.3,-698.14 1032.95,-683.55"/>
<polygon fill="black" stroke="black" points="1034.84,-686.5 1041.8,-678.52 1031.38,-680.42 1034.84,-686.5"/>
</g>
<!-- INSTALL_ENV_IMG -->
<g id="node9" class="node">
<title>INSTALL_ENV_IMG</title>
<path fill="royalblue" stroke="black" d="M1030.5,-481.35C1030.5,-483.99 975.59,-486.13 908,-486.13 840.41,-486.13 785.5,-483.99 785.5,-481.35 785.5,-481.35 785.5,-438.4 785.5,-438.4 785.5,-435.76 840.41,-433.62 908,-433.62 975.59,-433.62 1030.5,-435.76 1030.5,-438.4 1030.5,-438.4 1030.5,-481.35 1030.5,-481.35"/>
<path fill="none" stroke="black" d="M1030.5,-481.35C1030.5,-478.72 975.59,-476.58 908,-476.58 840.41,-476.58 785.5,-478.72 785.5,-481.35"/>
<text text-anchor="middle" x="908" y="-463.68" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_env</text>
<text text-anchor="middle" x="908" y="-448.68" font-family="Times,serif" font-size="14.00">install_env</text>
</g>
<!-- ENV_IMG&#45;&gt;INSTALL_ENV_IMG -->
<g id="edge4" class="edge">
<title>ENV_IMG&#45;&gt;INSTALL_ENV_IMG</title>
<path fill="none" stroke="black" d="M908,-729.33C908,-676.94 908,-556.49 908,-496.37"/>
<polygon fill="black" stroke="black" points="911.5,-496.22 908,-486.22 904.5,-496.22 911.5,-496.22"/>
</g>
<!-- ENV_TAR -->
<g id="node13" class="node">
<title>ENV_TAR</title>
<polygon fill="royalblue" stroke="black" points="186,-52 16,-52 16,-16 192,-16 192,-46 186,-52"/>
<polyline fill="none" stroke="black" points="186,-52 186,-46 "/>
<polyline fill="none" stroke="black" points="192,-46 186,-46 "/>
<text text-anchor="middle" x="104" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_env.tar</text>
</g>
<!-- ENV_IMG&#45;&gt;ENV_TAR -->
<g id="edge13" class="edge">
<title>ENV_IMG&#45;&gt;ENV_TAR</title>
<path fill="none" stroke="red" d="M810.87,-751.31C609.47,-743.14 165,-717.82 165,-653.38 165,-653.38 165,-653.38 165,-149.12 165,-115.26 143.85,-81.71 126.46,-59.84"/>
<polygon fill="red" stroke="red" points="129.09,-57.54 120.03,-52.05 123.7,-61.99 129.09,-57.54"/>
<text text-anchor="middle" x="246.5" y="-404.55" font-family="Times,serif" font-size="14.00">make save_amd64_env</text>
</g>
<!-- BUILD_IMG -->
<g id="node7" class="node">
<title>BUILD_IMG</title>
<path fill="royalblue" stroke="black" d="M1245,-584.6C1245,-587.24 1199.28,-589.38 1143,-589.38 1086.72,-589.38 1041,-587.24 1041,-584.6 1041,-584.6 1041,-541.65 1041,-541.65 1041,-539.01 1086.72,-536.87 1143,-536.87 1199.28,-536.87 1245,-539.01 1245,-541.65 1245,-541.65 1245,-584.6 1245,-584.6"/>
<path fill="none" stroke="black" d="M1245,-584.6C1245,-581.97 1199.28,-579.83 1143,-579.83 1086.72,-579.83 1041,-581.97 1041,-584.6"/>
<text text-anchor="middle" x="1143" y="-566.92" font-family="Times,serif" font-size="14.00">cpu_features:amd64_build</text>
<text text-anchor="middle" x="1143" y="-551.92" font-family="Times,serif" font-size="14.00">build</text>
</g>
<!-- DEVEL_IMG&#45;&gt;BUILD_IMG -->
<g id="edge2" class="edge">
<title>DEVEL_IMG&#45;&gt;BUILD_IMG</title>
<path fill="none" stroke="black" d="M1102.49,-626.14C1108.28,-617.28 1114.88,-607.17 1121.04,-597.73"/>
<polygon fill="black" stroke="black" points="1124.01,-599.59 1126.55,-589.3 1118.15,-595.76 1124.01,-599.59"/>
</g>
<!-- DEVEL_TAR -->
<g id="node14" class="node">
<title>DEVEL_TAR</title>
<polygon fill="royalblue" stroke="black" points="395.5,-52 210.5,-52 210.5,-16 401.5,-16 401.5,-46 395.5,-52"/>
<polyline fill="none" stroke="black" points="395.5,-52 395.5,-46 "/>
<polyline fill="none" stroke="black" points="401.5,-46 395.5,-46 "/>
<text text-anchor="middle" x="306" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_devel.tar</text>
</g>
<!-- DEVEL_IMG&#45;&gt;DEVEL_TAR -->
<g id="edge14" class="edge">
<title>DEVEL_IMG&#45;&gt;DEVEL_TAR</title>
<path fill="none" stroke="red" d="M982.94,-648.59C792.56,-642.05 405,-621.67 405,-564.12 405,-564.12 405,-564.12 405,-149.12 405,-110.26 371.83,-78.15 343.9,-58"/>
<polygon fill="red" stroke="red" points="345.65,-54.96 335.44,-52.14 341.66,-60.71 345.65,-54.96"/>
<text text-anchor="middle" x="493" y="-352.93" font-family="Times,serif" font-size="14.00">make save_amd64_devel</text>
</g>
<!-- TEST_IMG -->
<g id="node8" class="node">
<title>TEST_IMG</title>
<path fill="royalblue" stroke="black" d="M1245,-481.35C1245,-483.99 1201.07,-486.13 1147,-486.13 1092.93,-486.13 1049,-483.99 1049,-481.35 1049,-481.35 1049,-438.4 1049,-438.4 1049,-435.76 1092.93,-433.62 1147,-433.62 1201.07,-433.62 1245,-435.76 1245,-438.4 1245,-438.4 1245,-481.35 1245,-481.35"/>
<path fill="none" stroke="black" d="M1245,-481.35C1245,-478.72 1201.07,-476.58 1147,-476.58 1092.93,-476.58 1049,-478.72 1049,-481.35"/>
<text text-anchor="middle" x="1147" y="-463.68" font-family="Times,serif" font-size="14.00">cpu_features:amd64_test</text>
<text text-anchor="middle" x="1147" y="-448.68" font-family="Times,serif" font-size="14.00">test</text>
</g>
<!-- BUILD_IMG&#45;&gt;TEST_IMG -->
<g id="edge3" class="edge">
<title>BUILD_IMG&#45;&gt;TEST_IMG</title>
<path fill="none" stroke="black" d="M1144,-536.84C1144.48,-524.63 1145.07,-509.79 1145.59,-496.47"/>
<polygon fill="black" stroke="black" points="1149.1,-496.32 1146,-486.19 1142.11,-496.05 1149.1,-496.32"/>
</g>
<!-- BUILD_IMG&#45;&gt;INSTALL_ENV_IMG -->
<g id="edge5" class="edge">
<title>BUILD_IMG&#45;&gt;INSTALL_ENV_IMG</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M1084.61,-536.97C1051.68,-522.78 1010.38,-504.99 976,-490.17"/>
<polygon fill="black" stroke="black" points="977.07,-486.82 966.5,-486.08 974.3,-493.25 977.07,-486.82"/>
<text text-anchor="middle" x="1080" y="-507.8" font-family="Times,serif" font-size="14.00">copy install</text>
</g>
<!-- BUILD_TAR -->
<g id="node15" class="node">
<title>BUILD_TAR</title>
<polygon fill="royalblue" stroke="black" points="1812,-52 1630,-52 1630,-16 1818,-16 1818,-46 1812,-52"/>
<polyline fill="none" stroke="black" points="1812,-52 1812,-46 "/>
<polyline fill="none" stroke="black" points="1818,-46 1812,-46 "/>
<text text-anchor="middle" x="1724" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_build.tar</text>
</g>
<!-- BUILD_IMG&#45;&gt;BUILD_TAR -->
<g id="edge15" class="edge">
<title>BUILD_IMG&#45;&gt;BUILD_TAR</title>
<path fill="none" stroke="red" d="M1245.18,-554.53C1411.4,-540.79 1722,-508.71 1722,-460.88 1722,-460.88 1722,-460.88 1722,-149.12 1722,-119.36 1722.69,-85.23 1723.26,-62.11"/>
<polygon fill="red" stroke="red" points="1726.76,-62.1 1723.52,-52.01 1719.76,-61.92 1726.76,-62.1"/>
<text text-anchor="middle" x="1809" y="-301.3" font-family="Times,serif" font-size="14.00">make save_amd64_build</text>
</g>
<!-- TEST_TAR -->
<g id="node16" class="node">
<title>TEST_TAR</title>
<polygon fill="royalblue" stroke="black" points="1606,-52 1432,-52 1432,-16 1612,-16 1612,-46 1606,-52"/>
<polyline fill="none" stroke="black" points="1606,-52 1606,-46 "/>
<polyline fill="none" stroke="black" points="1612,-46 1606,-46 "/>
<text text-anchor="middle" x="1522" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_test.tar</text>
</g>
<!-- TEST_IMG&#45;&gt;TEST_TAR -->
<g id="edge16" class="edge">
<title>TEST_IMG&#45;&gt;TEST_TAR</title>
<path fill="none" stroke="red" d="M1245.07,-451.87C1356.77,-441.13 1524,-415.4 1524,-357.62 1524,-357.62 1524,-357.62 1524,-149.12 1524,-119.36 1523.31,-85.23 1522.74,-62.11"/>
<polygon fill="red" stroke="red" points="1526.24,-61.92 1522.48,-52.01 1519.24,-62.1 1526.24,-61.92"/>
<text text-anchor="middle" x="1607" y="-249.68" font-family="Times,serif" font-size="14.00">make save_amd64_test</text>
</g>
<!-- INSTALL_ENV_IMG&#45;&gt;INSTALL_DEVEL_IMG -->
<g id="edge6" class="edge">
<title>INSTALL_ENV_IMG&#45;&gt;INSTALL_DEVEL_IMG</title>
<path fill="none" stroke="black" d="M888.02,-433.59C877.81,-420.66 865.25,-404.76 854.26,-390.86"/>
<polygon fill="black" stroke="black" points="856.95,-388.62 848,-382.94 851.46,-392.96 856.95,-388.62"/>
</g>
<!-- INSTALL_ENV_TAR -->
<g id="node17" class="node">
<title>INSTALL_ENV_TAR</title>
<polygon fill="royalblue" stroke="black" points="1407.5,-52 1186.5,-52 1186.5,-16 1413.5,-16 1413.5,-46 1407.5,-52"/>
<polyline fill="none" stroke="black" points="1407.5,-52 1407.5,-46 "/>
<polyline fill="none" stroke="black" points="1413.5,-46 1407.5,-46 "/>
<text text-anchor="middle" x="1300" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_env.tar</text>
</g>
<!-- INSTALL_ENV_IMG&#45;&gt;INSTALL_ENV_TAR -->
<g id="edge17" class="edge">
<title>INSTALL_ENV_IMG&#45;&gt;INSTALL_ENV_TAR</title>
<path fill="none" stroke="red" d="M1012.88,-434.97C1121.03,-409.58 1274,-371.26 1274,-357.62 1274,-357.62 1274,-357.62 1274,-149.12 1274,-118.64 1282.93,-84.69 1290.32,-61.81"/>
<polygon fill="red" stroke="red" points="1293.71,-62.72 1293.57,-52.12 1287.07,-60.49 1293.71,-62.72"/>
<text text-anchor="middle" x="1381" y="-249.68" font-family="Times,serif" font-size="14.00">make save_amd64_install_env</text>
</g>
<!-- INSTALL_BUILD_IMG -->
<g id="node11" class="node">
<title>INSTALL_BUILD_IMG</title>
<path fill="royalblue" stroke="black" d="M955.5,-274.85C955.5,-277.49 898.35,-279.63 828,-279.63 757.65,-279.63 700.5,-277.49 700.5,-274.85 700.5,-274.85 700.5,-231.9 700.5,-231.9 700.5,-229.26 757.65,-227.12 828,-227.12 898.35,-227.12 955.5,-229.26 955.5,-231.9 955.5,-231.9 955.5,-274.85 955.5,-274.85"/>
<path fill="none" stroke="black" d="M955.5,-274.85C955.5,-272.22 898.35,-270.08 828,-270.08 757.65,-270.08 700.5,-272.22 700.5,-274.85"/>
<text text-anchor="middle" x="828" y="-257.18" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_build</text>
<text text-anchor="middle" x="828" y="-242.18" font-family="Times,serif" font-size="14.00">install_build</text>
</g>
<!-- INSTALL_DEVEL_IMG&#45;&gt;INSTALL_BUILD_IMG -->
<g id="edge8" class="edge">
<title>INSTALL_DEVEL_IMG&#45;&gt;INSTALL_BUILD_IMG</title>
<path fill="none" stroke="black" d="M828,-330.34C828,-318.13 828,-303.29 828,-289.97"/>
<polygon fill="black" stroke="black" points="831.5,-289.69 828,-279.69 824.5,-289.69 831.5,-289.69"/>
</g>
<!-- INSTALL_DEVEL_TAR -->
<g id="node18" class="node">
<title>INSTALL_DEVEL_TAR</title>
<polygon fill="royalblue" stroke="black" points="656,-52 420,-52 420,-16 662,-16 662,-46 656,-52"/>
<polyline fill="none" stroke="black" points="656,-52 656,-46 "/>
<polyline fill="none" stroke="black" points="662,-46 656,-46 "/>
<text text-anchor="middle" x="541" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_devel.tar</text>
</g>
<!-- INSTALL_DEVEL_IMG&#45;&gt;INSTALL_DEVEL_TAR -->
<g id="edge18" class="edge">
<title>INSTALL_DEVEL_IMG&#45;&gt;INSTALL_DEVEL_TAR</title>
<path fill="none" stroke="red" d="M761.39,-330.48C708.37,-307.05 636.42,-266.99 595,-209.25 562.63,-164.12 549.27,-99.06 544.06,-62.54"/>
<polygon fill="red" stroke="red" points="547.47,-61.64 542.7,-52.18 540.53,-62.55 547.47,-61.64"/>
<text text-anchor="middle" x="708.5" y="-198.05" font-family="Times,serif" font-size="14.00">make save_amd64_install_devel</text>
</g>
<!-- INSTALL_TEST_IMG -->
<g id="node12" class="node">
<title>INSTALL_TEST_IMG</title>
<path fill="royalblue" stroke="black" d="M948.5,-171.6C948.5,-174.24 893.15,-176.38 825,-176.38 756.85,-176.38 701.5,-174.24 701.5,-171.6 701.5,-171.6 701.5,-128.65 701.5,-128.65 701.5,-126.01 756.85,-123.87 825,-123.87 893.15,-123.87 948.5,-126.01 948.5,-128.65 948.5,-128.65 948.5,-171.6 948.5,-171.6"/>
<path fill="none" stroke="black" d="M948.5,-171.6C948.5,-168.97 893.15,-166.83 825,-166.83 756.85,-166.83 701.5,-168.97 701.5,-171.6"/>
<text text-anchor="middle" x="825" y="-153.93" font-family="Times,serif" font-size="14.00">cpu_features:amd64_install_test</text>
<text text-anchor="middle" x="825" y="-138.93" font-family="Times,serif" font-size="14.00">install_test</text>
</g>
<!-- INSTALL_BUILD_IMG&#45;&gt;INSTALL_TEST_IMG -->
<g id="edge9" class="edge">
<title>INSTALL_BUILD_IMG&#45;&gt;INSTALL_TEST_IMG</title>
<path fill="none" stroke="black" d="M827.25,-227.09C826.89,-214.88 826.45,-200.04 826.05,-186.72"/>
<polygon fill="black" stroke="black" points="829.54,-186.33 825.75,-176.44 822.55,-186.54 829.54,-186.33"/>
</g>
<!-- INSTALL_BUILD_TAR -->
<g id="node19" class="node">
<title>INSTALL_BUILD_TAR</title>
<polygon fill="royalblue" stroke="black" points="1162.5,-52 929.5,-52 929.5,-16 1168.5,-16 1168.5,-46 1162.5,-52"/>
<polyline fill="none" stroke="black" points="1162.5,-52 1162.5,-46 "/>
<polyline fill="none" stroke="black" points="1168.5,-46 1162.5,-46 "/>
<text text-anchor="middle" x="1049" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_build.tar</text>
</g>
<!-- INSTALL_BUILD_IMG&#45;&gt;INSTALL_BUILD_TAR -->
<g id="edge19" class="edge">
<title>INSTALL_BUILD_IMG&#45;&gt;INSTALL_BUILD_TAR</title>
<path fill="none" stroke="red" d="M882.76,-227.11C907.43,-214.09 935.91,-196.66 958,-176.25 994.16,-142.85 1022.19,-92.11 1037.09,-61.41"/>
<polygon fill="red" stroke="red" points="1040.34,-62.72 1041.46,-52.19 1034.02,-59.72 1040.34,-62.72"/>
<text text-anchor="middle" x="1117.5" y="-146.43" font-family="Times,serif" font-size="14.00">make save_amd64_install_build</text>
</g>
<!-- INSTALL_TEST_TAR -->
<g id="node20" class="node">
<title>INSTALL_TEST_TAR</title>
<polygon fill="royalblue" stroke="black" points="905.5,-52 680.5,-52 680.5,-16 911.5,-16 911.5,-46 905.5,-52"/>
<polyline fill="none" stroke="black" points="905.5,-52 905.5,-46 "/>
<polyline fill="none" stroke="black" points="911.5,-46 905.5,-46 "/>
<text text-anchor="middle" x="796" y="-30.3" font-family="Times,serif" font-size="14.00">docker_amd64_install_test.tar</text>
</g>
<!-- INSTALL_TEST_IMG&#45;&gt;INSTALL_TEST_TAR -->
<g id="edge20" class="edge">
<title>INSTALL_TEST_IMG&#45;&gt;INSTALL_TEST_TAR</title>
<path fill="none" stroke="red" d="M799.99,-123.98C795.9,-118.47 792.26,-112.36 790,-106 785.07,-92.11 786.03,-75.77 788.46,-62.27"/>
<polygon fill="red" stroke="red" points="791.96,-62.66 790.65,-52.15 785.12,-61.19 791.96,-62.66"/>
<text text-anchor="middle" x="898.5" y="-94.8" font-family="Times,serif" font-size="14.00">make save_amd64_install_test</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 21 KiB

+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -ex
rm -f ./*.svg ./*.png
for i in *.dot; do
plantuml -Tsvg "$i";
done
+48
View File
@@ -0,0 +1,48 @@
# Create a virtual environment with all tools installed
# ref: https://hub.docker.com/_/ubuntu
FROM ubuntu:latest AS env
LABEL maintainer="corentinl@google.com"
# Install system build dependencies
ENV PATH=/usr/local/bin:$PATH
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq git wget libssl-dev build-essential \
ninja-build python3 pkgconf libglib2.0-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ENTRYPOINT ["/usr/bin/bash", "-c"]
CMD ["/usr/bin/bash"]
# Install CMake 3.21.3
RUN wget "https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.sh" \
&& chmod a+x cmake-3.21.3-linux-x86_64.sh \
&& ./cmake-3.21.3-linux-x86_64.sh --prefix=/usr/local/ --skip-license \
&& rm cmake-3.21.3-linux-x86_64.sh
FROM env AS devel
WORKDIR /home/project
COPY . .
FROM devel AS build
RUN cmake -version
RUN cmake -S. -Bbuild
RUN cmake --build build --target all -v
RUN cmake --build build --target install -v
FROM build AS test
ENV CTEST_OUTPUT_ON_FAILURE=1
RUN cmake --build build --target test -v
# Test install rules
FROM env AS install_env
COPY --from=build /usr/local /usr/local/
FROM install_env AS install_devel
WORKDIR /home/sample
COPY cmake/ci/sample .
FROM install_devel AS install_build
RUN cmake -S. -Bbuild
RUN cmake --build build --target all -v
FROM install_build AS install_test
RUN cmake --build build --target test
@@ -0,0 +1,34 @@
# Create a virtual environment with all tools installed
# ref: https://hub.docker.com/_/ubuntu
FROM ubuntu:latest AS env
LABEL maintainer="corentinl@google.com"
# Install system build dependencies
ENV PATH=/usr/local/bin:$PATH
RUN apt-get update -qq \
&& DEBIAN_FRONTEND=noninteractive apt-get install -yq git wget libssl-dev build-essential \
ninja-build python3 pkgconf libglib2.0-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ENTRYPOINT ["/usr/bin/bash", "-c"]
CMD ["/usr/bin/bash"]
# Install CMake 3.21.3
RUN wget "https://cmake.org/files/v3.21/cmake-3.21.3-linux-x86_64.sh" \
&& chmod a+x cmake-3.21.3-linux-x86_64.sh \
&& ./cmake-3.21.3-linux-x86_64.sh --prefix=/usr/local/ --skip-license \
&& rm cmake-3.21.3-linux-x86_64.sh
FROM env AS devel
WORKDIR /home/project
COPY . .
ARG TARGET
ENV TARGET ${TARGET:-unknown}
FROM devel AS build
RUN cmake -version
RUN ./scripts/run_integration.sh build
FROM build AS test
RUN ./scripts/run_integration.sh qemu
RUN ./scripts/run_integration.sh test
+22
View File
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.15)
project(Sample VERSION 1.0.0 LANGUAGES CXX)
include(CTest)
find_package(CpuFeatures REQUIRED)
add_executable(sample main.cpp)
target_compile_features(sample PUBLIC cxx_std_11)
set_target_properties(sample PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
VERSION ${PROJECT_VERSION})
target_link_libraries(sample PRIVATE CpuFeatures::cpu_features)
if(BUILD_TESTING)
add_test(NAME sample_test COMMAND sample)
endif()
include(GNUInstallDirs)
install(TARGETS sample
EXPORT SampleTargets
DESTINATION ${CMAKE_INSTALL_BIN_DIR})
+11
View File
@@ -0,0 +1,11 @@
#include <iostream>
#include "cpuinfo_x86.h"
using namespace cpu_features;
int main(int /*argc*/, char** /*argv*/) {
static const X86Features features = GetX86Info().features;
std::cout << std::endl;
return 0;
}
@@ -0,0 +1,107 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.guest = :freebsd
config.vm.box = "generic/freebsd12"
config.ssh.shell = "sh"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
#config.vm.synced_folder "../../..", "/home/vagrant/project"
config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true
config.vm.provision "file", source: "../../../../CMakeLists.txt", destination: "$HOME/project/"
config.vm.provision "file", source: "../../../../cmake", destination: "$HOME/project/"
config.vm.provision "file", source: "../../../../include", destination: "$HOME/project/"
config.vm.provision "file", source: "../../../../src", destination: "$HOME/project/"
config.vm.provision "file", source: "../../../../test", destination: "$HOME/project/"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
# note: clang installed by default
config.vm.provision "env", type: "shell", inline:<<-SHELL
set -x
pkg update -f
pkg install -y git cmake
SHELL
config.vm.provision "devel", type: "shell", inline:<<-SHELL
set -x
cd project
ls
SHELL
config.vm.provision "configure", type: "shell", inline:<<-SHELL
set -x
cd project
cmake -S. -Bbuild -DBUILD_TESTING=ON
SHELL
config.vm.provision "build", type: "shell", inline:<<-SHELL
set -x
cd project
cmake --build build -v
SHELL
config.vm.provision "test", type: "shell", inline:<<-SHELL
set -x
cd project
cmake --build build --target test -v
SHELL
config.vm.provision "test", type: "shell", inline:<<-SHELL
set -x
cd project
cmake --build build --target install -v
SHELL
end
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG main
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef enum {
CPU_FEATURE_CACHE_NULL = 0,
CPU_FEATURE_CACHE_DATA = 1,
CPU_FEATURE_CACHE_INSTRUCTION = 2,
CPU_FEATURE_CACHE_UNIFIED = 3,
CPU_FEATURE_CACHE_TLB = 4,
CPU_FEATURE_CACHE_DTLB = 5,
CPU_FEATURE_CACHE_STLB = 6,
CPU_FEATURE_CACHE_PREFETCH = 7
} CacheType;
typedef struct {
int level;
CacheType cache_type;
int cache_size; // Cache size in bytes
int ways; // Associativity, 0 undefined, 0xFF fully associative
int line_size; // Cache line size in bytes
int tlb_entries; // number of entries for TLB
int partitioning; // number of lines per sector
} CacheLevelInfo;
// Increase this value if more cache levels are needed.
#ifndef CPU_FEATURES_MAX_CACHE_LEVEL
#define CPU_FEATURES_MAX_CACHE_LEVEL 10
#endif
typedef struct {
int size;
CacheLevelInfo levels[CPU_FEATURES_MAX_CACHE_LEVEL];
} CacheInfo;
CPU_FEATURES_END_CPP_NAMESPACE
#endif // CPU_FEATURES_INCLUDE_CPUINFO_COMMON_H_
+388
View File
@@ -0,0 +1,388 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
#define CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
////////////////////////////////////////////////////////////////////////////////
// Architectures
////////////////////////////////////////////////////////////////////////////////
#if defined(__pnacl__) || defined(__CLR_VER)
#define CPU_FEATURES_ARCH_VM
#endif
#if (defined(_M_IX86) || defined(__i386__)) && !defined(CPU_FEATURES_ARCH_VM)
#define CPU_FEATURES_ARCH_X86_32
#endif
#if (defined(_M_X64) || defined(__x86_64__)) && !defined(CPU_FEATURES_ARCH_VM)
#define CPU_FEATURES_ARCH_X86_64
#endif
#if defined(CPU_FEATURES_ARCH_X86_32) || defined(CPU_FEATURES_ARCH_X86_64)
#define CPU_FEATURES_ARCH_X86
#endif
#if (defined(__arm__) || defined(_M_ARM))
#define CPU_FEATURES_ARCH_ARM
#endif
#if (defined(__aarch64__) || defined(_M_ARM64))
#define CPU_FEATURES_ARCH_AARCH64
#endif
#if (defined(CPU_FEATURES_ARCH_AARCH64) || defined(CPU_FEATURES_ARCH_ARM))
#define CPU_FEATURES_ARCH_ANY_ARM
#endif
#if defined(__mips64)
#define CPU_FEATURES_ARCH_MIPS64
#endif
#if defined(__mips__) && !defined(__mips64) // mips64 also declares __mips__
#define CPU_FEATURES_ARCH_MIPS32
#endif
#if defined(CPU_FEATURES_ARCH_MIPS32) || defined(CPU_FEATURES_ARCH_MIPS64)
#define CPU_FEATURES_ARCH_MIPS
#endif
#if defined(__powerpc__)
#define CPU_FEATURES_ARCH_PPC
#endif
#if defined(__s390x__)
#define CPU_FEATURES_ARCH_S390X
#endif
#if defined(__riscv)
#define CPU_FEATURES_ARCH_RISCV
#endif
#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 32
#define CPU_FEATURES_ARCH_RISCV32
#endif
#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
#define CPU_FEATURES_ARCH_RISCV64
#endif
#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 128
#define CPU_FEATURES_ARCH_RISCV128
#endif
#if defined(__loongarch64)
#define CPU_FEATURES_ARCH_LOONGARCH
#endif
////////////////////////////////////////////////////////////////////////////////
// Os
////////////////////////////////////////////////////////////////////////////////
#if (defined(__freebsd__) || defined(__FreeBSD__))
#define CPU_FEATURES_OS_FREEBSD
#endif
#if defined(__ANDROID__)
#define CPU_FEATURES_OS_ANDROID
#endif
#if defined(__linux__) && !defined(CPU_FEATURES_OS_FREEBSD) && \
!defined(CPU_FEATURES_OS_ANDROID)
#define CPU_FEATURES_OS_LINUX
#endif
#if (defined(_WIN64) || defined(_WIN32))
#define CPU_FEATURES_OS_WINDOWS
#endif
#if (defined(__apple__) || defined(__APPLE__) || defined(__MACH__))
// From https://stackoverflow.com/a/49560690
#include "TargetConditionals.h"
#if defined(TARGET_OS_OSX)
#define CPU_FEATURES_OS_MACOS
#endif
#if defined(TARGET_OS_IPHONE)
// This is set for any non-Mac Apple products (IOS, TV, WATCH)
#define CPU_FEATURES_OS_IPHONE
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
// Compilers
////////////////////////////////////////////////////////////////////////////////
#if defined(__clang__)
#define CPU_FEATURES_COMPILER_CLANG
#endif
#if defined(__GNUC__) && !defined(__clang__)
#define CPU_FEATURES_COMPILER_GCC
#endif
#if defined(_MSC_VER)
#define CPU_FEATURES_COMPILER_MSC
#endif
////////////////////////////////////////////////////////////////////////////////
// Cpp
////////////////////////////////////////////////////////////////////////////////
#if defined(__cplusplus)
#define CPU_FEATURES_START_CPP_NAMESPACE \
namespace cpu_features { \
extern "C" {
#define CPU_FEATURES_END_CPP_NAMESPACE \
} \
}
#else
#define CPU_FEATURES_START_CPP_NAMESPACE
#define CPU_FEATURES_END_CPP_NAMESPACE
#endif
////////////////////////////////////////////////////////////////////////////////
// Compiler flags
////////////////////////////////////////////////////////////////////////////////
// Use the following to check if a feature is known to be available at
// compile time. See README.md for an example.
#if defined(CPU_FEATURES_ARCH_X86)
#if defined(__AES__)
#define CPU_FEATURES_COMPILED_X86_AES 1
#else
#define CPU_FEATURES_COMPILED_X86_AES 0
#endif // defined(__AES__)
#if defined(__F16C__)
#define CPU_FEATURES_COMPILED_X86_F16C 1
#else
#define CPU_FEATURES_COMPILED_X86_F16C 0
#endif // defined(__F16C__)
#if defined(__BMI__)
#define CPU_FEATURES_COMPILED_X86_BMI 1
#else
#define CPU_FEATURES_COMPILED_X86_BMI 0
#endif // defined(__BMI__)
#if defined(__BMI2__)
#define CPU_FEATURES_COMPILED_X86_BMI2 1
#else
#define CPU_FEATURES_COMPILED_X86_BMI2 0
#endif // defined(__BMI2__)
#if (defined(__SSE__) || (_M_IX86_FP >= 1))
#define CPU_FEATURES_COMPILED_X86_SSE 1
#else
#define CPU_FEATURES_COMPILED_X86_SSE 0
#endif
#if (defined(__SSE2__) || (_M_IX86_FP >= 2))
#define CPU_FEATURES_COMPILED_X86_SSE2 1
#else
#define CPU_FEATURES_COMPILED_X86_SSE2 0
#endif
#if defined(__SSE3__)
#define CPU_FEATURES_COMPILED_X86_SSE3 1
#else
#define CPU_FEATURES_COMPILED_X86_SSE3 0
#endif // defined(__SSE3__)
#if defined(__SSSE3__)
#define CPU_FEATURES_COMPILED_X86_SSSE3 1
#else
#define CPU_FEATURES_COMPILED_X86_SSSE3 0
#endif // defined(__SSSE3__)
#if defined(__SSE4_1__)
#define CPU_FEATURES_COMPILED_X86_SSE4_1 1
#else
#define CPU_FEATURES_COMPILED_X86_SSE4_1 0
#endif // defined(__SSE4_1__)
#if defined(__SSE4_2__)
#define CPU_FEATURES_COMPILED_X86_SSE4_2 1
#else
#define CPU_FEATURES_COMPILED_X86_SSE4_2 0
#endif // defined(__SSE4_2__)
#if defined(__AVX__)
#define CPU_FEATURES_COMPILED_X86_AVX 1
#else
#define CPU_FEATURES_COMPILED_X86_AVX 0
#endif // defined(__AVX__)
#if defined(__AVX2__)
#define CPU_FEATURES_COMPILED_X86_AVX2 1
#else
#define CPU_FEATURES_COMPILED_X86_AVX2 0
#endif // defined(__AVX2__)
#endif // defined(CPU_FEATURES_ARCH_X86)
#if defined(CPU_FEATURES_ARCH_ANY_ARM)
#if defined(__ARM_NEON__)
#define CPU_FEATURES_COMPILED_ANY_ARM_NEON 1
#else
#define CPU_FEATURES_COMPILED_ANY_ARM_NEON 0
#endif // defined(__ARM_NEON__)
#endif // defined(CPU_FEATURES_ARCH_ANY_ARM)
#if defined(CPU_FEATURES_ARCH_MIPS)
#if defined(__mips_msa)
#define CPU_FEATURES_COMPILED_MIPS_MSA 1
#else
#define CPU_FEATURES_COMPILED_MIPS_MSA 0
#endif // defined(__mips_msa)
#if defined(__mips3d)
#define CPU_FEATURES_COMPILED_MIPS_MIPS3D 1
#else
#define CPU_FEATURES_COMPILED_MIPS_MIPS3D 0
#endif
#endif // defined(CPU_FEATURES_ARCH_MIPS)
#if defined(CPU_FEATURES_ARCH_RISCV)
#if defined(__riscv_e)
#define CPU_FEATURES_COMPILED_RISCV_E 1
#else
#define CPU_FEATURES_COMPILED_RISCV_E 0
#endif
#if defined(__riscv_i)
#define CPU_FEATURES_COMPILED_RISCV_I 1
#else
#define CPU_FEATURES_COMPILED_RISCV_I 0
#endif
#if defined(__riscv_m)
#define CPU_FEATURES_COMPILED_RISCV_M 1
#else
#define CPU_FEATURES_COMPILED_RISCV_M 0
#endif
#if defined(__riscv_a)
#define CPU_FEATURES_COMPILED_RISCV_A 1
#else
#define CPU_FEATURES_COMPILED_RISCV_A 0
#endif
#if defined(__riscv_f)
#define CPU_FEATURES_COMPILED_RISCV_F 1
#else
#define CPU_FEATURES_COMPILED_RISCV_F 0
#endif
#if defined(__riscv_d)
#define CPU_FEATURES_COMPILED_RISCV_D 1
#else
#define CPU_FEATURES_COMPILED_RISCV_D 0
#endif
#if defined(__riscv_q)
#define CPU_FEATURES_COMPILED_RISCV_Q 1
#else
#define CPU_FEATURES_COMPILED_RISCV_Q 0
#endif
#if defined(__riscv_c)
#define CPU_FEATURES_COMPILED_RISCV_C 1
#else
#define CPU_FEATURES_COMPILED_RISCV_C 0
#endif
#if defined(__riscv_v)
#define CPU_FEATURES_COMPILED_RISCV_V 1
#else
#define CPU_FEATURES_COMPILED_RISCV_V 0
#endif
#if defined(__riscv_zba)
#define CPU_FEATURES_COMPILED_RISCV_ZBA 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZBA 0
#endif
#if defined(__riscv_zbb)
#define CPU_FEATURES_COMPILED_RISCV_ZBB 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZBB 0
#endif
#if defined(__riscv_zbc)
#define CPU_FEATURES_COMPILED_RISCV_ZBC 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZBC 0
#endif
#if defined(__riscv_zbs)
#define CPU_FEATURES_COMPILED_RISCV_ZBS 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZBS 0
#endif
#if defined(__riscv_zfh)
#define CPU_FEATURES_COMPILED_RISCV_ZFH 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZFH 0
#endif
#if defined(__riscv_zfhmin)
#define CPU_FEATURES_COMPILED_RISCV_ZFHMIN 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZFHMIN 0
#endif
#if defined(__riscv_zknd)
#define CPU_FEATURES_COMPILED_RISCV_ZKND 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKND 0
#endif
#if defined(__riscv_zkne)
#define CPU_FEATURES_COMPILED_RISCV_ZKNE 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKNE 0
#endif
#if defined(__riscv_zknh)
#define CPU_FEATURES_COMPILED_RISCV_ZKNH 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKNH 0
#endif
#if defined(__riscv_zksed)
#define CPU_FEATURES_COMPILED_RISCV_ZKSED 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKSED 0
#endif
#if defined(__riscv_zksh)
#define CPU_FEATURES_COMPILED_RISCV_ZKSH 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKSH 0
#endif
#if defined(__riscv_zkr)
#define CPU_FEATURES_COMPILED_RISCV_ZKR 1
#else
#define CPU_FEATURES_COMPILED_RISCV_ZKR 0
#endif
#endif // defined(CPU_FEATURES_ARCH_RISCV)
////////////////////////////////////////////////////////////////////////////////
// Utils
////////////////////////////////////////////////////////////////////////////////
// Communicates to the compiler that the block is unreachable
#if defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
#define CPU_FEATURES_UNREACHABLE() __builtin_unreachable()
#elif defined(CPU_FEATURES_COMPILER_MSC)
#define CPU_FEATURES_UNREACHABLE() __assume(0)
#else
#define CPU_FEATURES_UNREACHABLE()
#endif
// Communicates to the compiler that the function is now deprecated
#if defined(CPU_FEATURES_COMPILER_CLANG) || defined(CPU_FEATURES_COMPILER_GCC)
#define CPU_FEATURES_DEPRECATED(message) __attribute__((deprecated(message)))
#elif defined(CPU_FEATURES_COMPILER_MSC)
#define CPU_FEATURES_DEPRECATED(message) __declspec(deprecated(message))
#else
#define CPU_FEATURES_DEPRECATED(message)
#endif
#endif // CPU_FEATURES_INCLUDE_CPU_FEATURES_MACROS_H_
+301
View File
@@ -0,0 +1,301 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
////////////////////////////////////////////////////////////////////////////////
// A note on Windows AArch64 implementation
////////////////////////////////////////////////////////////////////////////////
// Getting cpu info via EL1 system registers is not possible, so we delegate it
// to the Windows API (i.e., IsProcessorFeaturePresent and GetNativeSystemInfo).
// The `implementer`, `variant` and `part` fields of the `Aarch64Info` struct
// are not used, so they are set to 0. To get `revision` we use
// `wProcessorRevision` from `SYSTEM_INFO`.
//
// Cryptographic Extension:
// -----------------------------------------------------------------------------
// According to documentation Arm Architecture Reference Manual for
// A-profile architecture. A2.3 The Armv8 Cryptographic Extension. The Armv8.0
// Cryptographic Extension provides instructions for the acceleration of
// encryption and decryption, and includes the following features: FEAT_AES,
// FEAT_PMULL, FEAT_SHA1, FEAT_SHA256.
// see: https://developer.arm.com/documentation/ddi0487/latest
//
// We use `PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE` to detect all Armv8.0 crypto
// features. This value reports all features or nothing, so even if you only
// have support FEAT_AES and FEAT_PMULL, it will still return false.
//
// From Armv8.2, an implementation of the Armv8.0 Cryptographic Extension can
// include either or both of:
//
// • The AES functionality, including support for multiplication of 64-bit
// polynomials. The ID_AA64ISAR0_EL1.AES field indicates whether this
// functionality is supported.
// • The SHA1 and SHA2-256 functionality. The ID_AA64ISAR0_EL1.{SHA2, SHA1}
// fields indicate whether this functionality is supported.
//
// ID_AA64ISAR0_EL1.AES, bits [7:4]:
// Indicates support for AES instructions in AArch64 state. Defined values are:
// - 0b0000 No AES instructions implemented.
// - 0b0001 AESE, AESD, AESMC, and AESIMC instructions implemented.
// - 0b0010 As for 0b0001, plus PMULL/PMULL2 instructions operating on 64-bit
// data quantities.
//
// FEAT_AES implements the functionality identified by the value 0b0001.
// FEAT_PMULL implements the functionality identified by the value 0b0010.
// From Armv8, the permitted values are 0b0000 and 0b0010.
//
// ID_AA64ISAR0_EL1.SHA1, bits [11:8]:
// Indicates support for SHA1 instructions in AArch64 state. Defined values are:
// - 0b0000 No SHA1 instructions implemented.
// - 0b0001 SHA1C, SHA1P, SHA1M, SHA1H, SHA1SU0, and SHA1SU1 instructions
// implemented.
//
// FEAT_SHA1 implements the functionality identified by the value 0b0001.
// From Armv8, the permitted values are 0b0000 and 0b0001.
// If the value of ID_AA64ISAR0_EL1.SHA2 is 0b0000, this field must have the
// value 0b0000.
//
// ID_AA64ISAR0_EL1.SHA2, bits [15:12]:
// Indicates support for SHA2 instructions in AArch64 state. Defined values are:
// - 0b0000 No SHA2 instructions implemented.
// - 0b0001 Implements instructions: SHA256H, SHA256H2, SHA256SU0, and
// SHA256SU1.
// - 0b0010 Implements instructions:
// • SHA256H, SHA256H2, SHA256SU0, and SHA256SU1.
// • SHA512H, SHA512H2, SHA512SU0, and SHA512SU1.
//
// FEAT_SHA256 implements the functionality identified by the value 0b0001.
// FEAT_SHA512 implements the functionality identified by the value 0b0010.
//
// In Armv8, the permitted values are 0b0000 and 0b0001.
// From Armv8.2, the permitted values are 0b0000, 0b0001, and 0b0010.
//
// If the value of ID_AA64ISAR0_EL1.SHA1 is 0b0000, this field must have the
// value 0b0000.
//
// If the value of this field is 0b0010, ID_AA64ISAR0_EL1.SHA3
// must have the value 0b0001.
//
// Other cryptographic features that we cannot detect such as sha512, sha3, sm3,
// sm4, sveaes, svepmull, svesha3, svesm4 we set to 0.
//
// FP/SIMD:
// -----------------------------------------------------------------------------
// FP/SIMD must be implemented on all Armv8.0 implementations, but
// implementations targeting specialized markets may support the following
// combinations:
//
// • No NEON or floating-point.
// • Full floating-point and SIMD support with exception trapping.
// • Full floating-point and SIMD support without exception trapping.
//
// ref:
// https://developer.arm.com/documentation/den0024/a/AArch64-Floating-point-and-NEON
//
// So, we use `PF_ARM_VFP_32_REGISTERS_AVAILABLE`,
// `PF_ARM_NEON_INSTRUCTIONS_AVAILABLE` to detect `asimd` and `fp`
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
int fp : 1; // Floating-point.
int asimd : 1; // Advanced SIMD.
int evtstrm : 1; // Generic timer generated events.
int aes : 1; // Hardware-accelerated Advanced Encryption Standard.
int pmull : 1; // Polynomial multiply long.
int sha1 : 1; // Hardware-accelerated SHA1.
int sha2 : 1; // Hardware-accelerated SHA2-256.
int crc32 : 1; // Hardware-accelerated CRC-32.
int atomics : 1; // Armv8.1 atomic instructions.
int fphp : 1; // Half-precision floating point support.
int asimdhp : 1; // Advanced SIMD half-precision support.
int cpuid : 1; // Access to certain ID registers.
int asimdrdm : 1; // Rounding Double Multiply Accumulate/Subtract.
int jscvt : 1; // Support for JavaScript conversion.
int fcma : 1; // Floating point complex numbers.
int lrcpc : 1; // Support for weaker release consistency.
int dcpop : 1; // Data persistence writeback.
int sha3 : 1; // Hardware-accelerated SHA3.
int sm3 : 1; // Hardware-accelerated SM3.
int sm4 : 1; // Hardware-accelerated SM4.
int asimddp : 1; // Dot product instruction.
int sha512 : 1; // Hardware-accelerated SHA512.
int sve : 1; // Scalable Vector Extension.
int asimdfhm : 1; // Additional half-precision instructions.
int dit : 1; // Data independent timing.
int uscat : 1; // Unaligned atomics support.
int ilrcpc : 1; // Additional support for weaker release consistency.
int flagm : 1; // Flag manipulation instructions.
int ssbs : 1; // Speculative Store Bypass Safe PSTATE bit.
int sb : 1; // Speculation barrier.
int paca : 1; // Address authentication.
int pacg : 1; // Generic authentication.
int dcpodp : 1; // Data cache clean to point of persistence.
int sve2 : 1; // Scalable Vector Extension (version 2).
int sveaes : 1; // SVE AES instructions.
int svepmull : 1; // SVE polynomial multiply long instructions.
int svebitperm : 1; // SVE bit permute instructions.
int svesha3 : 1; // SVE SHA3 instructions.
int svesm4 : 1; // SVE SM4 instructions.
int flagm2 : 1; // Additional flag manipulation instructions.
int frint : 1; // Floating point to integer rounding.
int svei8mm : 1; // SVE Int8 matrix multiplication instructions.
int svef32mm : 1; // SVE FP32 matrix multiplication instruction.
int svef64mm : 1; // SVE FP64 matrix multiplication instructions.
int svebf16 : 1; // SVE BFloat16 instructions.
int i8mm : 1; // Int8 matrix multiplication instructions.
int bf16 : 1; // BFloat16 instructions.
int dgh : 1; // Data Gathering Hint instruction.
int rng : 1; // True random number generator support.
int bti : 1; // Branch target identification.
int mte : 1; // Memory tagging extension.
int ecv : 1; // Enhanced counter virtualization.
int afp : 1; // Alternate floating-point behaviour.
int rpres : 1; // 12-bit reciprocal (square root) estimate precision.
int mte3 : 1; // MTE asymmetric fault handling.
int sme : 1; // Scalable Matrix Extension.
int smei16i64 : 1; // 16-bit to 64-bit integer widening outer product.
int smef64f64 : 1; // FP64 to FP64 outer product.
int smei8i32 : 1; // 8-bit to 32-bit integer widening outer product.
int smef16f32 : 1; // FP16 to FP32 outer product.
int smeb16f32 : 1; // BFloat16 to FP32 outper product.
int smef32f32 : 1; // FP32 to FP32 outer product.
int smefa64 : 1; // Full A64 support for SME in streaming mode.
int wfxt : 1; // WFE and WFI with timeout.
int ebf16 : 1; // Extended BFloat16 instructions.
int sveebf16 : 1; // SVE BFloat16 instructions.
int cssc : 1; // Common short sequence compression instructions.
int rprfm : 1; // Range Prefetch Memory hint instruction.
int sve2p1 : 1; // Scalable Vector Extension (version 2.1).
int sme2 : 1; // Scalable Matrix Extension (version 2).
int sme2p1 : 1; // Scalable Matrix Extension (version 2.1).
int smei16i32 : 1; // 16-bit to 64-bit integer widening outer product.
int smebi32i32 : 1; // 1-bit binary to 32-bit integer outer product.
int smeb16b16 : 1; // SME2.1 BFloat16 instructions.
int smef16f16 : 1; // FP16 to FP16 outer product.
// Make sure to update Aarch64FeaturesEnum below if you add a field here.
} Aarch64Features;
typedef struct {
Aarch64Features features;
int implementer; // We set 0 for Windows.
int variant; // We set 0 for Windows.
int part; // We set 0 for Windows.
int revision; // We use GetNativeSystemInfo to get processor revision for
// Windows.
} Aarch64Info;
Aarch64Info GetAarch64Info(void);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
AARCH64_FP,
AARCH64_ASIMD,
AARCH64_EVTSTRM,
AARCH64_AES,
AARCH64_PMULL,
AARCH64_SHA1,
AARCH64_SHA2,
AARCH64_CRC32,
AARCH64_ATOMICS,
AARCH64_FPHP,
AARCH64_ASIMDHP,
AARCH64_CPUID,
AARCH64_ASIMDRDM,
AARCH64_JSCVT,
AARCH64_FCMA,
AARCH64_LRCPC,
AARCH64_DCPOP,
AARCH64_SHA3,
AARCH64_SM3,
AARCH64_SM4,
AARCH64_ASIMDDP,
AARCH64_SHA512,
AARCH64_SVE,
AARCH64_ASIMDFHM,
AARCH64_DIT,
AARCH64_USCAT,
AARCH64_ILRCPC,
AARCH64_FLAGM,
AARCH64_SSBS,
AARCH64_SB,
AARCH64_PACA,
AARCH64_PACG,
AARCH64_DCPODP,
AARCH64_SVE2,
AARCH64_SVEAES,
AARCH64_SVEPMULL,
AARCH64_SVEBITPERM,
AARCH64_SVESHA3,
AARCH64_SVESM4,
AARCH64_FLAGM2,
AARCH64_FRINT,
AARCH64_SVEI8MM,
AARCH64_SVEF32MM,
AARCH64_SVEF64MM,
AARCH64_SVEBF16,
AARCH64_I8MM,
AARCH64_BF16,
AARCH64_DGH,
AARCH64_RNG,
AARCH64_BTI,
AARCH64_MTE,
AARCH64_ECV,
AARCH64_AFP,
AARCH64_RPRES,
AARCH64_MTE3,
AARCH64_SME,
AARCH64_SME_I16I64,
AARCH64_SME_F64F64,
AARCH64_SME_I8I32,
AARCH64_SME_F16F32,
AARCH64_SME_B16F32,
AARCH64_SME_F32F32,
AARCH64_SME_FA64,
AARCH64_WFXT,
AARCH64_EBF16,
AARCH64_SVE_EBF16,
AARCH64_CSSC,
AARCH64_RPRFM,
AARCH64_SVE2P1,
AARCH64_SME2,
AARCH64_SME2P1,
AARCH64_SME_I16I32,
AARCH64_SME_BI32I32,
AARCH64_SME_B16B16,
AARCH64_SME_F16F16,
AARCH64_LAST_,
} Aarch64FeaturesEnum;
int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
Aarch64FeaturesEnum value);
const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_AARCH64)
#error "Including cpuinfo_aarch64.h from a non-aarch64 target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_AARCH64_H_
+121
View File
@@ -0,0 +1,121 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
#include <stdint.h> // uint32_t
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
int swp : 1; // SWP instruction (atomic read-modify-write)
int half : 1; // Half-word loads and stores
int thumb : 1; // Thumb (16-bit instruction set)
int _26bit : 1; // "26 Bit" Model (Processor status register folded into
// program counter)
int fastmult : 1; // 32x32->64-bit multiplication
int fpa : 1; // Floating point accelerator
int vfp : 1; // Vector Floating Point.
int edsp : 1; // DSP extensions (the 'e' variant of the ARM9 CPUs, and all
// others above)
int java : 1; // Jazelle (Java bytecode accelerator)
int iwmmxt : 1; // Intel Wireless MMX Technology.
int crunch : 1; // MaverickCrunch coprocessor
int thumbee : 1; // ThumbEE
int neon : 1; // Advanced SIMD.
int vfpv3 : 1; // VFP version 3
int vfpv3d16 : 1; // VFP version 3 with 16 D-registers
int tls : 1; // TLS register
int vfpv4 : 1; // VFP version 4 with fast context switching
int idiva : 1; // SDIV and UDIV hardware division in ARM mode.
int idivt : 1; // SDIV and UDIV hardware division in Thumb mode.
int vfpd32 : 1; // VFP with 32 D-registers
int lpae : 1; // Large Physical Address Extension (>4GB physical memory on
// 32-bit architecture)
int evtstrm : 1; // kernel event stream using generic architected timer
int aes : 1; // Hardware-accelerated Advanced Encryption Standard.
int pmull : 1; // Polynomial multiply long.
int sha1 : 1; // Hardware-accelerated SHA1.
int sha2 : 1; // Hardware-accelerated SHA2-256.
int crc32 : 1; // Hardware-accelerated CRC-32.
// Make sure to update ArmFeaturesEnum below if you add a field here.
} ArmFeatures;
typedef struct {
ArmFeatures features;
int implementer;
int architecture;
int variant;
int part;
int revision;
} ArmInfo;
// TODO(user): Add macros to know which features are present at compile
// time.
ArmInfo GetArmInfo(void);
// Compute CpuId from ArmInfo.
uint32_t GetArmCpuId(const ArmInfo* const info);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
ARM_SWP,
ARM_HALF,
ARM_THUMB,
ARM_26BIT,
ARM_FASTMULT,
ARM_FPA,
ARM_VFP,
ARM_EDSP,
ARM_JAVA,
ARM_IWMMXT,
ARM_CRUNCH,
ARM_THUMBEE,
ARM_NEON,
ARM_VFPV3,
ARM_VFPV3D16,
ARM_TLS,
ARM_VFPV4,
ARM_IDIVA,
ARM_IDIVT,
ARM_VFPD32,
ARM_LPAE,
ARM_EVTSTRM,
ARM_AES,
ARM_PMULL,
ARM_SHA1,
ARM_SHA2,
ARM_CRC32,
ARM_LAST_,
} ArmFeaturesEnum;
int GetArmFeaturesEnumValue(const ArmFeatures* features, ArmFeaturesEnum value);
const char* GetArmFeaturesEnumName(ArmFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_ARM)
#error "Including cpuinfo_arm.h from a non-arm target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_ARM_H_
+77
View File
@@ -0,0 +1,77 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
#if !defined(CPU_FEATURES_ARCH_LOONGARCH)
#error "Including cpuinfo_loongarch.h from a non-loongarch target."
#endif
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
// Base
int CPUCFG : 1; // Instruction for Identify CPU Features
// Extension
int LAM : 1; // Extension for Atomic Memory Access Instructions
int UAL : 1; // Extension for Non-Aligned Memory Access
int FPU : 1; // Extension for Basic Floating-Point Instructions
int LSX : 1; // Extension for Loongson SIMD eXtension
int LASX : 1; // Extension for Loongson Advanced SIMD eXtension
int CRC32 : 1; // Extension for Cyclic Redundancy Check Instructions
int COMPLEX : 1; // Extension for Complex Vector Operation Instructions
int CRYPTO : 1; // Extension for Encryption And Decryption Vector Instructions
int LVZ : 1; // Extension for Virtualization
int LBT_X86 : 1; // Extension for X86 Binary Translation Extension
int LBT_ARM : 1; // Extension for ARM Binary Translation Extension
int LBT_MIPS : 1; // Extension for MIPS Binary Translation Extension
int PTW : 1; // Extension for Page Table Walker
} LoongArchFeatures;
typedef struct {
LoongArchFeatures features;
} LoongArchInfo;
typedef enum {
LOONGARCH_CPUCFG,
LOONGARCH_LAM,
LOONGARCH_UAL,
LOONGARCH_FPU,
LOONGARCH_LSX,
LOONGARCH_LASX,
LOONGARCH_CRC32,
LOONGARCH_COMPLEX,
LOONGARCH_CRYPTO,
LOONGARCH_LVZ,
LOONGARCH_LBT_X86,
LOONGARCH_LBT_ARM,
LOONGARCH_LBT_MIPS,
LOONGARCH_PTW,
LOONGARCH_LAST_,
} LoongArchFeaturesEnum;
LoongArchInfo GetLoongArchInfo(void);
int GetLoongArchFeaturesEnumValue(const LoongArchFeatures* features,
LoongArchFeaturesEnum value);
const char* GetLoongArchFeaturesEnumName(LoongArchFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#endif // CPU_FEATURES_INCLUDE_CPUINFO_LOONGARCH_H_
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
int msa : 1; // MIPS SIMD Architecture
// https://www.mips.com/products/architectures/ase/simd/
int eva : 1; // Enhanced Virtual Addressing
// https://www.mips.com/products/architectures/mips64/
int r6 : 1; // True if is release 6 of the processor.
int mips16 : 1; // Compressed instructions
int mdmx : 1; // MIPS Digital Media Extension
int mips3d : 1; // 3D graphics acceleration
// MIPS(r) Architecture for Programmers, Volume IV-c
int smart : 1; // Smart-card cryptography
// MIPS(r) Architecture for Programmers, Volume IV-d
int dsp : 1; // Digital Signal Processing
// MIPS(r) Architecture for Programmers, Volume IV-e
// https://www.mips.com/products/architectures/ase/dsp/
// Make sure to update MipsFeaturesEnum below if you add a field here.
} MipsFeatures;
typedef struct {
MipsFeatures features;
} MipsInfo;
MipsInfo GetMipsInfo(void);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
MIPS_MSA,
MIPS_EVA,
MIPS_R6,
MIPS_MIPS16,
MIPS_MDMX,
MIPS_MIPS3D,
MIPS_SMART,
MIPS_DSP,
MIPS_LAST_,
} MipsFeaturesEnum;
int GetMipsFeaturesEnumValue(const MipsFeatures* features,
MipsFeaturesEnum value);
const char* GetMipsFeaturesEnumName(MipsFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_MIPS)
#error "Including cpuinfo_mips.h from a non-mips target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_MIPS_H_
+149
View File
@@ -0,0 +1,149 @@
// Copyright 2018 IBM
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
int ppc32 : 1;
int ppc64 : 1;
int ppc601 : 1;
int altivec : 1;
int fpu : 1;
int mmu : 1;
int mac_4xx : 1;
int unifiedcache : 1;
int spe : 1;
int efpsingle : 1;
int efpdouble : 1;
int no_tb : 1;
int power4 : 1;
int power5 : 1;
int power5plus : 1;
int cell : 1;
int booke : 1;
int smt : 1;
int icachesnoop : 1;
int arch205 : 1;
int pa6t : 1;
int dfp : 1;
int power6ext : 1;
int arch206 : 1;
int vsx : 1;
int pseries_perfmon_compat : 1;
int truele : 1;
int ppcle : 1;
int arch207 : 1;
int htm : 1;
int dscr : 1;
int ebb : 1;
int isel : 1;
int tar : 1;
int vcrypto : 1;
int htm_nosc : 1;
int arch300 : 1;
int ieee128 : 1;
int darn : 1;
int scv : 1;
int htm_no_suspend : 1;
// Make sure to update PPCFeaturesEnum below if you add a field here.
} PPCFeatures;
typedef struct {
PPCFeatures features;
} PPCInfo;
PPCInfo GetPPCInfo(void);
typedef struct {
char platform[64]; // 0 terminated string
char base_platform[64]; // 0 terminated string
} PPCPlatformTypeStrings;
typedef struct {
char platform[64]; // 0 terminated string
char model[64]; // 0 terminated string
char machine[64]; // 0 terminated string
char cpu[64]; // 0 terminated string
PPCPlatformTypeStrings type;
} PPCPlatformStrings;
PPCPlatformStrings GetPPCPlatformStrings(void);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
PPC_32, /* 32 bit mode execution */
PPC_64, /* 64 bit mode execution */
PPC_601_INSTR, /* Old POWER ISA */
PPC_HAS_ALTIVEC, /* SIMD Unit*/
PPC_HAS_FPU, /* Floating Point Unit */
PPC_HAS_MMU, /* Memory management unit */
PPC_HAS_4xxMAC,
PPC_UNIFIED_CACHE, /* Unified instruction and data cache */
PPC_HAS_SPE, /* Signal processing extention unit */
PPC_HAS_EFP_SINGLE, /* SPE single precision fpu */
PPC_HAS_EFP_DOUBLE, /* SPE double precision fpu */
PPC_NO_TB, /* No timebase */
PPC_POWER4,
PPC_POWER5,
PPC_POWER5_PLUS,
PPC_CELL, /* Cell broadband engine */
PPC_BOOKE, /* Embedded ISA */
PPC_SMT, /* Simultaneous multi-threading */
PPC_ICACHE_SNOOP,
PPC_ARCH_2_05, /* ISA 2.05 - POWER6 */
PPC_PA6T, /* PA Semi 6T core ISA */
PPC_HAS_DFP, /* Decimal floating point unit */
PPC_POWER6_EXT,
PPC_ARCH_2_06, /* ISA 2.06 - POWER7 */
PPC_HAS_VSX, /* Vector-scalar extension */
PPC_PSERIES_PERFMON_COMPAT, /* Set of backwards compatibile performance
monitoring events */
PPC_TRUE_LE,
PPC_PPC_LE,
PPC_ARCH_2_07, /* ISA 2.07 - POWER8 */
PPC_HTM, /* Hardware Transactional Memory */
PPC_DSCR, /* Data stream control register */
PPC_EBB, /* Event base branching */
PPC_ISEL, /* Integer select instructions */
PPC_TAR, /* Target address register */
PPC_VEC_CRYPTO, /* Vector cryptography instructions */
PPC_HTM_NOSC, /* Transactions aborted when syscall made*/
PPC_ARCH_3_00, /* ISA 3.00 - POWER9 */
PPC_HAS_IEEE128, /* VSX IEEE Binary Float 128-bit */
PPC_DARN, /* Deliver a random number instruction */
PPC_SCV, /* scv syscall */
PPC_HTM_NO_SUSPEND, /* TM w/out suspended state */
PPC_LAST_,
} PPCFeaturesEnum;
int GetPPCFeaturesEnumValue(const PPCFeatures* features, PPCFeaturesEnum value);
const char* GetPPCFeaturesEnumName(PPCFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_PPC)
#error "Including cpuinfo_ppc.h from a non-ppc target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_PPC_H_
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
#if !defined(CPU_FEATURES_ARCH_RISCV)
#error "Including cpuinfo_riscv.h from a non-riscv target."
#endif
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
// Base
int RV32I : 1; // Base Integer Instruction Set, 32-bit
int RV64I : 1; // Base Integer Instruction Set, 64-bit
// Extension
int M : 1; // Standard Extension for Integer Multiplication/Division
int A : 1; // Standard Extension for Atomic Instructions
int F : 1; // Standard Extension for Single-Precision Floating-Point
int D : 1; // Standard Extension for Double-Precision Floating-Point
int Q : 1; // Standard Extension for Quad-Precision Floating-Point
int C : 1; // Standard Extension for Compressed Instructions
int V : 1; // Standard Extension for Vector Instructions
int Zicsr : 1; // Control and Status Register (CSR)
int Zifencei : 1; // Instruction-Fetch Fence
} RiscvFeatures;
typedef struct {
RiscvFeatures features;
char uarch[64]; // 0 terminated string
char vendor[64]; // 0 terminated string
} RiscvInfo;
typedef enum {
RISCV_RV32I,
RISCV_RV64I,
RISCV_M,
RISCV_A,
RISCV_F,
RISCV_D,
RISCV_Q,
RISCV_C,
RISCV_V,
RISCV_Zicsr,
RISCV_Zifencei,
RISCV_LAST_,
} RiscvFeaturesEnum;
RiscvInfo GetRiscvInfo(void);
int GetRiscvFeaturesEnumValue(const RiscvFeatures* features,
RiscvFeaturesEnum value);
const char* GetRiscvFeaturesEnumName(RiscvFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#endif // CPU_FEATURES_INCLUDE_CPUINFO_RISCV_H_
+108
View File
@@ -0,0 +1,108 @@
// Copyright 2022 IBM
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_S390X_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_S390X_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
typedef struct {
int esan3: 1; // instructions named N3, "backported" to esa-mode
int zarch: 1; // z/Architecture mode active
int stfle: 1; // store-facility-list-extended
int msa: 1; // message-security assist
int ldisp: 1; // long-displacement
int eimm: 1; // extended-immediate
int dfp: 1; // decimal floating point & perform floating point operation
int edat: 1; // huge page support
int etf3eh: 1; // extended-translation facility 3 enhancement
int highgprs: 1; // 64-bit register support for 31-bit processes
int te: 1; // transactional execution
int vx: 1; // vector extension facility
int vxd: 1; // vector-packed-decimal facility
int vxe: 1; // vector-enhancement facility 1
int gs: 1; // guarded-storage facility
int vxe2: 1; // vector-enhancements facility 2
int vxp: 1; // vector-packed-decimal-enhancement facility
int sort: 1; // enhanced-sort facility
int dflt: 1; // deflate-conversion facility
int vxp2: 1; // vector-packed-decimal-enhancement facility 2
int nnpa: 1; // neural network processing assist facility
int pcimio: 1; // PCI mio facility
int sie: 1; // virtualization support
// Make sure to update S390XFeaturesEnum below if you add a field here.
} S390XFeatures;
typedef struct {
S390XFeatures features;
} S390XInfo;
S390XInfo GetS390XInfo(void);
typedef struct {
char platform[64]; // 0 terminated string
} S390XPlatformTypeStrings;
typedef struct {
int num_processors; // -1 if N/A
S390XPlatformTypeStrings type;
} S390XPlatformStrings;
S390XPlatformStrings GetS390XPlatformStrings(void);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
S390_ESAN3,
S390_ZARCH,
S390_STFLE,
S390_MSA,
S390_LDISP,
S390_EIMM,
S390_DFP,
S390_EDAT,
S390_ETF3EH,
S390_HIGHGPRS,
S390_TE,
S390_VX,
S390_VXD,
S390_VXE,
S390_GS,
S390_VXE2,
S390_VXP,
S390_SORT,
S390_DFLT,
S390_VXP2,
S390_NNPA,
S390_PCIMIO,
S390_SIE,
S390X_LAST_,
} S390XFeaturesEnum;
int GetS390XFeaturesEnumValue(const S390XFeatures* features, S390XFeaturesEnum value);
const char* GetS390XFeaturesEnumName(S390XFeaturesEnum);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_S390X)
#error "Including cpuinfo_s390x.h from a non-s390x target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_S390X_H_
+295
View File
@@ -0,0 +1,295 @@
// Copyright 2017 Google LLC
// Copyright 2020 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
#define CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
#include "cpu_features_cache_info.h"
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
// CPUID Vendors
#define CPU_FEATURES_VENDOR_GENUINE_INTEL "GenuineIntel"
#define CPU_FEATURES_VENDOR_AUTHENTIC_AMD "AuthenticAMD"
#define CPU_FEATURES_VENDOR_HYGON_GENUINE "HygonGenuine"
#define CPU_FEATURES_VENDOR_CENTAUR_HAULS "CentaurHauls"
#define CPU_FEATURES_VENDOR_SHANGHAI " Shanghai "
// See https://en.wikipedia.org/wiki/CPUID for a list of x86 cpu features.
// The field names are based on the short name provided in the wikipedia tables.
typedef struct {
int fpu : 1;
int tsc : 1;
int cx8 : 1;
int clfsh : 1;
int mmx : 1;
int aes : 1;
int erms : 1;
int f16c : 1;
int fma4 : 1;
int fma3 : 1;
int vaes : 1;
int vpclmulqdq : 1;
int bmi1 : 1;
int hle : 1;
int bmi2 : 1;
int rtm : 1;
int rdseed : 1;
int clflushopt : 1;
int clwb : 1;
int sse : 1;
int sse2 : 1;
int sse3 : 1;
int ssse3 : 1;
int sse4_1 : 1;
int sse4_2 : 1;
int sse4a : 1;
int avx : 1;
int avx_vnni : 1;
int avx2 : 1;
int avx512f : 1;
int avx512cd : 1;
int avx512er : 1;
int avx512pf : 1;
int avx512bw : 1;
int avx512dq : 1;
int avx512vl : 1;
int avx512ifma : 1;
int avx512vbmi : 1;
int avx512vbmi2 : 1;
int avx512vnni : 1;
int avx512bitalg : 1;
int avx512vpopcntdq : 1;
int avx512_4vnniw : 1;
int avx512_4vbmi2 : 1; // Note: this is an alias to avx512_4fmaps.
int avx512_second_fma : 1;
int avx512_4fmaps : 1;
int avx512_bf16 : 1;
int avx512_vp2intersect : 1;
int avx512_fp16 : 1;
int amx_bf16 : 1;
int amx_tile : 1;
int amx_int8 : 1;
int amx_fp16 : 1;
int pclmulqdq : 1;
int smx : 1;
int sgx : 1;
int cx16 : 1; // aka. CMPXCHG16B
int sha : 1;
int popcnt : 1;
int movbe : 1;
int rdrnd : 1;
int dca : 1;
int ss : 1;
int adx : 1;
int lzcnt : 1; // Note: this flag is called ABM for AMD, LZCNT for Intel.
int gfni : 1;
int movdiri : 1;
int movdir64b : 1;
int fs_rep_mov : 1; // Fast short REP MOV
int fz_rep_movsb : 1; // Fast zero-length REP MOVSB
int fs_rep_stosb : 1; // Fast short REP STOSB
int fs_rep_cmpsb_scasb : 1; // Fast short REP CMPSB/SCASB
int lam: 1; // Intel Linear Address Mask
int uai: 1; // AMD Upper Address Ignore
// Make sure to update X86FeaturesEnum below if you add a field here.
} X86Features;
typedef struct {
X86Features features;
int family;
int model;
int stepping;
char vendor[13]; // 0 terminated string
char brand_string[49]; // 0 terminated string
} X86Info;
// Calls cpuid and returns an initialized X86info.
X86Info GetX86Info(void);
// Returns cache hierarchy informations.
// Can call cpuid multiple times.
CacheInfo GetX86CacheInfo(void);
typedef enum {
X86_UNKNOWN,
ZHAOXIN_ZHANGJIANG, // ZhangJiang
ZHAOXIN_WUDAOKOU, // WuDaoKou
ZHAOXIN_LUJIAZUI, // LuJiaZui
ZHAOXIN_YONGFENG, // YongFeng
INTEL_80486, // 80486
INTEL_P5, // P5
INTEL_LAKEMONT, // LAKEMONT
INTEL_CORE, // CORE
INTEL_PNR, // PENRYN
INTEL_NHM, // NEHALEM
INTEL_ATOM_BNL, // BONNELL
INTEL_WSM, // WESTMERE
INTEL_SNB, // SANDYBRIDGE
INTEL_IVB, // IVYBRIDGE
INTEL_ATOM_SMT, // SILVERMONT
INTEL_HSW, // HASWELL
INTEL_BDW, // BROADWELL
INTEL_SKL, // SKYLAKE
INTEL_CCL, // CASCADELAKE
INTEL_ATOM_GMT, // GOLDMONT
INTEL_ATOM_GMT_PLUS, // GOLDMONT+
INTEL_ATOM_TMT, // TREMONT
INTEL_KBL, // KABY LAKE
INTEL_CFL, // COFFEE LAKE
INTEL_WHL, // WHISKEY LAKE
INTEL_CML, // COMET LAKE
INTEL_CNL, // CANNON LAKE
INTEL_ICL, // ICE LAKE
INTEL_TGL, // TIGER LAKE
INTEL_SPR, // SAPPHIRE RAPIDS
INTEL_ADL, // ALDER LAKE
INTEL_RCL, // ROCKET LAKE
INTEL_RPL, // RAPTOR LAKE
INTEL_KNIGHTS_M, // KNIGHTS MILL
INTEL_KNIGHTS_L, // KNIGHTS LANDING
INTEL_KNIGHTS_F, // KNIGHTS FERRY
INTEL_KNIGHTS_C, // KNIGHTS CORNER
INTEL_NETBURST, // NETBURST
AMD_HAMMER, // K8 HAMMER
AMD_K10, // K10
AMD_K11, // K11
AMD_K12, // K12 LLANO
AMD_BOBCAT, // K14 BOBCAT
AMD_PILEDRIVER, // K15 PILEDRIVER
AMD_STREAMROLLER, // K15 STREAMROLLER
AMD_EXCAVATOR, // K15 EXCAVATOR
AMD_BULLDOZER, // K15 BULLDOZER
AMD_JAGUAR, // K16 JAGUAR
AMD_PUMA, // K16 PUMA
AMD_ZEN, // K17 ZEN
AMD_ZEN_PLUS, // K17 ZEN+
AMD_ZEN2, // K17 ZEN 2
AMD_ZEN3, // K19 ZEN 3
AMD_ZEN4, // K19 ZEN 4
X86_MICROARCHITECTURE_LAST_,
} X86Microarchitecture;
// Returns the underlying microarchitecture by looking at X86Info's vendor,
// family and model.
X86Microarchitecture GetX86Microarchitecture(const X86Info* info);
// Calls cpuid and fills the brand_string.
// - brand_string *must* be of size 49 (beware of array decaying).
// - brand_string will be zero terminated.
CPU_FEATURES_DEPRECATED("brand_string is now embedded in X86Info by default")
void FillX86BrandString(char brand_string[49]);
////////////////////////////////////////////////////////////////////////////////
// Introspection functions
typedef enum {
X86_FPU,
X86_TSC,
X86_CX8,
X86_CLFSH,
X86_MMX,
X86_AES,
X86_ERMS,
X86_F16C,
X86_FMA4,
X86_FMA3,
X86_VAES,
X86_VPCLMULQDQ,
X86_BMI1,
X86_HLE,
X86_BMI2,
X86_RTM,
X86_RDSEED,
X86_CLFLUSHOPT,
X86_CLWB,
X86_SSE,
X86_SSE2,
X86_SSE3,
X86_SSSE3,
X86_SSE4_1,
X86_SSE4_2,
X86_SSE4A,
X86_AVX,
X86_AVX_VNNI,
X86_AVX2,
X86_AVX512F,
X86_AVX512CD,
X86_AVX512ER,
X86_AVX512PF,
X86_AVX512BW,
X86_AVX512DQ,
X86_AVX512VL,
X86_AVX512IFMA,
X86_AVX512VBMI,
X86_AVX512VBMI2,
X86_AVX512VNNI,
X86_AVX512BITALG,
X86_AVX512VPOPCNTDQ,
X86_AVX512_4VNNIW,
X86_AVX512_4VBMI2, // Note: this is an alias to X86_AVX512_4FMAPS.
X86_AVX512_SECOND_FMA,
X86_AVX512_4FMAPS,
X86_AVX512_BF16,
X86_AVX512_VP2INTERSECT,
X86_AVX512_FP16,
X86_AMX_BF16,
X86_AMX_TILE,
X86_AMX_INT8,
X86_AMX_FP16,
X86_PCLMULQDQ,
X86_SMX,
X86_SGX,
X86_CX16,
X86_SHA,
X86_POPCNT,
X86_MOVBE,
X86_RDRND,
X86_DCA,
X86_SS,
X86_ADX,
X86_LZCNT,
X86_GFNI,
X86_MOVDIRI,
X86_MOVDIR64B,
X86_FS_REP_MOV,
X86_FZ_REP_MOVSB,
X86_FS_REP_STOSB,
X86_FS_REP_CMPSB_SCASB,
X86_LAM,
X86_UAI,
X86_LAST_,
} X86FeaturesEnum;
int GetX86FeaturesEnumValue(const X86Features* features, X86FeaturesEnum value);
const char* GetX86FeaturesEnumName(X86FeaturesEnum);
const char* GetX86MicroarchitectureName(X86Microarchitecture);
CPU_FEATURES_END_CPP_NAMESPACE
#if !defined(CPU_FEATURES_ARCH_X86)
#error "Including cpuinfo_x86.h from a non-x86 target."
#endif
#endif // CPU_FEATURES_INCLUDE_CPUINFO_X86_H_
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2017 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
#define CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "cpu_features_macros.h"
CPU_FEATURES_START_CPP_NAMESPACE
inline static bool IsBitSet(uint32_t reg, uint32_t bit) {
return (reg >> bit) & 0x1;
}
inline static uint32_t ExtractBitRange(uint32_t reg, uint32_t msb,
uint32_t lsb) {
const uint64_t bits = msb - lsb + 1ULL;
const uint64_t mask = (1ULL << bits) - 1ULL;
assert(msb >= lsb);
return (reg >> lsb) & mask;
}
CPU_FEATURES_END_CPP_NAMESPACE
#endif // CPU_FEATURES_INCLUDE_INTERNAL_BIT_UTILS_H_

Some files were not shown because too many files have changed in this diff Show More