diff --git a/.dockerignore b/.dockerignore
index 852f8ce..e2cfd15 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,3 +2,4 @@ bin/**
dist/**
docker/**
cmake-build*
+.ccache/**
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 268577f..07a2a7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,6 @@ build_docker.local.sh
bin/*
dist/*
-external/cv2pdb/*
\ No newline at end of file
+external/cv2pdb/*
+
+.ccache/*
\ No newline at end of file
diff --git a/.idea/runConfigurations/Clion_Init_.xml b/.idea/runConfigurations/Clion_Init_.xml
new file mode 100644
index 0000000..f9184c5
--- /dev/null
+++ b/.idea/runConfigurations/Clion_Init_.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Deploy_Extra_x64_.xml b/.idea/runConfigurations/Deploy_Extra_x64_.xml
new file mode 100644
index 0000000..01a4d13
--- /dev/null
+++ b/.idea/runConfigurations/Deploy_Extra_x64_.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Deploy_Extra_x86_.xml b/.idea/runConfigurations/Deploy_Extra_x86_.xml
new file mode 100644
index 0000000..1943362
--- /dev/null
+++ b/.idea/runConfigurations/Deploy_Extra_x86_.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Distribute_Release_.xml b/.idea/runConfigurations/Distribute_Release_.xml
index 92e6fd5..91591e1 100644
--- a/.idea/runConfigurations/Distribute_Release_.xml
+++ b/.idea/runConfigurations/Distribute_Release_.xml
@@ -15,6 +15,11 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/spicetools_cfg.xml b/.idea/runConfigurations/spicetools_cfg.xml
index 274f9f5..50740a7 100644
--- a/.idea/runConfigurations/spicetools_cfg.xml
+++ b/.idea/runConfigurations/spicetools_cfg.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.idea/runConfigurations/spicetools_spice.xml b/.idea/runConfigurations/spicetools_spice.xml
index f4ccb32..9c8b413 100644
--- a/.idea/runConfigurations/spicetools_spice.xml
+++ b/.idea/runConfigurations/spicetools_spice.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/.idea/runConfigurations/spicetools_spice64.xml b/.idea/runConfigurations/spicetools_spice64.xml
index fdb2a51..66044e6 100644
--- a/.idea/runConfigurations/spicetools_spice64.xml
+++ b/.idea/runConfigurations/spicetools_spice64.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d089335..6cf6ad5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.9)
+cmake_minimum_required(VERSION 3.12)
cmake_policy(SET CMP0069 NEW)
project(spicetools)
include(CheckIPOSupported)
@@ -14,7 +14,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
add_compile_definitions(RAPIDJSON_HAS_STDSTRING)
if(MSVC)
-
# disable intermediate manifest
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
@@ -34,27 +33,53 @@ if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DYNAMICBASE:NO")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DYNAMICBASE:NO")
- # use statically linked runtime
- set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
- set(CompilerFlags
- CMAKE_CXX_FLAGS
- CMAKE_CXX_FLAGS_DEBUG
- CMAKE_CXX_FLAGS_RELEASE
- CMAKE_C_FLAGS
- CMAKE_C_FLAGS_DEBUG
- CMAKE_C_FLAGS_RELEASE
- )
- foreach(CompilerFlag ${CompilerFlags})
- string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
- endforeach()
+ set(USE_STATIC_MSVCRT ON CACHE BOOL "If enabled, will force the use of static crt instead of dynamic")
- # 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")
+ if(USE_STATIC_MSVCRT)
+ # use statically linked runtime
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>")
+ set(CompilerFlags
+ CMAKE_CXX_FLAGS
+ CMAKE_CXX_FLAGS_DEBUG
+ CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_C_FLAGS
+ CMAKE_C_FLAGS_DEBUG
+ CMAKE_C_FLAGS_RELEASE
+ )
+
+ foreach(CompilerFlag ${CompilerFlags})
+ string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
+ endforeach()
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("$<$:/ZI>")
+ add_link_options("$<$:/SAFESEH:NO>")
+
+ # enable fast pdb generation in debug
+ add_link_options("$<$:/DEBUG:FASTLINK>")
+
+ # enable pdb generation for release builds
+ add_compile_options("$<$:/Zi>")
+ add_link_options("$<$:/DEBUG:FULL>")
+
+ # enable COMDAT folding for even smaller release builds
+ add_link_options("$<$:/OPT:ICF>")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# disable warnings about using non _s variants like strncpy
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
@@ -94,8 +119,13 @@ else()
# hide ident strings
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_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(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")
# performance
- set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Ofast -pipe")
- set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Ofast -pipe")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -pipe")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -pipe")
# ensure frame pointers are enabled
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-omit-frame-pointer")
@@ -128,8 +158,10 @@ else()
# hide ident strings
set(CMAKE_C_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(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fvisibility=hidden")
@@ -151,6 +183,10 @@ else()
set(CMAKE_C_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
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")
@@ -169,14 +205,13 @@ endif()
add_compile_definitions(
WIN32_LEAN_AND_MEAN
_WIN32_IE=0x0400
- OPENVR_BUILD_STATIC
)
# acioemu log
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACIOEMU_LOG")
# 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_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/imgui EXCLUDE_FROM_ALL)
add_subdirectory(external/minhook EXCLUDE_FROM_ALL)
-add_subdirectory(external/openvr EXCLUDE_FROM_ALL)
-add_subdirectory(external/lua EXCLUDE_FROM_ALL)
+add_subdirectory(external/cpu_features EXCLUDE_FROM_ALL)
# set link time optimizations (disabled for Debug builds for speed, disabled
# for RelWithDebInfo builds due to "lto1: error: two or more sections for"
# errors)
check_ipo_supported()
-if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo" OR CMAKE_BUILD_TYPE MATCHES "Debug")
- set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
-else()
- set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
-endif()
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF)
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
# resources
###########
@@ -266,6 +299,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
api/serial.cpp
api/modules/drs.cpp
api/modules/lcd.cpp
+ api/modules/ddr.cpp
+ api/modules/resize.cpp
# avs
avs/core.cpp
@@ -297,17 +332,6 @@ set(SOURCE_FILES ${SOURCE_FILES}
# external asio
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/cardio_hid.cpp
external/cardio/cardio_window.cpp
@@ -335,10 +359,15 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/hpm/io.cpp
games/iidx/iidx.cpp
games/iidx/io.cpp
+ games/iidx/poke.cpp
games/iidx/bi2a.cpp
games/iidx/bi2x.cpp
games/iidx/bi2x_hook.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/sdvx.cpp
games/sdvx/io.cpp
@@ -347,6 +376,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/jb/io.cpp
games/nost/nost.cpp
games/nost/io.cpp
+ games/nost/poke.cpp
games/gitadora/gitadora.cpp
games/gitadora/io.cpp
games/mga/mga.cpp
@@ -362,6 +392,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/rf3d/rf3d.cpp
games/rf3d/io.cpp
games/museca/io.cpp
+ games/museca/museca.cpp
games/dea/dea.cpp
games/dea/io.cpp
games/qma/qma.cpp
@@ -372,6 +403,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/ddr/p3io/p3io.cpp
games/ddr/p3io/sate.cpp
games/ddr/p3io/usbmem.cpp
+ games/ddr/p4io/p4io.cpp
+ games/ddr/p4io/p4io.h
games/mfc/mfc.cpp
games/mfc/io.cpp
games/ftt/ftt.cpp
@@ -400,12 +433,23 @@ set(SOURCE_FILES ${SOURCE_FILES}
games/onpara/touchpanel.cpp
games/bc/bc.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/audio/audio.cpp
hooks/audio/buffer.cpp
hooks/audio/util.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_enumerator.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_render_client.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/implementations/asio.cpp
hooks/audio/implementations/wave_out.cpp
@@ -425,6 +470,8 @@ set(SOURCE_FILES ${SOURCE_FILES}
hooks/devicehook.cpp
hooks/graphics/graphics.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_device.cpp
hooks/graphics/backends/d3d9/d3d9_fake_swapchain.cpp
@@ -441,6 +488,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
hooks/setupapihook.cpp
hooks/sleephook.cpp
hooks/unisintrhook.cpp
+ hooks/winuser.cpp
# launcher
launcher/launcher.cpp
@@ -459,18 +507,21 @@ set(SOURCE_FILES ${SOURCE_FILES}
misc/extdev.cpp
misc/sciunit.cpp
misc/sde.cpp
- misc/vrutil.cpp
misc/wintouchemu.cpp
+ # nvapi
+ nvapi/nvapi.cpp
+
# overlay
overlay/overlay.cpp
overlay/window.cpp
overlay/imgui/extensions.cpp
- overlay/imgui/impl_dx9.cpp
overlay/imgui/impl_spice.cpp
overlay/imgui/impl_sw.cpp
overlay/windows/acio_status_buffers.cpp
+ overlay/windows/camera_control.cpp
overlay/windows/card_manager.cpp
+ overlay/windows/drs_dancefloor.cpp
overlay/windows/screen_resize.cpp
overlay/windows/sdvx_sub.cpp
overlay/windows/config.cpp
@@ -485,11 +536,9 @@ set(SOURCE_FILES ${SOURCE_FILES}
overlay/windows/iopanel_gfdm.cpp
overlay/windows/iopanel_iidx.cpp
overlay/windows/keypad.cpp
- overlay/windows/kfcontrol.cpp
overlay/windows/log.cpp
overlay/windows/midi.cpp
overlay/windows/patch_manager.cpp
- overlay/windows/vr.cpp
overlay/windows/wnd_manager.cpp
# rawinput
@@ -498,6 +547,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
rawinput/piuio.cpp
rawinput/touch.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.cpp
@@ -505,30 +560,12 @@ set(SOURCE_FILES ${SOURCE_FILES}
reader/structuredmessage.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.cpp
# touch
touch/touch.cpp
+ touch/touch_indicators.cpp
touch/win7.cpp
touch/win8.cpp
@@ -541,6 +578,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
util/libutils.cpp
util/fileutils.cpp
util/resutils.cpp
+ util/unity_player.cpp
util/utils.cpp
util/memutils.cpp
util/rc4.cpp
@@ -548,17 +586,22 @@ set(SOURCE_FILES ${SOURCE_FILES}
util/time.cpp
util/cpuutils.cpp
util/netutils.cpp
+ util/sysutils.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
###########
set(RESOURCE_FILES build/manifest.manifest build/manifest.rc build/icon.rc cfg/Win32D.rc)
add_executable(spicetools_spice ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_spice
- PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard
- PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi)
+ 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 PROPERTIES PREFIX "")
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
@@ -566,14 +609,33 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
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
#############
set(RESOURCE_FILES build/manifest.manifest build/manifest64.rc build/icon.rc cfg/Win32D.rc)
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
- PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard
- PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api64 lua_static imm32 dwmapi ntdll)
+ 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 imm32 dwmapi CpuFeatures::cpu_features)
+
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
@@ -582,7 +644,6 @@ IF(NOT MSVC)
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
endif()
-
# spicecfg.exe
##############
@@ -590,8 +651,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)
add_executable(spicetools_cfg WIN32 ${SOURCE_FILES} ${RESOURCE_FILES})
target_link_libraries(spicetools_cfg
- PUBLIC ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard
- PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi)
+ 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 imm32 dwmapi CpuFeatures::cpu_features)
set_target_properties(spicetools_cfg PROPERTIES PREFIX "")
set_target_properties(spicetools_cfg PROPERTIES OUTPUT_NAME "spicecfg")
target_compile_definitions(spicetools_cfg PRIVATE SPICETOOLS_SPICECFG_STANDALONE=1)
@@ -649,6 +710,36 @@ if(NOT MSVC)
set_target_properties(spicetools_stubs_kld64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
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
####################
@@ -658,14 +749,14 @@ set_target_properties(spicetools_cfg
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
# 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
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive32"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/32")
# 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
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/archive64"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64"
diff --git a/Dockerfile b/Dockerfile
index 0300cf9..6472577 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
FROM spicetools/deps
WORKDIR /src
-RUN chown user:user /src
-USER user
-COPY --chown=user:user . /src
-CMD ./build_all.sh
+
+COPY --from=gitroot . /src/.git
+COPY . /src/src/spice2x
+WORKDIR /src/src/spice2x
+ENTRYPOINT ["./build_all.sh"]
diff --git a/README.md b/README.md
index a555a36..e7c754d 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ If you need more examples, you can check the example code.
"id": 1,
"module": "card",
"function": "insert",
- "params": [0, "E004000000000000"]
+ "params": [0, "E004010000000000"]
}
```
#### 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
- 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
- read()
- 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()
- 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
Unless otherwise noted, all files are licensed under the GPLv3.
See the LICENSE file for the full license text.
diff --git a/acio/acio.cpp b/acio/acio.cpp
index 4fcd566..2ae28f4 100644
--- a/acio/acio.cpp
+++ b/acio/acio.cpp
@@ -70,6 +70,9 @@ void acio::attach() {
libraryhook_hook_library("libacioex.dll", acio::DLL_INSTANCE);
libraryhook_hook_library("libacio_ex.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);
// get hook mode
diff --git a/acio/bi2a/bi2a.cpp b/acio/bi2a/bi2a.cpp
index 1198473..68b2235 100644
--- a/acio/bi2a/bi2a.cpp
+++ b/acio/bi2a/bi2a.cpp
@@ -2,6 +2,7 @@
#include "avs/game.h"
#include "games/ddr/io.h"
+#include "games/ddr/ddr.h"
#include "games/sdvx/sdvx.h"
#include "games/sdvx/io.h"
#include "games/drs/io.h"
@@ -9,6 +10,7 @@
#include "misc/eamuse.h"
#include "util/logging.h"
#include "util/utils.h"
+#include "util/tapeled.h"
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) {
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
@@ -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) {
// 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");
}
@@ -458,9 +602,151 @@ static bool __cdecl ac_io_bi2a_control_tapeled_rec_set(uint8_t* data, size_t x_s
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,
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;
}
diff --git a/acio/bmpu/bmpu.cpp b/acio/bmpu/bmpu.cpp
index c3e1f16..e8ccb5f 100644
--- a/acio/bmpu/bmpu.cpp
+++ b/acio/bmpu/bmpu.cpp
@@ -33,6 +33,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
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;
@@ -44,6 +49,11 @@ static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
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;
@@ -55,6 +65,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
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;
@@ -66,6 +81,11 @@ static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
if (avs::game::is_model("KDM")) {
auto &lights = games::dea::get_lights();
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;
diff --git a/acio/icca/icca.cpp b/acio/icca/icca.cpp
index 6477031..82d3b4f 100644
--- a/acio/icca/icca.cpp
+++ b/acio/icca/icca.cpp
@@ -25,6 +25,13 @@ struct ICCA_STATUS {
uint32_t key_edge;
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");
enum ICCA_WORKFLOW {
@@ -52,6 +59,7 @@ struct ICCA_UNIT {
char key_serial = 0;
bool uid_skip = false;
bool initialized = false;
+ int felica_retries = 0;
};
static ICCA_UNIT ICCA_UNITS[2] {};
static bool IS_LAST_CARD_FELICA = false;
@@ -88,14 +96,25 @@ static inline void update_card(int unit_id) {
// eamio keypress
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;
+ static bool kb_insert_press_old[2] = {false, false};
// get unit
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
- static bool kb_insert_press_old[2] = {false, false};
- if (eamuse_card_insert_consume(icca_get_active_count(), unit_id) ||
- (kb_insert_press && !kb_insert_press_old[unit_id])) {
+ if (card_presented || key_pressed || (0 < unit->felica_retries)) {
if (!unit->card_cmd_pressed) {
unit->card_cmd_pressed = true;
if (unit->state == GET_USERID || unit->state == CLOSE_EJECT || unit->state == STEP) {
@@ -288,17 +307,29 @@ static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
// funny workaround
if (acio::ICCA_COMPAT_ACTIVE) {
+ if (avs::game::is_model("LA9")) {
+ auto p = (ICCA_STATUS*) status;
- // the struct is different (28 bytes instead of 24) but nobody ain't got time for that
- auto p = (ICCA_STATUS*) status;
- p->error = p->key_level << 16;
- p->front_sensor = p->uid[0];
- p->rear_sensor = p->uid[1];
- for (size_t i = 2; i < sizeof(p->uid); i++) {
- p->uid[i - 2] = p->uid[i];
+ 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
+ auto p = (ICCA_STATUS*) status;
+ p->error = p->key_level << 16;
+ p->front_sensor = p->uid[0];
+ p->rear_sensor = p->uid[1];
+ for (size_t i = 2; i < sizeof(p->uid); i++) {
+ p->uid[i - 2] = p->uid[i];
+ }
+ p->uid[sizeof(p->uid) - 2] = 0;
+ p->uid[sizeof(p->uid) - 1] = 0;
}
- p->uid[sizeof(p->uid) - 2] = 0;
- p->uid[sizeof(p->uid) - 1] = 0;
}
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);
IS_LAST_CARD_FELICA = felica;
+ if (0 < unit->felica_retries) {
+ unit->felica_retries--;
+ }
+
// check for error
return unit->state != ERR_GETUID;
}
diff --git a/acio/mdxf/mdxf.cpp b/acio/mdxf/mdxf.cpp
index 6f57674..8fe81d2 100644
--- a/acio/mdxf/mdxf.cpp
+++ b/acio/mdxf/mdxf.cpp
@@ -16,13 +16,26 @@ static uint8_t COUNTER = 0;
// buffers
#pragma pack(push, 1)
static struct {
- uint8_t STATUS_BUFFER_17[7][STATUS_BUFFER_SIZE] {};
- uint8_t STATUS_BUFFER_18[7][STATUS_BUFFER_SIZE] {};
+ uint8_t STATUS_BUFFER_P1[7][STATUS_BUFFER_SIZE] {};
+ uint8_t STATUS_BUFFER_P2[7][STATUS_BUFFER_SIZE] {};
} BUFFERS {};
#pragma pack(pop)
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
*/
@@ -33,13 +46,13 @@ static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer,
if (avs::game::is_model("MDX")) {
// 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
- if (node == 17) {
- memcpy(buffer, BUFFERS.STATUS_BUFFER_17[i], STATUS_BUFFER_SIZE);
- } else if (node == 18) {
- memcpy(buffer, BUFFERS.STATUS_BUFFER_18[i], STATUS_BUFFER_SIZE);
+ if (node == 17 || node == 25) {
+ memcpy(buffer, BUFFERS.STATUS_BUFFER_P1[i], STATUS_BUFFER_SIZE);
+ } else if (node == 18 || node == 26) {
+ memcpy(buffer, BUFFERS.STATUS_BUFFER_P2[i], STATUS_BUFFER_SIZE);
} else {
// fill with zeros on unknown node
@@ -52,16 +65,49 @@ static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer,
return true;
}
-static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t a3) {
- // TODO(felix): DDR BIO2 lights
+static bool __cdecl ac_io_mdxf_set_output_level(unsigned int a1, unsigned int a2, uint8_t value) {
+ 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;
}
static bool __cdecl ac_io_mdxf_update_control_status_buffer(int node) {
// increase counter
- COUNTER = (COUNTER + 1) % std::size(BUFFERS.STATUS_BUFFER_17);
+ COUNTER = (COUNTER + 1) % std::size(BUFFERS.STATUS_BUFFER_P1);
// check 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;
switch (node) {
case 17:
- buffer = BUFFERS.STATUS_BUFFER_17[COUNTER];
+ case 25:
+ buffer = BUFFERS.STATUS_BUFFER_P1[COUNTER];
break;
case 18:
- buffer = BUFFERS.STATUS_BUFFER_18[COUNTER];
+ case 26:
+ buffer = BUFFERS.STATUS_BUFFER_P2[COUNTER];
break;
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 RIGHT = bit 40-43 = byte 5, bit 0-3
// 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_DOWN,
games::ddr::Buttons::P1_PANEL_LEFT,
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_DOWN,
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;
switch (node) {
case 17:
- button_map = &buttons_17[0];
+ case 25:
+ button_map = &buttons_p1[0];
break;
case 18:
- button_map = &buttons_18[0];
+ case 26:
+ button_map = &buttons_p2[0];
break;
}
+ *(uint64_t*)&buffer[0x18] = arkGetTickTime64();
+
// get buttons
auto &buttons = games::ddr::get_buttons();
diff --git a/acio/module.h b/acio/module.h
index da07c09..2d35175 100644
--- a/acio/module.h
+++ b/acio/module.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
diff --git a/acio/panb/panb.cpp b/acio/panb/panb.cpp
index b499660..188a17f 100644
--- a/acio/panb/panb.cpp
+++ b/acio/panb/panb.cpp
@@ -6,6 +6,11 @@
#include "util/logging.h"
#include "avs/game.h"
+// std::min
+#ifdef min
+#undef min
+#endif
+
using namespace GameAPI;
// static stuff
@@ -79,6 +84,32 @@ static bool __cdecl ac_io_panb_start_auto_input() {
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() {
// check freeze
@@ -107,6 +138,7 @@ static bool __cdecl ac_io_panb_update_control_status_buffer() {
auto &analogs = games::nost::get_analogs();
// mappings
+ // "normal" buttons - these are velocity sensitive (digital or MIDI)
static const size_t button_mapping[] = {
games::nost::Buttons::Key1, games::nost::Buttons::Key2,
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::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[] = {
games::nost::Analogs::Key1, games::nost::Analogs::Key2,
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;
// 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 &analog1 = analogs.at(analog_mapping[key_pair * 2 + 1]);
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()) {
- 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
- auto velocity0 = Buttons::getVelocity(RI_MGR, buttons.at(button_mapping[key_pair * 2 + 0]));
- auto velocity1 = Buttons::getVelocity(RI_MGR, buttons.at(button_mapping[key_pair * 2 + 1]));
- if (velocity0 > 0.f) {
- state0 = (uint8_t) (velocity0 * 15.999f);
+ // check digital buttons
+ const auto button0 = panb_get_button_velocity(
+ buttons.at(button_mapping[key_pair * 2 + 0]),
+ buttons.at(soft_button_mapping[key_pair * 2 + 0]),
+ 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
diff --git a/acio2emu/firmware/bi2x.cpp b/acio2emu/firmware/bi2x.cpp
index 123751c..bd287f6 100644
--- a/acio2emu/firmware/bi2x.cpp
+++ b/acio2emu/firmware/bi2x.cpp
@@ -44,7 +44,7 @@ namespace acio2emu::firmware {
break;
case 785: { // write output
- auto count = write_output(std::span{cur.base(), static_cast(in.payload.end() - cur)});
+ auto count = write_output(std::span{&*cur, static_cast(in.payload.end() - cur)});
if (count < 0) {
return false;
}
diff --git a/acioemu/acioemu.cpp b/acioemu/acioemu.cpp
index be452ef..658cbc3 100644
--- a/acioemu/acioemu.cpp
+++ b/acioemu/acioemu.cpp
@@ -108,6 +108,10 @@ std::optional ACIOEmu::read() {
return this->response_buffer->get();
}
+size_t ACIOEmu::bytes_available() {
+ return this->response_buffer->size();
+}
+
void ACIOEmu::msg_parse() {
#ifdef ACIOEMU_LOG
diff --git a/acioemu/acioemu.h b/acioemu/acioemu.h
index b9f597c..57328e0 100644
--- a/acioemu/acioemu.h
+++ b/acioemu/acioemu.h
@@ -28,5 +28,6 @@ namespace acioemu {
void write(uint8_t byte);
std::optional read();
+ size_t bytes_available();
};
}
diff --git a/acioemu/handle.cpp b/acioemu/handle.cpp
index c8d8841..4ebe3ea 100644
--- a/acioemu/handle.cpp
+++ b/acioemu/handle.cpp
@@ -4,8 +4,9 @@
#include "rawinput/rawinput.h"
#include "util/utils.h"
-acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
+acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount) {
this->com_port = lpCOMPort;
+ this->icca_node_count = iccaNodeCount;
}
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
@@ -16,7 +17,7 @@ bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
// 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;
}
@@ -62,6 +63,10 @@ int acioemu::ACIOHandle::device_io(
return -1;
}
+size_t acioemu::ACIOHandle::bytes_available() {
+ return acio_emu.bytes_available();
+}
+
bool acioemu::ACIOHandle::close() {
log_info("acioemu", "Closed {} (ACIO)", ws2s(com_port));
diff --git a/acioemu/handle.h b/acioemu/handle.h
index a1fd9cc..5caa3d7 100644
--- a/acioemu/handle.h
+++ b/acioemu/handle.h
@@ -9,10 +9,12 @@ namespace acioemu {
private:
LPCWSTR com_port;
+ uint8_t icca_node_count;
+
acioemu::ACIOEmu acio_emu;
public:
- ACIOHandle(LPCWSTR lpCOMPort);
+ ACIOHandle(LPCWSTR lpCOMPort, uint8_t iccaNodeCount = 2);
bool open(LPCWSTR lpFileName) override;
@@ -23,6 +25,8 @@ namespace acioemu {
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
DWORD nOutBufferSize) override;
+ size_t bytes_available() override;
+
bool close() override;
};
}
diff --git a/acioemu/icca.cpp b/acioemu/icca.cpp
index 2fbc36d..2733aa7 100644
--- a/acioemu/icca.cpp
+++ b/acioemu/icca.cpp
@@ -8,6 +8,10 @@
using namespace acioemu;
+namespace acioemu {
+ bool ICCA_DEVICE_HACK = false;
+}
+
ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count) {
// init defaults
@@ -98,11 +102,13 @@ bool ICCADevice::parse_msg(MessageData *msg_in,
// send version data
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
if (
- avs::game::is_model({"LDJ", "TBS"}) ||
+ avs::game::is_model({"LDJ", "TBS", "UJK"}) ||
// SDVX Valkyrie cabinet mode
(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");
+ } else if (avs::game::is_model({"VFG"})) {
+ this->set_version(msg, 0x3, 0, 1, 7, 0, "ICCB");
} else {
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_CLEAR:
+ case 0x30: // GetBoardProductNumber
+ case 0x31: // GetMicomInfo
+ case 0x3A: // ???
case 0x0116: // ???
case 0x0120: // ???
case 0xFF: // BROADCAST
@@ -385,19 +394,48 @@ void ICCADevice::update_card(int unit) {
static int KEYPAD_EAMUSE_MAPPING[] = {
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[]{
- 0x100,
- 0x200,
- 0x2000,
- 2,
- 0x400,
- 0x4000,
- 4,
- 0x800,
- 0x8000,
- 8,
- 1,
- 0x1000
+ 0x100, // 0
+ 0x200, // 1
+ 0x2000, // 2
+ 2, // 3
+ 0x400, // 4
+ 0x4000, // 5
+ 4, // 6
+ 0x800, // 7
+ 0x8000, // 8
+ 8, // 9
+ 1, // .
+ 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[]{
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
if (eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])) {
- this->keypad[unit] |= KEYPAD_KEY_CODES[i];
+
+ if (ICCA_DEVICE_HACK) {
+ this->keypad[unit] |= KEYPAD_KEY_CODES_ALT[i];
+ } else {
+ this->keypad[unit] |= KEYPAD_KEY_CODES[i];
+ }
+
if (!this->keypad_last[unit][i] && update_edge) {
this->keydown[unit] = (this->keypad_capture[unit] << 4) | KEYPAD_KEY_CODE_NUMS[n];
this->keypad_last[unit][i] = true;
diff --git a/acioemu/icca.h b/acioemu/icca.h
index f7d3aea..2ca83be 100644
--- a/acioemu/icca.h
+++ b/acioemu/icca.h
@@ -11,6 +11,8 @@
#include "reader/crypt.h"
namespace acioemu {
+ extern bool ICCA_DEVICE_HACK;
+
class ICCADevice : public ACIODeviceEmu {
private:
bool type_new;
diff --git a/api/controller.cpp b/api/controller.cpp
index 300923a..1c08e38 100644
--- a/api/controller.cpp
+++ b/api/controller.cpp
@@ -18,6 +18,7 @@
#include "modules/capture.h"
#include "modules/coin.h"
#include "modules/control.h"
+#include "modules/ddr.h"
#include "modules/drs.h"
#include "modules/iidx.h"
#include "modules/info.h"
@@ -26,6 +27,7 @@
#include "modules/lights.h"
#include "modules/memory.h"
#include "modules/touch.h"
+#include "modules/resize.h"
#include "request.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::Coin());
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::IIDX());
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::Memory());
state->modules.push_back(new modules::Touch());
+ state->modules.push_back(new modules::Resize());
}
void Controller::free_state(api::ClientState *state) {
diff --git a/api/modules/control.cpp b/api/modules/control.cpp
index b5e1326..346c5f8 100644
--- a/api/modules/control.cpp
+++ b/api/modules/control.cpp
@@ -27,34 +27,6 @@ namespace api::modules {
{ 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) {
functions["raise"] = std::bind(&Control::raise, 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");
// exit windows
- if (!ExitWindowsEx(EWX_POWEROFF | EWX_FORCE,
+ if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN | EWX_FORCE,
SHTDN_REASON_MAJOR_APPLICATION |
SHTDN_REASON_MINOR_MAINTENANCE))
return error(res, "Unable to shutdown system");
diff --git a/api/modules/ddr.cpp b/api/modules/ddr.cpp
new file mode 100644
index 0000000..2c3f3fe
--- /dev/null
+++ b/api/modules/ddr.cpp
@@ -0,0 +1,54 @@
+#include "ddr.h"
+#include
+#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);
+ }
+}
diff --git a/api/modules/ddr.h b/api/modules/ddr.h
new file mode 100644
index 0000000..95ed300
--- /dev/null
+++ b/api/modules/ddr.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+#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);
+ };
+}
diff --git a/api/modules/lights.cpp b/api/modules/lights.cpp
index b09c680..749b0af 100644
--- a/api/modules/lights.cpp
+++ b/api/modules/lights.cpp
@@ -1,5 +1,7 @@
#include "lights.h"
#include
+#include
+
#include "external/rapidjson/document.h"
#include "misc/eamuse.h"
#include "cfg/light.h"
@@ -17,11 +19,16 @@ namespace api::modules {
functions["read"] = std::bind(&Lights::read, this, _1, _2);
functions["write"] = std::bind(&Lights::write, 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(name: str, ...)
*/
void Lights::read(api::Request &req, Response &res) {
@@ -30,17 +37,39 @@ namespace api::modules {
return;
}
- // add state for each light
- for (auto &light : *this->lights) {
- Value state(kArrayType);
- Value light_name(light.getName().c_str(), res.doc()->GetAllocator());
- Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
- Value light_enabled(light.override_enabled);
- state.PushBack(light_name, res.doc()->GetAllocator());
- state.PushBack(light_state, res.doc()->GetAllocator());
- state.PushBack(light_enabled, res.doc()->GetAllocator());
- res.add_data(state);
+ // all lights for this game
+ if (req.params.Size() == 0) {
+ // add state for each light
+ for (auto &light : *this->lights) {
+ get_light(light, res);
+ }
+
+ return;
}
+
+ // specified light names
+ for (Value ¶m : 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 light_name(light.getName().c_str(), res.doc()->GetAllocator());
+ Value light_state(GameAPI::Lights::readLight(RI_MGR, light));
+ Value light_enabled(light.override_enabled);
+ state.PushBack(light_name, res.doc()->GetAllocator());
+ state.PushBack(light_state, res.doc()->GetAllocator());
+ state.PushBack(light_enabled, res.doc()->GetAllocator());
+ res.add_data(state);
}
/**
@@ -88,6 +117,7 @@ namespace api::modules {
/**
* write_reset()
+ * write_reset(name: str, ...)
* write_reset([name: str], ...)
*/
void Lights::write_reset(Request &req, Response &res) {
@@ -104,7 +134,12 @@ namespace api::modules {
if (params.Size() == 0) {
if (lights != nullptr) {
for (auto &light : *this->lights) {
- light.override_enabled = false;
+ if (light.override_enabled) {
+ if (cfg::CONFIGURATOR_STANDALONE) {
+ GameAPI::Lights::writeLight(RI_MGR, light, light.last_state);
+ }
+ light.override_enabled = false;
+ }
}
}
return;
@@ -112,26 +147,28 @@ namespace api::modules {
// loop parameters
for (Value ¶m : req.params.GetArray()) {
+ const char* light_name = nullptr;
// check params
- if (!param.IsArray()) {
- error(res, "parameters must be arrays");
- return;
+ if (param.IsArray()) {
+ if (param.Size() < 1) {
+ error_params_insufficient(res);
+ continue;
+ }
+ if (!param[0].IsString()) {
+ error_type(res, "name", "string");
+ continue;
+ }
+ // get params
+ light_name = param[0].GetString();
+ } else if (param.IsString()) {
+ light_name = param.GetString();
+ } else {
+ error(res, "parameters must be arrays or strings");
}
- if (param.Size() < 1) {
- error_params_insufficient(res);
- continue;
- }
- if (!param[0].IsString()) {
- error_type(res, "name", "string");
- continue;
- }
-
- // get params
- auto light_name = param[0].GetString();
// 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);
continue;
}
@@ -146,16 +183,20 @@ namespace api::modules {
}
// find light
- for (auto &light : *this->lights) {
- if (light.getName() == name) {
- light.override_state = CLAMP(state, 0.f, 1.f);
- light.override_enabled = true;
- return true;
- }
- }
+ if (this->lights_by_names.contains(name)) {
+ auto &light = this->lights_by_names.at(name).get();
+ light.override_state = CLAMP(state, 0.f, 1.f);
+ light.override_enabled = true;
- // unknown light
- return false;
+ if (cfg::CONFIGURATOR_STANDALONE) {
+ GameAPI::Lights::writeLight(RI_MGR, light, state);
+ }
+
+ return true;
+ } else {
+ // unknown light
+ return false;
+ }
}
bool Lights::write_light_reset(std::string name) {
@@ -166,14 +207,13 @@ namespace api::modules {
}
// find light
- for (auto &light : *this->lights) {
- if (light.getName() == name) {
- light.override_enabled = false;
- return true;
- }
+ if (this->lights_by_names.contains(name)) {
+ auto &light = this->lights_by_names.at(name).get();
+ light.override_enabled = false;
+ return true;
+ } else {
+ // unknown light
+ return false;
}
-
- // unknown light
- return false;
}
}
diff --git a/api/modules/lights.h b/api/modules/lights.h
index b174210..b020f35 100644
--- a/api/modules/lights.h
+++ b/api/modules/lights.h
@@ -1,6 +1,8 @@
#pragma once
#include
+#include
+
#include "api/module.h"
#include "api/request.h"
#include "cfg/api.h"
@@ -15,6 +17,7 @@ namespace api::modules {
// state
std::vector *lights;
+ robin_hood::unordered_map> lights_by_names;
// function definitions
void read(Request &req, Response &res);
@@ -22,6 +25,7 @@ namespace api::modules {
void write_reset(Request &req, Response &res);
// helper
+ void get_light(Light &light, Response &res);
bool write_light(std::string name, float state);
bool write_light_reset(std::string name);
};
diff --git a/api/modules/resize.cpp b/api/modules/resize.cpp
new file mode 100644
index 0000000..84e70e5
--- /dev/null
+++ b/api/modules/resize.cpp
@@ -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 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;
+ }
+ }
+}
diff --git a/api/modules/resize.h b/api/modules/resize.h
new file mode 100644
index 0000000..ca6f1da
--- /dev/null
+++ b/api/modules/resize.h
@@ -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);
+ };
+}
diff --git a/api/modules/touch.cpp b/api/modules/touch.cpp
index 7358311..ca7eba2 100644
--- a/api/modules/touch.cpp
+++ b/api/modules/touch.cpp
@@ -4,6 +4,7 @@
#include "external/rapidjson/document.h"
#include "avs/game.h"
+#include "hooks/graphics/graphics.h"
#include "misc/eamuse.h"
#include "launcher/launcher.h"
#include "touch/touch.h"
@@ -18,7 +19,12 @@ namespace api::modules {
Touch::Touch() : Module("touch") {
is_sdvx = avs::game::is_model("KFC");
+
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["write"] = std::bind(&Touch::write, this, _1, _2);
diff --git a/api/resources/lua/README.md b/api/resources/lua/README.md
deleted file mode 100644
index d88ca17..0000000
--- a/api/resources/lua/README.md
+++ /dev/null
@@ -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.
diff --git a/api/resources/lua/boot_iidx_tt_lights.lua b/api/resources/lua/boot_iidx_tt_lights.lua
deleted file mode 100644
index aeaa2e7..0000000
--- a/api/resources/lua/boot_iidx_tt_lights.lua
+++ /dev/null
@@ -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
diff --git a/api/resources/lua/config_example.lua b/api/resources/lua/config_example.lua
deleted file mode 100644
index ae09b55..0000000
--- a/api/resources/lua/config_example.lua
+++ /dev/null
@@ -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
diff --git a/api/resources/python/spiceapi/__init__.py b/api/resources/python/spiceapi/__init__.py
index 5cd290a..b7fe465 100644
--- a/api/resources/python/spiceapi/__init__.py
+++ b/api/resources/python/spiceapi/__init__.py
@@ -12,3 +12,4 @@ from .keypads import *
from .lights import *
from .memory import *
from .touch import *
+from .resize import *
\ No newline at end of file
diff --git a/api/resources/python/spiceapi/lights.py b/api/resources/python/spiceapi/lights.py
index 038373d..dba33f3 100644
--- a/api/resources/python/spiceapi/lights.py
+++ b/api/resources/python/spiceapi/lights.py
@@ -2,8 +2,14 @@ from .connection import Connection
from .request import Request
-def lights_read(con: Connection):
- res = con.request(Request("lights", "read"))
+def lights_read(con: Connection, light_names=None):
+ 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()
diff --git a/api/resources/python/spiceapi/resize.py b/api/resources/python/spiceapi/resize.py
new file mode 100644
index 0000000..92f585e
--- /dev/null
+++ b/api/resources/python/spiceapi/resize.py
@@ -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)
diff --git a/api/resources/python/spiceremote.py b/api/resources/python/spiceremote.py
index 3db10f9..52e37f1 100644
--- a/api/resources/python/spiceremote.py
+++ b/api/resources/python/spiceremote.py
@@ -179,7 +179,7 @@ class ControlTab(ttk.Frame):
self.card_lbl = ttk.Label(self.card, text="Card")
self.card_lbl.grid(row=0, columnspan=2)
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_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)
@@ -397,6 +397,65 @@ class LightsTab(ttk.Frame):
# set text
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):
"""The main application frame."""
@@ -419,6 +478,8 @@ class MainApp(ttk.Frame):
self.tabs.add(self.tab_analogs, text="Analogs")
self.tab_lights = LightsTab(self, self.tabs)
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.tabs.add(self.tab_manual, text="Manual")
self.tabs.pack(expand=True, fill=tk.BOTH)
diff --git a/avs/automap.cpp b/avs/automap.cpp
index 54fdba4..8f2fe43 100644
--- a/avs/automap.cpp
+++ b/avs/automap.cpp
@@ -471,7 +471,7 @@ namespace avs::automap {
ENABLED = true;
// 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");
}
diff --git a/avs/core.cpp b/avs/core.cpp
index c31de64..32d000b 100644
--- a/avs/core.cpp
+++ b/avs/core.cpp
@@ -334,8 +334,12 @@ namespace avs {
.avs_fs_close = "XC0bbe97000119",
.avs_fs_dump_mountpoint = "XC0bbe970000c5",
.avs_fs_mount = "XC0bbe97000099",
+ .avs_fs_fstat = "XC0bbe970000ce",
.avs_fs_lstat = "XC0bbe9700005f",
+ .avs_fs_read = "XC0bbe97000134",
.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_del_protocol = "XC0bbe97000026",
.avs_net_addrinfobyaddr = "XC0bbe970000a4",
@@ -1360,9 +1364,11 @@ namespace avs {
fclose(file);
// error checking
- auto err = avs::core::property_get_error(property);
- if (err > 0) {
- log_fatal("avs-core", "failed to read config file ({}): {}", filename, error_str(err));
+ if (avs::core::property_get_error) {
+ auto err = avs::core::property_get_error(property);
+ if (err > 0) {
+ log_fatal("avs-core", "failed to read config file ({}): {}", filename, error_str(err));
+ }
}
// return value
@@ -1467,7 +1473,7 @@ namespace avs {
}
}
- bool load_dll() {
+ void load_dll() {
log_info("avs-core", "loading DLL");
// detect DLL name
@@ -1479,12 +1485,30 @@ namespace avs {
#else
DLL_NAME = "libavs-win32.dll";
#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
- DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME, false);
+ DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME);
if (!DLL_INSTANCE) {
- return false;
+ return;
}
// check by version string if obtained
@@ -1709,7 +1733,7 @@ namespace avs {
}
// success
- return true;
+ return;
}
static void create_dir(
diff --git a/avs/core.h b/avs/core.h
index 2f7adf2..50644fa 100644
--- a/avs/core.h
+++ b/avs/core.h
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
#include
#include
@@ -448,7 +449,7 @@ namespace avs {
// functions
void set_default_heap_size(const std::string &dll_name);
void create_log();
- bool load_dll();
+ void load_dll();
void boot();
void copy_defaults();
void shutdown();
diff --git a/avs/ea3.cpp b/avs/ea3.cpp
index cf485ce..58598b4 100644
--- a/avs/ea3.cpp
+++ b/avs/ea3.cpp
@@ -133,6 +133,24 @@ namespace avs {
#else
DLL_NAME = "libavs-win32-ea3.dll";
#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
diff --git a/avs/game.cpp b/avs/game.cpp
index e9c3796..ca5c1a2 100644
--- a/avs/game.cpp
+++ b/avs/game.cpp
@@ -72,8 +72,35 @@ namespace avs {
log_info("avs-game", "loading DLL '{}'", DLL_NAME);
// load game instance
- if (fileutils::verify_header_pe(MODULE_PATH / DLL_NAME)) {
- DLL_INSTANCE = libutils::load_library(MODULE_PATH / DLL_NAME);
+ const auto dll_path = 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
diff --git a/build_all.sh b/build_all.sh
old mode 100644
new mode 100755
index b1de981..713512a
--- a/build_all.sh
+++ b/build_all.sh
@@ -19,6 +19,25 @@ function trap_error_exit {
trap trap_error_dbg DEBUG
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
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null || echo "none")
GIT_HEAD=$(git rev-parse HEAD || echo "none")
@@ -41,8 +60,8 @@ DIST_ENABLE=1
DIST_FOLDER="./dist"
DIST_NAME="spice2x-$(date +%y)-$(date +%m)-$(date +%d).zip"
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_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_spice64"
+TARGETS_32="spicetools_stubs_kbt spicetools_stubs_kld spicetools_cfg spicetools_spice spicetools_spice_laa"
+TARGETS_64="spicetools_stubs_kbt64 spicetools_stubs_kld64 spicetools_stubs_nvEncodeAPI64 spicetools_stubs_nvcuvid spicetools_stubs_nvcuda spicetools_spice64"
# determine build type
BUILD_TYPE="Release"
@@ -57,7 +76,7 @@ then
fi
# determine number of cores
-CORES=$(awk '/^processor\t/ {cores[$NF]++} END {print length(cores)}' /proc/cpuinfo)
+CORES=$(nproc)
# print information
echo ""
@@ -74,34 +93,45 @@ echo "Build Type: $BUILD_TYPE"
echo "Cores: $CORES"
echo ""
-# 32 bit
-echo "Building 32bit targets..."
-echo "========================="
-if ((CLEAN_BUILD > 0))
+if ((IGNORE_CACHE > 0))
then
- rm -rf ${BUILDDIR_32}
+ echo "Ignoring build cache..."
+else
+ export CCACHE_DIR="$(pwd)/.ccache"
+ export CMAKE_CXX_COMPILER_LAUNCHER=ccache
+ export CMAKE_C_COMPILER_LAUNCHER=ccache
fi
-mkdir -p ${BUILDDIR_32}
-pushd ${BUILDDIR_32} > /dev/null
-cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_32}
-popd > /dev/null
-# 64 bit
-echo ""
-echo "Building 64bit targets..."
-echo "========================="
-if ((CLEAN_BUILD > 0))
-then
- rm -rf ${BUILDDIR_64}
-fi
-mkdir -p ${BUILDDIR_64}
-pushd ${BUILDDIR_64} > /dev/null
-cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && make -j ${CORES} ${TARGETS_64}
-popd > /dev/null
+time (
+ # 32 bit
+ echo "Building 32bit targets..."
+ echo "========================="
+ if ((CLEAN_BUILD > 0))
+ then
+ rm -rf ${BUILDDIR_32}
+ fi
+ mkdir -p ${BUILDDIR_32}
+ pushd ${BUILDDIR_32} > /dev/null
+ cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_32} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_32}
+ popd > /dev/null
-echo ""
-echo "Compilation process done :)"
-echo "==========================="
+ # 64 bit
+ echo ""
+ echo "Building 64bit targets..."
+ echo "========================="
+ if ((CLEAN_BUILD > 0))
+ then
+ rm -rf ${BUILDDIR_64}
+ fi
+ mkdir -p ${BUILDDIR_64}
+ pushd ${BUILDDIR_64} > /dev/null
+ cmake -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_64} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} $OLDPWD && ninja ${TARGETS_64}
+ popd > /dev/null
+
+ echo ""
+ echo "Compilation process done :)"
+ echo "==========================="
+)
# generate PDBs
if false # ((DEBUG > 0))
@@ -152,7 +182,8 @@ echo "Copy files to output directory..."
rm -rf ${OUTDIR}
mkdir -p ${OUTDIR}
#mkdir -p ${OUTDIR}/stubs/32
-#mkdir -p ${OUTDIR}/stubs/64
+mkdir -p ${OUTDIR}/stubs/64
+mkdir -p ${OUTDIR}/extras/largeaddressaware
if false # ((DEBUG > 0))
then
# debug files
@@ -166,28 +197,22 @@ then
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/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
# release files
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_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/kld.dll ${OUTDIR}/stubs/32 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/kld.dll ${OUTDIR}/stubs/64 2>/dev/null
-fi
-
-# compress using UPX
-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
+ 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
fi
# pack source files to output directory
@@ -196,7 +221,7 @@ mkdir -p ${OUTDIR}/src
if ((INCLUDE_SRC > 0))
then
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?"
fi
diff --git a/build_docker.bat b/build_docker.bat
index 4d0d2a3..7072a75 100644
--- a/build_docker.bat
+++ b/build_docker.bat
@@ -1,6 +1,6 @@
docker build --pull external/docker -t spicetools/deps
-docker build . -t spicetools/spice --no-cache
-docker run --rm -it -v %cd%/dist:/src/dist -v %cd%/bin:/src/bin spicetools/spice
+docker build --build-context gitroot=%cd%/../../.git . -t 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 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
diff --git a/build_docker.sh b/build_docker.sh
old mode 100644
new mode 100755
index 6d63f7e..f47ca69
--- a/build_docker.sh
+++ b/build_docker.sh
@@ -1,6 +1,4 @@
#!/bin/bash
-export DOCKER_BUILDKIT=0
-
docker build --pull $PWD/external/docker -t spicetools/deps --platform linux/x86_64
-docker build . -t spicetools/spice:latest --no-cache
-docker run --rm -it -v $PWD/dist:/src/dist -v $PWD/bin:/src/bin spicetools/spice
+docker build --build-context gitroot=$PWD/../../.git . -t spicetools/spice:latest
+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 "$@"
diff --git a/build_msvc.py b/build_msvc.py
deleted file mode 100644
index e09c831..0000000
--- a/build_msvc.py
+++ /dev/null
@@ -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()
diff --git a/build_release.bat b/build_release.bat
new file mode 100644
index 0000000..111e908
--- /dev/null
+++ b/build_release.bat
@@ -0,0 +1,2 @@
+del /s /q .ccache
+call build_docker.bat
\ No newline at end of file
diff --git a/cfg/analog.cpp b/cfg/analog.cpp
index 0aee08f..44abb41 100644
--- a/cfg/analog.cpp
+++ b/cfg/analog.cpp
@@ -55,20 +55,39 @@ std::string Analog::getDisplayString(rawinput::RawInputManager *manager) {
}
case rawinput::MIDI: {
auto midi = device->midiInfo;
+ // update strings in button.cpp as well
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()) {
- 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()
+ midi->controls_onoff.size())
{
- return "MIDI ONOFF " + indexString + " (" + device->desc + ")";
- } else if (index == midi->controls_precision.size() + midi->controls_single.size()
- + midi->controls_onoff.size())
+ const int index_rel = index - midi->controls_precision.size() - midi->controls_single.size();
+ const int channel = (index_rel / 6) + 1;
+ 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 {
- return "MIDI Unknown " + indexString + " (" + device->desc + ")";
+ return "MIDI Unknown Index " + indexString + " (" + device->desc + ")";
}
}
case rawinput::DESTROYED:
@@ -158,3 +177,98 @@ float Analog::normalizeAngle(float rads) {
}
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;
+}
\ No newline at end of file
diff --git a/cfg/analog.h b/cfg/analog.h
index 2a04f3f..16c8f80 100644
--- a/cfg/analog.h
+++ b/cfg/analog.h
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
#define ANALOG_HISTORY_CNT 10
#define M_TAU (2 * M_PI)
@@ -22,7 +23,7 @@ class Analog {
private:
std::string name;
std::string device_identifier = "";
- unsigned short index = 0xFF;
+ unsigned short index = USHRT_MAX;
float sensitivity = 1.f;
float deadzone = 0.f;
bool deadzone_mirror = false;
@@ -30,7 +31,7 @@ private:
float last_state = 0.5f;
bool sensitivity_set = false;
bool deadzone_set = false;
-
+
// smoothing function
bool smoothing = false;
std::array vector_history;
@@ -41,8 +42,22 @@ private:
float previous_raw_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 delay_buffer;
+
float calculateAngularDifference(float old_rads, float new_rads);
float normalizeAngle(float rads);
+ float normalizeAnalogValue(float value);
public:
@@ -57,6 +72,8 @@ public:
std::string getDisplayString(rawinput::RawInputManager* manager);
float getSmoothedValue(float raw_rads);
float applyAngularSensitivity(float raw_rads);
+ float applyMultiplier(float raw_value);
+ float applyDeadzone(float raw_value);
inline bool isSet() {
if (this->override_enabled) {
@@ -72,6 +89,9 @@ public:
setDeadzone(0.f);
invert = false;
smoothing = false;
+ setMultiplier(1);
+ setRelativeMode(false);
+ setDelayBufferDepth(0);
}
inline const std::string &getName() const {
@@ -144,6 +164,16 @@ public:
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 {
return this->last_state;
}
@@ -151,4 +181,31 @@ public:
inline void setLastState(float 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 &getDelayBuffer() {
+ return this->delay_buffer;
+ }
};
diff --git a/cfg/api.cpp b/cfg/api.cpp
index 357b4a4..ace41c4 100644
--- a/cfg/api.cpp
+++ b/cfg/api.cpp
@@ -17,6 +17,8 @@ std::vector