Initial re-upload of spice2x-24-08-24
This commit is contained in:
commit
caa9e02285
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
bin/**
|
||||
dist/**
|
||||
docker/**
|
||||
cmake-build*
|
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
.vscode/*
|
||||
|
||||
# JetBrains IDEs
|
||||
.idea
|
||||
.idea_modules
|
||||
*.iws
|
||||
|
||||
# CMake
|
||||
cmake-build*
|
||||
|
||||
# user config
|
||||
build_all.local.sh
|
||||
build_docker.local.sh
|
||||
|
||||
# output from build script
|
||||
bin/*
|
||||
dist/*
|
||||
|
||||
external/cv2pdb/*
|
768
CMakeLists.txt
Normal file
768
CMakeLists.txt
Normal file
@ -0,0 +1,768 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
project(spicetools)
|
||||
include(CheckIPOSupported)
|
||||
|
||||
# set language level
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||
|
||||
# niceities for vscode
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||
|
||||
# for RapidJSON
|
||||
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")
|
||||
|
||||
# disable warnings about using non _s variants like strncpy
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# disable warnings about using deprecated winsock2 functions
|
||||
add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
# RapidJSON does this
|
||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
|
||||
# define M_PI
|
||||
add_compile_definitions(_USE_MATH_DEFINES)
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DYNAMICBASE:NO")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DYNAMICBASE:NO")
|
||||
|
||||
set(USE_STATIC_MSVCRT ON CACHE BOOL "If enabled, will force the use of static crt instead of dynamic")
|
||||
|
||||
if(USE_STATIC_MSVCRT)
|
||||
# use statically linked runtime
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>: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("$<$<CONFIG:DEBUG>:/ZI>")
|
||||
add_link_options("$<$<CONFIG:DEBUG>:/SAFESEH:NO>")
|
||||
|
||||
# enable fast pdb generation in debug
|
||||
add_link_options("$<$<CONFIG:DEBUG>:/DEBUG:FASTLINK>")
|
||||
|
||||
# enable pdb generation for release builds
|
||||
add_compile_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/Zi>")
|
||||
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/DEBUG:FULL>")
|
||||
|
||||
# enable COMDAT folding for even smaller release builds
|
||||
add_link_options("$<$<CONFIG:RELEASE,MINSIZEREL>:/OPT:ICF>")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
# disable warnings about using non _s variants like strncpy
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# disable warnings about using deprecated winsock2 functions
|
||||
add_compile_definitions(_WINSOCK_DEPRECATED_NO_WARNINGS)
|
||||
|
||||
# RapidJSON does this
|
||||
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunknown-warning-option")
|
||||
|
||||
# static linking
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static")
|
||||
else()
|
||||
|
||||
# warnings
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # enable stuff
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith") # but we love pointer arithmetic :)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # since CLion does clang pragmas
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-address") # to allow checking function pointers for null
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-cast-function-type") # we actually do this a lot
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-class-memaccess") # RapidJSON does this
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") # RapidJSON issue
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") # for all those stubs
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter") # for all those stubs
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-stringop-truncation") # since we do that from time to time
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs") # for our logging system
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") # fmtlib workaround
|
||||
|
||||
# release flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Release")
|
||||
|
||||
# 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")
|
||||
|
||||
# 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")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
|
||||
# remove symbol table and relocation information
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
|
||||
|
||||
# performance
|
||||
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")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-omit-frame-pointer")
|
||||
|
||||
# no debug
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
|
||||
|
||||
# file prefix map for relative working directory
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||
endif()
|
||||
|
||||
# release with debug info flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
|
||||
|
||||
# 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")
|
||||
|
||||
# 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")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fvisibility=hidden -fvisibility-inlines-hidden")
|
||||
|
||||
# generate dwarf
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -gdwarf")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -gdwarf")
|
||||
|
||||
# ensure frame pointers are enabled
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
|
||||
# debug flags
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
|
||||
# generate dwarf
|
||||
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")
|
||||
|
||||
# optimize for debugging
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og -pipe")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -pipe")
|
||||
endif()
|
||||
|
||||
# static linking
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -static-libgcc")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -static-libgcc -static-libstdc++")
|
||||
endif()
|
||||
|
||||
# default defines
|
||||
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})
|
||||
|
||||
# add external libraries
|
||||
add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/discord-rpc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(external/hash-library EXCLUDE_FROM_ALL)
|
||||
add_compile_definitions(IMGUI_DISABLE_DEMO_WINDOWS)
|
||||
add_compile_definitions(IMGUI_DISABLE_DEBUG_TOOLS)
|
||||
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)
|
||||
add_subdirectory(external/stepmaniax-sdk 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()
|
||||
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
|
||||
###########
|
||||
set_source_files_properties(build/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/manifest.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/icon.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(cfg/Win32D.rc PROPERTIES LANGUAGE RC)
|
||||
set_source_files_properties(build/manifest64.rc PROPERTIES LANGUAGE RC)
|
||||
|
||||
# sources
|
||||
#########
|
||||
|
||||
set(SOURCE_FILES ${SOURCE_FILES}
|
||||
|
||||
# acio
|
||||
acio/acio.cpp
|
||||
acio/module.cpp
|
||||
acio/pix/pix.cpp
|
||||
acio/core/core.cpp
|
||||
acio/hgth/hgth.cpp
|
||||
acio/bmpu/bmpu.cpp
|
||||
acio/hbhi/hbhi.cpp
|
||||
acio/hdxs/hdxs.cpp
|
||||
acio/kfca/kfca.cpp
|
||||
acio/i36g/i36g.cpp
|
||||
acio/panb/panb.cpp
|
||||
acio/icca/icca.cpp
|
||||
acio/j32d/j32d.cpp
|
||||
acio/bi2a/bi2a.cpp
|
||||
acio/klpa/klpa.cpp
|
||||
acio/mdxf/mdxf.cpp
|
||||
acio/pjei/pjei.cpp
|
||||
acio/pjec/pjec.cpp
|
||||
acio/i36i/i36i.cpp
|
||||
acio/nddb/nddb.cpp
|
||||
acio/la9a/la9a.cpp
|
||||
|
||||
# acioemu
|
||||
acioemu/acioemu.cpp
|
||||
acioemu/device.cpp
|
||||
acioemu/handle.cpp
|
||||
acioemu/icca.cpp
|
||||
|
||||
# acio2emu
|
||||
acio2emu/handle.cpp
|
||||
acio2emu/packet.cpp
|
||||
acio2emu/firmware/bi2x.cpp
|
||||
|
||||
# api
|
||||
api/controller.cpp
|
||||
api/websocket.cpp
|
||||
api/request.cpp
|
||||
api/response.cpp
|
||||
api/module.cpp
|
||||
api/modules/card.cpp
|
||||
api/modules/buttons.cpp
|
||||
api/modules/capture.cpp
|
||||
api/modules/analogs.cpp
|
||||
api/modules/lights.cpp
|
||||
api/modules/memory.cpp
|
||||
api/modules/coin.cpp
|
||||
api/modules/info.cpp
|
||||
api/modules/keypads.cpp
|
||||
api/modules/control.cpp
|
||||
api/modules/touch.cpp
|
||||
api/modules/iidx.cpp
|
||||
api/serial.cpp
|
||||
api/modules/drs.cpp
|
||||
api/modules/lcd.cpp
|
||||
|
||||
# avs
|
||||
avs/core.cpp
|
||||
avs/ea3.cpp
|
||||
avs/game.cpp
|
||||
avs/automap.cpp
|
||||
avs/ssl.cpp
|
||||
|
||||
# build
|
||||
build/defs.cpp
|
||||
|
||||
# cfg
|
||||
cfg/spicecfg.cpp
|
||||
cfg/analog.cpp
|
||||
cfg/game.cpp
|
||||
cfg/button.cpp
|
||||
cfg/config.cpp
|
||||
cfg/api.cpp
|
||||
cfg/option.cpp
|
||||
cfg/light.cpp
|
||||
cfg/configurator.cpp
|
||||
cfg/configurator_wnd.cpp
|
||||
cfg/screen_resize.cpp
|
||||
|
||||
# easrv
|
||||
easrv/easrv.cpp
|
||||
easrv/smartea.cpp
|
||||
|
||||
# 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
|
||||
external/cardio/cardio_runner.cpp
|
||||
|
||||
# external misc
|
||||
external/scard/scard.cpp
|
||||
external/stackwalker/stackwalker.cpp
|
||||
external/tinyxml2/tinyxml2.cpp
|
||||
external/http-parser/http_parser.c
|
||||
external/usbhidusage/usb-hid-usage.c
|
||||
external/toojpeg/toojpeg.cpp
|
||||
|
||||
# games
|
||||
games/game.cpp
|
||||
games/io.cpp
|
||||
games/shared/lcdhandle.cpp
|
||||
games/shared/printer.cpp
|
||||
games/shared/twtouch.cpp
|
||||
games/popn/popn.cpp
|
||||
games/popn/io.cpp
|
||||
games/bbc/bbc.cpp
|
||||
games/bbc/io.cpp
|
||||
games/hpm/hpm.cpp
|
||||
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/sdvx/bi2x_hook.cpp
|
||||
games/sdvx/sdvx.cpp
|
||||
games/sdvx/io.cpp
|
||||
games/sdvx/camera.cpp
|
||||
games/jb/jb.cpp
|
||||
games/jb/io.cpp
|
||||
games/nost/nost.cpp
|
||||
games/nost/io.cpp
|
||||
games/gitadora/gitadora.cpp
|
||||
games/gitadora/io.cpp
|
||||
games/mga/mga.cpp
|
||||
games/mga/io.cpp
|
||||
games/mga/gunio.cpp
|
||||
games/sc/sc.cpp
|
||||
games/sc/io.cpp
|
||||
games/rb/rb.cpp
|
||||
games/rb/io.cpp
|
||||
games/rb/touch.cpp
|
||||
games/bs/bs.cpp
|
||||
games/bs/io.cpp
|
||||
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
|
||||
games/qma/io.cpp games/qma/ezusb.cpp
|
||||
games/ddr/ddr.cpp
|
||||
games/ddr/io.cpp
|
||||
games/ddr/p3io/foot.cpp
|
||||
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
|
||||
games/ftt/io.cpp
|
||||
games/loveplus/loveplus.cpp
|
||||
games/loveplus/io.cpp
|
||||
games/scotto/scotto.cpp
|
||||
games/scotto/io.cpp
|
||||
games/drs/drs.cpp
|
||||
games/drs/io.cpp
|
||||
games/we/we.cpp
|
||||
games/we/io.cpp
|
||||
games/we/touchpanel.cpp
|
||||
games/shogikai/shogikai.cpp
|
||||
games/shogikai/io.cpp
|
||||
games/otoca/otoca.cpp
|
||||
games/otoca/io.cpp
|
||||
games/otoca/p4io.cpp
|
||||
games/silentscope/silentscope.cpp
|
||||
games/silentscope/io.cpp
|
||||
games/pcm/pcm.cpp
|
||||
games/pcm/io.cpp
|
||||
games/onpara/onpara.cpp
|
||||
games/onpara/io.cpp
|
||||
games/onpara/westboard.cpp
|
||||
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
|
||||
|
||||
# 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
|
||||
hooks/audio/backends/wasapi/audio_render_client.cpp
|
||||
hooks/audio/backends/wasapi/dummy_audio_client.cpp
|
||||
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
|
||||
hooks/avshook.cpp
|
||||
hooks/cfgmgr32hook.cpp
|
||||
hooks/debughook.cpp
|
||||
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
|
||||
hooks/graphics/backends/d3d9/d3d9_swapchain.cpp
|
||||
hooks/graphics/backends/d3d9/d3d9_texture.cpp
|
||||
hooks/input/dinput8/fake_backend.cpp
|
||||
hooks/input/dinput8/fake_device.cpp
|
||||
hooks/input/dinput8/hook.cpp
|
||||
hooks/lang.cpp
|
||||
hooks/libraryhook.cpp
|
||||
hooks/networkhook.cpp
|
||||
hooks/powrprof.cpp
|
||||
#hooks/rom.cpp
|
||||
hooks/setupapihook.cpp
|
||||
hooks/sleephook.cpp
|
||||
hooks/unisintrhook.cpp
|
||||
hooks/winuser.cpp
|
||||
|
||||
# launcher
|
||||
launcher/launcher.cpp
|
||||
launcher/signal.cpp
|
||||
launcher/superexit.cpp
|
||||
launcher/logger.cpp
|
||||
launcher/richpresence.cpp
|
||||
launcher/shutdown.cpp
|
||||
launcher/options.cpp
|
||||
|
||||
# misc
|
||||
misc/bt5api.cpp
|
||||
misc/clipboard.cpp
|
||||
misc/device.cpp
|
||||
misc/eamuse.cpp
|
||||
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/screen_resize.cpp
|
||||
overlay/windows/sdvx_sub.cpp
|
||||
overlay/windows/config.cpp
|
||||
overlay/windows/control.cpp
|
||||
overlay/windows/eadev.cpp
|
||||
overlay/windows/fps.cpp
|
||||
overlay/windows/generic_sub.cpp
|
||||
overlay/windows/iidx_seg.cpp
|
||||
overlay/windows/iidx_sub.cpp
|
||||
overlay/windows/iopanel.cpp
|
||||
overlay/windows/iopanel_ddr.cpp
|
||||
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
|
||||
rawinput/rawinput.cpp
|
||||
rawinput/sextet.cpp
|
||||
rawinput/piuio.cpp
|
||||
rawinput/touch.cpp
|
||||
rawinput/hotplug.cpp
|
||||
rawinput/smxstage.cpp
|
||||
rawinput/smxstage.h
|
||||
|
||||
# reader
|
||||
reader/reader.cpp
|
||||
reader/message.cpp
|
||||
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
|
||||
|
||||
# util
|
||||
util/sigscan.cpp
|
||||
util/detour.cpp
|
||||
util/logging.cpp
|
||||
util/detour.cpp
|
||||
util/peb.cpp
|
||||
util/libutils.cpp
|
||||
util/fileutils.cpp
|
||||
util/resutils.cpp
|
||||
util/utils.cpp
|
||||
util/memutils.cpp
|
||||
util/rc4.cpp
|
||||
util/crypt.cpp
|
||||
util/time.cpp
|
||||
util/cpuutils.cpp
|
||||
util/netutils.cpp
|
||||
util/lz77.cpp
|
||||
util/tapeled.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 winhttp
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi CpuFeatures::cpu_features smx)
|
||||
set_target_properties(spicetools_spice PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice PROPERTIES OUTPUT_NAME "spice")
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
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})
|
||||
target_link_libraries(spicetools_spice64
|
||||
PUBLIC d3d9 ws2_32 version comctl32 shlwapi iphlpapi hid secur32 setupapi psapi winmm winscard winhttp mfplat mf mfreadwrite mfuuid strmiids dxva2
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api64 lua_static imm32 dwmapi CpuFeatures::cpu_features smx)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_spice64 PROPERTIES OUTPUT_NAME "spice64")
|
||||
target_compile_definitions(spicetools_spice64 PRIVATE SPICE64=1)
|
||||
|
||||
IF(NOT MSVC)
|
||||
set_target_properties(spicetools_spice64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
|
||||
# spicecfg.exe
|
||||
##############
|
||||
|
||||
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 winhttp strmiids
|
||||
PRIVATE fmt-header-only discord-rpc imgui hash-library minhook openvr_api lua_static imm32 dwmapi CpuFeatures::cpu_features smx)
|
||||
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)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_cfg PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# stubs
|
||||
#######
|
||||
|
||||
# kbt.dll
|
||||
set(SOURCE_FILES stubs/stubs.cpp)
|
||||
add_library(spicetools_stubs_kbt SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kbt PRIVATE fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES OUTPUT_NAME "kbt")
|
||||
target_compile_definitions(spicetools_stubs_kbt PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kbt PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# kbt.dll 64bit
|
||||
add_library(spicetools_stubs_kbt64 SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kbt64 PRIVATE fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES OUTPUT_NAME "kbt")
|
||||
target_compile_definitions(spicetools_stubs_kbt64 PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kbt64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-m64")
|
||||
endif()
|
||||
|
||||
# kld.dll
|
||||
set(SOURCE_FILES stubs/stubs.cpp)
|
||||
add_library(spicetools_stubs_kld SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kld PRIVATE fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES OUTPUT_NAME "kld")
|
||||
target_compile_definitions(spicetools_stubs_kld PRIVATE STUB=1)
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(spicetools_stubs_kld PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32")
|
||||
endif()
|
||||
|
||||
# kld.dll 64bit
|
||||
add_library(spicetools_stubs_kld64 SHARED ${SOURCE_FILES} stubs/stubs.def)
|
||||
target_link_libraries(spicetools_stubs_kld64 PRIVATE fmt-header-only)
|
||||
set_target_properties(spicetools_stubs_kld64 PROPERTIES PREFIX "")
|
||||
set_target_properties(spicetools_stubs_kld64 PROPERTIES OUTPUT_NAME "kld")
|
||||
target_compile_definitions(spicetools_stubs_kld64 PRIVATE STUB=1)
|
||||
|
||||
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
|
||||
####################
|
||||
|
||||
# output config
|
||||
set_target_properties(spicetools_cfg
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools")
|
||||
|
||||
# output 32bit
|
||||
set_target_properties(spicetools_spice 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 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"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/spicetools/64")
|
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@ -0,0 +1,6 @@
|
||||
FROM spicetools/deps
|
||||
WORKDIR /src
|
||||
RUN chown user:user /src
|
||||
USER user
|
||||
COPY --chown=user:user . /src
|
||||
CMD ./build_all.sh
|
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
261
README.md
Normal file
261
README.md
Normal file
@ -0,0 +1,261 @@
|
||||
SpiceTools
|
||||
==========
|
||||
This is a loader for various arcade games developed by 573.
|
||||
The project is using CMake as it's build system, with a custom build
|
||||
script for making packages ready for distribution and to keep it easy
|
||||
for people not knowing how to use CMake.
|
||||
|
||||
## Building/Distribution
|
||||
|
||||
We're currently using Arch Linux for building the binaries.
|
||||
You'll need:
|
||||
- MinGW-64 packages (can be found in the AUR)
|
||||
- bash
|
||||
- git
|
||||
- zip
|
||||
- upx (optional)
|
||||
|
||||
For any other GNU/Linux distributions (or Windows lol), you're on your
|
||||
own.
|
||||
|
||||
To build the project, run:
|
||||
|
||||
$ ./build_all.sh
|
||||
|
||||
## Build Configuration
|
||||
|
||||
You can tweak some settings at the beginning of the build script. You
|
||||
might want to modify the paths of the toolchains if yours differ. It's
|
||||
also possible to disable UPX compression and source distribution for
|
||||
example.
|
||||
|
||||
If you're not using an IDE and want to run the build script from command
|
||||
line manually each time you make a change, you can set CLEAN_BUILD to 0
|
||||
so it will only compile what's needed. When modifying the resources you
|
||||
should build from scratch, since CMake isn't able to track changes of
|
||||
those files (e.g. changelog.txt, licenses.txt).
|
||||
|
||||
You can put your custom build script under "build_all.local.sh" if you
|
||||
don't want git to track the changes you made to the build settings.
|
||||
|
||||
## Debug Build
|
||||
|
||||
To get proper stackstraces, you need to build with the DEBUG setting
|
||||
enabled. This will set the CMake build type to debug and the script
|
||||
will try to make use of cv2pdb.
|
||||
Check external/cv2pdb/README.md for details on how to set it up.
|
||||
It is required to use cv2pdb to generate PDB output, because at time of
|
||||
writing, MinGW still didn't support Microsoft's proprietary file format.
|
||||
|
||||
## API
|
||||
|
||||
SpiceTools is providing a TCP/JSON based API. You can enable it with the
|
||||
`-api [PORT]` option. It's recommended to also set a password with
|
||||
`-apipass [PASS]`.
|
||||
|
||||
The protocol is meant to be simple, easy to use and with few overhead.
|
||||
To connect to the API, just open a TCP connection to the host computer
|
||||
at the specified port.
|
||||
To make a request you write the UTF-8 encoded JSON contents to the
|
||||
socket. To mark the end of the request, you need to terminate the JSON
|
||||
string with `0x00`. The server returns its answer on the same way,
|
||||
UTF-8 encoded JSON terminated with NULL.
|
||||
|
||||
To save space, by default the JSONs are without whitespace. To enable
|
||||
pretty printing, use `-apipretty`.
|
||||
|
||||
If you want to test the API server without running a game, you can
|
||||
run it headless via `-apidebug`.
|
||||
|
||||
If a password is specified, both request and response are encrypted
|
||||
using RC4 with the key being the password encoded in UTF-8.
|
||||
While only providing weak security when the password is static,
|
||||
it's simple to implement and requires no authentication protocol.
|
||||
The password is also able to change dynamically, see
|
||||
`control.session_refresh()` for details.
|
||||
|
||||
If you don't want to worry about all the details, you can just use one
|
||||
of the supplied libraries (check /api/resources). There's also a little
|
||||
remote control GUI available for python.
|
||||
|
||||
### WebSocket
|
||||
SpiceTools opens a WebSocket server on the specified port plus one.
|
||||
That means if the API is on 1337, the WebSocket server will be on 1338.
|
||||
The protocol is the very same as for the normal API, but instead of
|
||||
directly sending the data over TCP you need to send binary datapackets.
|
||||
The included dart spiceapi library also has a WebSocket implementation.
|
||||
|
||||
### Example Call
|
||||
This example inserts the a card into P1's reader slot.
|
||||
If you need more examples, you can check the example code.
|
||||
|
||||
#### Request
|
||||
```JSON
|
||||
{
|
||||
"id": 1,
|
||||
"module": "card",
|
||||
"function": "insert",
|
||||
"params": [0, "E004010000000000"]
|
||||
}
|
||||
```
|
||||
#### Response
|
||||
```JSON
|
||||
{
|
||||
"id": 1,
|
||||
"errors": [],
|
||||
"data": []
|
||||
}
|
||||
```
|
||||
|
||||
### Modules
|
||||
|
||||
For the sake of simplifying this documentation, I will describe functions
|
||||
as if they were called via a normal programming language. That means,
|
||||
besides `module`/`function` needing to be set accordingly, the `params` field
|
||||
must contain all the parameters described below. Some of them are optional.
|
||||
The returning data will always be in the `data` field of the response.
|
||||
|
||||
Errors will be reported as strings in the `errors` field. Sometimes, you can
|
||||
receive more than one error. The error messages may be changed/improved in the
|
||||
future, so you shouldn't rely on them. If the error list is empty, the function
|
||||
executed successfully.
|
||||
|
||||
The `id` of the response will always match the `id` of the request. The API
|
||||
server itself doesn't expect any logic of your ID choices, however you can use
|
||||
them to make sure your answers aren't arriving out of order. Currently it
|
||||
doesn't matter since the TCP protocol doesn't allow for out of order data,
|
||||
however this may change when/if support for UDP is being introduced. The only
|
||||
restriction is that the ID has to be a valid 64-bit unsigned integer.
|
||||
|
||||
#### Card
|
||||
- insert(index: uint, card_id: hex)
|
||||
- inserts a card which gets read by the emulated card readers for the game
|
||||
- index has to be either 0 (for P1) or 1 (for P2)
|
||||
- card_id has to be a valid 16-character hex string
|
||||
|
||||
#### Coin
|
||||
- get()
|
||||
- returns the amount of unprocessed coins in queue
|
||||
- not equal to the amount of coins shown ingame since the game may drain it
|
||||
- set(amount: int)
|
||||
- sets the amount of coins in queue for the game to process
|
||||
- insert(amount: int)
|
||||
- adds the amount to the coins in queue
|
||||
- amount is optional and defaults to 1
|
||||
- blocker_get()
|
||||
- returns the current coin blocker state (false: open, true: closed)
|
||||
|
||||
#### Info
|
||||
- avs()
|
||||
- returns a dict including the AVS model, dest, spec, rev and ext
|
||||
- launcher()
|
||||
- returns a dict including the version, compile date/time, system time and
|
||||
the arguments used to start the process
|
||||
- memory()
|
||||
- returns a dict including total, total used and bytes used by the process
|
||||
for both physical and virtual RAM
|
||||
|
||||
#### Keypads
|
||||
For all functions in this module, the keypad parameter must be
|
||||
either 0 (for P1) or 1 (for P2). Accepted keypad characters are "0" to "9" for
|
||||
the single digit numbers, "A" for the double zero and "D" for the decimal key.
|
||||
- write(keypad: uint, input: str)
|
||||
- writes all characters in input sequentially to the keypad
|
||||
- this should be used for things like PIN input
|
||||
- set(keypad: uint, key: char, ...)
|
||||
- clears all overrides, then applies each given key as override
|
||||
- if a key is overridden, it shows up as pressed in-game
|
||||
- get(keypad: uint)
|
||||
- returns all pressed keys of the specified keypad
|
||||
|
||||
#### Analogs/Buttons/Lights
|
||||
All of those three modules have equally named methods for you to call.
|
||||
- read()
|
||||
- returns an array of state objects containing name, state and a bool
|
||||
- the bool indicates if the object is active (e.g. the button was overridden)
|
||||
- write([name: str, state: float], ...)
|
||||
- applies the states as an override value
|
||||
- the device binding via the config will be ignored while an override is set
|
||||
- if an override value is set, the object will be marked as active
|
||||
- write_reset([name: str], ...)
|
||||
- removes the override value from the objects specified by name
|
||||
- if no names were passed, all overrides will be removed
|
||||
|
||||
#### Touch
|
||||
- read()
|
||||
- returns an array of state objects containing id, x and y
|
||||
- write([id: uint, x: int, y: int], ...)
|
||||
- adds the given touch points having an unknown ID
|
||||
- overrides old touch points when the ID matches
|
||||
- write_reset(id: uint, ...)
|
||||
- removes the touch points matching one of the given IDs
|
||||
|
||||
#### Control
|
||||
This module only functions if a password is being used to communicate,
|
||||
with the single exception of session_refresh().
|
||||
- raise(signal: str)
|
||||
- raises a signal to the current process
|
||||
- signal is a string and must be one of the following values:
|
||||
"SIGABRT", "SIGFPE", "SIGILL", "SIGINT", "SIGSEGV", "SIGTERM"
|
||||
- the signal will be raised while the message is being processed
|
||||
- exit(code: int)
|
||||
- code is optional and defaults to 0
|
||||
- the process will end while the message is being processed
|
||||
- restart()
|
||||
- spawns a new instance with the same arguments as passed to the main executable
|
||||
- session_refresh()
|
||||
- generates a new secure password and applies it after the response
|
||||
- can be used to increase the security by using the password as some kind of
|
||||
session token
|
||||
- recommended to call directly after connecting and once in a while for
|
||||
persistent connections
|
||||
- shutdown()
|
||||
- tries to force shutdown the computer
|
||||
- reboot()
|
||||
- tries to force reboot the computer
|
||||
|
||||
#### Memory
|
||||
This module only functions if a password is being used to communicate.
|
||||
All offsets are specified in file offsets of the corresponding `dll_name`,
|
||||
which also means that your hex edits are applicable directly.
|
||||
- write(dll_name: str, data: hex, offset: uint)
|
||||
- writes the bytes in data to the specified offset
|
||||
- read(dll_name: str, offset: uint, size: uint)
|
||||
- reads bytes starting from offset and returns it
|
||||
- signature(dll_name: str, signature: hex, replacement: hex, offset: uint,
|
||||
usage: uint)
|
||||
- tries to find the signature in the module's memory
|
||||
- unknown bytes are masked out via "??" (e.g. "75??90????74")
|
||||
- masking out bytes works for both the signature and the replacement
|
||||
- offset is the byte difference between the offset of the found signature and
|
||||
the offset where the replacement data gets written to
|
||||
- usage is the number of the result being used for replacement, starting at 0
|
||||
- returns the offset where the replacement data gets written to
|
||||
- the replacement string can be empty, so you can only get the address of the
|
||||
signature while not actually replacing anything
|
||||
|
||||
#### IIDX
|
||||
- ticker_get()
|
||||
- returns a string of the characters displayed on the 16 segment display
|
||||
- ticker_set(text: str)
|
||||
- sets the contents of the 16 segment display and disables writes from game
|
||||
- ticker_reset()
|
||||
- re-enables writes from game
|
||||
|
||||
#### LCD
|
||||
- info()
|
||||
- returns information about the serial LCD controller some games use
|
||||
|
||||
## License
|
||||
Unless otherwise noted, all files are licensed under the GPLv3.
|
||||
See the LICENSE file for the full license text.
|
||||
Files not originating from this project should be placed in "external",
|
||||
they each have their own license.
|
||||
|
||||
###### What that means for you
|
||||
- If you want to distribute the binaries, you'll have to include the
|
||||
sources, otherwise you're violating the GPL.
|
||||
- If you want to merge some of this project's code into similar
|
||||
software, that software has to be put under the GPL as well.
|
||||
You'll probably have to modify a lot anyways, so you can just use the
|
||||
source as some kind of documentation.
|
140
acio/acio.cpp
Normal file
140
acio/acio.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "acio.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "cfg/config.h"
|
||||
#include "cfg/api.h"
|
||||
#include "hooks/libraryhook.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "bi2a/bi2a.h"
|
||||
#include "bmpu/bmpu.h"
|
||||
#include "core/core.h"
|
||||
#include "hbhi/hbhi.h"
|
||||
#include "hdxs/hdxs.h"
|
||||
#include "hgth/hgth.h"
|
||||
#include "i36g/i36g.h"
|
||||
#include "i36i/i36i.h"
|
||||
#include "icca/icca.h"
|
||||
#include "j32d/j32d.h"
|
||||
#include "kfca/kfca.h"
|
||||
#include "klpa/klpa.h"
|
||||
#include "mdxf/mdxf.h"
|
||||
#include "nddb/nddb.h"
|
||||
#include "panb/panb.h"
|
||||
#include "pix/pix.h"
|
||||
#include "pjec/pjec.h"
|
||||
#include "pjei/pjei.h"
|
||||
#include "la9a/la9a.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
// globals
|
||||
namespace acio {
|
||||
HINSTANCE DLL_INSTANCE = nullptr;
|
||||
std::vector<acio::ACIOModule *> MODULES;
|
||||
}
|
||||
|
||||
/*
|
||||
* decide on hook mode used
|
||||
* libacio compiled using ICC64 sometimes doesn't leave enough space to insert the inline hooks
|
||||
* in this case, we want to use IAT instead
|
||||
*/
|
||||
static inline acio::HookMode get_hookmode() {
|
||||
#ifdef SPICE64
|
||||
return acio::HookMode::IAT;
|
||||
#else
|
||||
return acio::HookMode::INLINE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void acio::attach() {
|
||||
log_info("acio", "SpiceTools ACIO");
|
||||
|
||||
// load settings and instance
|
||||
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
|
||||
|
||||
/*
|
||||
* library hook
|
||||
* some games have a second DLL laying around which gets loaded dynamically
|
||||
* we just give it the same instance as the normal one so the hooks still work
|
||||
*/
|
||||
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
|
||||
acio::HookMode hook_mode = get_hookmode();
|
||||
|
||||
// load modules
|
||||
MODULES.push_back(new acio::BI2AModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::BMPUModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::CoreModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HBHIModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HDXSModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::HGTHModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::I36GModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::I36IModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::ICCAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::J32DModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::KFCAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::KLPAModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::MDXFModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::NDDBModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PANBModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PJECModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::PJEIModule(acio::DLL_INSTANCE, hook_mode));
|
||||
MODULES.push_back(new acio::LA9AModule(acio::DLL_INSTANCE, hook_mode));
|
||||
|
||||
/*
|
||||
* PIX is special and needs another DLL.
|
||||
* we load that module only if the file exists.
|
||||
*/
|
||||
if (fileutils::file_exists(MODULE_PATH / "libacio_pix.dll")) {
|
||||
HINSTANCE pix_instance = libutils::load_library(MODULE_PATH / "libacio_pix.dll");
|
||||
MODULES.push_back(new acio::PIXModule(pix_instance, hook_mode));
|
||||
}
|
||||
|
||||
// apply modules
|
||||
for (auto &module : MODULES) {
|
||||
module->attach();
|
||||
}
|
||||
}
|
||||
|
||||
void acio::attach_icca() {
|
||||
log_info("acio", "SpiceTools ACIO ICCA");
|
||||
|
||||
// load instance if needed
|
||||
if (!acio::DLL_INSTANCE) {
|
||||
acio::DLL_INSTANCE = LoadLibraryA("libacio.dll");
|
||||
}
|
||||
|
||||
// get hook mode
|
||||
acio::HookMode hook_mode = get_hookmode();
|
||||
|
||||
// load single module
|
||||
auto icca_module = new acio::ICCAModule(acio::DLL_INSTANCE, hook_mode);
|
||||
icca_module->attach();
|
||||
MODULES.push_back(icca_module);
|
||||
}
|
||||
|
||||
void acio::detach() {
|
||||
|
||||
// clear modules
|
||||
while (!MODULES.empty()) {
|
||||
delete MODULES.back();
|
||||
MODULES.pop_back();
|
||||
}
|
||||
}
|
18
acio/acio.h
Normal file
18
acio/acio.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// globals
|
||||
extern HINSTANCE DLL_INSTANCE;
|
||||
extern std::vector<acio::ACIOModule *> MODULES;
|
||||
|
||||
void attach();
|
||||
void attach_icca();
|
||||
void detach();
|
||||
}
|
763
acio/bi2a/bi2a.cpp
Normal file
763
acio/bi2a/bi2a.cpp
Normal file
@ -0,0 +1,763 @@
|
||||
#include "bi2a.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ddr/io.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/sdvx/io.h"
|
||||
#include "games/drs/io.h"
|
||||
#include "games/drs/drs.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/tapeled.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[272] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static unsigned int BI2A_VOLL = 0;
|
||||
static unsigned int BI2A_VOLR = 0;
|
||||
|
||||
|
||||
static bool __cdecl ac_io_bi2a_init_is_finished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_get_control_status_buffer(void *buffer) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, std::size(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
/*
|
||||
* Unmapped Buttons
|
||||
*
|
||||
* Control Bit
|
||||
* EX BUTTON 1 93
|
||||
* EX BUTTON 2 92
|
||||
* EX ANALOG 1 170-183
|
||||
* EX ANALOG 2 186-199
|
||||
*/
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sdvx::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Test))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 19);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Service))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 18);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::CoinMech))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 17);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Start))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 85);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_A))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 84);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_B))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 83);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_C))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 82);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_D))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 81);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_L))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 80);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_R))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 95);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Headphone))) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
|
||||
// volume left
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
BI2A_VOLL = (BI2A_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
BI2A_VOLL = (BI2A_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
BI2A_VOLR = (BI2A_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
BI2A_VOLR = (BI2A_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// update volumes
|
||||
auto &analogs = games::sdvx::get_analogs();
|
||||
auto vol_left = BI2A_VOLL;
|
||||
auto vol_right = BI2A_VOLR;
|
||||
if (analogs.at(0).isSet() || analogs.at(1).isSet()) {
|
||||
vol_left += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_L)) * 1023.99f);
|
||||
vol_right += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_R)) * 1023.99f);
|
||||
}
|
||||
|
||||
// proper loops
|
||||
vol_left %= 1024;
|
||||
vol_right %= 1024;
|
||||
|
||||
// save volumes in buffer
|
||||
*((uint16_t*) &STATUS_BUFFER[17]) = (uint16_t) ((vol_left) << 2);
|
||||
*((uint16_t*) &STATUS_BUFFER[19]) = (uint16_t) ((vol_right) << 2);
|
||||
}
|
||||
|
||||
// DanceDanceRevolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::ddr::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::COIN_MECH)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::SERVICE)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::TEST)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[2] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_START)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 7;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_UP)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_DOWN)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_LEFT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P1_MENU_RIGHT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[10] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_START)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_UP)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_DOWN)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_LEFT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ddr::Buttons::P2_MENU_RIGHT)) == Buttons::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[11] |= 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
// DANCERUSH
|
||||
if (avs::game::is_model("REC")) {
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, std::size(STATUS_BUFFER));
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::drs::get_buttons();
|
||||
|
||||
// test
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 19);
|
||||
}
|
||||
|
||||
// service
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 18);
|
||||
}
|
||||
|
||||
// coin
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::CoinMech)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 17);
|
||||
}
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 86);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 85);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 84);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P1_Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 83);
|
||||
}
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 93);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 92);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 91);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 90);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::drs::Buttons::P2_Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 89);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_current_coinstock(size_t index, DWORD *coins) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// get coins and return success
|
||||
*coins = (DWORD) eamuse_coin_get_stock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_consume_coinstock(size_t index, int amount) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// calculate new stock
|
||||
auto stock = eamuse_coin_get_stock();
|
||||
auto stock_new = stock - amount;
|
||||
|
||||
// check new stock
|
||||
if (stock_new < 0)
|
||||
return false;
|
||||
|
||||
// apply new stock
|
||||
eamuse_coin_set_stock(stock_new);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_lock_coincounter(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// enable coin blocker
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_unlock_coincounter(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return false;
|
||||
|
||||
// disable coin blocker
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_control_coin_blocker_close(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return;
|
||||
|
||||
// enable coin blocker
|
||||
eamuse_coin_set_block(true);
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_control_coin_blocker_open(size_t index) {
|
||||
|
||||
// check index
|
||||
if (index > 1)
|
||||
return;
|
||||
|
||||
// disable coin blocker
|
||||
eamuse_coin_set_block(false);
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_control_led_bright(size_t index, uint8_t brightness) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
/*
|
||||
* Control R G B
|
||||
* =======================
|
||||
* WING UP 28 29 30
|
||||
* WING LOW 31 32 33
|
||||
* WOOFER 0 1 3
|
||||
* CONTROLLER 4 5 6
|
||||
*
|
||||
* Values go up to 255.
|
||||
*
|
||||
*
|
||||
* Control Index
|
||||
* ==================
|
||||
* START BUTTON 8
|
||||
* A BUTTON 9
|
||||
* B BUTTON 10
|
||||
* C BUTTON 11
|
||||
* D BUTTON 12
|
||||
* FX L BUTTON 13
|
||||
* FX R BUTTON 14
|
||||
* POP 24
|
||||
* TITLE LEFT 25
|
||||
* TITLE RIGHT 26
|
||||
*
|
||||
* Values go up to 127.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
int light1, light2;
|
||||
float max;
|
||||
} mapping[] = {
|
||||
{ games::sdvx::Lights::WOOFER_R, -1, 255 },
|
||||
{ games::sdvx::Lights::WOOFER_G, -1, 255 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::WOOFER_B, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_R, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_G, -1, 255 },
|
||||
{ games::sdvx::Lights::CONTROLLER_B, -1, 255 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::START, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_A, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_B, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_C, -1, 127 },
|
||||
{ games::sdvx::Lights::BT_D, -1, 127 },
|
||||
{ games::sdvx::Lights::FX_L, -1, 127 },
|
||||
{ games::sdvx::Lights::FX_R, -1, 127 },
|
||||
{ -1, -1, 0 }, { -1, -1, 0 }, { -1, -1, 0 },
|
||||
{ games::sdvx::Lights::GENERATOR_R, -1, 255 },
|
||||
{ games::sdvx::Lights::GENERATOR_G, -1, 255 },
|
||||
{ games::sdvx::Lights::GENERATOR_B, -1, 255 },
|
||||
{ -1, -1, 0 }, { -1, -1, 0 }, { -1, -1, 0 },
|
||||
{ games::sdvx::Lights::POP, -1, 127 },
|
||||
{ games::sdvx::Lights::TITLE_LEFT, -1, 127 },
|
||||
{ games::sdvx::Lights::TITLE_RIGHT, -1, 127 },
|
||||
{ -1, -1, 0 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_R, games::sdvx::Lights::WING_LEFT_UP_R, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_G, games::sdvx::Lights::WING_LEFT_UP_G, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_UP_B, games::sdvx::Lights::WING_LEFT_UP_B, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_R, games::sdvx::Lights::WING_LEFT_LOW_R, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_G, games::sdvx::Lights::WING_LEFT_LOW_G, 255 },
|
||||
{ games::sdvx::Lights::WING_RIGHT_LOW_B, games::sdvx::Lights::WING_LEFT_LOW_B, 255 },
|
||||
};
|
||||
|
||||
// ignore index out of range
|
||||
if (index > std::size(mapping)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// get light from mapping
|
||||
auto light = mapping[index];
|
||||
|
||||
// write lights
|
||||
if (light.light1 >= 0) {
|
||||
Lights::writeLight(RI_MGR, lights[light.light1], brightness / light.max);
|
||||
} else {
|
||||
log_warning("sdvx", "light unset {} {}", index, (int) brightness);
|
||||
}
|
||||
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
|
||||
return true;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_get_watchdog_time_min() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_bi2a_get_watchdog_time_now() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bi2a_watchdog_off() {
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_init(uint8_t param) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_set_watchdog_time(uint16_t time) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_set_amp_volume(uint8_t a1, uint8_t a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_init(uint8_t a1, uint8_t a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_init_is_finished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
log_fatal("drs", "DRS tapeled wrong dimensions");
|
||||
}
|
||||
|
||||
// copy data into our buffer - 4 bytes per pixel BGR
|
||||
for (size_t i = 0; i < x_sz * y_sz; i++) {
|
||||
games::drs::DRS_TAPELED[i][0] = data[i*4+2];
|
||||
games::drs::DRS_TAPELED[i][1] = data[i*4+1];
|
||||
games::drs::DRS_TAPELED[i][2] = data[i*4];
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bi2a_tapeled_send() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bi2a_get_exbio2_status(uint8_t *info) {
|
||||
// surely this meme never gets old
|
||||
info[5] = 5;
|
||||
info[6] = 7;
|
||||
info[7] = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
acio::BI2AModule::BI2AModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("BI2A", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::BI2AModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_init_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_init);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_set_amp_volume);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_init);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_init_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_get_exbio2_status);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_tapeled_rec_set);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_control_tapeled_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bi2a_tapeled_send);
|
||||
}
|
13
acio/bi2a/bi2a.h
Normal file
13
acio/bi2a/bi2a.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BI2AModule : public ACIOModule {
|
||||
public:
|
||||
BI2AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
596
acio/bmpu/bmpu.cpp
Normal file
596
acio/bmpu/bmpu.cpp
Normal file
@ -0,0 +1,596 @@
|
||||
#include "bmpu.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/api.h"
|
||||
#include "cfg/light.h"
|
||||
#include "games/bbc/io.h"
|
||||
#include "games/dea/io.h"
|
||||
#include "games/ftt/io.h"
|
||||
#include "games/museca/io.h"
|
||||
#include "games/silentscope/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static char __cdecl ac_io_bmpu_consume_coinstock(int a1, int a2) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_1p_start_led_off() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_1p_start_led_on() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P1Start), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_2p_start_led_off() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 0.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_2p_start_led_on() {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
auto &lights = games::dea::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::P2Start), 1.f);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_coin_blocker_close() {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_control_coin_blocker_open() {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::museca::get_lights();
|
||||
|
||||
// control mapping
|
||||
static const int mapping[] = {
|
||||
games::museca::Lights::UnderLED3G,
|
||||
games::museca::Lights::UnderLED3R,
|
||||
games::museca::Lights::UnderLED2B,
|
||||
games::museca::Lights::UnderLED2G,
|
||||
games::museca::Lights::UnderLED2R,
|
||||
games::museca::Lights::UnderLED1B,
|
||||
games::museca::Lights::UnderLED1G,
|
||||
games::museca::Lights::UnderLED1R,
|
||||
-1, -1, -1, -1,
|
||||
games::museca::Lights::SideB,
|
||||
games::museca::Lights::SideG,
|
||||
games::museca::Lights::SideR,
|
||||
games::museca::Lights::UnderLED3B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bbc::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::bbc::Lights::UNDER_LED3_G,
|
||||
games::bbc::Lights::UNDER_LED3_R,
|
||||
games::bbc::Lights::UNDER_LED2_B,
|
||||
games::bbc::Lights::UNDER_LED2_G,
|
||||
games::bbc::Lights::UNDER_LED2_R,
|
||||
games::bbc::Lights::UNDER_LED1_B,
|
||||
games::bbc::Lights::UNDER_LED1_G,
|
||||
games::bbc::Lights::UNDER_LED1_R,
|
||||
-1, -1, -1, -1,
|
||||
games::bbc::Lights::IC_CARD_B,
|
||||
games::bbc::Lights::IC_CARD_G,
|
||||
games::bbc::Lights::IC_CARD_R,
|
||||
games::bbc::Lights::UNDER_LED3_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::ftt::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::ftt::Lights::Pad3_G,
|
||||
games::ftt::Lights::Pad3_R,
|
||||
games::ftt::Lights::Pad2_B,
|
||||
games::ftt::Lights::Pad2_G,
|
||||
games::ftt::Lights::Pad2_R,
|
||||
games::ftt::Lights::Pad1_B,
|
||||
games::ftt::Lights::Pad1_G,
|
||||
games::ftt::Lights::Pad1_R,
|
||||
-1, -1, -1, -1,
|
||||
games::ftt::Lights::Pad4_B,
|
||||
games::ftt::Lights::Pad4_G,
|
||||
games::ftt::Lights::Pad4_R,
|
||||
games::ftt::Lights::Pad3_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dance Evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
games::dea::Lights::P2LRButton,
|
||||
games::dea::Lights::P1LRButton,
|
||||
-1,
|
||||
games::dea::Lights::TitleB,
|
||||
games::dea::Lights::TitleR,
|
||||
games::dea::Lights::TitleG,
|
||||
-1,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 128.f ? 1.f : brightness / 128.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++)
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_control_led_bright_pack(int a1, int a2, int a3) {
|
||||
// TODO(felix): NDD lights
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_current_coinstock(int a1, int *a2) {
|
||||
*a2 = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_control_status_buffer(void *buffer) {
|
||||
size_t buffer_len = 0;
|
||||
|
||||
if (avs::game::is_model({ "KDM", "MMD" })) {
|
||||
buffer_len = sizeof(STATUS_BUFFER);
|
||||
} else if (avs::game::is_model("PIX")) {
|
||||
buffer_len = 16;
|
||||
} else if (avs::game::is_model("R66")) {
|
||||
buffer_len = 56;
|
||||
} else if (avs::game::is_model("NDD")) {
|
||||
buffer_len = 56;
|
||||
}
|
||||
|
||||
if (buffer_len > 0) {
|
||||
memcpy(buffer, &STATUS_BUFFER, buffer_len);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *__cdecl ac_io_bmpu_get_softwareid(char *a1) {
|
||||
*a1 = 0;
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char *__cdecl ac_io_bmpu_get_systemid(char *a1) {
|
||||
*a1 = 0;
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_init_outport() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_lock_coincounter(signed int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_isfinished(DWORD *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_softwareplug(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_check_systemplug() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_req_secplug_missing_check() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_req_secplug_missing_check_isfinished(DWORD *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_set_outport_led(uint8_t *data1, uint8_t *data2) {
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::dea::Lights::SideUpperLeftR,
|
||||
games::dea::Lights::SideUpperLeftG,
|
||||
games::dea::Lights::SideUpperLeftB,
|
||||
games::dea::Lights::SideLowerLeft1R,
|
||||
games::dea::Lights::SideLowerLeft1G,
|
||||
games::dea::Lights::SideLowerLeft1B,
|
||||
games::dea::Lights::SideLowerLeft2R,
|
||||
games::dea::Lights::SideLowerLeft2G,
|
||||
games::dea::Lights::SideLowerLeft2B,
|
||||
games::dea::Lights::SideLowerLeft3R,
|
||||
games::dea::Lights::SideLowerLeft3G,
|
||||
games::dea::Lights::SideLowerLeft3B,
|
||||
games::dea::Lights::SideUpperRightR,
|
||||
games::dea::Lights::SideUpperRightG,
|
||||
games::dea::Lights::SideUpperRightB,
|
||||
games::dea::Lights::SideLowerRight1R,
|
||||
games::dea::Lights::SideLowerRight1G,
|
||||
games::dea::Lights::SideLowerRight1B,
|
||||
games::dea::Lights::SideLowerRight2R,
|
||||
games::dea::Lights::SideLowerRight2G,
|
||||
games::dea::Lights::SideLowerRight2B,
|
||||
games::dea::Lights::SideLowerRight3R,
|
||||
games::dea::Lights::SideLowerRight3G,
|
||||
games::dea::Lights::SideLowerRight3B,
|
||||
};
|
||||
|
||||
// write lights
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
float brightness = data1[i * 2] / 255.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), brightness);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_bmpu_set_output_mode(__int16 a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_unlock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_update_control_status_buffer() {
|
||||
unsigned int control_data = 0;
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// DEA
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::dea::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Start))) {
|
||||
control_data |= 0x00000001;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Left))) {
|
||||
control_data |= 0x00000008;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P1Right))) {
|
||||
control_data |= 0x00000010;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Start))) {
|
||||
control_data |= 0x00000100;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Left))) {
|
||||
control_data |= 0x00000800;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::dea::Buttons::P2Right))) {
|
||||
control_data |= 0x00001000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::ftt::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::museca::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Start))) {
|
||||
control_data |= 0x00000001;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::bbc::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::Service))) {
|
||||
control_data |= 0x0F000000;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::Test))) {
|
||||
control_data |= 0xF0000000;
|
||||
}
|
||||
|
||||
// set control data
|
||||
auto buffer = reinterpret_cast<unsigned int *>(STATUS_BUFFER);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
buffer[i] = control_data;
|
||||
}
|
||||
}
|
||||
|
||||
// Silent Scope Bone Eater
|
||||
if (avs::game::is_model("NDD")) {
|
||||
|
||||
// clear state
|
||||
memset(STATUS_BUFFER, 0, 56);
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::silentscope::get_buttons();
|
||||
|
||||
// get control data
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::TEST))) {
|
||||
STATUS_BUFFER[7] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SERVICE))) {
|
||||
STATUS_BUFFER[7] |= 0x2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::COIN_MECH))) {
|
||||
STATUS_BUFFER[7] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::START))) {
|
||||
STATUS_BUFFER[5] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::UP))) {
|
||||
STATUS_BUFFER[5] |= 0x2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::DOWN))) {
|
||||
STATUS_BUFFER[5] |= 0x4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::LEFT))) {
|
||||
STATUS_BUFFER[5] |= 0x8;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::RIGHT))) {
|
||||
STATUS_BUFFER[5] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SCOPE_RIGHT))) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::SCOPE_LEFT))) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::silentscope::Buttons::GUN_PRESSED))) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
|
||||
// joy stick raw input
|
||||
auto &analogs = games::silentscope::get_analogs();
|
||||
unsigned short joy_x = 0x7FFF;
|
||||
unsigned short joy_y = 0x7FFF;
|
||||
if (analogs.at(games::silentscope::Analogs::GUN_X).isSet()) {
|
||||
joy_x = (unsigned short) (Analogs::getState(RI_MGR, analogs.at(games::silentscope::Analogs::GUN_X)) * USHRT_MAX);
|
||||
}
|
||||
if (analogs.at(games::silentscope::Analogs::GUN_Y).isSet()) {
|
||||
joy_y = (unsigned short) (Analogs::getState(RI_MGR, analogs.at(games::silentscope::Analogs::GUN_Y)) * USHRT_MAX);
|
||||
}
|
||||
|
||||
// invert X axis
|
||||
joy_x = USHRT_MAX - joy_x;
|
||||
|
||||
STATUS_BUFFER[8] = HIBYTE(joy_x);
|
||||
STATUS_BUFFER[9] = LOBYTE(joy_x);
|
||||
STATUS_BUFFER[10] = HIBYTE(joy_y);
|
||||
STATUS_BUFFER[11] = LOBYTE(joy_y);
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_bmpu_set_watchdog_time(char a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_bmpu_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_bmpu_watchdog_off() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::BMPUModule::BMPUModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("BMPU", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::BMPUModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_1p_start_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_1p_start_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_2p_start_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_2p_start_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_control_led_bright_pack);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_init_outport);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_outport_led);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_output_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_bmpu_watchdog_off);
|
||||
}
|
13
acio/bmpu/bmpu.h
Normal file
13
acio/bmpu/bmpu.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class BMPUModule : public ACIOModule {
|
||||
public:
|
||||
BMPUModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
190
acio/core/core.cpp
Normal file
190
acio/core/core.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include "core.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "misc/wintouchemu.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
|
||||
// static stuff
|
||||
static int ACIO_WARMUP = 0;
|
||||
static HHOOK ACIO_KB_HOOK = nullptr;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
// needed for some games to make GetAsyncKeyState() working
|
||||
static LRESULT CALLBACK ac_io_kb_hook_callback(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
CallNextHookEx(ACIO_KB_HOOK, nCode, wParam, lParam);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_begin(
|
||||
size_t dev,
|
||||
const char *ver,
|
||||
unsigned int *val,
|
||||
size_t flags,
|
||||
void *ptr,
|
||||
size_t baud)
|
||||
{
|
||||
if (ACIO_KB_HOOK == nullptr) {
|
||||
ACIO_KB_HOOK = SetWindowsHookEx(WH_KEYBOARD_LL, ac_io_kb_hook_callback, GetModuleHandle(nullptr), 0);
|
||||
}
|
||||
|
||||
// always return success
|
||||
if (val && avs::game::is_model("KFC")) {
|
||||
*val = 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_begin_get_status() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_end(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_end_get_status(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_get_rs232c_status(char *a1, int a2) {
|
||||
return memset(a1, 0, 88);
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_get_version(uint8_t *a1, int a2) {
|
||||
|
||||
// some games have version checks
|
||||
// pop'n music only accepts versions bigger than 1.X.X (check yourself), anything starting with 2 works though
|
||||
memset(a1 + 5, 2, 1);
|
||||
memset(a1 + 6, 0, 1);
|
||||
memset(a1 + 7, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *__cdecl ac_io_get_version_string() {
|
||||
static const char *version = "1.25.0";
|
||||
return version;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_is_active(int a1, int a2) {
|
||||
if (a1 == 1 && avs::game::is_model("JMA")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (char) (++ACIO_WARMUP > 601 ? 1 : 0);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_is_active2(int a1, int *a2, int a3) {
|
||||
ACIO_WARMUP = 601;
|
||||
*a2 = 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_is_active_device(int index, int a2) {
|
||||
|
||||
// for scotto
|
||||
static bool CHECKED_24 = false;
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// disable mysterious LED devices
|
||||
if (index >= 12 && index <= 15)
|
||||
return false;
|
||||
}
|
||||
|
||||
// scotto
|
||||
if (avs::game::is_model("NSC") && index == 24) {
|
||||
|
||||
// scotto expects device index 24 to come online after
|
||||
// it initializes device index 22
|
||||
if (!CHECKED_24) {
|
||||
CHECKED_24 = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// dunno for what game we did this again
|
||||
return (char) (index != 5);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_reset(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_secplug_set_encodedpasswd(void *a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_set_soft_watch_dog(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_soft_watch_dog_on(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_soft_watch_dog_off() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_update(int a1) {
|
||||
|
||||
// flush device output
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
// update wintouchemu
|
||||
wintouchemu::update();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_get_firmware_update_device_index() {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_go_firmware_update() {
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_set_get_status_device(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::CoreModule::CoreModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("Core", module, hookMode) {
|
||||
}
|
||||
|
||||
void acio::CoreModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_begin);
|
||||
ACIO_MODULE_HOOK(ac_io_begin_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_end);
|
||||
ACIO_MODULE_HOOK(ac_io_end_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_get_rs232c_status);
|
||||
ACIO_MODULE_HOOK(ac_io_get_version);
|
||||
ACIO_MODULE_HOOK(ac_io_get_version_string);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active2);
|
||||
ACIO_MODULE_HOOK(ac_io_is_active_device);
|
||||
ACIO_MODULE_HOOK(ac_io_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_secplug_set_encodedpasswd);
|
||||
ACIO_MODULE_HOOK(ac_io_set_soft_watch_dog);
|
||||
ACIO_MODULE_HOOK(ac_io_soft_watch_dog_on);
|
||||
ACIO_MODULE_HOOK(ac_io_soft_watch_dog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_update);
|
||||
ACIO_MODULE_HOOK(ac_io_get_firmware_update_device_index);
|
||||
ACIO_MODULE_HOOK(ac_io_go_firmware_update);
|
||||
ACIO_MODULE_HOOK(ac_io_set_get_status_device);
|
||||
}
|
13
acio/core/core.h
Normal file
13
acio/core/core.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class CoreModule : public ACIOModule {
|
||||
public:
|
||||
CoreModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
565
acio/hbhi/hbhi.cpp
Normal file
565
acio/hbhi/hbhi.cpp
Normal file
@ -0,0 +1,565 @@
|
||||
#include "hbhi.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/rf3d/io.h"
|
||||
#include "games/sc/io.h"
|
||||
#include "games/hpm/io.h"
|
||||
#include "avs/game.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hbhi_add_coin(int a1, int a2) {
|
||||
eamuse_coin_add();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_consume_coinstock(int a1, int a2) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method, not a real ACIO one
|
||||
*/
|
||||
static inline int __cdecl ac_io_hbhi_control_lamp_set(uint32_t lamp_bits, float value) {
|
||||
|
||||
// steel chronicle
|
||||
if (avs::game::is_model("KGG")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sc::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideGreen), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::SideBlue), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterGreen), value);
|
||||
}
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::CenterBlue), value);
|
||||
}
|
||||
if (lamp_bits & 0x40) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::ControllerRed), value);
|
||||
}
|
||||
if (lamp_bits & 0x80) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::sc::Lights::ControllerBlue), value);
|
||||
}
|
||||
}
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::hpm::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_RED_P2_GREEN), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_YELLOW), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_GREEN), value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_RED), value);
|
||||
}
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x40) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_YELLOW), value);
|
||||
}
|
||||
if (lamp_bits & 0x80) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P2_START), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_control_lamp_bright(uint32_t lamp_bits, uint8_t value) {
|
||||
ac_io_hbhi_control_lamp_set(lamp_bits, value / 31.f);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_mode(uint32_t mode) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_off(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_lamp_set(lamp_bits, 0.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_lamp_on(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_lamp_set(lamp_bits, 1.f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method, not a real ACIO one
|
||||
*/
|
||||
static inline int __cdecl ac_io_hbhi_control_parallel_set(uint8_t lamp_bits, float value) {
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::hpm::get_lights();
|
||||
|
||||
// write lights
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::P1_START), value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_BLUE), value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_ORANGE), value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights.at(games::hpm::Lights::SPEAKER_RED), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_parallel_off(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_parallel_set(lamp_bits, 0.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_parallel_on(uint8_t lamp_bits) {
|
||||
return ac_io_hbhi_control_parallel_set(lamp_bits, 1.f);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_control_reset() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_create_get_status_thread(void *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_current_coinstock(int a1, int *coinstock) {
|
||||
*coinstock = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_coin_input_wave_buffer(int *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_hbhi_get_control_status_buffer(uint8_t *buffer) {
|
||||
|
||||
// return buffer
|
||||
memcpy(buffer, STATUS_BUFFER, std::size(STATUS_BUFFER));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_softwareid(char *a1) {
|
||||
memset(a1, 'F', 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_get_systemid(char *a1) {
|
||||
memset(a1, 'F', 16);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_hbhi_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_hbhi_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_lock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_disburse() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_carddispenser_disburse_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_get_status() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_req_carddispenser_get_status_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_carddispenser_init() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_carddispenser_init_isfinished(int *a1) {
|
||||
*a1 += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_coin_input_wave() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_get_control_status(int *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_secplug_check_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check_softwareplug(char *a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_check_systemplug() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_req_secplug_missing_check() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_secplug_missing_check_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_volume_control(char a1, char a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_req_volume_control_isfinished(int *a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_reset_coin_slot_noise_flag(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hbhi_set_framing_err_packet_send_interval(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hbhi_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_unlock_coincounter(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hbhi_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// steel chronicle
|
||||
if (avs::game::is_model("KGG")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sc::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::LButton))) {
|
||||
STATUS_BUFFER[12] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::L1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::L2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::JogLeft))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::JogRight))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::RButton))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::R1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sc::Buttons::R2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
|
||||
// get analogs
|
||||
auto &analogs = games::sc::get_analogs();
|
||||
|
||||
auto joy_left_x = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::LEFT_X)) * USHRT_MAX;
|
||||
auto joy_left_y = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::LEFT_Y)) * USHRT_MAX;
|
||||
auto joy_right_x = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::RIGHT_X)) * USHRT_MAX;
|
||||
auto joy_right_y = Analogs::getState(RI_MGR, analogs.at(games::sc::Analogs::RIGHT_Y)) * USHRT_MAX;
|
||||
|
||||
// because these are flight sticks, the X axis is inverted
|
||||
*((uint16_t *) &STATUS_BUFFER[20]) = USHRT_MAX - (uint16_t) joy_left_x;
|
||||
*((uint16_t *) &STATUS_BUFFER[22]) = (uint16_t) joy_left_y;
|
||||
*((uint16_t *) &STATUS_BUFFER[24]) = USHRT_MAX - (uint16_t) joy_right_x;
|
||||
*((uint16_t *) &STATUS_BUFFER[26]) = (uint16_t) joy_right_y;
|
||||
}
|
||||
|
||||
// hello popn music
|
||||
if (avs::game::is_model("JMP")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::hpm::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0x00, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[5] |= 1 << 2;
|
||||
}
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_Start))) {
|
||||
STATUS_BUFFER[4] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_3))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P1_4))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_Start))) {
|
||||
STATUS_BUFFER[6] |= 1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_1))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_2))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_3))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::hpm::Buttons::P2_4))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
// road fighters 3D
|
||||
if (avs::game::is_model("JGT")) {
|
||||
static int lever_state = 0;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::rf3d::get_buttons();
|
||||
|
||||
// reset
|
||||
memset(STATUS_BUFFER, 0x00, 64);
|
||||
|
||||
// check buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Service))) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Test))) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[5] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::View))) {
|
||||
STATUS_BUFFER[12] |= 1 << 2;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Toggle2D3D))) {
|
||||
STATUS_BUFFER[12] |= 1 << 3;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverUp))) {
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverDown))) {
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverLeft))) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
lever_state = 0;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::LeverRight))) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
lever_state = 0;
|
||||
}
|
||||
|
||||
// auto lever buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::AutoLeverUp)) && lever_state < 6) {
|
||||
lever_state++;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::AutoLeverDown)) && lever_state > 0) {
|
||||
lever_state--;
|
||||
}
|
||||
|
||||
// auto lever logic
|
||||
switch (lever_state) {
|
||||
case 1:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 6;
|
||||
break;
|
||||
case 2:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 6;
|
||||
break;
|
||||
case 3:
|
||||
STATUS_BUFFER[12] |= 1 << 4;
|
||||
break;
|
||||
case 4:
|
||||
STATUS_BUFFER[12] |= 1 << 5;
|
||||
break;
|
||||
case 5:
|
||||
STATUS_BUFFER[12] |= 1 << 4 | 1 << 7;
|
||||
break;
|
||||
case 6:
|
||||
STATUS_BUFFER[12] |= 1 << 5 | 1 << 7;
|
||||
break;
|
||||
default:
|
||||
lever_state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_hbhi_watchdog_off() {
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HBHIModule::HBHIModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HBHI", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HBHIModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_add_coin);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_off);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_lamp_on);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_parallel_off);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_parallel_on);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_coin_input_wave_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_disburse);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_disburse_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_get_status_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_init);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_carddispenser_init_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_coin_input_wave);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_reset_coin_slot_noise_flag);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hbhi_watchdog_off);
|
||||
}
|
13
acio/hbhi/hbhi.h
Normal file
13
acio/hbhi/hbhi.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HBHIModule : public ACIOModule {
|
||||
public:
|
||||
HBHIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
200
acio/hdxs/hdxs.cpp
Normal file
200
acio/hdxs/hdxs.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
#include "hdxs.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/popn/io.h"
|
||||
#include "games/rb/io.h"
|
||||
#include "games/dea/io.h"
|
||||
#include "avs/game.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[32] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hdxs_get_control_status_buffer(int a1, void *a2) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(a2, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_scroll(int a1, char a2, char a3, char a4, char a5, char a6, char a7, char a8, char a9,
|
||||
char a10, char a11, char a12, char a13) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_set_pattern(int index, char r, char g, char b, uint64_t led_bits) {
|
||||
|
||||
// reflec beat
|
||||
if (avs::game::is_model({"KBR", "LBR", "MBR"})) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::rb::get_lights();
|
||||
|
||||
// set values
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::rb::Lights::PoleB), b / 127.f);
|
||||
}
|
||||
|
||||
// dance evolution
|
||||
if (avs::game::is_model("KDM")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::dea::get_lights();
|
||||
|
||||
// decide on index
|
||||
switch (index) {
|
||||
case 12:
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperLeftB), b / 127.f);
|
||||
break;
|
||||
case 14:
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightR), r / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightG), g / 127.f);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::dea::Lights::SideUpperRightB), b / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
// popn
|
||||
if (avs::game::is_model("M39")) {
|
||||
|
||||
// mappings
|
||||
static const uint64_t top_led_bits[] = {
|
||||
0x80000000000000,
|
||||
0x40000000000000,
|
||||
0x20000000000000,
|
||||
0x10000000000000,
|
||||
0x8000000000000,
|
||||
0x4000000000000,
|
||||
0x2000000000000,
|
||||
0x1000000000000,
|
||||
0x800000000000,
|
||||
0x400000000000,
|
||||
0x200000000000,
|
||||
0x100000000000,
|
||||
0x80000000000,
|
||||
0x40000000000,
|
||||
0x20000000000,
|
||||
0x10000000000,
|
||||
0x8000000000,
|
||||
0x4000000000,
|
||||
0x2000000000,
|
||||
0x1000000000,
|
||||
0x800000000,
|
||||
0x400000000,
|
||||
0x200000000,
|
||||
0x100000000,
|
||||
0x80000000,
|
||||
0x40000000,
|
||||
0x20000000,
|
||||
0x10000000,
|
||||
0x8000000,
|
||||
0x4000000,
|
||||
0x2000000,
|
||||
0x1000000,
|
||||
};
|
||||
static const size_t light_mapping[] {
|
||||
games::popn::Lights::TopLED1,
|
||||
games::popn::Lights::TopLED2,
|
||||
games::popn::Lights::TopLED3,
|
||||
games::popn::Lights::TopLED4,
|
||||
games::popn::Lights::TopLED5,
|
||||
games::popn::Lights::TopLED6,
|
||||
games::popn::Lights::TopLED7,
|
||||
games::popn::Lights::TopLED8,
|
||||
games::popn::Lights::TopLED9,
|
||||
games::popn::Lights::TopLED10,
|
||||
games::popn::Lights::TopLED11,
|
||||
games::popn::Lights::TopLED12,
|
||||
games::popn::Lights::TopLED13,
|
||||
games::popn::Lights::TopLED14,
|
||||
games::popn::Lights::TopLED15,
|
||||
games::popn::Lights::TopLED16,
|
||||
games::popn::Lights::TopLED17,
|
||||
games::popn::Lights::TopLED18,
|
||||
games::popn::Lights::TopLED19,
|
||||
games::popn::Lights::TopLED20,
|
||||
games::popn::Lights::TopLED21,
|
||||
games::popn::Lights::TopLED22,
|
||||
games::popn::Lights::TopLED23,
|
||||
games::popn::Lights::TopLED24,
|
||||
games::popn::Lights::TopLED25,
|
||||
games::popn::Lights::TopLED26,
|
||||
games::popn::Lights::TopLED27,
|
||||
games::popn::Lights::TopLED28,
|
||||
games::popn::Lights::TopLED29,
|
||||
games::popn::Lights::TopLED30,
|
||||
games::popn::Lights::TopLED31,
|
||||
games::popn::Lights::TopLED32,
|
||||
};
|
||||
|
||||
// get lights
|
||||
auto &lights = games::popn::get_lights();
|
||||
|
||||
// bit scan
|
||||
for (int i = 0; i < 32; i++) {
|
||||
bool value = (led_bits & top_led_bits[i]) > 0;
|
||||
Lights::writeLight(RI_MGR, lights.at(light_mapping[i]), value ? 1.f : 0.f);
|
||||
}
|
||||
|
||||
// write RGB
|
||||
auto value_r = r / 127.f;
|
||||
auto value_g = g / 127.f;
|
||||
auto value_b = b / 127.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_R), value_r);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_G), value_g);
|
||||
Lights::writeLight(RI_MGR, lights.at(games::popn::Lights::TopLED_B), value_b);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_led_set_rgb_mask(int a1, char a2, char a3, long a4) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hdxs_update_control_status_buffer(int a1) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_hdxs_set_framing_err_packet_send_interval(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HDXSModule::HDXSModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HDXS", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HDXSModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_scroll);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_set_pattern);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_led_set_rgb_mask);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_hdxs_set_framing_err_packet_send_interval);
|
||||
}
|
13
acio/hdxs/hdxs.h
Normal file
13
acio/hdxs/hdxs.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HDXSModule : public ACIOModule {
|
||||
public:
|
||||
HDXSModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
122
acio/hgth/hgth.cpp
Normal file
122
acio/hgth/hgth.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "hgth.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "cfg/api.h"
|
||||
#include "games/rf3d/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// state
|
||||
static uint8_t STATUS_BUFFER[32] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_hgth_set_senddata(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hgth_update_recvdata() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Road Fighters 3D
|
||||
if (avs::game::is_model("JGT")) {
|
||||
|
||||
// keypad mirror fix
|
||||
acio::ICCA_FLIP_ROWS = true;
|
||||
|
||||
// variables
|
||||
uint16_t wheel = 0x7FFF;
|
||||
uint16_t accelerator = 0x00;
|
||||
uint16_t brake = 0x00;
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::rf3d::get_buttons();
|
||||
|
||||
// check buttons
|
||||
bool wheel_button = false;
|
||||
bool accelerate_button = false;
|
||||
bool brake_button = false;
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::WheelLeft))) {
|
||||
wheel -= 0x7FFF;
|
||||
wheel_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::WheelRight))) {
|
||||
wheel += 0x8000;
|
||||
wheel_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Accelerate))) {
|
||||
accelerator = 0xFFFF;
|
||||
accelerate_button = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::rf3d::Buttons::Brake))) {
|
||||
brake = 0xFFFF;
|
||||
brake_button = true;
|
||||
}
|
||||
|
||||
// analogs
|
||||
auto &analogs = games::rf3d::get_analogs();
|
||||
if (!wheel_button && analogs.at(games::rf3d::Analogs::Wheel).isSet()) {
|
||||
wheel = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Wheel)) * 0xFFFF);
|
||||
}
|
||||
if (!accelerate_button && analogs.at(games::rf3d::Analogs::Accelerate).isSet()) {
|
||||
accelerator = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Accelerate)) * 0xFFFF);
|
||||
}
|
||||
if (!brake_button && analogs.at(games::rf3d::Analogs::Brake).isSet()) {
|
||||
brake = (uint16_t) (Analogs::getState(RI_MGR, analogs.at(games::rf3d::Analogs::Brake)) * 0xFFFF);
|
||||
}
|
||||
|
||||
// write values
|
||||
*((uint16_t *) STATUS_BUFFER + 1) = wheel;
|
||||
*((uint16_t *) STATUS_BUFFER + 2) = accelerator;
|
||||
*((uint16_t *) STATUS_BUFFER + 3) = brake;
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_hgth_get_recvdata(void *buffer) {
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_hgth_directreq_set_handle_limit(char a1, int *a2) {
|
||||
*a2 = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_hgth_directreq_set_handle_limit_isfinished(int *a1) {
|
||||
*a1 = 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::HGTHModule::HGTHModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("HGTH", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::HGTHModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_set_senddata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_update_recvdata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_get_recvdata);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_directreq_set_handle_limit);
|
||||
ACIO_MODULE_HOOK(ac_io_hgth_directreq_set_handle_limit_isfinished);
|
||||
}
|
13
acio/hgth/hgth.h
Normal file
13
acio/hgth/hgth.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class HGTHModule : public ACIOModule {
|
||||
public:
|
||||
HGTHModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
375
acio/i36g/i36g.cpp
Normal file
375
acio/i36g/i36g.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
#include "i36g.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "avs/game.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/mga/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[88 * 2] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_i36g_add_coin(int a1, int a2, int a3) {
|
||||
|
||||
// not so sure we want to add coins
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_consume_coinstock(int a1, int a2, int a3) {
|
||||
eamuse_coin_consume_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_coin_blocker_close(int a1, int a2) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_coin_blocker_open(int a1, int a2) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_lamp_bright(uint32_t device, uint32_t lamp_bits, uint8_t brightness) {
|
||||
|
||||
// calculate value
|
||||
float value = (float) brightness / 255.f;
|
||||
|
||||
// get lights
|
||||
auto &lights = games::mga::get_lights();
|
||||
|
||||
// cabinet device
|
||||
if (device == 21) {
|
||||
if (lamp_bits & 1) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftR], value);
|
||||
}
|
||||
if (lamp_bits & 2) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftG], value);
|
||||
}
|
||||
if (lamp_bits & 4) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::LeftB], value);
|
||||
}
|
||||
if (lamp_bits & 8) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightR], value);
|
||||
}
|
||||
if (lamp_bits & 16) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightG], value);
|
||||
}
|
||||
if (lamp_bits & 32) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::RightB], value);
|
||||
}
|
||||
if (lamp_bits & 512) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::Start], value);
|
||||
}
|
||||
}
|
||||
|
||||
// gun device
|
||||
if (device == 22) {
|
||||
if (lamp_bits & 1) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunR], value);
|
||||
}
|
||||
if (lamp_bits & 2) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunG], value);
|
||||
}
|
||||
if (lamp_bits & 4) {
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunB], value);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_control_motor_power(int device, uint8_t strength) {
|
||||
|
||||
// gun device
|
||||
if (device == 22) {
|
||||
float value = (float) strength / 255.f;
|
||||
auto &lights = games::mga::get_lights();
|
||||
Lights::writeLight(RI_MGR, lights[games::mga::Lights::GunVibration], value);
|
||||
}
|
||||
|
||||
// success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_current_coinstock(int a1, int a2, int *a3) {
|
||||
|
||||
// get coinstock
|
||||
*a3 = eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_coin_input_wave_buffer(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_i36g_get_control_status_buffer(int device, void *buffer) {
|
||||
|
||||
// cabinet buffer
|
||||
if (device == 21) {
|
||||
memcpy(buffer, &STATUS_BUFFER[0], 88);
|
||||
}
|
||||
|
||||
// gun buffer
|
||||
if (device == 22) {
|
||||
memcpy(buffer, &STATUS_BUFFER[88], 88);
|
||||
}
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_softwareid(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_get_systemid(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_get_watchdog_status(int a1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_i36g_get_watchdog_time_min(int a1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_i36g_get_watchdog_time_now(int a1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_lock_coincounter(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_coin_input_wave(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_get_control_status(int a1, int *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_secplug_check_isfinished(int a1, int *a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check_softwareplug(int a1, char *a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_check_systemplug(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_req_secplug_missing_check(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_secplug_missing_check_isfinished(int a1, int *a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_volume_control(int a1, char a2, char a3, char a4, char a5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_req_volume_control_isfinished(int a1, int *ret_state) {
|
||||
*ret_state = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_set_cmdmode(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_set_framing_err_packet_send_interval(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_set_watchdog_time(int a1, short a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_i36g_unlock_coincounter(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36g_update_control_status_buffer(int node) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Metal Gear Arcade
|
||||
if (avs::game::is_model("I36")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::mga::get_buttons();
|
||||
|
||||
// cabinet device
|
||||
if (node == 21) {
|
||||
|
||||
// clear status buffer
|
||||
memset(&STATUS_BUFFER[0], 0, 88);
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Service))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 44);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Test))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 45);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::CoinMech))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 42);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Start))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[0], 124);
|
||||
}
|
||||
}
|
||||
|
||||
// gun device
|
||||
if (node == 22) {
|
||||
|
||||
// clear status buffer
|
||||
memset(&STATUS_BUFFER[88], 0, 88);
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::TriggerButton))
|
||||
|| (GetKeyState(VK_LBUTTON) & 0x100) != 0) { // mouse button
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 109);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::FrontTop))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 108);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::FrontBottom))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 106);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideLeft))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 107);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideRight))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 105);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SideLever))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 104);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::SwitchButton))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 125);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::mga::Buttons::Top))) {
|
||||
ARRAY_SETB(&STATUS_BUFFER[88], 124);
|
||||
}
|
||||
|
||||
// joy stick
|
||||
unsigned short joy_x = 0x7FFF;
|
||||
unsigned short joy_y = 0x7FFF;
|
||||
bool joy_x_pressed = false;
|
||||
bool joy_y_pressed = false;
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyForwards])) {
|
||||
joy_y -= 0x7FFF;
|
||||
joy_y_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyBackwards])) {
|
||||
joy_y += 0x7FFF;
|
||||
joy_y_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyLeft])) {
|
||||
joy_x -= 0x7FFF;
|
||||
joy_x_pressed = true;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons[games::mga::Buttons::JoyRight])) {
|
||||
joy_x += 0x7FFF;
|
||||
joy_x_pressed = true;
|
||||
}
|
||||
|
||||
// joy stick raw input
|
||||
auto &analogs = games::mga::get_analogs();
|
||||
if (!joy_x_pressed && analogs[games::mga::Analogs::JoyX].isSet()) {
|
||||
joy_x = (unsigned short) (Analogs::getState(RI_MGR, analogs[games::mga::Analogs::JoyX]) * 0xFFFF);
|
||||
}
|
||||
if (!joy_y_pressed && analogs[games::mga::Analogs::JoyY].isSet()) {
|
||||
joy_y = (unsigned short) (Analogs::getState(RI_MGR, analogs[games::mga::Analogs::JoyY]) * 0xFFFF);
|
||||
}
|
||||
|
||||
// save joy stick
|
||||
STATUS_BUFFER[88 + 42] = LOBYTE(joy_y);
|
||||
STATUS_BUFFER[88 + 43] = HIBYTE(joy_y);
|
||||
STATUS_BUFFER[88 + 44] = LOBYTE(joy_x);
|
||||
STATUS_BUFFER[88 + 45] = HIBYTE(joy_x);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_i36g_watchdog_off(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::I36GModule::I36GModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("I36G", module, hookMode) {
|
||||
this->status_buffer = &STATUS_BUFFER[0];
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::I36GModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_add_coin);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_control_motor_power);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_coin_input_wave_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_coin_input_wave);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_softwareplug);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_check_systemplug);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_cmdmode);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36g_watchdog_off);
|
||||
|
||||
// I36S links
|
||||
this->hook((void *) ac_io_i36g_update_control_status_buffer,
|
||||
"ac_io_i36s_update_control_status_buffer");
|
||||
this->hook((void *) ac_io_i36g_get_control_status_buffer,
|
||||
"ac_io_i36s_get_control_status_buffer");
|
||||
this->hook((void *) ac_io_i36g_set_cmdmode,
|
||||
"ac_io_i36s_set_cmdmode");
|
||||
}
|
13
acio/i36g/i36g.h
Normal file
13
acio/i36g/i36g.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36GModule : public ACIOModule {
|
||||
public:
|
||||
I36GModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
125
acio/i36i/i36i.cpp
Normal file
125
acio/i36i/i36i.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "i36i.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[48];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_i36i_ps3_controller_pwr_on() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_ps3_controller_pwr_off() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_usb_controller_bus_IO() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_usb_controller_bus_PC() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_req_get_usb_desc(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_i36i_req_get_usb_desc_isfinished(
|
||||
int a1, uint32_t *a2, int a3, uint32_t *out_size, uint8_t *in_data, unsigned int in_size) {
|
||||
|
||||
// DualShock 3 device descriptor
|
||||
static uint8_t DS3_DESC[] {
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0x68, 0x02, // idProduct 0x0268
|
||||
0x00, 0x01, // bcdDevice 1.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
// copy descriptor to buffer
|
||||
*out_size = MIN(sizeof(DS3_DESC), in_size);
|
||||
memcpy(in_data, DS3_DESC, *out_size);
|
||||
|
||||
// we apparently need this too
|
||||
*a2 = 3;
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
acio::I36IModule::I36IModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("I36I", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::I36IModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_ps3_controller_pwr_on);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_ps3_controller_pwr_off);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_usb_controller_bus_IO);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_usb_controller_bus_PC);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_req_get_usb_desc);
|
||||
ACIO_MODULE_HOOK(ac_io_i36i_req_get_usb_desc_isfinished);
|
||||
}
|
13
acio/i36i/i36i.h
Normal file
13
acio/i36i/i36i.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class I36IModule : public ACIOModule {
|
||||
public:
|
||||
I36IModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
472
acio/icca/icca.cpp
Normal file
472
acio/icca/icca.cpp
Normal file
@ -0,0 +1,472 @@
|
||||
#include <cmath>
|
||||
#include "icca.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/time.h"
|
||||
|
||||
// settings
|
||||
namespace acio {
|
||||
bool ICCA_FLIP_ROWS = false;
|
||||
bool ICCA_COMPAT_ACTIVE = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
struct ICCA_STATUS {
|
||||
uint8_t status_code;
|
||||
uint8_t solenoid;
|
||||
uint8_t front_sensor;
|
||||
uint8_t rear_sensor;
|
||||
uint8_t uid[8];
|
||||
int32_t error;
|
||||
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 {
|
||||
STEP,
|
||||
SLEEP,
|
||||
START,
|
||||
INIT,
|
||||
READY,
|
||||
GET_USERID,
|
||||
ACTIVE,
|
||||
EJECT,
|
||||
EJECT_CHECK,
|
||||
END,
|
||||
CLOSE_EJECT,
|
||||
CLOSE_E_CHK,
|
||||
CLOSE_END,
|
||||
ERR_GETUID = -2
|
||||
};
|
||||
struct ICCA_UNIT {
|
||||
struct ICCA_STATUS status {};
|
||||
enum ICCA_WORKFLOW state = STEP;
|
||||
bool card_cmd_pressed = false;
|
||||
bool card_in = false;
|
||||
double card_in_time = 0.0;
|
||||
char key_serial = 0;
|
||||
bool uid_skip = false;
|
||||
bool initialized = false;
|
||||
};
|
||||
static ICCA_UNIT ICCA_UNITS[2] {};
|
||||
static bool IS_LAST_CARD_FELICA = false;
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static double CARD_TIMEOUT = 2.0;
|
||||
|
||||
static inline int icca_get_active_count() {
|
||||
int active_count = 0;
|
||||
for (auto unit : ICCA_UNITS) {
|
||||
active_count += unit.initialized;
|
||||
}
|
||||
return active_count;
|
||||
}
|
||||
|
||||
static inline int icca_get_unit_id(int unit_id) {
|
||||
if (icca_get_active_count() < 2)
|
||||
return 1;
|
||||
else {
|
||||
if (unit_id > 1) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void update_card(int unit_id) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// get unit
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
// 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 (!unit->card_cmd_pressed) {
|
||||
unit->card_cmd_pressed = true;
|
||||
if (unit->state == GET_USERID || unit->state == CLOSE_EJECT || unit->state == STEP) {
|
||||
if (unit->uid_skip || eamuse_get_card(icca_get_active_count(), unit_id, unit->status.uid)) {
|
||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||
|
||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||
unit->status.error = 0;
|
||||
} else {
|
||||
unit->state = ERR_GETUID;
|
||||
memset(unit->status.uid, 0, 8);
|
||||
}
|
||||
unit->card_in = true;
|
||||
unit->card_in_time = get_performance_seconds();
|
||||
} else if (unit->state == EJECT_CHECK) {
|
||||
unit->state = SLEEP;
|
||||
unit->card_in = false;
|
||||
}
|
||||
} else {
|
||||
unit->state = acio::ICCA_COMPAT_ACTIVE ? START : ACTIVE;
|
||||
unit->status.error = 0;
|
||||
unit->card_in = true;
|
||||
unit->card_in_time = get_performance_seconds();
|
||||
}
|
||||
} else {
|
||||
unit->card_cmd_pressed = false;
|
||||
unit->state = CLOSE_EJECT;
|
||||
if (fabs(get_performance_seconds() - unit->card_in_time) > CARD_TIMEOUT) {
|
||||
unit->card_in = false;
|
||||
}
|
||||
}
|
||||
|
||||
// save state
|
||||
kb_insert_press_old[unit_id] = kb_insert_press;
|
||||
}
|
||||
|
||||
static bool KEYPAD_LAST[2][12];
|
||||
static uint32_t KEYPAD_EAMUSE_MAPPING[] = {
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
static uint32_t KEYPAD_KEY_CODES[] = {
|
||||
0x100,
|
||||
0x200,
|
||||
0x2000,
|
||||
2,
|
||||
0x400,
|
||||
0x4000,
|
||||
4,
|
||||
0x800,
|
||||
0x8000,
|
||||
8,
|
||||
1,
|
||||
0x1000
|
||||
};
|
||||
static uint32_t KEYPAD_KEY_CODE_NUMS[] = {
|
||||
0, 1, 5, 9, 2, 6, 10, 3, 7, 11, 8, 4
|
||||
};
|
||||
|
||||
static inline void keypad_update(int unit_id) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset unit
|
||||
struct ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
unit->status.key_level = 0;
|
||||
unit->status.error = 0;
|
||||
|
||||
// get eamu state
|
||||
int index = unit_id > 0 && icca_get_active_count() > 1 ? 1 : 0;
|
||||
uint16_t eamu_state = eamuse_get_keypad_state(index);
|
||||
|
||||
// iterate keypad
|
||||
for (int n = 0; n < 12; n++) {
|
||||
int i = n;
|
||||
|
||||
// flip 123 with 789
|
||||
if (acio::ICCA_FLIP_ROWS) {
|
||||
if (!n)
|
||||
i = 11;
|
||||
else if (n < 4)
|
||||
i = n + 6;
|
||||
else if (n > 6 && n < 10)
|
||||
i = n - 6;
|
||||
else if (n == 11)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
// check if pressed
|
||||
if ((eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])))
|
||||
{
|
||||
unit->status.key_level |= KEYPAD_KEY_CODES[i];
|
||||
|
||||
if (!KEYPAD_LAST[unit_id][i]) {
|
||||
unit->status.key_edge = KEYPAD_KEY_CODES[n] << 16;
|
||||
unit->status.key_edge |= 0x80 | (unit->key_serial << 4) | KEYPAD_KEY_CODE_NUMS[n];
|
||||
|
||||
unit->key_serial += 1;
|
||||
unit->key_serial &= 0x07;
|
||||
}
|
||||
KEYPAD_LAST[unit_id][i] = true;
|
||||
} else {
|
||||
unit->status.key_edge &= ~(KEYPAD_KEY_CODES[n] << 16);
|
||||
|
||||
KEYPAD_LAST[unit_id][i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_icca_cardunit_init(int unit_id) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
|
||||
// dirty workaround code
|
||||
if (icca_get_active_count() < 1)
|
||||
ICCA_UNITS[unit_id].initialized = true;
|
||||
else {
|
||||
ICCA_UNITS[0].initialized = true;
|
||||
ICCA_UNITS[1].initialized = true;
|
||||
}
|
||||
|
||||
// initial poll
|
||||
eamuse_get_keypad_state(unit_id);
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_cardunit_init_isfinished(int unit_id, DWORD *status) {
|
||||
*status = READY;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_crypt_init(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_device_control_iccard_power_supply_off(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_device_control_iccard_power_supply_on(int unit_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_device_control_isfinished(int unit_id, DWORD *a2) {
|
||||
if (a2 && avs::game::is_model("KFC")) {
|
||||
*a2 = 6;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_get_keep_alive_error(int unit_id, DWORD *a2) {
|
||||
*a2 = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_status(void *a1, void *a2) {
|
||||
|
||||
// Metal Gear Arcade and Charge Machine had the args swapped so we need to check for valid pointers!
|
||||
if (reinterpret_cast<uintptr_t>(a2) > 2) {
|
||||
std::swap(a1, a2);
|
||||
|
||||
// honestly this could be used to detect if compat mode should be active
|
||||
// but we are too lazy to check if all games still work with this change
|
||||
//acio::ICCA_COMPAT_ACTIVE = true;
|
||||
}
|
||||
|
||||
// and best just leave this casting mess alone unless something is wrong with it.
|
||||
// long long is required because casting to int loses precision on 64-bit
|
||||
void *status = a1;
|
||||
int unit_id = static_cast<int>(reinterpret_cast<long long>(a2));
|
||||
|
||||
// update state
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
keypad_update(unit_id);
|
||||
update_card(unit_id);
|
||||
|
||||
// copy state to output buffer
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
unit->status.status_code = unit->state;
|
||||
memcpy(status, &unit->status, sizeof(struct ICCA_STATUS));
|
||||
|
||||
// funny workaround
|
||||
if (acio::ICCA_COMPAT_ACTIVE) {
|
||||
if (avs::game::is_model("LA9")) {
|
||||
auto p = (ICCA_STATUS*) status;
|
||||
|
||||
ICCA_STATUS_LA9 p_la9;
|
||||
p_la9.status_code = unit->state;
|
||||
p_la9.card_in = unit->card_in;
|
||||
memcpy(p_la9.uid, p->uid, sizeof(p_la9.uid));
|
||||
p_la9.error = p->error;
|
||||
memcpy(p_la9.uid2, p->uid, sizeof(p_la9.uid));
|
||||
|
||||
memcpy(status, &p_la9, sizeof(ICCA_STATUS_LA9));
|
||||
} else {
|
||||
// the struct is different (28 bytes instead of 24) but nobody ain't got time for that
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_uid(int unit_id, char *card) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
// copy card
|
||||
memcpy(card, unit->status.uid, 8);
|
||||
|
||||
// set felica flag
|
||||
IS_LAST_CARD_FELICA = is_card_uid_felica(unit->status.uid);
|
||||
|
||||
// check for error
|
||||
return unit->state != ERR_GETUID;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_get_uid_felica(int unit_id, char *card) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
|
||||
// copy card
|
||||
memcpy(card, unit->status.uid, 8);
|
||||
|
||||
// set felica flag
|
||||
bool felica = is_card_uid_felica(unit->status.uid);
|
||||
card[8] = (char) (felica ? 1 : 0);
|
||||
IS_LAST_CARD_FELICA = felica;
|
||||
|
||||
// check for error
|
||||
return unit->state != ERR_GETUID;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_is_felica() {
|
||||
return IS_LAST_CARD_FELICA;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_req_uid(int unit_id) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
unit->state = GET_USERID;
|
||||
|
||||
update_card(unit_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_req_uid_isfinished(int unit_id, DWORD *read_state) {
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
if (unit->card_in) {
|
||||
if (fabs(get_performance_seconds() - unit->card_in_time) < CARD_TIMEOUT) {
|
||||
unit->state = END;
|
||||
} else {
|
||||
unit->state = ERR_GETUID;
|
||||
}
|
||||
unit->card_in = false;
|
||||
}
|
||||
*read_state = unit->state;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_send_keep_alive_packet(int a1, int a2, int a3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_icca_workflow(int workflow, int unit_id) {
|
||||
|
||||
unit_id = icca_get_unit_id(unit_id);
|
||||
ICCA_UNIT *unit = &ICCA_UNITS[unit_id];
|
||||
switch (workflow) {
|
||||
case STEP:
|
||||
if (avs::game::is_model("JDZ"))
|
||||
unit->state = SLEEP;
|
||||
else
|
||||
unit->state = STEP;
|
||||
break;
|
||||
case SLEEP:
|
||||
unit->state = SLEEP;
|
||||
break;
|
||||
case INIT:
|
||||
unit->state = READY;
|
||||
break;
|
||||
case START:
|
||||
if (unit->card_in)
|
||||
unit->state = ACTIVE;
|
||||
else
|
||||
unit->state = READY;
|
||||
break;
|
||||
case EJECT:
|
||||
unit->card_in = false;
|
||||
break;
|
||||
case CLOSE_EJECT:
|
||||
unit->state = unit->card_in ? EJECT_CHECK : SLEEP;
|
||||
break;
|
||||
case CLOSE_END:
|
||||
unit->state = SLEEP;
|
||||
break;
|
||||
case GET_USERID:
|
||||
unit->state = GET_USERID;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return unit->state;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_icca_req_status(int a1, char a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_icca_req_status_isfinished(int a1, int *a2) {
|
||||
*a2 = 11;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::ICCAModule::ICCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("ICCA", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &ICCA_UNITS[0];
|
||||
this->status_buffer_size = sizeof(ICCA_UNITS);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::ICCAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_cardunit_init_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_crypt_init);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_iccard_power_supply_off);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_iccard_power_supply_on);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_device_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_keep_alive_error);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_uid);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_get_uid_felica);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_is_felica);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_uid);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_uid_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_send_keep_alive_packet);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_workflow);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_status);
|
||||
ACIO_MODULE_HOOK(ac_io_icca_req_status_isfinished);
|
||||
}
|
21
acio/icca/icca.h
Normal file
21
acio/icca/icca.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
// settings
|
||||
extern bool ICCA_FLIP_ROWS;
|
||||
extern bool ICCA_COMPAT_ACTIVE;
|
||||
|
||||
class ICCAModule : public ACIOModule {
|
||||
public:
|
||||
ICCAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool is_card_uid_felica(uint8_t *uid) {
|
||||
return uid[0] != 0xE0 && uid[1] != 0x04;
|
||||
}
|
158
acio/j32d/j32d.cpp
Normal file
158
acio/j32d/j32d.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
#include "j32d.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ftt/io.h"
|
||||
#include "games/scotto/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint32_t STATUS_BUFFER[20] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static uint32_t STATUS_BUFFER_COUNTER = 1;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_j32d_get_control_status_buffer(size_t a1, void* buffer, int a3) {
|
||||
|
||||
// set counter
|
||||
STATUS_BUFFER[14] = STATUS_BUFFER_COUNTER++;
|
||||
|
||||
// copy buffer
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool __cdecl ac_io_j32d_update_control_status_buffer(size_t a1) {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// FutureTomTom
|
||||
if (avs::game::is_model("MMD")) {
|
||||
|
||||
// process buttons
|
||||
auto &buttons = games::ftt::get_buttons();
|
||||
float pad1_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad1));
|
||||
float pad2_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad2));
|
||||
float pad3_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad3));
|
||||
float pad4_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::ftt::Buttons::Pad4));
|
||||
// FIXME(felix): this logic seems wrong for analog handling but correct for digital inputs
|
||||
if (pad1_vel > 0.f) {
|
||||
STATUS_BUFFER[6] = (int) (51.f * pad1_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad2))) {
|
||||
STATUS_BUFFER[7] = (int) (51.f * pad2_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad3))) {
|
||||
STATUS_BUFFER[8] = (int) (51.f * pad3_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::ftt::Buttons::Pad4))) {
|
||||
STATUS_BUFFER[9] = (int) (51.f * pad4_vel + 0.5f);
|
||||
}
|
||||
|
||||
// process analogs
|
||||
auto &analogs = games::ftt::get_analogs();
|
||||
auto pad1_analog = analogs.at(games::ftt::Analogs::Pad1);
|
||||
auto pad2_analog = analogs.at(games::ftt::Analogs::Pad2);
|
||||
auto pad3_analog = analogs.at(games::ftt::Analogs::Pad3);
|
||||
auto pad4_analog = analogs.at(games::ftt::Analogs::Pad4);
|
||||
if (pad1_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad1_analog) + 0.5f);
|
||||
STATUS_BUFFER[6] = MAX(STATUS_BUFFER[6], val);
|
||||
}
|
||||
if (pad2_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad2_analog) + 0.5f);
|
||||
STATUS_BUFFER[7] = MAX(STATUS_BUFFER[7], val);
|
||||
}
|
||||
if (pad3_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad3_analog) + 0.5f);
|
||||
STATUS_BUFFER[8] = MAX(STATUS_BUFFER[8], val);
|
||||
}
|
||||
if (pad4_analog.isSet()) {
|
||||
auto val = (uint32_t) (51.f * Analogs::getState(RI_MGR, pad4_analog) + 0.5f);
|
||||
STATUS_BUFFER[9] = MAX(STATUS_BUFFER[9], val);
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::scotto::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Cup1))) {
|
||||
STATUS_BUFFER[5] |= 0x1;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Cup2))) {
|
||||
STATUS_BUFFER[5] |= 0x2;
|
||||
}
|
||||
|
||||
// process button emulation for pads
|
||||
float first_pad_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::FirstPad));
|
||||
float pad_a_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadA));
|
||||
float pad_b_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadB));
|
||||
float pad_c_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadC));
|
||||
float pad_d_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadD));
|
||||
float pad_e_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadE));
|
||||
float pad_f_vel = Buttons::getVelocity(RI_MGR, buttons.at(games::scotto::Buttons::PadF));
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::FirstPad))) {
|
||||
STATUS_BUFFER[6] = (int) (191.f * first_pad_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadA))) {
|
||||
STATUS_BUFFER[7] = (int) (51.f * pad_a_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadB))) {
|
||||
STATUS_BUFFER[8] = (int) (51.f * pad_b_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadC))) {
|
||||
STATUS_BUFFER[9] = (int) (51.f * pad_c_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadD))) {
|
||||
STATUS_BUFFER[10] = (int) (51.f * pad_d_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadE))) {
|
||||
STATUS_BUFFER[11] = (int) (51.f * pad_e_vel + 0.5f);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::PadF))) {
|
||||
STATUS_BUFFER[12] = (int) (51.f * pad_f_vel + 0.5f);
|
||||
}
|
||||
|
||||
// TODO(felix): analogs
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::J32DModule::J32DModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("J32D", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &STATUS_BUFFER[0];
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::J32DModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_j32d_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_j32d_update_control_status_buffer);
|
||||
}
|
13
acio/j32d/j32d.h
Normal file
13
acio/j32d/j32d.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class J32DModule : public ACIOModule {
|
||||
public:
|
||||
J32DModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
484
acio/kfca/kfca.cpp
Normal file
484
acio/kfca/kfca.cpp
Normal file
@ -0,0 +1,484 @@
|
||||
#include "kfca.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/bs/io.h"
|
||||
#include "games/nost/io.h"
|
||||
#include "games/scotto/io.h"
|
||||
#include "games/sdvx/sdvx.h"
|
||||
#include "games/sdvx/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// globals
|
||||
uint8_t KFCA_VOL_SOUND = 96;
|
||||
uint8_t KFCA_VOL_HEADPHONE = 96;
|
||||
uint8_t KFCA_VOL_EXTERNAL = 96;
|
||||
uint8_t KFCA_VOL_WOOFER = 96;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[64] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
static unsigned int KFCA_VOLL = 0;
|
||||
static unsigned int KFCA_VOLR = 0;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static int __cdecl ac_io_kfca_control_button_led(unsigned int button, bool state) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] = {
|
||||
games::sdvx::Lights::BT_A,
|
||||
games::sdvx::Lights::BT_B,
|
||||
games::sdvx::Lights::BT_C,
|
||||
games::sdvx::Lights::BT_D,
|
||||
games::sdvx::Lights::FX_L,
|
||||
games::sdvx::Lights::FX_R,
|
||||
games::sdvx::Lights::START,
|
||||
games::sdvx::Lights::GENERATOR_B,
|
||||
};
|
||||
|
||||
// check if button is mapped
|
||||
if (button < 8) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// write light
|
||||
float value = state ? 1.f : 0.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[button]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] = {
|
||||
games::scotto::Lights::PAD_F_B,
|
||||
games::scotto::Lights::PAD_E_R,
|
||||
games::scotto::Lights::PAD_E_B,
|
||||
~0u,
|
||||
~0u,
|
||||
~0u,
|
||||
games::scotto::Lights::PAD_F_R,
|
||||
games::scotto::Lights::BUTTON,
|
||||
};
|
||||
|
||||
// check if button is mapped
|
||||
if (button < std::size(mapping) && button[mapping] != ~0u) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::scotto::get_lights();
|
||||
|
||||
// write light
|
||||
float value = state ? 1.f : 0.f;
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[button]), value);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
|
||||
// control mapping
|
||||
static const size_t mapping[] {
|
||||
games::sdvx::Lights::WING_LEFT_UP_R,
|
||||
games::sdvx::Lights::WING_LEFT_UP_G,
|
||||
games::sdvx::Lights::WING_LEFT_UP_B,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_R,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_G,
|
||||
games::sdvx::Lights::WING_RIGHT_UP_B,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_R,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_G,
|
||||
games::sdvx::Lights::WING_LEFT_LOW_B,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_R,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_G,
|
||||
games::sdvx::Lights::WING_RIGHT_LOW_B,
|
||||
games::sdvx::Lights::WOOFER_R,
|
||||
games::sdvx::Lights::WOOFER_G,
|
||||
games::sdvx::Lights::WOOFER_B,
|
||||
games::sdvx::Lights::CONTROLLER_R,
|
||||
games::sdvx::Lights::CONTROLLER_G,
|
||||
games::sdvx::Lights::CONTROLLER_B,
|
||||
games::sdvx::Lights::GENERATOR_R,
|
||||
games::sdvx::Lights::GENERATOR_G,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BeatStream
|
||||
if (avs::game::is_model("NBT")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bs::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
~0u, ~0u, ~0u,
|
||||
games::bs::Lights::RightR,
|
||||
games::bs::Lights::RightG,
|
||||
games::bs::Lights::RightB,
|
||||
games::bs::Lights::LeftR,
|
||||
games::bs::Lights::LeftG,
|
||||
games::bs::Lights::LeftB,
|
||||
games::bs::Lights::BottomR,
|
||||
games::bs::Lights::BottomG,
|
||||
games::bs::Lights::BottomB,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] != ~0u && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::nost::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
~0u, ~0u, ~0u,
|
||||
games::nost::Lights::TitleR,
|
||||
games::nost::Lights::TitleG,
|
||||
games::nost::Lights::TitleB,
|
||||
~0u, ~0u, ~0u,
|
||||
games::nost::Lights::BottomR,
|
||||
games::nost::Lights::BottomG,
|
||||
games::nost::Lights::BottomB,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] != ~0u && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::scotto::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::scotto::Lights::CUP_R,
|
||||
games::scotto::Lights::CUP_G,
|
||||
games::scotto::Lights::CUP_B,
|
||||
games::scotto::Lights::PAD_A_R,
|
||||
games::scotto::Lights::PAD_A_G,
|
||||
games::scotto::Lights::PAD_A_B,
|
||||
games::scotto::Lights::PAD_B_R,
|
||||
games::scotto::Lights::PAD_B_G,
|
||||
games::scotto::Lights::PAD_B_B,
|
||||
games::scotto::Lights::PAD_C_R,
|
||||
games::scotto::Lights::PAD_C_G,
|
||||
games::scotto::Lights::PAD_C_B,
|
||||
games::scotto::Lights::PAD_D_R,
|
||||
games::scotto::Lights::PAD_D_G,
|
||||
games::scotto::Lights::PAD_D_B,
|
||||
games::scotto::Lights::FIRST_PAD_R,
|
||||
games::scotto::Lights::FIRST_PAD_G,
|
||||
games::scotto::Lights::FIRST_PAD_B,
|
||||
games::scotto::Lights::PAD_F_G,
|
||||
games::scotto::Lights::PAD_E_G,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_current_coinstock(int a1, DWORD *a2) {
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_kfca_get_control_status_buffer(void *target_buffer) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(target_buffer, STATUS_BUFFER, 64);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_kfca_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_kfca_req_volume_control(
|
||||
uint8_t vol_sound, uint8_t vol_headphone, uint8_t vol_external, uint8_t vol_woofer) {
|
||||
|
||||
// update globals
|
||||
KFCA_VOL_SOUND = vol_sound;
|
||||
KFCA_VOL_HEADPHONE = vol_headphone;
|
||||
KFCA_VOL_EXTERNAL = vol_external;
|
||||
KFCA_VOL_WOOFER = vol_woofer;
|
||||
|
||||
// Sound Voltex
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::sdvx::get_lights();
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_SOUND],
|
||||
(100 - vol_sound) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_HEADPHONE],
|
||||
(100 - vol_headphone) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_EXTERNAL],
|
||||
(100 - vol_external) / 100.f);
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::sdvx::Lights::VOLUME_WOOFER],
|
||||
(100 - vol_woofer) / 100.f);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_kfca_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_kfca_update_control_status_buffer() {
|
||||
static const int input_offset = 4;
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, 64);
|
||||
|
||||
// SDVX
|
||||
if (avs::game::is_model("KFC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::sdvx::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Start))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x08;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_A))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_B))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x02;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_C))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x01;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::BT_D))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_L))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::FX_R))) {
|
||||
STATUS_BUFFER[input_offset + 11] |= 0x08;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::Headphone))) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x20;
|
||||
}
|
||||
|
||||
// volume left
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Left))) {
|
||||
KFCA_VOLL = (KFCA_VOLL - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_L_Right))) {
|
||||
KFCA_VOLL = (KFCA_VOLL + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// volume right
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Left))) {
|
||||
KFCA_VOLR = (KFCA_VOLR - games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::sdvx::Buttons::VOL_R_Right))) {
|
||||
KFCA_VOLR = (KFCA_VOLR + games::sdvx::DIGITAL_KNOB_SENS) & 1023;
|
||||
}
|
||||
|
||||
// update volumes
|
||||
auto &analogs = games::sdvx::get_analogs();
|
||||
auto vol_left = KFCA_VOLL;
|
||||
auto vol_right = KFCA_VOLR;
|
||||
if (analogs.at(0).isSet() || analogs.at(1).isSet()) {
|
||||
vol_left += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_L)) * 1023.99f);
|
||||
vol_right += (unsigned int) (Analogs::getState(RI_MGR,
|
||||
analogs.at(games::sdvx::Analogs::VOL_R)) * 1023.99f);
|
||||
}
|
||||
|
||||
// proper loops
|
||||
vol_left %= 1024;
|
||||
vol_right %= 1024;
|
||||
|
||||
// save volumes in buffer
|
||||
STATUS_BUFFER[input_offset + 16 + 0] |= (unsigned char) ((vol_left << 6) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 1] |= (unsigned char) ((vol_left >> 2) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 2] |= (unsigned char) ((vol_right << 6) & 0xFF);
|
||||
STATUS_BUFFER[input_offset + 16 + 3] |= (unsigned char) ((vol_right >> 2) & 0xFF);
|
||||
}
|
||||
|
||||
// Beatstream
|
||||
if (avs::game::is_model("NBT")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::bs::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bs::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::nost::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::Service))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::Test))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::nost::Buttons::CoinMech))) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// Scotto
|
||||
if (avs::game::is_model("NSC")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::scotto::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::CoinMech)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 1] |= 0x04;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Start)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x20;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Up)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x10;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::scotto::Buttons::Down)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[input_offset + 9] |= 0x08;
|
||||
}
|
||||
|
||||
// the code also checks `input_offset + 9` for 0x01 but that does not trigger any response
|
||||
// in the "I/O CHECK" scene
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_kfca_watchdog_off() {
|
||||
}
|
||||
|
||||
// yes this is spelled "marge" instead of "merge"
|
||||
static int __cdecl ac_io_kfca_set_status_marge_func(void *cb) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::KFCAModule::KFCAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("KFCA", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::KFCAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_button_led);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_kfca_set_status_marge_func);
|
||||
}
|
18
acio/kfca/kfca.h
Normal file
18
acio/kfca/kfca.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
extern uint8_t KFCA_VOL_SOUND;
|
||||
extern uint8_t KFCA_VOL_HEADPHONE;
|
||||
extern uint8_t KFCA_VOL_EXTERNAL;
|
||||
extern uint8_t KFCA_VOL_WOOFER;
|
||||
|
||||
class KFCAModule : public ACIOModule {
|
||||
public:
|
||||
KFCAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
238
acio/klpa/klpa.cpp
Normal file
238
acio/klpa/klpa.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
#include "klpa.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/loveplus/io.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
static uint8_t STATUS_BUFFER[48];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
static const size_t LOVEPLUS_LIGHTS_MAPPING[] = {
|
||||
games::loveplus::Lights::Red,
|
||||
games::loveplus::Lights::Green,
|
||||
games::loveplus::Lights::Blue,
|
||||
SIZE_MAX,
|
||||
games::loveplus::Lights::Right,
|
||||
games::loveplus::Lights::Left,
|
||||
};
|
||||
|
||||
static char __cdecl ac_io_klpa_consume_coinstock(int a1, DWORD *a2) {
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_coin_blocker_close(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_coin_blocker_open(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_led_off(size_t index) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_control_led_on(size_t index) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_create_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_klpa_current_coinstock(int a1, DWORD *a2) {
|
||||
|
||||
// check bounds
|
||||
if (a1 < 0 || a1 >= 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*a2 = (DWORD) eamuse_coin_get_stock();
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void* __cdecl ac_io_klpa_get_control_status_buffer(void *a1) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(a1, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_klpa_get_io_command_mode(void *a1) {
|
||||
memset(a1, 0, 4);
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_led_reset() {
|
||||
if (avs::game::is_model("KLP")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
for (const auto &mapping : LOVEPLUS_LIGHTS_MAPPING) {
|
||||
if (mapping != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping), 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_io_command_mode(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_io_command_mode_is_finished(uint8_t *a1) {
|
||||
*a1 = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_klpa_set_led_bright(size_t index, uint8_t brightness) {
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP") && index < std::size(LOVEPLUS_LIGHTS_MAPPING)) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::loveplus::get_lights();
|
||||
|
||||
if (LOVEPLUS_LIGHTS_MAPPING[index] != SIZE_MAX) {
|
||||
Lights::writeLight(RI_MGR, lights.at(LOVEPLUS_LIGHTS_MAPPING[index]), brightness / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_sound_mute(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_sound_mute_is_finished(int a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_klpa_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_klpa_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// reset buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// LovePlus
|
||||
if (avs::game::is_model("KLP")) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::loveplus::get_buttons();
|
||||
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Test)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[5] |= 1 << 5;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Service)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[5] |= 1 << 4;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Left)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[12] |= 1 << 6;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::loveplus::Buttons::Right)) == Buttons::State::BUTTON_PRESSED) {
|
||||
STATUS_BUFFER[12] |= 1 << 7;
|
||||
}
|
||||
|
||||
// x[9] & 0x3F) = volume output level?
|
||||
// x[11] & 0x3F) = volume output level?
|
||||
// x[12] |= (1 << 4) = headphone jack
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::KLPAModule::KLPAModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("KLPA", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::KLPAModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_coin_blocker_close);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_coin_blocker_open);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_led_off);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_control_led_on);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_get_io_command_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_led_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_io_command_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_io_command_mode_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_sound_mute);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_sound_mute_is_finished);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_klpa_update_control_status_buffer);
|
||||
}
|
13
acio/klpa/klpa.h
Normal file
13
acio/klpa/klpa.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class KLPAModule : public ACIOModule {
|
||||
public:
|
||||
KLPAModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
113
acio/la9a/la9a.cpp
Normal file
113
acio/la9a/la9a.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
#include "la9a.h"
|
||||
|
||||
#include "games/pcm/io.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "touch/touch.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace acio {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct la9a_control_status {
|
||||
uint8_t p1 : 6;
|
||||
uint8_t service_button : 1;
|
||||
uint8_t test_button : 1;
|
||||
uint8_t p2[9];
|
||||
uint8_t lcd_counter;
|
||||
uint8_t p3[5];
|
||||
uint16_t touch_x;
|
||||
uint16_t touch_y;
|
||||
uint16_t touch_z;
|
||||
uint8_t p4[26];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static struct la9a_control_status CONTROL_STATUS {};
|
||||
static bool TOUCH_ATTACHED = false;
|
||||
|
||||
static bool __cdecl ac_io_la9a_set_error_message(int, unsigned int, int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_la9a_update_control_status_buffer() {
|
||||
CONTROL_STATUS.touch_z = 0xFF;
|
||||
CONTROL_STATUS.test_button = 0;
|
||||
CONTROL_STATUS.service_button = 0;
|
||||
|
||||
// attach touch handler on the first call to this function
|
||||
if (!TOUCH_ATTACHED) {
|
||||
log_misc("la9a", "attach touch handler");
|
||||
|
||||
HWND hwnd = FindWindowBeginsWith("LA9");
|
||||
if (!hwnd) {
|
||||
log_fatal("la9a", "LA9 window not found");
|
||||
}
|
||||
touch_create_wnd(hwnd);
|
||||
graphics_hook_window(hwnd, nullptr);
|
||||
|
||||
if (GRAPHICS_SHOW_CURSOR) {
|
||||
ShowCursor(1);
|
||||
}
|
||||
|
||||
TOUCH_ATTACHED = true;
|
||||
}
|
||||
|
||||
// update touch
|
||||
std::vector<TouchPoint> touch_points;
|
||||
touch_get_points(touch_points);
|
||||
|
||||
if (!touch_points.empty()) {
|
||||
auto &touch_point = touch_points[0];
|
||||
|
||||
// TODO: `x` and `y` should be clamped [0, std::numeric_limits<uint16_t>::max())
|
||||
CONTROL_STATUS.touch_x = static_cast<uint16_t>(touch_point.x);
|
||||
CONTROL_STATUS.touch_y = static_cast<uint16_t>(touch_point.y);
|
||||
CONTROL_STATUS.touch_z = 0;
|
||||
}
|
||||
CONTROL_STATUS.lcd_counter++;
|
||||
|
||||
// update buttons
|
||||
auto &buttons = games::pcm::get_buttons();
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::pcm::Buttons::Test])) {
|
||||
CONTROL_STATUS.test_button = 1;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::pcm::Buttons::Service])) {
|
||||
CONTROL_STATUS.service_button = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_la9a_update_counter(int, int) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cdecl ac_io_la9a_update_lcd(int) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_la9a_get_control_status_buffer(struct la9a_control_status *control_status) {
|
||||
*control_status = CONTROL_STATUS;
|
||||
}
|
||||
|
||||
LA9AModule::LA9AModule(HMODULE module, HookMode hookMode) : ACIOModule("LA9A", module, hookMode) {
|
||||
//this->status_buffer = STATUS_BUFFER;
|
||||
//this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
//this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void LA9AModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_set_error_message);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_lcd);
|
||||
ACIO_MODULE_HOOK(ac_io_la9a_update_counter);
|
||||
}
|
||||
}
|
13
acio/la9a/la9a.h
Normal file
13
acio/la9a/la9a.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class LA9AModule : public ACIOModule {
|
||||
public:
|
||||
LA9AModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
209
acio/mdxf/mdxf.cpp
Normal file
209
acio/mdxf/mdxf.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
#include "mdxf.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "games/ddr/io.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// constants
|
||||
const size_t STATUS_BUFFER_SIZE = 32;
|
||||
|
||||
// static stuff
|
||||
static uint8_t COUNTER = 0;
|
||||
|
||||
// buffers
|
||||
#pragma pack(push, 1)
|
||||
static struct {
|
||||
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
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_mdxf_get_control_status_buffer(int node, void *buffer, uint8_t a3, uint8_t a4) {
|
||||
|
||||
// Dance Dance Revolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// get buffer index
|
||||
auto i = (COUNTER + a3) % std::size(BUFFERS.STATUS_BUFFER_P1);
|
||||
|
||||
// copy buffer
|
||||
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
|
||||
memset(buffer, 0, STATUS_BUFFER_SIZE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
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_P1);
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
uint8_t *buffer = nullptr;
|
||||
switch (node) {
|
||||
case 17:
|
||||
case 25:
|
||||
buffer = BUFFERS.STATUS_BUFFER_P1[COUNTER];
|
||||
break;
|
||||
case 18:
|
||||
case 26:
|
||||
buffer = BUFFERS.STATUS_BUFFER_P2[COUNTER];
|
||||
break;
|
||||
default:
|
||||
|
||||
// return failure on unknown node
|
||||
return false;
|
||||
}
|
||||
memset(buffer, 0, STATUS_BUFFER_SIZE);
|
||||
|
||||
// Dance Dance Revolution
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// Sensor Map (LDUR):
|
||||
// FOOT DOWN = bit 32-35 = byte 4, bit 0-3
|
||||
// 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_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_p2[] = {
|
||||
games::ddr::Buttons::P2_PANEL_UP,
|
||||
games::ddr::Buttons::P2_PANEL_DOWN,
|
||||
games::ddr::Buttons::P2_PANEL_LEFT,
|
||||
games::ddr::Buttons::P2_PANEL_RIGHT,
|
||||
};
|
||||
|
||||
// decide on button map
|
||||
const size_t *button_map = nullptr;
|
||||
switch (node) {
|
||||
case 17:
|
||||
case 25:
|
||||
button_map = &buttons_p1[0];
|
||||
break;
|
||||
case 18:
|
||||
case 26:
|
||||
button_map = &buttons_p2[0];
|
||||
break;
|
||||
}
|
||||
|
||||
*(uint64_t*)&buffer[0x18] = arkGetTickTime64();
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::ddr::get_buttons();
|
||||
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[0]))) {
|
||||
buffer[4] |= 0xF0;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[1]))) {
|
||||
buffer[4] |= 0x0F;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[2]))) {
|
||||
buffer[5] |= 0xF0;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons.at(button_map[3]))) {
|
||||
buffer[5] |= 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::MDXFModule::MDXFModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("MDXF", module, hookMode) {
|
||||
this->status_buffer = (uint8_t*) &BUFFERS;
|
||||
this->status_buffer_size = sizeof(BUFFERS);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::MDXFModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_set_output_level);
|
||||
ACIO_MODULE_HOOK(ac_io_mdxf_update_control_status_buffer);
|
||||
}
|
13
acio/mdxf/mdxf.h
Normal file
13
acio/mdxf/mdxf.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class MDXFModule : public ACIOModule {
|
||||
public:
|
||||
MDXFModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
39
acio/module.cpp
Normal file
39
acio/module.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "module.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/libutils.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
const char *acio::hook_mode_str(acio::HookMode hook_mode) {
|
||||
switch (hook_mode) {
|
||||
case HookMode::INLINE:
|
||||
return "Inline";
|
||||
case HookMode::IAT:
|
||||
return "IAT";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook functions depending on the specified mode.
|
||||
* We don't care about errors here since different versions of libacio contain different feature sets,
|
||||
* which means that not all hooks must/can succeed.
|
||||
*/
|
||||
void acio::ACIOModule::hook(void *func, const char *func_name) {
|
||||
switch (this->hook_mode) {
|
||||
case HookMode::INLINE:
|
||||
detour::inline_hook(func, libutils::try_proc(this->module, func_name));
|
||||
break;
|
||||
case HookMode::IAT:
|
||||
detour::iat_try(func_name, func);
|
||||
break;
|
||||
default:
|
||||
log_warning("acio", "unable to hook using mode {}", hook_mode_str(this->hook_mode));
|
||||
}
|
||||
}
|
||||
|
||||
void acio::ACIOModule::attach() {
|
||||
log_info("acio", "module attach: {} {}", this->name, hook_mode_str(this->hook_mode));
|
||||
this->attached = true;
|
||||
}
|
56
acio/module.h
Normal file
56
acio/module.h
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
// macro for lazy typing of hooks
|
||||
#define ACIO_MODULE_HOOK(f) this->hook(reinterpret_cast<void *>(f), #f)
|
||||
|
||||
namespace acio {
|
||||
|
||||
/*
|
||||
* Hook Modes
|
||||
* Since some versions can't handle inline hooking
|
||||
*/
|
||||
enum class HookMode {
|
||||
INLINE,
|
||||
IAT
|
||||
};
|
||||
|
||||
// this makes logging easier
|
||||
const char *hook_mode_str(HookMode hook_mode);
|
||||
|
||||
/*
|
||||
* The ACIO module itself
|
||||
* Inherit this for extending our libacio implementation
|
||||
*/
|
||||
class ACIOModule {
|
||||
protected:
|
||||
|
||||
// the magic
|
||||
void hook(void* func, const char *func_name);
|
||||
|
||||
public:
|
||||
|
||||
ACIOModule(std::string name, HMODULE module, HookMode hook_mode) :
|
||||
name(std::move(name)),
|
||||
module(module),
|
||||
hook_mode(hook_mode) {};
|
||||
|
||||
virtual ~ACIOModule() = default;
|
||||
|
||||
virtual void attach();
|
||||
|
||||
// settings
|
||||
std::string name;
|
||||
HMODULE module;
|
||||
HookMode hook_mode;
|
||||
bool attached = false;
|
||||
|
||||
// buffer state (optional)
|
||||
uint8_t *status_buffer = nullptr;
|
||||
size_t status_buffer_size = 0;
|
||||
bool *status_buffer_freeze = nullptr;
|
||||
};
|
||||
}
|
64
acio/nddb/nddb.cpp
Normal file
64
acio/nddb/nddb.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "nddb.h"
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[4] {};
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static void __cdecl ac_io_nddb_control_pwm(int a1, int a2) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_control_pwm({}, {})", a1, a2);
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_nddb_control_solenoide(int a1, int a2) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_control_solenoide({}, {})", a1, a2);
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_destroy_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_nddb_get_control_status_buffer(void *buffer) {
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_req_solenoide_control(uint8_t *buffer) {
|
||||
log_misc("acio::nddb", "ac_io_nddb_req_solenoide_control");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_nddb_update_control_status_buffer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::NDDBModule::NDDBModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("NDDB", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::NDDBModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_control_pwm);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_control_solenoide);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_req_solenoide_control);
|
||||
ACIO_MODULE_HOOK(ac_io_nddb_update_control_status_buffer);
|
||||
}
|
13
acio/nddb/nddb.h
Normal file
13
acio/nddb/nddb.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class NDDBModule : public ACIOModule {
|
||||
public:
|
||||
NDDBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
203
acio/panb/panb.cpp
Normal file
203
acio/panb/panb.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
#include "panb.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/nost/io.h"
|
||||
#include "games/nost/nost.h"
|
||||
#include "util/logging.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[277];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static long __cdecl ac_io_panb_control_led_bright(size_t index, uint8_t value) {
|
||||
|
||||
// nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::nost::get_lights();
|
||||
|
||||
// mapping
|
||||
static const size_t mapping[] {
|
||||
games::nost::Lights::Key1R, games::nost::Lights::Key1G, games::nost::Lights::Key1B,
|
||||
games::nost::Lights::Key2R, games::nost::Lights::Key2G, games::nost::Lights::Key2B,
|
||||
games::nost::Lights::Key3R, games::nost::Lights::Key3G, games::nost::Lights::Key3B,
|
||||
games::nost::Lights::Key4R, games::nost::Lights::Key4G, games::nost::Lights::Key4B,
|
||||
games::nost::Lights::Key5R, games::nost::Lights::Key5G, games::nost::Lights::Key5B,
|
||||
games::nost::Lights::Key6R, games::nost::Lights::Key6G, games::nost::Lights::Key6B,
|
||||
games::nost::Lights::Key7R, games::nost::Lights::Key7G, games::nost::Lights::Key7B,
|
||||
games::nost::Lights::Key8R, games::nost::Lights::Key8G, games::nost::Lights::Key8B,
|
||||
games::nost::Lights::Key9R, games::nost::Lights::Key9G, games::nost::Lights::Key9B,
|
||||
games::nost::Lights::Key10R, games::nost::Lights::Key10G, games::nost::Lights::Key10B,
|
||||
games::nost::Lights::Key11R, games::nost::Lights::Key11G, games::nost::Lights::Key11B,
|
||||
games::nost::Lights::Key12R, games::nost::Lights::Key12G, games::nost::Lights::Key12B,
|
||||
games::nost::Lights::Key13R, games::nost::Lights::Key13G, games::nost::Lights::Key13B,
|
||||
games::nost::Lights::Key14R, games::nost::Lights::Key14G, games::nost::Lights::Key14B,
|
||||
games::nost::Lights::Key15R, games::nost::Lights::Key15G, games::nost::Lights::Key15B,
|
||||
games::nost::Lights::Key16R, games::nost::Lights::Key16G, games::nost::Lights::Key16B,
|
||||
games::nost::Lights::Key17R, games::nost::Lights::Key17G, games::nost::Lights::Key17B,
|
||||
games::nost::Lights::Key18R, games::nost::Lights::Key18G, games::nost::Lights::Key18B,
|
||||
games::nost::Lights::Key19R, games::nost::Lights::Key19G, games::nost::Lights::Key19B,
|
||||
games::nost::Lights::Key20R, games::nost::Lights::Key20G, games::nost::Lights::Key20B,
|
||||
games::nost::Lights::Key21R, games::nost::Lights::Key21G, games::nost::Lights::Key21B,
|
||||
games::nost::Lights::Key22R, games::nost::Lights::Key22G, games::nost::Lights::Key22B,
|
||||
games::nost::Lights::Key23R, games::nost::Lights::Key23G, games::nost::Lights::Key23B,
|
||||
games::nost::Lights::Key24R, games::nost::Lights::Key24G, games::nost::Lights::Key24B,
|
||||
games::nost::Lights::Key25R, games::nost::Lights::Key25G, games::nost::Lights::Key25B,
|
||||
games::nost::Lights::Key26R, games::nost::Lights::Key26G, games::nost::Lights::Key26B,
|
||||
games::nost::Lights::Key27R, games::nost::Lights::Key27G, games::nost::Lights::Key27B,
|
||||
games::nost::Lights::Key28R, games::nost::Lights::Key28G, games::nost::Lights::Key28B,
|
||||
};
|
||||
|
||||
// write light
|
||||
if (index < std::size(mapping)) {
|
||||
Lights::writeLight(RI_MGR, lights.at(mapping[index]), value / 127.f);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long __cdecl ac_io_panb_control_reset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void* __cdecl ac_io_panb_get_control_status_buffer(uint8_t* buffer) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_panb_start_auto_input() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_panb_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, 277);
|
||||
|
||||
/*
|
||||
* first byte is number of input data
|
||||
* when it's set to 0 the game will not update it's key states
|
||||
* setting it too high will make the game read over the buffer
|
||||
*
|
||||
* unsure why you would send more than one set of data, so
|
||||
* we just set it to 1 and provide our current status
|
||||
*/
|
||||
STATUS_BUFFER[0] = 1;
|
||||
|
||||
// Nostalgia
|
||||
if (avs::game::is_model("PAN")) {
|
||||
|
||||
// get buttons/analogs
|
||||
auto &buttons = games::nost::get_buttons();
|
||||
auto &analogs = games::nost::get_analogs();
|
||||
|
||||
// mappings
|
||||
static const size_t button_mapping[] = {
|
||||
games::nost::Buttons::Key1, games::nost::Buttons::Key2,
|
||||
games::nost::Buttons::Key3, games::nost::Buttons::Key4,
|
||||
games::nost::Buttons::Key5, games::nost::Buttons::Key6,
|
||||
games::nost::Buttons::Key7, games::nost::Buttons::Key8,
|
||||
games::nost::Buttons::Key9, games::nost::Buttons::Key10,
|
||||
games::nost::Buttons::Key11, games::nost::Buttons::Key12,
|
||||
games::nost::Buttons::Key13, games::nost::Buttons::Key14,
|
||||
games::nost::Buttons::Key15, games::nost::Buttons::Key16,
|
||||
games::nost::Buttons::Key17, games::nost::Buttons::Key18,
|
||||
games::nost::Buttons::Key19, games::nost::Buttons::Key20,
|
||||
games::nost::Buttons::Key21, games::nost::Buttons::Key22,
|
||||
games::nost::Buttons::Key23, games::nost::Buttons::Key24,
|
||||
games::nost::Buttons::Key25, games::nost::Buttons::Key26,
|
||||
games::nost::Buttons::Key27, games::nost::Buttons::Key28,
|
||||
};
|
||||
static const size_t analog_mapping[] = {
|
||||
games::nost::Analogs::Key1, games::nost::Analogs::Key2,
|
||||
games::nost::Analogs::Key3, games::nost::Analogs::Key4,
|
||||
games::nost::Analogs::Key5, games::nost::Analogs::Key6,
|
||||
games::nost::Analogs::Key7, games::nost::Analogs::Key8,
|
||||
games::nost::Analogs::Key9, games::nost::Analogs::Key10,
|
||||
games::nost::Analogs::Key11, games::nost::Analogs::Key12,
|
||||
games::nost::Analogs::Key13, games::nost::Analogs::Key14,
|
||||
games::nost::Analogs::Key15, games::nost::Analogs::Key16,
|
||||
games::nost::Analogs::Key17, games::nost::Analogs::Key18,
|
||||
games::nost::Analogs::Key19, games::nost::Analogs::Key20,
|
||||
games::nost::Analogs::Key21, games::nost::Analogs::Key22,
|
||||
games::nost::Analogs::Key23, games::nost::Analogs::Key24,
|
||||
games::nost::Analogs::Key25, games::nost::Analogs::Key26,
|
||||
games::nost::Analogs::Key27, games::nost::Analogs::Key28,
|
||||
};
|
||||
|
||||
// iterate pairs of keys
|
||||
for (size_t key_pair = 0; key_pair < 28 / 2; key_pair++) {
|
||||
|
||||
// default states
|
||||
uint8_t state0 = 0;
|
||||
uint8_t state1 = 0;
|
||||
|
||||
// check analogs
|
||||
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);
|
||||
}
|
||||
if (analog1.isSet()) {
|
||||
state1 = (uint8_t) (Analogs::getState(RI_MGR, analog1) * 15.999f);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (velocity1 > 0.f) {
|
||||
state1 = (uint8_t) (velocity1 * 15.999f);
|
||||
}
|
||||
|
||||
// build value
|
||||
uint8_t value = 0;
|
||||
value |= state0 << 4;
|
||||
value |= state1 & 0xF;
|
||||
|
||||
// set value
|
||||
STATUS_BUFFER[key_pair + 3] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PANBModule::PANBModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PANB", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PANBModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_panb_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_start_auto_input);
|
||||
ACIO_MODULE_HOOK(ac_io_panb_update_control_status_buffer);
|
||||
}
|
13
acio/panb/panb.h
Normal file
13
acio/panb/panb.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PANBModule : public ACIOModule {
|
||||
public:
|
||||
PANBModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
520
acio/pix/pix.cpp
Normal file
520
acio/pix/pix.cpp
Normal file
@ -0,0 +1,520 @@
|
||||
#include "pix.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "games/museca/io.h"
|
||||
#include "games/bbc/io.h"
|
||||
#include "util/utils.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static int ACIO_PIX_WARMUP = 0;
|
||||
static uint8_t STATUS_BUFFER[60];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static char __cdecl ac_io_pix_begin(char a1, long long a2, int a3, int a4, int a5, int a6) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_begin_get_status(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_end(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_end_get_status(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_firmware_update_device_index(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_node_no(int a1, int a2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_recv_log(long long a1, void *a2, int a3) {
|
||||
return a2;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_rs232c_status(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_get_send_log(long long a1, void *a2, int a3) {
|
||||
return a2;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_get_version(void *a1, int a2, int a3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *__cdecl ac_io_pix_get_version_string() {
|
||||
static const char *version = "1.25.0";
|
||||
return version;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_go_firmware_update(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active(int a1, int a2) {
|
||||
return (char) (++ACIO_PIX_WARMUP > 601 ? 1 : 0);
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active2(int a1, int *a2, int a3) {
|
||||
ACIO_PIX_WARMUP = 601;
|
||||
*a2 = 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_is_active_device(int a1, int a2) {
|
||||
return (char) (a1 != 5);
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_reset(int a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_change_expand_mode(char a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_control_led_bright(uint32_t led_field, uint8_t brightness) {
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::museca::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::museca::Lights::Spinner1R,
|
||||
games::museca::Lights::Spinner1G,
|
||||
games::museca::Lights::Spinner1B,
|
||||
games::museca::Lights::Spinner2R,
|
||||
games::museca::Lights::Spinner2G,
|
||||
games::museca::Lights::Spinner2B,
|
||||
games::museca::Lights::Spinner3R,
|
||||
games::museca::Lights::Spinner3G,
|
||||
games::museca::Lights::Spinner3B,
|
||||
games::museca::Lights::Spinner4R,
|
||||
games::museca::Lights::Spinner4G,
|
||||
games::museca::Lights::Spinner4B,
|
||||
games::museca::Lights::Spinner5R,
|
||||
games::museca::Lights::Spinner5G,
|
||||
games::museca::Lights::Spinner5B,
|
||||
games::museca::Lights::TitleR,
|
||||
games::museca::Lights::TitleG,
|
||||
games::museca::Lights::TitleB
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness > 127.f ? 1.f : brightness / 127.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get lights
|
||||
auto &lights = games::bbc::get_lights();
|
||||
|
||||
// control mapping
|
||||
static int mapping[] = {
|
||||
games::bbc::Lights::P1_DISC_R,
|
||||
games::bbc::Lights::P1_DISC_G,
|
||||
games::bbc::Lights::P1_DISC_B,
|
||||
games::bbc::Lights::P3_DISC_R,
|
||||
games::bbc::Lights::P3_DISC_G,
|
||||
games::bbc::Lights::P3_DISC_B,
|
||||
games::bbc::Lights::P2_DISC_R,
|
||||
games::bbc::Lights::P2_DISC_G,
|
||||
games::bbc::Lights::P2_DISC_B,
|
||||
games::bbc::Lights::P4_DISC_R,
|
||||
games::bbc::Lights::P4_DISC_G,
|
||||
games::bbc::Lights::P4_DISC_B,
|
||||
games::bbc::Lights::P1_R,
|
||||
games::bbc::Lights::P1_B,
|
||||
-1, -1, -1, -1, -1, -1,
|
||||
games::bbc::Lights::P2_R,
|
||||
games::bbc::Lights::P2_B,
|
||||
games::bbc::Lights::P3_R,
|
||||
games::bbc::Lights::P3_B,
|
||||
games::bbc::Lights::P4_R,
|
||||
games::bbc::Lights::P4_B,
|
||||
};
|
||||
|
||||
// write light
|
||||
float value = brightness / 255.f;
|
||||
for (size_t i = 0; i < std::size(mapping); i++) {
|
||||
if (mapping[i] >= 0 && led_field & (1 << i)) {
|
||||
Lights::writeLight(RI_MGR, lights.at((size_t) mapping[i]), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_control_reset() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_create_get_status_thread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_destroy_get_status_thread() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_rvol_get_control_status_buffer(void *a1) {
|
||||
|
||||
// copy buffer
|
||||
return memcpy(a1, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_get_watchdog_status() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_pix_rvol_get_watchdog_time_min() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static short __cdecl ac_io_pix_rvol_get_watchdog_time_now() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_modify_auto_input_get(long long a1, long long a2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_rvol_req_get_control_status(DWORD *a1) {
|
||||
*a1 = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_req_volume_control(char a1, char a2, char a3, char a4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_req_volume_control_isfinished(DWORD *a1) {
|
||||
*a1 = 5;
|
||||
return true;
|
||||
}
|
||||
|
||||
static long long __cdecl ac_io_pix_rvol_set_framing_err_packet_send_interval(long long a1) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_set_watchdog_time(short a1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pix_rvol_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// MUSECA
|
||||
if (avs::game::is_model("PIX")) {
|
||||
|
||||
// get input
|
||||
auto &buttons = games::museca::get_buttons();
|
||||
|
||||
// get slowdown status
|
||||
bool slowdown = Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::AnalogSlowdown));
|
||||
|
||||
// update disk buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 107);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 104);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 123);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 42);
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Press)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 44);
|
||||
|
||||
// foot pedal (inverted)
|
||||
if (!Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::FootPedal)))
|
||||
ARRAY_SETB(STATUS_BUFFER, 43);
|
||||
|
||||
// update analogs
|
||||
static uint8_t analogs[5] = { 0, 0, 0, 0, 0 };
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Minus))) {
|
||||
analogs[0] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk1Plus))) {
|
||||
analogs[0] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Minus))) {
|
||||
analogs[1] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk2Plus))) {
|
||||
analogs[1] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Minus))) {
|
||||
analogs[2] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk3Plus))) {
|
||||
analogs[2] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Minus))) {
|
||||
analogs[3] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk4Plus))) {
|
||||
analogs[3] += slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Minus))) {
|
||||
analogs[4] -= slowdown ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::museca::Buttons::Disk5Plus))) {
|
||||
analogs[4] += slowdown ? 3 : 12;
|
||||
}
|
||||
|
||||
// raw input analogs
|
||||
auto &analog_list = games::museca::get_analogs();
|
||||
size_t analog_mapping[] = {
|
||||
games::museca::Analogs::Disk1,
|
||||
games::museca::Analogs::Disk2,
|
||||
games::museca::Analogs::Disk3,
|
||||
games::museca::Analogs::Disk4,
|
||||
games::museca::Analogs::Disk5,
|
||||
};
|
||||
uint8_t set_values[5];
|
||||
std::copy(std::begin(analogs), std::end(analogs), std::begin(set_values));
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
auto &analog_item = analog_list.at(analog_mapping[i]);
|
||||
if (analog_item.isSet()) {
|
||||
set_values[i] = analogs[i] + (uint8_t) (Analogs::getState(RI_MGR, analog_item) * 255.99f);
|
||||
}
|
||||
}
|
||||
|
||||
// set analogs
|
||||
for (int i = 0; i < 5; i++)
|
||||
STATUS_BUFFER[20 + i] = set_values[i];
|
||||
}
|
||||
|
||||
// BISHI BASHI CHANNEL
|
||||
if (avs::game::is_model("R66")) {
|
||||
|
||||
// get input
|
||||
auto &buttons = games::bbc::get_buttons();
|
||||
auto &analogs = games::bbc::get_analogs();
|
||||
|
||||
// get slowdown status
|
||||
bool slowdown1 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskSlowdown));
|
||||
bool slowdown2 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskSlowdown));
|
||||
bool slowdown3 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskSlowdown));
|
||||
bool slowdown4 = Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskSlowdown));
|
||||
|
||||
// update buttons
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_R)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 44);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 107);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_B)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 41);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 39);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 123);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 55);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 71);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 104);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 87);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_R)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 103);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_G)) == Buttons::State::BUTTON_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 42);
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_B)) == Buttons::State::BUTTON_NOT_PRESSED) {
|
||||
ARRAY_SETB(STATUS_BUFFER, 119);
|
||||
}
|
||||
|
||||
// update analogs
|
||||
static uint8_t analog_states[4] = { 0, 0, 0, 0 };
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[0] -= slowdown1 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P1_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[0] += slowdown1 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[1] -= slowdown2 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P2_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[1] += slowdown2 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[2] -= slowdown3 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P3_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[2] += slowdown3 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskMinus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[3] -= slowdown4 ? 3 : 12;
|
||||
}
|
||||
if (Buttons::getState(RI_MGR, buttons.at(games::bbc::Buttons::P4_DiskPlus)) == Buttons::State::BUTTON_PRESSED) {
|
||||
analog_states[3] += slowdown4 ? 3 : 12;
|
||||
}
|
||||
|
||||
// raw input analogs
|
||||
uint8_t set_values[4];
|
||||
size_t analog_mappings[] = {
|
||||
games::bbc::Analogs::P1_Disk,
|
||||
games::bbc::Analogs::P2_Disk,
|
||||
games::bbc::Analogs::P3_Disk,
|
||||
games::bbc::Analogs::P4_Disk,
|
||||
};
|
||||
std::copy(std::begin(analog_states), std::end(analog_states), std::begin(set_values));
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
auto &analog_item = analogs.at(analog_mappings[i]);
|
||||
if (analog_item.isSet()) {
|
||||
set_values[i] = analog_states[i] + (uint8_t) (Analogs::getState(RI_MGR, analog_item) * 255.99f);
|
||||
}
|
||||
}
|
||||
|
||||
// flip disk 2/3
|
||||
set_values[1] ^= set_values[2];
|
||||
set_values[2] ^= set_values[1];
|
||||
set_values[1] ^= set_values[2];
|
||||
|
||||
// set analogs
|
||||
for (int i = 0; i < 4; i++) {
|
||||
STATUS_BUFFER[20 + i] = set_values[i];
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_pix_rvol_watchdog_off() {
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_secplug_set_encodedpasswd(void *a1, unsigned int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_set_get_status_device(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static void *__cdecl ac_io_pix_set_soft_watch_dog(void *a1, int a2) {
|
||||
return a1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_soft_watch_dog_off(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_soft_watch_dog_on(int a1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char __cdecl ac_io_pix_update(long long a1) {
|
||||
|
||||
// flush outputs
|
||||
RI_MGR->devices_flush_output();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char* __cdecl ac_io_pix_version() {
|
||||
static const char *version = "Version: 1.25.0\nBuild Date: Sep 20 2016 15:16:13\nBuild Host: DEMETER\n";
|
||||
return version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PIXModule::PIXModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PIX", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PIXModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pix_begin);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_begin_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_end);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_end_get_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_firmware_update_device_index);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_node_no);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_recv_log);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_rs232c_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_send_log);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_version);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_get_version_string);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_go_firmware_update);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active2);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_is_active_device);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_change_expand_mode);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_control_led_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_control_reset);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_create_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_destroy_get_status_thread);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_time_min);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_get_watchdog_time_now);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_modify_auto_input_get);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_get_control_status);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_volume_control);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_req_volume_control_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_set_framing_err_packet_send_interval);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_set_watchdog_time);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_rvol_watchdog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_secplug_set_encodedpasswd);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_set_get_status_device);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_set_soft_watch_dog);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_soft_watch_dog_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_soft_watch_dog_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_update);
|
||||
ACIO_MODULE_HOOK(ac_io_pix_version);
|
||||
}
|
13
acio/pix/pix.h
Normal file
13
acio/pix/pix.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PIXModule : public ACIOModule {
|
||||
public:
|
||||
PIXModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
156
acio/pjec/pjec.cpp
Normal file
156
acio/pjec/pjec.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
#include "pjec.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "avs/game.h"
|
||||
#include "games/we/io.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[72];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_pjec_get_ps2() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __cdecl ac_io_pjec_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjec_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
auto &buttons = games::we::get_buttons();
|
||||
auto &analogs = games::we::get_analogs();
|
||||
|
||||
/*
|
||||
* Device Types
|
||||
* 0x00 - Unknown Device
|
||||
* 0x01 - Mouse
|
||||
* 0x02 - Rotate Controller
|
||||
* 0x03 - Gun Controller K
|
||||
* 0x04 - Digital Controller <- Accepted
|
||||
* 0x05 - Analog Joystick
|
||||
* 0x06 - Gun Controller N
|
||||
* 0x07 - Analog Controller <- Accepted
|
||||
* 0x08 - USB Analog Controller
|
||||
*/
|
||||
|
||||
// set device type
|
||||
STATUS_BUFFER[0] = 0x07;
|
||||
|
||||
// set device present
|
||||
STATUS_BUFFER[2] = 0x5A;
|
||||
|
||||
// reset analogs to center
|
||||
STATUS_BUFFER[8] = 0x7F;
|
||||
STATUS_BUFFER[9] = 0x7F;
|
||||
STATUS_BUFFER[10] = 0x7F;
|
||||
STATUS_BUFFER[11] = 0x7F;
|
||||
|
||||
// apply analogs
|
||||
if (analogs[games::we::Analogs::PadStickLeftX].isSet()) {
|
||||
STATUS_BUFFER[8] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickLeftX]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickLeftY].isSet()) {
|
||||
STATUS_BUFFER[9] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickLeftY]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickRightX].isSet()) {
|
||||
STATUS_BUFFER[10] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickRightX]) * 255.9999f);
|
||||
}
|
||||
if (analogs[games::we::Analogs::PadStickRightY].isSet()) {
|
||||
STATUS_BUFFER[11] = (uint8_t) (GameAPI::Analogs::getState(RI_MGR,
|
||||
analogs[games::we::Analogs::PadStickRightY]) * 255.9999f);
|
||||
}
|
||||
|
||||
// apply buttons
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadStart])) {
|
||||
STATUS_BUFFER[4] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadSelect])) {
|
||||
STATUS_BUFFER[4] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadUp])) {
|
||||
STATUS_BUFFER[4] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadDown])) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadLeft])) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadRight])) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadTriangle])) {
|
||||
STATUS_BUFFER[5] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadCross])) {
|
||||
STATUS_BUFFER[5] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadSquare])) {
|
||||
STATUS_BUFFER[5] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadCircle])) {
|
||||
STATUS_BUFFER[5] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL1])) {
|
||||
STATUS_BUFFER[5] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL2])) {
|
||||
STATUS_BUFFER[5] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadL3])) {
|
||||
STATUS_BUFFER[4] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR1])) {
|
||||
STATUS_BUFFER[5] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR2])) {
|
||||
STATUS_BUFFER[5] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::PadR3])) {
|
||||
STATUS_BUFFER[4] |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
acio::PJECModule::PJECModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PJEC", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PJECModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_get_ps2);
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjec_update_control_status_buffer);
|
||||
}
|
13
acio/pjec/pjec.h
Normal file
13
acio/pjec/pjec.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJECModule : public ACIOModule {
|
||||
public:
|
||||
PJECModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
223
acio/pjei/pjei.cpp
Normal file
223
acio/pjei/pjei.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include "pjei.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "games/we/io.h"
|
||||
#include "avs/game.h"
|
||||
|
||||
//using namespace GameAPI;
|
||||
|
||||
// static stuff
|
||||
static uint8_t STATUS_BUFFER[40];
|
||||
static bool STATUS_BUFFER_FREEZE = false;
|
||||
|
||||
/*
|
||||
* Implementations
|
||||
*/
|
||||
|
||||
static bool __cdecl ac_io_pjei_current_coinstock(int a1, uint32_t *coinstock) {
|
||||
*coinstock = eamuse_coin_get_stock();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_consume_coinstock(int a1, uint32_t amount) {
|
||||
return eamuse_coin_consume(amount);
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_get_softwareid(char *dst) {
|
||||
static char DATA[] = "0140FFFFFFFFFFFFFFFF";
|
||||
memcpy(dst, DATA, sizeof(DATA));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_get_systemid(char *dst) {
|
||||
static char DATA[] = "0140FFFFFFFFFFFFFFFF";
|
||||
memcpy(dst, DATA, sizeof(DATA));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_update_control_status_buffer() {
|
||||
|
||||
// check freeze
|
||||
if (STATUS_BUFFER_FREEZE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// clear buffer
|
||||
memset(STATUS_BUFFER, 0, sizeof(STATUS_BUFFER));
|
||||
|
||||
// Winning Eleven
|
||||
if (avs::game::is_model({ "KCK", "NCK" })) {
|
||||
|
||||
// get buttons
|
||||
auto &buttons = games::we::get_buttons();
|
||||
|
||||
// apply buttons
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Service])) {
|
||||
STATUS_BUFFER[16] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Test])) {
|
||||
STATUS_BUFFER[16] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::CoinMech])) {
|
||||
STATUS_BUFFER[16] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Start])) {
|
||||
STATUS_BUFFER[4] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Up])) {
|
||||
STATUS_BUFFER[4] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Down])) {
|
||||
STATUS_BUFFER[4] |= 0x20;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Left])) {
|
||||
STATUS_BUFFER[4] |= 0x10;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::Right])) {
|
||||
STATUS_BUFFER[4] |= 0x08;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonA])) {
|
||||
STATUS_BUFFER[4] |= 0x04;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonB])) {
|
||||
STATUS_BUFFER[4] |= 0x02;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonC])) {
|
||||
STATUS_BUFFER[4] |= 0x01;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonD])) {
|
||||
STATUS_BUFFER[6] |= 0x80;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonE])) {
|
||||
STATUS_BUFFER[6] |= 0x40;
|
||||
}
|
||||
if (GameAPI::Buttons::getState(RI_MGR, buttons[games::we::Buttons::ButtonF])) {
|
||||
STATUS_BUFFER[6] |= 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ac_io_pjei_get_control_status_buffer(uint8_t *buffer) {
|
||||
memcpy(buffer, STATUS_BUFFER, sizeof(STATUS_BUFFER));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_check_isfinished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_missing_check() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_req_secplug_missing_check_isfinished() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_lock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_unlock_coincounter(int a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_coin_blocker_on(bool a1) {
|
||||
eamuse_coin_set_block(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_coin_blocker_off(bool a1) {
|
||||
eamuse_coin_set_block(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method for easily setting the light values
|
||||
*/
|
||||
static void ac_io_pjei_control_lamp_set(uint8_t lamp_bits, uint8_t brightness) {
|
||||
auto &lights = games::we::get_lights();
|
||||
float value = CLAMP(brightness / 31.f, 0.f, 1.f);
|
||||
if (lamp_bits & 0x20) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftRed], value);
|
||||
}
|
||||
if (lamp_bits & 0x10) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftGreen], value);
|
||||
}
|
||||
if (lamp_bits & 0x08) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::LeftBlue], value);
|
||||
}
|
||||
if (lamp_bits & 0x04) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightRed], value);
|
||||
}
|
||||
if (lamp_bits & 0x02) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightGreen], value);
|
||||
}
|
||||
if (lamp_bits & 0x01) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, lights[games::we::Lights::RightBlue], value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_on(uint8_t lamp_bits) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bits, 31);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_off(uint8_t lamp_bits) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bits, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_bright(uint8_t lamp_bit, uint8_t brightness) {
|
||||
ac_io_pjei_control_lamp_set(lamp_bit, brightness);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __cdecl ac_io_pjei_control_lamp_mode(int mode) {
|
||||
// mode -> [0,1] (0 is static, 1 is brightness?)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
acio::PJEIModule::PJEIModule(HMODULE module, acio::HookMode hookMode) : ACIOModule("PJEI", module, hookMode) {
|
||||
this->status_buffer = STATUS_BUFFER;
|
||||
this->status_buffer_size = sizeof(STATUS_BUFFER);
|
||||
this->status_buffer_freeze = &STATUS_BUFFER_FREEZE;
|
||||
}
|
||||
|
||||
void acio::PJEIModule::attach() {
|
||||
ACIOModule::attach();
|
||||
|
||||
// hooks
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_current_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_consume_coinstock);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_softwareid);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_systemid);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_update_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_get_control_status_buffer);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_check);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_missing_check);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_req_secplug_missing_check_isfinished);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_lock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_unlock_coincounter);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_coin_blocker_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_coin_blocker_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_on);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_off);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_bright);
|
||||
ACIO_MODULE_HOOK(ac_io_pjei_control_lamp_mode);
|
||||
}
|
13
acio/pjei/pjei.h
Normal file
13
acio/pjei/pjei.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "../module.h"
|
||||
|
||||
namespace acio {
|
||||
|
||||
class PJEIModule : public ACIOModule {
|
||||
public:
|
||||
PJEIModule(HMODULE module, HookMode hookMode);
|
||||
|
||||
virtual void attach() override;
|
||||
};
|
||||
}
|
67
acio2emu/firmware/bi2x.cpp
Normal file
67
acio2emu/firmware/bi2x.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "bi2x.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
bool BI2XNode::handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) {
|
||||
auto cur = in.payload.begin();
|
||||
while ((cur + 1) < in.payload.end()) {
|
||||
auto cmd = (cur[0] << 8) | cur[1];
|
||||
out.push_back(*cur++);
|
||||
out.push_back(*cur++);
|
||||
out.push_back(0);
|
||||
|
||||
switch (cmd) {
|
||||
case 2: // query firmware version
|
||||
read_firmware_version(out);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 16:
|
||||
out.push_back(2);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 800:
|
||||
case 802:
|
||||
case 19:
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 120:
|
||||
out.push_back(3);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 801:
|
||||
out.push_back(33);
|
||||
out.push_back(0);
|
||||
cur = in.payload.end();
|
||||
break;
|
||||
|
||||
case 784: // poll input
|
||||
if (!read_input(out)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 785: { // write output
|
||||
auto count = write_output(std::span{&*cur, static_cast<size_t>(in.payload.end() - cur)});
|
||||
if (count < 0) {
|
||||
return false;
|
||||
}
|
||||
cur += count;
|
||||
break;
|
||||
}
|
||||
|
||||
case 786:
|
||||
cur += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("bi2x", "unknown command: {}", cmd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
22
acio2emu/firmware/bi2x.h
Normal file
22
acio2emu/firmware/bi2x.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/node.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace acio2emu::firmware {
|
||||
class BI2XNode : public Node {
|
||||
virtual void read_firmware_version(std::vector<uint8_t> &buffer) = 0;
|
||||
|
||||
virtual bool read_input(std::vector<uint8_t> &buffer) = 0;
|
||||
virtual int write_output(std::span<const uint8_t> buffer) = 0;
|
||||
|
||||
/*
|
||||
* acio2emu::Node
|
||||
*/
|
||||
bool handle_packet(const acio2emu::Packet &in, std::vector<uint8_t> &out) override;
|
||||
};
|
||||
}
|
120
acio2emu/handle.cpp
Normal file
120
acio2emu/handle.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
#include "handle.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h" // ws2s
|
||||
|
||||
namespace acio2emu {
|
||||
class MasterNode : public Node {
|
||||
private:
|
||||
const IOBHandle *iob_;
|
||||
|
||||
public:
|
||||
MasterNode(const IOBHandle *iob) : iob_(iob) { }
|
||||
|
||||
bool handle_packet(const Packet &in, std::vector<uint8_t> &out) {
|
||||
// were we sent a command?
|
||||
if (in.payload.size() >= 2) {
|
||||
if (in.payload[0] != 0 || in.payload[1] != 1) {
|
||||
// unknown command
|
||||
return false;
|
||||
}
|
||||
|
||||
// assign node ids
|
||||
out.push_back(0);
|
||||
out.push_back(1);
|
||||
for (int i = 0; i < iob_->number_of_nodes(); i++) {
|
||||
out.push_back(i * 16);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
IOBHandle::IOBHandle(std::wstring device) : device_(device) {
|
||||
nodes_[0] = std::make_unique<MasterNode>(this);
|
||||
}
|
||||
|
||||
bool IOBHandle::register_node(std::unique_ptr<Node> node) {
|
||||
if ((number_of_nodes_ - 1) >= 16) {
|
||||
// too many nodes
|
||||
return false;
|
||||
}
|
||||
|
||||
nodes_[number_of_nodes_++] = std::move(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::number_of_nodes() const {
|
||||
// don't include the master node
|
||||
return number_of_nodes_ - 1;
|
||||
}
|
||||
|
||||
void IOBHandle::forward_packet_(const Packet &packet) {
|
||||
// clear the output queue
|
||||
output_ = {};
|
||||
|
||||
auto node = packet.node / 2;
|
||||
if (node >= number_of_nodes_) {
|
||||
log_warning("acio2emu", "cannot forward packet: node out of range: {} >= {}", node, number_of_nodes_);
|
||||
return;
|
||||
}
|
||||
|
||||
// forward the packet to the node
|
||||
std::vector<uint8_t> payload;
|
||||
if (!nodes_[node]->handle_packet(packet, payload)) {
|
||||
// error in handler
|
||||
return;
|
||||
}
|
||||
|
||||
// encode the response
|
||||
encode_packet(output_, node, packet.tag, payload);
|
||||
}
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool IOBHandle::open(LPCWSTR lpFileName) {
|
||||
if (device_ != lpFileName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("acio2emu", "Opened {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOBHandle::close() {
|
||||
log_info("acio2emu", "Closed {} (ACIO2)", ws2s(device_));
|
||||
return true;
|
||||
}
|
||||
|
||||
int IOBHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
DWORD i = 0;
|
||||
|
||||
while (!output_.empty() && i < nNumberOfBytesToRead) {
|
||||
buffer[i++] = output_.front();
|
||||
output_.pop();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int IOBHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
if (decoder_.update(buffer[i])) {
|
||||
// forward the packet to a node
|
||||
forward_packet_(decoder_.packet());
|
||||
}
|
||||
}
|
||||
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int IOBHandle::device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) {
|
||||
return -1;
|
||||
}
|
||||
}
|
44
acio2emu/handle.h
Normal file
44
acio2emu/handle.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <queue>
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
#include "acio2emu/node.h"
|
||||
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class IOBHandle : public CustomHandle {
|
||||
private:
|
||||
std::wstring device_;
|
||||
|
||||
std::array<std::unique_ptr<Node>, 17> nodes_;
|
||||
// the first node is reserved for the "master" node
|
||||
int number_of_nodes_ = 1;
|
||||
|
||||
PacketDecoder decoder_;
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
void forward_packet_(const Packet &packet);
|
||||
|
||||
public:
|
||||
IOBHandle(std::wstring device);
|
||||
|
||||
bool register_node(std::unique_ptr<Node> node);
|
||||
int number_of_nodes() const;
|
||||
|
||||
/*
|
||||
* CustomHandle
|
||||
*/
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize) override;
|
||||
bool close() override;
|
||||
};
|
||||
}
|
40
acio2emu/internal/crc.h
Normal file
40
acio2emu/internal/crc.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
inline uint8_t crc4_lgp_c(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x0D, 0x03, 0x0E,
|
||||
0x06, 0x0B, 0x05, 0x08,
|
||||
0x0C, 0x01, 0x0F, 0x02,
|
||||
0x0A, 0x07, 0x09, 0x04,
|
||||
};
|
||||
|
||||
crc &= 15;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline uint8_t crc7_lgp_48(uint8_t crc, const uint8_t *data, size_t len) {
|
||||
static constexpr uint8_t tbl[] = {
|
||||
0x00, 0x09, 0x12, 0x1B,
|
||||
0x24, 0x2D, 0x36, 0x3F,
|
||||
0x48, 0x41, 0x5A, 0x53,
|
||||
0x6C, 0x65, 0x7E, 0x77
|
||||
};
|
||||
|
||||
crc &= 127;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto b = data[i];
|
||||
crc = (((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) >> 4) ^ tbl[(((crc >> 4) ^ (tbl[(b ^ crc) & 0x0F])) ^ (b >> 4)) & 0x0F];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
}
|
140
acio2emu/internal/lz.h
Normal file
140
acio2emu/internal/lz.h
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <cstdint>
|
||||
|
||||
namespace acio2emu::detail {
|
||||
class InflateTransformer {
|
||||
private:
|
||||
std::queue<uint8_t> output_;
|
||||
|
||||
uint8_t flags_ = 0, flag_shift_ = 0;
|
||||
|
||||
uint8_t window_[85] = {};
|
||||
int window_offset_ = 81;
|
||||
|
||||
enum class inflateStep {
|
||||
readFlags,
|
||||
processFlags,
|
||||
copyStored,
|
||||
copyFromWindow,
|
||||
} step_ = inflateStep::readFlags;
|
||||
|
||||
void window_put_(uint8_t b) {
|
||||
window_[window_offset_++] = b;
|
||||
window_offset_ %= sizeof(window_);
|
||||
}
|
||||
|
||||
uint8_t window_get_(int offset) {
|
||||
return window_[offset % sizeof(window_)];
|
||||
}
|
||||
|
||||
public:
|
||||
void put(uint8_t b) {
|
||||
auto consumed = false;
|
||||
|
||||
while (true) {
|
||||
switch (step_) {
|
||||
case inflateStep::readFlags:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
flags_ = b;
|
||||
flag_shift_ = 0;
|
||||
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
|
||||
case inflateStep::processFlags:
|
||||
// have we processed every flag?
|
||||
if (flag_shift_ > 6) {
|
||||
step_ = inflateStep::readFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
flag_shift_++;
|
||||
|
||||
if (flags_ & (1 << flag_shift_)) {
|
||||
// emit 0xAA when both bits are set
|
||||
output_.push(0xAA);
|
||||
}
|
||||
else {
|
||||
// copy from the window when only the lower bit is set
|
||||
step_ = inflateStep::copyFromWindow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
step_ = inflateStep::copyStored;
|
||||
}
|
||||
flag_shift_++;
|
||||
|
||||
break;
|
||||
|
||||
case inflateStep::copyFromWindow: {
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
// determine the match size, default is 2-bytes
|
||||
auto offset = b;
|
||||
auto size = 2;
|
||||
|
||||
if (offset >= 0xAA) {
|
||||
// 4-byte match
|
||||
size = 4;
|
||||
offset -= 0xAB;
|
||||
}
|
||||
else if (offset >= 0x55) {
|
||||
// 3-byte match
|
||||
size = 3;
|
||||
offset -= 0x55;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < size; i ++) {
|
||||
auto cur = window_get_(offset + i);
|
||||
|
||||
window_put_(cur);
|
||||
output_.push(cur);
|
||||
}
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
|
||||
case inflateStep::copyStored:
|
||||
if (consumed) {
|
||||
// need more data
|
||||
return;
|
||||
}
|
||||
consumed = true;
|
||||
|
||||
window_put_(b);
|
||||
output_.push(b);
|
||||
|
||||
// continue processing flags
|
||||
step_ = inflateStep::processFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get() {
|
||||
if (output_.empty()) {
|
||||
// output queue is empty
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto b = output_.front();
|
||||
output_.pop();
|
||||
|
||||
return b;
|
||||
}
|
||||
};
|
||||
}
|
14
acio2emu/node.h
Normal file
14
acio2emu/node.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "acio2emu/packet.h"
|
||||
|
||||
namespace acio2emu {
|
||||
class Node {
|
||||
public:
|
||||
virtual ~Node() {}
|
||||
|
||||
virtual bool handle_packet(const Packet &in, std::vector<uint8_t> &out) = 0;
|
||||
};
|
||||
}
|
259
acio2emu/packet.cpp
Normal file
259
acio2emu/packet.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
#include "packet.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "acio2emu/internal/crc.h"
|
||||
|
||||
namespace acio2emu {
|
||||
static constexpr uint8_t SOF = 0xAA;
|
||||
static constexpr uint8_t ESC = 0xFF;
|
||||
|
||||
static void encode_payload_(std::queue<uint8_t> &out, const std::vector<uint8_t> &payload) {
|
||||
for (auto b : payload) {
|
||||
if (b == SOF || b == ESC) {
|
||||
out.push(ESC);
|
||||
b = ~b;
|
||||
}
|
||||
out.push(b);
|
||||
}
|
||||
// compute and write the payload's CRC
|
||||
out.push(detail::crc7_lgp_48(0x7F, payload.data(), payload.size()) ^ 0x7F);
|
||||
}
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload) {
|
||||
auto size = payload.size();
|
||||
if (size > 127) {
|
||||
log_warning("acio2emu", "cannot encode packet: payload too large: {} > 127", payload.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the header
|
||||
uint8_t header[5] = {
|
||||
SOF,
|
||||
static_cast<uint8_t>(node * 3),
|
||||
tag,
|
||||
static_cast<uint8_t>(size),
|
||||
0,
|
||||
};
|
||||
// compute the header's CRC
|
||||
header[4] = detail::crc4_lgp_c(0x0F, &header[1], sizeof(header) - 1) ^ 0x0F;
|
||||
// push the header to the output queue
|
||||
for (size_t i = 0; i < sizeof(header); i++) {
|
||||
out.push(header[i]);
|
||||
}
|
||||
|
||||
encode_payload_(out, payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PacketDecoder::set_step_(readStep s) {
|
||||
#ifndef NDEBUG
|
||||
auto valid = true;
|
||||
switch (s) {
|
||||
case readStep::idle:
|
||||
case readStep::readNode:
|
||||
// transition from any step/state allowed
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
if (step_ != readStep::readNode) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize:
|
||||
if (step_ != readStep::readTag) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
if (step_ != readStep::readPayloadSize) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
if (step_ != readStep::readPayloadFlags) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
if (step_ != readStep::readPayloadFlags &&
|
||||
step_ != readStep::readReplacementByte &&
|
||||
step_ != readStep::readEscaped
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
if (step_ != readStep::readPayload) {
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
log_fatal("acio2emu", "cannot set step: unknown value: {}", s);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
log_fatal("acio2emu", "illegal transition detected: {} -> {}", step_, s);
|
||||
}
|
||||
#endif
|
||||
step_ = s;
|
||||
}
|
||||
|
||||
int PacketDecoder::update_payload_size_(uint8_t b) {
|
||||
if ((b & 0x80) == 0) {
|
||||
payload_size_ = (payload_size_ << 7) | (b & 0x7F);
|
||||
// finished
|
||||
return 0;
|
||||
}
|
||||
else if ((b & 0x40) != 0 && payload_size_count_ < 5) {
|
||||
payload_size_count_++;
|
||||
payload_size_count_ = (payload_size_count_ << 6) | (b & 0x3F);
|
||||
// continuation required
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
// invalid value or invalid state
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PacketDecoder::deobfuscate_(uint8_t b) {
|
||||
if ((b ^ 0xAA) == 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
auto mask = 0x55;
|
||||
if ((b & 0x80) == 0) {
|
||||
mask = 0x7F;
|
||||
}
|
||||
|
||||
return (b ^ lcg_()) & mask;
|
||||
}
|
||||
|
||||
void PacketDecoder::reset_(readStep s) {
|
||||
set_step_(s);
|
||||
packet_ = {};
|
||||
payload_size_ = 0;
|
||||
payload_size_count_ = 0;
|
||||
}
|
||||
|
||||
bool PacketDecoder::update(uint8_t b) {
|
||||
// is this the start of a packet?
|
||||
if (b == SOF) {
|
||||
reset_(readStep::readNode);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (step_) {
|
||||
case readStep::readNode:
|
||||
packet_.node = b;
|
||||
|
||||
set_step_(readStep::readTag);
|
||||
break;
|
||||
|
||||
case readStep::readTag:
|
||||
packet_.tag = b;
|
||||
|
||||
set_step_(readStep::readPayloadSize);
|
||||
break;
|
||||
|
||||
case readStep::readPayloadSize: {
|
||||
auto status = update_payload_size_(b);
|
||||
if (status == 0) {
|
||||
// finished reading payload size
|
||||
packet_.payload.reserve(payload_size_);
|
||||
set_step_(readStep::readPayloadFlags);
|
||||
}
|
||||
else if (status == -1) {
|
||||
// reset on error
|
||||
reset_(readStep::idle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case readStep::readPayloadFlags:
|
||||
obfuscated_ = (b & (1 << 4)) != 0;
|
||||
encoding_ = static_cast<payloadEncoding>(b >> 5);
|
||||
|
||||
if (obfuscated_) {
|
||||
lcg_.seed(packet_.tag ^ 0x55);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::replace) {
|
||||
set_step_(readStep::readReplacementByte);
|
||||
}
|
||||
else {
|
||||
set_step_(readStep::readPayload);
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
// reset the InflateTransformer
|
||||
inflate_ = {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readReplacementByte:
|
||||
substitute_ = b;
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
case readStep::readPayload:
|
||||
// do we need to deobfuscate?
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
|
||||
if (encoding_ == payloadEncoding::lz) {
|
||||
inflate_.put(b);
|
||||
for (int i = inflate_.get(); i >= 0; i = inflate_.get()) {
|
||||
packet_.payload.push_back(i);
|
||||
}
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::replace && b == substitute_) {
|
||||
packet_.payload.push_back(SOF);
|
||||
}
|
||||
else if (encoding_ == payloadEncoding::byteStuffing && b == ESC) {
|
||||
set_step_(readStep::readEscaped);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
packet_.payload.push_back(b);
|
||||
}
|
||||
break;
|
||||
|
||||
case readStep::readEscaped:
|
||||
b = ~b;
|
||||
if (obfuscated_) {
|
||||
b = deobfuscate_(b);
|
||||
}
|
||||
packet_.payload.push_back(b);
|
||||
|
||||
set_step_(readStep::readPayload);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ((step_ == readStep::readPayload || step_ == readStep::readPayloadFlags) &&
|
||||
(packet_.payload.size() >= payload_size_)) {
|
||||
set_step_(readStep::idle);
|
||||
// finished reading packet
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Packet &PacketDecoder::packet() {
|
||||
return packet_;
|
||||
}
|
||||
}
|
65
acio2emu/packet.h
Normal file
65
acio2emu/packet.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <random> // std::linear_congruential_engine
|
||||
#include <cstdint>
|
||||
|
||||
#include "acio2emu/internal/lz.h"
|
||||
|
||||
namespace acio2emu {
|
||||
struct Packet {
|
||||
uint8_t node;
|
||||
uint8_t tag;
|
||||
std::vector<uint8_t> payload;
|
||||
};
|
||||
|
||||
class PacketDecoder {
|
||||
private:
|
||||
Packet packet_ = {};
|
||||
|
||||
// order matters, don't change this enum!
|
||||
enum payloadEncoding {
|
||||
byteStuffing,
|
||||
raw,
|
||||
unknown,
|
||||
replace,
|
||||
lz,
|
||||
} encoding_;
|
||||
|
||||
uint32_t payload_size_ = 0, payload_size_count_ = 0;
|
||||
|
||||
// payloadEncoding::replace state
|
||||
uint8_t substitute_;
|
||||
|
||||
// payloadEncoding::lz state
|
||||
detail::InflateTransformer inflate_;
|
||||
|
||||
// deobfuscation state
|
||||
bool obfuscated_;
|
||||
std::linear_congruential_engine<uint32_t, 1103515245, 12345, 0> lcg_;
|
||||
|
||||
enum class readStep {
|
||||
idle,
|
||||
readNode,
|
||||
readTag,
|
||||
readPayloadSize,
|
||||
readPayloadFlags,
|
||||
readReplacementByte,
|
||||
readPayload,
|
||||
readEscaped,
|
||||
} step_ = readStep::idle;
|
||||
|
||||
void set_step_(readStep s);
|
||||
void reset_(readStep s);
|
||||
|
||||
int update_payload_size_(uint8_t b);
|
||||
uint8_t deobfuscate_(uint8_t b);
|
||||
|
||||
public:
|
||||
bool update(uint8_t b);
|
||||
const Packet &packet();
|
||||
};
|
||||
|
||||
bool encode_packet(std::queue<uint8_t> &out, uint8_t node, uint8_t tag, const std::vector<uint8_t> &payload);
|
||||
}
|
209
acioemu/acioemu.cpp
Normal file
209
acioemu/acioemu.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
#include "acioemu.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
ACIOEmu::ACIOEmu() {
|
||||
this->devices = new std::vector<ACIODeviceEmu *>();
|
||||
this->response_buffer = new circular_buffer<uint8_t>(4096);
|
||||
this->read_buffer = new circular_buffer<uint8_t>(1024);
|
||||
}
|
||||
|
||||
ACIOEmu::~ACIOEmu() {
|
||||
|
||||
// delete devices
|
||||
for (auto device : *this->devices) {
|
||||
delete device;
|
||||
}
|
||||
delete this->devices;
|
||||
|
||||
// delete buffers
|
||||
delete this->response_buffer;
|
||||
delete this->read_buffer;
|
||||
}
|
||||
|
||||
void ACIOEmu::add_device(ACIODeviceEmu *device) {
|
||||
this->devices->push_back(device);
|
||||
}
|
||||
|
||||
void ACIOEmu::write(uint8_t byte) {
|
||||
|
||||
// insert into buffer
|
||||
if (!invert) {
|
||||
if (byte == ACIO_ESCAPE) {
|
||||
invert = true;
|
||||
} else {
|
||||
this->read_buffer->put(byte);
|
||||
}
|
||||
} else {
|
||||
byte = ~byte;
|
||||
invert = false;
|
||||
this->read_buffer->put(byte);
|
||||
}
|
||||
|
||||
// clean garbage
|
||||
while (!this->read_buffer->empty() && this->read_buffer->peek() != 0xAA) {
|
||||
this->read_buffer->get();
|
||||
}
|
||||
while (this->read_buffer->size() > 1 && this->read_buffer->peek(1) == 0xAA) {
|
||||
this->read_buffer->get();
|
||||
}
|
||||
|
||||
// handshake counter
|
||||
static unsigned int handshake_counter = 0;
|
||||
if (byte == 0xAA) {
|
||||
handshake_counter++;
|
||||
} else {
|
||||
handshake_counter = 0;
|
||||
}
|
||||
|
||||
// check for handshake
|
||||
if (handshake_counter > 1) {
|
||||
|
||||
/*
|
||||
* small hack - BIO2 seems to expect more bytes here - sending two bytes each time fixes it
|
||||
* TODO replace this handshake code with something better
|
||||
*/
|
||||
this->response_buffer->put(ACIO_SOF);
|
||||
this->response_buffer->put(ACIO_SOF);
|
||||
handshake_counter--;
|
||||
return;
|
||||
}
|
||||
|
||||
// parse
|
||||
if (!this->read_buffer->empty() && this->read_buffer->size() >= 6) {
|
||||
bool is_complete = false;
|
||||
|
||||
// check if broadcast
|
||||
if (this->read_buffer->peek(1) == ACIO_BROADCAST) {
|
||||
|
||||
// check msg data size
|
||||
auto data_size = this->read_buffer->peek(2);
|
||||
|
||||
// check if msg is complete (SOF + checksum + broadcast header + data_size)
|
||||
is_complete = this->read_buffer->size() >= 2u + 2u + data_size;
|
||||
} else {
|
||||
|
||||
// check msg data size
|
||||
auto data_size = this->read_buffer->peek(5);
|
||||
|
||||
// check if msg is complete (SOF + checksum + command header + data_size)
|
||||
is_complete = this->read_buffer->size() >= 2u + MSG_HEADER_SIZE + data_size;
|
||||
}
|
||||
|
||||
// parse message if complete
|
||||
if (is_complete) {
|
||||
this->msg_parse();
|
||||
this->read_buffer->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<uint8_t> ACIOEmu::read() {
|
||||
if (this->response_buffer->empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return this->response_buffer->get();
|
||||
}
|
||||
|
||||
size_t ACIOEmu::bytes_available() {
|
||||
return this->response_buffer->size();
|
||||
}
|
||||
|
||||
void ACIOEmu::msg_parse() {
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "MSG RECV: {}", bin2hex(*this->read_buffer));
|
||||
#endif
|
||||
|
||||
// calculate checksum
|
||||
uint8_t chk = 0;
|
||||
size_t max = this->read_buffer->size() - 1;
|
||||
for (size_t i = 1; i < max; i++) {
|
||||
chk += this->read_buffer->peek(i);
|
||||
}
|
||||
|
||||
// check checksum
|
||||
uint8_t chk_receive = this->read_buffer->peek(this->read_buffer->size() - 1);
|
||||
if (chk != chk_receive) {
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "detected wrong checksum: {}/{}", chk, chk_receive);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// get message data
|
||||
auto msg_data = this->read_buffer->peek_all();
|
||||
auto msg_in = (MessageData *) &msg_data[1];
|
||||
|
||||
// correct cmd code endianness if this is not a broadcast
|
||||
if (msg_in->addr != ACIO_BROADCAST) {
|
||||
msg_in->cmd.code = acio_u16(msg_in->cmd.code);
|
||||
}
|
||||
|
||||
// pass to applicable device
|
||||
uint8_t node_offset = 0;
|
||||
for (auto device : *this->devices) {
|
||||
if (device->is_applicable(node_offset, msg_in->addr)) {
|
||||
auto cur_offset = msg_in->addr - node_offset - 1;
|
||||
if (cur_offset < 0) {
|
||||
break;
|
||||
}
|
||||
if (device->parse_msg(msg_in, this->response_buffer)) {
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
node_offset += device->node_count;
|
||||
}
|
||||
|
||||
// ignore broadcast messages by default
|
||||
if (msg_in->addr == ACIO_BROADCAST) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default Behavior
|
||||
* If you want to do anything different, just handle the
|
||||
* commands in your own device implementation.
|
||||
*/
|
||||
switch (msg_in->cmd.code) {
|
||||
|
||||
// node count report
|
||||
case ACIO_CMD_ASSIGN_ADDRS: {
|
||||
if (msg_in->addr == 0x00 && node_offset > 0) {
|
||||
auto msg = ACIODeviceEmu::create_msg(msg_in, 1, &node_offset);
|
||||
ACIODeviceEmu::write_msg(msg, this->response_buffer);
|
||||
delete msg;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// status 0 defaults
|
||||
case ACIO_CMD_CLEAR:
|
||||
case ACIO_CMD_STARTUP:
|
||||
case 0x80: // KEEPALIVE
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = ACIODeviceEmu::create_msg_status(msg_in, 0);
|
||||
ACIODeviceEmu::write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "UNHANDLED MSG FOR ADDR: {}, CMD: 0x{:x}), DATA: {}",
|
||||
msg_in->addr,
|
||||
msg_in->cmd.code,
|
||||
bin2hex(*this->read_buffer));
|
||||
#endif
|
||||
}
|
33
acioemu/acioemu.h
Normal file
33
acioemu/acioemu.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "icca.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOEmu {
|
||||
private:
|
||||
std::vector<ACIODeviceEmu *> *devices;
|
||||
circular_buffer<uint8_t> *response_buffer;
|
||||
circular_buffer<uint8_t> *read_buffer;
|
||||
bool invert = false;
|
||||
|
||||
void msg_parse();
|
||||
|
||||
public:
|
||||
|
||||
explicit ACIOEmu();
|
||||
~ACIOEmu();
|
||||
|
||||
void add_device(ACIODeviceEmu *device);
|
||||
|
||||
void write(uint8_t byte);
|
||||
std::optional<uint8_t> read();
|
||||
size_t bytes_available();
|
||||
};
|
||||
}
|
80
acioemu/bi2a.h
Normal file
80
acioemu/bi2a.h
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include "device.h"
|
||||
#include "hooks/sleephook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct bio2_bi2a_state_in {
|
||||
uint8_t pad0[3];
|
||||
uint8_t panel[4];
|
||||
uint8_t deck_switch[14];
|
||||
uint8_t pad21[2];
|
||||
uint8_t led_ticker[9];
|
||||
uint8_t spot_light_1[4];
|
||||
uint8_t neon_light;
|
||||
uint8_t spot_light_2[4];
|
||||
uint8_t pad41[7];
|
||||
};
|
||||
|
||||
struct bio2_bi2a_status {
|
||||
uint8_t slider_1;
|
||||
uint8_t system;
|
||||
uint8_t slider_2;
|
||||
uint8_t pad3;
|
||||
uint8_t slider_3;
|
||||
uint8_t pad5;
|
||||
uint8_t slider_4;
|
||||
uint8_t slider_5;
|
||||
uint8_t pad8;
|
||||
uint8_t panel;
|
||||
uint8_t pad10[6];
|
||||
uint8_t tt_p1;
|
||||
uint8_t tt_p2;
|
||||
uint8_t p1_s1;
|
||||
uint8_t pad20;
|
||||
uint8_t p1_s2;
|
||||
uint8_t pad22;
|
||||
uint8_t p1_s3;
|
||||
uint8_t pad24;
|
||||
uint8_t p1_s4;
|
||||
uint8_t pad26;
|
||||
uint8_t p1_s5;
|
||||
uint8_t pad28;
|
||||
uint8_t p1_s6;
|
||||
uint8_t pad30;
|
||||
uint8_t p1_s7;
|
||||
uint8_t pad32;
|
||||
uint8_t p2_s1;
|
||||
uint8_t pad34;
|
||||
uint8_t p2_s2;
|
||||
uint8_t pad36;
|
||||
uint8_t p2_s3;
|
||||
uint8_t pad38;
|
||||
uint8_t p2_s4;
|
||||
uint8_t pad40;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class BI2A : public ACIODeviceEmu {
|
||||
private:
|
||||
uint8_t coin_counter = 0;
|
||||
|
||||
public:
|
||||
explicit BI2A(bool type_new, bool flip_order, bool keypad_thread, uint8_t node_count);
|
||||
~BI2A() override;
|
||||
|
||||
bool parse_msg(unsigned int node_offset,
|
||||
MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
void update_card(int unit);
|
||||
void update_keypad(int unit, bool update_edge);
|
||||
void update_status(int unit);
|
||||
};
|
||||
}
|
118
acioemu/device.cpp
Normal file
118
acioemu/device.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "device.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
void ACIODeviceEmu::set_header(MessageData* data, uint8_t addr, uint16_t code, uint8_t pid,
|
||||
uint8_t data_size)
|
||||
{
|
||||
// flag as response
|
||||
if (addr != 0) {
|
||||
addr |= ACIO_RESPONSE_FLAG;
|
||||
}
|
||||
|
||||
// set header data
|
||||
data->addr = addr;
|
||||
data->cmd.code = acio_u16(code);
|
||||
data->cmd.pid = pid;
|
||||
data->cmd.data_size = data_size;
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::set_version(MessageData* data, uint32_t type, uint8_t flag,
|
||||
uint8_t ver_major, uint8_t ver_minor, uint8_t ver_rev, std::string code)
|
||||
{
|
||||
|
||||
// set version data
|
||||
auto data_version = &data->cmd.data_version;
|
||||
data_version->type = type;
|
||||
data_version->flag = flag;
|
||||
data_version->ver_major = ver_major;
|
||||
data_version->ver_minor = ver_minor;
|
||||
data_version->ver_rev = ver_rev;
|
||||
strncpy(data_version->code, code.c_str(), sizeof(data_version->code));
|
||||
strncpy(data_version->date, __DATE__, sizeof(data_version->date));
|
||||
strncpy(data_version->time, __TIME__, sizeof(data_version->time));
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg(uint8_t addr, uint16_t code, uint8_t pid, size_t data_size,
|
||||
uint8_t *data)
|
||||
{
|
||||
// check data size
|
||||
if (data_size > 0xFF) {
|
||||
log_warning("acio", "data size > 255: {}", data_size);
|
||||
data_size = 0xFF;
|
||||
}
|
||||
|
||||
// allocate data
|
||||
auto data_raw = new uint8_t[MSG_HEADER_SIZE + data_size];
|
||||
|
||||
// set header
|
||||
auto msg = (MessageData *) &data_raw[0];
|
||||
set_header(msg, addr, code, pid, (uint8_t) data_size);
|
||||
|
||||
// set data
|
||||
if (data) {
|
||||
memcpy(data_raw + MSG_HEADER_SIZE, data, data_size);
|
||||
} else {
|
||||
memset(data_raw + MSG_HEADER_SIZE, 0, data_size);
|
||||
}
|
||||
|
||||
// return prepared message
|
||||
return msg;
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg(MessageData *msg_in, size_t data_size, uint8_t *data) {
|
||||
return create_msg(msg_in->addr, msg_in->cmd.code, msg_in->cmd.pid, data_size, data);
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg_status(uint8_t addr, uint16_t code, uint8_t pid, uint8_t status) {
|
||||
return create_msg(addr, code, pid, 1, &status);
|
||||
}
|
||||
|
||||
MessageData *ACIODeviceEmu::create_msg_status(MessageData *msg_in, uint8_t status) {
|
||||
return create_msg_status(msg_in->addr, msg_in->cmd.code, msg_in->cmd.pid, status);
|
||||
}
|
||||
|
||||
bool ACIODeviceEmu::is_applicable(uint8_t node_offset, uint8_t node) {
|
||||
return node > node_offset && node <= node_offset + this->node_count;
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::write_msg(const uint8_t *data, size_t size, circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
// header
|
||||
for (int i = 0; i < 2; i++) {
|
||||
response_buffer->put(ACIO_SOF);
|
||||
}
|
||||
|
||||
// msg data and checksum
|
||||
uint8_t b, chk = 0;
|
||||
for (size_t i = 0; i <= size; i++) {
|
||||
|
||||
// set byte to data or checksum
|
||||
if (i < size) {
|
||||
b = data[i];
|
||||
chk += b;
|
||||
} else {
|
||||
b = chk;
|
||||
}
|
||||
|
||||
// check for escape
|
||||
if (b == ACIO_SOF || b == ACIO_ESCAPE) {
|
||||
response_buffer->put(ACIO_ESCAPE);
|
||||
response_buffer->put(~b);
|
||||
} else {
|
||||
response_buffer->put(b);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "ACIO MSG OUT: AA{}{:02X}", bin2hex(data, size), chk);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ACIODeviceEmu::write_msg(MessageData *msg, circular_buffer<uint8_t> *response_buffer) {
|
||||
auto data = reinterpret_cast<const uint8_t *>(msg);
|
||||
write_msg(data, MSG_HEADER_SIZE + msg->cmd.data_size, response_buffer);
|
||||
}
|
103
acioemu/device.h
Normal file
103
acioemu/device.h
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util/circular_buffer.h"
|
||||
|
||||
// convert big-endian to little-endian
|
||||
#define acio_u16 _byteswap_ushort
|
||||
#define acio_u32 _byteswap_ulong
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
constexpr uint8_t ACIO_SOF = 0xAA;
|
||||
constexpr uint8_t ACIO_ESCAPE = 0xFF;
|
||||
constexpr uint8_t ACIO_BROADCAST = 0x70;
|
||||
constexpr uint8_t ACIO_RESPONSE_FLAG = 0x80;
|
||||
|
||||
// general command codes
|
||||
enum acio_cmd_codes {
|
||||
ACIO_CMD_ASSIGN_ADDRS = 0x0001,
|
||||
ACIO_CMD_GET_VERSION = 0x0002,
|
||||
ACIO_CMD_STARTUP = 0x0003,
|
||||
ACIO_CMD_KEEPALIVE = 0x0080,
|
||||
ACIO_CMD_CLEAR = 0x0100,
|
||||
};
|
||||
|
||||
// message structs
|
||||
#pragma pack(push, 1)
|
||||
struct VersionData {
|
||||
uint32_t type;
|
||||
uint8_t flag;
|
||||
uint8_t ver_major;
|
||||
uint8_t ver_minor;
|
||||
uint8_t ver_rev;
|
||||
char code[4];
|
||||
char date[16];
|
||||
char time[16];
|
||||
};
|
||||
|
||||
struct MessageData {
|
||||
uint8_t addr;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint16_t code;
|
||||
uint8_t pid;
|
||||
uint8_t data_size;
|
||||
|
||||
union {
|
||||
uint8_t raw[0xFF];
|
||||
uint8_t status;
|
||||
VersionData data_version;
|
||||
};
|
||||
} cmd;
|
||||
|
||||
struct {
|
||||
uint8_t data_size;
|
||||
uint8_t raw[0xFF];
|
||||
} broadcast;
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// message sizes
|
||||
constexpr size_t MSG_HEADER_SIZE = 5;
|
||||
constexpr size_t MSG_VERSION_SIZE = sizeof(VersionData);
|
||||
|
||||
class ACIODeviceEmu {
|
||||
public:
|
||||
|
||||
// attributes
|
||||
uint8_t node_count = 0;
|
||||
|
||||
/*
|
||||
* Helper functions for getting/setting the message contents
|
||||
*/
|
||||
static void set_header(MessageData* data, uint8_t addr, uint16_t code, uint8_t pid, uint8_t data_size);
|
||||
static void set_version(MessageData* data, uint32_t type, uint8_t flag,
|
||||
uint8_t ver_major, uint8_t ver_minor, uint8_t ver_rev,
|
||||
std::string code);
|
||||
|
||||
/*
|
||||
* This function creates a basic message with optional parameter data.
|
||||
* If data is set to null, the parameter data will be initialized with 0x00
|
||||
*/
|
||||
static MessageData* create_msg(uint8_t addr, uint16_t cmd, uint8_t pid,
|
||||
size_t data_size, uint8_t *data = nullptr);
|
||||
static MessageData* create_msg(MessageData* msg_in, size_t data_size, uint8_t *data = nullptr);
|
||||
|
||||
/*
|
||||
* Helper functions for generating messages
|
||||
*/
|
||||
static MessageData* create_msg_status(uint8_t addr, uint16_t code, uint8_t pid, uint8_t status);
|
||||
static MessageData* create_msg_status(MessageData* msg_in, uint8_t status);
|
||||
|
||||
virtual ~ACIODeviceEmu() = default;
|
||||
|
||||
virtual bool is_applicable(uint8_t node_offset, uint8_t node);
|
||||
virtual bool parse_msg(MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) = 0;
|
||||
static void write_msg(const uint8_t *data, size_t size, circular_buffer<uint8_t> *response_buffer);
|
||||
static void write_msg(MessageData *msg, circular_buffer<uint8_t> *response_buffer);
|
||||
};
|
||||
}
|
73
acioemu/handle.cpp
Normal file
73
acioemu/handle.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "handle.h"
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput/rawinput.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
acioemu::ACIOHandle::ACIOHandle(LPCWSTR lpCOMPort) {
|
||||
this->com_port = lpCOMPort;
|
||||
}
|
||||
|
||||
bool acioemu::ACIOHandle::open(LPCWSTR lpFileName) {
|
||||
if (wcscmp(lpFileName, com_port) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("acioemu", "Opened {} (ACIO)", ws2s(com_port));
|
||||
|
||||
// ACIO device
|
||||
acio_emu.add_device(new acioemu::ICCADevice(false, true, 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) {
|
||||
auto buffer = reinterpret_cast<uint8_t *>(lpBuffer);
|
||||
|
||||
// read from emu
|
||||
DWORD bytes_read = 0;
|
||||
while (bytes_read < nNumberOfBytesToRead) {
|
||||
auto cur_byte = acio_emu.read();
|
||||
|
||||
if (cur_byte.has_value()) {
|
||||
buffer[bytes_read++] = cur_byte.value();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// return amount of bytes read
|
||||
return (int) bytes_read;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) {
|
||||
auto buffer = reinterpret_cast<const uint8_t *>(lpBuffer);
|
||||
|
||||
// write to emu
|
||||
for (DWORD i = 0; i < nNumberOfBytesToWrite; i++) {
|
||||
acio_emu.write(buffer[i]);
|
||||
}
|
||||
|
||||
// return all data written
|
||||
return (int) nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
int acioemu::ACIOHandle::device_io(
|
||||
DWORD dwIoControlCode,
|
||||
LPVOID lpInBuffer,
|
||||
DWORD nInBufferSize,
|
||||
LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize
|
||||
) {
|
||||
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));
|
||||
|
||||
return true;
|
||||
}
|
30
acioemu/handle.h
Normal file
30
acioemu/handle.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "acioemu/acioemu.h"
|
||||
#include "hooks/devicehook.h"
|
||||
|
||||
namespace acioemu {
|
||||
|
||||
class ACIOHandle : public CustomHandle {
|
||||
private:
|
||||
LPCWSTR com_port;
|
||||
|
||||
acioemu::ACIOEmu acio_emu;
|
||||
|
||||
public:
|
||||
ACIOHandle(LPCWSTR lpCOMPort);
|
||||
|
||||
bool open(LPCWSTR lpFileName) override;
|
||||
|
||||
int read(LPVOID lpBuffer, DWORD nNumberOfBytesToRead) override;
|
||||
|
||||
int write(LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite) override;
|
||||
|
||||
int device_io(DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer,
|
||||
DWORD nOutBufferSize) override;
|
||||
|
||||
size_t bytes_available() override;
|
||||
|
||||
bool close() override;
|
||||
};
|
||||
}
|
581
acioemu/icca.cpp
Normal file
581
acioemu/icca.cpp
Normal file
@ -0,0 +1,581 @@
|
||||
#include "icca.h"
|
||||
|
||||
#include "acio/icca/icca.h"
|
||||
#include "avs/game.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace acioemu;
|
||||
|
||||
namespace acioemu {
|
||||
bool ICCA_DEVICE_HACK = false;
|
||||
}
|
||||
|
||||
ICCADevice::ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count) {
|
||||
|
||||
// init defaults
|
||||
this->type_new = false;
|
||||
this->flip_order = flip_order;
|
||||
this->node_count = node_count;
|
||||
this->cards = new uint8_t *[node_count] {};
|
||||
this->cards_time = new time_t[node_count] {};
|
||||
this->status = new uint8_t[node_count * 16] {};
|
||||
this->accept = new bool[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->accept[i] = true;
|
||||
}
|
||||
this->hold = new bool[node_count] {};
|
||||
this->keydown = new uint8_t[node_count] {};
|
||||
this->keypad = new uint16_t[node_count] {};
|
||||
this->keypad_last = new bool*[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->keypad_last[i] = new bool[12] {};
|
||||
}
|
||||
this->keypad_capture = new uint8_t[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->keypad_capture[i] = 0x08;
|
||||
}
|
||||
this->crypt = new std::optional<Crypt>[node_count] {};
|
||||
this->counter = new uint8_t[node_count] {};
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
this->counter[i] = 2;
|
||||
}
|
||||
|
||||
// keypad thread for faster polling
|
||||
if (keypad_thread) {
|
||||
this->keypad_thread = new std::thread([this]() {
|
||||
while (this->cards) {
|
||||
for (int unit = 0; unit < this->node_count; unit++) {
|
||||
this->update_keypad(unit, false);
|
||||
}
|
||||
Sleep(7);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ICCADevice::~ICCADevice() {
|
||||
|
||||
// stop thread
|
||||
delete keypad_thread;
|
||||
|
||||
// delete cards in array
|
||||
for (int i = 0; i < node_count; i++) {
|
||||
delete cards[i];
|
||||
}
|
||||
|
||||
// delete the rest
|
||||
delete[] cards;
|
||||
delete[] cards_time;
|
||||
delete[] status;
|
||||
delete[] accept;
|
||||
delete[] hold;
|
||||
delete[] keydown;
|
||||
delete[] keypad;
|
||||
delete[] keypad_last;
|
||||
delete[] keypad_capture;
|
||||
delete[] crypt;
|
||||
delete[] counter;
|
||||
}
|
||||
|
||||
bool ICCADevice::parse_msg(MessageData *msg_in,
|
||||
circular_buffer<uint8_t> *response_buffer) {
|
||||
|
||||
// get unit
|
||||
int unit = msg_in->addr - 1;
|
||||
if (this->flip_order) {
|
||||
unit = this->node_count - unit - 1;
|
||||
}
|
||||
if (unit != 0 && unit != 1) {
|
||||
log_fatal("icca", "invalid unit: {}", unit);
|
||||
}
|
||||
|
||||
#ifdef ACIOEMU_LOG
|
||||
log_info("acioemu", "ICCA ADDR: {}, CMD: 0x{:04x}", unit, msg_in->cmd.code);
|
||||
#endif
|
||||
|
||||
// check command
|
||||
switch (msg_in->cmd.code) {
|
||||
case ACIO_CMD_GET_VERSION: {
|
||||
|
||||
// send version data
|
||||
auto msg = this->create_msg(msg_in, MSG_VERSION_SIZE);
|
||||
if (
|
||||
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 {
|
||||
this->set_version(msg, 0x3, 0, 1, 6, 0, "ICCA");
|
||||
}
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0130: { // REINITIALIZE
|
||||
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0131: { // READ CARD UID
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
// explicitly set no card since this is just read
|
||||
msg->cmd.raw[0] = 0x01;
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0135: { // SET ACTION
|
||||
|
||||
// check for data
|
||||
if (msg_in->cmd.data_size >= 2) {
|
||||
|
||||
// subcommand
|
||||
switch (msg_in->cmd.raw[1]) {
|
||||
case 0x00: // ACCEPT DISABLE
|
||||
this->accept[unit] = false;
|
||||
break;
|
||||
case 0x11: // ACCEPT ENABLE
|
||||
this->accept[unit] = true;
|
||||
break;
|
||||
case 0x12: // EJECT
|
||||
if (this->cards[unit] != nullptr) {
|
||||
delete this->cards[unit];
|
||||
}
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no break, return status
|
||||
[[fallthrough]];
|
||||
}
|
||||
case 0x0134: { // GET STATUS
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0160: { // KEY EXCHANGE
|
||||
|
||||
// if this cmd is called, the reader type must be new
|
||||
this->type_new = true;
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 4);
|
||||
|
||||
// set key
|
||||
msg->cmd.raw[0] = 0xBE;
|
||||
msg->cmd.raw[1] = 0xEF;
|
||||
msg->cmd.raw[2] = 0xCA;
|
||||
msg->cmd.raw[3] = 0xFE;
|
||||
|
||||
// convert keys
|
||||
uint32_t game_key =
|
||||
msg_in->cmd.raw[0] << 24 |
|
||||
msg_in->cmd.raw[1] << 16 |
|
||||
msg_in->cmd.raw[2] << 8 |
|
||||
msg_in->cmd.raw[3];
|
||||
uint32_t reader_key =
|
||||
msg->cmd.raw[0] << 24 |
|
||||
msg->cmd.raw[1] << 16 |
|
||||
msg->cmd.raw[2] << 8 |
|
||||
msg->cmd.raw[3];
|
||||
|
||||
log_info("icca", "client key: {:08x}", game_key);
|
||||
log_info("icca", "reader key: {:08x}", reader_key);
|
||||
|
||||
this->crypt[unit].emplace();
|
||||
this->crypt[unit]->set_keys(reader_key, game_key);
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x0161: { // READ CARD UID NEW
|
||||
|
||||
// if this cmd is called, the reader type must be new
|
||||
this->type_new = true;
|
||||
|
||||
// decide on answer
|
||||
int answer_type = 0;
|
||||
//if (avs::game::is_model("LDJ"))
|
||||
//answer_type = 1;
|
||||
// SDVX Old cabinet mode
|
||||
if (avs::game::is_model("KFC") && avs::game::SPEC[0] != 'G' && avs::game::SPEC[0] != 'H')
|
||||
answer_type = 1;
|
||||
if (avs::game::is_model("L44"))
|
||||
answer_type = 2;
|
||||
|
||||
// check answer type
|
||||
switch (answer_type) {
|
||||
case 1: {
|
||||
|
||||
// send status 1
|
||||
auto msg = this->create_msg_status(msg_in, 1);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 16);
|
||||
|
||||
// update card
|
||||
update_card(unit);
|
||||
|
||||
// check for card
|
||||
if (this->cards[unit] != nullptr) {
|
||||
|
||||
// copy into data buffer
|
||||
memcpy(msg->cmd.raw, this->cards[unit], 8);
|
||||
|
||||
// delete card
|
||||
delete this->cards[unit];
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
}
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
// send response with no data
|
||||
auto msg = this->create_msg(msg_in, 0);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0164: { // GET STATUS ENC
|
||||
|
||||
// build data array
|
||||
auto msg = this->create_msg(msg_in, 18);
|
||||
|
||||
// update things
|
||||
update_card(unit);
|
||||
update_keypad(unit, true);
|
||||
update_status(unit);
|
||||
|
||||
// copy status
|
||||
memcpy(msg->cmd.raw, &status[unit * 16], 16);
|
||||
|
||||
if (this->crypt[unit].has_value()) {
|
||||
auto &crypt = this->crypt[unit];
|
||||
uint16_t crc = crypt->crc(msg->cmd.raw, 16);
|
||||
|
||||
msg->cmd.raw[16] = (uint8_t) (crc >> 8);
|
||||
msg->cmd.raw[17] = (uint8_t) crc;
|
||||
|
||||
crypt->crypt(msg->cmd.raw, 18);
|
||||
} else {
|
||||
log_warning("icca", "'GET STATUS ENC' message received with no crypt keys initialized");
|
||||
}
|
||||
|
||||
// write message
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case 0x013A: { // POWER CONTROL (tentative name, used in 1.7 firmware)
|
||||
// TODO(felix): isolate this logic to LDJ and/or firmware 1.7 emulation
|
||||
|
||||
if (this->counter[unit] > 0) {
|
||||
this->counter[unit]--;
|
||||
}
|
||||
//log_info("icca", "counter[{}] = {}", unit, this->counter[unit]);
|
||||
|
||||
auto msg = this->create_msg_status(msg_in, this->counter[unit]);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
case ACIO_CMD_STARTUP:
|
||||
case ACIO_CMD_CLEAR:
|
||||
case 0x30: // GetBoardProductNumber
|
||||
case 0x31: // GetMicomInfo
|
||||
case 0x0116: // ???
|
||||
case 0x0120: // ???
|
||||
case 0xFF: // BROADCAST
|
||||
{
|
||||
// send status 0
|
||||
auto msg = this->create_msg_status(msg_in, 0x00);
|
||||
write_msg(msg, response_buffer);
|
||||
delete msg;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// mark as handled
|
||||
return true;
|
||||
}
|
||||
|
||||
void ICCADevice::update_card(int unit) {
|
||||
|
||||
// wavepass timeout after 10s
|
||||
if (this->cards[unit] != nullptr) {
|
||||
time_t t_now;
|
||||
time(&t_now);
|
||||
|
||||
if (difftime(t_now, this->cards_time[unit]) >= 10.f) {
|
||||
if (this->cards[unit] != nullptr) {
|
||||
delete this->cards[unit];
|
||||
}
|
||||
this->cards[unit] = nullptr;
|
||||
this->hold[unit] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool kb_insert_press = false;
|
||||
|
||||
// eamio keypress
|
||||
kb_insert_press |= eamuse_get_keypad_state((size_t) unit) & (1 << EAM_IO_INSERT);
|
||||
|
||||
// check for card
|
||||
if (this->cards[unit] == nullptr && (eamuse_card_insert_consume(this->node_count, unit) || kb_insert_press)) {
|
||||
auto card = new uint8_t[8];
|
||||
|
||||
if (!eamuse_get_card(this->node_count, unit, card)) {
|
||||
|
||||
// invalid card found
|
||||
delete[] card;
|
||||
|
||||
} else {
|
||||
this->cards[unit] = card;
|
||||
time(&this->cards_time[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, // 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
|
||||
};
|
||||
|
||||
void ICCADevice::update_keypad(int unit, bool update_edge) {
|
||||
|
||||
// lock keypad so threads can't interfere
|
||||
std::lock_guard<std::mutex> lock(this->keypad_mutex);
|
||||
|
||||
// reset unit
|
||||
this->keypad[unit] = 0;
|
||||
|
||||
// get eamu key states
|
||||
uint16_t eamu_state = eamuse_get_keypad_state((size_t) unit);
|
||||
|
||||
// iterate keypad
|
||||
bool edge = false;
|
||||
for (int n = 0; n < 12; n++) {
|
||||
int i = n;
|
||||
|
||||
// check if pressed
|
||||
if (eamu_state & (1 << KEYPAD_EAMUSE_MAPPING[i])) {
|
||||
|
||||
if (ICCA_DEVICE_HACK) {
|
||||
this->keypad[unit] |= KEYPAD_KEY_CODES_ALT[i];
|
||||
} else {
|
||||
this->keypad[unit] |= KEYPAD_KEY_CODES[i];
|
||||
}
|
||||
|
||||
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;
|
||||
edge = true;
|
||||
}
|
||||
} else {
|
||||
this->keypad_last[unit][i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update keypad capture
|
||||
if (update_edge && edge) {
|
||||
this->keypad_capture[unit]++;
|
||||
this->keypad_capture[unit] |= 0x08;
|
||||
} else {
|
||||
this->keydown[unit] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ICCADevice::update_status(int unit) {
|
||||
|
||||
// get buffer
|
||||
uint8_t *buffer = &this->status[unit * 16];
|
||||
|
||||
// clear buffer
|
||||
memset(buffer, 0x00, 16);
|
||||
|
||||
// check for card
|
||||
bool card = false;
|
||||
if (this->cards[unit] != nullptr) {
|
||||
|
||||
// copy card into buffer
|
||||
memcpy(buffer + 2, this->cards[unit], 8);
|
||||
card = true;
|
||||
}
|
||||
|
||||
// check for reader type
|
||||
if (this->type_new) {
|
||||
|
||||
// check for card
|
||||
if (card) {
|
||||
|
||||
// set status to card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
/*
|
||||
* set card type
|
||||
* 0x00 - ISO15696
|
||||
* 0x01 - FELICA
|
||||
*/
|
||||
bool felica = buffer[2] != 0xE0 && buffer[3] != 0x04;
|
||||
buffer[1] = felica ? 0x01 : 0x00;
|
||||
buffer[10] = felica ? 0x01 : 0x00;
|
||||
|
||||
} else if (
|
||||
avs::game::is_model({"LDJ", "TBS"}) ||
|
||||
// SDVX Valkyrie cabinet mode
|
||||
(avs::game::is_model("KFC") && (avs::game::SPEC[0] == 'G' || avs::game::SPEC[0] == 'H'))) {
|
||||
|
||||
// set status to 0 otherwise reader power on fails
|
||||
buffer[0] = 0x00;
|
||||
} else {
|
||||
|
||||
// set status to no card present (1 or 4)
|
||||
buffer[0] = 0x04;
|
||||
}
|
||||
} else { // old reader
|
||||
|
||||
// check for card
|
||||
if (card && accept[unit]) {
|
||||
this->hold[unit] = true;
|
||||
}
|
||||
|
||||
// check for hold
|
||||
if (this->hold[unit]) {
|
||||
|
||||
// set status to card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
/*
|
||||
* sensors
|
||||
* 0x10 - OLD READER FRONT
|
||||
* 0x20 - OLD READER BACK
|
||||
*/
|
||||
|
||||
// activate both sensors
|
||||
buffer[1] = 0x30;
|
||||
} else {
|
||||
|
||||
// card present but reader isn't accepting it
|
||||
if (card) {
|
||||
|
||||
// set card present
|
||||
buffer[0] = 0x02;
|
||||
|
||||
// set front sensor
|
||||
buffer[1] = 0x10;
|
||||
|
||||
} else {
|
||||
|
||||
// no card present
|
||||
buffer[0] = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
// card type not present for old reader
|
||||
buffer[10] = 0x00;
|
||||
}
|
||||
|
||||
// other flags
|
||||
buffer[11] = 0x03;
|
||||
buffer[12] = keydown[unit];
|
||||
buffer[13] = 0x00;
|
||||
buffer[14] = (uint8_t) (keypad[unit] >> 8);
|
||||
buffer[15] = (uint8_t) (keypad[unit] & 0xFF);
|
||||
}
|
44
acioemu/icca.h
Normal file
44
acioemu/icca.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "device.h"
|
||||
#include "hooks/sleephook.h"
|
||||
#include "reader/crypt.h"
|
||||
|
||||
namespace acioemu {
|
||||
extern bool ICCA_DEVICE_HACK;
|
||||
|
||||
class ICCADevice : public ACIODeviceEmu {
|
||||
private:
|
||||
bool type_new;
|
||||
bool flip_order;
|
||||
std::thread *keypad_thread;
|
||||
std::mutex keypad_mutex;
|
||||
uint8_t **cards;
|
||||
time_t *cards_time;
|
||||
uint8_t *status;
|
||||
bool *accept;
|
||||
bool *hold;
|
||||
uint8_t *keydown;
|
||||
uint16_t *keypad;
|
||||
bool **keypad_last;
|
||||
uint8_t *keypad_capture;
|
||||
std::optional<Crypt> *crypt;
|
||||
uint8_t *counter;
|
||||
|
||||
public:
|
||||
explicit ICCADevice(bool flip_order, bool keypad_thread, uint8_t node_count);
|
||||
~ICCADevice() override;
|
||||
|
||||
bool parse_msg(MessageData *msg_in, circular_buffer<uint8_t> *response_buffer) override;
|
||||
|
||||
void update_card(int unit);
|
||||
void update_keypad(int unit, bool update_edge);
|
||||
void update_status(int unit);
|
||||
};
|
||||
}
|
459
api/controller.cpp
Normal file
459
api/controller.cpp
Normal file
@ -0,0 +1,459 @@
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "cfg/configurator.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/analogs.h"
|
||||
#include "modules/buttons.h"
|
||||
#include "modules/card.h"
|
||||
#include "modules/capture.h"
|
||||
#include "modules/coin.h"
|
||||
#include "modules/control.h"
|
||||
#include "modules/drs.h"
|
||||
#include "modules/iidx.h"
|
||||
#include "modules/info.h"
|
||||
#include "modules/keypads.h"
|
||||
#include "modules/lcd.h"
|
||||
#include "modules/lights.h"
|
||||
#include "modules/memory.h"
|
||||
#include "modules/touch.h"
|
||||
#include "request.h"
|
||||
#include "response.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
using namespace api;
|
||||
|
||||
Controller::Controller(unsigned short port, std::string password, bool pretty)
|
||||
: port(port), password(std::move(password)), pretty(pretty)
|
||||
{
|
||||
if (!crypt::INITIALIZED && !this->password.empty()) {
|
||||
log_fatal("api", "API server with password cannot be used without crypt module");
|
||||
}
|
||||
|
||||
// WSA startup
|
||||
WSADATA wsa_data;
|
||||
int error;
|
||||
if ((error = WSAStartup(MAKEWORD(2, 2), &wsa_data)) != 0) {
|
||||
log_warning("api", "WSAStartup() returned {}", error);
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// create socket
|
||||
this->server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (this->server == INVALID_SOCKET) {
|
||||
log_warning("api", "could not create listener socket: {}", get_last_error_string());
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// configure socket
|
||||
int opt_enable = 1;
|
||||
if (setsockopt(this->server, SOL_SOCKET, SO_REUSEADDR,
|
||||
reinterpret_cast<const char *>(&opt_enable), sizeof(int)) == -1)
|
||||
{
|
||||
log_warning("api", "could not set socket option SO_REUSEADDR: {}", get_last_error_string());
|
||||
}
|
||||
if (setsockopt(this->server, IPPROTO_TCP, TCP_NODELAY,
|
||||
reinterpret_cast<const char *>(&opt_enable), sizeof(int)) == -1)
|
||||
{
|
||||
log_warning("api", "could not set socket option TCP_NODELAY: {}", get_last_error_string());
|
||||
}
|
||||
|
||||
// create address
|
||||
sockaddr_in server_address{};
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_port = htons(this->port);
|
||||
server_address.sin_addr.s_addr = INADDR_ANY;
|
||||
memset(&server_address.sin_zero, 0, sizeof(server_address.sin_zero));
|
||||
|
||||
// bind socket to address
|
||||
if (bind(this->server, (sockaddr *) &server_address, sizeof(sockaddr)) == -1) {
|
||||
log_warning("api", "could not bind socket on port {}: {}", port, get_last_error_string());
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// set socket to listen
|
||||
if (listen(this->server, server_backlog) == -1) {
|
||||
log_warning("api", "could not listen to socket on port {}: {}", port, get_last_error_string());
|
||||
this->server = INVALID_SOCKET;
|
||||
if (!cfg::CONFIGURATOR_STANDALONE) {
|
||||
log_fatal("api", "failed to start server");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// start workers
|
||||
this->server_running = true;
|
||||
for (int i = 0; i < server_worker_count; i++) {
|
||||
this->server_workers.emplace_back(std::thread([this] {
|
||||
this->server_worker();
|
||||
}));
|
||||
}
|
||||
|
||||
// log success
|
||||
log_info("api", "API server is listening on port: {}", this->port);
|
||||
log_info("api", "Using password: {}", this->password.empty() ? "no" : "yes");
|
||||
|
||||
// start websocket on next port
|
||||
this->websocket = new WebSocketController(this, port + 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
|
||||
// stop websocket
|
||||
delete this->websocket;
|
||||
|
||||
// stop serial controllers
|
||||
for (auto &s : this->serial) {
|
||||
delete s;
|
||||
}
|
||||
|
||||
// mark server stop
|
||||
this->server_running = false;
|
||||
|
||||
// close socket
|
||||
if (this->server != INVALID_SOCKET) {
|
||||
closesocket(this->server);
|
||||
}
|
||||
|
||||
// lock handlers
|
||||
std::lock_guard<std::mutex> handlers_guard(this->server_handlers_m);
|
||||
|
||||
// join threads
|
||||
for (auto &worker : this->server_workers) {
|
||||
worker.join();
|
||||
}
|
||||
for (auto &handler : this->server_handlers) {
|
||||
handler.join();
|
||||
}
|
||||
|
||||
// cleanup WSA
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void Controller::listen_serial(std::string port, DWORD baud) {
|
||||
this->serial.push_back(new SerialController(this, port, baud));
|
||||
}
|
||||
|
||||
void Controller::server_worker() {
|
||||
|
||||
// connection loop
|
||||
while (this->server_running) {
|
||||
|
||||
// create client state
|
||||
ClientState client_state {};
|
||||
|
||||
// accept connection
|
||||
int socket_in_size = sizeof(sockaddr_in);
|
||||
client_state.socket = accept(this->server, (sockaddr *) &client_state.address, &socket_in_size);
|
||||
if (client_state.socket == INVALID_SOCKET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// lock handlers
|
||||
std::lock_guard<std::mutex> handlers_guard(this->server_handlers_m);
|
||||
|
||||
// check connection limit
|
||||
if (this->server_handlers.size() >= server_connection_limit) {
|
||||
log_warning("api", "connection limit hit");
|
||||
closesocket(client_state.socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle connection
|
||||
this->server_handlers.emplace_back(std::thread([this, client_state] {
|
||||
this->connection_handler(client_state);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::connection_handler(api::ClientState client_state) {
|
||||
|
||||
// get address string
|
||||
char client_address_str_data[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &client_state.address.sin_addr, client_address_str_data, INET_ADDRSTRLEN);
|
||||
std::string client_address_str(client_address_str_data);
|
||||
|
||||
// log connection
|
||||
log_info("api", "client connected: {}", client_address_str);
|
||||
client_states_m.lock();
|
||||
client_states.emplace_back(&client_state);
|
||||
client_states_m.unlock();
|
||||
|
||||
// init state
|
||||
init_state(&client_state);
|
||||
|
||||
// listen loop
|
||||
std::vector<char> message_buffer;
|
||||
char receive_buffer[server_receive_buffer_size];
|
||||
while (this->server_running && !client_state.close) {
|
||||
|
||||
// receive data
|
||||
int received_length = recv(client_state.socket, receive_buffer, server_receive_buffer_size, 0);
|
||||
if (received_length < 0) {
|
||||
|
||||
// if the received length is < 0, we've got an error
|
||||
log_warning("api", "receive error: {}", WSAGetLastError());
|
||||
break;
|
||||
} else if (received_length == 0) {
|
||||
|
||||
// if the received length is 0, the connection is closed
|
||||
break;
|
||||
}
|
||||
|
||||
// cipher
|
||||
if (client_state.cipher != nullptr) {
|
||||
client_state.cipher->crypt(
|
||||
(uint8_t *) receive_buffer,
|
||||
(size_t) received_length
|
||||
);
|
||||
}
|
||||
|
||||
// put into buffer
|
||||
for (int i = 0; i < received_length; i++) {
|
||||
|
||||
// check for escape byte
|
||||
if (receive_buffer[i] == 0) {
|
||||
|
||||
// get response
|
||||
std::vector<char> send_buffer;
|
||||
this->process_request(&client_state, &message_buffer, &send_buffer);
|
||||
|
||||
// clear message buffer
|
||||
message_buffer.clear();
|
||||
|
||||
// check send buffer for content
|
||||
if (!send_buffer.empty()) {
|
||||
|
||||
// cipher
|
||||
if (client_state.cipher != nullptr) {
|
||||
client_state.cipher->crypt(
|
||||
(uint8_t *) send_buffer.data(),
|
||||
(size_t) send_buffer.size()
|
||||
);
|
||||
}
|
||||
|
||||
// send data
|
||||
send(client_state.socket, send_buffer.data(), (int) send_buffer.size(), 0);
|
||||
|
||||
// check for password change
|
||||
process_password_change(&client_state);
|
||||
}
|
||||
} else {
|
||||
|
||||
// append to message
|
||||
message_buffer.push_back(receive_buffer[i]);
|
||||
|
||||
// check buffer size
|
||||
if (message_buffer.size() > server_message_buffer_max_size) {
|
||||
message_buffer.clear();
|
||||
client_state.close = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log disconnect
|
||||
log_info("api", "client disconnected: {}", client_address_str);
|
||||
client_states_m.lock();
|
||||
client_states.erase(std::remove(client_states.begin(), client_states.end(), &client_state));
|
||||
client_states_m.unlock();
|
||||
|
||||
// close connection
|
||||
closesocket(client_state.socket);
|
||||
|
||||
// free state
|
||||
free_state(&client_state);
|
||||
}
|
||||
|
||||
bool Controller::process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out) {
|
||||
return this->process_request(state, &(*in)[0], in->size(), out);
|
||||
}
|
||||
|
||||
bool Controller::process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out) {
|
||||
|
||||
// parse document
|
||||
Document document;
|
||||
document.Parse(in, in_size);
|
||||
|
||||
// check for parse error
|
||||
if (document.HasParseError()) {
|
||||
|
||||
// return empty response and close connection
|
||||
out->push_back(0);
|
||||
state->close = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// build request and response
|
||||
Request request(document);
|
||||
Response response(request.id);
|
||||
bool success = true;
|
||||
|
||||
// check if request has parse error
|
||||
if (request.parse_error) {
|
||||
Value module_error("Request parse error (invalid message format?).");
|
||||
response.add_error(module_error);
|
||||
success = false;
|
||||
} else {
|
||||
|
||||
// find module
|
||||
bool module_found = false;
|
||||
for (auto module : state->modules) {
|
||||
if (module->name == request.module) {
|
||||
module_found = true;
|
||||
|
||||
// check password force
|
||||
if (module->password_force && this->password.empty() && request.function != "session_refresh") {
|
||||
Value err("Module requires the password to be set.");
|
||||
response.add_error(err);
|
||||
break;
|
||||
}
|
||||
|
||||
// handle request
|
||||
module->handle(request, response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if module wasn't found
|
||||
if (!module_found) {
|
||||
Value module_error("Unknown module.");
|
||||
response.add_error(module_error);
|
||||
}
|
||||
|
||||
// check for password change
|
||||
if (response.password_changed) {
|
||||
state->password = response.password;
|
||||
state->password_change = true;
|
||||
}
|
||||
}
|
||||
|
||||
// write response
|
||||
auto response_out = response.get_string(this->pretty);
|
||||
out->insert(out->end(), response_out.begin(), response_out.end());
|
||||
out->push_back(0);
|
||||
return success;
|
||||
}
|
||||
|
||||
void Controller::process_password_change(api::ClientState *state) {
|
||||
|
||||
// check for password change
|
||||
if (state->password_change) {
|
||||
state->password_change = false;
|
||||
delete state->cipher;
|
||||
if (state->password.empty()) {
|
||||
state->cipher = nullptr;
|
||||
} else {
|
||||
state->cipher = new util::RC4(
|
||||
(uint8_t *) state->password.c_str(),
|
||||
state->password.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::init_state(api::ClientState *state) {
|
||||
|
||||
// check if already initialized
|
||||
if (!state->modules.empty()) {
|
||||
log_fatal("api", "client state double initialization");
|
||||
}
|
||||
|
||||
// cipher
|
||||
state->cipher = nullptr;
|
||||
state->password = this->password;
|
||||
if (!this->password.empty()) {
|
||||
state->cipher = new util::RC4((uint8_t *) this->password.c_str(), this->password.size());
|
||||
}
|
||||
|
||||
// create module instances
|
||||
state->modules.push_back(new modules::Analogs());
|
||||
state->modules.push_back(new modules::Buttons());
|
||||
state->modules.push_back(new modules::Card());
|
||||
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::DRS());
|
||||
state->modules.push_back(new modules::IIDX());
|
||||
state->modules.push_back(new modules::Info());
|
||||
state->modules.push_back(new modules::Keypads());
|
||||
state->modules.push_back(new modules::LCD());
|
||||
state->modules.push_back(new modules::Lights());
|
||||
state->modules.push_back(new modules::Memory());
|
||||
state->modules.push_back(new modules::Touch());
|
||||
}
|
||||
|
||||
void Controller::free_state(api::ClientState *state) {
|
||||
|
||||
// free modules
|
||||
for (auto module : state->modules) {
|
||||
delete module;
|
||||
}
|
||||
|
||||
// free cipher
|
||||
delete state->cipher;
|
||||
}
|
||||
|
||||
void Controller::free_socket() {
|
||||
if (this->server != INVALID_SOCKET) {
|
||||
closesocket(this->server);
|
||||
this->server = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
this->websocket->free_socket();
|
||||
|
||||
for (auto &s : this->serial) {
|
||||
s->free_port();
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::obtain_client_states(std::vector<ClientState> *vec) {
|
||||
std::lock_guard<std::mutex> lock(this->client_states_m);
|
||||
for (auto &state : this->client_states) {
|
||||
vec->push_back(*state);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Controller::get_ip_address(sockaddr_in addr) {
|
||||
switch (addr.sin_family) {
|
||||
default:
|
||||
case AF_INET: {
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
auto ret = inet_ntop(AF_INET, &(addr.sin_addr), buf, sizeof(buf));
|
||||
if (ret != nullptr) {
|
||||
return std::string(ret);
|
||||
} else {
|
||||
return "unknown (" + to_string(WSAGetLastError()) + ")";
|
||||
}
|
||||
}
|
||||
case AF_INET6: {
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
auto ret = inet_ntop(AF_INET6, &(addr.sin_addr), buf, sizeof(buf));
|
||||
if (ret != nullptr) {
|
||||
return std::string(ret);
|
||||
} else {
|
||||
return "unknown (" + to_string(WSAGetLastError()) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
api/controller.h
Normal file
82
api/controller.h
Normal file
@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "util/rc4.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "websocket.h"
|
||||
#include "serial.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
struct ClientState {
|
||||
SOCKADDR_IN address;
|
||||
SOCKET socket;
|
||||
bool close = false;
|
||||
std::vector<Module*> modules;
|
||||
std::string password;
|
||||
bool password_change = false;
|
||||
util::RC4 *cipher = nullptr;
|
||||
};
|
||||
|
||||
class Controller {
|
||||
private:
|
||||
|
||||
// configuration
|
||||
const static int server_backlog = 16;
|
||||
const static int server_receive_buffer_size = 64 * 1024;
|
||||
const static int server_message_buffer_max_size = 64 * 1024;
|
||||
const static int server_worker_count = 2;
|
||||
const static int server_connection_limit = 4096;
|
||||
|
||||
// settings
|
||||
unsigned short port;
|
||||
std::string password;
|
||||
bool pretty;
|
||||
|
||||
// server
|
||||
WebSocketController *websocket;
|
||||
std::vector<SerialController *> serial;
|
||||
std::vector<std::thread> server_workers;
|
||||
std::vector<std::thread> server_handlers;
|
||||
std::mutex server_handlers_m;
|
||||
std::vector<api::ClientState *> client_states;
|
||||
std::mutex client_states_m;
|
||||
SOCKET server;
|
||||
void server_worker();
|
||||
void connection_handler(ClientState client_state);
|
||||
|
||||
public:
|
||||
|
||||
// state
|
||||
bool server_running;
|
||||
|
||||
// constructor / destructor
|
||||
Controller(unsigned short port, std::string password, bool pretty);
|
||||
~Controller();
|
||||
|
||||
void listen_serial(std::string port, DWORD baud);
|
||||
|
||||
bool process_request(ClientState *state, std::vector<char> *in, std::vector<char> *out);
|
||||
bool process_request(ClientState *state, const char *in, size_t in_size, std::vector<char> *out);
|
||||
static void process_password_change(ClientState *state);
|
||||
|
||||
void init_state(ClientState *state);
|
||||
static void free_state(ClientState *state);
|
||||
|
||||
void free_socket();
|
||||
void obtain_client_states(std::vector<ClientState> *output);
|
||||
|
||||
std::string get_ip_address(sockaddr_in addr);
|
||||
|
||||
inline const std::string &get_password() const {
|
||||
return this->password;
|
||||
}
|
||||
};
|
||||
}
|
43
api/module.cpp
Normal file
43
api/module.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include <utility>
|
||||
|
||||
#include "util/logging.h"
|
||||
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
bool LOGGING = false;
|
||||
|
||||
Module::Module(std::string name, bool password_force) {
|
||||
this->name = std::move(name);
|
||||
this->password_force = password_force;
|
||||
}
|
||||
|
||||
void Module::handle(Request &req, Response &res) {
|
||||
|
||||
// log module access
|
||||
if (LOGGING)
|
||||
log_info("api::" + this->name, "handling request");
|
||||
|
||||
// find function
|
||||
auto pos = functions.find(req.function);
|
||||
if (pos == functions.end())
|
||||
return error_function_unknown(res);
|
||||
|
||||
// call function
|
||||
pos->second(req, res);
|
||||
}
|
||||
|
||||
void Module::error(Response &res, std::string err) {
|
||||
|
||||
// log the warning
|
||||
log_warning("api::" + this->name, "error: {}", err);
|
||||
|
||||
// add error to response
|
||||
Value val(err.c_str(), res.doc()->GetAllocator());
|
||||
res.add_error(val);
|
||||
}
|
||||
}
|
67
api/module.h
Normal file
67
api/module.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "response.h"
|
||||
#include "request.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
// logging setting
|
||||
extern bool LOGGING;
|
||||
|
||||
// callback
|
||||
typedef std::function<void(Request &, Response &)> ModuleFunctionCallback;
|
||||
|
||||
class Module {
|
||||
protected:
|
||||
|
||||
// map of available functions
|
||||
robin_hood::unordered_map<std::string, ModuleFunctionCallback> functions;
|
||||
|
||||
// default constructor
|
||||
explicit Module(std::string name, bool password_force=false);
|
||||
|
||||
public:
|
||||
|
||||
// virtual deconstructor
|
||||
virtual ~Module() = default;
|
||||
|
||||
// name of the module (should match namespace)
|
||||
std::string name;
|
||||
bool password_force;
|
||||
|
||||
// the magic
|
||||
void handle(Request &req, Response &res);
|
||||
|
||||
/*
|
||||
* Error definitions.
|
||||
*/
|
||||
|
||||
void error(Response &res, std::string err);
|
||||
void error_type(Response &res, const std::string &field, const std::string &type) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be a " << type;
|
||||
error(res, s.str());
|
||||
};
|
||||
void error_size(Response &res, const std::string &field, size_t size) {
|
||||
std::ostringstream s;
|
||||
s << field << " must be of size " << size;
|
||||
error(res, s.str());
|
||||
}
|
||||
void error_unknown(Response &res, const std::string &field, const std::string &name) {
|
||||
std::ostringstream s;
|
||||
s << "Unknown " << field << ": " << name;
|
||||
error(res, s.str());
|
||||
}
|
||||
|
||||
#define ERR(name, err) void error_##name(Response &res) { error(res, err); }
|
||||
ERR(function_unknown, "Unknown function.");
|
||||
ERR(params_insufficient, "Insufficient number of parameters.");
|
||||
#undef ERR
|
||||
};
|
||||
}
|
177
api/modules/analogs.cpp
Normal file
177
api/modules/analogs.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
#include "analogs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/analog.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Analogs::Analogs() : Module("analogs") {
|
||||
functions["read"] = std::bind(&Analogs::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Analogs::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Analogs::write_reset, this, _1, _2);
|
||||
analogs = games::get_analogs(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Analogs::read(api::Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
Value state(kArrayType);
|
||||
Value analog_name(analog.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value analog_state(GameAPI::Analogs::getState(RI_MGR, analog));
|
||||
Value analog_enabled(analog.override_enabled);
|
||||
state.PushBack(analog_name, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_state, res.doc()->GetAllocator());
|
||||
state.PushBack(analog_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Analogs::write(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
auto analog_state = param[1].GetFloat();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog(analog_name, analog_state)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Analogs::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs)
|
||||
return;
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (analogs != nullptr) {
|
||||
for (auto &analog : *this->analogs) {
|
||||
analog.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto analog_name = param[0].GetString();
|
||||
|
||||
// write analog state
|
||||
if (!this->write_analog_reset(analog_name)) {
|
||||
error_unknown(res, "analog", analog_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Analogs::write_analog(std::string name, float state) {
|
||||
|
||||
// check analog cache
|
||||
if (!this->analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_state = CLAMP(state, 0.f, 1.f);
|
||||
analog.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Analogs::write_analog_reset(std::string name) {
|
||||
|
||||
// check analog cache
|
||||
if (!analogs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find analog
|
||||
for (auto &analog : *this->analogs) {
|
||||
if (analog.getName() == name) {
|
||||
analog.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown analog
|
||||
return false;
|
||||
}
|
||||
}
|
28
api/modules/analogs.h
Normal file
28
api/modules/analogs.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Analogs : public Module {
|
||||
public:
|
||||
Analogs();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Analog> *analogs;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_analog(std::string name, float state);
|
||||
bool write_analog_reset(std::string name);
|
||||
};
|
||||
}
|
182
api/modules/buttons.cpp
Normal file
182
api/modules/buttons.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#include "buttons.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/button.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Buttons::Buttons() : Module("buttons") {
|
||||
functions["read"] = std::bind(&Buttons::read, this, _1, _2);
|
||||
functions["write"] = std::bind(&Buttons::write, this, _1, _2);
|
||||
functions["write_reset"] = std::bind(&Buttons::write_reset, this, _1, _2);
|
||||
buttons = games::get_buttons(eamuse_get_game());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Buttons::read(api::Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add state for each button
|
||||
for (auto &button : *this->buttons) {
|
||||
Value state(kArrayType);
|
||||
Value button_name(button.getName().c_str(), res.doc()->GetAllocator());
|
||||
Value button_state(GameAPI::Buttons::getVelocity(RI_MGR, button));
|
||||
Value button_enabled(button.override_enabled);
|
||||
state.PushBack(button_name, res.doc()->GetAllocator());
|
||||
state.PushBack(button_state, res.doc()->GetAllocator());
|
||||
state.PushBack(button_enabled, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: bool/float], ...)
|
||||
*/
|
||||
void Buttons::write(Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsBool() && !param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "bool or float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto button_name = param[0].GetString();
|
||||
auto button_state = param[1].IsBool() ? param[1].GetBool() : param[1].GetFloat() > 0;
|
||||
auto button_velocity = param[1].IsFloat() ? param[1].GetFloat() : (button_state ? 1.f : 0.f);
|
||||
|
||||
// write button state
|
||||
if (!this->write_button(button_name, button_velocity)) {
|
||||
error_unknown(res, "button", button_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Buttons::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (buttons != nullptr) {
|
||||
for (auto &button : *this->buttons) {
|
||||
button.override_enabled = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 1) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto button_name = param[0].GetString();
|
||||
|
||||
// write button state
|
||||
if (!this->write_button_reset(button_name)) {
|
||||
error_unknown(res, "button", button_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Buttons::write_button(std::string name, float state) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find button
|
||||
for (auto &button : *this->buttons) {
|
||||
if (button.getName() == name) {
|
||||
button.override_state = state > 0.f ?
|
||||
GameAPI::Buttons::BUTTON_PRESSED : GameAPI::Buttons::BUTTON_NOT_PRESSED;
|
||||
button.override_velocity = CLAMP(state, 0.f, 1.f);
|
||||
button.override_enabled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown button
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Buttons::write_button_reset(std::string name) {
|
||||
|
||||
// check button cache
|
||||
if (!this->buttons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find button
|
||||
for (auto &button : *this->buttons) {
|
||||
if (button.getName() == name) {
|
||||
button.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown button
|
||||
return false;
|
||||
}
|
||||
}
|
28
api/modules/buttons.h
Normal file
28
api/modules/buttons.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Buttons : public Module {
|
||||
public:
|
||||
Buttons();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Button> *buttons;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_button(std::string name, float state);
|
||||
bool write_button_reset(std::string name);
|
||||
};
|
||||
}
|
82
api/modules/capture.cpp
Normal file
82
api/modules/capture.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "capture.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "hooks/graphics/graphics.h"
|
||||
#include "util/crypt.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
static thread_local std::vector<uint8_t> CAPTURE_BUFFER;
|
||||
|
||||
Capture::Capture() : Module("capture") {
|
||||
functions["get_screens"] = std::bind(&Capture::get_screens, this, _1, _2);
|
||||
functions["get_jpg"] = std::bind(&Capture::get_jpg, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_screens()
|
||||
*/
|
||||
void Capture::get_screens(Request &req, Response &res) {
|
||||
|
||||
// aquire screens
|
||||
std::vector<int> screens;
|
||||
graphics_screens_get(screens);
|
||||
|
||||
// add screens to response
|
||||
for (auto &screen : screens) {
|
||||
res.add_data(screen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_jpg([screen=0, quality=70, downscale=0, divide=1])
|
||||
* screen: uint specifying the window
|
||||
* quality: uint in range [0, 100]
|
||||
* reduce: uint for dividing image size
|
||||
*/
|
||||
void Capture::get_jpg(Request &req, Response &res) {
|
||||
CAPTURE_BUFFER.reserve(1024 * 128);
|
||||
|
||||
// settings
|
||||
int screen = 0;
|
||||
int quality = 70;
|
||||
int divide = 1;
|
||||
if (req.params.Size() > 0 && req.params[0].IsUint())
|
||||
screen = req.params[0].GetUint();
|
||||
if (req.params.Size() > 1 && req.params[1].IsUint())
|
||||
quality = req.params[1].GetUint();
|
||||
if (req.params.Size() > 2 && req.params[2].IsUint())
|
||||
divide = req.params[2].GetUint();
|
||||
|
||||
// receive JPEG data
|
||||
uint64_t timestamp = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
graphics_capture_trigger(screen);
|
||||
bool success = graphics_capture_receive_jpeg(screen, [] (uint8_t byte) {
|
||||
CAPTURE_BUFFER.push_back(byte);
|
||||
}, true, quality, true, divide, ×tamp, &width, &height);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// encode to base64
|
||||
auto encoded = crypt::base64_encode(
|
||||
CAPTURE_BUFFER.data(),
|
||||
CAPTURE_BUFFER.size());
|
||||
|
||||
// clear buffer
|
||||
CAPTURE_BUFFER.clear();
|
||||
|
||||
// add data to response
|
||||
Value data;
|
||||
data.SetString(encoded.c_str(), encoded.length(), res.doc()->GetAllocator());
|
||||
res.add_data(timestamp);
|
||||
res.add_data(width);
|
||||
res.add_data(height);
|
||||
res.add_data(data);
|
||||
}
|
||||
}
|
18
api/modules/capture.h
Normal file
18
api/modules/capture.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Capture : public Module {
|
||||
public:
|
||||
Capture();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get_screens(Request &req, Response &res);
|
||||
void get_jpg(Request &req, Response &res);
|
||||
};
|
||||
}
|
53
api/modules/card.cpp
Normal file
53
api/modules/card.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "card.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Card::Card() : Module("card") {
|
||||
functions["insert"] = std::bind(&Card::insert, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* insert(index, card_id)
|
||||
* index: uint in range [0, 1]
|
||||
* card_id: hex string of length 16
|
||||
*/
|
||||
void Card::insert(Request &req, Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 2)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsUint())
|
||||
return error_type(res, "index", "uint");
|
||||
if (!req.params[1].IsString())
|
||||
return error_type(res, "card_id", "hex string");
|
||||
if (req.params[1].GetStringLength() != 16)
|
||||
return error_size(res, "card_id", 16);
|
||||
|
||||
// get params
|
||||
auto index = req.params[0].GetUint();
|
||||
auto card_hex = req.params[1].GetString();
|
||||
|
||||
// convert to binary
|
||||
uint8_t card_bin[8] {};
|
||||
if (!hex2bin(card_hex, card_bin)) {
|
||||
return error_type(res, "card_id", "hex string");
|
||||
}
|
||||
|
||||
// log
|
||||
if (LOGGING) {
|
||||
log_info("api::card", "inserting card: {}", card_hex);
|
||||
}
|
||||
|
||||
// insert card
|
||||
eamuse_card_insert(index & 1, card_bin);
|
||||
}
|
||||
}
|
17
api/modules/card.h
Normal file
17
api/modules/card.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Card : public Module {
|
||||
public:
|
||||
Card();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void insert(Request &req, Response &res);
|
||||
};
|
||||
}
|
79
api/modules/coin.cpp
Normal file
79
api/modules/coin.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "coin.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Coin::Coin() : Module("coin") {
|
||||
functions["get"] = std::bind(&Coin::get, this, _1, _2);
|
||||
functions["set"] = std::bind(&Coin::set, this, _1, _2);
|
||||
functions["insert"] = std::bind(&Coin::insert, this, _1, _2);
|
||||
functions["blocker_get"] = std::bind(&Coin::blocker_get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* get()
|
||||
*/
|
||||
void Coin::get(api::Request &req, api::Response &res) {
|
||||
|
||||
// get coin stock
|
||||
auto coin_stock = eamuse_coin_get_stock();
|
||||
|
||||
// insert value
|
||||
Value coin_stock_val(coin_stock);
|
||||
res.add_data(coin_stock_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* set(amount: int)
|
||||
*/
|
||||
void Coin::set(api::Request &req, api::Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "amount", "int");
|
||||
|
||||
// set coin stock
|
||||
eamuse_coin_set_stock(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* insert()
|
||||
* insert(amount: int)
|
||||
*/
|
||||
void Coin::insert(api::Request &req, api::Response &res) {
|
||||
|
||||
// insert()
|
||||
if (req.params.Size() == 0) {
|
||||
eamuse_coin_add();
|
||||
return;
|
||||
}
|
||||
|
||||
// check params
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "amount", "int");
|
||||
|
||||
// add to coin stock
|
||||
eamuse_coin_set_stock(eamuse_coin_get_stock() + std::max(0, req.params[0].GetInt()));
|
||||
}
|
||||
|
||||
/*
|
||||
* blocker_get()
|
||||
*/
|
||||
void Coin::blocker_get(api::Request &req, api::Response &res) {
|
||||
|
||||
// get block status
|
||||
auto block_status = eamuse_coin_get_block();
|
||||
|
||||
// insert value
|
||||
Value block_val(block_status);
|
||||
res.add_data(block_val);
|
||||
}
|
||||
}
|
20
api/modules/coin.h
Normal file
20
api/modules/coin.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Coin : public Module {
|
||||
public:
|
||||
Coin();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void get(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void insert(Request &req, Response &res);
|
||||
void blocker_get(Request &req, Response &res);
|
||||
};
|
||||
}
|
152
api/modules/control.cpp
Normal file
152
api/modules/control.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include "control.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <functional>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "launcher/shutdown.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/crypt.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct SignalMapping {
|
||||
int signum;
|
||||
const char* name;
|
||||
};
|
||||
static SignalMapping SIGNAL_MAPPINGS[] = {
|
||||
{ SIGABRT, "SIGABRT" },
|
||||
{ SIGFPE, "SIGFPE" },
|
||||
{ SIGILL, "SIGILL" },
|
||||
{ SIGINT, "SIGINT" },
|
||||
{ SIGSEGV, "SIGSEGV" },
|
||||
{ SIGTERM, "SIGTERM" },
|
||||
};
|
||||
|
||||
Control::Control() : Module("control", true) {
|
||||
functions["raise"] = std::bind(&Control::raise, this, _1, _2);
|
||||
functions["exit"] = std::bind(&Control::exit, this, _1, _2);
|
||||
functions["restart"] = std::bind(&Control::restart, this, _1, _2);
|
||||
functions["session_refresh"] = std::bind(&Control::session_refresh, this, _1, _2);
|
||||
functions["shutdown"] = std::bind(&Control::shutdown, this, _1, _2);
|
||||
functions["reboot"] = std::bind(&Control::reboot, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* raise(signal: str)
|
||||
*/
|
||||
void Control::raise(Request &req, Response &res) {
|
||||
|
||||
// check args
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "signal", "string");
|
||||
|
||||
// get signal
|
||||
auto signal_str = req.params[0].GetString();
|
||||
int signal_val = -1;
|
||||
for (auto mapping : SIGNAL_MAPPINGS) {
|
||||
if (_stricmp(mapping.name, signal_str) == 0) {
|
||||
signal_val = mapping.signum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check if not found
|
||||
if (signal_val < 0)
|
||||
return error_unknown(res, "signal", signal_str);
|
||||
|
||||
// raise signal
|
||||
if (::raise(signal_val))
|
||||
return error(res, "Failed to raise signo " + to_string(signal_val));
|
||||
}
|
||||
|
||||
/**
|
||||
* exit()
|
||||
* exit(code: int)
|
||||
*/
|
||||
void Control::exit(Request &req, Response &res) {
|
||||
|
||||
// exit()
|
||||
if (req.params.Size() == 0) {
|
||||
launcher::shutdown();
|
||||
}
|
||||
|
||||
// check code
|
||||
if (!req.params[0].IsInt())
|
||||
return error_type(res, "code", "int");
|
||||
|
||||
// exit
|
||||
launcher::shutdown(req.params[0].GetInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* restart()
|
||||
*/
|
||||
void Control::restart(Request &req, Response &res) {
|
||||
|
||||
// restart launcher
|
||||
launcher::restart();
|
||||
}
|
||||
|
||||
/**
|
||||
* session_refresh()
|
||||
*/
|
||||
void Control::session_refresh(Request &req, Response &res) {
|
||||
|
||||
// generate new password
|
||||
uint8_t password_bin[128];
|
||||
crypt::random_bytes(password_bin, std::size(password_bin));
|
||||
std::string password = bin2hex(&password_bin[0], std::size(password_bin));
|
||||
|
||||
// add to response
|
||||
Value password_val(password.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(password_val);
|
||||
|
||||
// change password
|
||||
res.password_change(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* shutdown()
|
||||
*/
|
||||
void Control::shutdown(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_HYBRID_SHUTDOWN | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to shutdown system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* reboot()
|
||||
*/
|
||||
void Control::reboot(Request &req, Response &res) {
|
||||
|
||||
// acquire privileges
|
||||
if (!acquire_shutdown_privs())
|
||||
return error(res, "Unable to acquire shutdown privileges");
|
||||
|
||||
// exit windows
|
||||
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE,
|
||||
SHTDN_REASON_MAJOR_APPLICATION |
|
||||
SHTDN_REASON_MINOR_MAINTENANCE))
|
||||
return error(res, "Unable to reboot system");
|
||||
|
||||
// terminate this process
|
||||
launcher::shutdown(0);
|
||||
}
|
||||
}
|
22
api/modules/control.h
Normal file
22
api/modules/control.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Control : public Module {
|
||||
public:
|
||||
Control();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void raise(Request &req, Response &res);
|
||||
void exit(Request &req, Response &res);
|
||||
void restart(Request &req, Response &res);
|
||||
void session_refresh(Request &req, Response &res);
|
||||
void shutdown(Request &req, Response &res);
|
||||
void reboot(Request &req, Response &res);
|
||||
};
|
||||
}
|
91
api/modules/drs.cpp
Normal file
91
api/modules/drs.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
#include "drs.h"
|
||||
#include <functional>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/drs/drs.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
DRS::DRS() : Module("drs") {
|
||||
functions["tapeled_get"] = std::bind(&DRS::tapeled_get, this, _1, _2);
|
||||
functions["touch_set"] = std::bind(&DRS::touch_set, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_get()
|
||||
*/
|
||||
void DRS::tapeled_get(Request &req, Response &res) {
|
||||
|
||||
// copy data to array
|
||||
Value tapeled(kArrayType);
|
||||
const size_t tape_len = sizeof(games::drs::DRS_TAPELED);
|
||||
const uint8_t *tape_raw = (uint8_t*) games::drs::DRS_TAPELED;
|
||||
tapeled.Reserve(tape_len, res.doc()->GetAllocator());
|
||||
for (size_t i = 0; i < tape_len; i++) {
|
||||
tapeled.PushBack(tape_raw[i], res.doc()->GetAllocator());
|
||||
}
|
||||
|
||||
// add to response
|
||||
res.add_data(tapeled);
|
||||
}
|
||||
|
||||
void DRS::touch_set(Request &req, Response &res) {
|
||||
|
||||
// get all touch points
|
||||
games::drs::drs_touch_t touches[16];
|
||||
size_t i = 0;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (param.Size() < 6) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsUint()) {
|
||||
error_type(res, "type", "uint");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsUint()) {
|
||||
error_type(res, "id", "uint");
|
||||
continue;
|
||||
}
|
||||
if (!param[2].IsDouble()) {
|
||||
error_type(res, "x", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[3].IsDouble()) {
|
||||
error_type(res, "y", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[4].IsDouble()) {
|
||||
error_type(res, "width", "double");
|
||||
continue;
|
||||
}
|
||||
if (!param[5].IsDouble()) {
|
||||
error_type(res, "height", "double");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto touch_type = param[0].GetUint();
|
||||
auto touch_id = param[1].GetUint();
|
||||
auto touch_x = param[2].GetDouble();
|
||||
auto touch_y = param[3].GetDouble();
|
||||
auto width = param[4].GetDouble();
|
||||
auto height = param[5].GetDouble();
|
||||
|
||||
touches[i].type = touch_type;
|
||||
touches[i].id = touch_id;
|
||||
touches[i].x = touch_x;
|
||||
touches[i].y = touch_y;
|
||||
touches[i].width = width;
|
||||
touches[i].height = height;
|
||||
i++;
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
games::drs::fire_touches(touches, i);
|
||||
}
|
||||
}
|
19
api/modules/drs.h
Normal file
19
api/modules/drs.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class DRS : public Module {
|
||||
public:
|
||||
DRS();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void tapeled_get(Request &req, Response &res);
|
||||
void touch_set(Request &req, Response &res);
|
||||
};
|
||||
}
|
72
api/modules/iidx.cpp
Normal file
72
api/modules/iidx.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "iidx.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "games/iidx/iidx.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// settings
|
||||
static const size_t TICKER_SIZE = 9;
|
||||
|
||||
IIDX::IIDX() : Module("iidx") {
|
||||
functions["ticker_get"] = std::bind(&IIDX::ticker_get, this, _1, _2);
|
||||
functions["ticker_set"] = std::bind(&IIDX::ticker_set, this, _1, _2);
|
||||
functions["ticker_reset"] = std::bind(&IIDX::ticker_reset, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_get()
|
||||
*/
|
||||
void IIDX::ticker_get(api::Request &req, Response &res) {
|
||||
|
||||
// get led ticker
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.lock();
|
||||
Value led_ticker(StringRef(games::iidx::IIDXIO_LED_TICKER, TICKER_SIZE), res.doc()->GetAllocator());
|
||||
games::iidx::IIDX_LED_TICKER_LOCK.unlock();
|
||||
|
||||
// add to response
|
||||
res.add_data(led_ticker);
|
||||
}
|
||||
|
||||
/**
|
||||
* ticker_set(text: str)
|
||||
*/
|
||||
void IIDX::ticker_set(api::Request &req, api::Response &res) {
|
||||
|
||||
// check param
|
||||
if (req.params.Size() < 1)
|
||||
return error_params_insufficient(res);
|
||||
if (!req.params[0].IsString())
|
||||
return error_type(res, "text", "str");
|
||||
|
||||
// get param
|
||||
auto text = req.params[0].GetString();
|
||||
auto text_len = req.params[0].GetStringLength();
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
|
||||
// set to read only
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = true;
|
||||
|
||||
// set led ticker
|
||||
memset(games::iidx::IIDXIO_LED_TICKER, ' ', TICKER_SIZE);
|
||||
for (size_t i = 0; i < TICKER_SIZE && i < text_len; i++) {
|
||||
games::iidx::IIDXIO_LED_TICKER[i] = text[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IIDX::ticker_reset(api::Request &req, api::Response &res) {
|
||||
|
||||
// lock
|
||||
std::lock_guard<std::mutex> ticker_lock(games::iidx::IIDX_LED_TICKER_LOCK);
|
||||
|
||||
// disable read only
|
||||
games::iidx::IIDXIO_LED_TICKER_READONLY = false;
|
||||
}
|
||||
}
|
19
api/modules/iidx.h
Normal file
19
api/modules/iidx.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class IIDX : public Module {
|
||||
public:
|
||||
IIDX();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void ticker_get(Request &req, Response &res);
|
||||
void ticker_set(Request &req, Response &res);
|
||||
void ticker_reset(Request &req, Response &res);
|
||||
};
|
||||
}
|
98
api/modules/info.cpp
Normal file
98
api/modules/info.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "info.h"
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "avs/game.h"
|
||||
#include "avs/ea3.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "build/defs.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Info::Info() : Module("info") {
|
||||
functions["avs"] = std::bind(&Info::avs, this, _1, _2);
|
||||
functions["launcher"] = std::bind(&Info::launcher, this, _1, _2);
|
||||
functions["memory"] = std::bind(&Info::memory, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* avs()
|
||||
*/
|
||||
void Info::avs(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("model", StringRef(avs::game::MODEL, 3), alloc);
|
||||
info.AddMember("dest", StringRef(avs::game::DEST, 1), alloc);
|
||||
info.AddMember("spec", StringRef(avs::game::SPEC, 1), alloc);
|
||||
info.AddMember("rev", StringRef(avs::game::REV, 1), alloc);
|
||||
info.AddMember("ext", StringRef(avs::game::EXT, 10), alloc);
|
||||
info.AddMember("services", StringRef(avs::ea3::EA3_BOOT_URL.c_str()), alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* launcher()
|
||||
*/
|
||||
void Info::launcher(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build args
|
||||
Value args(kArrayType);
|
||||
for (int count = 0; count < LAUNCHER_ARGC; count++) {
|
||||
auto arg = LAUNCHER_ARGV[count];
|
||||
args.PushBack(StringRef(arg), alloc);
|
||||
}
|
||||
|
||||
// get system time
|
||||
auto t_now = std::time(nullptr);
|
||||
auto tm_now = *std::gmtime(&t_now);
|
||||
auto tm_str = to_string(std::put_time(&tm_now, "%Y-%m-%dT%H:%M:%SZ"));
|
||||
Value system_time(tm_str.c_str(), alloc);
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("version", StringRef(VERSION_STRING), alloc);
|
||||
info.AddMember("compile_date", StringRef(__DATE__), alloc);
|
||||
info.AddMember("compile_time", StringRef(__TIME__), alloc);
|
||||
info.AddMember("system_time", system_time, alloc);
|
||||
info.AddMember("args", args, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* memory()
|
||||
*/
|
||||
void Info::memory(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("mem_total", memutils::mem_total(), alloc);
|
||||
info.AddMember("mem_total_used", memutils::mem_total_used(), alloc);
|
||||
info.AddMember("mem_used", memutils::mem_used(), alloc);
|
||||
info.AddMember("vmem_total", memutils::vmem_total(), alloc);
|
||||
info.AddMember("vmem_total_used", memutils::vmem_total_used(), alloc);
|
||||
info.AddMember("vmem_used", memutils::vmem_used(), alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
19
api/modules/info.h
Normal file
19
api/modules/info.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Info : public Module {
|
||||
public:
|
||||
Info();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void avs(Request &req, Response &res);
|
||||
void launcher(Request &req, Response &res);
|
||||
void memory(Request &req, Response &res);
|
||||
};
|
||||
}
|
176
api/modules/keypads.cpp
Normal file
176
api/modules/keypads.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#include "keypads.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "avs/game.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
struct KeypadMapping {
|
||||
char character;
|
||||
uint16_t state;
|
||||
};
|
||||
|
||||
static KeypadMapping KEYPAD_MAPPINGS[] = {
|
||||
{ '0', 1 << EAM_IO_KEYPAD_0 },
|
||||
{ '1', 1 << EAM_IO_KEYPAD_1 },
|
||||
{ '2', 1 << EAM_IO_KEYPAD_2 },
|
||||
{ '3', 1 << EAM_IO_KEYPAD_3 },
|
||||
{ '4', 1 << EAM_IO_KEYPAD_4 },
|
||||
{ '5', 1 << EAM_IO_KEYPAD_5 },
|
||||
{ '6', 1 << EAM_IO_KEYPAD_6 },
|
||||
{ '7', 1 << EAM_IO_KEYPAD_7 },
|
||||
{ '8', 1 << EAM_IO_KEYPAD_8 },
|
||||
{ '9', 1 << EAM_IO_KEYPAD_9 },
|
||||
{ 'A', 1 << EAM_IO_KEYPAD_00 },
|
||||
{ 'D', 1 << EAM_IO_KEYPAD_DECIMAL },
|
||||
};
|
||||
|
||||
Keypads::Keypads() : Module("keypads") {
|
||||
functions["write"] = std::bind(&Keypads::write, this, _1, _2);
|
||||
functions["set"] = std::bind(&Keypads::set, this, _1, _2);
|
||||
functions["get"] = std::bind(&Keypads::get, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(keypad: uint, input: str)
|
||||
*/
|
||||
void Keypads::write(Request &req, Response &res) {
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 2) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
if (!req.params[1].IsString()) {
|
||||
return error_type(res, "input", "string");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto keypad = req.params[0].GetUint();
|
||||
auto input = std::string(req.params[1].GetString());
|
||||
|
||||
// process all chars
|
||||
for (auto c : input) {
|
||||
uint16_t state = 0;
|
||||
|
||||
// find mapping
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, &c, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "char", std::string("") + c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write input to keypad.
|
||||
* We try to make sure it was accepted by waiting a bit more than two frames.
|
||||
*/
|
||||
DWORD sleep_time = 70;
|
||||
if (avs::game::is_model("MDX")) {
|
||||
|
||||
// cuz fuck DDR
|
||||
sleep_time = 150;
|
||||
}
|
||||
|
||||
// set
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
Sleep(sleep_time);
|
||||
|
||||
// unset
|
||||
eamuse_set_keypad_overrides(keypad, 0);
|
||||
Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set(keypad: uint, key: char, ...)
|
||||
*/
|
||||
void Keypads::set(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// iterate params
|
||||
uint16_t state = 0;
|
||||
auto params = req.params.GetArray();
|
||||
for (size_t i = 1; i < params.Size(); i++) {
|
||||
auto ¶m = params[i];
|
||||
|
||||
// check key
|
||||
if (!param.IsString()) {
|
||||
error_type(res, "key", "char");
|
||||
}
|
||||
if (param.GetStringLength() < 1) {
|
||||
error_size(res, "key", 1);
|
||||
}
|
||||
|
||||
// find mapping
|
||||
auto key = param.GetString();
|
||||
bool mapping_found = false;
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (_strnicmp(&mapping.character, key, 1) == 0) {
|
||||
state |= mapping.state;
|
||||
mapping_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check for error
|
||||
if (!mapping_found) {
|
||||
return error_unknown(res, "key", key);
|
||||
}
|
||||
}
|
||||
|
||||
// set keypad state
|
||||
eamuse_set_keypad_overrides(keypad, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* get(keypad: uint)
|
||||
*/
|
||||
void Keypads::get(Request &req, Response &res) {
|
||||
|
||||
// check keypad
|
||||
if (req.params.Size() < 1) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsUint()) {
|
||||
return error_type(res, "keypad", "uint");
|
||||
}
|
||||
auto keypad = req.params[0].GetUint();
|
||||
|
||||
// get keypad state
|
||||
auto state = eamuse_get_keypad_state(keypad);
|
||||
|
||||
// add keys to response
|
||||
for (auto &mapping : KEYPAD_MAPPINGS) {
|
||||
if (state & mapping.state) {
|
||||
Value val(&mapping.character, 1);
|
||||
res.add_data(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
api/modules/keypads.h
Normal file
19
api/modules/keypads.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Keypads : public Module {
|
||||
public:
|
||||
Keypads();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void set(Request &req, Response &res);
|
||||
void get(Request &req, Response &res);
|
||||
};
|
||||
}
|
36
api/modules/lcd.cpp
Normal file
36
api/modules/lcd.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "lcd.h"
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "games/shared/lcdhandle.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
LCD::LCD() : Module("lcd") {
|
||||
functions["info"] = std::bind(&LCD::info, this, _1, _2);
|
||||
}
|
||||
|
||||
/*
|
||||
* info()
|
||||
*/
|
||||
void LCD::info(Request &req, Response &res) {
|
||||
|
||||
// get allocator
|
||||
auto &alloc = res.doc()->GetAllocator();
|
||||
|
||||
// build info object
|
||||
Value info(kObjectType);
|
||||
info.AddMember("enabled", games::shared::LCD_ENABLED, alloc);
|
||||
info.AddMember("csm", StringRef(games::shared::LCD_CSM.c_str()), alloc);
|
||||
info.AddMember("bri", games::shared::LCD_BRI, alloc);
|
||||
info.AddMember("con", games::shared::LCD_CON, alloc);
|
||||
info.AddMember("bl", games::shared::LCD_BL, alloc);
|
||||
info.AddMember("red", games::shared::LCD_RED, alloc);
|
||||
info.AddMember("green", games::shared::LCD_GREEN, alloc);
|
||||
info.AddMember("blue", games::shared::LCD_BLUE, alloc);
|
||||
|
||||
// add info object
|
||||
res.add_data(info);
|
||||
}
|
||||
}
|
17
api/modules/lcd.h
Normal file
17
api/modules/lcd.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class LCD : public Module {
|
||||
public:
|
||||
LCD();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void info(Request &req, Response &res);
|
||||
};
|
||||
}
|
191
api/modules/lights.cpp
Normal file
191
api/modules/lights.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
#include "lights.h"
|
||||
#include <functional>
|
||||
#include <cfg/configurator.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "misc/eamuse.h"
|
||||
#include "cfg/light.h"
|
||||
#include "launcher/launcher.h"
|
||||
#include "games/io.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
Lights::Lights() : Module("lights") {
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Lights::read(api::Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([name: str, state: float], ...)
|
||||
*/
|
||||
void Lights::write(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
if (param.Size() < 2) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsString()) {
|
||||
error_type(res, "name", "string");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsFloat() && !param[1].IsInt()) {
|
||||
error_type(res, "state", "float");
|
||||
continue;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto light_name = param[0].GetString();
|
||||
auto light_state = param[1].GetFloat();
|
||||
|
||||
// write light state
|
||||
if (!this->write_light(light_name, light_state)) {
|
||||
error_unknown(res, "light", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset()
|
||||
* write_reset([name: str], ...)
|
||||
*/
|
||||
void Lights::write_reset(Request &req, Response &res) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get params
|
||||
auto params = req.params.GetArray();
|
||||
|
||||
// write_reset()
|
||||
if (params.Size() == 0) {
|
||||
if (lights != nullptr) {
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.override_enabled) {
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, light.last_state);
|
||||
}
|
||||
light.override_enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// loop parameters
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsArray()) {
|
||||
error(res, "parameters must be arrays");
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
error_unknown(res, "analog", light_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Lights::write_light(std::string name, float state) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_state = CLAMP(state, 0.f, 1.f);
|
||||
light.override_enabled = true;
|
||||
|
||||
if (cfg::CONFIGURATOR_STANDALONE) {
|
||||
GameAPI::Lights::writeLight(RI_MGR, light, state);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Lights::write_light_reset(std::string name) {
|
||||
|
||||
// check light cache
|
||||
if (!this->lights) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find light
|
||||
for (auto &light : *this->lights) {
|
||||
if (light.getName() == name) {
|
||||
light.override_enabled = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// unknown light
|
||||
return false;
|
||||
}
|
||||
}
|
28
api/modules/lights.h
Normal file
28
api/modules/lights.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
#include "cfg/api.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Lights : public Module {
|
||||
public:
|
||||
Lights();
|
||||
|
||||
private:
|
||||
|
||||
// state
|
||||
std::vector<Light> *lights;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
|
||||
// helper
|
||||
bool write_light(std::string name, float state);
|
||||
bool write_light_reset(std::string name);
|
||||
};
|
||||
}
|
233
api/modules/memory.cpp
Normal file
233
api/modules/memory.cpp
Normal file
@ -0,0 +1,233 @@
|
||||
#include "memory.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/libutils.h"
|
||||
#include "util/memutils.h"
|
||||
#include "util/sigscan.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
// global lock to prevent simultaneous access to memory
|
||||
static std::mutex MEMORY_LOCK;
|
||||
|
||||
Memory::Memory() : Module("memory", true) {
|
||||
functions["write"] = std::bind(&Memory::write, this, _1, _2);
|
||||
functions["read"] = std::bind(&Memory::read, this, _1, _2);
|
||||
functions["signature"] = std::bind(&Memory::signature, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* write(dll_name: str, data: hex, offset: uint)
|
||||
*/
|
||||
void Memory::write(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "data", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto data = req.params[1].GetString();
|
||||
intptr_t offset = req.params[2].GetUint();
|
||||
|
||||
// convert data to bin
|
||||
size_t data_bin_size = strlen(data) / 2;
|
||||
auto data_bin = std::make_unique<uint8_t[]>(data_bin_size);
|
||||
hex2bin(data, data_bin.get());
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
if (offset + data_bin_size >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
auto data_pos = reinterpret_cast<uint8_t *>(module_info.lpBaseOfDll) + offset;
|
||||
|
||||
// replace data
|
||||
memutils::VProtectGuard guard(data_pos, data_bin_size);
|
||||
memcpy(data_pos, data_bin.get(), data_bin_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* read(dll_name: str, offset: uint, size: uint)
|
||||
*/
|
||||
void Memory::read(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 3) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "str");
|
||||
}
|
||||
if (!req.params[1].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[2].IsUint()) {
|
||||
return error_type(res, "size", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
intptr_t offset = req.params[1].GetUint();
|
||||
auto size = req.params[2].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// convert offset to RVA
|
||||
offset = libutils::offset2rva(dll_path, offset);
|
||||
if (offset == ~0) {
|
||||
return error(res, "Couldn't convert offset to RVA.");
|
||||
}
|
||||
|
||||
// get module information
|
||||
MODULEINFO module_info {};
|
||||
if (!GetModuleInformation(GetCurrentProcess(), module, &module_info, sizeof(MODULEINFO))) {
|
||||
return error(res, "Couldn't get module information.");
|
||||
}
|
||||
|
||||
// check bounds
|
||||
auto max = offset + size;
|
||||
if ((size_t) max >= (size_t) module_info.lpBaseOfDll + module_info.SizeOfImage) {
|
||||
return error(res, "Data out of bounds.");
|
||||
}
|
||||
|
||||
// read memory to hex (without virtual protect)
|
||||
std::string hex = bin2hex((uint8_t*) module_info.lpBaseOfDll + offset, size);
|
||||
Value hex_val(hex.c_str(), res.doc()->GetAllocator());
|
||||
res.add_data(hex_val);
|
||||
}
|
||||
|
||||
/**
|
||||
* signature(
|
||||
* dll_name: str,
|
||||
* signature: hex,
|
||||
* replacement: hex,
|
||||
* offset: uint,
|
||||
* usage: uint)
|
||||
*
|
||||
* Both signature and replacement will ignore bytes specified as "??" in the hex string.
|
||||
* The offset specifies the offset between the found signature and the position to write the replacement to.
|
||||
* The resulting integer is the file offset where the replacement was written to.
|
||||
*/
|
||||
void Memory::signature(Request &req, Response &res) {
|
||||
std::lock_guard<std::mutex> lock(MEMORY_LOCK);
|
||||
|
||||
// check params
|
||||
if (req.params.Size() < 5) {
|
||||
return error_params_insufficient(res);
|
||||
}
|
||||
if (!req.params[0].IsString()) {
|
||||
return error_type(res, "dll_name", "string");
|
||||
}
|
||||
if (!req.params[1].IsString() || (req.params[1].GetStringLength() & 1)) {
|
||||
return error_type(res, "signature", "hex string");
|
||||
}
|
||||
if (!req.params[2].IsString() || (req.params[2].GetStringLength() & 1)) {
|
||||
return error_type(res, "replacement", "hex string");
|
||||
}
|
||||
if (!req.params[3].IsUint()) {
|
||||
return error_type(res, "offset", "uint");
|
||||
}
|
||||
if (!req.params[4].IsUint()) {
|
||||
return error_type(res, "usage", "uint");
|
||||
}
|
||||
|
||||
// get params
|
||||
auto dll_name = req.params[0].GetString();
|
||||
auto dll_path = MODULE_PATH / dll_name;
|
||||
auto signature = req.params[1].GetString();
|
||||
auto replacement = req.params[2].GetString();
|
||||
auto offset = req.params[3].GetUint();
|
||||
auto usage = req.params[4].GetUint();
|
||||
|
||||
// check if file exists in modules
|
||||
if (!fileutils::file_exists(dll_path)) {
|
||||
return error(res, "Couldn't find " + dll_path.string());
|
||||
}
|
||||
|
||||
// get module
|
||||
auto module = libutils::try_module(dll_name);
|
||||
if (!module) {
|
||||
return error(res, "Couldn't find module.");
|
||||
}
|
||||
|
||||
// execute
|
||||
auto result = replace_pattern(
|
||||
module,
|
||||
signature,
|
||||
replacement,
|
||||
offset,
|
||||
usage
|
||||
);
|
||||
|
||||
// check result
|
||||
if (!result) {
|
||||
return error(res, std::string("Pattern not found in memory of ") + dll_name);
|
||||
}
|
||||
|
||||
// convert to offset
|
||||
auto rva = result - reinterpret_cast<intptr_t>(module);
|
||||
result = libutils::rva2offset(dll_path, rva);
|
||||
if (result == -1) {
|
||||
return error(res, "Couldn't convert RVA to file offset.");
|
||||
}
|
||||
|
||||
// add result
|
||||
Value result_val(result);
|
||||
res.add_data(result_val);
|
||||
}
|
||||
}
|
19
api/modules/memory.h
Normal file
19
api/modules/memory.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Memory : public Module {
|
||||
public:
|
||||
Memory();
|
||||
|
||||
private:
|
||||
|
||||
// function definitions
|
||||
void write(Request &req, Response &res);
|
||||
void read(Request &req, Response &res);
|
||||
void signature(Request &req, Response &res);
|
||||
};
|
||||
}
|
145
api/modules/touch.cpp
Normal file
145
api/modules/touch.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include "touch.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#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"
|
||||
#include "util/utils.h"
|
||||
#include "games/iidx/iidx.h"
|
||||
|
||||
using namespace std::placeholders;
|
||||
using namespace rapidjson;
|
||||
|
||||
|
||||
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);
|
||||
functions["write_reset"] = std::bind(&Touch::write_reset, this, _1, _2);
|
||||
}
|
||||
|
||||
/**
|
||||
* read()
|
||||
*/
|
||||
void Touch::read(api::Request &req, Response &res) {
|
||||
|
||||
// get touch points
|
||||
std::vector<TouchPoint> touch_points;
|
||||
touch_get_points(touch_points);
|
||||
|
||||
// add state for each touch point
|
||||
for (auto &touch : touch_points) {
|
||||
Value state(kArrayType);
|
||||
Value id((uint64_t) touch.id);
|
||||
Value x((int64_t) touch.x);
|
||||
Value y((int64_t) touch.y);
|
||||
Value mouse((bool) touch.mouse);
|
||||
state.PushBack(id, res.doc()->GetAllocator());
|
||||
state.PushBack(x, res.doc()->GetAllocator());
|
||||
state.PushBack(y, res.doc()->GetAllocator());
|
||||
state.PushBack(mouse, res.doc()->GetAllocator());
|
||||
res.add_data(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write([id: uint, x: int, y: int], ...)
|
||||
*/
|
||||
void Touch::write(Request &req, Response &res) {
|
||||
|
||||
// get all touch points
|
||||
std::vector<TouchPoint> touch_points;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (param.Size() < 3) {
|
||||
error_params_insufficient(res);
|
||||
continue;
|
||||
}
|
||||
if (!param[0].IsUint()) {
|
||||
error_type(res, "id", "uint");
|
||||
continue;
|
||||
}
|
||||
if (!param[1].IsInt()) {
|
||||
error_type(res, "x", "int");
|
||||
continue;
|
||||
}
|
||||
if (!param[2].IsInt()) {
|
||||
error_type(res, "y", "int");
|
||||
continue;
|
||||
}
|
||||
// TODO: optional mouse parameter
|
||||
|
||||
// get params
|
||||
auto touch_id = param[0].GetUint();
|
||||
auto touch_x = param[1].GetInt();
|
||||
auto touch_y = param[2].GetInt();
|
||||
|
||||
apply_touch_errata(touch_x, touch_y);
|
||||
|
||||
touch_points.emplace_back(TouchPoint {
|
||||
.id = touch_id,
|
||||
.x = touch_x,
|
||||
.y = touch_y,
|
||||
.mouse = false,
|
||||
});
|
||||
}
|
||||
|
||||
// apply touch points
|
||||
touch_write_points(&touch_points);
|
||||
}
|
||||
|
||||
/**
|
||||
* write_reset(id: uint, ...)
|
||||
*/
|
||||
void Touch::write_reset(Request &req, Response &res) {
|
||||
|
||||
// get all IDs
|
||||
std::vector<DWORD> touch_point_ids;
|
||||
for (Value ¶m : req.params.GetArray()) {
|
||||
|
||||
// check params
|
||||
if (!param.IsUint()) {
|
||||
error_type(res, "id", "uint");
|
||||
continue;
|
||||
}
|
||||
|
||||
// remember touch ID
|
||||
auto touch_id = param.GetUint();
|
||||
touch_point_ids.emplace_back(touch_id);
|
||||
}
|
||||
|
||||
// remove all IDs
|
||||
touch_remove_points(&touch_point_ids);
|
||||
}
|
||||
|
||||
void Touch::apply_touch_errata(int &x, int &y) {
|
||||
int x_raw = x;
|
||||
int y_raw = y;
|
||||
|
||||
if (is_tdj_fhd) {
|
||||
// deal with TDJ FHD resolution mismatch (upgrade 720p to 1080p)
|
||||
// we don't know what screen is being shown on the companion and the API doesn't specify
|
||||
// the target of the touch events so just assume it's the sub screen
|
||||
x = x_raw * 1920 / 1280;
|
||||
y = y_raw * 1080 / 720;
|
||||
} else if (is_sdvx) {
|
||||
// for exceed gear, they are both 1080p screens, but need to apply transformation
|
||||
x = 1080 - y_raw;
|
||||
y = x_raw;
|
||||
}
|
||||
}
|
||||
}
|
24
api/modules/touch.h
Normal file
24
api/modules/touch.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "api/module.h"
|
||||
#include "api/request.h"
|
||||
|
||||
namespace api::modules {
|
||||
|
||||
class Touch : public Module {
|
||||
public:
|
||||
Touch();
|
||||
|
||||
private:
|
||||
bool is_sdvx;
|
||||
bool is_tdj_fhd;
|
||||
|
||||
// function definitions
|
||||
void read(Request &req, Response &res);
|
||||
void write(Request &req, Response &res);
|
||||
void write_reset(Request &req, Response &res);
|
||||
void apply_touch_errata(int &x, int &y);
|
||||
|
||||
};
|
||||
}
|
55
api/request.cpp
Normal file
55
api/request.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "request.h"
|
||||
#include "../util/logging.h"
|
||||
#include "module.h"
|
||||
|
||||
using namespace rapidjson;
|
||||
|
||||
namespace api {
|
||||
|
||||
Request::Request(rapidjson::Document &document) {
|
||||
Value::MemberIterator it;
|
||||
this->parse_error = false;
|
||||
|
||||
// get ID
|
||||
it = document.FindMember("id");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsUint64()) {
|
||||
log_warning("api", "Request ID is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->id = (*it).value.GetUint64();
|
||||
|
||||
// get module
|
||||
it = document.FindMember("module");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request module is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->module = (*it).value.GetString();
|
||||
|
||||
// get function
|
||||
it = document.FindMember("function");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsString()) {
|
||||
log_warning("api", "Request function is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->function = (*it).value.GetString();
|
||||
|
||||
// get params
|
||||
it = document.FindMember("params");
|
||||
if (it == document.MemberEnd() || !(*it).value.IsArray()) {
|
||||
log_warning("api", "Request params is invalid");
|
||||
this->parse_error = true;
|
||||
return;
|
||||
}
|
||||
this->params = document["params"];
|
||||
|
||||
// log request
|
||||
if (LOGGING) {
|
||||
log_info("api", "new request > id: {}, module: {}, function: {}",
|
||||
this->id, this->module, this->function);
|
||||
}
|
||||
}
|
||||
}
|
21
api/request.h
Normal file
21
api/request.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "external/rapidjson/document.h"
|
||||
|
||||
namespace api {
|
||||
|
||||
class Request {
|
||||
public:
|
||||
uint64_t id;
|
||||
std::string module;
|
||||
std::string function;
|
||||
rapidjson::Value params;
|
||||
bool parse_error;
|
||||
|
||||
Request(rapidjson::Document &document);
|
||||
};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user