Merge branch 'master' into 'windows_random'

# Conflicts:
#   .gitignore
#   build_win_debug_experimental.bat
#   build_win_lobby_connect.bat
#   build_win_release.bat
#   build_win_release_experimental.bat
#   dll/base.cpp
This commit is contained in:
Nemirtingas 2020-10-13 10:19:46 +00:00
commit c077e8f8d7
252 changed files with 160695 additions and 25921 deletions

10
.gitignore vendored
View File

@ -1,15 +1,20 @@
.vs/*
.vscode/*
*.bin
*.o
net.pb.*
*steam_api*
release/*
/build*/
*.obj
/dll/net.pb.cc
/dll/net.pb.h
base.exp
base.lib
rtlgenrandom*
steamclient.exp
steamclient.lib
<<<<<<< .gitignore
external
.vs
*.sln
@ -17,4 +22,7 @@ external
Debug
Release
Win32
x64
x64
=======
out/*
>>>>>>> .gitignore

View File

@ -56,15 +56,13 @@ build_steamos:
build_windows:
stage: build
image: fedora
image: fedora:29
script:
- dnf -y install wine wget p7zip sed dos2unix
- unix2dos *.txt
- unix2dos files_example/*.txt files_example/*/*.txt
- sed -i 's/C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\amd64\\vcvars64.bat/.\\sdk_standalone\\set_vars64.bat/g' build_env_x64.bat
- sed -i 's/C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\vcvars32.bat/.\\sdk_standalone\\set_vars32.bat/g' build_env_x86.bat
- sed -i 's/..\\vcpkg\\packages\\/.\\/g' build_set_protobuf_directories.bat
- sed -i 's/..\\vcpkg\\installed\\/.\\protobuf_/g' build_set_protobuf_directories.bat
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/0119304e030098b4821d73170fe52084/protobuf_x64-windows-static.7z'
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/4185a97ab363ddc1859127e59ec68581/protobuf_x86-windows-static.7z'
@ -73,18 +71,89 @@ build_windows:
- 7za x sdk_standalone.7z -osdk_standalone
- DLL_FILES="$(ls dll/*.cpp | tr "\n" " ")"; sed "s|dll/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls detours/*.cpp | tr "\n" " ")"; sed "s|detours/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls overlay_experimental/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls overlay_experimental/windows/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/windows/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/*.cpp | tr "\n" " ")"; sed "s|ImGui/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/impls/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls ImGui/impls/windows/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/windows/\*.cpp|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls dll/*.proto | tr "\n" " " | sed "s/.proto/.pb.cc/g")"; sed "s|dll/\*.cc|$DLL_FILES|g" -i *.bat
- DLL_FILES="$(ls steamclient_loader/*.cpp | tr "\n" " ")"; sed "s|steamclient_loader/\*.cpp|$DLL_FILES|g" -i *.bat
- sed "s| /MP12 | /MP4 |g" -i *.bat
- export WINEDEBUG=-all
- wine cmd /c build_win_debug_experimental.bat
- wine cmd /c build_win_release.bat
- mkdir release/debug_experimental
- mv steam_api.dll steam_api64.dll release/debug_experimental/
- rm -f steamclient.dll steamclient64.dll
- wine cmd /c build_win_debug_experimental_steamclient.bat
- mkdir release/debug_experimental_steamclient
- mv steamclient.dll steamclient64.dll release/debug_experimental_steamclient/
- cp Readme_debug.txt release/debug_experimental/Readme.txt
artifacts:
paths:
- release/
expire_in: 1 day
build_cmake_linux:
stage: build
image: ubuntu
when: manual
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt update -y
- apt install build-essential cmake libprotobuf-dev protobuf-compiler ninja-build -y
script:
- mkdir cmake-builds && cd cmake-builds
- mkdir x64-release && cd x64-release
- cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" && ninja
- cd ..
# - mkdir x64-experimental-release && cd x64-experimental-release
# - cmake ../../ -G "Ninja" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD:BOOL=ON && ninja
# - cd ..
artifacts:
paths:
- cmake-builds/
expire_in: 1 day
build_cmake_windows:
stage: build
image: fedora:29
when: manual
before_script:
- dnf -y install wine wget p7zip sed dos2unix unzip
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
- 7za x sdk_standalone.7z -osdk_standalone
- wget 'https://github.com/Kitware/CMake/releases/download/v3.15.0-rc1/cmake-3.15.0-rc1-win64-x64.zip'
- 7za x cmake-3.15.0-rc1-win64-x64.zip
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/0119304e030098b4821d73170fe52084/protobuf_x64-windows-static.7z'
- 7za x protobuf_x64-windows-static.7z -oprotobuf_x64-windows-static
script:
- export WINEDEBUG=-all
- wine cmd /c
- mkdir cmake-builds && cd cmake-builds
- mkdir x64-release && cd x64-release
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo nmake.exe >> cmake-build.bat
- wine cmd /c cmake-build.bat
- cd ..
- mkdir x64-experimental-release && cd x64-experimental-release
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD=ON -DEMU_OVERLAY=ON -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
- echo nmake.exe >> cmake-build.bat
- wine cmd /c cmake-build.bat
- cd ..
artifacts:
paths:
- cmake-builds/
expire_in: 1 day
deploy_all:
stage: deploy
image: fedora
@ -94,6 +163,10 @@ deploy_all:
script:
- ls -lah
- dnf -y install git tree
- mkdir -p release/source_code
- git bundle create release/source_code/source_code.bundle --all
- "echo \"This is a git bundle of the full repo, to use: git clone source_code.bundle --branch master\" > release/source_code/Readme.txt"
- mv linux release/
- shopt -s extglob
- rm -rf .g*
@ -101,10 +174,11 @@ deploy_all:
- mv release/* ./
- rm -rf release
- echo $CI_JOB_ID > job_id
- tree
artifacts:
name: "Goldberg_Lan_Steam_Emu_$CI_COMMIT_REF_NAME-$CI_COMMIT_TAG-$CI_COMMIT_SHORT_SHA"
paths:
- .
- ./
pages:
image: fedora

331
CMakeLists.txt Normal file
View File

@ -0,0 +1,331 @@
# Based on: https://github.com/ttroy50/cmake-examples/blob/master/03-code-generation/protobuf/CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
# Set the project name
project(goldberg_emulator)
if(MSVC)
# Set static environment (results in static compile flags) if Visual Studio is used (dynamic by default)
# Officially recommended solution: https://gitlab.kitware.com/cmake/community/wikis/FAQ#how-can-i-build-my-msvc-application-with-a-static-runtime
# Should be replaced by a better solution in the future: https://gitlab.kitware.com/cmake/cmake/merge_requests/3211
foreach(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if(${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif(${flag_var} MATCHES "/MD")
endforeach(flag_var)
# Disable MSVC++ warning C4996: 'may be unsafe/disable deprecation'
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
# Add option to enable experimental build
option(EMU_EXPERIMENTAL_BUILD "Enable experimental build" OFF)
# Set CXX standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Find the protobuf compiler and libraries
include(FindProtobuf)
find_package(Protobuf 3.1.0 REQUIRED)
# Generate the .h and .cxx files for dll/net.proto
protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS dll/net.proto)
# Print path to generated files
message(STATUS "PROTO_SRCS = ${PROTO_SRCS}")
message(STATUS "PROTO_HDRS = ${PROTO_HDRS}")
message(STATUS "PROTOBUF_INCLUDE_DIRS = ${PROTOBUF_INCLUDE_DIRS}")
message(STATUS "PROTOBUF_LIBRARIES = ${PROTOBUF_LIBRARIES}")
message(STATUS "PROTOBUF_PROTOC_EXECUTABLE = ${PROTOBUF_PROTOC_EXECUTABLE}")
# Setup the lib/exe names for the targets
if(WIN32)
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
set(LIB_STEAM_API steam_api64)
set(LIB_STEAMCLIENT steamclient64)
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets64)
set(BIN_LOBBY_CONNECT lobby_connect64)
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file64)
else()
set(LIB_STEAM_API steam_api)
set(LIB_STEAMCLIENT steamclient)
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets)
set(BIN_LOBBY_CONNECT lobby_connect)
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file)
endif()
elseif(UNIX AND NOT APPLE)
set(LIB_STEAM_API steam_api)
set(LIB_STEAMCLIENT steamclient)
set(LIB_STEAMNETWORKINGSOCKETS steamnetworkingsockets)
set(BIN_LOBBY_CONNECT lobby_connect)
set(BIN_GENERATE_INTERFACES_FILE generate_interfaces_file)
else()
message(FATAL_ERROR "Other platforms not supported...")
endif()
# Gather the files that are shared between multiple targets
file(GLOB DLL_SRC_SHARED
dll/*.h
dll/*.cpp
)
file(GLOB DETOURS_SRC_SHARED
detours/*.cpp
)
if(WIN32)
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
overlay_experimental/*.cpp
overlay_experimental/windows/*.cpp
ImGui/*.cpp
ImGui/impls/*.cpp
ImGui/impls/windows/*.cpp
glew/glew.c
)
elseif(UNIX)
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
overlay_experimental/*.cpp
overlay_experimental/linux/*.cpp
ImGui/*.cpp
ImGui/impls/*.cpp
ImGui/impls/linux/*.cpp
glew/glew.c
)
endif()
###################################################
# Setup for the steam_api(64).dll / libsteam_api.so
###################################################
# Setup the target
add_library(${LIB_STEAM_API}
SHARED
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
$<$<AND:$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:${OVERLAY_EXPERIMENTAL_SRC_SHARED}>
${DLL_SRC_SHARED}
${PROTO_SRCS}
${PROTO_HDRS}
)
# Include the required directories
target_include_directories(${LIB_STEAM_API}
PRIVATE
${PROTOBUF_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/ImGui
${CMAKE_CURRENT_SOURCE_DIR}/glew/include
)
# Link the required libraries
target_link_libraries(${LIB_STEAM_API}
PRIVATE
protobuf::libprotobuf
$<$<BOOL:${WIN32}>:ws2_32>
$<$<BOOL:${WIN32}>:iphlpapi>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:opengl32.lib>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:Winmm.lib>
)
# Add target compile definitions
target_compile_definitions(${LIB_STEAM_API}
PRIVATE
$<$<CONFIG:>:EMU_RELEASE_BUILD>
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
$<$<BOOL:${EMU_OVERLAY}>:EMU_OVERLAY>
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:GLEW_STATIC>
)
# Install the target
if(WIN32)
install(TARGETS
${LIB_STEAM_API}
RUNTIME DESTINATION ./
)
else()
install(TARGETS
${LIB_STEAM_API}
LIBRARY DESTINATION ./
)
endif()
########################################################
# Setup for the steamclient(64).dll / libsteamclient.so?
########################################################
# Setup the target
add_library(${LIB_STEAMCLIENT}
SHARED
steamclient.cpp
)
# Add target compile definitions
target_compile_definitions(${LIB_STEAMCLIENT}
PRIVATE
$<$<CONFIG:>:EMU_RELEASE_BUILD>
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
)
# Install the target
if(WIN32)
install(TARGETS
${LIB_STEAMCLIENT}
RUNTIME DESTINATION ./
)
else()
install(TARGETS
${LIB_STEAMCLIENT}
LIBRARY DESTINATION ./
)
endif()
if(NOT WIN32)
message(STATUS "Target library 'steamclient' is only suported for windows at this time... Disabling Build ALL inclusion for this target")
set_target_properties(${LIB_STEAMCLIENT} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
##############################################################################
# Setup for the steamnetworkingsockets(64).dll / libsteamnetworkingsockets.so?
##############################################################################
# Setup the target
add_library(${LIB_STEAMNETWORKINGSOCKETS}
SHARED
steamnetworkingsockets.cpp
)
# Add target compile definitions
target_compile_definitions(${LIB_STEAMNETWORKINGSOCKETS}
PRIVATE
$<$<CONFIG:>:EMU_RELEASE_BUILD>
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
)
# Setup install rules for the target
if(WIN32)
install(TARGETS
${LIB_STEAMNETWORKINGSOCKETS}
RUNTIME DESTINATION ./
)
else()
install(TARGETS
${LIB_STEAMNETWORKINGSOCKETS}
LIBRARY DESTINATION ./
)
endif()
if(NOT WIN32)
message(STATUS "Target library 'steamnetworkingsockets' is only supported for windows at this time... Disabling Build ALL inclusion for this target")
set_target_properties(${LIB_STEAMNETWORKINGSOCKETS} PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
endif()
###########################################################################
# Setup for the lobby_connect(64).exe / lobby_connect
###########################################################################
# Setup the target
add_executable(${BIN_LOBBY_CONNECT}
lobby_connect.cpp
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
${DLL_SRC_SHARED}
${PROTO_SRCS}
${PROTO_HDRS}
)
target_include_directories(${BIN_LOBBY_CONNECT}
PRIVATE
${PROTOBUF_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR}
)
# Link the required libraries
target_link_libraries(${BIN_LOBBY_CONNECT}
PRIVATE
protobuf::libprotobuf
$<$<BOOL:${WIN32}>:ws2_32>
$<$<BOOL:${WIN32}>:iphlpapi>
$<$<BOOL:${WIN32}>:comdlg32>
${CMAKE_DL_LIBS}
-debug:none
)
# Add target compile definitions
target_compile_definitions(${BIN_LOBBY_CONNECT}
PRIVATE
NO_DISK_WRITES
LOBBY_CONNECT
$<$<CONFIG:>:EMU_RELEASE_BUILD>
$<$<CONFIG:Release>:EMU_RELEASE_BUILD>
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
)
# Setup install rules for the target
install(TARGETS
${BIN_LOBBY_CONNECT}
RUNTIME DESTINATION lobby_connect/
)
###########################################################################
# Setup for the generate_interfaces_file(64).exe / generate_interfaces_file
###########################################################################
# Setup the target
add_executable(${BIN_GENERATE_INTERFACES_FILE}
generate_interfaces_file.cpp
)
# Link the required libraries
target_link_libraries(${BIN_GENERATE_INTERFACES_FILE}
PRIVATE
-debug:none
)
# Setup install rules for the target
install(TARGETS
${BIN_GENERATE_INTERFACES_FILE}
RUNTIME DESTINATION tools/
)
###########################################################################
# Installation setup for non target files and directories
###########################################################################
install(FILES
Readme_lobby_connect.txt
DESTINATION lobby_connect/
)
install(FILES
scripts/find_interfaces.sh
scripts/find_interfaces.ps1
Readme_generate_interfaces.txt
DESTINATION tools/
)
install(FILES
Readme_release.txt
files_example/steam_appid.EDIT_AND_RENAME.txt
files_example/steam_interfaces.EXAMPLE.txt
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${PROJECT_SOURCE_DIR}/Readme_experimental.txt>
$<$<CONFIG:Debug>:${PROJECT_SOURCE_DIR}/Readme_debug.txt>
DESTINATION ./
)
install(DIRECTORY
files_example/steam_settings.EXAMPLE
DESTINATION ./
)

520
CMakeSettings.json Normal file
View File

@ -0,0 +1,520 @@
{
"configurations": [
{
"name": "Windows-x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x64-windows-static",
"type": "STRING"
}
],
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake"
},
{
"name": "Windows-x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x64-windows-static",
"type": "STRING"
}
],
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake"
},
{
"name": "Windows-x86-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x86" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x86-windows-static",
"type": "STRING"
}
],
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake"
},
{
"name": "Windows-x86-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x86" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x86-windows-static",
"type": "STRING"
}
],
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake"
},
{
"name": "Linux-x64-Release",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync",
"variables": []
},
{
"name": "Linux-x64-Debug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync",
"variables": []
},
{
"name": "Linux-x86-Release",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync",
"variables": []
},
{
"name": "Linux-x86-Debug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync",
"variables": []
},
{
"name": "Windows-x64-ExperimentalRelease",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x64-windows-static",
"type": "STRING"
},
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
],
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake"
},
{
"name": "Windows-x64-ExperimentalDebug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x64-windows-static",
"type": "STRING"
},
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
]
},
{
"name": "Windows-x86-ExperimentalRelease",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake",
"inheritEnvironments": [ "msvc_x86" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x86-windows-static",
"type": "STRING"
},
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
]
},
{
"name": "Windows-x86-ExperimentalDebug",
"generator": "Ninja",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"cmakeToolchain": "../vcpkg/scripts/buildsystems/vcpkg.cmake",
"inheritEnvironments": [ "msvc_x86" ],
"variables": [
{
"name": "VCPKG_TARGET_TRIPLET",
"value": "x86-windows-static",
"type": "STRING"
},
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
]
},
{
"name": "Linux-x64-ExperimentalRelease",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync"
},
{
"name": "Linux-x64-ExperimentalDebug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync"
},
{
"name": "Linux-x86-ExperimentalRelease",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync"
},
{
"name": "Linux-x86-ExperimentalDebug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/local/bin/cmake",
"remoteCopySourcesExclusionList": [ ".vs", ".git" ],
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "true",
"type": "STRING"
}
],
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/var/tmp/src/${workspaceHash}/${name}",
"remoteBuildRoot": "/var/tmp/build/${workspaceHash}/build/${name}",
"remoteInstallRoot": "/var/tmp/build/${workspaceHash}/install/${name}",
"remoteCopySources": true,
"rsyncCommandArgs": "-t --delete --delete-excluded",
"remoteCopyBuildOutput": true,
"remoteCopySourcesMethod": "rsync"
},
{
"name": "WSL-x64-Release",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": []
},
{
"name": "WSL-x64-Debug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": []
},
{
"name": "WSL-x86-Release",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": []
},
{
"name": "WSL-x86-Debug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": []
},
{
"name": "WSL-x64-ExperimentalRelease",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "True",
"type": "BOOL"
}
]
},
{
"name": "WSL-x64-ExperimentalDebug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x64" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "True",
"type": "BOOL"
}
]
},
{
"name": "WSL-x86-ExperimentalRelease",
"generator": "Unix Makefiles",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "True",
"type": "BOOL"
}
]
},
{
"name": "WSL-x86-ExperimentalDebug",
"generator": "Unix Makefiles",
"configurationType": "Debug",
"buildRoot": "${projectDir}\\out\\${workspaceHash}\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeExecutable": "/usr/bin/cmake",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "linux_x86" ],
"wslPath": "${defaultWSLPath}",
"addressSanitizerRuntimeFlags": "detect_leaks=0",
"variables": [
{
"name": "EMU_EXPERIMENTAL_BUILD",
"value": "True",
"type": "BOOL"
}
]
}
]
}

97
ImGui/imconfig.h Normal file
View File

@ -0,0 +1,97 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
// It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
#define IMGUI_DISABLE_DEMO_WINDOWS
#define IMGUI_DISABLE_METRICS_WINDOW
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_DISABLE_OSX_FUNCTIONS // [OSX] Won't use and link with any OSX function (clipboard).
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Using 32-bits vertex indices (default is 16-bits) is one way to allow large meshes with more than 64K vertices.
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bits indices).
// Another way to allow large meshes while keeping 16-bits indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools
// Use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
// Have the Item Picker break in the ItemAdd() function instead of ItemHoverable() - which is earlier in the code, will catch a few extra items, allow picking items other than Hovered one.
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/
#define IMGUI_INCLUDE_IMGUI_USER_H
#define IMGUI_IMPL_OPENGL_LOADER_GLEW

9899
ImGui/imgui.cpp Normal file

File diff suppressed because it is too large Load Diff

2224
ImGui/imgui.h Normal file

File diff suppressed because it is too large Load Diff

3386
ImGui/imgui_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

1714
ImGui/imgui_internal.h Normal file

File diff suppressed because it is too large Load Diff

5
ImGui/imgui_user.h Normal file
View File

@ -0,0 +1,5 @@
namespace ImGui
{
IMGUI_API bool ColoredInputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool ColoredInputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}

8340
ImGui/imgui_widgets.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,634 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "../imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
#if defined(__APPLE__)
#include "TargetConditionals.h"
#endif
// Auto-detect GL version
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#endif
#endif
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <GLES2/gl2.h>
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#else
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code
#else
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif
#endif
// Desktop GL has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 0
#else
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1
#endif
// OpenGL Data
static char g_GlslVersionString[32] = "";
static GLuint g_FontTexture = 0;
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
#if defined(IMGUI_IMPL_OPENGL_ES2)
if (glsl_version == NULL)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
if (glsl_version == NULL)
glsl_version = "#version 300 es";
#else
if (glsl_version == NULL)
glsl_version = "#version 130";
#endif
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
bool ImGui_ImplOpenGL3_NewFrame()
{
if (!g_FontTexture)
return ImGui_ImplOpenGL3_CreateDeviceObjects();
return true;
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
#ifdef GL_SAMPLER_BINDING
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
#endif
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
if (last_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
else
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
#else
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
#endif
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision mediump float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
g_ShaderHandle = glCreateProgram();
glAttachShader(g_ShaderHandle, g_VertHandle);
glAttachShader(g_ShaderHandle, g_FragHandle);
glLinkProgram(g_ShaderHandle);
CheckProgram(g_ShaderHandle, "shader program");
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &g_VboHandle);
glGenBuffers(1, &g_ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
g_VboHandle = g_ElementsHandle = 0;
if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
if (g_VertHandle) glDeleteShader(g_VertHandle);
g_VertHandle = 0;
if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
if (g_FragHandle) glDeleteShader(g_FragHandle);
g_FragHandle = 0;
if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
g_ShaderHandle = 0;
ImGui_ImplOpenGL3_DestroyFontsTexture();
}

View File

@ -0,0 +1,47 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About Desktop OpenGL function loaders:
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
// Specific OpenGL versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Set default OpenGL3 loader to be gl3w
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#endif
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();

View File

@ -0,0 +1,338 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [?] Platform: Clipboard support
// [?] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [?] Platform: Keyboard arrays indexed using
// [?] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#include "imgui.h"
#include "imgui_impl_x11.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <GL/glew.h>
#include <ctime>
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-08-31: Initial X11 implementation
// X11 Data
static Display* g_Display = nullptr;
static Window g_Window = 0;
static uint64_t g_Time = 0;
static uint64_t g_TicksPerSecond = 0;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
static bool g_HasGamepad = false;
static bool g_WantUpdateHasGamepad = true;
bool GetKeyState(int keysym, char keys[32])
{
int keycode = XKeysymToKeycode(g_Display, keysym);
return keys[keycode/8] & (1<<keycode%8);
}
bool IsKeySys(int key)
{
switch(key)
{
case XK_Shift_L : case XK_Shift_R :
case XK_Control_L: case XK_Control_R :
case XK_Alt_L : case XK_Alt_R :
case XK_Super_L : case XK_Super_R :
case XK_Caps_Lock: case XK_Shift_Lock:
case XK_BackSpace: case XK_Delete :
case XK_Left : case XK_Right :
case XK_Up : case XK_Down :
case XK_Prior : case XK_Next :
case XK_Home : case XK_End :
case XK_Insert : case XK_Return :
return true;
}
return false;
}
// Functions
bool ImGui_ImplX11_Init(void *display, void *window)
{
timespec ts, tsres;
clock_getres(CLOCK_MONOTONIC_RAW, &tsres);
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
g_TicksPerSecond = 1000000000.0f / (static_cast<uint64_t>(tsres.tv_nsec) + static_cast<uint64_t>(tsres.tv_sec)*1000000000);
g_Time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
//if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
// return false;
//if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
// return false;
// Setup back-end capabilities flags
g_Display = reinterpret_cast<Display*>(display);
g_Window = reinterpret_cast<Window>(window);
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_x11";
io.ImeWindowHandle = nullptr;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
io.KeyMap[ImGuiKey_Tab] = XKeysymToKeycode(g_Display, XK_Tab);
io.KeyMap[ImGuiKey_LeftArrow] = XKeysymToKeycode(g_Display, XK_Left);
io.KeyMap[ImGuiKey_RightArrow] = XKeysymToKeycode(g_Display, XK_Right);
io.KeyMap[ImGuiKey_UpArrow] = XKeysymToKeycode(g_Display, XK_Up);
io.KeyMap[ImGuiKey_DownArrow] = XKeysymToKeycode(g_Display, XK_Down);
io.KeyMap[ImGuiKey_PageUp] = XKeysymToKeycode(g_Display, XK_Prior);
io.KeyMap[ImGuiKey_PageDown] = XKeysymToKeycode(g_Display, XK_Next);
io.KeyMap[ImGuiKey_Home] = XKeysymToKeycode(g_Display, XK_Home);
io.KeyMap[ImGuiKey_End] = XKeysymToKeycode(g_Display, XK_End);
io.KeyMap[ImGuiKey_Insert] = XKeysymToKeycode(g_Display, XK_Insert);
io.KeyMap[ImGuiKey_Delete] = XKeysymToKeycode(g_Display, XK_Delete);
io.KeyMap[ImGuiKey_Backspace] = XKeysymToKeycode(g_Display, XK_BackSpace);
io.KeyMap[ImGuiKey_Space] = XKeysymToKeycode(g_Display, XK_space);
io.KeyMap[ImGuiKey_Enter] = XKeysymToKeycode(g_Display, XK_Return);
io.KeyMap[ImGuiKey_Escape] = XKeysymToKeycode(g_Display, XK_Escape);
io.KeyMap[ImGuiKey_A] = XKeysymToKeycode(g_Display, XK_A);
io.KeyMap[ImGuiKey_C] = XKeysymToKeycode(g_Display, XK_C);
io.KeyMap[ImGuiKey_V] = XKeysymToKeycode(g_Display, XK_V);
io.KeyMap[ImGuiKey_X] = XKeysymToKeycode(g_Display, XK_X);
io.KeyMap[ImGuiKey_Y] = XKeysymToKeycode(g_Display, XK_Y);
io.KeyMap[ImGuiKey_Z] = XKeysymToKeycode(g_Display, XK_Z);
return true;
}
void ImGui_ImplX11_Shutdown()
{
g_Display = nullptr;
g_Window = 0;
}
static bool ImGui_ImplX11_UpdateMouseCursor()
{
/*
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return false;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
::SetCursor(NULL);
}
else
{
// Show OS mouse cursor
LPTSTR win32_cursor = IDC_ARROW;
switch (imgui_cursor)
{
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
}
::SetCursor(::LoadCursor(NULL, win32_cursor));
}
*/
return true;
}
static void ImGui_ImplX11_UpdateMousePos()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
// POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
// ::ClientToScreen(g_hWnd, &pos);
// ::SetCursorPos(pos.x, pos.y);
}
// Set mouse position
Window unused_window;
int rx, ry, x, y;
unsigned int mask;
XQueryPointer(g_Display, g_Window, &unused_window, &unused_window, &rx, &ry, &x, &y, &mask);
io.MousePos = ImVec2((float)x, (float)y);
}
// Gamepad navigation mapping
static void ImGui_ImplX11_UpdateGamepads()
{
/* TODO: support linux gamepad ?
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad)
{
XINPUT_CAPABILITIES caps;
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
g_WantUpdateHasGamepad = false;
}
XINPUT_STATE xinput_state;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
{
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
#undef MAP_BUTTON
#undef MAP_ANALOG
}
*/
}
void ImGui_ImplX11_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
unsigned int width, height;
Window unused_window;
int unused_int;
unsigned int unused_unsigned_int;
XGetGeometry(g_Display, (Window)g_Window, &unused_window, &unused_int, &unused_int, &width, &height, &unused_unsigned_int, &unused_unsigned_int);
io.DisplaySize.x = width;
io.DisplaySize.y = height;
timespec ts, tsres;
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
uint64_t current_time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
g_Time = current_time;
// Read keyboard modifiers inputs
char keys[32];
XQueryKeymap(g_Display, keys);
io.KeyCtrl = GetKeyState(XK_Control_L, keys);
io.KeyShift = GetKeyState(XK_Shift_L, keys);
io.KeyAlt = GetKeyState(XK_Alt_L, keys);
io.KeySuper = false;
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
// Update OS mouse position
ImGui_ImplX11_UpdateMousePos();
/*
// Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor)
{
g_LastMouseCursor = mouse_cursor;
ImGui_ImplX11_UpdateMouseCursor();
}
*/
// Update game controllers (if enabled and available)
ImGui_ImplX11_UpdateGamepads();
}
// Process X11 mouse/keyboard inputs.
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent &event)
{
if (ImGui::GetCurrentContext() == NULL)
return 0;
ImGuiIO& io = ImGui::GetIO();
switch (event.type)
{
case ButtonPress:
case ButtonRelease:
switch(event.xbutton.button)
{
case Button1:
io.MouseDown[0] = event.type == ButtonPress;
break;
case Button2:
io.MouseDown[2] = event.type == ButtonPress;
break;
case Button3:
io.MouseDown[1] = event.type == ButtonPress;
break;
case Button4: // Mouse wheel up
if( event.type == ButtonPress )
io.MouseWheel += 1;
return 0;
case Button5: // Mouse wheel down
if( event.type == ButtonPress )
io.MouseWheel -= 1;
return 0;
}
break;
case KeyPress:
{
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
if( IsKeySys(key) )
io.KeysDown[event.xkey.keycode] = true;
else
io.AddInputCharacter(key);
return 0;
}
case KeyRelease:
{
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
if( IsKeySys(key) )
io.KeysDown[event.xkey.keycode] = false;
return 0;
}
}
/*
case WM_DEVICECHANGE:
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
g_WantUpdateHasGamepad = true;
return 0;
}
*/
return 0;
}

View File

@ -0,0 +1,21 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#pragma once
IMGUI_IMPL_API bool ImGui_ImplX11_Init(void* display, void* window);
IMGUI_IMPL_API void ImGui_ImplX11_Shutdown();
IMGUI_IMPL_API void ImGui_ImplX11_NewFrame();
// Handler for Win32 messages, update mouse/keyboard data.
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
/*
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent *event);
*/

View File

@ -0,0 +1,557 @@
// dear imgui: Renderer for DirectX10
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX10: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX10: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example.
// 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-04-09: Misc: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) on other back-ends.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX10: Disabling depth-write.
#include "../../imgui.h"
#include "imgui_impl_dx10.h"
// DirectX
#include <stdio.h>
#include <d3d10_1.h>
#include <d3d10.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D10Device* g_pd3dDevice = NULL;
static IDXGIFactory* g_pFactory = NULL;
static ID3D10Buffer* g_pVB = NULL;
static ID3D10Buffer* g_pIB = NULL;
static ID3D10VertexShader* g_pVertexShader = NULL;
static ID3D10InputLayout* g_pInputLayout = NULL;
static ID3D10Buffer* g_pVertexConstantBuffer = NULL;
static ID3D10PixelShader* g_pPixelShader = NULL;
static ID3D10SamplerState* g_pFontSampler = NULL;
static ID3D10ShaderResourceView*g_pFontTextureView = NULL;
static ID3D10RasterizerState* g_pRasterizerState = NULL;
static ID3D10BlendState* g_pBlendState = NULL;
static ID3D10DepthStencilState* g_pDepthStencilState = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
{
// Setup viewport
D3D10_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D10_VIEWPORT));
vp.Width = (UINT)draw_data->DisplaySize.x;
vp.Height = (UINT)draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader);
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
// Setup render state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D10Device* ctx = g_pd3dDevice;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Copy and convert all vertices into a single contiguous buffer
ImDrawVert* vtx_dst = NULL;
ImDrawIdx* idx_dst = NULL;
g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
g_pVB->Unmap();
g_pIB->Unmap();
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
void* mapped_resource;
if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
g_pVertexConstantBuffer->Unmap();
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX10_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D10RasterizerState* RS;
ID3D10BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D10DepthStencilState* DepthStencilState;
ID3D10ShaderResourceView* PSShaderResource;
ID3D10SamplerState* PSSampler;
ID3D10PixelShader* PS;
ID3D10VertexShader* VS;
D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D10InputLayout* InputLayout;
};
BACKUP_DX10_STATE old;
old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
ctx->PSGetShader(&old.PS);
ctx->VSGetShader(&old.VS);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y)};
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX10_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D10_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D10Texture2D *pTexture = NULL;
D3D10_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
// Create texture view
D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
ZeroMemory(&srv_desc, sizeof(srv_desc));
srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = desc.MipLevels;
srv_desc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
// Create texture sampler
{
D3D10_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
}
}
bool ImGui_ImplDX10_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pFontSampler)
ImGui_ImplDX10_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX10 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pVertexShader) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateVertexShader(ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pVertexShader) != S_OK)
return false;
#endif
// Create the input layout
D3D10_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
#ifdef USE_D3DCOMPILE
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pInputLayout) != S_OK)
return false;
#endif
// Create the constant buffer
{
D3D10_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
}
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), &g_pPixelShader) != S_OK)
return false;
#else
if (g_pd3dDevice->CreatePixelShader(ImGui_pixelShaderDX10, ImGui_pixelShaderDX10_len, &g_pPixelShader) != S_OK)
return false;
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D10_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.BlendEnable[0] = true;
desc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
desc.BlendOp = D3D10_BLEND_OP_ADD;
desc.SrcBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
desc.DestBlendAlpha = D3D10_BLEND_ZERO;
desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
}
// Create the rasterizer state
{
D3D10_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D10_FILL_SOLID;
desc.CullMode = D3D10_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
}
// Create depth-stencil State
{
D3D10_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D10_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
}
ImGui_ImplDX10_CreateFontsTexture();
return true;
}
void ImGui_ImplDX10_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
#ifdef USE_D3DCOMPILE
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
#endif
}
bool ImGui_ImplDX10_Init(ID3D10Device* device)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx10";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
g_pd3dDevice = device;
g_pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef();
return true;
}
void ImGui_ImplDX10_Shutdown()
{
ImGui_ImplDX10_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}
bool ImGui_ImplDX10_NewFrame()
{
if (!g_pFontSampler)
return ImGui_ImplDX10_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,23 @@
// dear imgui: Renderer for DirectX10
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct ID3D10Device;
IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device);
IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX10_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();

View File

@ -0,0 +1,684 @@
// dear imgui: Renderer for DirectX11
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX11: Disabling depth-write.
#include "../../imgui.h"
#include "imgui_impl_dx11.h"
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D11Device* g_pd3dDevice = NULL;
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
static IDXGIFactory* g_pFactory = NULL;
static ID3D11Buffer* g_pVB = NULL;
static ID3D11Buffer* g_pIB = NULL;
static ID3D11VertexShader* g_pVertexShader = NULL;
static ID3D11InputLayout* g_pInputLayout = NULL;
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
static ID3D11PixelShader* g_pPixelShader = NULL;
static ID3D11SamplerState* g_pFontSampler = NULL;
static ID3D11ShaderResourceView*g_pFontTextureView = NULL;
static ID3D11RasterizerState* g_pRasterizerState = NULL;
static ID3D11BlendState* g_pBlendState = NULL;
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{
// Setup viewport
D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(g_pInputLayout);
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(g_pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
ctx->PSSetShader(g_pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
// Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
ctx->RSSetState(g_pRasterizerState);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(g_pVB, 0);
ctx->Unmap(g_pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(g_pVertexConstantBuffer, 0);
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX11_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D11RasterizerState* RS;
ID3D11BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D11DepthStencilState* DepthStencilState;
ID3D11ShaderResourceView* PSShaderResource;
ID3D11SamplerState* PSSampler;
ID3D11PixelShader* PS;
ID3D11VertexShader* VS;
UINT PSInstancesCount, VSInstancesCount;
ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D11InputLayout* InputLayout;
};
BACKUP_DX11_STATE old;
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
old.PSInstancesCount = old.VSInstancesCount = 256;
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX11_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D *pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
// Create texture sampler
{
D3D11_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
}
}
bool ImGui_ImplDX11_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX11 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
const char* target;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1: target = "vs_4_0_level_9_1"; break;
case D3D_FEATURE_LEVEL_9_2: target = "vs_4_0_level_9_2"; break;
case D3D_FEATURE_LEVEL_9_3: target = "vs_4_0_level_9_3"; break;
case D3D_FEATURE_LEVEL_10_0: target = "vs_4_0"; break;
case D3D_FEATURE_LEVEL_10_1: target = "vs_4_1"; break;
case D3D_FEATURE_LEVEL_11_0: target = "vs_5_0"; break;
case D3D_FEATURE_LEVEL_11_1: target = "vs_5_0"; break;
default: target = "vs_4_0";
}
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
return false;
#else
unsigned char* byteCode;
SIZE_T byteCodeSize;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1:
byteCode = ImGui_vertexShaderDX11_9_1;
byteCodeSize = ImGui_vertexShaderDX11_9_1_len;
break;
case D3D_FEATURE_LEVEL_9_2:
byteCode = ImGui_vertexShaderDX11_9_2;
byteCodeSize = ImGui_vertexShaderDX11_9_2_len;
break;
case D3D_FEATURE_LEVEL_9_3:
byteCode = ImGui_vertexShaderDX11_9_3;
byteCodeSize = ImGui_vertexShaderDX11_9_3_len;
break;
case D3D_FEATURE_LEVEL_10_0:
byteCode = ImGui_vertexShaderDX11_10_0;
byteCodeSize = ImGui_vertexShaderDX11_10_0_len;
break;
case D3D_FEATURE_LEVEL_10_1:
byteCode = ImGui_vertexShaderDX11_10_1;
byteCodeSize = ImGui_vertexShaderDX11_10_1_len;
break;
case D3D_FEATURE_LEVEL_11_0:
byteCode = ImGui_vertexShaderDX11_11_0;
byteCodeSize = ImGui_vertexShaderDX11_11_0_len;
break;
case D3D_FEATURE_LEVEL_11_1:
byteCode = ImGui_vertexShaderDX11_11_1;
byteCodeSize = ImGui_vertexShaderDX11_11_1_len;
break;
default:
byteCode = ImGui_vertexShaderDX11;
byteCodeSize = ImGui_vertexShaderDX11_len;
}
auto x = g_pd3dDevice->CreateVertexShader(byteCode, byteCodeSize, NULL, &g_pVertexShader);
if (x != S_OK)
return false;
#endif
// Create the input layout
D3D11_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
#ifdef USE_D3DCOMPILE
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
return false;
#else
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX11, ImGui_vertexShaderDX11_len, &g_pInputLayout) != S_OK)
return false;
#endif
// Create the constant buffer
{
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
}
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
const char* target;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1: target = "ps_4_0_level_9_1"; break;
case D3D_FEATURE_LEVEL_9_2: target = "ps_4_0_level_9_2"; break;
case D3D_FEATURE_LEVEL_9_3: target = "ps_4_0_level_9_3"; break;
case D3D_FEATURE_LEVEL_10_0: target = "ps_4_0"; break;
case D3D_FEATURE_LEVEL_10_1: target = "ps_4_1"; break;
case D3D_FEATURE_LEVEL_11_0: target = "ps_5_0"; break;
case D3D_FEATURE_LEVEL_11_1: target = "ps_5_0"; break;
default: target = "ps_4_0";
}
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
return false;
#else
unsigned char* byteCode;
SIZE_T byteCodeSize;
switch (g_pd3dDevice->GetFeatureLevel())
{
case D3D_FEATURE_LEVEL_9_1:
byteCode = ImGui_pixelShaderDX11_9_1;
byteCodeSize = ImGui_pixelShaderDX11_9_1_len;
break;
case D3D_FEATURE_LEVEL_9_2:
byteCode = ImGui_pixelShaderDX11_9_2;
byteCodeSize = ImGui_pixelShaderDX11_9_2_len;
break;
case D3D_FEATURE_LEVEL_9_3:
byteCode = ImGui_pixelShaderDX11_9_3;
byteCodeSize = ImGui_pixelShaderDX11_9_3_len;
break;
case D3D_FEATURE_LEVEL_10_0:
byteCode = ImGui_pixelShaderDX11_10_0;
byteCodeSize = ImGui_pixelShaderDX11_10_0_len;
break;
case D3D_FEATURE_LEVEL_10_1:
byteCode = ImGui_pixelShaderDX11_10_1;
byteCodeSize = ImGui_pixelShaderDX11_10_1_len;
break;
case D3D_FEATURE_LEVEL_11_0:
byteCode = ImGui_pixelShaderDX11_11_0;
byteCodeSize = ImGui_pixelShaderDX11_11_0_len;
break;
case D3D_FEATURE_LEVEL_11_1:
byteCode = ImGui_pixelShaderDX11_11_1;
byteCodeSize = ImGui_pixelShaderDX11_11_1_len;
break;
default:
byteCode = ImGui_pixelShaderDX11;
byteCodeSize = ImGui_pixelShaderDX11_len;
}
if (g_pd3dDevice->CreatePixelShader(byteCode, byteCodeSize, NULL, &g_pPixelShader) != S_OK)
return false;
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D11_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
}
// Create the rasterizer state
{
D3D11_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
}
// Create depth-stencil State
{
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
}
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
void ImGui_ImplDX11_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
#ifdef USE_D3DCOMPILE
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
#endif
}
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
g_pd3dDevice = device;
g_pd3dDeviceContext = device_context;
g_pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
g_pd3dDevice->AddRef();
g_pd3dDeviceContext->AddRef();
return true;
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_InvalidateDeviceObjects();
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
}
bool ImGui_ImplDX11_NewFrame()
{
if (!g_pFontSampler)
return ImGui_ImplDX11_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,24 @@
// dear imgui: Renderer for DirectX11
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct ID3D11Device;
struct ID3D11DeviceContext;
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX11_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();

View File

@ -0,0 +1,677 @@
// dear imgui: Renderer for DirectX12
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// Issues:
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Various minor tidying up.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
#include "../../imgui.h"
#include "imgui_impl_dx12.h"
// DirectX
#include <d3d12.h>
#include <dxgi1_4.h>
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
#ifdef USE_D3DCOMPILE
static ID3DBlob* g_pVertexShaderBlob = NULL;
static ID3DBlob* g_pPixelShaderBlob = NULL;
#endif
// DirectX data
static ID3D12Device* g_pd3dDevice = NULL;
static ID3D12RootSignature* g_pRootSignature = NULL;
static ID3D12PipelineState* g_pPipelineState = NULL;
static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN;
static ID3D12Resource* g_pFontTextureResource = NULL;
static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {};
static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {};
struct FrameResources
{
ID3D12Resource* IndexBuffer;
ID3D12Resource* VertexBuffer;
int IndexBufferSize;
int VertexBufferSize;
};
static FrameResources* g_pFrameResources = NULL;
static UINT g_numFramesInFlight = 0;
static UINT g_frameIndex = UINT_MAX;
struct VERTEX_CONSTANT_BUFFER
{
float mvp[4][4];
};
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr)
{
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
VERTEX_CONSTANT_BUFFER vertex_constant_buffer;
{
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
}
// Setup viewport
D3D12_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0.0f;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
D3D12_VERTEX_BUFFER_VIEW vbv;
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
vbv.SizeInBytes = fr->VertexBufferSize * stride;
vbv.StrideInBytes = stride;
ctx->IASetVertexBuffers(0, 1, &vbv);
D3D12_INDEX_BUFFER_VIEW ibv;
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
ctx->IASetIndexBuffer(&ibv);
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->SetPipelineState(g_pPipelineState);
ctx->SetGraphicsRootSignature(g_pRootSignature);
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
// Setup blend factor
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendFactor(blend_factor);
}
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// FIXME: I'm assuming that this only gets called once per frame!
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
g_frameIndex = g_frameIndex + 1;
FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight];
// Create and grow vertex/index buffers if needed
if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount)
{
if (fr->VertexBuffer != NULL) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0)
return;
}
if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount)
{
if (fr->IndexBuffer != NULL) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
void* vtx_resource, *idx_resource;
D3D12_RANGE range;
memset(&range, 0, sizeof(D3D12_RANGE));
if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
return;
if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
fr->VertexBuffer->Unmap(0, &range);
fr->IndexBuffer->Unmap(0, &range);
// Setup desired DX state
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Apply Scissor, Bind texture, Draw
const D3D12_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
ctx->SetGraphicsRootDescriptorTable(1, *(D3D12_GPU_DESCRIPTOR_HANDLE*)&pcmd->TextureId);
ctx->RSSetScissorRects(1, &r);
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
}
static void ImGui_ImplDX12_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_DEFAULT;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Alignment = 0;
desc.Width = width;
desc.Height = height;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* pTexture = NULL;
g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture));
UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
UINT uploadSize = height * uploadPitch;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Alignment = 0;
desc.Width = uploadSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
ID3D12Resource* uploadBuffer = NULL;
HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
IM_ASSERT(SUCCEEDED(hr));
void* mapped = NULL;
D3D12_RANGE range = { 0, uploadSize };
hr = uploadBuffer->Map(0, &range, &mapped);
IM_ASSERT(SUCCEEDED(hr));
for (int y = 0; y < height; y++)
memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4);
uploadBuffer->Unmap(0, &range);
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = uploadBuffer;
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srcLocation.PlacedFootprint.Footprint.Width = width;
srcLocation.PlacedFootprint.Footprint.Height = height;
srcLocation.PlacedFootprint.Footprint.Depth = 1;
srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = pTexture;
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstLocation.SubresourceIndex = 0;
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = pTexture;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
ID3D12Fence* fence = NULL;
hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
IM_ASSERT(SUCCEEDED(hr));
HANDLE event = CreateEvent(0, 0, 0, 0);
IM_ASSERT(event != NULL);
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 1;
ID3D12CommandQueue* cmdQueue = NULL;
hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
IM_ASSERT(SUCCEEDED(hr));
ID3D12CommandAllocator* cmdAlloc = NULL;
hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
IM_ASSERT(SUCCEEDED(hr));
ID3D12GraphicsCommandList* cmdList = NULL;
hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList));
IM_ASSERT(SUCCEEDED(hr));
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
cmdList->ResourceBarrier(1, &barrier);
hr = cmdList->Close();
IM_ASSERT(SUCCEEDED(hr));
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*) &cmdList);
hr = cmdQueue->Signal(fence, 1);
IM_ASSERT(SUCCEEDED(hr));
fence->SetEventOnCompletion(1, event);
WaitForSingleObject(event, INFINITE);
cmdList->Release();
cmdAlloc->Release();
cmdQueue->Release();
CloseHandle(event);
fence->Release();
uploadBuffer->Release();
// Create texture view
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle);
if (g_pFontTextureResource != NULL)
g_pFontTextureResource->Release();
g_pFontTextureResource = pTexture;
}
// Store our identifier
static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
io.Fonts->TexID = (ImTextureID)g_hFontSrvGpuDescHandle.ptr;
}
bool ImGui_ImplDX12_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (g_pPipelineState)
ImGui_ImplDX12_InvalidateDeviceObjects();
// Create the root signature
{
D3D12_DESCRIPTOR_RANGE descRange = {};
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
descRange.NumDescriptors = 1;
descRange.BaseShaderRegister = 0;
descRange.RegisterSpace = 0;
descRange.OffsetInDescriptorsFromTableStart = 0;
D3D12_ROOT_PARAMETER param[2] = {};
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
param[0].Constants.ShaderRegister = 0;
param[0].Constants.RegisterSpace = 0;
param[0].Constants.Num32BitValues = 16;
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
param[1].DescriptorTable.NumDescriptorRanges = 1;
param[1].DescriptorTable.pDescriptorRanges = &descRange;
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
staticSampler.MipLODBias = 0.f;
staticSampler.MaxAnisotropy = 0;
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
staticSampler.MinLOD = 0.f;
staticSampler.MaxLOD = 0.f;
staticSampler.ShaderRegister = 0;
staticSampler.RegisterSpace = 0;
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc = {};
desc.NumParameters = _countof(param);
desc.pParameters = param;
desc.NumStaticSamplers = 1;
desc.pStaticSamplers = &staticSampler;
desc.Flags =
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
ID3DBlob* blob = NULL;
static decltype(D3D12SerializeRootSignature)* D3D12SerializeRootSignature = (decltype(D3D12SerializeRootSignature))GetProcAddress(GetModuleHandle("d3d12.dll"), "D3D12SerializeRootSignature");
if (D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK)
return false;
g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature));
blob->Release();
}
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX12 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
psoDesc.NodeMask = 1;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.pRootSignature = g_pRootSignature;
psoDesc.SampleMask = UINT_MAX;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = g_RTVFormat;
psoDesc.SampleDesc.Count = 1;
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
#ifdef USE_D3DCOMPILE
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
if (D3DCompile == nullptr)
return false;
#endif
// Create the vertex shader
{
#ifdef USE_D3DCOMPILE
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &g_pVertexShaderBlob, NULL);
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
psoDesc.VS = { g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize() };
#else
psoDesc.VS = { ImGui_vertexShaderDX12, ImGui_vertexShaderDX12_len };
#endif
// Create the input layout
static D3D12_INPUT_ELEMENT_DESC local_layout[] = {
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
psoDesc.InputLayout = { local_layout, 3 };
}
// Create the pixel shader
{
#ifdef USE_D3DCOMPILE
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
SamplerState sampler0 : register(s0);\
Texture2D texture0 : register(t0);\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &g_pPixelShaderBlob, NULL);
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
return false;
psoDesc.PS = { g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize() };
#else
psoDesc.PS = { ImGui_pixelShaderDX12, ImGui_pixelShaderDX12_len };
#endif
}
#ifdef USE_D3DCOMPILE
unload_d3dcompile();
#endif
// Create the blending setup
{
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
}
// Create the rasterizer state
{
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
desc.FillMode = D3D12_FILL_MODE_SOLID;
desc.CullMode = D3D12_CULL_MODE_NONE;
desc.FrontCounterClockwise = FALSE;
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc.DepthClipEnable = true;
desc.MultisampleEnable = FALSE;
desc.AntialiasedLineEnable = FALSE;
desc.ForcedSampleCount = 0;
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
}
// Create depth-stencil State
{
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
desc.DepthEnable = false;
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc.BackFace = desc.FrontFace;
}
if (g_pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&g_pPipelineState)) != S_OK)
return false;
ImGui_ImplDX12_CreateFontsTexture();
return true;
}
void ImGui_ImplDX12_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
ImGuiIO& io = ImGui::GetIO();
#ifdef USE_D3DCOMPILE
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
#endif
if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; }
if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; }
if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
for (UINT i = 0; i < g_numFramesInFlight; i++)
{
FrameResources* fr = &g_pFrameResources[i];
if (fr->IndexBuffer) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
if (fr->VertexBuffer) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
}
}
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device;
g_RTVFormat = rtv_format;
g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
g_pFrameResources = new FrameResources[num_frames_in_flight];
g_numFramesInFlight = num_frames_in_flight;
g_frameIndex = UINT_MAX;
// Create buffers with a default size (they will later be grown as needed)
for (int i = 0; i < num_frames_in_flight; i++)
{
FrameResources* fr = &g_pFrameResources[i];
fr->IndexBuffer = NULL;
fr->VertexBuffer = NULL;
fr->IndexBufferSize = 10000;
fr->VertexBufferSize = 5000;
}
return true;
}
void ImGui_ImplDX12_Shutdown()
{
ImGui_ImplDX12_InvalidateDeviceObjects();
delete[] g_pFrameResources;
g_pFrameResources = NULL;
g_pd3dDevice = NULL;
g_hFontSrvCpuDescHandle.ptr = 0;
g_hFontSrvGpuDescHandle.ptr = 0;
g_numFramesInFlight = 0;
g_frameIndex = UINT_MAX;
}
bool ImGui_ImplDX12_NewFrame()
{
if (!g_pPipelineState)
return ImGui_ImplDX12_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,34 @@
// dear imgui: Renderer for DirectX12
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// Issues:
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
enum DXGI_FORMAT;
struct ID3D12Device;
struct ID3D12GraphicsCommandList;
struct D3D12_CPU_DESCRIPTOR_HANDLE;
struct D3D12_GPU_DESCRIPTOR_HANDLE;
// cmd_list is the command list that the implementation will use to render imgui draw lists.
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX12_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();

View File

@ -0,0 +1,286 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "../../imgui.h"
#include "imgui_impl_dx9.h"
// DirectX
#include <d3d9.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
// DirectX data
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
struct CUSTOMVERTEX
{
float pos[3];
D3DCOLOR col;
float uv[2];
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
{
// Setup viewport
D3DVIEWPORT9 vp;
vp.X = vp.Y = 0;
vp.Width = (DWORD)draw_data->DisplaySize.x;
vp.Height = (DWORD)draw_data->DisplaySize.y;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
g_pd3dDevice->SetViewport(&vp);
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
g_pd3dDevice->SetPixelShader(NULL);
g_pd3dDevice->SetVertexShader(NULL);
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
{
float L = draw_data->DisplayPos.x + 0.5f;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
float T = draw_data->DisplayPos.y + 0.5f;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
D3DMATRIX mat_projection =
{ { {
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
} } };
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
}
}
// Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// Create and grow buffers if needed
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
{
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
return;
}
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
{
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
return;
}
// Backup the DX9 state
IDirect3DStateBlock9* d3d9_state_block = NULL;
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
return;
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world, last_view, last_projection;
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
// FIXME-OPT: This is a waste of resource, the ideal is to use imconfig.h and
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
CUSTOMVERTEX* vtx_dst;
ImDrawIdx* idx_dst;
if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
return;
if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
return;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data;
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
{
vtx_dst->pos[0] = vtx_src->pos.x;
vtx_dst->pos[1] = vtx_src->pos.y;
vtx_dst->pos[2] = 0.0f;
vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000) >> 16) | ((vtx_src->col & 0xFF) << 16); // RGBA --> ARGB for DirectX9
vtx_dst->uv[0] = vtx_src->uv.x;
vtx_dst->uv[1] = vtx_src->uv.y;
vtx_dst++;
vtx_src++;
}
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
idx_dst += cmd_list->IdxBuffer.Size;
}
g_pVB->Unlock();
g_pIB->Unlock();
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
g_pd3dDevice->SetIndices(g_pIB);
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// Setup desired DX state
ImGui_ImplDX9_SetupRenderState(draw_data);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX9_SetupRenderState(draw_data);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->TextureId;
g_pd3dDevice->SetTexture(0, texture);
g_pd3dDevice->SetScissorRect(&r);
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount/3);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore the DX9 transform
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// Restore the DX9 state
d3d9_state_block->Apply();
d3d9_state_block->Release();
}
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
{
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_dx9";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
g_pd3dDevice = device;
g_pd3dDevice->AddRef();
return true;
}
void ImGui_ImplDX9_Shutdown()
{
ImGui_ImplDX9_InvalidateDeviceObjects();
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
}
static bool ImGui_ImplDX9_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
// Upload texture to graphics system
g_FontTexture = NULL;
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
return false;
D3DLOCKED_RECT tex_locked_rect;
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
return false;
for (int y = 0; y < height; y++)
memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
g_FontTexture->UnlockRect(0);
// Store our identifier
io.Fonts->TexID = (ImTextureID)g_FontTexture;
return true;
}
bool ImGui_ImplDX9_CreateDeviceObjects()
{
if (!g_pd3dDevice)
return false;
if (!ImGui_ImplDX9_CreateFontsTexture())
return false;
return true;
}
void ImGui_ImplDX9_InvalidateDeviceObjects()
{
if (!g_pd3dDevice)
return;
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
}
bool ImGui_ImplDX9_NewFrame()
{
if (!g_FontTexture)
return ImGui_ImplDX9_CreateDeviceObjects();
return true;
}

View File

@ -0,0 +1,23 @@
// dear imgui: Renderer for DirectX9
// This needs to be used along with a Platform Binding (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
#pragma once
struct IDirect3DDevice9;
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
IMGUI_IMPL_API bool ImGui_ImplDX9_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing ImGui state.
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();

View File

@ -0,0 +1,324 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#include "../../imgui.h"
#include "imgui_impl_win32.h"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <XInput.h>
#include <tchar.h>
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
// Win32 Data
static HWND g_hWnd = 0;
static INT64 g_Time = 0;
static INT64 g_TicksPerSecond = 0;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
static bool g_HasGamepad = false;
static bool g_WantUpdateHasGamepad = true;
// Functions
bool ImGui_ImplWin32_Init(void* hwnd)
{
if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
return false;
if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
return false;
// Setup back-end capabilities flags
g_hWnd = (HWND)hwnd;
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_win32";
io.ImeWindowHandle = hwnd;
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
io.KeyMap[ImGuiKey_Home] = VK_HOME;
io.KeyMap[ImGuiKey_End] = VK_END;
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
io.KeyMap[ImGuiKey_X] = 'X';
io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z';
return true;
}
void ImGui_ImplWin32_Shutdown()
{
g_hWnd = (HWND)0;
}
static bool ImGui_ImplWin32_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return false;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
::SetCursor(NULL);
}
else
{
// Show OS mouse cursor
LPTSTR win32_cursor = IDC_ARROW;
switch (imgui_cursor)
{
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
}
::SetCursor(::LoadCursor(NULL, win32_cursor));
}
return true;
}
static void ImGui_ImplWin32_UpdateMousePos()
{
ImGuiIO& io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
{
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
::ClientToScreen(g_hWnd, &pos);
::SetCursorPos(pos.x, pos.y);
}
// Set mouse position
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
POINT pos;
if (HWND active_window = ::GetForegroundWindow())
if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd))
if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos))
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
}
/*
#ifdef _MSC_VER
#pragma comment(lib, "xinput")
#endif
// Gamepad navigation mapping
static void ImGui_ImplWin32_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
if (g_WantUpdateHasGamepad)
{
XINPUT_CAPABILITIES caps;
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
g_WantUpdateHasGamepad = false;
}
XINPUT_STATE xinput_state;
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
{
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
#undef MAP_BUTTON
#undef MAP_ANALOG
}
}
*/
void ImGui_ImplWin32_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
// Setup display size (every frame to accommodate for window resizing)
RECT rect;
::GetClientRect(g_hWnd, &rect);
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
// Setup time step
INT64 current_time;
::QueryPerformanceCounter((LARGE_INTEGER *)&current_time);
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
g_Time = current_time;
// Read keyboard modifiers inputs
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
io.KeySuper = false;
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
// Update OS mouse position
ImGui_ImplWin32_UpdateMousePos();
// Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if (g_LastMouseCursor != mouse_cursor)
{
g_LastMouseCursor = mouse_cursor;
ImGui_ImplWin32_UpdateMouseCursor();
}
// Update game controllers (if enabled and available)
//ImGui_ImplWin32_UpdateGamepads();
}
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
#ifndef WM_MOUSEHWHEEL
#define WM_MOUSEHWHEEL 0x020E
#endif
#ifndef DBT_DEVNODES_CHANGED
#define DBT_DEVNODES_CHANGED 0x0007
#endif
// Process Win32 mouse/keyboard inputs.
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui::GetCurrentContext() == NULL)
return 0;
ImGuiIO& io = ImGui::GetIO();
switch (msg)
{
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
{
int button = 0;
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
// if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
// ::SetCapture(hwnd);
io.MouseDown[button] = true;
return 0;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
{
int button = 0;
if (msg == WM_LBUTTONUP) { button = 0; }
if (msg == WM_RBUTTONUP) { button = 1; }
if (msg == WM_MBUTTONUP) { button = 2; }
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
io.MouseDown[button] = false;
// if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
// ::ReleaseCapture();
return 0;
}
case WM_MOUSEWHEEL:
io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return 0;
case WM_MOUSEHWHEEL:
io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (wParam < 256)
io.KeysDown[wParam] = 1;
return 0;
case WM_KEYUP:
case WM_SYSKEYUP:
if (wParam < 256)
io.KeysDown[wParam] = 0;
return 0;
case WM_CHAR:
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
io.AddInputCharacter((unsigned int)wParam);
return 0;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
return 1;
return 0;
case WM_DEVICECHANGE:
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
g_WantUpdateHasGamepad = true;
return 0;
}
return 0;
}

View File

@ -0,0 +1,21 @@
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// Implemented features:
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
#pragma once
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
// Handler for Win32 messages, update mouse/keyboard data.
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
/*
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
*/

630
ImGui/imstb_rectpack.h Normal file
View File

@ -0,0 +1,630 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 0.99.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v0.99 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
//
// Version history:
//
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
#else
typedef unsigned short stbrp_coord;
#endif
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
#ifndef STBRP_LARGE_RECTS
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
#else
context->extra[1].y = 65535;
#endif
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height < c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
#ifdef STBRP_LARGE_RECTS
#define STBRP__MAXVAL 0xffffffff
#else
#define STBRP__MAXVAL 0xffff
#endif
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

1417
ImGui/imstb_textedit.h Normal file

File diff suppressed because it is too large Load Diff

4903
ImGui/imstb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
CXX=clang++
CXX_FLAGS += -fPIC -std=c++11
LD_FLAGS += -shared -lprotobuf-lite -Wl,--no-undefined
LD_FLAGS += -shared -lprotobuf-lite -ldl -Wl,--no-undefined
LIBRARY_NAME=libsteam_api.so
RM = rm -f

231
README.md
View File

@ -87,6 +87,237 @@ Run the command:
git pull
```
## Building using CMake
The following targets are included withing the CMake configuration for this project:
- Shared Libraries:
- steam_api
- steamclient
- steamnetworkingsockets
- Executables:
- lobby_connect
- generate_interfaces
While all targets are included for all platforms/build variants, there are a couple of points to note:
- All targets are supported for All Windows (x64 and x86) platform/build variant combinations
- Build all inclusion of 'steamclient' and 'steamnetworkingsockets' is disabled for All Linux/WSL platforms (the targets code does currently not support these platforms)
- Experimental build variants will fail on Linux/WSL platforms (this is due to the code of the target experimental 'steam_api' variant not supporting these platforms)
The CMake configuration for this project also includes install support. Installing the project will result in a cleaner set of output files (than the raw build files) and will copy over the appropriate readmes, tools and other support files from the projects directory.
This install is structured as followed:
```
+ install-folder
|- (lib)steam_api(64).[dll|so]
|- (lib)steamclient(64).[dll|so]
|- (lib)steamnetworkingsockets(64).[dll|so]
|- Readme_release.txt
|- Readme_debug.txt // Only for debug build's
|- Readme_experimental.txt // Only for experimental build's
|- steam_appid.EDIT_AND_RENAME.txt
|- steam_interfaces.EXAMPLE.txt
|+ lobby_connect
|- lobby_connect(64)(.exe)
|- Readme_lobby_connect.txt
|+ tools
|- generate_interfaces(64)(.exe)
|- find_interfaces.ps1
|- find_interfaces.sh
|- Readme_generate_interfaces.txt
|+ steam_settings.EXAMPLE
|- ... // steam_settings example files
```
Note that if no `CMAKE_INSTALL_PREFIX` define is set for CMake generation (or another method of setting a custom installation directory is used) the default OS specific install directories will be used, these are:
- On Windows `c:/Program Files/${PROJECT_NAME}`
- On Linux `/usr/local`
Please see the ['Change the installation directory'](#change-the-installation-directory) section of this readme for more information.
### Windows
#### Prerequisistes
- Visual Studio 2019 Installed or Build Tools for Visual Studio 2019
- Can both be obtained here: https://visualstudio.microsoft.com/downloads/
- (Optional) If you are planning to use Visual Studio make sure you include the following workloads during installation:
- 'Desktop Development with C++'
- 'C++ CMake tools for Windows' (Optional of the 'Desktop Development with C++' workload)
- (Optional) If you want build for Linux from Visual studio also include the 'Linux development with C++' workload.
- CMake ^3.6
- Can be obtained here: https://cmake.org/download/
- VCPKG
- Can be downloaded here: https://github.com/microsoft/vcpkg/archive/master.zip
- (Optional) For ease of use I advise you to extract the contents of this zip (the vcpkg-master folder) along side your goldberg_emulator folder and rename the vcpkg-master folder to just vcpkg, resulting in the following folder structure:
```
+ some-top-level-folder
|- vcpkg
|- goldberg_emulator
```
- Can be installed by running the `bootstrap-vcpkg.bat` from the installation folder
- protobuf ^3.1 && protobuf compiler
- Can be installed (via VCPKG) by running `vcpkg install protobuf --triplet x64-windows-static && vcpkg install protobuf --triplet x86-windows-static`
- Alternatively you can try to compile them from the source, for instructions see: https://github.com/protocolbuffers/protobuf )
#### Generate and Build using Visual Studio 2019
This repo includes a CMakeSettings.json file which contains the configurations for the following target platforms and build variants:
- Windows-x64-Release
- Windows-x64-Debug
- Windows-x86-Release
- Windows-x86-Debug
- Windows-x64-ExperimentalRelease
- Windows-x64-ExperimentalDebug
- Windows-x86-ExperimentalRelease
- Windows-x86-ExperimentalDebug
- Linux-x64-Release
- Linux-x64-Debug
- Linux-x86-Release
- Linux-x86-Debug
- Linux-x64-ExperimentalRelease
- Linux-x64-ExperimentalDebug
- Linux-x86-ExperimentalRelease
- Linux-x86-ExperimentalDebug
- WSL-x64-Release
- WSL-x64-Debug
- WSL-x86-Release
- WSL-x86-Debug
- WSL-x64-ExperimentalRelease
- WSL-x64-ExperimentalDebug
- WSL-x86-ExperimentalRelease
- WSL-x86-ExperimentalDebug
These configurations should be automatically loaded when opening the goldberg_emulator folder in Visual Studio.
For more information on how to use these configurations (and CMake project in Visual Studio in general) please see:
https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=vs-2019
Visual Studio builds for Windows and WSL configurations from will be outputted to the following folder:
`${projectDir}\out\${workspaceHash}\build\<configuration name>`
You can also opt to install directly from Visual Studio. Visual Studio installs for Windows configurations from will be outputted to the following folder:
`${projectDir}\out\install\<configuration name>`
While using these configurations are a couple of points to note:
- If you installed VCPKG into a different folder you might need to update the 'cmakeToolchain' field for each Windows configuration in the CMakeSettings.json to reflect the new VCPKG folder location
- Linux build configurations require a connection to a target Linux system in order to work, more information on how to set this up can be found here:
https://docs.microsoft.com/en-us/cpp/linux/connect-to-your-remote-linux-computer?view=vs-2019
- WSL build configurations require a Windows Subsystem Linux to be installed:
https://docs.microsoft.com/en-us/cpp/linux/connect-to-your-remote-linux-computer?view=vs-2019#connect-to-wsl
- Both the Linux build system and the WSL instance require the same prerequisites as a found in the 'Building using CMake - Linux' section below
- Direct installation of the project from Visual Studio is currently only supported for Windows build configuration due to limited support for remote install in Visual Studio.
#### Generate x64
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars64.bat"
cd "<build folder>"
cmake "<goldberg_emulator src folder>" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="<vcpkg installation folder>\scripts\buildsystems\vcpkg.cmake"
```
Note that if you are using the Build Tools for Visual Studio 2019 the path to the vcvars64.bat is slightly diffrent:
```
call "<Path to Build Tools for Visual Studio 2019 Installation Folder>\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
```
#### Build x64
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars64.bat"
cd "<build folder>"
nmake
```
#### Install x64
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars64.bat"
cd "<build folder>"
nmake install
```
#### Generate x86
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars86.bat"
cd "<build folder>"
cmake "<goldberg_emulator src folder>" -DVCPKG_TARGET_TRIPLET:STRING="x86-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="<vcpkg installation folder>\scripts\buildsystems\vcpkg.cmake"
```
Note that if you are using the Build Tools for Visual Studio 2019 the path to the vcvars86.bat is slightly different:
```
call "<Path to Build Tools for Visual Studio 2019 Installation Folder>\2019\BuildTools\VC\Auxiliary\Build\vcvars86.bat"
```
#### Build x86
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars86.bat"
cd "<build folder>"
nmake
```
#### Install x86
```
call "<Path to Microsoft Visual Studio Installation Folder>\2019\VC\Auxiliary\Build\vcvars86.bat"
cd "<build folder>"
nmake install
```
### Linux
#### Prerequisistes
- Compile tools
- Can usually be obtained via your distro's package manager (e.g. on ubuntu: `sudo apt install build-essential`)
- Cmake ^3.6
- Can usually be obtained via your distro's package manager (e.g. on ubuntu: `sudo apt install cmake`)
- protobuf ^3.1 && protobuf compiler
- Can usually be obtained via your distro's package manager (e.g. on ubuntu(^disco): `sudo apt install libprotobuf-dev protobuf-compiler`)
- Alternatively you can try to compile them from the source, for instructions see: https://github.com/protocolbuffers/protobuf )
#### Generate x64/x86
```
cd "<build folder>"
cmake "<goldberg_emulator src folder>"
```
#### Build
```
cd "<build folder>"
make
```
#### Install
```
cd "<build folder>"
make install
```
### Additional CMake Related Options
#### Change the target build system
To set the generator, append `-G "<Generator Name>"` e.g.
```
cmake .. -G "Ninja" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="..\vcpkg\scripts\buildsystems\vcpkg.cmake"
```
#### Change the target build type
To set the build type, append `-DCMAKE_BUILD_TYPE:STRING="<Build Type>"` e.g.
```
cmake .. -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="..\vcpkg\scripts\buildsystems\vcpkg.cmake" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo"
```
#### Enable the experimental build
To set the experimental build, append `-DEMU_EXPERIMENTAL_BUILD:BOOL=ON` e.g.
```
cmake .. -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="..\vcpkg\scripts\buildsystems\vcpkg.cmake" -DEMU_EXPERIMENTAL_BUILD:BOOL=ON
```
#### Build with the ninja build system
To build a cmake config generated with Ninja:
```
cd "<build folder>"
ninja
```
#### Change the installation directory
To use a custom installation direction, append `-DCMAKE_INSTALL_PREFIX:STRING="<Custom Installation Directory>"` e.g.
```
cmake .. -DCMAKE_INSTALL_PREFIX:STRING="./install/" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE:STRING="..\vcpkg\scripts\buildsystems\vcpkg.cmake" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo"
```
If you do not want to preset the installation directory during the generation step you can also use a build tool or OS specific overwrite, some examples of this are:
- On Windows `nmake install prefix="<Custom Installation Directory>"`
- On Linux `make DESTDIR="<Custom Installation Directory>" install`
## Design Choices / FAQ

View File

@ -0,0 +1,4 @@
This is a build of the experimental version of my emu in steamclient mode with an included loader. See both the regular and experimental readmes for how to configure it.
Note that all emu config files should be put beside the steamclient dll. You do not need to put a steam_interfaces.txt file for the steamclient version of the emu.
To use the loader, put both steamclient dlls and the loader in a folder and edit the config file. Make sure you put the right appid in the ini file.

View File

@ -29,6 +29,9 @@ Note that these are global so you won't have to change them for each game. For g
If you want to change your steam_id on a per game basis, simply create a settings folder in the game unique directory (Full path: C:\Users\<Your windows user name>\AppData\Roaming\Goldberg SteamEmu Saves\<appid>\settings)
In that settings folder create a user_steam_id.txt file that contains the valid steam id that you want to use for that game only.
You can also make the emu ignore certain global settings by using a force_account_name.txt, force_language.txt or force_steamid.txt that you put in the <path where my emu lib is>\steam_settings\ folder.
See the steam_settings.EXAMPLE folder for an example.
If for some reason you want it to save in the game directory you can create a file named local_save.txt right beside steam_api(64).dll (libsteam_api.so on linux)
The only thing that file should contain is the name of the save directory. This can be useful if you want to use different global settings like a different account name or steam id for a particular game.
Note that this save directory will be beside where the emu dll (or .so) is which may not be the same as the game path.
@ -41,6 +44,14 @@ If the DLC file is present, the emulator will only unlock the DLCs in that file.
The contents of this file are: appid=DLC name
See the steam_settings.EXAMPLE folder for an example.
Depots:
This is pretty rare but some games might use depot ids to see if dlcs are installed. You can provide a list of installed depots to the game with a steam_settings\depots.txt file.
See the steam_settings.EXAMPLE folder for an example.
Subscribed Groups:
Some games like payday 2 check which groups you are subscribed in and unlock things based on that. You can provide a list of subscribed groups to the game with a steam_settings\subscribed_groups.txt file.
See steam_settings.EXAMPLE\subscribed_groups.EXAMPLE.txt for an example for payday 2.
App paths:
Some rare games might need to be provided one or more paths to app ids. For example the path to where a dlc is installed. This sets the paths returned by the Steam_Apps::GetAppInstallDir function.
See steam_settings.EXAMPLE\app_paths.EXAMPLE.txt for an example.
@ -60,20 +71,103 @@ The steam appid can also be set using the SteamAppId or SteamGameId env variable
Offline mode:
Some games that connect to online servers might only work if the steam emu behaves like steam is in offline mode. If you need this create a offline.txt file in the steam_settings folder.
Disable networking:
If for some reason you want to disable all the networking functionality of the emu you can create a disable_networking.txt file in the steam_settings folder. This will of course break all the
networking functionality so games that use networking related functionality like lobbies or those that launch a server in the background will not work.
Custom Broadcast ips:
If you want to set custom ips (or domains) which the emulator will send broadcast packets to, make a list of them, one on each line in: Goldberg SteamEmu Saves\settings\custom_broadcasts.txt
If the custom ips/domains are specific for one game only you can put the custom_broadcasts.txt in the steam_settings\ folder.
An example is provided in steam_settings.EXAMPLE\custom_broadcasts.EXAMPLE.txt
Achievements, Items or Inventory:
Create a folder named steam_settings right beside steam_api.dll if there isn't one already. In that folder, create a file named items.json and/or achievements.json which will contain every item/achievement you want to have in your game.
An example can be found in steam_settings.EXAMPLE that works with Killing Floor 2.
The items.json syntax is simple, you SHOULD validate your .json file before trying to run your game or you won't have any item in your inventory. Just look for "online json validator" on your web brower to valide your file.
You can use https://steamdb.info/ to list items and attributes they have and put them into your .json.
Keep in mind that some item are not valid to have in your inventory. For example, in PayDay2 all items below item_id 50000 will make your game crash.
items.json should contain all the item definitions for the game, default_items.json is the quantity of each item that you want a user to have initially in their inventory. By default the user will have no items.
Leaderboards:
By default the emulator assumes all leaderboards queried by the game (FindLeaderboard()) exist and creates them with the most common options (sort method descending, display type numeric)
In some games this default behavior doesn't work and so you may need to tweak which leaderboards the game sees.
To do that, you can put a leaderboards.txt file in the steam_settings folder.
An empty leaderboards.txt makes the emu behave as if any leaderboard queried by the game using FindLeaderboard does not exist.
The format is: LEADERBOARD_NAME=sort method=display type
For the sort methods: 0 = none, 1 = ascending, 2 = descending
For the display type: 0 = none, 1 = numeric, 2 = time seconds, 3 = milliseconds
An example can be found in steam_settings.EXAMPLE
Stats:
By default this emulators assumes all stats do not exist unless they have been written once by the game. This works for the majority of games but some games might read a stat for the first time
and expect a default value to be read when doing so. To set the type for each stat along with the default value, put a stats.txt file in the steam_settings/ folder.
The format is: STAT_NAME=type=default value
The type can be: int, float or avgrate
The default value is simply a number that represents the default value for the stat.
Support for CPY steam_api(64).dll cracks: See the build in the experimental folder.
Notes:
You must all be on the same LAN for it to work. This is an early work so a lot of games will likely not work.
You must all be on the same LAN for it to work.
IMPORTANT:
Do not run more than one steam game with the same appid at the same time on the same computer with my emu or there might be network issues (dedicated servers should be fine though).
Overlay (Note: at the moment this feature is only enabled in the windows experimental builds):
The overlay can be disabled by putting a file named disable_overlay.txt in the steam_settings folder. This is for games that depend on the steam overlay to let people join multiplayer games.
Use SHIFT-TAB to open the overlay.
Controller (Note: at the moment this feature is only enabled in the windows experimental builds and the linux builds):
SteamController/SteamInput support is limited to XInput controllers. If your controller is not XInput, there are many tools (at least for windows) that you can use to make it emulate an XInput one.
Steam uses things called action sets for controller configuration. An action set is a group of action names. Action names are bound to buttons, triggers or joysticks.
The emulator needs to know for each action set, which button is linked to which action name. Create a ACTION_SET_NAME.txt file in the steam_settings\controller folder for every action set the game uses.
To see an example for the game Crystar see: steam_settings.EXAMPLE\controller.EXAMPLE
In the action set txt files the format is:
For digital actions (buttons, on or off): ACTION_NAME=BUTTON_NAME
For analog actions (joysticks, triggers): ACTION_NAME=ANALOG_NAME=input source mode
Actions can be bound to more than one button by separating the buttons with , like this: ACTION_NAME=A,B
If you want to configure a game yourself, find the xbox360 or xbox one vdf file for the game and you should be able to figure things out.
For example to get the vdf file for the game Crystar: https://steamdb.info/app/981750/config/
If you look at: steamcontrollerconfigdetails, you will see something like: 1779660455/controller_type: controller_xbox360
1779660455 refers to a file id that you can dl using your favorite steam workshop downloader site.
The url would be: https://steamcommunity.com/sharedfiles/filedetails/?id=1779660455
The glyphs directory contains some glyphs for the controller buttons for the games that use the GetGlyphForActionOrigin function.
If you want to use the real steam glyphs instead of the free ones in the example directory copy them from: <Steam Directory>\tenfoot\resource\images\library\controller\api folder.
Valid digital button names:
DUP
DDOWN
DLEFT
DRIGHT
START
BACK
LSTICK
RSTICK
LBUMPER
RBUMPER
A
B
X
Y
DLTRIGGER (emulated buttons, the joy ones are used by games in menus for example. When the game wants to know if the trigger is pressed without the intensity)
DRTRIGGER
DLJOYUP
DLJOYDOWN
DLJOYLEFT
DLJOYRIGHT
DRJOYUP
DRJOYDOWN
DRJOYLEFT
DRJOYRIGHT
Valid analog names:
LTRIGGER
RTRIGGER
LJOY
RJOY

50
blog/1_year.md Normal file
View File

@ -0,0 +1,50 @@
# Goldberg Emulator v0.2.5 : 1 Year of Steam Emulator Development
If you just want the download link or more info go to [the Gitlab pages website](https://mr_goldberg.gitlab.io/goldberg_emulator/)
For those that don't know what this is, this is a steam api replacement that lets you play games that use the steam multiplayer apis on LAN without needing steam installed.
It has been a bit more than 1 year since I started working on this steam emulator. I started working on this project on August 3, 2018 and released a first version on August 12, 2018. It has been a much more challenging project than I originally expected. I made it open source on April 13, 2019.
About 1 year ago I decided that I wanted to improve my C++ skills. I had never written any big projects in C++ before, only in C.
In one year with me working on it during my sometimes inexistent free time it has gone from nothing to a very accurate Steam Emulator that works on almost every game.
The main thing I noticed when developing my steam emulator is that too many games seem to be written by people who have no idea what they are doing. They seem to write random code until they get something that works. This leads to some game having convulted behaviors that make me wonder if it's actually part of a DRM scheme that prevent it from working on shitty steam emulators.
Steam userids are stored as a 64bit integer but the part that represents the actual id of the user is a 32bit unsigned integer. However some games (Nioh, Senran Kagura Estival Versus) treat this as a signed int and assume it to be positive. This means if your id is more than 2147483648, bugs in those games start to appear (Nioh crashes and Estival Versus multiplayer doesn't work). There are likely more games that make this mistake. Steam seems to assign ids sequentially so the 2147483648 and next users to create a steam account will likely run into a few weird game breaking bugs.
If you look at the emulator code, you might see a lot of callbacks and behaviours have artificial delays added in. This is because many games depend on those delays and some depend on some operations being always faster than others. For example, the game Ignite calls the logon function on the gameserver interface and the createlobby function on the matchmaking interface at the same time. If for some reason creating a lobby is slower than logging on as a gameserver the created multiplayer game will not be joinable.
I have constantly needed to slow down things in order to fix games.
In the beginning I made the networking functions to have as little delay as possible but that was a mistake because many games (like Age of Empires 2 HD) expect at least some delay.
If valve decides to ever make their steam api faster or change any of the undefined behavior that their api has, many games will break. This means they are stuck and can never change or fix anything. Any change they make has the risk of breaking games that will likely never be updated.
An example of something weird I have had to do is add a microsecond sleep in the SteamAPI_RunCallbacks() function. This function is usually called by the game during every game frame. When I was testing the game, I noticed that the game didn't lag with my slow debug build that prints a lot of stuff to disk while it lagged with my much faster release build. After some tests I hypothesized that it was probably lagging because the thread never yielded to other threads. Of course the std thread yield function didn't do anything but a microsecond sleep completely fixed the lag.
In some games like Senran Kagura Peach Beach Splash when the user leaves a lobby, the game leaves the lobby then right away call some functions on that lobby. If the lobby function returns as if the lobby doesn't exist anymore the game crashes. It turns out that on real steam lobbies stay alive and functions called on them succeed (and act like you are still in the lobby) even if you leave them and there's nobody else in them.
There are also games like School Girl Zombie Hunter that create a lobby and then search for that created lobby. If the search takes less than 3 seconds, the game gives you a nice error that it failed to create the lobby.
Games like No Mans Sky which for some reason creates multiple lobbies when they could have made it much more simple. For anyone that knows about the game it should not come as a surprise that the developers are incompetent. It's clear just looking at how long the game takes to load. The game also has a profanity filter written by top minds which when I tried the game a few months ago for some reason censored the word "noob" but not words like "kike".
An advice if you are a game dev. If you think doing a hash/signature check on the steam api dll will prevent your game from being pirated you are an idiot. The steam emulators made specifically for piracy bypass that easily. Don't do like Risk of Rain 2 that only checks if the steam dll was signed (but not with which signature so a self signed emu is all that's needed to bypass the check).
If you love puzzles which are a real pain in the ass to solve and if you like being frustrated while debugging why some of the most shitty game code ever written by man doesn't work then steam emu development is for you.
As for the future of the emulator thanks to Nemirtingas an overlay might be coming very soon.
### Changelog for 0.2.5
```
v0.2.5
Inventory support added (Thank you Nemirtingas, see the Readme for more info)
Initial Steam Networking Sockets implementation (Fixes Hat in Time Multiplayer)
Updated to sdk 1.46
More accurate auth behavior.
Leaderboards are now configurable (see Readme, some games like Million Arthur or Roof Rage need a steam_settings\leaderboards.txt to work properly).
Various remote storage improvements (Fixed saving/loading in a few games)
A bunch of accuracy improvements and other fixes.
```

37
build_env_x64.bat Normal file → Executable file
View File

@ -1,16 +1,37 @@
if exist "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" goto vs14
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2017
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2019
@echo off
cd /d "%~dp0"
rem Put in the base path in which Visual Studio is installed, default would be C:\Program Files (x86)
:vs14
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
set VS_Base_Path=C:\Program Files (x86)
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" goto vs2019
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" goto vs2017
if exist "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" goto vs14
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2019_bt
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2017_bt
if exist ".\sdk_standalone\set_vars64.bat" goto gitlabci
:vs2019
call "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:vs2017
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
call "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:vs2019
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
:vs14
call "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
goto batend
:vs2019_bt
call "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:vs2017_bt
call "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
goto batend
:gitlabci
call ".\sdk_standalone\set_vars64.bat"
goto batend
:batend

38
build_env_x86.bat Normal file → Executable file
View File

@ -1,16 +1,38 @@
if exist "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" goto vs14
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2017
if exist "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2019
@echo off
cd /d "%~dp0"
rem Put in the base path in which Visual Studio is installed, default would be C:\Program Files (x86)
:vs14
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"
set VS_Base_Path=C:\Program Files (x86)
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" goto vs2019
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" goto vs2017
if exist "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64_x86\vcvarsamd64_x86.bat" goto vs14
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2019_bt
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2017_bt
if exist ".\sdk_standalone\set_vars32.bat" goto gitlabci
:vs2019
call "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:vs2017
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
call "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:vs2019
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
:vs14
call "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64_x86\vcvarsamd64_x86.bat"
goto batend
:vs2019_bt
call "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:vs2017_bt
call "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
goto batend
:gitlabci
call ".\sdk_standalone\set_vars32.bat"
goto batend
:batend

0
build_linux.sh Normal file → Executable file
View File

20
build_set_protobuf_directories.bat Normal file → Executable file
View File

@ -1,10 +1,20 @@
SET PROTOBUF_X86_DIRECTORY=..\vcpkg\packages\protobuf_x86-windows-static
SET PROTOBUF_X64_DIRECTORY=..\vcpkg\packages\protobuf_x64-windows-static
@echo off
cd /d "%~dp0"
SET PROTOBUF_X86_DIRECTORY=..\vcpkg\installed\x86-windows-static
SET PROTOBUF_X64_DIRECTORY=..\vcpkg\installed\x64-windows-static
rem location of protoc in protobuf directories:
SET PROTOC_DIRECTORY=\tools\protobuf\protoc.exe
SET PROTOC_X86_EXE=%PROTOBUF_X86_DIRECTORY%\tools\protobuf\protoc.exe
SET PROTOC_X64_EXE=%PROTOBUF_X64_DIRECTORY%\tools\protobuf\protoc.exe
if exist "%PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib" (
SET PROTOBUF_X86_LIBRARY=%PROTOBUF_X86_DIRECTORY%\lib\libprotobuf-lite.lib
) else (
SET PROTOBUF_X86_LIBRARY=%PROTOBUF_X86_DIRECTORY%\lib\libprotobuf.lib
)
if exist "%PROTOBUF_X64_DIRECTORY%\lib\libprotobuf-lite.lib" (
SET PROTOBUF_LIBRARY=\lib\libprotobuf-lite.lib
SET PROTOBUF_X64_LIBRARY=%PROTOBUF_X64_DIRECTORY%\lib\libprotobuf-lite.lib
) else (
SET PROTOBUF_LIBRARY=\lib\libprotobuf.lib
SET PROTOBUF_X64_LIBRARY=%PROTOBUF_X64_DIRECTORY%\lib\libprotobuf.lib
)

View File

@ -5,9 +5,12 @@ mkdir -p linux/x86_64
mkdir -p linux/lobby_connect
mkdir -p linux/tools
cp scripts/find_interfaces.sh linux/tools/
cp scripts/steamclient_loader.sh linux/tools/
../protobuf/prefix_x86/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect32
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built32
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built_lobby_connect32
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built32_steamclient
../protobuf/prefix/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect64
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built64
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built_lobby_connect64
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built64_steamclient

View File

@ -1,10 +1,14 @@
@echo off
cd /d "%~dp0"
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl /LD /I%PROTOBUF_X86_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X86_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api.dll
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl /LD /I%PROTOBUF_X64_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X64_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api64.dll
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll

View File

@ -0,0 +1,11 @@
cd /d "%~dp0"
call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient64.dll
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll

2
build_win_find_interfaces.bat Normal file → Executable file
View File

@ -1,3 +1,5 @@
@echo off
cd /d "%~dp0"
mkdir release\tools
del /Q release\tools\*
call build_env_x86.bat

View File

@ -1,9 +1,11 @@
@echo off
cd /d "%~dp0"
mkdir release\lobby_connect
del /Q release\lobby_connect\*
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl /DNO_DISK_WRITES /DLOBBY_CONNECT /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ lobby_connect.cpp dll/*.cpp dll/*.cc %PROTOBUF_X86_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib Comdlg32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\lobby_connect\lobby_connect.exe
cl /DNO_DISK_WRITES /DLOBBY_CONNECT /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ lobby_connect.cpp dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib Shell32.lib Comdlg32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\lobby_connect\lobby_connect.exe
del /Q /S release\lobby_connect\*.lib
del /Q /S release\lobby_connect\*.exp
copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt

View File

@ -1,17 +1,22 @@
@echo off
cd /d "%~dp0"
del /Q /S release\*
rmdir /S /Q release\experimental
rmdir /S /Q release\experimental_steamclient
rmdir /S /Q release\lobby_connect
rmdir /S /Q release
mkdir release
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc %PROTOBUF_X86_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll
%PROTOBUF_X64_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc %PROTOBUF_X64_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll
copy Readme_release.txt release\Readme.txt
xcopy /s files_example\* release\
call build_win_release_experimental.bat
call build_win_release_experimental_steamclient.bat
call build_win_lobby_connect.bat
call build_win_find_interfaces.bat

View File

@ -1,12 +1,14 @@
@echo off
cd /d "%~dp0"
mkdir release\experimental
del /Q release\experimental\*
call build_set_protobuf_directories.bat
%PROTOBUF_X86_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X86_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient.dll
%PROTOBUF_X64_DIRECTORY%%PROTOC_DIRECTORY% -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x64.bat
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp %PROTOBUF_X64_DIRECTORY%%PROTOBUF_LIBRARY% Iphlpapi.lib Ws2_32.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DNDEBUG steamclient.cpp /EHsc /MP12 /Ox /link /OUT:release\experimental\steamclient64.dll
copy Readme_experimental.txt release\experimental\Readme.txt

View File

@ -0,0 +1,16 @@
@echo off
cd /d "%~dp0"
mkdir release\experimental_steamclient
del /Q release\experimental_steamclient\*
call build_set_protobuf_directories.bat
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
call build_env_x86.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DGLEW_STATIC /IImGui /Iglew\include /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient.dll
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
cl steamclient_loader/*.cpp advapi32.lib user32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient_loader.exe
copy steamclient_loader\ColdClientLoader.ini release\experimental_steamclient\
call build_env_x64.bat
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DGLEW_STATIC /IImGui /Iglew\include /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient64.dll
copy Readme_experimental_steamclient.txt release\experimental_steamclient\Readme.txt

738
controller/gamepad.c Normal file
View File

@ -0,0 +1,738 @@
/**
* Gamepad Input Library
* Sean Middleditch
* Copyright (C) 2010 Sean Middleditch
* LICENSE: MIT/X
*/
#include <math.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#define GAMEPAD_EXPORT 1
#include "gamepad.h"
/* Platform-specific includes */
#if defined(_WIN32)
# define WIN32_LEAN_AND_MEAN 1
# undef UNICODE
# include "windows.h"
# include "xinput.h"
# pragma comment(lib, "XINPUT9_1_0.lib")
#elif defined(__linux__)
# include <linux/joystick.h>
# include <stdio.h>
# include <fcntl.h>
# include <unistd.h>
# include <dirent.h>
# include <sys/stat.h>
# include <time.h>
#else
# error "Unknown platform in gamepad.c"
#endif
#define BUTTON_TO_FLAG(b) (1 << (b))
/* Axis information */
typedef struct GAMEPAD_AXIS GAMEPAD_AXIS;
struct GAMEPAD_AXIS {
int x, y;
float nx, ny;
float length;
float angle;
GAMEPAD_STICKDIR dirLast, dirCurrent;
};
/* Trigger value information */
typedef struct GAMEPAD_TRIGINFO GAMEPAD_TRIGINFO;
struct GAMEPAD_TRIGINFO {
int value;
float length;
GAMEPAD_BOOL pressedLast, pressedCurrent;
};
/* Structure for state of a particular gamepad */
typedef struct GAMEPAD_STATE GAMEPAD_STATE;
struct GAMEPAD_STATE {
GAMEPAD_AXIS stick[STICK_COUNT];
GAMEPAD_TRIGINFO trigger[TRIGGER_COUNT];
int bLast, bCurrent, flags;
#if defined(__linux__)
char* device;
int fd;
int effect;
double axis_min[ABS_MAX];
double axis_max[ABS_MAX];
#endif
};
/* State of the four gamepads */
static GAMEPAD_STATE STATE[4];
/* Note whether a gamepad is currently connected */
#define FLAG_CONNECTED (1<<0)
#define FLAG_RUMBLE (1<<1)
/* Prototypes for utility functions */
static void GamepadResetState (GAMEPAD_DEVICE gamepad);
static void GamepadUpdateCommon (void);
static void GamepadUpdateDevice (GAMEPAD_DEVICE gamepad);
static void GamepadUpdateStick (GAMEPAD_AXIS* axis, float deadzone);
static void GamepadUpdateTrigger (GAMEPAD_TRIGINFO* trig);
/* Various values of PI */
#define PI_1_4 0.78539816339744f
#define PI_1_2 1.57079632679489f
#define PI_3_4 2.35619449019234f
#define PI 3.14159265358979f
/* Platform-specific implementation code */
#if defined(_WIN32)
void GamepadInit(void) {
int i;
for (i = 0; i != GAMEPAD_COUNT; ++i) {
STATE[i].flags = 0;
}
}
void GamepadUpdate(void) {
GamepadUpdateCommon();
}
static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) {
XINPUT_STATE xs;
if (XInputGetState(gamepad, &xs) == 0) {
/* reset if the device was not already connected */
if ((STATE[gamepad].flags & FLAG_CONNECTED) == 0) {
GamepadResetState(gamepad);
}
/* mark that we are connected w/ rumble support */
STATE[gamepad].flags |= FLAG_CONNECTED|FLAG_RUMBLE;
/* update state */
STATE[gamepad].bCurrent = xs.Gamepad.wButtons;
STATE[gamepad].trigger[TRIGGER_LEFT].value = xs.Gamepad.bLeftTrigger;
STATE[gamepad].trigger[TRIGGER_RIGHT].value = xs.Gamepad.bRightTrigger;
STATE[gamepad].stick[STICK_LEFT].x = xs.Gamepad.sThumbLX;
STATE[gamepad].stick[STICK_LEFT].y = xs.Gamepad.sThumbLY;
STATE[gamepad].stick[STICK_RIGHT].x = xs.Gamepad.sThumbRX;
STATE[gamepad].stick[STICK_RIGHT].y = xs.Gamepad.sThumbRY;
} else {
/* disconnected */
STATE[gamepad].flags &= ~FLAG_CONNECTED;
}
}
void GamepadShutdown(void) {
/* no Win32 shutdown required */
}
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right, unsigned int rumble_length_ms) {
//TODO: rumble_length_ms
if ((STATE[gamepad].flags & FLAG_RUMBLE) != 0) {
XINPUT_VIBRATION vib;
ZeroMemory(&vib, sizeof(vib));
vib.wLeftMotorSpeed = (WORD)(left * 65535);
vib.wRightMotorSpeed = (WORD)(right * 65535);
XInputSetState(gamepad, &vib);
}
}
#elif defined(__linux__)
#define test_bit(nr, addr) \
(((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
static int IsGamepad(int fd, char *namebuf, const size_t namebuflen)
{
struct input_id inpid;
//uint16_t *guid16 = (uint16_t *)guid->data;
/* When udev is enabled we only get joystick devices here, so there's no need to test them */
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
return (0);
}
if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
return 0;
}
if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
return 0;
}
if (ioctl(fd, EVIOCGID, &inpid) < 0) {
return 0;
}
//printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
//memset(guid->data, 0, sizeof(guid->data));
/* We only need 16 bits for each of these; space them out to fill 128. */
/* Byteswap so devices get same GUID on little/big endian platforms. */
/*
*guid16++ = SDL_SwapLE16(inpid.bustype);
*guid16++ = 0;
if (inpid.vendor && inpid.product) {
*guid16++ = SDL_SwapLE16(inpid.vendor);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(inpid.product);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(inpid.version);
*guid16++ = 0;
} else {
strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
}
if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
return 0;
}
*/
return 1;
}
static void GamepadAddDevice(const char* devPath);
static void GamepadRemoveDevice(const char* devPath);
static void GamepadDetect()
{
DIR *folder;
struct dirent *dent;
folder = opendir("/dev/input");
if (folder) {
while ((dent = readdir(folder))) {
int len = strlen(dent->d_name);
if (len > 5 && strncmp(dent->d_name, "event", 5) == 0) {
char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/input/%s", dent->d_name);
GamepadAddDevice(path);
}
}
closedir(folder);
}
for (int i = 0; i != GAMEPAD_COUNT; ++i) {
if ((STATE[i].flags & FLAG_CONNECTED) && STATE[i].device) {
struct stat sb;
//printf("%s\n", STATE[i].device);
if (stat(STATE[i].device, &sb) == -1) {
GamepadRemoveDevice(STATE[i].device);
}
}
}
}
/* Helper to add a new device */
static void GamepadAddDevice(const char* devPath) {
int i;
/* try to find a free controller */
for (i = 0; i != GAMEPAD_COUNT; ++i) {
if ((STATE[i].flags & FLAG_CONNECTED) == 0) {
break;
}
if (STATE[i].device && strcmp(devPath, STATE[i].device) == 0) {
return;
}
}
if (i == GAMEPAD_COUNT) {
return;
}
int fd = open(devPath, O_RDWR, 0);
if (fd < 0) return;
char namebuf[128];
int is_gamepad = IsGamepad(fd, namebuf, sizeof (namebuf));
if (!is_gamepad) {
close(fd);
return;
}
/* copy the device path */
STATE[i].device = strdup(devPath);
if (STATE[i].device == NULL) {
return;
}
/* reset device state */
GamepadResetState((GAMEPAD_DEVICE)i);
fcntl(fd, F_SETFL, O_NONBLOCK);
STATE[i].fd = fd;
STATE[i].flags |= FLAG_CONNECTED;
int controller = i;
{
int i, t;
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
/* See if this device uses the new unified event API */
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
/* Get the number of buttons, axes, and other thingamajigs */
for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
if (test_bit(i, keybit)) {
//printf("Joystick has button: 0x%x\n", i);
}
}
for (i = 0; i < BTN_JOYSTICK; ++i) {
if (test_bit(i, keybit)) {
//printf("Joystick has button: 0x%x\n", i);
}
}
for (i = 0; i < ABS_MAX; ++i) {
/* Skip hats */
if (i == ABS_HAT0X) {
i = ABS_HAT3Y;
continue;
}
if (test_bit(i, absbit)) {
struct input_absinfo absinfo;
if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
continue;
}
/*
printf("Joystick has absolute axis: 0x%.2x\n", i);
printf("Values = { %d, %d, %d, %d, %d }\n",
absinfo.value, absinfo.minimum, absinfo.maximum,
absinfo.fuzz, absinfo.flat);
*/
STATE[controller].axis_min[i] = absinfo.minimum;
STATE[controller].axis_max[i] = absinfo.maximum;
}
}
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
struct input_absinfo absinfo;
int hat_index = (i - ABS_HAT0X) / 2;
if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
continue;
}
/*
printf("Joystick has hat %d\n", hat_index);
printf("Values = { %d, %d, %d, %d, %d }\n",
absinfo.value, absinfo.minimum, absinfo.maximum,
absinfo.fuzz, absinfo.flat);
*/
//joystick->hwdata->hats_indices[joystick->nhats++] = hat_index;
}
}
if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
//++joystick->nballs;
}
}
if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
if (test_bit(FF_RUMBLE, ffbit)) {
STATE[controller].flags |= FLAG_RUMBLE;
}
if (test_bit(FF_SINE, ffbit)) {
//printf("sine\n");
}
}
}
}
/* Helper to remove a device */
static void GamepadRemoveDevice(const char* devPath) {
int i;
for (i = 0; i != GAMEPAD_COUNT; ++i) {
if (STATE[i].device != NULL && strcmp(STATE[i].device, devPath) == 0) {
if (STATE[i].fd != -1) {
close(STATE[i].fd);
STATE[i].fd = -1;
}
free(STATE[i].device);
STATE[i].device = 0;
STATE[i].flags = 0;
break;
}
}
}
void GamepadInit(void) {
struct udev_list_entry* devices;
struct udev_list_entry* item;
struct udev_enumerate* enu;
int i;
/* initialize connection state */
for (i = 0; i != GAMEPAD_COUNT; ++i) {
STATE[i].flags = 0;
STATE[i].fd = STATE[i].effect = -1;
}
GamepadDetect();
}
void GamepadUpdate(void) {
static unsigned long last = 0;
unsigned long cur = time(NULL);
if (last + 2 < cur) {
GamepadDetect();
last = cur;
}
GamepadUpdateCommon();
}
static int adjust_values_trigger(double min, double max, double value)
{
return (((value + (0 - min)) / (max - min)) * 255.0);
}
static int adjust_values_stick(double min, double max, double value)
{
return (((value + (0 - min)) / (max - min)) * (65535.0)) - 32768.0;
}
static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) {
if (STATE[gamepad].flags & FLAG_CONNECTED) {
struct input_event events[32];
int i, len;
int code;
while ((len = read(STATE[gamepad].fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
for (i = 0; i < len; ++i) {
int button = 0;
code = events[i].code;
switch (events[i].type) {
case EV_KEY:
//printf("EV_KEY %i\n", code);
switch (code) {
case BTN_SOUTH: button = BUTTON_A; break;
case BTN_EAST: button = BUTTON_B; break;
case BTN_NORTH: button = BUTTON_X; break;
case BTN_WEST: button = BUTTON_Y; break;
case BTN_TL: button = BUTTON_LEFT_SHOULDER; break;
case BTN_TR: button = BUTTON_RIGHT_SHOULDER; break;
case BTN_SELECT: button = BUTTON_BACK; break;
case BTN_START: button = BUTTON_START; break;
case BTN_MODE: button = 0; break; /* XBOX button */
case BTN_THUMBL: button = BUTTON_LEFT_THUMB; break;
case BTN_THUMBR: button = BUTTON_RIGHT_THUMB; break;
default: button = 0; break;
}
if (events[i].value) {
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(button);
} else {
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(button);
}
break;
case EV_ABS:
switch (code) {
case ABS_HAT0X:
case ABS_HAT0Y:
case ABS_HAT1X:
case ABS_HAT1Y:
case ABS_HAT2X:
case ABS_HAT2Y:
case ABS_HAT3X:
case ABS_HAT3Y:
//code -= ABS_HAT0X;
//printf("ABS_HAT %i\n", code);
switch(code) {
case ABS_HAT0X:
if (events[i].value < 0) {
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_LEFT);
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
} else if (events[i].value > 0) {
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT);
} else {
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT) & ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
}
break;
case ABS_HAT0Y:
if (events[i].value < 0) {
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_UP);
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
} else if (events[i].value > 0) {
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP);
} else {
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP) & ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
}
break;
}
break;
default:
//printf("EV_ABS %i %i\n", code, events[i].value);
if (code == ABS_Z || code == ABS_RZ) {
int value = adjust_values_trigger(STATE[gamepad].axis_min[code], STATE[gamepad].axis_max[code], events[i].value);
switch(code) {
case ABS_Z : STATE[gamepad].trigger[TRIGGER_LEFT].value = value; break;
case ABS_RZ: STATE[gamepad].trigger[TRIGGER_RIGHT].value = value; break;
}
} else {
int value = adjust_values_stick(STATE[gamepad].axis_min[code], STATE[gamepad].axis_max[code], events[i].value);
switch(code) {
case ABS_X : STATE[gamepad].stick[STICK_LEFT].x = value; break;
case ABS_Y : STATE[gamepad].stick[STICK_LEFT].y = -value; break;
case ABS_RX: STATE[gamepad].stick[STICK_RIGHT].x = value; break;
case ABS_RY: STATE[gamepad].stick[STICK_RIGHT].y = -value; break;
}
}
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
case REL_Y:
code -= REL_X;
//printf("EV_REL %i %i\n", code, events[i].value);
break;
default:
break;
}
break;
case EV_SYN:
switch (code) {
case SYN_DROPPED :
//printf("Event SYN_DROPPED detected\n");
break;
default:
break;
}
default:
break;
}
}
}
}
}
void GamepadShutdown(void) {
int i;
/* cleanup devices */
for (i = 0; i != GAMEPAD_COUNT; ++i) {
if (STATE[i].device != NULL) {
free(STATE[i].device);
}
if (STATE[i].fd != -1) {
close(STATE[i].fd);
}
}
}
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right, unsigned int rumble_length_ms) {
if (STATE[gamepad].fd != -1) {
struct input_event play;
struct ff_effect ff;
/* define an effect for this rumble setting */
ff.type = FF_RUMBLE;
ff.id = STATE[gamepad].effect;
ff.u.rumble.strong_magnitude = (unsigned short)(left * 65535);
ff.u.rumble.weak_magnitude = (unsigned short)(right * 65535);
ff.replay.length = rumble_length_ms;
ff.replay.delay = 0;
/* upload the effect */
if (ioctl(STATE[gamepad].fd, EVIOCSFF, &ff) != -1) {
STATE[gamepad].effect = ff.id;
}
/* play the effect */
play.type = EV_FF;
play.code = STATE[gamepad].effect;
play.value = 1;
write(STATE[gamepad].fd, (const void*)&play, sizeof(play));
}
}
#else /* !defined(_WIN32) && !defined(__linux__) */
# error "Unknown platform in gamepad.c"
#endif /* end of platform implementations */
GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device) {
return (STATE[device].flags & FLAG_CONNECTED) != 0 ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) {
return (STATE[device].bCurrent & BUTTON_TO_FLAG(button)) != 0 ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
GAMEPAD_BOOL GamepadButtonTriggered(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) {
return ((STATE[device].bLast & BUTTON_TO_FLAG(button)) == 0 &&
(STATE[device].bCurrent & BUTTON_TO_FLAG(button)) != 0) ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
GAMEPAD_BOOL GamepadButtonReleased(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button) {
return ((STATE[device].bCurrent & BUTTON_TO_FLAG(button)) == 0 &&
(STATE[device].bLast & BUTTON_TO_FLAG(button)) != 0) ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
int GamepadTriggerValue(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) {
return STATE[device].trigger[trigger].value;
}
float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) {
return STATE[device].trigger[trigger].length;
}
GAMEPAD_BOOL GamepadTriggerDown(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) {
return STATE[device].trigger[trigger].pressedCurrent;
}
GAMEPAD_BOOL GamepadTriggerTriggered(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) {
return (STATE[device].trigger[trigger].pressedCurrent &&
!STATE[device].trigger[trigger].pressedLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
GAMEPAD_BOOL GamepadTriggerReleased(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger) {
return (!STATE[device].trigger[trigger].pressedCurrent &&
STATE[device].trigger[trigger].pressedLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
void GamepadStickXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, int *outX, int *outY) {
*outX = STATE[device].stick[stick].x;
*outY = STATE[device].stick[stick].y;
}
float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) {
return STATE[device].stick[stick].length;
}
void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float *outX, float *outY) {
*outX = STATE[device].stick[stick].nx;
*outY = STATE[device].stick[stick].ny;
}
float GamepadStickAngle(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) {
return STATE[device].stick[stick].angle;
}
GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick) {
return STATE[device].stick[stick].dirCurrent;
}
GAMEPAD_BOOL GamepadStickDirTriggered(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, GAMEPAD_STICKDIR dir) {
return (STATE[device].stick[stick].dirCurrent == dir &&
STATE[device].stick[stick].dirCurrent != STATE[device].stick[stick].dirLast) ? GAMEPAD_TRUE : GAMEPAD_FALSE;
}
/* initialize common gamepad state */
static void GamepadResetState(GAMEPAD_DEVICE gamepad) {
memset(STATE[gamepad].stick, 0, sizeof(STATE[gamepad].stick));
memset(STATE[gamepad].trigger, 0, sizeof(STATE[gamepad].trigger));
STATE[gamepad].bLast = STATE[gamepad].bCurrent = 0;
}
/* Update individual sticks */
static void GamepadUpdateCommon(void) {
int i;
for (i = 0; i != GAMEPAD_COUNT; ++i) {
/* store previous button state */
STATE[i].bLast = STATE[i].bCurrent;
/* per-platform update routines */
GamepadUpdateDevice((GAMEPAD_DEVICE)i);
/* calculate refined stick and trigger values */
if ((STATE[i].flags & FLAG_CONNECTED) != 0) {
GamepadUpdateStick(&STATE[i].stick[STICK_LEFT], GAMEPAD_DEADZONE_LEFT_STICK);
GamepadUpdateStick(&STATE[i].stick[STICK_RIGHT], GAMEPAD_DEADZONE_RIGHT_STICK);
GamepadUpdateTrigger(&STATE[i].trigger[TRIGGER_LEFT]);
GamepadUpdateTrigger(&STATE[i].trigger[TRIGGER_RIGHT]);
}
}
}
/* Update stick info */
static void GamepadUpdateStick(GAMEPAD_AXIS* axis, float deadzone) {
// determine magnitude of stick
axis->length = sqrtf((float)(axis->x*axis->x) + (float)(axis->y*axis->y));
if (axis->length > deadzone) {
// clamp length to maximum value
if (axis->length > 32767.0f) {
axis->length = 32767.0f;
}
// normalized X and Y values
axis->nx = axis->x / axis->length;
axis->ny = axis->y / axis->length;
//fix special case
if (axis->nx < -1.0) axis->nx = -1.0;
if (axis->ny < -1.0) axis->ny = -1.0;
// adjust length for deadzone and find normalized length
axis->length -= deadzone;
axis->length /= (32767.0f - deadzone);
// find angle of stick in radians
axis->angle = atan2f((float)axis->y, (float)axis->x);
} else {
axis->x = axis->y = 0;
axis->nx = axis->ny = 0.0f;
axis->length = axis->angle = 0.0f;
}
/* update the stick direction */
axis->dirLast = axis->dirCurrent;
axis->dirCurrent = STICKDIR_CENTER;
/* check direction to see if it's non-centered */
if (axis->length != 0.f) {
if (axis->angle >= PI_1_4 && axis->angle < PI_3_4) {
axis->dirCurrent = STICKDIR_UP;
} else if (axis->angle >= -PI_3_4 && axis->angle < -PI_1_4) {
axis->dirCurrent = STICKDIR_DOWN;
} else if (axis->angle >= PI_3_4 || axis->angle < -PI_3_4) {
axis->dirCurrent = STICKDIR_LEFT;
} else /* if (axis->angle < PI_1_4 && axis->angle >= -PI_1_4) */ {
axis->dirCurrent = STICKDIR_RIGHT;
}
}
}
/* Update trigger info */
static void GamepadUpdateTrigger(GAMEPAD_TRIGINFO* trig) {
trig->pressedLast = trig->pressedCurrent;
if (trig->value > GAMEPAD_DEADZONE_TRIGGER) {
trig->length = ((trig->value - GAMEPAD_DEADZONE_TRIGGER) / (255.0f - GAMEPAD_DEADZONE_TRIGGER));
trig->pressedCurrent = GAMEPAD_TRUE;
} else {
trig->value = 0;
trig->length = 0.0f;
trig->pressedCurrent = GAMEPAD_FALSE;
}
}

325
controller/gamepad.h Normal file
View File

@ -0,0 +1,325 @@
/**
* Gamepad Input Library
* Sean Middleditch <sean@middleditch.us>
* Copyright (C) 2010,2011 Sean Middleditch
* LICENSE: MIT/X
*/
#if !defined(GAMEPAD_H)
#define GAMEPAD_H 1
#if defined(__cplusplus)
extern "C" {
#endif
#define GAMEPAD_STATIC_LIB
#if defined(GAMEPAD_STATIC_LIB)
# define GAMEPAD_API
#else
# if defined(_WIN32)
# if defined(GAMEPAD_EXPORT)
# define GAMEPAD_API __declspec(dllexport)
# else
# define GAMEPAD_API __declspec(dllimport)
# endif
# elif defined(__GNUC__) && defined(GAMEPAD_EXPORT)
# define GAMEPAD_API __attribute__((visibility("default")))
# else
# define GAMEPAD_API extern
# endif
#endif
/**
* Enumeration of the possible devices.
*
* Only four devices are supported as this is the limit of Windows.
*/
enum GAMEPAD_DEVICE {
GAMEPAD_0 = 0, /**< First gamepad */
GAMEPAD_1 = 1, /**< Second gamepad */
GAMEPAD_2 = 2, /**< Third gamepad */
GAMEPAD_3 = 3, /**< Fourth gamepad */
GAMEPAD_COUNT /**< Maximum number of supported gamepads */
};
/**
* Enumeration of the possible buttons.
*/
enum GAMEPAD_BUTTON {
BUTTON_DPAD_UP = 0, /**< UP on the direction pad */
BUTTON_DPAD_DOWN = 1, /**< DOWN on the direction pad */
BUTTON_DPAD_LEFT = 2, /**< LEFT on the direction pad */
BUTTON_DPAD_RIGHT = 3, /**< RIGHT on the direction pad */
BUTTON_START = 4, /**< START button */
BUTTON_BACK = 5, /**< BACK button */
BUTTON_LEFT_THUMB = 6, /**< Left analog stick button */
BUTTON_RIGHT_THUMB = 7, /**< Right analog stick button */
BUTTON_LEFT_SHOULDER = 8, /**< Left bumper button */
BUTTON_RIGHT_SHOULDER = 9, /**< Right bumper button */
BUTTON_A = 12, /**< A button */
BUTTON_B = 13, /**< B button */
BUTTON_X = 14, /**< X button */
BUTTON_Y = 15, /**< Y button */
BUTTON_COUNT /**< Maximum number of supported buttons */
};
/**
* Enumeration of the possible pressure/trigger buttons.
*/
enum GAMEPAD_TRIGGER {
TRIGGER_LEFT = 0, /**< Left trigger */
TRIGGER_RIGHT = 1, /**< Right trigger */
TRIGGER_COUNT /**< Number of triggers */
};
/**
* Enumeration of the analog sticks.
*/
enum GAMEPAD_STICK {
STICK_LEFT = 0, /**< Left stick */
STICK_RIGHT = 1, /**< Right stick */
STICK_COUNT /**< Number of analog sticks */
};
/**
* Enumeration of main stick directions.
*
* This is used for some of the convenience routines in the library.
*/
enum GAMEPAD_STICKDIR {
STICKDIR_CENTER = 0, /**< CENTER, no direction */
STICKDIR_UP = 1, /**< UP direction */
STICKDIR_DOWN = 2, /**< DOWN direction */
STICKDIR_LEFT = 3, /**< LEFT direction */
STICKDIR_RIGHT = 4, /**< RIGHT direction */
STICKDIR_COUNT
};
/**
* Enumeration for true/false values
*/
enum GAMEPAD_BOOL {
GAMEPAD_FALSE = 0, /**< FALSE value for boolean parameters */
GAMEPAD_TRUE = 1 /**< TRUE value for boolean parameters */
};
typedef enum GAMEPAD_DEVICE GAMEPAD_DEVICE;
typedef enum GAMEPAD_BUTTON GAMEPAD_BUTTON;
typedef enum GAMEPAD_TRIGGER GAMEPAD_TRIGGER;
typedef enum GAMEPAD_STICK GAMEPAD_STICK;
typedef enum GAMEPAD_STICKDIR GAMEPAD_STICKDIR;
typedef enum GAMEPAD_BOOL GAMEPAD_BOOL;
#define GAMEPAD_DEADZONE_LEFT_STICK 7849 /**< Suggested deadzone magnitude for left analog stick */
#define GAMEPAD_DEADZONE_RIGHT_STICK 8689 /**< Suggested deadzone magnitude for right analog stick */
#define GAMEPAD_DEADZONE_TRIGGER 30 /**< Suggested deadzone for triggers */
/**
* Initialize the library.
*
* This is critical on non-Windows platforms.
*/
GAMEPAD_API void GamepadInit(void);
/**
* Shutdown the library.
*
* This will release resources allocated by the library internally.
*
* This should be called after forking as well.
*/
GAMEPAD_API void GamepadShutdown(void);
/**
* Updates the state of the gamepads.
*
* This must be called (at least) once per game loop.
*/
GAMEPAD_API void GamepadUpdate(void);
/**
* Test if a particular gamepad is connected.
*
* \param device The device to check.
* \returns GAMEPAD_TRUE if the device is connected, GAMEPAD_FALSE if it is not.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadIsConnected(GAMEPAD_DEVICE device);
/**
* Test if a particular button is being pressed.
*
* \param device The device to check.
* \param button The button to check.
* \returns GAMEPAD_TRUE if the button is down, GAMEPAD_FALSE if it is not.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadButtonDown(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button);
/**
* Test if a particular button has been depressed since the previous call to GamepadUpdate.
*
* \param device The device to check.
* \param button The button to check.
* \returns GAMEPAD_TRUE if the button has been pressed, GAMEPAD_FALSE if it is not or if it was depressed the previous frame.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadButtonTriggered(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button);
/**
* Test if a particular button has been released since the previous call to GamepadUpdate.
*
* \param device The device to check.
* \param button The button to check.
* \returns GAMEPAD_TRUE if the button has been released, GAMEPAD_FALSE if it is down or if it was not down the previous frame.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadButtonReleased(GAMEPAD_DEVICE device, GAMEPAD_BUTTON button);
/**
* Get the trigger value (depression magnitude) in its raw form.
*
* \param device The device to check.
* \param trigger The trigger to check.
* \returns Trigger depression magnitude (0 to 32767).
*/
GAMEPAD_API int GamepadTriggerValue(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger);
/**
* Get the trigger value (depression magnitude) in normalized form.
*
* \param device The device to check.
* \param trigger The trigger to check.
* \returns Trigger depression magnitude (0 to 1).
*/
GAMEPAD_API float GamepadTriggerLength(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger);
/**
* Test if a trigger is depressed
*
* \param device The device to check.
* \param trigger The trigger to check.
* \returns GAMEPAD_TRUE if down, GAMEPAD_FALSE otherwise.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadTriggerDown(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger);
/**
* Test if a trigger is depressed
*
* \param device The device to check.
* \param trigger The trigger to check.
* \returns GAMEPAD_TRUE if triggered, GAMEPAD_FALSE otherwise.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadTriggerTriggered(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger);
/**
* Test if a trigger is depressed
*
* \param device The device to check.
* \param trigger The trigger to check.
* \returns GAMEPAD_TRUE if released, GAMEPAD_FALSE otherwise.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadTriggerReleased(GAMEPAD_DEVICE device, GAMEPAD_TRIGGER trigger);
/**
* Set the rumble motors on/off.
*
* To turn off the rumble effect, set values to 0 for both motors.
*
* The left motor is the low-frequency/strong motor, and the right motor is the high-frequency/weak motor.
*
* \param device The device to update.
* \param left Left motor strengh (0 to 1).
* \param right Right motor strengh (0 to 1).
* \param rumble_length_ms rumble time in ms (0 = unlimited).
*/
GAMEPAD_API void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right, unsigned int rumble_length_ms);
/**
* Query the position of an analog stick as raw values.
*
* The values retrieved by this function represent the magnitude of the analog
* stick in each direction. Note that it shouldn't be possible to get full
* magnitude in one direction unless the other direction has a magnitude of
* zero, as the stick has a circular movement range.
*
* \param device The device to check.
* \param stick The stick to check.
* \param outX Pointer to integer to store the X magnitude in (-32767 to 32767).
* \param outX Pointer to integer to store the Y magnitude in (-32767 to 32767).
*/
GAMEPAD_API void GamepadStickXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, int* outX, int* outY);
/**
* Query the position of an analog stick as normalized values.
*
* The values retrieved by this function represent the magnitude of the analog
* stick in each direction. Note that it shouldn't be possible to get full
* magnitude in one direction unless the other direction has a magnitude of
* zero, as the stick has a circular movement range.
*
* \param device The device to check.
* \param stick The stick to check.
* \param outX Pointer to float to store the X magnitude in (-1 to 1).
* \param outX Pointer to float to store the Y magnitude in (-1 to 1).
*/
GAMEPAD_API void GamepadStickNormXY(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, float* outX, float* outY);
/**
* Query the magnitude of an analog stick.
*
* This returns the normalized value of the magnitude of the stick. That is,
* if the stick is pushed all the way in any direction, it returns 1.0.
*
* \param device The device to check.
* \param stick The stick to check.
* \returns The magnitude of the stick (0 to 1).
*/
GAMEPAD_API float GamepadStickLength(GAMEPAD_DEVICE device, GAMEPAD_STICK stick);
/**
* Query the direction of a stick (in radians).
*
* This returns the direction of the stick. This value is in radians, not
* degrees. Zero is to the right, and the angle increases in a
* counter-clockwise direction.
*
* \param device The device to check.
* \param stick The stick to check.
* \returns The angle of the stick (0 to 2*PI).
*/
GAMEPAD_API float GamepadStickAngle(GAMEPAD_DEVICE device, GAMEPAD_STICK stick);
/**
* Get the direction the stick is pushed in (if any).
*
* This is a useful utility function for when the stick should be treated as a simple
* directional pad, such as for menu UIs.
*
* \param device The device to check.
* \param stick The trigger to check.
* \returns The stick's current direction.
*/
GAMEPAD_API GAMEPAD_STICKDIR GamepadStickDir(GAMEPAD_DEVICE device, GAMEPAD_STICK stick);
/**
* Test whether a stick has been pressed in a particular direction since the last update.
*
* This only returns true if the stick was centered last frame.
*
* This is a useful utility function for when the stick should be treated as a simple
* directional pad, such as for menu UIs.
*
* \param device The device to check.
* \param stick The trigger to check.
* \param stickdir The direction to check for.
* \returns GAMEPAD_TRUE if the stick is pressed in the specified direction, GAMEPAD_FALSE otherwise.
*/
GAMEPAD_API GAMEPAD_BOOL GamepadStickDirTriggered(GAMEPAD_DEVICE device, GAMEPAD_STICK stick, GAMEPAD_STICKDIR dir);
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

View File

@ -7,34 +7,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#include <stddef.h>
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#include <stddef.h>
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]

View File

@ -7,33 +7,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#if (_MSC_VER < 1299)
#pragma warning(disable: 4710)
#endif
//#define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define NOTHROW
//////////////////////////////////////////////////////////////////////////////
@ -880,7 +862,8 @@ struct _DETOUR_TRAMPOLINE
{
// An ARM64 instruction is 4 bytes long.
//
// The overwrite is always 2 instructions plus a literal, so 16 bytes, 4 instructions.
// The overwrite is always composed of 3 instructions (12 bytes) which perform an indirect jump
// using _DETOUR_TRAMPOLINE::pbDetour as the address holding the target location.
//
// Copied instructions can expand.
//
@ -915,7 +898,7 @@ struct _DETOUR_TRAMPOLINE
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 184);
enum {
SIZE_OF_JMP = 16
SIZE_OF_JMP = 12
};
inline ULONG fetch_opcode(PBYTE pbCode)
@ -929,6 +912,79 @@ inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
pbCode += 4;
}
struct ARM64_INDIRECT_JMP {
struct {
ULONG Rd : 5;
ULONG immhi : 19;
ULONG iop : 5;
ULONG immlo : 2;
ULONG op : 1;
} ardp;
struct {
ULONG Rt : 5;
ULONG Rn : 5;
ULONG imm : 12;
ULONG opc : 2;
ULONG iop1 : 2;
ULONG V : 1;
ULONG iop2 : 3;
ULONG size : 2;
} ldr;
ULONG br;
};
#pragma warning(push)
#pragma warning(disable:4201)
union ARM64_INDIRECT_IMM {
struct {
ULONG64 pad : 12;
ULONG64 adrp_immlo : 2;
ULONG64 adrp_immhi : 19;
};
LONG64 value;
};
#pragma warning(pop)
PBYTE detour_gen_jmp_indirect(BYTE *pbCode, ULONG64 *pbJmpVal)
{
// adrp x17, [jmpval]
// ldr x17, [x17, jmpval]
// br x17
struct ARM64_INDIRECT_JMP *pIndJmp;
union ARM64_INDIRECT_IMM jmpIndAddr;
jmpIndAddr.value = (((LONG64)pbJmpVal) & 0xFFFFFFFFFFFFF000) -
(((LONG64)pbCode) & 0xFFFFFFFFFFFFF000);
pIndJmp = (struct ARM64_INDIRECT_JMP *)pbCode;
pbCode = (BYTE *)(pIndJmp + 1);
pIndJmp->ardp.Rd = 17;
pIndJmp->ardp.immhi = jmpIndAddr.adrp_immhi;
pIndJmp->ardp.iop = 0x10;
pIndJmp->ardp.immlo = jmpIndAddr.adrp_immlo;
pIndJmp->ardp.op = 1;
pIndJmp->ldr.Rt = 17;
pIndJmp->ldr.Rn = 17;
pIndJmp->ldr.imm = (((ULONG64)pbJmpVal) & 0xFFF) / 8;
pIndJmp->ldr.opc = 1;
pIndJmp->ldr.iop1 = 1;
pIndJmp->ldr.V = 0;
pIndJmp->ldr.iop2 = 7;
pIndJmp->ldr.size = 3;
pIndJmp->br = 0xD61F0220;
return pbCode;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
@ -995,7 +1051,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the
calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or
a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address
within ±4GB of the current PC.
within +/- 4GB of the current PC.
PC-rel. addressing
This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are
@ -1062,7 +1118,10 @@ inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
// The encoding used by detour_gen_jmp_indirect actually enables a
// displacement of +/- 4GiB. In the future, this could be changed to
// reflect that. For now, just reuse the x86 logic which is plenty.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi));
@ -1250,6 +1309,65 @@ static PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi)
return NULL;
}
static PVOID detour_alloc_trampoline_allocate_new(PBYTE pbTarget,
PDETOUR_TRAMPOLINE pLo,
PDETOUR_TRAMPOLINE pHi)
{
PVOID pbTry = NULL;
// NB: We must always also start the search at an offset from pbTarget
// in order to maintain ASLR entropy.
#if defined(DETOURS_64BIT)
// Try looking 1GB below or lower.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);
}
// Try looking 1GB above or higher.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);
}
// Try looking 1GB below or higher.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);
}
// Try looking 1GB above or lower.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);
}
#endif
// Try anything below.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);
}
// try anything above.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);
}
return pbTry;
}
PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget,
_Out_ PDWORD pcbAllocatedSize)
{
PDETOUR_TRAMPOLINE pLo;
PDETOUR_TRAMPOLINE pHi;
detour_find_jmp_bounds((PBYTE)pbTarget, &pLo, &pHi);
PVOID pbNewlyAllocated =
detour_alloc_trampoline_allocate_new((PBYTE)pbTarget, pLo, pHi);
if (pbNewlyAllocated == NULL) {
DETOUR_TRACE(("Couldn't find available memory region!\n"));
*pcbAllocatedSize = 0;
return NULL;
}
*pcbAllocatedSize = DETOUR_REGION_SIZE;
return pbNewlyAllocated;
}
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
{
// We have to place trampolines within +/- 2GB of target.
@ -1294,41 +1412,10 @@ static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
// Round pbTarget down to 64KB block.
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
PVOID pbTry = NULL;
// NB: We must always also start the search at an offset from pbTarget
// in order to maintain ASLR entropy.
#if defined(DETOURS_64BIT)
// Try looking 1GB below or lower.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);
}
// Try looking 1GB above or higher.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);
}
// Try looking 1GB below or higher.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);
}
// Try looking 1GB above or lower.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);
}
#endif
// Try anything below.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);
}
// try anything above.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);
}
if (pbTry != NULL) {
s_pRegion = (DETOUR_REGION*)pbTry;
PVOID pbNewlyAllocated =
detour_alloc_trampoline_allocate_new(pbTarget, pLo, pHi);
if (pbNewlyAllocated != NULL) {
s_pRegion = (DETOUR_REGION*)pbNewlyAllocated;
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
s_pRegion->pFree = NULL;
s_pRegion->pNext = s_pRegions;
@ -1655,7 +1742,7 @@ LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
PBYTE pbCode = detour_gen_jmp_indirect(o->pbTarget, (ULONG64*)&(o->pTrampoline->pbDetour));
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);

View File

@ -16,6 +16,34 @@
//////////////////////////////////////////////////////////////////////////////
//
#ifdef DETOURS_INTERNAL
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#include <windows.h>
#if (_MSC_VER < 1310)
#else
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
#endif
#endif // DETOURS_INTERNAL
//////////////////////////////////////////////////////////////////////////////
//
#undef DETOURS_X64
#undef DETOURS_X86
#undef DETOURS_IA64
@ -61,7 +89,12 @@
//#define DETOURS_OPTION_BITS 32
#endif
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
/////////////////////////////////////////////////////////////// Helper Macros.
//
#define DETOURS_STRINGIFY_(x) #x
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS)
//////////////////////////////////////////////////////////////////////////////
//
@ -387,7 +420,6 @@ typedef struct _DETOUR_EXE_RESTORE
#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this
BYTE raw[sizeof(IMAGE_NT_HEADERS64) +
sizeof(IMAGE_SECTION_HEADER) * 32];
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108);
#else
BYTE raw[0x108 + sizeof(IMAGE_SECTION_HEADER) * 32];
#endif
@ -396,6 +428,10 @@ typedef struct _DETOUR_EXE_RESTORE
} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE;
#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108);
#endif
// The size can change, but assert for clarity due to the muddying #ifdefs.
#ifdef _WIN64
C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x688);
@ -431,11 +467,6 @@ typedef struct _DETOUR_EXE_HELPER
0,\
}
/////////////////////////////////////////////////////////////// Helper Macros.
//
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define DETOURS_STRINGIFY_(x) #x
///////////////////////////////////////////////////////////// Binary Typedefs.
//
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)(
@ -523,6 +554,8 @@ PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,
_Out_opt_ LONG *plExtra);
BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule,
_In_ BOOL fLimitReferencesToModule);
PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget,
_Out_ PDWORD pcbAllocatedSize);
///////////////////////////////////////////////////// Loaded Binary Functions.
//

View File

@ -14,8 +14,8 @@
#include <detours.h>
#else
#ifndef DETOURS_STRINGIFY
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define DETOURS_STRINGIFY_(x) #x
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#endif
#define VER_FILEFLAGSMASK 0x3fL
@ -24,4 +24,4 @@
#define VER_FILETYPE 0x00000002L
#define VER_FILESUBTYPE 0x00000000L
#endif
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS)

View File

@ -7,28 +7,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#include <limits.h>
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#include <limits.h>
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#undef ASSERT
#define ASSERT(x)
@ -260,6 +247,11 @@ class CDetourDis
#define ENTRY_CopyFF ENTRY_DataIgnored &CDetourDis::CopyFF
#define ENTRY_CopyVex2 ENTRY_DataIgnored &CDetourDis::CopyVex2
#define ENTRY_CopyVex3 ENTRY_DataIgnored &CDetourDis::CopyVex3
#define ENTRY_CopyEvex ENTRY_DataIgnored &CDetourDis::CopyEvex // 62, 3 byte payload, then normal with implied prefixes like vex
#define ENTRY_CopyXop ENTRY_DataIgnored &CDetourDis::CopyXop // 0x8F ... POP /0 or AMD XOP
#define ENTRY_CopyBytesXop 5, 5, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm
#define ENTRY_CopyBytesXop1 6, 6, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm8
#define ENTRY_CopyBytesXop4 9, 9, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm32
#define ENTRY_Invalid ENTRY_DataIgnored &CDetourDis::Invalid
#define ENTRY_End ENTRY_DataIgnored NULL
@ -289,6 +281,9 @@ class CDetourDis
PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p);
PBYTE CopyEvex(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyXop(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
protected:
static const COPYENTRY s_rceCopyTable[257];
@ -303,6 +298,7 @@ class CDetourDis
BOOL m_bAddressOverride;
BOOL m_bRaxOverride; // AMD64 only
BOOL m_bVex;
BOOL m_bEvex;
BOOL m_bF2;
BOOL m_bF3; // x86 only
BYTE m_nSegmentOverride;
@ -337,6 +333,7 @@ CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra)
m_bF2 = FALSE;
m_bF3 = FALSE;
m_bVex = FALSE;
m_bEvex = FALSE;
m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget;
m_plExtra = plExtra ? plExtra : &m_lScratchExtra;
@ -368,8 +365,11 @@ PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
UINT nBytesFixed;
ASSERT(!m_bVex || pEntry->nFlagBits == 0);
ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16);
if (m_bVex || m_bEvex)
{
ASSERT(pEntry->nFlagBits == 0);
ASSERT(pEntry->nFixedSize == pEntry->nFixedSize16);
}
UINT const nModOffset = pEntry->nModOffset;
UINT const nFlagBits = pEntry->nFlagBits;
@ -748,33 +748,42 @@ PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
return pbOut;
}
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
PBYTE CDetourDis::CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p)
// m is first instead of last in the hopes of pbDst/pbSrc being
// passed along efficiently in the registers they were already in.
{
static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod };
static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 };
static const COPYENTRY Invalid = { 0xC4, ENTRY_Invalid };
static const COPYENTRY ceInvalid = { 0xC4, ENTRY_Invalid };
m_bVex = TRUE;
REFCOPYENTRY pEntry;
switch (m) {
default: pEntry = &Invalid; break;
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]]; break;
case 2: pEntry = &ceF38; break;
case 3: pEntry = &ceF3A; break;
}
switch (pbSrc[-1] & 3) { // p in last byte
switch (p & 3) {
case 0: break;
case 1: m_bOperandOverride = TRUE; break;
case 2: m_bF3 = TRUE; break;
case 3: m_bF2 = TRUE; break;
}
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
REFCOPYENTRY pEntry;
switch (m) {
default: return Invalid(&ceInvalid, pbDst, pbSrc);
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]];
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
case 2: return CopyBytes(&ceF38, pbDst, pbSrc);
case 3: return CopyBytes(&ceF3A, pbDst, pbSrc);
}
}
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
// m is first instead of last in the hopes of pbDst/pbSrc being
// passed along efficiently in the registers they were already in.
{
m_bVex = TRUE;
BYTE const p = (BYTE)(pbSrc[-1] & 3); // p in last byte
return CopyVexEvexCommon(m, pbDst, pbSrc, p);
}
PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
// 3 byte VEX prefix 0xC4
{
@ -835,6 +844,78 @@ PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
return CopyVexCommon(1, pbDst + 2, pbSrc + 2);
}
PBYTE CDetourDis::CopyEvex(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
// 62, 3 byte payload, x86 with implied prefixes like Vex
// for 32bit, mode 0xC0 else fallback to bound /r
{
// NOTE: Intel and Wikipedia number these differently.
// Intel says 0-2, Wikipedia says 1-3.
BYTE const p0 = pbSrc[1];
#ifdef DETOURS_X86
const static COPYENTRY ceBound = { 0x62, ENTRY_CopyBytes2Mod };
if ((p0 & 0xC0) != 0xC0) {
return CopyBytes(&ceBound, pbDst, pbSrc);
}
#endif
static const COPYENTRY ceInvalid = { 0x62, ENTRY_Invalid };
if ((p0 & 0x0C) != 0)
return Invalid(&ceInvalid, pbDst, pbSrc);
BYTE const p1 = pbSrc[2];
if ((p1 & 0x04) != 0x04)
return Invalid(&ceInvalid, pbDst, pbSrc);
// Copy 4 byte prefix.
*(UNALIGNED ULONG *)pbDst = *(UNALIGNED ULONG*)pbSrc;
m_bEvex = TRUE;
#ifdef DETOURS_X64
m_bRaxOverride |= !!(p1 & 0x80); // w
#endif
return CopyVexEvexCommon(p0 & 3u, pbDst + 4, pbSrc + 4, p1 & 3u);
}
PBYTE CDetourDis::CopyXop(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
/* 3 byte AMD XOP prefix 0x8F
byte0: 0x8F
byte1: RXBmmmmm
byte2: WvvvvLpp
byte3: opcode
mmmmm >= 8, else pop
mmmmm only otherwise defined for 8, 9, A.
pp is like VEX but only instructions with 0 are defined
*/
{
const static COPYENTRY cePop = { 0x8F, ENTRY_CopyBytes2Mod };
const static COPYENTRY ceXop = { 0x8F, ENTRY_CopyBytesXop };
const static COPYENTRY ceXop1 = { 0x8F, ENTRY_CopyBytesXop1 };
const static COPYENTRY ceXop4 = { 0x8F, ENTRY_CopyBytesXop4 };
BYTE const m = (BYTE)(pbSrc[1] & 0x1F);
ASSERT(m <= 10);
switch (m)
{
default:
return CopyBytes(&cePop, pbDst, pbSrc);
case 8: // modrm with 8bit immediate
return CopyBytes(&ceXop1, pbDst, pbSrc);
case 9: // modrm with no immediate
return CopyBytes(&ceXop, pbDst, pbSrc);
case 10: // modrm with 32bit immediate
return CopyBytes(&ceXop4, pbDst, pbSrc);
}
}
//////////////////////////////////////////////////////////////////////////////
//
PBYTE CDetourDis::s_pbModuleBeg = NULL;
@ -1030,11 +1111,11 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
#ifdef DETOURS_X64
{ 0x60, ENTRY_Invalid }, // Invalid
{ 0x61, ENTRY_Invalid }, // Invalid
{ 0x62, ENTRY_Invalid }, // Invalid (not yet implemented Intel EVEX support)
{ 0x62, ENTRY_CopyEvex }, // EVEX / AVX512
#else
{ 0x60, ENTRY_CopyBytes1 }, // PUSHAD
{ 0x61, ENTRY_CopyBytes1 }, // POPAD
{ 0x62, ENTRY_CopyBytes2Mod }, // BOUND /r
{ 0x62, ENTRY_CopyEvex }, // BOUND /r and EVEX / AVX512
#endif
{ 0x63, ENTRY_CopyBytes2Mod }, // 32bit ARPL /r, 64bit MOVSXD
{ 0x64, ENTRY_CopyBytesSegment }, // FS prefix
@ -1084,7 +1165,7 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
{ 0x8C, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8D, ENTRY_CopyBytes2Mod }, // LEA /r
{ 0x8E, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8F, ENTRY_CopyBytes2Mod }, // POP /0
{ 0x8F, ENTRY_CopyXop }, // POP /0 or AMD XOP
{ 0x90, ENTRY_CopyBytes1 }, // NOP
{ 0x91, ENTRY_CopyBytes1 }, // XCHG
{ 0x92, ENTRY_CopyBytes1 }, // XCHG

View File

@ -9,39 +9,18 @@
// Used for for payloads, byways, and imports.
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#if _MSC_VER >= 1310
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
#endif
#if (_MSC_VER < 1299)
#if _MSC_VER < 1299
#pragma warning(disable: 4710)
#endif
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
namespace Detour
{
//////////////////////////////////////////////////////////////////////////////
@ -1714,17 +1693,13 @@ BOOL CImage::Write(HANDLE hFile)
m_nNextFileAddr = Max(m_SectionHeaders[n].PointerToRawData +
m_SectionHeaders[n].SizeOfRawData,
m_nNextFileAddr);
#if 0
m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +
m_SectionHeaders[n].Misc.VirtualSize,
m_nNextVirtAddr);
#else
// Old images have VirtualSize == 0 as a matter of course, e.g. NT 3.1.
// In which case, use SizeOfRawData instead.
m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +
(m_SectionHeaders[n].Misc.VirtualSize
? m_SectionHeaders[n].Misc.VirtualSize
: SectionAlign(m_SectionHeaders[n].SizeOfRawData)),
m_nNextVirtAddr);
#endif
m_nExtraOffset = Max(m_nNextFileAddr, m_nExtraOffset);
@ -1857,7 +1832,7 @@ BOOL CImage::Write(HANDLE hFile)
for (CImageImportFile *pImportFile = m_pImportFiles;
pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {
ZeroMemory(piidDst, sizeof(piidDst));
ZeroMemory(piidDst, sizeof(*piidDst));
nameTable.Allocate(pImportFile->m_pszName, (DWORD *)&piidDst->Name);
piidDst->TimeDateStamp = 0;
piidDst->ForwarderChain = pImportFile->m_nForwarderChain;
@ -1899,7 +1874,7 @@ BOOL CImage::Write(HANDLE hFile)
}
piidDst++;
}
ZeroMemory(piidDst, sizeof(piidDst));
ZeroMemory(piidDst, sizeof(*piidDst));
//////////////////////////////////////////////////////////////////////////
//

View File

@ -9,27 +9,6 @@
// Module enumeration functions.
//
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include <windows.h>
#if (_MSC_VER < 1310)
#else
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
#endif
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
@ -38,10 +17,6 @@
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
@ -164,8 +139,8 @@ PDETOUR_SYM_INFO DetourLoadImageHlp(VOID)
return pSymInfo;
}
PVOID WINAPI DetourFindFunction(_In_ PCSTR pszModule,
_In_ PCSTR pszFunction)
PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule,
_In_ LPCSTR pszFunction)
{
/////////////////////////////////////////////// First, try GetProcAddress.
//

View File

@ -18,49 +18,45 @@
#include "base.h"
#include <random>
// Random device generator
static std::random_device rd;
static std::mt19937_64 gen(rd());
template<typename T>
static void randombytes(T& _buf)
static void randombytes(uint8_t* buf, size_t len)
{
// uniform integer distribution
std::uniform_int_distribution<int64_t> dis;
static std::random_device rd;
static std::mt19937_64 gen(rd());
std::uniform_int_distribution<uint64_t> dis;
uint8_t* buf = reinterpret_cast<uint8_t*>(&_buf);
while (len >= sizeof(uint64_t))
{
*reinterpret_cast<uint64_t*>(buf) = dis(gen);
len -= sizeof(uint64_t);
buf += sizeof(uint64_t);
}
while (len != 0)
{
switch (len)
{
case 1:
*buf = dis(gen);
--len;
++buf;
break;
// Make sure we can hold our buffer size as int64_t
constexpr size_t rand_buf_len = sizeof(T) / sizeof(int64_t) + (sizeof(T)%sizeof(int64_t) ? 1 : 0);
int64_t rand_buf[rand_buf_len];
// Generate some (pseudo) random numbers
for (int i = 0; i < rand_buf_len; ++i)
rand_buf[i] = dis(gen);
// Copy the random bytes to buffer
memcpy(buf, rand_buf, sizeof(T));
case 2: case 3:
*reinterpret_cast<uint16_t*>(buf) = dis(gen);
len -= 2;
buf += 2;
break;
case 4: case 5: case 6: case 7:
*reinterpret_cast<uint32_t*>(buf) = dis(gen);
len -= 4;
buf += 4;
break;
}
}
}
static void randombytes(char* buf, size_t len)
{
// uniform integer distribution
std::uniform_int_distribution<int64_t> dis;
// Make sure we can hold our buffer size as int64_t
size_t rand_buf_len = len / sizeof(int64_t) + (len % sizeof(int64_t) ? 1 : 0);
int64_t* rand_buf = new int64_t[rand_buf_len];
// Generate some (pseudo) random numbers
for (int i = 0; i < rand_buf_len; ++i)
rand_buf[i] = dis(gen);
// Copy the random bytes to buffer
memcpy(buf, rand_buf, len);
// Don't forget to free it
delete[]rand_buf;
}
#ifdef STEAM_WIN32
#ifdef __WINDOWS__
#include <windows.h>
#include <direct.h>
@ -147,49 +143,59 @@ static unsigned generate_account_id()
CSteamID generate_steam_id_user()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeIndividual);
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeIndividual);
}
static CSteamID generate_steam_anon_user()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
}
CSteamID generate_steam_id_server()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeGameServer);
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeGameServer);
}
CSteamID generate_steam_id_anonserver()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
}
CSteamID generate_steam_id_lobby()
{
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance | k_EChatInstanceFlagLobby, k_EUniversePublic, k_EAccountTypeChat);
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance | k_EChatInstanceFlagLobby, k_EUniversePublic, k_EAccountTypeChat);
}
#ifndef STEAM_WIN32
#include <sys/types.h>
#include <dirent.h>
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
{
if (timeout == 0.0) return true;
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
if (std::chrono::duration_cast<std::chrono::duration<double>>(now - old).count() > timeout) {
return true;
}
return false;
}
#ifdef __LINUX__
std::string get_lib_path() {
std::string dir = "/proc/self/map_files";
DIR *dp;
int i = 0;
struct dirent *ep;
dp = opendir (dir.c_str());
unsigned long long int p = (unsigned long long int)&get_lib_path;
uintptr_t p = (uintptr_t)&get_lib_path;
if (dp != NULL)
{
while ((ep = readdir (dp))) {
if (memcmp(ep->d_name, ".", 2) != 0 && memcmp(ep->d_name, "..", 3) != 0) {
char *upper = NULL;
unsigned long long int lower_bound = strtoull(ep->d_name, &upper, 16);
uintptr_t lower_bound = strtoull(ep->d_name, &upper, 16);
if (lower_bound) {
++upper;
unsigned long long int upper_bound = strtoull(upper, &upper, 16);
uintptr_t upper_bound = strtoull(upper, &upper, 16);
if (upper_bound && (lower_bound < p && p < upper_bound)) {
std::string path = dir + PATH_SEPARATOR + ep->d_name;
char link[PATH_MAX] = {};
@ -212,20 +218,35 @@ std::string get_lib_path() {
}
#endif
std::string get_full_program_path()
std::string get_full_lib_path()
{
std::string program_path;
#if defined(STEAM_WIN32)
#if defined(__WINDOWS__)
char DllPath[MAX_PATH] = {0};
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
program_path = DllPath;
#else
program_path = get_lib_path();
#endif
program_path = program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
return program_path;
}
std::string get_full_program_path()
{
std::string env_program_path = get_env_variable("SteamAppPath");
if (env_program_path.length()) {
if (env_program_path.back() != PATH_SEPARATOR[0]) {
env_program_path = env_program_path.append(PATH_SEPARATOR);
}
return env_program_path;
}
std::string program_path;
program_path = get_full_lib_path();
return program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
}
std::string get_current_path()
{
std::string path;
@ -277,13 +298,15 @@ Auth_Ticket_Manager::Auth_Ticket_Manager(class Settings *settings, class Network
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &steam_auth_ticket_callback, this);
}
void Auth_Ticket_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp)
#define STEAM_TICKET_PROCESS_TIME 0.03
void Auth_Ticket_Manager::launch_callback(CSteamID id, EAuthSessionResponse resp, double delay)
{
ValidateAuthTicketResponse_t data;
data.m_SteamID = id;
data.m_eAuthSessionResponse = resp;
data.m_OwnerSteamID = id;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), delay);
}
void Auth_Ticket_Manager::launch_callback_gs(CSteamID id, bool approved)
@ -335,7 +358,7 @@ uint32 Auth_Ticket_Manager::getTicket( void *pTicket, int cbMaxTicket, uint32 *p
GetAuthSessionTicketResponse_t data;
data.m_hAuthTicket = ttt;
data.m_eResult = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), STEAM_TICKET_PROCESS_TIME);
outbound.push_back(ticket_data);
@ -404,15 +427,16 @@ EBeginAuthSessionResult Auth_Ticket_Manager::beginAuth(const void *pAuthTicket,
memcpy(&number, ((char *)pAuthTicket) + sizeof(uint64), sizeof(number));
data.id = CSteamID(id);
data.number = number;
data.created = std::chrono::high_resolution_clock::now();
for (auto & t : inbound) {
if (t.id == data.id) {
if (t.id == data.id && !check_timedout(t.created, STEAM_TICKET_PROCESS_TIME)) {
return k_EBeginAuthSessionResultDuplicateRequest;
}
}
inbound.push_back(data);
launch_callback(steamID, k_EAuthSessionResponseOK);
launch_callback(steamID, k_EAuthSessionResponseOK, STEAM_TICKET_PROCESS_TIME);
return k_EBeginAuthSessionResultOK;
}
@ -423,12 +447,18 @@ uint32 Auth_Ticket_Manager::countInboundAuth()
bool Auth_Ticket_Manager::endAuth(CSteamID id)
{
auto ticket = std::find_if(inbound.begin(), inbound.end(), [&id](Auth_Ticket_Data const& item) { return item.id == id; });
if (inbound.end() == ticket)
return false;
bool erased = false;
auto t = std::begin(inbound);
while (t != std::end(inbound)) {
if (t->id == id) {
erased = true;
t = inbound.erase(t);
} else {
++t;
}
}
inbound.erase(ticket);
return true;
return erased;
}
void Auth_Ticket_Manager::Callback(Common_Message *msg)
@ -459,6 +489,7 @@ void Auth_Ticket_Manager::Callback(Common_Message *msg)
auto t = std::begin(inbound);
while (t != std::end(inbound)) {
if (t->id.ConvertToUint64() == msg->source_id() && t->number == number) {
PRINT_DEBUG("TICKET CANCELED\n");
launch_callback(t->id, k_EAuthSessionResponseAuthTicketCanceled);
t = inbound.erase(t);
} else {
@ -470,8 +501,46 @@ void Auth_Ticket_Manager::Callback(Common_Message *msg)
}
#ifdef EMU_EXPERIMENTAL_BUILD
#ifdef STEAM_WIN32
#include "../detours/detours.h"
#ifdef __WINDOWS__
struct ips_test {
uint32_t ip_from;
uint32_t ip_to;
};
static std::vector<struct ips_test> adapter_ips;
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
{
adapter_ips.clear();
for (unsigned i = 0; i < num_ips; ++i) {
struct ips_test ip_a;
PRINT_DEBUG("from: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&from[i])[0], ((unsigned char *)&from[i])[1], ((unsigned char *)&from[i])[2], ((unsigned char *)&from[i])[3]);
PRINT_DEBUG("to: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&to[i])[0], ((unsigned char *)&to[i])[1], ((unsigned char *)&to[i])[2], ((unsigned char *)&to[i])[3]);
ip_a.ip_from = ntohl(from[i]);
ip_a.ip_to = ntohl(to[i]);
if (ip_a.ip_to < ip_a.ip_from) continue;
if ((ip_a.ip_to - ip_a.ip_from) > (1 << 25)) continue;
PRINT_DEBUG("added\n");
adapter_ips.push_back(ip_a);
}
}
static bool is_adapter_ip(unsigned char *ip)
{
uint32_t ip_temp = 0;
memcpy(&ip_temp, ip, sizeof(ip_temp));
ip_temp = ntohl(ip_temp);
for (auto &i : adapter_ips) {
if (i.ip_from <= ip_temp && ip_temp <= i.ip_to) {
PRINT_DEBUG("ADAPTER IP %hhu.%hhu.%hhu.%hhu\n", ip[0], ip[1], ip[2], ip[3]);
return true;
}
}
return false;
}
static bool is_lan_ip(const sockaddr *addr, int namelen)
{
@ -482,6 +551,7 @@ static bool is_lan_ip(const sockaddr *addr, int namelen)
unsigned char ip[4];
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
if (is_adapter_ip(ip)) return true;
if (ip[0] == 127) return true;
if (ip[0] == 10) return true;
if (ip[0] == 192 && ip[1] == 168) return true;
@ -552,13 +622,6 @@ inline bool file_exists (const std::string& name) {
return (stat (name.c_str(), &buffer) == 0);
}
#ifdef DETOURS_64BIT
#define DLL_NAME "steam_api64.dll"
#else
#define DLL_NAME "steam_api.dll"
#endif
HMODULE (WINAPI *Real_GetModuleHandleA)(LPCSTR lpModuleName) = GetModuleHandleA;
HMODULE WINAPI Mine_GetModuleHandleA(LPCSTR lpModuleName)
{
@ -604,12 +667,6 @@ static void load_dll()
}
}
#ifdef DETOURS_64BIT
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x64.dll"
#else
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x86.dll"
#endif
static void load_lumaCEG()
{
std::string path = get_full_program_path();
@ -656,7 +713,6 @@ bool crack_SteamAPI_Init()
return false;
}
#include <winhttp.h>
HINTERNET (WINAPI *Real_WinHttpConnect)(
IN HINTERNET hSession,
@ -684,6 +740,35 @@ HINTERNET WINAPI Mine_WinHttpConnect(
}
}
HINTERNET (WINAPI *Real_WinHttpOpenRequest)(
IN HINTERNET hConnect,
IN LPCWSTR pwszVerb,
IN LPCWSTR pwszObjectName,
IN LPCWSTR pwszVersion,
IN LPCWSTR pwszReferrer,
IN LPCWSTR *ppwszAcceptTypes,
IN DWORD dwFlags
);
HINTERNET WINAPI Mine_WinHttpOpenRequest(
IN HINTERNET hConnect,
IN LPCWSTR pwszVerb,
IN LPCWSTR pwszObjectName,
IN LPCWSTR pwszVersion,
IN LPCWSTR pwszReferrer,
IN LPCWSTR *ppwszAcceptTypes,
IN DWORD dwFlags
) {
PRINT_DEBUG("Mine_WinHttpOpenRequest %ls %ls %ls %ls %i\n", pwszVerb, pwszObjectName, pwszVersion, pwszReferrer, dwFlags);
if (dwFlags & WINHTTP_FLAG_SECURE) {
dwFlags ^= WINHTTP_FLAG_SECURE;
}
return Real_WinHttpOpenRequest(hConnect, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer, ppwszAcceptTypes, dwFlags);
}
static bool network_functions_attached = false;
BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
switch ( dwReason ) {
@ -700,7 +785,10 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
if (winhttp) {
Real_WinHttpConnect = (decltype(Real_WinHttpConnect))GetProcAddress(winhttp, "WinHttpConnect");
DetourAttach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
// Real_WinHttpOpenRequest = (decltype(Real_WinHttpOpenRequest))GetProcAddress(winhttp, "WinHttpOpenRequest");
// DetourAttach( &(PVOID &)Real_WinHttpOpenRequest, Mine_WinHttpOpenRequest );
}
DetourTransactionCommit();
network_functions_attached = true;
}
@ -717,6 +805,7 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
DetourDetach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect );
if (Real_WinHttpConnect) {
DetourDetach( &(PVOID &)Real_WinHttpConnect, Mine_WinHttpConnect );
// DetourDetach( &(PVOID &)Real_WinHttpOpenRequest, Mine_WinHttpOpenRequest );
}
DetourTransactionCommit();
}
@ -724,6 +813,16 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
}
return TRUE;
}
#else
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
{
}
#endif
#else
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
{
}
#endif

View File

@ -18,57 +18,14 @@
#ifndef BASE_INCLUDE
#define BASE_INCLUDE
#if defined(WIN32) || defined(_WIN32)
#define STEAM_WIN32
#pragma warning( disable : 4716)
#ifndef NOMINMAX
# define NOMINMAX
#endif
#endif
#define STEAM_API_EXPORTS
#include "../sdk_includes/steam_gameserver.h"
#include "../sdk_includes/steamdatagram_tickets.h"
#include <algorithm>
#include <vector>
#include <map>
#include <mutex>
//#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
#ifdef STEAM_WIN32
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <processthreadsapi.h>
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define PATH_SEPARATOR "\\"
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
#endif
#else
#include <arpa/inet.h>
#define PATH_SEPARATOR "/"
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
#endif
#endif
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
#ifdef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...)
#endif
#include "settings.h"
#include "local_storage.h"
#include "network.h"
#include "defines.h"
#include "common_includes.h"
#define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); }
extern std::recursive_mutex global_mutex;
std::string get_env_variable(std::string name);
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
class CCallbackMgr
{
@ -89,11 +46,13 @@ public:
};
#define STEAM_CALLRESULT_TIMEOUT 120.0
#define STEAM_CALLRESULT_WAIT_FOR_CB 0.01
struct Steam_Call_Result {
Steam_Call_Result(SteamAPICall_t a, int icb, void *r, unsigned int s, double r_in, bool run_cc_cb) {
api_call = a;
result.resize(s);
memcpy(&(result[0]), r, s);
if (s > 0 && r != NULL)
memcpy(&(result[0]), r, s);
created = std::chrono::high_resolution_clock::now();
run_in = r_in;
run_call_completed_cb = run_cc_cb;
@ -109,8 +68,12 @@ struct Steam_Call_Result {
return check_timedout(created, STEAM_CALLRESULT_TIMEOUT);
}
bool call_completed() {
return (!reserved) && check_timedout(created, run_in);
}
bool can_execute() {
return (!reserved) && (!to_delete) && check_timedout(created, run_in);
return (!to_delete) && call_completed() && (has_cb() || check_timedout(created, STEAM_CALLRESULT_WAIT_FOR_CB));
}
bool has_cb() {
@ -134,13 +97,17 @@ CSteamID generate_steam_id_user();
CSteamID generate_steam_id_server();
CSteamID generate_steam_id_anonserver();
CSteamID generate_steam_id_lobby();
std::string get_full_lib_path();
std::string get_full_program_path();
std::string get_current_path();
std::string canonical_path(std::string path);
#define DEFAULT_CB_TIMEOUT 0.002
class SteamCallResults {
std::vector<struct Steam_Call_Result> callresults;
std::vector<class CCallbackBase *> completed_callbacks;
void (*cb_all)(std::vector<char> result, int callback) = nullptr;
public:
void addCallCompleted(class CCallbackBase *cb) {
@ -167,14 +134,14 @@ public:
bool exists(SteamAPICall_t api_call) {
auto cr = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cr == callresults.end()) return false;
if (cr->reserved) return false;
if (!cr->call_completed()) return false;
return true;
}
bool callback_result(SteamAPICall_t api_call, void *copy_to, unsigned int size) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
if (cb_result->reserved) return false;
if (!cb_result->call_completed()) return false;
if (cb_result->result.size() > size) return false;
memcpy(copy_to, &(cb_result->result[0]), cb_result->result.size());
@ -203,20 +170,22 @@ public:
if (it != cr.callbacks.end()) {
cr.callbacks.erase(it);
}
if (cr.callbacks.size() == 0) {
cr.to_delete = true;
}
}
}
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
if (cb_result != callresults.end()) {
if (cb_result->reserved) {
std::chrono::high_resolution_clock::time_point created = cb_result->created;
std::vector<class CCallbackBase *> temp_cbs = cb_result->callbacks;
*cb_result = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
cb_result->callbacks = temp_cbs;
cb_result->created = created;
return cb_result->api_call;
}
} else {
@ -236,10 +205,14 @@ public:
return callresults.back().api_call;
}
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
return addCallResult(generate_steam_api_call_id(), iCallback, result, size, timeout, run_call_completed_cb);
}
void setCbAll(void (*cb_all)(std::vector<char> result, int callback)) {
this->cb_all = cb_all;
}
void runCallResults() {
unsigned long current_size = callresults.size();
for (unsigned i = 0; i < current_size; ++i) {
@ -255,9 +228,9 @@ public:
callresults[index].run_call_completed_cb = false;
}
callresults[index].to_delete = true;
if (callresults[index].has_cb()) {
std::vector<class CCallbackBase *> temp_cbs = callresults[index].callbacks;
callresults[index].to_delete = true;
for (auto & cb : temp_cbs) {
PRINT_DEBUG("Calling callresult %p %i\n", cb, cb->GetICallback());
global_mutex.unlock();
@ -276,18 +249,30 @@ public:
if (run_call_completed_cb) {
//can it happen that one is removed during the callback?
std::vector<class CCallbackBase *> callbacks = completed_callbacks;
for (auto & cb: callbacks) {
SteamAPICallCompleted_t data;
data.m_hAsyncCall = api_call;
data.m_iCallback = iCallback;
data.m_cubParam = result.size();
SteamAPICallCompleted_t data;
data.m_hAsyncCall = api_call;
data.m_iCallback = iCallback;
data.m_cubParam = result.size();
for (auto & cb: callbacks) {
PRINT_DEBUG("Call complete cb %i %p %llu\n", iCallback, cb, api_call);
//TODO: check if this is a problem or not.
SteamAPICallCompleted_t temp = data;
global_mutex.unlock();
cb->Run(&data);
cb->Run(&temp);
global_mutex.lock();
}
if (cb_all) {
std::vector<char> res;
res.resize(sizeof(data));
memcpy(&(res[0]), &data, sizeof(data));
cb_all(res, data.k_iCallback);
}
} else {
if (cb_all) {
cb_all(result, iCallback);
}
}
} else {
if (callresults[index].timed_out()) {
@ -366,14 +351,18 @@ public:
SteamAPICall_t api_id = results->addCallResult(iCallback, result, size, timeout, false);
results->addCallBack(api_id, cb);
}
if (callbacks[iCallback].callbacks.empty()) {
results->addCallResult(iCallback, result, size, timeout, false);
}
}
void addCBResult(int iCallback, void *result, unsigned int size) {
addCBResult(iCallback, result, size, 0.0, false);
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, false);
}
void addCBResult(int iCallback, void *result, unsigned int size, bool dont_post_if_already) {
addCBResult(iCallback, result, size, 0.0, dont_post_if_already);
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, dont_post_if_already);
}
void addCBResult(int iCallback, void *result, unsigned int size, double timeout) {
@ -397,14 +386,7 @@ public:
void runCallBacks() {
for (auto & c : callbacks) {
std::vector<std::vector<char>> res_back = c.second.results;
c.second.results.clear();
for (auto r : res_back) {
for (auto cb: c.second.callbacks) {
//PRINT_DEBUG("Calling callback %i\n", cb->GetICallback());
//cb->Run(&(r[0]));
}
}
}
}
};
@ -412,6 +394,7 @@ public:
struct Auth_Ticket_Data {
CSteamID id;
uint64 number;
std::chrono::high_resolution_clock::time_point created;
};
class Auth_Ticket_Manager {
@ -419,7 +402,7 @@ class Auth_Ticket_Manager {
class Networking *network;
class SteamCallBacks *callbacks;
void launch_callback(CSteamID id, EAuthSessionResponse resp);
void launch_callback(CSteamID id, EAuthSessionResponse resp, double delay=0);
void launch_callback_gs(CSteamID id, bool approved);
std::vector<struct Auth_Ticket_Data> inbound, outbound;
public:
@ -471,6 +454,7 @@ public:
}
};
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips);
#ifdef EMU_EXPERIMENTAL_BUILD
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID);
bool crack_SteamAPI_Init();

172
dll/common_includes.h Normal file
View File

@ -0,0 +1,172 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_COMMON_INCLUDES__
#define __INCLUDED_COMMON_INCLUDES__
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
#define __WINDOWS_64__
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
#define __WINDOWS_32__
#endif
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
#define __WINDOWS__
#endif
#if defined(__linux__) || defined(linux)
#if defined(__x86_64__)
#define __LINUX_64__
#else
#define __LINUX_32__
#endif
#endif
#if defined(__LINUX_32__) || defined(__LINUX_64__)
#define __LINUX__
#endif
#if defined(__WINDOWS__)
#define STEAM_WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
#define STEAM_API_EXPORTS
#if defined(__WINDOWS__)
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <processthreadsapi.h>
#include <windows.h>
#include <direct.h>
#include <iphlpapi.h> // Include winsock2 before this, or winsock2 iphlpapi will be unavailable
#include <shlobj.h>
#define MSG_NOSIGNAL 0
#define SystemFunction036 NTAPI SystemFunction036
#include <ntsecapi.h>
#undef SystemFunction036
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
#endif
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define PATH_SEPARATOR "\\"
#ifdef EMU_EXPERIMENTAL_BUILD
#include <winhttp.h>
#include "../detours/detours.h"
#ifdef DETOURS_64BIT
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x64.dll"
#define DLL_NAME "steam_api64.dll"
#else
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x86.dll"
#define DLL_NAME "steam_api.dll"
#endif
#endif
#elif defined(__LINUX__)
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <linux/netdevice.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <netdb.h>
#include <dlfcn.h>
#include <utime.h>
#define PATH_MAX_STRING_SIZE 512
#ifndef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
#endif
#define PATH_SEPARATOR "/"
#endif
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
#ifdef EMU_RELEASE_BUILD
#define PRINT_DEBUG(...)
#endif
// C/C++ includes
#include <cstdint>
#include <algorithm>
#include <string>
#include <chrono>
#include <cctype>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <iterator>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <string.h>
#include <stdio.h>
// Other libs includes
#include "../json/json.hpp"
#include "../controller/gamepad.h"
// Steamsdk includes
#include "../sdk_includes/steam_api.h"
#include "../sdk_includes/steam_gameserver.h"
#include "../sdk_includes/steamdatagram_tickets.h"
// Emulator includes
#include "net.pb.h"
#include "settings.h"
#include "local_storage.h"
#include "network.h"
// Emulator defines
#define CLIENT_HSTEAMUSER 1
#define SERVER_HSTEAMUSER 1
#define DEFAULT_NAME "Goldberg"
#define PROGRAM_NAME "Goldberg SteamEmu"
#define DEFAULT_LANGUAGE "english"
#define LOBBY_CONNECT_APPID ((uint32)-2)
#endif//__INCLUDED_COMMON_INCLUDES__

View File

@ -1,12 +0,0 @@
//TODO: put these in a common .h
#define CLIENT_HSTEAMUSER 12
#define SERVER_HSTEAMUSER 13
#define CLIENT_STEAM_PIPE 5
#define SERVER_STEAM_PIPE 6
#define DEFAULT_NAME "Goldberg"
#define PROGRAM_NAME "Goldberg SteamEmu"
#define DEFAULT_LANGUAGE "english"
#define LOBBY_CONNECT_APPID ((uint32)-2)

File diff suppressed because it is too large Load Diff

View File

@ -16,37 +16,19 @@
<http://www.gnu.org/licenses/>. */
#include "steam_client.h"
#ifdef STEAMCLIENT_DLL
#define STEAMAPI_API static
#define STEAMCLIENT_API S_API_EXPORT
#else
#define STEAMAPI_API S_API_EXPORT
#define STEAMCLIENT_API static
#endif
Steam_Client *get_steam_client();
bool steamclient_has_ipv6_functions();
S_API ISteamClient *SteamClient();
S_API ISteamUser *SteamUser();
S_API ISteamFriends *SteamFriends();
S_API ISteamUtils *SteamUtils();
S_API ISteamMatchmaking *SteamMatchmaking();
S_API ISteamUserStats *SteamUserStats();
S_API ISteamApps *SteamApps();
S_API ISteamNetworking *SteamNetworking();
S_API ISteamMatchmakingServers *SteamMatchmakingServers();
S_API ISteamRemoteStorage *SteamRemoteStorage();
S_API ISteamScreenshots *SteamScreenshots();
S_API ISteamHTTP *SteamHTTP();
S_API ISteamController *SteamController();
S_API ISteamUGC *SteamUGC();
S_API ISteamAppList *SteamAppList();
S_API ISteamMusic *SteamMusic();
S_API ISteamMusicRemote *SteamMusicRemote();
S_API ISteamHTMLSurface *SteamHTMLSurface();
S_API ISteamInventory *SteamInventory();
S_API ISteamVideo *SteamVideo();
S_API ISteamParentalSettings *SteamParentalSettings();
S_API ISteamClient *SteamGameServerClient();
S_API ISteamGameServer *SteamGameServer();
S_API ISteamUtils *SteamGameServerUtils();
S_API ISteamNetworking *SteamGameServerNetworking();
S_API ISteamGameServerStats *SteamGameServerStats();
S_API ISteamHTTP *SteamGameServerHTTP();
S_API ISteamInventory *SteamGameServerInventory();
S_API ISteamUGC *SteamGameServerUGC();
S_API ISteamApps *SteamGameServerApps();
HSteamUser flat_hsteamuser();
HSteamPipe flat_hsteampipe();
HSteamUser flat_gs_hsteamuser();
HSteamPipe flat_gs_hsteampipe();

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,15 @@
#include "local_storage.h"
#include <fstream>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#define STBI_ONLY_PNG
#define STBI_ONLY_JPEG
#include "../stb/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_WRITE_STATIC
#include "../stb/stb_image_write.h"
struct File_Data {
std::string name;
@ -75,12 +83,12 @@ int Local_Storage::store_data_settings(std::string file, char *data, unsigned in
return -1;
}
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length)
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
{
return -1;
}
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length)
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
{
return -1;
}
@ -125,14 +133,38 @@ bool Local_Storage::update_save_filenames(std::string folder)
return true;
}
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
{
return false;
}
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
{
return false;
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
{
return false;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
{
return std::vector<std::string>();
}
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
{
return std::vector<image_pixel_t>();
}
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
{
return false;
}
#else
#if defined(WIN32) || defined(_WIN32)
#include <windows.h>
#if defined(__WINDOWS__)
static BOOL DirectoryExists(LPCSTR szPath)
{
@ -158,11 +190,6 @@ static void create_directory(std::string strPath)
createDirectoryRecursively(strPath);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
static std::vector<struct File_Data> get_filenames(std::string strPath)
{
std::vector<struct File_Data> output;
@ -192,6 +219,8 @@ static std::vector<struct File_Data> get_filenames(std::string strPath)
static std::vector<struct File_Data> get_filenames_recursive(std::string base_path)
{
if (base_path.back() == *PATH_SEPARATOR)
base_path.pop_back();
std::vector<struct File_Data> output;
std::string strPath = base_path;
strPath = strPath.append("\\*");
@ -211,11 +240,11 @@ static std::vector<struct File_Data> get_filenames_recursive(std::string base_pa
std::string dir_name = ffd.cFileName;
std::string path = base_path;
path += "\\";
path += PATH_SEPARATOR;
path += dir_name;
std::vector<struct File_Data> lower = get_filenames_recursive(path);
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [dir_name](File_Data f) {f.name = dir_name + "\\" + f.name; return f;});
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = dir_name + "\\" + f.name; return f;});
} else {
File_Data f;
f.name = ffd.cFileName;
@ -230,14 +259,7 @@ static std::vector<struct File_Data> get_filenames_recursive(std::string base_pa
return output;
}
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <dirent.h>
#define PATH_MAX_STRING_SIZE 512
#else
/* recursive mkdir */
static int mkdir_p(const char *dir, const mode_t mode) {
@ -354,7 +376,7 @@ static std::vector<struct File_Data> get_filenames_recursive(std::string base_pa
path += dir_name;
std::vector<struct File_Data> lower = get_filenames_recursive(path);
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [dir_name](File_Data f) {f.name = dir_name + "/" + f.name; return f;});
std::transform(lower.begin(), lower.end(), std::back_inserter(output), [&dir_name](File_Data f) {f.name = dir_name + "/" + f.name; return f;});
}
}
}
@ -374,14 +396,9 @@ std::string Local_Storage::get_program_path()
std::string Local_Storage::get_game_settings_path()
{
return get_program_path().append(GAME_SETTINGS_FOLDER).append(PATH_SEPARATOR);
return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR);
}
#if defined(STEAM_WIN32)
#include <shlobj.h>
#include <sstream>
#endif
std::string Local_Storage::get_user_appdata_path()
{
std::string user_appdata_path = "SAVE";
@ -504,7 +521,7 @@ std::string Local_Storage::get_path(std::string folder)
std::string Local_Storage::get_global_settings_path()
{
return save_directory + SETTINGS_STORAGE_FOLDER + PATH_SEPARATOR;
return save_directory + settings_storage_folder + PATH_SEPARATOR;
}
std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
@ -533,22 +550,19 @@ int Local_Storage::store_data_settings(std::string file, char *data, unsigned in
return store_file_data(get_global_settings_path(), file, data, length);
}
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length)
int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset)
{
std::ifstream myfile;
myfile.open(full_path, std::ios::binary | std::ios::in);
if (!myfile.is_open()) return -1;
std::streampos size = myfile.tellg();
myfile.seekg (0, std::ios::beg);
if (size > max_length) max_length = size;
myfile.seekg (offset, std::ios::beg);
myfile.read (data, max_length);
myfile.close();
return myfile.gcount();
}
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length)
int Local_Storage::get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset)
{
file = sanitize_file_name(file);
if (folder.back() != *PATH_SEPARATOR) {
@ -556,7 +570,7 @@ int Local_Storage::get_data(std::string folder, std::string file, char *data, un
}
std::string full_path = save_directory + appid + folder + file;
return get_file_data(full_path, data, max_length);
return get_file_data(full_path, data, max_length, offset);
}
int Local_Storage::get_data_settings(std::string file, char *data, unsigned int max_length)
@ -584,7 +598,19 @@ bool Local_Storage::file_exists(std::string folder, std::string file)
std::string full_path = save_directory + appid + folder + file;
struct stat buffer;
return (stat (full_path.c_str(), &buffer) == 0);
if (stat(full_path.c_str(), &buffer) != 0)
return false;
#if defined(STEAM_WIN32)
if ( buffer.st_mode & _S_IFDIR)
return false;
#else
if (S_ISDIR(buffer.st_mode))
return false;
#endif
return true;
}
unsigned int Local_Storage::file_size(std::string folder, std::string file)
@ -667,4 +693,96 @@ bool Local_Storage::update_save_filenames(std::string folder)
return true;
}
bool Local_Storage::load_json(std::string full_path, nlohmann::json& json)
{
std::ifstream inventory_file(full_path);
// If there is a file and we opened it
if (inventory_file)
{
inventory_file.seekg(0, std::ios::end);
size_t size = inventory_file.tellg();
std::string buffer(size, '\0');
inventory_file.seekg(0);
// Read it entirely, if the .json file gets too big,
// I should look into this and split reads into smaller parts.
inventory_file.read(&buffer[0], size);
inventory_file.close();
try {
json = std::move(nlohmann::json::parse(buffer));
PRINT_DEBUG("Loaded json \"%s\". Loaded %u items.\n", full_path.c_str(), json.size());
return true;
} catch (std::exception& e) {
PRINT_DEBUG("Error while parsing \"%s\" json: %s\n", full_path.c_str(), e.what());
}
}
else
{
PRINT_DEBUG("Couldn't open file \"%s\" to read json\n", full_path.c_str());
}
return false;
}
bool Local_Storage::load_json_file(std::string folder, std::string const&file, nlohmann::json& json)
{
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string inv_path = std::move(save_directory + appid + folder);
std::string full_path = inv_path + file;
return load_json(full_path, json);
}
bool Local_Storage::write_json_file(std::string folder, std::string const&file, nlohmann::json const& json)
{
if (!folder.empty() && folder.back() != *PATH_SEPARATOR) {
folder.append(PATH_SEPARATOR);
}
std::string inv_path = std::move(save_directory + appid + folder);
std::string full_path = inv_path + file;
create_directory(inv_path);
std::ofstream inventory_file(full_path, std::ios::trunc | std::ios::out);
if (inventory_file)
{
inventory_file << std::setw(2) << json;
return true;
}
PRINT_DEBUG("Couldn't open file \"%s\" to write json\n", full_path.c_str());
return false;
}
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
{
std::vector<image_pixel_t> res;
FILE* hFile = fopen(image_path.c_str(), "r");
if (hFile != nullptr)
{
int width, height;
image_pixel_t* img = (image_pixel_t*)stbi_load_from_file(hFile, &width, &height, nullptr, 4);
if (img != nullptr)
{
res.resize(width*height);
std::copy(img, img + width * height, res.begin());
stbi_image_free(img);
}
fclose(hFile);
}
return res;
}
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
{
std::string screenshot_path = std::move(save_directory + appid + screenshots_folder + PATH_SEPARATOR);
create_directory(screenshot_path);
screenshot_path += image_path;
return stbi_write_png(screenshot_path.c_str(), width, height, channels, img_ptr, 0) == 1;
}
#endif

View File

@ -15,23 +15,43 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#ifndef LOCAL_STORAGE_INCLUDE
#define LOCAL_STORAGE_INCLUDE
#define SETTINGS_STORAGE_FOLDER "settings"
#define REMOTE_STORAGE_FOLDER "remote"
#define STATS_STORAGE_FOLDER "stats"
#define USER_DATA_FOLDER "local"
#define GAME_SETTINGS_FOLDER "steam_settings"
#include <string>
#include "base.h"
#define MAX_FILENAME_LENGTH 300
union image_pixel_t
{
uint32_t pixel;
struct pixel_channels_t
{
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} channels;
};
struct image_t
{
size_t width;
size_t height;
std::vector<image_pixel_t> pix_map;
};
class Local_Storage {
public:
static constexpr auto inventory_storage_folder = "inventory";
static constexpr auto settings_storage_folder = "settings";
static constexpr auto remote_storage_folder = "remote";
static constexpr auto stats_storage_folder = "stats";
static constexpr auto user_data_storage = "local";
static constexpr auto screenshots_folder = "screenshots";
static constexpr auto game_settings_folder = "steam_settings";
private:
std::string save_directory;
std::string appid;
public:
@ -39,14 +59,14 @@ public:
static std::string get_game_settings_path();
static std::string get_user_appdata_path();
Local_Storage(std::string save_directory);
static int get_file_data(std::string full_path, char *data, unsigned int max_length);
static int get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset=0);
void setAppId(uint32 appid);
static int store_file_data(std::string folder, std::string file, char *data, unsigned int length);
static std::vector<std::string> get_filenames_path(std::string path);
int store_data(std::string folder, std::string file, char *data, unsigned int length);
int store_data_settings(std::string file, char *data, unsigned int length);
int get_data(std::string folder, std::string file, char *data, unsigned int max_length);
int get_data(std::string folder, std::string file, char *data, unsigned int max_length, unsigned int offset=0);
int get_data_settings(std::string file, char *data, unsigned int max_length);
int count_files(std::string folder);
bool iterate_file(std::string folder, int index, char *output_filename, int32 *output_size);
@ -58,6 +78,13 @@ public:
std::string get_path(std::string folder);
bool update_save_filenames(std::string folder);
bool load_json(std::string full_path, nlohmann::json& json);
bool load_json_file(std::string folder, std::string const& file, nlohmann::json& json);
bool write_json_file(std::string folder, std::string const& file, nlohmann::json const& json);
std::vector<image_pixel_t> load_image(std::string const& image_path);
bool save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels);
};
#endif

View File

@ -79,7 +79,7 @@ message Low_Level {
Types type = 1;
}
message Network {
message Network_pb {
uint32 channel = 1;
bytes data = 2;
@ -112,8 +112,7 @@ message Network_Old {
message Networking_Sockets {
enum Types {
CONNECTION_REQUEST_IP = 0;
CONNECTION_REQUEST_STEAMID = 1;
CONNECTION_REQUEST = 0;
CONNECTION_ACCEPTED = 2;
CONNECTION_END = 3;
DATA = 4;
@ -186,6 +185,17 @@ message Friend_Messages {
}
}
message Steam_Messages {
enum Types {
FRIEND_CHAT = 0;
}
Types type = 1;
oneof message_data {
bytes message = 2;
}
}
message Common_Message {
uint64 source_id = 1;
uint64 dest_id = 2;
@ -194,13 +204,14 @@ message Common_Message {
Low_Level low_level = 4;
Lobby lobby = 5;
Lobby_Messages lobby_messages = 6;
Network network = 7;
Network_pb network = 7;
Gameserver gameserver = 8;
Friend friend = 9;
Auth_Ticket auth_ticket = 10;
Friend_Messages friend_messages = 11;
Network_Old network_old = 12;
Networking_Sockets networking_sockets = 13;
Steam_Messages steam_messages = 14;
}
uint32 source_ip = 128;

View File

@ -17,25 +17,11 @@
#include "network.h"
#if defined(STEAM_WIN32)
#define MSG_NOSIGNAL 0
#else
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <linux/netdevice.h>
#include <netdb.h>
#endif
#define MAX_BROADCASTS 16
static int number_broadcasts = -1;
static IP_PORT broadcasts[MAX_BROADCASTS];
static uint32_t lower_range_ips[MAX_BROADCASTS];
static uint32_t upper_range_ips[MAX_BROADCASTS];
#define BROADCAST_INTERVAL 5.0
#define HEARTBEAT_TIMEOUT 20.0
@ -43,7 +29,44 @@ static IP_PORT broadcasts[MAX_BROADCASTS];
#if defined(STEAM_WIN32)
#include <iphlpapi.h>
//windows xp support
static int
inet_pton4(const char *src, uint32_t *dst)
{
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
u_char tmp[sizeof(uint32_t)], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
size_t nx = *tp * 10 + (pch - digits);
if (nx > 255)
return (0);
*tp = (u_char) nx;
if (! saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
memcpy(dst, tmp, sizeof(uint32_t));
return (1);
}
static void get_broadcast_info(uint16 port)
{
@ -71,17 +94,16 @@ static void get_broadcast_info(uint16 port)
IP_ADAPTER_INFO *pAdapter = pAdapterInfo;
while (pAdapter) {
unsigned long gateway = 0, subnet_mask = 0;
uint32_t iface_ip = 0, subnet_mask = 0;
if (inet_pton(AF_INET, pAdapter->IpAddressList.IpMask.String, &subnet_mask) == 1
&& inet_pton(AF_INET, pAdapter->GatewayList.IpAddress.String, &gateway) == 1) {
if (inet_pton4(pAdapter->IpAddressList.IpMask.String, &subnet_mask) == 1
&& inet_pton4(pAdapter->IpAddressList.IpAddress.String, &iface_ip) == 1) {
IP_PORT *ip_port = &broadcasts[number_broadcasts];
//ip_port->ip.family = AF_INET;
uint32 gateway_ip = ntohl(gateway), subnet_ip = ntohl(subnet_mask);
uint32 broadcast_ip = gateway_ip + ~subnet_ip - 1;
ip_port->ip = htonl(broadcast_ip);
uint32 broadcast_ip = iface_ip | ~subnet_mask;
ip_port->ip = broadcast_ip;
ip_port->port = port;
lower_range_ips[number_broadcasts] = iface_ip & subnet_mask;
upper_range_ips[number_broadcasts] = broadcast_ip;
number_broadcasts++;
if (number_broadcasts >= MAX_BROADCASTS) {
@ -222,6 +244,11 @@ static void run_at_startup()
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR)
return;
for (int i = 0; i < 10; ++i) {
//hack: the game Full Mojo Rampage calls WSACleanup on startup so we call WSAStartup a few times so it doesn't get deallocated.
WSAStartup(MAKEWORD(2, 2), &wsaData);
}
#else
#endif
@ -287,6 +314,7 @@ static bool send_broadcasts(sock_t sock, uint16 port, char *data, unsigned long
if (number_broadcasts < 0 || check_timedout(last_get_broadcast_info, 60.0)) {
PRINT_DEBUG("get_broadcast_info\n");
get_broadcast_info(port);
set_adapter_ips(lower_range_ips, upper_range_ips, number_broadcasts);
last_get_broadcast_info = std::chrono::high_resolution_clock::now();
}
@ -379,7 +407,10 @@ unsigned int receive_buffer_amount(sock_t sock)
static void send_tcp_pending(struct TCP_Socket &socket)
{
int len = send(socket.sock, &(socket.send_buffer[0]), socket.send_buffer.size(), MSG_NOSIGNAL);
size_t buf_size = socket.send_buffer.size();
if (buf_size == 0) return;
int len = send(socket.sock, &(socket.send_buffer[0]), buf_size, MSG_NOSIGNAL);
if (len <= 0) return;
socket.send_buffer.erase(socket.send_buffer.begin(), socket.send_buffer.begin() + len);
@ -440,18 +471,6 @@ static bool recv_tcp(struct TCP_Socket &socket)
return false;
}
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
{
if (timeout == 0.0) return true;
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
if (std::chrono::duration_cast<std::chrono::duration<double>>(now - old).count() > timeout) {
return true;
}
return false;
}
static void socket_timeouts(struct TCP_Socket &socket, double extra_time)
{
if (check_timedout(socket.last_heartbeat_sent, HEARTBEAT_TIMEOUT / 2.0)) {
@ -530,6 +549,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
PRINT_DEBUG("has_networking_sockets\n");
run_callbacks(CALLBACK_ID_NETWORKING_SOCKETS, msg);
}
if (msg->has_steam_messages()) {
PRINT_DEBUG("has_steam_messages\n");
run_callbacks(CALLBACK_ID_STEAM_MESSAGES, msg);
}
}
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)
@ -704,18 +728,26 @@ bool Networking::handle_low_level_udp(Common_Message *msg, IP_PORT ip_port)
#define NUM_TCP_WAITING 128
Networking::Networking(CSteamID id, uint32 appid, uint16 port, std::set<uint32_t> *custom_broadcasts)
Networking::Networking(CSteamID id, uint32 appid, uint16 port, std::set<uint32_t> *custom_broadcasts, bool disable_sockets)
{
run_at_startup();
tcp_port = udp_port = port;
own_ip = 0x7F000001;
alive = true;
last_run = std::chrono::high_resolution_clock::now();
this->appid = appid;
if (disable_sockets) {
enabled = false;
udp_socket = -1;
tcp_socket = -1;
return;
}
if (custom_broadcasts) {
std::transform(custom_broadcasts->begin(), custom_broadcasts->end(), std::back_inserter(this->custom_broadcasts), [](uint32 ip) {return htonl(ip);});
}
run_at_startup();
sock_t sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
PRINT_DEBUG("UDP socket: %u\n", sock);
if (is_socket_valid(sock) && set_socket_nonblocking(sock)) {
@ -1052,6 +1084,7 @@ void Networking::Run()
void Networking::addListenId(CSteamID id)
{
if (!enabled) return;
auto i = std::find(ids.begin(), ids.end(), id);
if (i != ids.end()) {
return;
@ -1083,8 +1116,20 @@ bool Networking::sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool
return true;
}
uint32 Networking::getIP(CSteamID id)
{
Connection *conn = find_connection(id, this->appid);
if (conn) {
return ntohl(conn->tcp_ip_port.ip);
}
return 0;
}
bool Networking::sendTo(Common_Message *msg, bool reliable, Connection *conn)
{
if (!enabled) return false;
bool ret = false;
CSteamID dest_id((uint64)msg->dest_id());
if (std::find(ids.begin(), ids.end(), dest_id) != ids.end()) {

View File

@ -15,13 +15,10 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#ifndef NETWORK_INCLUDE
#define NETWORK_INCLUDE
#include "net.pb.h"
#include <chrono>
#include "base.h"
inline bool protobuf_message_equal(const google::protobuf::MessageLite& msg_a,
const google::protobuf::MessageLite& msg_b) {
@ -38,8 +35,6 @@ typedef unsigned int sock_t;
typedef int sock_t;
#endif
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
struct IP_PORT {
uint32 ip;
uint16 port;
@ -60,6 +55,7 @@ enum Callback_Ids {
CALLBACK_ID_AUTH_TICKET,
CALLBACK_ID_FRIEND_MESSAGES,
CALLBACK_ID_NETWORKING_SOCKETS,
CALLBACK_ID_STEAM_MESSAGES,
CALLBACK_IDS_MAX
};
@ -124,7 +120,7 @@ public:
//NOTE: for all functions ips/ports are passed/returned in host byte order
//ex: 127.0.0.1 should be passed as 0x7F000001
static std::set<uint32> resolve_ip(std::string dns);
Networking(CSteamID id, uint32 appid, uint16 port, std::set<uint32_t> *custom_broadcasts);
Networking(CSteamID id, uint32 appid, uint16 port, std::set<uint32_t> *custom_broadcasts, bool disable_sockets);
void addListenId(CSteamID id);
void setAppID(uint32 appid);
void Run();
@ -134,6 +130,7 @@ public:
bool sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool reliable);
bool setCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
uint32 getIP(CSteamID id);
uint32 getOwnIP();
void shutDown();

View File

@ -53,6 +53,7 @@ Settings::Settings(CSteamID steam_id, CGameID game_id, std::string name, std::st
this->unlockAllDLCs = true;
this->offline = offline;
this->create_unknown_leaderboards = true;
}
CSteamID Settings::get_local_steam_id()
@ -193,3 +194,23 @@ std::string Settings::getAppInstallPath(AppId_t appID)
{
return app_paths[appID];
}
void Settings::setLeaderboard(std::string leaderboard, enum ELeaderboardSortMethod sort_method, enum ELeaderboardDisplayType display_type)
{
Leaderboard_config leader;
leader.sort_method = sort_method;
leader.display_type = display_type;
leaderboards[leaderboard] = leader;
}
int Settings::add_image(std::string data, uint32 width, uint32 height)
{
int last = images.size() + 1;
struct Image_Data dt;
dt.width = width;
dt.height = height;
dt.data = data;
images[last] = dt;
return last;
}

View File

@ -15,12 +15,11 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include <set>
#ifndef SETTINGS_INCLUDE
#define SETTINGS_INCLUDE
#include "base.h"
struct DLC_entry {
AppId_t appID;
std::string name;
@ -33,6 +32,37 @@ struct Mod_entry {
std::string path;
};
struct Leaderboard_config {
enum ELeaderboardSortMethod sort_method;
enum ELeaderboardDisplayType display_type;
};
enum Stat_Type {
STAT_TYPE_INT,
STAT_TYPE_FLOAT,
STAT_TYPE_AVGRATE
};
struct Stat_config {
enum Stat_Type type;
union {
float default_value_float;
uint32 default_value_int;
};
};
struct Image_Data {
uint32 width;
uint32 height;
std::string data;
};
struct Controller_Settings {
std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_sets;
std::map<std::string, std::string> action_set_layer_parents;
std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_set_layers;
};
class Settings {
CSteamID steam_id;
CGameID game_id;
@ -44,6 +74,10 @@ class Settings {
std::vector<struct DLC_entry> DLCs;
std::vector<struct Mod_entry> mods;
std::map<AppId_t, std::string> app_paths;
std::map<std::string, Leaderboard_config> leaderboards;
std::map<std::string, Stat_config> stats;
bool create_unknown_leaderboards;
uint16 port;
public:
#ifdef LOBBY_CONNECT
@ -61,6 +95,8 @@ public:
void set_lobby(CSteamID lobby_id);
CSteamID get_lobby();
bool is_offline() {return offline; }
uint16 get_port() {return port;}
void set_port(uint16 port) { this->port = port;}
//DLC stuff
void unlockAllDLC(bool value);
@ -69,6 +105,9 @@ public:
bool hasDLC(AppId_t appID);
bool getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name);
//Depots
std::vector<DepotId_t> depots;
//App Install paths
void setAppInstallPath(AppId_t appID, std::string path);
std::string getAppInstallPath(AppId_t appID);
@ -78,6 +117,36 @@ public:
Mod_entry getMod(PublishedFileId_t id);
bool isModInstalled(PublishedFileId_t id);
std::set<PublishedFileId_t> modSet();
//leaderboards
void setLeaderboard(std::string leaderboard, enum ELeaderboardSortMethod sort_method, enum ELeaderboardDisplayType display_type);
std::map<std::string, Leaderboard_config> getLeaderboards() { return leaderboards; }
void setCreateUnknownLeaderboards(bool enable) {create_unknown_leaderboards = enable;}
bool createUnknownLeaderboards() { return create_unknown_leaderboards; }
//custom broadcasts
std::set<uint32> custom_broadcasts;
//stats
std::map<std::string, Stat_config> getStats() { return stats; }
void setStatDefiniton(std::string name, struct Stat_config stat_config) {stats[name] = stat_config; }
//subscribed lobby/group ids
std::set<uint64> subscribed_groups;
//images
std::map<int, struct Image_Data> images;
int add_image(std::string data, uint32 width, uint32 height);
//controller
struct Controller_Settings controller_settings;
std::string glyphs_directory;
//networking
bool disable_networking = false;
//overlay
bool disable_overlay = false;
};
#endif

552
dll/settings_parser.cpp Normal file
View File

@ -0,0 +1,552 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "settings_parser.h"
static void consume_bom(std::ifstream &input)
{
int bom[3];
bom[0] = input.get();
bom[1] = input.get();
bom[2] = input.get();
if (bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) {
input.seekg(0);
}
}
static void load_custom_broadcasts(std::string broadcasts_filepath, std::set<uint32> &custom_broadcasts)
{
PRINT_DEBUG("Broadcasts file path: %s\n", broadcasts_filepath.c_str());
std::ifstream broadcasts_file(broadcasts_filepath);
consume_bom(broadcasts_file);
if (broadcasts_file.is_open()) {
std::string line;
while (std::getline(broadcasts_file, line)) {
std::set<uint32> ips = Networking::resolve_ip(line);
custom_broadcasts.insert(ips.begin(), ips.end());
}
}
}
template<typename Out>
static void split_string(const std::string &s, char delim, Out result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
static void load_gamecontroller_settings(Settings *settings)
{
std::string path = Local_Storage::get_game_settings_path() + "controller";
std::vector<std::string> paths = Local_Storage::get_filenames_path(path);
for (auto & p: paths) {
size_t length = p.length();
if (length < 4) continue;
if ( std::toupper(p.back()) != 'T') continue;
if ( std::toupper(p[length - 2]) != 'X') continue;
if ( std::toupper(p[length - 3]) != 'T') continue;
if (p[length - 4] != '.') continue;
PRINT_DEBUG("controller config %s\n", p.c_str());
std::string action_set_name = p.substr(0, length - 4);
std::transform(action_set_name.begin(), action_set_name.end(), action_set_name.begin(),[](unsigned char c){ return std::toupper(c); });
std::string controller_config_path = path + PATH_SEPARATOR + p;
std::ifstream input( controller_config_path );
if (input.is_open()) {
consume_bom(input);
std::map<std::string, std::pair<std::set<std::string>, std::string>> button_pairs;
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
std::string action_name;
std::string button_name;
std::string source_mode;
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
action_name = line.substr(0, deliminator);
std::size_t deliminator2 = line.find("=", deliminator + 1);
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
button_name = line.substr(deliminator + 1, deliminator2 - (deliminator + 1));
source_mode = line.substr(deliminator2 + 1);
} else {
button_name = line.substr(deliminator + 1);
source_mode = "";
}
}
std::transform(action_name.begin(), action_name.end(), action_name.begin(),[](unsigned char c){ return std::toupper(c); });
std::transform(button_name.begin(), button_name.end(), button_name.begin(),[](unsigned char c){ return std::toupper(c); });
std::pair<std::set<std::string>, std::string> button_config = {{}, source_mode};
split_string(button_name, ',', std::inserter(button_config.first, button_config.first.begin()));
button_pairs[action_name] = button_config;
PRINT_DEBUG("Added %s %s %s\n", action_name.c_str(), button_name.c_str(), source_mode.c_str());
}
settings->controller_settings.action_sets[action_set_name] = button_pairs;
PRINT_DEBUG("Added %u action names to %s\n", button_pairs.size(), action_set_name.c_str());
}
}
settings->glyphs_directory = path + (PATH_SEPARATOR "glyphs" PATH_SEPARATOR);
}
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out)
{
std::string program_path = Local_Storage::get_program_path(), save_path = Local_Storage::get_user_appdata_path();;
PRINT_DEBUG("Current Path %s save_path: %s\n", program_path.c_str(), save_path.c_str());
char array[10] = {};
array[0] = '0';
Local_Storage::get_file_data(Local_Storage::get_game_settings_path() + "steam_appid.txt", array, sizeof(array) - 1);
uint32 appid = 0;
try {
appid = std::stoi(array);
} catch (...) {}
if (!appid) {
memset(array, 0, sizeof(array));
array[0] = '0';
Local_Storage::get_file_data("steam_appid.txt", array, sizeof(array) - 1);
try {
appid = std::stoi(array);
} catch (...) {}
if (!appid) {
memset(array, 0, sizeof(array));
array[0] = '0';
Local_Storage::get_file_data(program_path + "steam_appid.txt", array, sizeof(array) - 1);
try {
appid = std::stoi(array);
} catch (...) {}
}
}
if (!appid) {
std::string str_appid = get_env_variable("SteamAppId");
std::string str_gameid = get_env_variable("SteamGameId");
PRINT_DEBUG("str_appid %s str_gameid: %s\n", str_appid.c_str(), str_gameid.c_str());
uint32 appid_env = 0;
uint32 gameid_env = 0;
if (str_appid.size() > 0) {
try {
appid_env = std::stoul(str_appid);
} catch (...) {
appid_env = 0;
}
}
if (str_gameid.size() > 0) {
try {
gameid_env = std::stoul(str_gameid);
} catch (...) {
gameid_env = 0;
}
}
PRINT_DEBUG("appid_env %u gameid_env: %u\n", appid_env, gameid_env);
if (appid_env) {
appid = appid_env;
}
if (gameid_env) {
appid = gameid_env;
}
}
{
char array[33] = {};
if (Local_Storage::get_file_data(program_path + "local_save.txt", array, sizeof(array) - 1) != -1) {
save_path = program_path + Settings::sanitize(array);
}
}
PRINT_DEBUG("Set save_path: %s\n", save_path.c_str());
Local_Storage *local_storage = new Local_Storage(save_path);
local_storage->setAppId(appid);
// Listen port
char array_port[10] = {};
array_port[0] = '0';
local_storage->get_data_settings("listen_port.txt", array_port, sizeof(array_port) - 1);
uint16 port = std::stoi(array_port);
if (port == 0) {
port = DEFAULT_PORT;
snprintf(array_port, sizeof(array_port), "%hu", port);
local_storage->store_data_settings("listen_port.txt", array_port, strlen(array_port));
}
// Custom broadcasts
std::set<uint32> custom_broadcasts;
load_custom_broadcasts(local_storage->get_global_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
load_custom_broadcasts(Local_Storage::get_game_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
// Acount name
char name[32] = {};
if (local_storage->get_data_settings("account_name.txt", name, sizeof(name) - 1) <= 0) {
strcpy(name, DEFAULT_NAME);
local_storage->store_data_settings("account_name.txt", name, strlen(name));
}
// Language
char language[32] = {};
if (local_storage->get_data_settings("language.txt", language, sizeof(language) - 1) <= 0) {
strcpy(language, DEFAULT_LANGUAGE);
local_storage->store_data_settings("language.txt", language, strlen(language));
}
// Steam ID
char array_steam_id[32] = {};
CSteamID user_id;
uint64 steam_id = 0;
bool generate_new = false;
//try to load steam id from game specific settings folder first
if (local_storage->get_data(Local_Storage::settings_storage_folder, "user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
user_id = CSteamID((uint64)std::atoll(array_steam_id));
if (!user_id.IsValid()) {
generate_new = true;
}
} else {
generate_new = true;
}
if (generate_new) {
generate_new = false;
if (local_storage->get_data_settings("user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
user_id = CSteamID((uint64)std::atoll(array_steam_id));
if (!user_id.IsValid()) {
generate_new = true;
}
} else {
generate_new = true;
}
}
if (generate_new) {
user_id = generate_steam_id_user();
uint64 steam_id = user_id.ConvertToUint64();
char temp_text[32] = {};
snprintf(temp_text, sizeof(temp_text), "%llu", steam_id);
local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text));
}
bool steam_offline_mode = false;
bool disable_networking = false;
bool disable_overlay = false;
{
std::string steam_settings_path = Local_Storage::get_game_settings_path();
std::vector<std::string> paths = Local_Storage::get_filenames_path(steam_settings_path);
for (auto & p: paths) {
PRINT_DEBUG("steam settings path %s\n", p.c_str());
if (p == "offline.txt") {
steam_offline_mode = true;
} else if (p == "disable_networking.txt") {
disable_networking = true;
} else if (p == "disable_overlay.txt") {
disable_overlay = true;
} else if (p == "force_language.txt") {
int len = Local_Storage::get_file_data(steam_settings_path + "force_language.txt", language, sizeof(language) - 1);
if (len > 0) language[len] = 0;
} else if (p == "force_steamid.txt") {
char steam_id_text[32] = {};
if (Local_Storage::get_file_data(steam_settings_path + "force_steamid.txt", steam_id_text, sizeof(steam_id_text) - 1) > 0) {
CSteamID temp_id = CSteamID((uint64)std::atoll(steam_id_text));
if (temp_id.IsValid()) {
user_id = temp_id;
}
}
} else if (p == "force_account_name.txt") {
int len = Local_Storage::get_file_data(steam_settings_path + "force_account_name.txt", name, sizeof(name) - 1);
if (len > 0) name[len] = 0;
}
}
}
Settings *settings_client = new Settings(user_id, CGameID(appid), name, language, steam_offline_mode);
Settings *settings_server = new Settings(generate_steam_id_server(), CGameID(appid), name, language, steam_offline_mode);
settings_client->set_port(port);
settings_server->set_port(port);
settings_client->custom_broadcasts = custom_broadcasts;
settings_server->custom_broadcasts = custom_broadcasts;
settings_client->disable_networking = disable_networking;
settings_server->disable_networking = disable_networking;
settings_client->disable_overlay = disable_overlay;
settings_server->disable_overlay = disable_overlay;
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";
std::ifstream input( dlc_config_path );
if (input.is_open()) {
consume_bom(input);
settings_client->unlockAllDLC(false);
settings_server->unlockAllDLC(false);
PRINT_DEBUG("Locking all DLC\n");
for( std::string line; std::getline( input, line ); ) {
if (!line.empty() && line.front() == '#') {
continue;
}
if (!line.empty() && line.back() == '\n') {
line.pop_back();
}
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
AppId_t appid = stol(line.substr(0, deliminator));
std::string name = line.substr(deliminator + 1);
bool available = true;
if (appid) {
PRINT_DEBUG("Adding DLC: %u|%s| %u\n", appid, name.c_str(), available);
settings_client->addDLC(appid, name, available);
settings_server->addDLC(appid, name, available);
}
}
}
} else {
//unlock all DLC
PRINT_DEBUG("Unlocking all DLC\n");
settings_client->unlockAllDLC(true);
settings_server->unlockAllDLC(true);
}
}
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "app_paths.txt";
std::ifstream input( dlc_config_path );
if (input.is_open()) {
consume_bom(input);
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
AppId_t appid = stol(line.substr(0, deliminator));
std::string rel_path = line.substr(deliminator + 1);
std::string path = canonical_path(program_path + rel_path);
if (appid) {
if (path.size()) {
PRINT_DEBUG("Adding app path: %u|%s|\n", appid, path.c_str());
settings_client->setAppInstallPath(appid, path);
settings_server->setAppInstallPath(appid, path);
} else {
PRINT_DEBUG("Error adding app path for: %u does this path exist? |%s|\n", appid, rel_path.c_str());
}
}
}
}
}
}
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "leaderboards.txt";
std::ifstream input( dlc_config_path );
if (input.is_open()) {
consume_bom(input);
settings_client->setCreateUnknownLeaderboards(false);
settings_server->setCreateUnknownLeaderboards(false);
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
std::string leaderboard;
unsigned int sort_method = 0;
unsigned int display_type = 0;
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
leaderboard = line.substr(0, deliminator);
std::size_t deliminator2 = line.find("=", deliminator + 1);
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
sort_method = stol(line.substr(deliminator + 1, deliminator2 - (deliminator + 1)));
display_type = stol(line.substr(deliminator2 + 1));
}
}
if (leaderboard.size() && sort_method <= k_ELeaderboardSortMethodDescending && display_type <= k_ELeaderboardDisplayTypeTimeMilliSeconds) {
PRINT_DEBUG("Adding leaderboard: %s|%u|%u\n", leaderboard.c_str(), sort_method, display_type);
settings_client->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type);
settings_server->setLeaderboard(leaderboard, (ELeaderboardSortMethod)sort_method, (ELeaderboardDisplayType)display_type);
} else {
PRINT_DEBUG("Error adding leaderboard for: %s, are sort method %u or display type %u valid?\n", leaderboard.c_str(), sort_method, display_type);
}
}
}
}
{
std::string stats_config_path = Local_Storage::get_game_settings_path() + "stats.txt";
std::ifstream input( stats_config_path );
if (input.is_open()) {
consume_bom(input);
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
std::string stat_name;
std::string stat_type;
std::string stat_default_value;
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
stat_name = line.substr(0, deliminator);
std::size_t deliminator2 = line.find("=", deliminator + 1);
if (deliminator2 != std::string::npos && deliminator2 != line.size()) {
stat_type = line.substr(deliminator + 1, deliminator2 - (deliminator + 1));
stat_default_value = line.substr(deliminator2 + 1);
} else {
stat_type = line.substr(deliminator + 1);
stat_default_value = "0";
}
}
std::transform(stat_type.begin(), stat_type.end(), stat_type.begin(),[](unsigned char c){ return std::tolower(c); });
struct Stat_config config = {};
try {
if (stat_type == "float") {
config.type = Stat_Type::STAT_TYPE_FLOAT;
config.default_value_float = std::stof(stat_default_value);
} else if (stat_type == "int") {
config.type = Stat_Type::STAT_TYPE_INT;
config.default_value_int = std::stol(stat_default_value);
} else if (stat_type == "avgrate") {
config.type = Stat_Type::STAT_TYPE_AVGRATE;
config.default_value_float = std::stof(stat_default_value);
} else {
PRINT_DEBUG("Error adding stat %s, type %s isn't valid\n", stat_name.c_str(), stat_type.c_str());
continue;
}
} catch (...) {
PRINT_DEBUG("Error adding stat %s, default value %s isn't valid\n", stat_name.c_str(), stat_default_value.c_str());
continue;
}
if (stat_name.size()) {
PRINT_DEBUG("Adding stat type: %s|%u|%f|%u\n", stat_name.c_str(), config.type, config.default_value_float, config.default_value_int);
settings_client->setStatDefiniton(stat_name, config);
settings_server->setStatDefiniton(stat_name, config);
} else {
PRINT_DEBUG("Error adding stat for: %s, empty name\n", stat_name.c_str());
}
}
}
}
{
std::string depots_config_path = Local_Storage::get_game_settings_path() + "depots.txt";
std::ifstream input( depots_config_path );
if (input.is_open()) {
consume_bom(input);
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
DepotId_t depot_id = stoul(line);
settings_client->depots.push_back(depot_id);
settings_server->depots.push_back(depot_id);
PRINT_DEBUG("Added depot %u\n", depot_id);
}
}
}
{
std::string depots_config_path = Local_Storage::get_game_settings_path() + "subscribed_groups.txt";
std::ifstream input( depots_config_path );
if (input.is_open()) {
consume_bom(input);
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.pop_back();
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.pop_back();
}
uint64 source_id = stoull(line);
settings_client->subscribed_groups.insert(source_id);
settings_server->subscribed_groups.insert(source_id);
PRINT_DEBUG("Added source %llu\n", source_id);
}
}
}
{
std::string mod_path = Local_Storage::get_game_settings_path() + "mods";
std::vector<std::string> paths = Local_Storage::get_filenames_path(mod_path);
for (auto & p: paths) {
PRINT_DEBUG("mod directory %s\n", p.c_str());
try {
PublishedFileId_t id = std::stoull(p);
settings_client->addMod(id, p, mod_path + PATH_SEPARATOR + p);
settings_server->addMod(id, p, mod_path + PATH_SEPARATOR + p);
} catch (...) {}
}
}
load_gamecontroller_settings(settings_client);
*settings_client_out = settings_client;
*settings_server_out = settings_server;
*local_storage_out = local_storage;
return appid;
}

27
dll/settings_parser.h Normal file
View File

@ -0,0 +1,27 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "settings.h"
#ifndef SETTINGS_PARSER_INCLUDE
#define SETTINGS_PARSER_INCLUDE
//returns appid
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out);
#endif

View File

@ -20,27 +20,32 @@
uint32 Steam_Applist::GetNumInstalledApps()
{
PRINT_DEBUG("Steam_Applist::GetNumInstalledApps\n");
return 0;
}
uint32 Steam_Applist::GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs )
{
PRINT_DEBUG("Steam_Applist::GetInstalledApps\n");
return 0;
}
// returns -1 if no name was found
int Steam_Applist::GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax )
{
PRINT_DEBUG("Steam_Applist::GetAppName\n");
return -1;
}
// returns -1 if no dir was found
int Steam_Applist::GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax )
{
PRINT_DEBUG("Steam_Applist::GetAppInstallDir\n");
return -1;
}
// return the buildid of this app, may change at any time based on backend updates to the game
int Steam_Applist::GetAppBuildId( AppId_t nAppID )
{
PRINT_DEBUG("Steam_Applist::GetAppBuildId\n");
return 10;
}

View File

@ -181,16 +181,27 @@ bool Steam_Apps::MarkContentCorrupt( bool bMissingFilesOnly )
// return installed depots in mount order
uint32 Steam_Apps::GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots )
{
PRINT_DEBUG("GetInstalledDepots\n");
return 0;
PRINT_DEBUG("GetInstalledDepots %u, %u\n", appID, cMaxDepots);
//TODO not sure about the behavior of this function, I didn't actually test this.
if (!pvecDepots) return 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int count = settings->depots.size();
if (cMaxDepots < count) count = cMaxDepots;
std::copy(settings->depots.begin(), settings->depots.begin() + count, pvecDepots);
return count;
}
uint32 Steam_Apps::GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots )
{
PRINT_DEBUG("GetInstalledDepots old\n");
return GetInstalledDepots( settings->get_local_game_id().AppID(), pvecDepots, cMaxDepots );
}
// returns current app install folder for AppID, returns folder name length
uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize )
{
PRINT_DEBUG("GetAppInstallDir %u %u\n", appID, cchFolderBufferSize);
PRINT_DEBUG("GetAppInstallDir %u %p %u\n", appID, pchFolder, cchFolderBufferSize);
//TODO return real path instead of dll path
if (!pchFolder || !cchFolderBufferSize) return 0;
std::string installed_path = settings->getAppInstallPath(appID);
if (installed_path.size() == 0) {
@ -207,8 +218,11 @@ uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchF
}
PRINT_DEBUG("path %s\n", installed_path.c_str());
snprintf(pchFolder, cchFolderBufferSize, "%s", installed_path.c_str());
return strlen(pchFolder);
if (cchFolderBufferSize && pchFolder) {
snprintf(pchFolder, cchFolderBufferSize, "%s", installed_path.c_str());
}
return installed_path.length(); //Real steam always returns the actual path length, not the copied one.
}
// returns true if that app is installed (not necessarily owned)
@ -248,7 +262,7 @@ bool Steam_Apps::GetDlcDownloadProgress( AppId_t nAppID, uint64 *punBytesDownloa
int Steam_Apps::GetAppBuildId()
{
PRINT_DEBUG("GetAppBuildId\n");
return 1;
return 10;
}
@ -289,3 +303,10 @@ bool Steam_Apps::BIsSubscribedFromFamilySharing()
PRINT_DEBUG("BIsSubscribedFromFamilySharing\n");
return false;
}
// check if game is a timed trial with limited playtime
bool Steam_Apps::BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed )
{
PRINT_DEBUG("BIsTimedTrial\n");
return false;
}

View File

@ -1,6 +1,13 @@
#include "base.h"
class Steam_Apps : public ISteamApps
class Steam_Apps :
public ISteamApps002,
public ISteamApps003,
public ISteamApps004,
public ISteamApps005,
public ISteamApps006,
public ISteamApps007,
public ISteamApps
{
Settings *settings;
class SteamCallResults *callback_results;
@ -49,6 +56,7 @@ public:
bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ); // returns current beta branch name, 'public' is the default branch
bool MarkContentCorrupt( bool bMissingFilesOnly ); // signal Steam that game files seems corrupt or missing
uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ); // return installed depots in mount order
uint32 GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots );
// returns current app install folder for AppID, returns folder name length
uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize );
@ -89,4 +97,7 @@ public:
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
bool BIsSubscribedFromFamilySharing();
// check if game is a timed trial with limited playtime
bool BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed );
};

View File

@ -16,282 +16,41 @@
<http://www.gnu.org/licenses/>. */
#include "steam_client.h"
#include "settings_parser.h"
#include <fstream>
static std::condition_variable kill_background_thread_cv;
static std::atomic_bool kill_background_thread;
static void background_thread(Steam_Client *client)
{
PRINT_DEBUG("background thread starting\n");
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
while (1) {
std::this_thread::sleep_for(std::chrono::seconds(1));
global_mutex.lock();
if (!client->network->isAlive()) {
global_mutex.unlock();
//delete network;
PRINT_DEBUG("background thread exit\n");
return;
if (kill_background_thread || kill_background_thread_cv.wait_for(lck, std::chrono::seconds(1)) != std::cv_status::timeout) {
if (kill_background_thread) {
PRINT_DEBUG("background thread exit\n");
return;
}
}
PRINT_DEBUG("background thread run\n");
client->network->Run();
client->steam_matchmaking->RunBackground();
global_mutex.unlock();
}
}
unsigned long long time = std::chrono::duration_cast<std::chrono::duration<unsigned long long>>(std::chrono::system_clock::now().time_since_epoch()).count();
static void load_custom_broadcasts(std::string broadcasts_filepath, std::set<uint32> &custom_broadcasts)
{
std::ifstream broadcasts_file(broadcasts_filepath);
PRINT_DEBUG("Broadcasts file path: %s\n", broadcasts_filepath.c_str());
if (broadcasts_file.is_open()) {
std::string line;
while (std::getline(broadcasts_file, line)) {
std::set<uint32> ips = Networking::resolve_ip(line);
custom_broadcasts.insert(ips.begin(), ips.end());
if (time > client->last_cb_run + 1) {
global_mutex.lock();
PRINT_DEBUG("background thread run\n");
client->network->Run();
client->run_every_runcb->run();
global_mutex.unlock();
}
}
}
Steam_Client::Steam_Client()
{
std::string program_path = Local_Storage::get_program_path(), save_path = Local_Storage::get_user_appdata_path();;
uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage);
PRINT_DEBUG("Current Path %s save_path: %s\n", program_path.c_str(), save_path.c_str());
char array[10] = {};
array[0] = '0';
Local_Storage::get_file_data(Local_Storage::get_game_settings_path() + "steam_appid.txt", array, sizeof(array) - 1);
uint32 appid = 0;
try {
appid = std::stoi(array);
} catch (...) {}
if (!appid) {
memset(array, 0, sizeof(array));
array[0] = '0';
Local_Storage::get_file_data("steam_appid.txt", array, sizeof(array) - 1);
try {
appid = std::stoi(array);
} catch (...) {}
if (!appid) {
memset(array, 0, sizeof(array));
array[0] = '0';
Local_Storage::get_file_data(program_path + "steam_appid.txt", array, sizeof(array) - 1);
try {
appid = std::stoi(array);
} catch (...) {}
}
}
if (!appid) {
std::string str_appid = get_env_variable("SteamAppId");
std::string str_gameid = get_env_variable("SteamGameId");
PRINT_DEBUG("str_appid %s str_gameid: %s\n", str_appid.c_str(), str_gameid.c_str());
uint32 appid_env = 0;
uint32 gameid_env = 0;
if (str_appid.size() > 0) {
try {
appid_env = std::stoul(str_appid);
} catch (...) {
appid_env = 0;
}
}
if (str_gameid.size() > 0) {
try {
gameid_env = std::stoul(str_gameid);
} catch (...) {
gameid_env = 0;
}
}
PRINT_DEBUG("appid_env %u gameid_env: %u\n", appid_env, gameid_env);
if (appid_env) {
appid = appid_env;
}
if (gameid_env) {
appid = gameid_env;
}
}
{
char array[33] = {};
if (Local_Storage::get_file_data(program_path + "local_save.txt", array, sizeof(array) - 1) != -1) {
save_path = program_path + Settings::sanitize(array);
}
}
PRINT_DEBUG("Set save_path: %s\n", save_path.c_str());
local_storage = new Local_Storage(save_path);
local_storage->setAppId(appid);
// Listen port
char array_port[10] = {};
array_port[0] = '0';
local_storage->get_data_settings("listen_port.txt", array_port, sizeof(array_port) - 1);
uint16 port = std::stoi(array_port);
if (port == 0) {
port = DEFAULT_PORT;
snprintf(array_port, sizeof(array_port), "%hu", port);
local_storage->store_data_settings("listen_port.txt", array_port, strlen(array_port));
}
// Custom broadcasts
std::set<uint32> custom_broadcasts;
load_custom_broadcasts(local_storage->get_global_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
load_custom_broadcasts(Local_Storage::get_game_settings_path() + "custom_broadcasts.txt", custom_broadcasts);
// Acount name
char name[32] = {};
if (local_storage->get_data_settings("account_name.txt", name, sizeof(name) - 1) <= 0) {
strcpy(name, DEFAULT_NAME);
local_storage->store_data_settings("account_name.txt", name, strlen(name));
}
// Language
char language[32] = {};
if (local_storage->get_data_settings("language.txt", language, sizeof(language) - 1) <= 0) {
strcpy(language, DEFAULT_LANGUAGE);
local_storage->store_data_settings("language.txt", language, strlen(language));
}
// Steam ID
char array_steam_id[32] = {};
CSteamID user_id;
uint64 steam_id = 0;
bool generate_new = false;
//try to load steam id from game specific settings folder first
if (local_storage->get_data(SETTINGS_STORAGE_FOLDER, "user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
user_id = CSteamID((uint64)std::atoll(array_steam_id));
if (!user_id.IsValid()) {
generate_new = true;
}
} else {
generate_new = true;
}
if (generate_new) {
generate_new = false;
if (local_storage->get_data_settings("user_steam_id.txt", array_steam_id, sizeof(array_steam_id) - 1) > 0) {
user_id = CSteamID((uint64)std::atoll(array_steam_id));
if (!user_id.IsValid()) {
generate_new = true;
}
} else {
generate_new = true;
}
}
if (generate_new) {
user_id = generate_steam_id_user();
uint64 steam_id = user_id.ConvertToUint64();
char temp_text[32] = {};
snprintf(temp_text, sizeof(temp_text), "%llu", steam_id);
local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text));
}
bool steam_offline_mode = false;
{
std::string steam_settings_path = Local_Storage::get_game_settings_path();
std::vector<std::string> paths = Local_Storage::get_filenames_path(steam_settings_path);
for (auto & p: paths) {
PRINT_DEBUG("steam settings path %s\n", p.c_str());
if (p == "offline.txt") {
steam_offline_mode = true;
}
}
}
settings_client = new Settings(user_id, CGameID(appid), name, language, steam_offline_mode);
settings_server = new Settings(generate_steam_id_server(), CGameID(appid), name, language, steam_offline_mode);
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";
std::ifstream input( dlc_config_path );
if (input.is_open()) {
settings_client->unlockAllDLC(false);
settings_server->unlockAllDLC(false);
PRINT_DEBUG("Locking all DLC\n");
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.erase(line.length()-1);
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.erase(line.length()-1);
}
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
AppId_t appid = stol(line.substr(0, deliminator));
std::string name = line.substr(deliminator + 1);
bool available = true;
if (appid) {
PRINT_DEBUG("Adding DLC: %u|%s| %u\n", appid, name.c_str(), available);
settings_client->addDLC(appid, name, available);
settings_server->addDLC(appid, name, available);
}
}
}
} else {
//unlock all DLC
PRINT_DEBUG("Unlocking all DLC\n");
settings_client->unlockAllDLC(true);
settings_server->unlockAllDLC(true);
}
}
{
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "app_paths.txt";
std::ifstream input( dlc_config_path );
if (input.is_open()) {
for( std::string line; getline( input, line ); ) {
if (!line.empty() && line[line.length()-1] == '\n') {
line.erase(line.length()-1);
}
if (!line.empty() && line[line.length()-1] == '\r') {
line.erase(line.length()-1);
}
std::size_t deliminator = line.find("=");
if (deliminator != 0 && deliminator != std::string::npos && deliminator != line.size()) {
AppId_t appid = stol(line.substr(0, deliminator));
std::string rel_path = line.substr(deliminator + 1);
std::string path = canonical_path(program_path + rel_path);
if (appid) {
if (path.size()) {
PRINT_DEBUG("Adding app path: %u|%s|\n", appid, path.c_str());
settings_client->setAppInstallPath(appid, path);
settings_server->setAppInstallPath(appid, path);
} else {
PRINT_DEBUG("Error adding app path for: %u does this path exist? |%s|\n", appid, rel_path.c_str());
}
}
}
}
}
}
{
std::string mod_path = Local_Storage::get_game_settings_path() + "mods";
std::vector<std::string> paths = Local_Storage::get_filenames_path(mod_path);
for (auto & p: paths) {
PRINT_DEBUG("mod directory %s\n", p.c_str());
try {
PublishedFileId_t id = std::stoull(p);
settings_client->addMod(id, p, mod_path + PATH_SEPARATOR + p);
settings_server->addMod(id, p, mod_path + PATH_SEPARATOR + p);
} catch (...) {}
}
}
network = new Networking(settings_server->get_local_steam_id(), appid, port, &custom_broadcasts);
network = new Networking(settings_server->get_local_steam_id(), appid, settings_server->get_port(), &(settings_server->custom_broadcasts), settings_server->disable_networking);
callback_results_client = new SteamCallResults();
callback_results_server = new SteamCallResults();
@ -299,50 +58,58 @@ Steam_Client::Steam_Client()
callbacks_server = new SteamCallBacks(callback_results_server);
run_every_runcb = new RunEveryRunCB();
PRINT_DEBUG("steam client init: id: %llu server id: %llu appid: %u port: %u \n", user_id.ConvertToUint64(), settings_server->get_local_steam_id().ConvertToUint64(), appid, port);
PRINT_DEBUG("steam client init: id: %llu server id: %llu appid: %u port: %u \n", settings_client->get_local_steam_id().ConvertToUint64(), settings_server->get_local_steam_id().ConvertToUint64(), appid, settings_server->get_port());
steam_overlay = new Steam_Overlay(settings_client, callback_results_client, callbacks_client, run_every_runcb, network);
steam_user = new Steam_User(settings_client, local_storage, network, callback_results_client, callbacks_client);
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_utils = new Steam_Utils(settings_client, callback_results_client);
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
steam_utils = new Steam_Utils(settings_client, callback_results_client, steam_overlay);
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client);
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, steam_overlay);
steam_apps = new Steam_Apps(settings_client, callback_results_client);
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
steam_remote_storage = new Steam_Remote_Storage(local_storage, callback_results_client);
steam_screenshots = new Steam_Screenshots();
steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client);
steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client);
steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client);
steam_controller = new Steam_Controller();
steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb);
steam_ugc = new Steam_UGC(settings_client, callback_results_client, callbacks_client);
steam_applist = new Steam_Applist();
steam_music = new Steam_Music(callbacks_client);
steam_musicremote = new Steam_MusicRemote();
steam_HTMLsurface = new Steam_HTMLsurface(settings_client, network, callback_results_client, callbacks_client);
steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client);
steam_inventory = new Steam_Inventory(settings_client, callback_results_client, callbacks_client, run_every_runcb, local_storage);
steam_video = new Steam_Video();
steam_parental = new Steam_Parental();
steam_networking_sockets = new Steam_Networking_Sockets(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_networking_sockets_serialized = new Steam_Networking_Sockets_Serialized(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_networking_messages = new Steam_Networking_Messages(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_game_coordinator = new Steam_Game_Coordinator(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_networking_utils = new Steam_Networking_Utils(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_unified_messages = new Steam_Unified_Messages(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_game_search = new Steam_Game_Search(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_parties = new Steam_Parties(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_remoteplay = new Steam_RemotePlay(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
steam_tv = new Steam_TV(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
PRINT_DEBUG("client init gameserver\n");
steam_gameserver = new Steam_GameServer(settings_server, network, callbacks_server);
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server);
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server, steam_overlay);
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server);
steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb);
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);
steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server);
steam_gameserver_inventory = new Steam_Inventory(settings_server, callback_results_server, callbacks_server, run_every_runcb, local_storage);
steam_gameserver_ugc = new Steam_UGC(settings_server, callback_results_server, callbacks_server);
steam_gameserver_apps = new Steam_Apps(settings_server, callback_results_server);
steam_gameserver_networking_sockets = new Steam_Networking_Sockets(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_gameserver_networking_sockets_serialized = new Steam_Networking_Sockets_Serialized(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_gameserver_networking_messages = new Steam_Networking_Messages(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_gameserver_game_coordinator = new Steam_Game_Coordinator(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
steam_masterserver_updater = new Steam_Masterserver_Updater(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
last_cb_run = 0;
PRINT_DEBUG("client init end\n");
}
@ -377,6 +144,11 @@ void Steam_Client::serverShutdown()
server_init = false;
}
void Steam_Client::clientShutdown()
{
user_logged_in = false;
}
void Steam_Client::setAppID(uint32 appid)
{
std::lock_guard<std::recursive_mutex> lock(global_mutex);
@ -395,14 +167,24 @@ void Steam_Client::setAppID(uint32 appid)
HSteamPipe Steam_Client::CreateSteamPipe()
{
PRINT_DEBUG("CreateSteamPipe\n");
return CLIENT_STEAM_PIPE;
HSteamPipe pipe = steam_pipe_counter++;
PRINT_DEBUG("creating pipe %i\n", pipe);
steam_pipes[pipe] = Steam_Pipe::NO_USER;
return pipe;
}
// Releases a previously created communications pipe
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
bool Steam_Client::BReleaseSteamPipe( HSteamPipe hSteamPipe )
{
PRINT_DEBUG("BReleaseSteamPipe\n");
PRINT_DEBUG("BReleaseSteamPipe %i\n", hSteamPipe);
if (steam_pipes.count(hSteamPipe)) {
steam_pipes.erase(hSteamPipe);
return true;
}
return false;
}
// connects to an existing global user, failing if none exists
@ -410,7 +192,17 @@ bool Steam_Client::BReleaseSteamPipe( HSteamPipe hSteamPipe )
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamUser Steam_Client::ConnectToGlobalUser( HSteamPipe hSteamPipe )
{
PRINT_DEBUG("ConnectToGlobalUser\n");
PRINT_DEBUG("ConnectToGlobalUser %i\n", hSteamPipe);
if (!steam_pipes.count(hSteamPipe)) {
return 0;
}
userLogIn();
#ifdef EMU_OVERLAY
if(!settings_client->disable_overlay)
steam_overlay->SetupOverlay();
#endif
steam_pipes[hSteamPipe] = Steam_Pipe::CLIENT;
return CLIENT_HSTEAMUSER;
}
@ -418,8 +210,20 @@ HSteamUser Steam_Client::ConnectToGlobalUser( HSteamPipe hSteamPipe )
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType )
{
PRINT_DEBUG("CreateLocalUser\n");
PRINT_DEBUG("CreateLocalUser %p %i\n", phSteamPipe, eAccountType);
//if (eAccountType == k_EAccountTypeIndividual) {
//Is this actually used?
//if (phSteamPipe) *phSteamPipe = CLIENT_STEAM_PIPE;
//return CLIENT_HSTEAMUSER;
//} else { //k_EAccountTypeGameServer
serverInit();
HSteamPipe pipe = CreateSteamPipe();
if (phSteamPipe) *phSteamPipe = pipe;
steam_pipes[pipe] = Steam_Pipe::SERVER;
steamclient_server_inited = true;
return SERVER_HSTEAMUSER;
//}
}
HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe )
@ -432,14 +236,16 @@ HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe )
void Steam_Client::ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser )
{
PRINT_DEBUG("ReleaseUser\n");
if (hUser == SERVER_HSTEAMUSER && steam_pipes.count(hSteamPipe)) {
steamclient_server_inited = false;
}
}
// retrieves the ISteamUser interface associated with the handle
ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamUser %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "SteamUser009") == 0) {
return (ISteamUser *)(void *)(ISteamUser009 *)steam_user;
@ -463,6 +269,8 @@ ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hStea
return (ISteamUser *)(void *)(ISteamUser018 *)steam_user;
} else if (strcmp(pchVersion, "SteamUser019") == 0) {
return (ISteamUser *)(void *)(ISteamUser019 *)steam_user;
} else if (strcmp(pchVersion, "SteamUser020") == 0) {
return (ISteamUser *)(void *)(ISteamUser020 *)steam_user;
} else if (strcmp(pchVersion, STEAMUSER_INTERFACE_VERSION) == 0) {
return (ISteamUser *)(void *)(ISteamUser *)steam_user;
} else {
@ -476,8 +284,8 @@ ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hStea
ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamGameServer %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!server_init) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "SteamGameServer005") == 0) {
return (ISteamGameServer *)(void *)(ISteamGameServer005 *)steam_gameserver;
} else if (strcmp(pchVersion, "SteamGameServer006") == 0) {
@ -492,6 +300,8 @@ ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSte
return (ISteamGameServer *)(void *)(ISteamGameServer010 *)steam_gameserver;
} else if (strcmp(pchVersion, "SteamGameServer011") == 0) {
return (ISteamGameServer *)(void *)(ISteamGameServer011 *)steam_gameserver;
} else if (strcmp(pchVersion, "SteamGameServer012") == 0) {
return (ISteamGameServer *)(void *)(ISteamGameServer012 *)steam_gameserver;
} else if (strcmp(pchVersion, STEAMGAMESERVER_INTERFACE_VERSION) == 0) {
return (ISteamGameServer *)(void *)(ISteamGameServer *)steam_gameserver;
} else {
@ -505,15 +315,19 @@ ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSte
// this must be set before CreateLocalUser()
void Steam_Client::SetLocalIPBinding( uint32 unIP, uint16 usPort )
{
PRINT_DEBUG("SetLocalIPBinding %u %hu\n", unIP, usPort);
PRINT_DEBUG("SetLocalIPBinding old %u %hu\n", unIP, usPort);
}
void Steam_Client::SetLocalIPBinding( const SteamIPAddress_t &unIP, uint16 usPort )
{
PRINT_DEBUG("SetLocalIPBinding %i %u %hu\n", unIP.m_eType, unIP.m_unIPv4, usPort);
}
// returns the ISteamFriends interface
ISteamFriends *Steam_Client::GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamFriends %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "SteamFriends004") == 0) {
return (ISteamFriends *)(void *)(ISteamFriends004 *)steam_friends;
@ -554,14 +368,13 @@ ISteamFriends *Steam_Client::GetISteamFriends( HSteamUser hSteamUser, HSteamPipe
ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamUtils %s\n", pchVersion);
if (!hSteamPipe) return NULL;
if (!steam_pipes.count(hSteamPipe)) return NULL;
Steam_Utils *steam_utils_temp;
if (hSteamPipe == SERVER_STEAM_PIPE) {
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_utils_temp = steam_gameserver_utils;
} else {
if (!user_logged_in) return NULL;
steam_utils_temp = steam_utils;
}
@ -579,6 +392,8 @@ ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pc
return (ISteamUtils *)(void *)(ISteamUtils007 *)steam_utils_temp;
} else if (strcmp(pchVersion, "SteamUtils008") == 0) {
return (ISteamUtils *)(void *)(ISteamUtils008 *)steam_utils_temp;
} else if (strcmp(pchVersion, "SteamUtils009") == 0) {
return (ISteamUtils *)(void *)(ISteamUtils009 *)steam_utils_temp;
} else if (strcmp(pchVersion, STEAMUTILS_INTERFACE_VERSION) == 0) {
return (ISteamUtils *)(void *)(ISteamUtils *)steam_utils_temp;
} else {
@ -592,8 +407,7 @@ ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pc
ISteamMatchmaking *Steam_Client::GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamMatchmaking %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "SteamMatchMaking001") == 0) {
//TODO
@ -630,8 +444,7 @@ ISteamMatchmaking *Steam_Client::GetISteamMatchmaking( HSteamUser hSteamUser, HS
ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamMatchmakingServers %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_matchmaking_servers;
}
@ -639,14 +452,14 @@ ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser
void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamGenericInterface %s\n", pchVersion);
if (!hSteamPipe) return NULL;
if (!steam_pipes.count(hSteamPipe)) return NULL;
bool server = false;
if (hSteamUser == SERVER_HSTEAMUSER) {
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
server = true;
} else {
if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) {
if (!user_logged_in) return NULL;
if (!hSteamUser) return NULL;
}
}
@ -662,8 +475,10 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
return (void *)(ISteamNetworkingSocketsSerialized002 *)steam_networking_sockets_serialized_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSocketsSerialized003") == 0) {
return (void *)(ISteamNetworkingSocketsSerialized003 *)steam_networking_sockets_serialized_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSocketsSerialized004") == 0) {
return (void *)(ISteamNetworkingSocketsSerialized004 *)steam_networking_sockets_serialized_temp;
} else {
return (void *)(ISteamNetworkingSocketsSerialized003 *)steam_networking_sockets_serialized_temp;
return (void *)(ISteamNetworkingSocketsSerialized004 *)steam_networking_sockets_serialized_temp;
}
} else if (strstr(pchVersion, "SteamNetworkingSockets") == pchVersion) {
Steam_Networking_Sockets *steam_networking_sockets_temp;
@ -675,9 +490,26 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
if (strcmp(pchVersion, "SteamNetworkingSockets001") == 0) {
return (void *)(ISteamNetworkingSockets001 *) steam_networking_sockets_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSockets002") == 0) {
return (void *)(ISteamNetworkingSockets002 *) steam_networking_sockets_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSockets003") == 0) {
return (void *)(ISteamNetworkingSockets003 *) steam_networking_sockets_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSockets006") == 0) {
return (void *)(ISteamNetworkingSockets006 *) steam_networking_sockets_temp;
} else if (strcmp(pchVersion, "SteamNetworkingSockets008") == 0) {
return (void *)(ISteamNetworkingSockets008 *) steam_networking_sockets_temp;
} else {
return (void *)(ISteamNetworkingSockets *) steam_networking_sockets_temp;
}
} else if (strstr(pchVersion, "SteamNetworkingMessages") == pchVersion) {
Steam_Networking_Messages *steam_networking_messages_temp;
if (server) {
steam_networking_messages_temp = steam_gameserver_networking_messages;
} else {
steam_networking_messages_temp = steam_networking_messages;
}
return (void *)(ISteamNetworkingMessages *)steam_networking_messages_temp;
} else if (strstr(pchVersion, "SteamGameCoordinator") == pchVersion) {
Steam_Game_Coordinator *steam_game_coordinator_temp;
if (server) {
@ -687,8 +519,14 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
}
return (void *)(ISteamGameCoordinator *)steam_game_coordinator_temp;
} else if (strstr(pchVersion, STEAMTV_INTERFACE_VERSION) == pchVersion) {
return (void *)(ISteamTV *)steam_tv;
} else if (strstr(pchVersion, "SteamNetworkingUtils") == pchVersion) {
if (strcmp(pchVersion, "SteamNetworkingUtils001") == 0) {
return (void *)(ISteamNetworkingUtils001 *)steam_networking_utils;
} else if (strcmp(pchVersion, "SteamNetworkingUtils002") == 0) {
return (void *)(ISteamNetworkingUtils002 *)steam_networking_utils;
} else if (strcmp(pchVersion, STEAMNETWORKINGUTILS_INTERFACE_VERSION) == 0) {
return (void *)(ISteamNetworkingUtils *)steam_networking_utils;
} else {
return (void *)(ISteamNetworkingUtils *)steam_networking_utils;
@ -699,8 +537,6 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
return GetISteamGameServerStats(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "SteamMatchMakingServers") == pchVersion) {
return GetISteamMatchmakingServers(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "STEAMREMOTESTORAGE_INTERFACE_VERSION") == pchVersion) {
return GetISteamRemoteStorage(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "SteamFriends") == pchVersion) {
return GetISteamFriends(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "SteamMatchMaking") == pchVersion) {
@ -747,6 +583,8 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
return GetISteamParties(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "SteamInput") == pchVersion) {
return GetISteamInput(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "STEAMREMOTEPLAY_INTERFACE_VERSION") == pchVersion) {
return GetISteamRemotePlay(hSteamUser, hSteamPipe, pchVersion);
} else if (strstr(pchVersion, "STEAMPARENTALSETTINGS_INTERFACE_VERSION") == pchVersion) {
return GetISteamParentalSettings(hSteamUser, hSteamPipe, pchVersion);
} else {
@ -760,8 +598,7 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamUserStats %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION001") == 0) {
//TODO
@ -785,6 +622,8 @@ ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteam
return (ISteamUserStats *)(void *)(ISteamUserStats009 *)steam_user_stats;
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION010") == 0) {
return (ISteamUserStats *)(void *)(ISteamUserStats010 *)steam_user_stats;
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION011") == 0) {
return (ISteamUserStats *)(void *)(ISteamUserStats011 *)steam_user_stats;
} else if (strcmp(pchVersion, STEAMUSERSTATS_INTERFACE_VERSION) == 0) {
return (ISteamUserStats *)(void *)(ISteamUserStats *)steam_user_stats;
} else {
@ -798,7 +637,7 @@ ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteam
ISteamGameServerStats *Steam_Client::GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamGameServerStats %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_gameserverstats;
}
@ -806,12 +645,33 @@ ISteamGameServerStats *Steam_Client::GetISteamGameServerStats( HSteamUser hSteam
ISteamApps *Steam_Client::GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamApps %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (hSteamUser == SERVER_HSTEAMUSER) {
return steam_gameserver_apps;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
Steam_Apps *steam_apps_temp;
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_apps_temp = steam_gameserver_apps;
} else {
steam_apps_temp = steam_apps;
}
if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION002") == 0) {
return (ISteamApps *)(void *)(ISteamApps002 *)steam_apps_temp;
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION003") == 0) {
return (ISteamApps *)(void *)(ISteamApps003 *)steam_apps_temp;
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION004") == 0) {
return (ISteamApps *)(void *)(ISteamApps004 *)steam_apps_temp;
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION005") == 0) {
return (ISteamApps *)(void *)(ISteamApps005 *)steam_apps_temp;
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION006") == 0) {
return (ISteamApps *)(void *)(ISteamApps006 *)steam_apps_temp;
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION007") == 0) {
return (ISteamApps *)(void *)(ISteamApps007 *)steam_apps_temp;
} else if (strcmp(pchVersion, STEAMAPPS_INTERFACE_VERSION) == 0) {
return (ISteamApps *)(void *)(ISteamApps *)steam_apps_temp;
} else {
return (ISteamApps *)(void *)(ISteamApps *)steam_apps_temp;
}
if (!user_logged_in) return NULL;
return steam_apps;
}
@ -819,14 +679,13 @@ ISteamApps *Steam_Client::GetISteamApps( HSteamUser hSteamUser, HSteamPipe hStea
ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamNetworking %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
Steam_Networking *steam_networking_temp;
if (hSteamUser == SERVER_HSTEAMUSER) {
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_networking_temp = steam_gameserver_networking;
} else {
if (!user_logged_in) return NULL;
steam_networking_temp = steam_networking;
}
@ -838,7 +697,9 @@ ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSte
return (ISteamNetworking *)(void *)(ISteamNetworking003 *)steam_networking_temp;
} else if (strcmp(pchVersion, "SteamNetworking004") == 0) {
return (ISteamNetworking *)(void *)(ISteamNetworking004 *)steam_networking_temp;
} else if (strcmp(pchVersion, STEAMUGC_INTERFACE_VERSION) == 0) {
} else if (strcmp(pchVersion, "SteamNetworking005") == 0) {
return (ISteamNetworking *)(void *)(ISteamNetworking005 *)steam_networking_temp;
} else if (strcmp(pchVersion, STEAMNETWORKING_INTERFACE_VERSION) == 0) {
return (ISteamNetworking *)(void *)(ISteamNetworking *)steam_networking_temp;
} else {
return (ISteamNetworking *)(void *)(ISteamNetworking *)steam_networking_temp;
@ -851,8 +712,7 @@ ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSte
ISteamRemoteStorage *Steam_Client::GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamRemoteStorage %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
if (strcmp(pchVersion, "STEAMREMOTESTORAGE_INTERFACE_VERSION001") == 0) {
return (ISteamRemoteStorage *)(void *)(ISteamRemoteStorage001 *)steam_remote_storage;
@ -893,8 +753,7 @@ ISteamRemoteStorage *Steam_Client::GetISteamRemoteStorage( HSteamUser hSteamuser
ISteamScreenshots *Steam_Client::GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamScreenshots %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_screenshots;
}
@ -902,7 +761,7 @@ ISteamScreenshots *Steam_Client::GetISteamScreenshots( HSteamUser hSteamuser, HS
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
void Steam_Client::RunFrame()
{
PRINT_DEBUG("RunFrame\n");
PRINT_DEBUG("Steam_Client::RunFrame\n");
}
// returns the number of IPC calls made since the last time this function was called
@ -928,19 +787,29 @@ void Steam_Client::SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction
bool Steam_Client::BShutdownIfAllPipesClosed()
{
PRINT_DEBUG("BShutdownIfAllPipesClosed\n");
return true;
if (!steam_pipes.size()) {
if (background_keepalive.joinable()) {
kill_background_thread = true;
kill_background_thread_cv.notify_one();
background_keepalive.join();
}
PRINT_DEBUG("all pipes closed\n");
return true;
}
return false;
}
// Expose HTTP interface
ISteamHTTP *Steam_Client::GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamHTTP %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (hSteamuser == SERVER_HSTEAMUSER) {
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
return steam_gameserver_http;
}
if (!user_logged_in) return NULL;
return steam_http;
}
@ -948,14 +817,14 @@ ISteamHTTP *Steam_Client::GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hStea
void *Steam_Client::DEPRECATED_GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("DEPRECATED_GetISteamUnifiedMessages %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return (void *)(ISteamUnifiedMessages *)steam_unified_messages;
}
ISteamUnifiedMessages *Steam_Client::GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamUnifiedMessages %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_unified_messages;
}
@ -963,8 +832,7 @@ ISteamUnifiedMessages *Steam_Client::GetISteamUnifiedMessages( HSteamUser hSteam
ISteamController *Steam_Client::GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamController %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
if (strcmp(pchVersion, "STEAMCONTROLLER_INTERFACE_VERSION") == 0) {
return (ISteamController *)(void *)(ISteamController001 *)steam_controller;
@ -992,13 +860,12 @@ ISteamController *Steam_Client::GetISteamController( HSteamUser hSteamUser, HSte
ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamUGC %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
Steam_UGC *steam_ugc_temp;
if (hSteamUser == SERVER_HSTEAMUSER) {
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_ugc_temp = steam_gameserver_ugc;
} else {
if (!user_logged_in) return NULL;
steam_ugc_temp = steam_ugc;
}
@ -1026,8 +893,12 @@ ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamP
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION010") == 0) {
return (ISteamUGC *)(void *)(ISteamUGC010 *)steam_ugc_temp;
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION011") == 0) {
//TODO
return (ISteamUGC *)(void *)(ISteamUGC *)steam_ugc_temp;
//TODO ?
return (ISteamUGC *)(void *)(ISteamUGC012 *)steam_ugc_temp;
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION012") == 0) {
return (ISteamUGC *)(void *)(ISteamUGC012 *)steam_ugc_temp;
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION013") == 0) {
return (ISteamUGC *)(void *)(ISteamUGC013 *)steam_ugc_temp;
} else if (strcmp(pchVersion, STEAMUGC_INTERFACE_VERSION) == 0) {
return (ISteamUGC *)(void *)(ISteamUGC *)steam_ugc_temp;
} else {
@ -1041,8 +912,7 @@ ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamP
ISteamAppList *Steam_Client::GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamAppList %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_applist;
}
@ -1050,8 +920,7 @@ ISteamAppList *Steam_Client::GetISteamAppList( HSteamUser hSteamUser, HSteamPipe
ISteamMusic *Steam_Client::GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamMusic %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_music;
}
@ -1059,8 +928,7 @@ ISteamMusic *Steam_Client::GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSt
ISteamMusicRemote *Steam_Client::GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion)
{
PRINT_DEBUG("GetISteamMusicRemote %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_musicremote;
}
@ -1068,8 +936,7 @@ ISteamMusicRemote *Steam_Client::GetISteamMusicRemote(HSteamUser hSteamuser, HSt
ISteamHTMLSurface *Steam_Client::GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion)
{
PRINT_DEBUG("GetISteamHTMLSurface %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
if (strcmp(pchVersion, "STEAMHTMLSURFACE_INTERFACE_VERSION_001") == 0) {
return (ISteamHTMLSurface *)(void *)(ISteamHTMLSurface001 *)steam_HTMLsurface;
@ -1118,16 +985,15 @@ void Steam_Client::Remove_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResu
ISteamInventory *Steam_Client::GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamInventory %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
Steam_Inventory *steam_inventory_temp;
Settings *settings_temp;
SteamCallBacks *callbacks_temp;
SteamCallResults *callback_results_temp;
if (hSteamuser == SERVER_HSTEAMUSER) {
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
steam_inventory_temp = steam_gameserver_inventory;
} else {
if (!user_logged_in) return NULL;
steam_inventory_temp = steam_inventory;
}
@ -1148,8 +1014,7 @@ ISteamInventory *Steam_Client::GetISteamInventory( HSteamUser hSteamuser, HSteam
ISteamVideo *Steam_Client::GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamVideo %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_video;
}
@ -1157,22 +1022,21 @@ ISteamVideo *Steam_Client::GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSt
ISteamParentalSettings *Steam_Client::GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamParentalSettings %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_parental;
}
ISteamMasterServerUpdater *Steam_Client::GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamMasterServerUpdater %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_masterserver_updater;
}
ISteamContentServer *Steam_Client::GetISteamContentServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamContentServer %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return NULL;
}
@ -1180,8 +1044,7 @@ ISteamContentServer *Steam_Client::GetISteamContentServer( HSteamUser hSteamUser
ISteamGameSearch *Steam_Client::GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamGameSearch %s\n", pchVersion);
if (!hSteamPipe || !hSteamuser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
return steam_game_search;
}
@ -1190,8 +1053,7 @@ ISteamGameSearch *Steam_Client::GetISteamGameSearch( HSteamUser hSteamuser, HSte
ISteamInput *Steam_Client::GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamInput %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_controller;
}
@ -1200,12 +1062,19 @@ ISteamInput *Steam_Client::GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSt
ISteamParties *Steam_Client::GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamParties %s\n", pchVersion);
if (!hSteamPipe || !hSteamUser) return NULL;
if (!user_logged_in) return NULL;
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_parties;
}
ISteamRemotePlay *Steam_Client::GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
{
PRINT_DEBUG("GetISteamRemotePlay %s\n", pchVersion);
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
return steam_remoteplay;
}
void Steam_Client::RegisterCallback( class CCallbackBase *pCallback, int iCallback)
{
int base_callback = (iCallback / 100) * 100;
@ -1696,6 +1565,11 @@ void Steam_Client::RunCallbacks(bool runClientCB, bool runGameserverCB)
callbacks_server->runCallBacks();
PRINT_DEBUG("Steam_Client::RunCallbacks callbacks_client\n");
callbacks_client->runCallBacks();
last_cb_run = std::chrono::duration_cast<std::chrono::duration<unsigned long long>>(std::chrono::system_clock::now().time_since_epoch()).count();
PRINT_DEBUG("Steam_Client::RunCallbacks done\n");
}
void Steam_Client::DestroyAllInterfaces()
{
PRINT_DEBUG("Steam_Client::DestroyAllInterfaces\n");
}

View File

@ -27,7 +27,11 @@
#include "steam_remote_storage.h"
#include "steam_screenshots.h"
#include "steam_http.h"
#ifdef CONTROLLER_SUPPORT
#include "steam_controller.h"
#else
#include "steam_controller_disabled.h"
#endif
#include "steam_ugc.h"
#include "steam_applist.h"
#include "steam_music.h"
@ -39,16 +43,25 @@
#include "steam_game_coordinator.h"
#include "steam_networking_socketsserialized.h"
#include "steam_networking_sockets.h"
#include "steam_networking_messages.h"
#include "steam_networking_utils.h"
#include "steam_unified_messages.h"
#include "steam_gamesearch.h"
#include "steam_parties.h"
#include "steam_remoteplay.h"
#include "steam_tv.h"
#include "steam_gameserver.h"
#include "steam_gameserverstats.h"
#include "steam_masterserver_updater.h"
#include <thread>
#include "../overlay_experimental/steam_overlay.h"
enum Steam_Pipe {
NO_USER,
CLIENT,
SERVER
};
class Steam_Client :
public ISteamClient007,
@ -62,6 +75,8 @@ public ISteamClient014,
public ISteamClient015,
public ISteamClient016,
public ISteamClient017,
public ISteamClient018,
public ISteamClient019,
public ISteamClient
{
public:
@ -94,11 +109,14 @@ public:
Steam_Parental *steam_parental;
Steam_Networking_Sockets *steam_networking_sockets;
Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized;
Steam_Networking_Messages *steam_networking_messages;
Steam_Game_Coordinator *steam_game_coordinator;
Steam_Networking_Utils *steam_networking_utils;
Steam_Unified_Messages *steam_unified_messages;
Steam_Game_Search *steam_game_search;
Steam_Parties *steam_parties;
Steam_RemotePlay *steam_remoteplay;
Steam_TV *steam_tv;
Steam_GameServer *steam_gameserver;
Steam_Utils *steam_gameserver_utils;
@ -110,12 +128,20 @@ public:
Steam_Apps *steam_gameserver_apps;
Steam_Networking_Sockets *steam_gameserver_networking_sockets;
Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized;
Steam_Networking_Messages *steam_gameserver_networking_messages;
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
Steam_Masterserver_Updater *steam_masterserver_updater;
Steam_Overlay* steam_overlay;
bool user_logged_in = false;
bool server_init = false;
std::thread background_keepalive;
bool steamclient_server_inited = false;
std::atomic<unsigned long long> last_cb_run;
unsigned steam_pipe_counter = 1;
std::map<HSteamPipe, enum Steam_Pipe> steam_pipes;
Steam_Client();
~Steam_Client();
@ -150,6 +176,7 @@ public:
// set the local IP and Port to bind to
// this must be set before CreateLocalUser()
void SetLocalIPBinding( uint32 unIP, uint16 usPort );
void SetLocalIPBinding( const SteamIPAddress_t &unIP, uint16 usPort );
// returns the ISteamFriends interface
ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
@ -257,6 +284,9 @@ public:
// Steam Parties interface
ISteamParties *GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
// Steam Remote Play interface
ISteamRemotePlay *GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
void RegisterCallback( class CCallbackBase *pCallback, int iCallback);
void UnregisterCallback( class CCallbackBase *pCallback);
@ -268,6 +298,9 @@ public:
void userLogIn();
void serverInit();
void serverShutdown();
void clientShutdown();
bool IsServerInit();
bool IsUserLogIn();
void DestroyAllInterfaces();
};

View File

@ -17,6 +17,55 @@
#include "base.h"
struct Controller_Map {
std::map<ControllerDigitalActionHandle_t, std::set<int>> active_digital;
std::map<ControllerAnalogActionHandle_t, std::pair<std::set<int>, enum EInputSourceMode>> active_analog;
};
struct Controller_Action {
ControllerHandle_t controller_handle;
struct Controller_Map active_map;
ControllerDigitalActionHandle_t active_set;
Controller_Action(ControllerHandle_t controller_handle) {
this->controller_handle = controller_handle;
}
void activate_action_set(ControllerDigitalActionHandle_t active_set, std::map<ControllerActionSetHandle_t, struct Controller_Map> &controller_maps) {
auto map = controller_maps.find(active_set);
if (map == controller_maps.end()) return;
this->active_set = active_set;
this->active_map = map->second;
}
std::set<int> button_id(ControllerDigitalActionHandle_t handle) {
auto a = active_map.active_digital.find(handle);
if (a == active_map.active_digital.end()) return {};
return a->second;
}
std::pair<std::set<int>, enum EInputSourceMode> analog_id(ControllerAnalogActionHandle_t handle) {
auto a = active_map.active_analog.find(handle);
if (a == active_map.active_analog.end()) return std::pair<std::set<int>, enum EInputSourceMode>({}, k_EInputSourceMode_None);
return a->second;
}
};
enum EXTRA_GAMEPAD_BUTTONS {
BUTTON_LTRIGGER = BUTTON_COUNT + 1,
BUTTON_RTRIGGER = BUTTON_COUNT + 2,
BUTTON_STICK_LEFT_UP = BUTTON_COUNT + 3,
BUTTON_STICK_LEFT_DOWN = BUTTON_COUNT + 4,
BUTTON_STICK_LEFT_LEFT = BUTTON_COUNT + 5,
BUTTON_STICK_LEFT_RIGHT = BUTTON_COUNT + 6,
BUTTON_STICK_RIGHT_UP = BUTTON_COUNT + 7,
BUTTON_STICK_RIGHT_DOWN = BUTTON_COUNT + 8,
BUTTON_STICK_RIGHT_LEFT = BUTTON_COUNT + 9,
BUTTON_STICK_RIGHT_RIGHT = BUTTON_COUNT + 10,
};
#define JOY_ID_START 10
class Steam_Controller :
public ISteamController001,
public ISteamController003,
@ -26,12 +75,177 @@ public ISteamController006,
public ISteamController,
public ISteamInput
{
class Settings *settings;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::map<std::string, int> button_strings = {
{"DUP", BUTTON_DPAD_UP},
{"DDOWN", BUTTON_DPAD_DOWN},
{"DLEFT", BUTTON_DPAD_LEFT},
{"DRIGHT", BUTTON_DPAD_RIGHT},
{"START", BUTTON_START},
{"BACK", BUTTON_BACK},
{"LSTICK", BUTTON_LEFT_THUMB},
{"RSTICK", BUTTON_RIGHT_THUMB},
{"LBUMPER", BUTTON_LEFT_SHOULDER},
{"RBUMPER", BUTTON_RIGHT_SHOULDER},
{"A", BUTTON_A},
{"B", BUTTON_B},
{"X", BUTTON_X},
{"Y", BUTTON_Y},
{"DLTRIGGER", BUTTON_LTRIGGER},
{"DRTRIGGER", BUTTON_RTRIGGER},
{"DLJOYUP", BUTTON_STICK_LEFT_UP},
{"DLJOYDOWN", BUTTON_STICK_LEFT_DOWN},
{"DLJOYLEFT", BUTTON_STICK_LEFT_LEFT},
{"DLJOYRIGHT", BUTTON_STICK_LEFT_RIGHT},
{"DRJOYUP", BUTTON_STICK_RIGHT_UP},
{"DRJOYDOWN", BUTTON_STICK_RIGHT_DOWN},
{"DRJOYLEFT", BUTTON_STICK_RIGHT_LEFT},
{"DRJOYRIGHT", BUTTON_STICK_RIGHT_RIGHT},
};
std::map<std::string, int> analog_strings = {
{"LTRIGGER", TRIGGER_LEFT},
{"RTRIGGER", TRIGGER_RIGHT},
{"LJOY", STICK_LEFT + JOY_ID_START},
{"RJOY", STICK_RIGHT + JOY_ID_START},
};
std::map<std::string, enum EInputSourceMode> analog_input_modes = {
{"joystick_move", k_EInputSourceMode_JoystickMove},
{"joystick_camera", k_EInputSourceMode_JoystickCamera},
{"trigger", k_EInputSourceMode_Trigger},
};
std::map<std::string, ControllerActionSetHandle_t> action_handles;
std::map<std::string, ControllerDigitalActionHandle_t> digital_action_handles;
std::map<std::string, ControllerAnalogActionHandle_t> analog_action_handles;
std::map<ControllerActionSetHandle_t, struct Controller_Map> controller_maps;
std::map<ControllerHandle_t, struct Controller_Action> controllers;
std::map<EInputActionOrigin, std::string> steaminput_glyphs;
std::map<EControllerActionOrigin, std::string> steamcontroller_glyphs;
bool disabled;
bool initialized;
void set_handles(std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_sets) {
uint64 handle_num = 1;
for (auto & set : action_sets) {
ControllerActionSetHandle_t action_handle_num = handle_num;
++handle_num;
action_handles[set.first] = action_handle_num;
for (auto & config_key : set.second) {
uint64 current_handle_num = handle_num;
++handle_num;
for (auto & button_string : config_key.second.first) {
auto digital = button_strings.find(button_string);
if (digital != button_strings.end()) {
ControllerDigitalActionHandle_t digital_handle_num = current_handle_num;
if (digital_action_handles.find(config_key.first) == digital_action_handles.end()) {
digital_action_handles[config_key.first] = digital_handle_num;
} else {
digital_handle_num = digital_action_handles[config_key.first];
}
controller_maps[action_handle_num].active_digital[digital_handle_num].insert(digital->second);
} else {
auto analog = analog_strings.find(button_string);
if (analog != analog_strings.end()) {
ControllerAnalogActionHandle_t analog_handle_num = current_handle_num;
enum EInputSourceMode source_mode;
if (analog->second == TRIGGER_LEFT || analog->second == TRIGGER_RIGHT) {
source_mode = k_EInputSourceMode_Trigger;
} else {
source_mode = k_EInputSourceMode_JoystickMove;
}
auto input_mode = analog_input_modes.find(config_key.second.second);
if (input_mode != analog_input_modes.end()) {
source_mode = input_mode->second;
}
if (analog_action_handles.find(config_key.first) == analog_action_handles.end()) {
analog_action_handles[config_key.first] = analog_handle_num;
} else {
analog_handle_num = analog_action_handles[config_key.first];
}
controller_maps[action_handle_num].active_analog[analog_handle_num].first.insert(analog->second);
controller_maps[action_handle_num].active_analog[analog_handle_num].second = source_mode;
} else {
PRINT_DEBUG("Did not recognize controller button %s\n", button_string.c_str());
continue;
}
}
}
}
}
}
public:
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_controller_run_every_runcb\n");
Steam_Controller *steam_controller = (Steam_Controller *)object;
steam_controller->RunCallbacks();
}
Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->run_every_runcb = run_every_runcb;
this->run_every_runcb->add(&Steam_Controller::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
set_handles(settings->controller_settings.action_sets);
disabled = !action_handles.size();
initialized = false;
}
~Steam_Controller()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Controller::steam_run_every_runcb, this);
}
// Init and Shutdown must be called when starting/ending use of this interface
bool Init()
{
PRINT_DEBUG("Steam_Controller::Init()\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (disabled) {
return true;
}
GamepadInit();
GamepadUpdate();
for (int i = 1; i < 5; ++i) {
struct Controller_Action cont_action(i);
//Activate the action set if there is only one present.
//TODO: I don't know if one gets activated by default when there's more than one
if (action_handles.size() == 1) {
cont_action.activate_action_set(action_handles.begin()->second, controller_maps);
}
controllers.insert(std::pair<ControllerHandle_t, struct Controller_Action>(i, cont_action));
}
initialized = true;
return true;
}
@ -44,6 +258,12 @@ bool Init( const char *pchAbsolutePathToControllerConfigVDF )
bool Shutdown()
{
PRINT_DEBUG("Steam_Controller::Shutdown()\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (disabled) {
return true;
}
GamepadShutdown();
return true;
}
@ -58,6 +278,11 @@ void SetOverrideMode( const char *pchMode )
void RunFrame()
{
PRINT_DEBUG("Steam_Controller::RunFrame()\n");
if (disabled || !initialized) {
return;
}
GamepadUpdate();
}
bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState )
@ -71,8 +296,20 @@ bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pS
// Returns the number of handles written to handlesOut
int GetConnectedControllers( ControllerHandle_t *handlesOut )
{
PRINT_DEBUG("GetConnectedControllers\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetConnectedControllers\n");
if (!handlesOut) return 0;
if (disabled) {
return 0;
}
int count = 0;
if (GamepadIsConnected(GAMEPAD_0)) {*handlesOut = GAMEPAD_0 + 1; ++handlesOut; ++count;};
if (GamepadIsConnected(GAMEPAD_1)) {*handlesOut = GAMEPAD_1 + 1; ++handlesOut; ++count;};
if (GamepadIsConnected(GAMEPAD_2)) {*handlesOut = GAMEPAD_2 + 1; ++handlesOut; ++count;};
if (GamepadIsConnected(GAMEPAD_3)) {*handlesOut = GAMEPAD_3 + 1; ++handlesOut; ++count;};
PRINT_DEBUG("returned %i connected controllers\n", count);
return count;
}
@ -80,7 +317,7 @@ int GetConnectedControllers( ControllerHandle_t *handlesOut )
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowBindingPanel( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("ShowBindingPanel\n");
PRINT_DEBUG("Steam_Controller::ShowBindingPanel\n");
return false;
}
@ -89,8 +326,15 @@ bool ShowBindingPanel( ControllerHandle_t controllerHandle )
// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls.
ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName )
{
PRINT_DEBUG("GetActionSetHandle %s\n", pszActionSetName);
return 124;
PRINT_DEBUG("Steam_Controller::GetActionSetHandle %s\n", pszActionSetName);
if (!pszActionSetName) return 0;
std::string upper_action_name(pszActionSetName);
std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });
auto set_handle = action_handles.find(upper_action_name);
if (set_handle == action_handles.end()) return 0;
return set_handle->second;
}
@ -99,34 +343,47 @@ ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName )
// your state loops, instead of trying to place it in all of your state transitions.
void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle )
{
PRINT_DEBUG("ActivateActionSet\n");
PRINT_DEBUG("Steam_Controller::ActivateActionSet %llu %llu\n", controllerHandle, actionSetHandle);
if (controllerHandle == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS) {
for (auto & c: controllers) {
c.second.activate_action_set(actionSetHandle, controller_maps);
}
}
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return;
controller->second.activate_action_set(actionSetHandle, controller_maps);
}
ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetCurrentActionSet\n");
return 124;
PRINT_DEBUG("Steam_Controller::GetCurrentActionSet %llu\n", controllerHandle);
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return 0;
return controller->second.active_set;
}
void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("ActivateActionSetLayer\n");
PRINT_DEBUG("Steam_Controller::ActivateActionSetLayer\n");
}
void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("DeactivateActionSetLayer\n");
PRINT_DEBUG("Steam_Controller::DeactivateActionSetLayer\n");
}
void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("DeactivateAllActionSetLayers\n");
PRINT_DEBUG("Steam_Controller::DeactivateAllActionSetLayers\n");
}
int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut )
{
PRINT_DEBUG("GetActiveActionSetLayers\n");
PRINT_DEBUG("Steam_Controller::GetActiveActionSetLayers\n");
return 0;
}
@ -136,17 +393,72 @@ int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerAct
// Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls.
ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetDigitalActionHandle %s\n", pszActionName);
return 123;
PRINT_DEBUG("Steam_Controller::GetDigitalActionHandle %s\n", pszActionName);
if (!pszActionName) return 0;
std::string upper_action_name(pszActionName);
std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });
auto handle = digital_action_handles.find(upper_action_name);
if (handle == digital_action_handles.end()) return 0;
return handle->second;
}
// Returns the current state of the supplied digital game action
ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle )
{
PRINT_DEBUG("GetDigitalActionData\n");
PRINT_DEBUG("Steam_Controller::GetDigitalActionData %llu %llu\n", controllerHandle, digitalActionHandle);
ControllerDigitalActionData_t digitalData;
digitalData.bActive = false;
digitalData.bState = false;
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return digitalData;
std::set<int> buttons = controller->second.button_id(digitalActionHandle);
if (!buttons.size()) return digitalData;
digitalData.bActive = true;
GAMEPAD_DEVICE device = (GAMEPAD_DEVICE)(controllerHandle - 1);
for (auto button : buttons) {
bool pressed = false;
if (button < BUTTON_COUNT) {
pressed = GamepadButtonDown(device, (GAMEPAD_BUTTON)button);
} else {
switch (button) {
case BUTTON_LTRIGGER:
pressed = GamepadTriggerLength(device, TRIGGER_LEFT) > 0.8;
break;
case BUTTON_RTRIGGER:
pressed = GamepadTriggerLength(device, TRIGGER_RIGHT) > 0.8;
break;
case BUTTON_STICK_LEFT_UP:
case BUTTON_STICK_LEFT_DOWN:
case BUTTON_STICK_LEFT_LEFT:
case BUTTON_STICK_LEFT_RIGHT:
pressed = GamepadStickLength(device, STICK_LEFT) > 0.1 &&
((int)GamepadStickDir(device, STICK_LEFT) == ((button - BUTTON_STICK_LEFT_UP) + 1));
break;
case BUTTON_STICK_RIGHT_UP:
case BUTTON_STICK_RIGHT_DOWN:
case BUTTON_STICK_RIGHT_LEFT:
case BUTTON_STICK_RIGHT_RIGHT:
pressed = GamepadStickLength(device, STICK_RIGHT) > 0.1 &&
((int)GamepadStickDir(device, STICK_RIGHT) == ((button - BUTTON_STICK_RIGHT_UP) + 1));
break;
default:
break;
}
}
if (pressed) {
digitalData.bState = true;
break;
}
}
return digitalData;
}
@ -155,32 +467,170 @@ ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controlle
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins\n");
EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
int ret = GetDigitalActionOrigins(controllerHandle, actionSetHandle, digitalActionHandle, origins );
for (int i = 0; i < ret; ++i) {
originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A));
}
return ret;
}
int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins steaminput\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetDigitalActionOrigins steaminput\n");
auto controller = controllers.find(inputHandle);
if (controller == controllers.end()) return 0;
auto map = controller_maps.find(actionSetHandle);
if (map == controller_maps.end()) return 0;
auto a = map->second.active_digital.find(digitalActionHandle);
if (a == map->second.active_digital.end()) return 0;
int count = 0;
for (auto button: a->second) {
switch (button) {
case BUTTON_A:
originsOut[count] = k_EInputActionOrigin_XBox360_A;
break;
case BUTTON_B:
originsOut[count] = k_EInputActionOrigin_XBox360_B;
break;
case BUTTON_X:
originsOut[count] = k_EInputActionOrigin_XBox360_X;
break;
case BUTTON_Y:
originsOut[count] = k_EInputActionOrigin_XBox360_Y;
break;
case BUTTON_LEFT_SHOULDER:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftBumper;
break;
case BUTTON_RIGHT_SHOULDER:
originsOut[count] = k_EInputActionOrigin_XBox360_RightBumper;
break;
case BUTTON_START:
originsOut[count] = k_EInputActionOrigin_XBox360_Start;
break;
case BUTTON_BACK:
originsOut[count] = k_EInputActionOrigin_XBox360_Back;
break;
case BUTTON_LTRIGGER:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Click;
break;
case BUTTON_RTRIGGER:
originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Click;
break;
case BUTTON_LEFT_THUMB:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Click;
break;
case BUTTON_RIGHT_THUMB:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Click;
break;
case BUTTON_STICK_LEFT_UP:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadNorth;
break;
case BUTTON_STICK_LEFT_DOWN:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadSouth;
break;
case BUTTON_STICK_LEFT_LEFT:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadWest;
break;
case BUTTON_STICK_LEFT_RIGHT:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_DPadEast;
break;
case BUTTON_STICK_RIGHT_UP:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadNorth;
break;
case BUTTON_STICK_RIGHT_DOWN:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadSouth;
break;
case BUTTON_STICK_RIGHT_LEFT:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadWest;
break;
case BUTTON_STICK_RIGHT_RIGHT:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_DPadEast;
break;
case BUTTON_DPAD_UP:
originsOut[count] = k_EInputActionOrigin_XBox360_DPad_North;
break;
case BUTTON_DPAD_DOWN:
originsOut[count] = k_EInputActionOrigin_XBox360_DPad_South;
break;
case BUTTON_DPAD_LEFT:
originsOut[count] = k_EInputActionOrigin_XBox360_DPad_West;
break;
case BUTTON_DPAD_RIGHT:
originsOut[count] = k_EInputActionOrigin_XBox360_DPad_East;
break;
default:
originsOut[count] = k_EInputActionOrigin_None;
break;
}
++count;
if (count >= STEAM_INPUT_MAX_ORIGINS) {
break;
}
}
return count;
}
// Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls.
ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetAnalogActionHandle %s\n", pszActionName);
return 125;
PRINT_DEBUG("Steam_Controller::GetAnalogActionHandle %s\n", pszActionName);
if (!pszActionName) return 0;
std::string upper_action_name(pszActionName);
std::transform(upper_action_name.begin(), upper_action_name.end(), upper_action_name.begin(),[](unsigned char c){ return std::toupper(c); });
auto handle = analog_action_handles.find(upper_action_name);
if (handle == analog_action_handles.end()) return 0;
return handle->second;
}
// Returns the current state of these supplied analog game action
ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle )
{
PRINT_DEBUG("GetAnalogActionData\n");
PRINT_DEBUG("Steam_Controller::GetAnalogActionData %llu %llu\n", controllerHandle, analogActionHandle);
ControllerAnalogActionData_t data;
data.eMode = k_EInputSourceMode_None;
data.x = data.y = 0;
data.bActive = false;
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return data;
auto analog = controller->second.analog_id(analogActionHandle);
if (!analog.first.size()) return data;
data.bActive = true;
data.eMode = analog.second;
for (auto a : analog.first) {
if (a >= JOY_ID_START) {
int joystick_id = a - JOY_ID_START;
GamepadStickNormXY((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id, &data.x, &data.y);
float length = GamepadStickLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_STICK) joystick_id);
data.x = data.x * length;
data.y = data.y * length;
} else {
data.x = GamepadTriggerLength((GAMEPAD_DEVICE)(controllerHandle - 1), (GAMEPAD_TRIGGER) a);
}
if (data.x || data.y) {
break;
}
}
return data;
}
@ -189,32 +639,73 @@ ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerH
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins\n");
EInputActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
int ret = GetAnalogActionOrigins(controllerHandle, actionSetHandle, analogActionHandle, origins );
for (int i = 0; i < ret; ++i) {
originsOut[i] = (EControllerActionOrigin)(origins[i] - (k_EInputActionOrigin_XBox360_A - k_EControllerActionOrigin_XBox360_A));
}
return ret;
}
int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins steaminput\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetAnalogActionOrigins steaminput\n");
auto controller = controllers.find(inputHandle);
if (controller == controllers.end()) return 0;
auto map = controller_maps.find(actionSetHandle);
if (map == controller_maps.end()) return 0;
auto a = map->second.active_analog.find(analogActionHandle);
if (a == map->second.active_analog.end()) return 0;
int count = 0;
for (auto b: a->second.first) {
switch (b) {
case TRIGGER_LEFT:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull;
break;
case TRIGGER_RIGHT:
originsOut[count] = k_EInputActionOrigin_XBox360_RightTrigger_Pull;
break;
case STICK_LEFT + JOY_ID_START:
originsOut[count] = k_EInputActionOrigin_XBox360_LeftStick_Move;
break;
case STICK_RIGHT + JOY_ID_START:
originsOut[count] = k_EInputActionOrigin_XBox360_RightStick_Move;
break;
default:
originsOut[count] = k_EInputActionOrigin_None;
break;
}
++count;
if (count >= STEAM_INPUT_MAX_ORIGINS) {
break;
}
}
return count;
}
void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction )
{
PRINT_DEBUG("StopAnalogActionMomentum\n");
PRINT_DEBUG("Steam_Controller::StopAnalogActionMomentum %llu %llu\n", controllerHandle, eAction);
}
// Trigger a haptic pulse on a controller
void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse\n");
PRINT_DEBUG("Steam_Controller::TriggerHapticPulse\n");
}
void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse old\n");
PRINT_DEBUG("Steam_Controller::TriggerHapticPulse old\n");
TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec );
}
@ -222,44 +713,59 @@ void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPa
// nFlags is currently unused and reserved for future use.
void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags )
{
PRINT_DEBUG("TriggerRepeatedHapticPulse\n");
PRINT_DEBUG("Steam_Controller::TriggerRepeatedHapticPulse\n");
}
// Tigger a vibration event on supported controllers.
void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed )
{
PRINT_DEBUG("TriggerVibration\n");
PRINT_DEBUG("Steam_Controller::TriggerVibration %hu %hu\n", usLeftSpeed, usRightSpeed);
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return;
unsigned int rumble_length_ms = 0;
#if defined(__linux__)
//FIXME: shadow of the tomb raider on linux doesn't seem to turn off the rumble so I made it expire after 100ms. Need to check if this is how linux steam actually behaves.
rumble_length_ms = 100;
#endif
GamepadSetRumble((GAMEPAD_DEVICE)(controllerHandle - 1), ((double)usLeftSpeed) / 65535.0, ((double)usRightSpeed) / 65535.0, rumble_length_ms);
}
// Set the controller LED color on supported controllers.
void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags )
{
PRINT_DEBUG("SetLEDColor\n");
PRINT_DEBUG("Steam_Controller::SetLEDColor\n");
}
// Returns the associated gamepad index for the specified controller, if emulating a gamepad
int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
{
PRINT_DEBUG("GetGamepadIndexForController\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetGamepadIndexForController\n");
auto controller = controllers.find(ulControllerHandle);
if (controller == controllers.end()) return -1;
return ulControllerHandle - 1;
}
// Returns the associated controller handle for the specified emulated gamepad
ControllerHandle_t GetControllerForGamepadIndex( int nIndex )
{
PRINT_DEBUG("GetControllerForGamepadIndex\n");
return 0;
PRINT_DEBUG("Steam_Controller::GetControllerForGamepadIndex\n");
ControllerHandle_t out = nIndex + 1;
auto controller = controllers.find(out);
if (controller == controllers.end()) return 0;
return out;
}
// Returns raw motion data from the specified controller
ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetMotionData\n");
PRINT_DEBUG("Steam_Controller::GetMotionData\n");
ControllerMotionData_t data = {};
return data;
}
@ -269,13 +775,13 @@ ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle )
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowDigitalActionOrigins\n");
PRINT_DEBUG("Steam_Controller::ShowDigitalActionOrigins\n");
return true;
}
bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowAnalogActionOrigins\n");
PRINT_DEBUG("Steam_Controller::ShowAnalogActionOrigins\n");
return true;
}
@ -283,13 +789,13 @@ bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAna
// Returns a localized string (from Steam's language setting) for the specified origin
const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin\n");
PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin\n");
return "Button String";
}
const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin steaminput\n");
PRINT_DEBUG("Steam_Controller::GetStringForActionOrigin steaminput\n");
return "Button String";
}
@ -297,58 +803,154 @@ const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
// Get a local path to art for on-screen glyph for a particular origin
const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin\n");
return "";
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin %i\n", eOrigin);
if (steamcontroller_glyphs.empty()) {
std::string dir = settings->glyphs_directory;
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_A] = dir + "button_a.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_B] = dir + "button_b.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_X] = dir + "button_x.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Y] = dir + "button_y.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
}
auto glyph = steamcontroller_glyphs.find(eOrigin);
if (glyph == steamcontroller_glyphs.end()) return "";
return glyph->second.c_str();
}
const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin steaminput\n");
return "";
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin steaminput %i\n", eOrigin);
if (steaminput_glyphs.empty()) {
std::string dir = settings->glyphs_directory;
steaminput_glyphs[k_EInputActionOrigin_XBox360_A] = dir + "button_a.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_B] = dir + "button_b.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_X] = dir + "button_x.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_Y] = dir + "button_y.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
//steaminput_glyphs[] = dir + "";
}
auto glyph = steaminput_glyphs.find(eOrigin);
if (glyph == steaminput_glyphs.end()) return "";
return glyph->second.c_str();
}
// Returns the input type for a particular handle
ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetInputTypeForHandle\n");
return k_ESteamInputType_Unknown;
PRINT_DEBUG("Steam_Controller::GetInputTypeForHandle\n");
auto controller = controllers.find(controllerHandle);
if (controller == controllers.end()) return k_ESteamInputType_Unknown;
return k_ESteamInputType_XBox360Controller;
}
const char *GetStringForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetStringForXboxOrigin\n");
PRINT_DEBUG("Steam_Controller::GetStringForXboxOrigin\n");
return "";
}
const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForXboxOrigin\n");
PRINT_DEBUG("Steam_Controller::GetGlyphForXboxOrigin\n");
return "";
}
EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin\n");
PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin steaminput\n");
PRINT_DEBUG("Steam_Controller::GetActionOriginFromXboxOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin\n");
PRINT_DEBUG("Steam_Controller::TranslateActionOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin steaminput\n");
PRINT_DEBUG("Steam_Controller::TranslateActionOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
bool GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor )
{
PRINT_DEBUG("Steam_Controller::GetControllerBindingRevision\n");
return false;
}
bool GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor )
{
PRINT_DEBUG("Steam_Controller::GetDeviceBindingRevision\n");
return false;
}
uint32 GetRemotePlaySessionID( InputHandle_t inputHandle )
{
PRINT_DEBUG("Steam_Controller::GetRemotePlaySessionID\n");
return 0;
}
void RunCallbacks()
{
RunFrame();
}
};

View File

@ -0,0 +1,375 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Controller :
public ISteamController001,
public ISteamController003,
public ISteamController004,
public ISteamController005,
public ISteamController006,
public ISteamController,
public ISteamInput
{
public:
Steam_Controller(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
}
// Init and Shutdown must be called when starting/ending use of this interface
bool Init()
{
PRINT_DEBUG("Steam_Controller::Init()\n");
return true;
}
bool Init( const char *pchAbsolutePathToControllerConfigVDF )
{
PRINT_DEBUG("Steam_Controller::Init() old\n");
return Init();
}
bool Shutdown()
{
PRINT_DEBUG("Steam_Controller::Shutdown()\n");
return true;
}
void SetOverrideMode( const char *pchMode )
{
PRINT_DEBUG("Steam_Controller::SetOverrideMode\n");
}
// Synchronize API state with the latest Steam Controller inputs available. This
// is performed automatically by SteamAPI_RunCallbacks, but for the absolute lowest
// possible latency, you call this directly before reading controller state.
void RunFrame()
{
PRINT_DEBUG("Steam_Controller::RunFrame()\n");
}
bool GetControllerState( uint32 unControllerIndex, SteamControllerState001_t *pState )
{
PRINT_DEBUG("Steam_Controller::GetControllerState()\n");
return false;
}
// Enumerate currently connected controllers
// handlesOut should point to a STEAM_CONTROLLER_MAX_COUNT sized array of ControllerHandle_t handles
// Returns the number of handles written to handlesOut
int GetConnectedControllers( ControllerHandle_t *handlesOut )
{
PRINT_DEBUG("GetConnectedControllers\n");
return 0;
}
// Invokes the Steam overlay and brings up the binding screen
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowBindingPanel( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("ShowBindingPanel\n");
return false;
}
// ACTION SETS
// Lookup the handle for an Action Set. Best to do this once on startup, and store the handles for all future API calls.
ControllerActionSetHandle_t GetActionSetHandle( const char *pszActionSetName )
{
PRINT_DEBUG("GetActionSetHandle %s\n", pszActionSetName);
return 124;
}
// Reconfigure the controller to use the specified action set (ie 'Menu', 'Walk' or 'Drive')
// This is cheap, and can be safely called repeatedly. It's often easier to repeatedly call it in
// your state loops, instead of trying to place it in all of your state transitions.
void ActivateActionSet( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle )
{
PRINT_DEBUG("ActivateActionSet\n");
}
ControllerActionSetHandle_t GetCurrentActionSet( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetCurrentActionSet\n");
return 124;
}
void ActivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("ActivateActionSetLayer\n");
}
void DeactivateActionSetLayer( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetLayerHandle )
{
PRINT_DEBUG("DeactivateActionSetLayer\n");
}
void DeactivateAllActionSetLayers( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("DeactivateAllActionSetLayers\n");
}
int GetActiveActionSetLayers( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t *handlesOut )
{
PRINT_DEBUG("GetActiveActionSetLayers\n");
return 0;
}
// ACTIONS
// Lookup the handle for a digital action. Best to do this once on startup, and store the handles for all future API calls.
ControllerDigitalActionHandle_t GetDigitalActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetDigitalActionHandle %s\n", pszActionName);
return 123;
}
// Returns the current state of the supplied digital game action
ControllerDigitalActionData_t GetDigitalActionData( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle )
{
PRINT_DEBUG("GetDigitalActionData\n");
ControllerDigitalActionData_t digitalData;
digitalData.bActive = false;
return digitalData;
}
// Get the origin(s) for a digital action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerDigitalActionHandle_t digitalActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins\n");
return 0;
}
int GetDigitalActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputDigitalActionHandle_t digitalActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetDigitalActionOrigins steaminput\n");
return 0;
}
// Lookup the handle for an analog action. Best to do this once on startup, and store the handles for all future API calls.
ControllerAnalogActionHandle_t GetAnalogActionHandle( const char *pszActionName )
{
PRINT_DEBUG("GetAnalogActionHandle %s\n", pszActionName);
return 125;
}
// Returns the current state of these supplied analog game action
ControllerAnalogActionData_t GetAnalogActionData( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle )
{
PRINT_DEBUG("GetAnalogActionData\n");
ControllerAnalogActionData_t data;
data.eMode = k_EInputSourceMode_None;
data.x = data.y = 0;
data.bActive = false;
return data;
}
// Get the origin(s) for an analog action within an action set. Returns the number of origins supplied in originsOut. Use this to display the appropriate on-screen prompt for the action.
// originsOut should point to a STEAM_CONTROLLER_MAX_ORIGINS sized array of EControllerActionOrigin handles
int GetAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerActionSetHandle_t actionSetHandle, ControllerAnalogActionHandle_t analogActionHandle, EControllerActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins\n");
return 0;
}
int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t actionSetHandle, InputAnalogActionHandle_t analogActionHandle, EInputActionOrigin *originsOut )
{
PRINT_DEBUG("GetAnalogActionOrigins steaminput\n");
return 0;
}
void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction )
{
PRINT_DEBUG("StopAnalogActionMomentum\n");
}
// Trigger a haptic pulse on a controller
void TriggerHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse\n");
}
void TriggerHapticPulse( uint32 unControllerIndex, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec )
{
PRINT_DEBUG("TriggerHapticPulse old\n");
TriggerHapticPulse(unControllerIndex, eTargetPad, usDurationMicroSec );
}
// Trigger a pulse with a duty cycle of usDurationMicroSec / usOffMicroSec, unRepeat times.
// nFlags is currently unused and reserved for future use.
void TriggerRepeatedHapticPulse( ControllerHandle_t controllerHandle, ESteamControllerPad eTargetPad, unsigned short usDurationMicroSec, unsigned short usOffMicroSec, unsigned short unRepeat, unsigned int nFlags )
{
PRINT_DEBUG("TriggerRepeatedHapticPulse\n");
}
// Tigger a vibration event on supported controllers.
void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLeftSpeed, unsigned short usRightSpeed )
{
PRINT_DEBUG("TriggerVibration\n");
}
// Set the controller LED color on supported controllers.
void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nColorG, uint8 nColorB, unsigned int nFlags )
{
PRINT_DEBUG("SetLEDColor\n");
}
// Returns the associated gamepad index for the specified controller, if emulating a gamepad
int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
{
PRINT_DEBUG("GetGamepadIndexForController\n");
return 0;
}
// Returns the associated controller handle for the specified emulated gamepad
ControllerHandle_t GetControllerForGamepadIndex( int nIndex )
{
PRINT_DEBUG("GetControllerForGamepadIndex\n");
return 0;
}
// Returns raw motion data from the specified controller
ControllerMotionData_t GetMotionData( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetMotionData\n");
ControllerMotionData_t data = {};
return data;
}
// Attempt to display origins of given action in the controller HUD, for the currently active action set
// Returns false is overlay is disabled / unavailable, or the user is not in Big Picture mode
bool ShowDigitalActionOrigins( ControllerHandle_t controllerHandle, ControllerDigitalActionHandle_t digitalActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowDigitalActionOrigins\n");
return true;
}
bool ShowAnalogActionOrigins( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t analogActionHandle, float flScale, float flXPosition, float flYPosition )
{
PRINT_DEBUG("ShowAnalogActionOrigins\n");
return true;
}
// Returns a localized string (from Steam's language setting) for the specified origin
const char *GetStringForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin\n");
return "Button String";
}
const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetStringForActionOrigin steaminput\n");
return "Button String";
}
// Get a local path to art for on-screen glyph for a particular origin
const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin\n");
return "";
}
const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForActionOrigin steaminput\n");
return "";
}
// Returns the input type for a particular handle
ESteamInputType GetInputTypeForHandle( ControllerHandle_t controllerHandle )
{
PRINT_DEBUG("GetInputTypeForHandle\n");
return k_ESteamInputType_Unknown;
}
const char *GetStringForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetStringForXboxOrigin\n");
return "";
}
const char *GetGlyphForXboxOrigin( EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetGlyphForXboxOrigin\n");
return "";
}
EControllerActionOrigin GetActionOriginFromXboxOrigin_( ControllerHandle_t controllerHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin GetActionOriginFromXboxOrigin( InputHandle_t inputHandle, EXboxOrigin eOrigin )
{
PRINT_DEBUG("GetActionOriginFromXboxOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
EControllerActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EControllerActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin\n");
return k_EControllerActionOrigin_None;
}
EInputActionOrigin TranslateActionOrigin( ESteamInputType eDestinationInputType, EInputActionOrigin eSourceOrigin )
{
PRINT_DEBUG("TranslateActionOrigin steaminput\n");
return k_EInputActionOrigin_None;
}
bool GetControllerBindingRevision( ControllerHandle_t controllerHandle, int *pMajor, int *pMinor )
{
PRINT_DEBUG("GetControllerBindingRevision\n");
return false;
}
bool GetDeviceBindingRevision( InputHandle_t inputHandle, int *pMajor, int *pMinor )
{
PRINT_DEBUG("GetDeviceBindingRevision\n");
return false;
}
uint32 GetRemotePlaySessionID( InputHandle_t inputHandle )
{
PRINT_DEBUG("GetRemotePlaySessionID\n");
return 0;
}
};

View File

@ -15,10 +15,20 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#ifndef __INCLUDED_STEAM_FRIENDS_H__
#define __INCLUDED_STEAM_FRIENDS_H__
#include "base.h"
#include "../overlay_experimental/steam_overlay.h"
#define SEND_FRIEND_RATE 4.0
struct Avatar_Numbers {
int smallest;
int medium;
int large;
};
class Steam_Friends :
public ISteamFriends004,
public ISteamFriends005,
@ -40,12 +50,13 @@ public ISteamFriends
class SteamCallBacks *callbacks;
class SteamCallResults *callback_results;
class RunEveryRunCB *run_every_runcb;
class Steam_Overlay* overlay;
Friend us;
bool modified;
std::vector<Friend> friends;
unsigned img_count;
std::map<uint64, struct Avatar_Numbers> avatars;
CSteamID lobby_id;
std::chrono::high_resolution_clock::time_point last_sent_friends;
@ -82,6 +93,28 @@ bool isAppIdCompatible(Friend *f)
return settings->get_local_game_id().AppID() == f->appid();
}
struct Avatar_Numbers add_friend_avatars(CSteamID id)
{
uint64 steam_id = id.ConvertToUint64();
auto avatar_ids = avatars.find(steam_id);
if (avatar_ids != avatars.end()) {
return avatar_ids->second;
}
//TODO: get real image data from self/other peers
struct Avatar_Numbers avatar_numbers;
std::string small_avatar(32 * 32 * 4, 0);
std::string medium_avatar(64 * 64 * 4, 0);
std::string large_avatar(184 * 184 * 4, 0);
avatar_numbers.smallest = settings->add_image(small_avatar, 32, 32);
avatar_numbers.medium = settings->add_image(medium_avatar, 64, 64);
avatar_numbers.large = settings->add_image(large_avatar, 184, 184);
avatars[steam_id] = avatar_numbers;
return avatar_numbers;
}
public:
static void steam_friends_callback(void *object, Common_Message *msg)
{
@ -99,13 +132,14 @@ static void steam_friends_run_every_runcb(void *object)
steam_friends->RunCallbacks();
}
Steam_Friends(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
Steam_Friends(Settings* settings, Networking* network, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Steam_Overlay* overlay):
settings(settings),
network(network),
callbacks(callbacks),
callback_results(callback_results),
run_every_runcb(run_every_runcb),
overlay(overlay)
{
this->settings = settings;
this->network = network;
this->callbacks = callbacks;
this->callback_results = callback_results;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_FRIEND, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_FRIEND_MESSAGES, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
@ -121,12 +155,9 @@ Steam_Friends(class Settings *settings, class Networking *network, class SteamCa
static bool ok_friend_flags(int iFriendFlags)
{
if (iFriendFlags & k_EFriendFlagBlocked) return false;
if (iFriendFlags & k_EFriendFlagIgnored) return false;
if (iFriendFlags & k_EFriendFlagIgnoredFriend) return false;
if (iFriendFlags & k_EFriendFlagFriendshipRequested) return false;
if (iFriendFlags & k_EFriendFlagRequestingFriendship) return false;
return true;
if (iFriendFlags & k_EFriendFlagImmediate) return true;
return false;
}
// returns the local players name - guaranteed to not be NULL.
@ -183,11 +214,11 @@ EPersonaState GetPersonaState()
// then GetFriendByIndex() can then be used to return the id's of each of those users
int GetFriendCount( int iFriendFlags )
{
PRINT_DEBUG("Steam_Friends::GetFriendCount\n");
PRINT_DEBUG("Steam_Friends::GetFriendCount %i\n", iFriendFlags);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int count = 0;
if (ok_friend_flags(iFriendFlags)) count = friends.size();
PRINT_DEBUG("count %i\n", count);
return count;
}
@ -446,7 +477,7 @@ SteamAPICall_t DownloadClanActivityCounts( STEAM_ARRAY_COUNT(cClansToRequest) CS
// steamIDSource can be the steamID of a group, game server, lobby or chat room
int GetFriendCountFromSource( CSteamID steamIDSource )
{
PRINT_DEBUG("Steam_Friends::GetFriendCountFromSource\n");
PRINT_DEBUG("Steam_Friends::GetFriendCountFromSource %llu\n", steamIDSource.ConvertToUint64());
//TODO
return 0;
}
@ -467,6 +498,10 @@ bool IsUserInSource( CSteamID steamIDUser, CSteamID steamIDSource )
if (settings->get_lobby() == steamIDSource) {
return true;
}
if (settings->subscribed_groups.find(steamIDSource.ConvertToUint64()) != settings->subscribed_groups.end()) {
return true;
}
} else {
Friend *f = find_friend(steamIDUser);
if (!f) return false;
@ -489,6 +524,7 @@ void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking )
void ActivateGameOverlay( const char *pchDialog )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
overlay->OpenOverlay(pchDialog);
}
@ -545,15 +581,17 @@ void SetPlayedWith( CSteamID steamIDUserPlayedWith )
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayInviteDialog\n");
// TODO: Here open the overlay
overlay->OpenOverlayInvite(steamIDLobby);
}
// gets the small (32x32) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
int GetSmallFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetSmallFriendAvatar\n");
++img_count;
return (img_count * 3) + 1;
//IMPORTANT NOTE: don't change friend avatar numbers for the same friend or else some games endlessly allocate stuff.
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend);
return numbers.smallest;
}
@ -561,8 +599,9 @@ int GetSmallFriendAvatar( CSteamID steamIDFriend )
int GetMediumFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetMediumFriendAvatar\n");
++img_count;
return (img_count * 3) + 2;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend);
return numbers.medium;
}
@ -571,8 +610,9 @@ int GetMediumFriendAvatar( CSteamID steamIDFriend )
int GetLargeFriendAvatar( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_Friends::GetLargeFriendAvatar\n");
++img_count;
return (img_count * 3) + 0;
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend);
return numbers.large;
}
int GetFriendAvatar( CSteamID steamIDFriend, int eAvatarSize )
@ -671,19 +711,19 @@ bool SetRichPresence( const char *pchKey, const char *pchValue )
PRINT_DEBUG("Steam_Friends::SetRichPresence %s %s\n", pchKey, pchValue ? pchValue : "NULL");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pchValue) {
#ifdef SHOW_DIALOG_RICH_CONNECT
if (std::string(pchKey) == std::string("connect"))
MessageBox(0, pchValue, pchKey, MB_OK);
#endif
(*us.mutable_rich_presence())[pchKey] = pchValue;
auto prev_value = (*us.mutable_rich_presence()).find(pchKey);
if (prev_value == (*us.mutable_rich_presence()).end() || prev_value->second != pchValue) {
(*us.mutable_rich_presence())[pchKey] = pchValue;
modified = true;
}
} else {
auto to_remove = us.mutable_rich_presence()->find(pchKey);
if (to_remove != us.mutable_rich_presence()->end()) {
us.mutable_rich_presence()->erase(to_remove);
modified = true;
}
}
modified = true;
return true;
}
@ -826,8 +866,13 @@ AppId_t GetFriendCoplayGame( CSteamID steamIDFriend )
STEAM_CALL_RESULT( JoinClanChatRoomCompletionResult_t )
SteamAPICall_t JoinClanChatRoom( CSteamID steamIDClan )
{
PRINT_DEBUG("Steam_Friends::JoinClanChatRoom\n");
return 0;
PRINT_DEBUG("Steam_Friends::JoinClanChatRoom %llu\n", steamIDClan.ConvertToUint64());
//TODO actually join a room
std::lock_guard<std::recursive_mutex> lock(global_mutex);
JoinClanChatRoomCompletionResult_t data;
data.m_steamIDClanChat = steamIDClan;
data.m_eChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
bool LeaveClanChatRoom( CSteamID steamIDClan )
@ -949,6 +994,20 @@ int GetNumChatsWithUnreadPriorityMessages()
return 0;
}
void ActivateGameOverlayRemotePlayTogetherInviteDialog( CSteamID steamIDLobby )
{
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayRemotePlayTogetherInviteDialog\n");
}
// Call this before calling ActivateGameOverlayToWebPage() to have the Steam Overlay Browser block navigations
// to your specified protocol (scheme) uris and instead dispatch a OverlayBrowserProtocolNavigation_t callback to your game.
// ActivateGameOverlayToWebPage() must have been called with k_EActivateGameOverlayToWebPageMode_Modal
bool RegisterProtocolInOverlayBrowser( const char *pchProtocol )
{
PRINT_DEBUG("Steam_Friends::RegisterProtocolInOverlayBrowser\n");
return false;
}
void RunCallbacks()
{
PRINT_DEBUG("Steam_Friends::RunCallbacks\n");
@ -981,6 +1040,7 @@ void Callback(Common_Message *msg)
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id; });
if (friends.end() != f) {
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
overlay->FriendDisconnect(*f);
friends.erase(f);
}
}
@ -1006,6 +1066,7 @@ void Callback(Common_Message *msg)
if (!f) {
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
friends.push_back(msg->friend_());
overlay->FriendConnect(msg->friend_());
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
}
} else {
@ -1026,23 +1087,50 @@ void Callback(Common_Message *msg)
if (msg->has_friend_messages()) {
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
PRINT_DEBUG("Steam_Friends Got Lobby Invite\n");
//TODO: the user should accept the invite first but we auto accept it because there's no gui yet
GameLobbyJoinRequested_t data;
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
Friend *f = find_friend((uint64)msg->source_id());
if (f) {
LobbyInvite_t data;
data.m_ulSteamIDUser = msg->source_id();
data.m_ulSteamIDLobby = msg->friend_messages().lobby_id();
data.m_ulGameID = f->appid();
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
if (overlay->Ready())
{
//TODO: the user should accept the invite first but we auto accept it because there's no gui yet
// Then we will handle it !
overlay->SetLobbyInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().lobby_id());
}
else
{
GameLobbyJoinRequested_t data;
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
if (msg->friend_messages().type() == Friend_Messages::GAME_INVITE) {
PRINT_DEBUG("Steam_Friends Got Game Invite\n");
//TODO: I'm pretty sure that the user should accept the invite before this is posted but we do like above
std::string const& connect_str = msg->friend_messages().connect_str();
GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
strncpy(data.m_rgchConnect, connect_str.c_str(), k_cchMaxRichPresenceValueLength - 1);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
if (overlay->Ready())
{
// Then we will handle it !
overlay->SetRichInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().connect_str().c_str());
}
else
{
std::string const& connect_str = msg->friend_messages().connect_str();
GameRichPresenceJoinRequested_t data = {};
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
strncpy(data.m_rgchConnect, connect_str.c_str(), k_cchMaxRichPresenceValueLength - 1);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
}
}
}
};
#endif//__INCLUDED_STEAM_FRIENDS_H__

View File

@ -16,7 +16,6 @@
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include <queue>
class Steam_Game_Coordinator :
public ISteamGameCoordinator

View File

@ -71,6 +71,7 @@ Steam_Game_Search(class Settings *settings, class Networking *network, class Ste
EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind )
{
PRINT_DEBUG("Steam_Game_Search::AddGameSearchParams\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -80,6 +81,7 @@ EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char
EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG("Steam_Game_Search::SearchForGameWithLobby\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -88,6 +90,7 @@ EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlaye
EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
{
PRINT_DEBUG("Steam_Game_Search::SearchForGameSolo\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -96,11 +99,13 @@ EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
EGameSearchErrorCode_t AcceptGame()
{
PRINT_DEBUG("Steam_Game_Search::AcceptGame\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
EGameSearchErrorCode_t DeclineGame()
{
PRINT_DEBUG("Steam_Game_Search::DeclineGame\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -108,6 +113,7 @@ EGameSearchErrorCode_t DeclineGame()
EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG("Steam_Game_Search::RetrieveConnectionDetails\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -115,6 +121,7 @@ EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pc
EGameSearchErrorCode_t EndGameSearch()
{
PRINT_DEBUG("Steam_Game_Search::EndGameSearch\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -125,6 +132,7 @@ EGameSearchErrorCode_t EndGameSearch()
EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("Steam_Game_Search::SetGameHostParams\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -132,6 +140,7 @@ EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchVal
EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails )
{
PRINT_DEBUG("Steam_Game_Search::SetConnectionDetails\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -143,6 +152,7 @@ EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, i
EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize )
{
PRINT_DEBUG("Steam_Game_Search::RequestPlayersForGame\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -152,6 +162,7 @@ EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, in
EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
{
PRINT_DEBUG("Steam_Game_Search::HostConfirmGameStart\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -160,6 +171,7 @@ EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
EGameSearchErrorCode_t CancelRequestPlayersForGame()
{
PRINT_DEBUG("Steam_Game_Search::CancelRequestPlayersForGame\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -167,6 +179,7 @@ EGameSearchErrorCode_t CancelRequestPlayersForGame()
EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult )
{
PRINT_DEBUG("Steam_Game_Search::SubmitPlayerResult\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
@ -175,6 +188,7 @@ EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID stea
EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID )
{
PRINT_DEBUG("Steam_Game_Search::EndGame\n");
return k_EGameSearchErrorCode_Failed_Offline;
}
void RunCallbacks()

View File

@ -177,7 +177,7 @@ bool Steam_GameServer::BSecure()
PRINT_DEBUG("BSecure\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!policy_response_called) return false;
return flags == eServerModeAuthenticationAndSecure;
return !!(flags & k_unServerFlagSecure);
}
CSteamID Steam_GameServer::GetSteamID()
@ -414,6 +414,7 @@ bool Steam_GameServer::BSetServerType( uint32 unServerFlags, uint32 unGameIP, ui
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
server_data.set_version(stoi(version));
flags = unServerFlags;
//TODO?
return true;
@ -541,15 +542,22 @@ SteamAPICall_t Steam_GameServer::GetServerReputation()
// Returns the public IP of the server according to Steam, useful when the server is
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
// connect to
uint32 Steam_GameServer::GetPublicIP()
uint32 Steam_GameServer::GetPublicIP_old()
{
PRINT_DEBUG("GetPublicIP\n");
PRINT_DEBUG("GetPublicIP_old\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
uint32 ip = network->getOwnIP();
PRINT_DEBUG("%X\n", ip);
return ip;
}
SteamIPAddress_t Steam_GameServer::GetPublicIP()
{
PRINT_DEBUG("GetPublicIP\n");
SteamIPAddress_t ip = SteamIPAddress_t::IPv4Any();
ip.m_unIPv4 = GetPublicIP_old();
return ip;
}
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
// socket to talk to the master server on, it lets the game use its socket to forward messages
@ -649,14 +657,14 @@ void Steam_GameServer::RunCallbacks()
if (temp_call_servers_connected) {
PRINT_DEBUG("Steam_GameServer::SteamServersConnected_t\n");
SteamServersConnected_t data;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
}
if (logged_in && !policy_response_called) {
PRINT_DEBUG("Steam_GameServer::GSPolicyResponse_t\n");
GSPolicyResponse_t data;
data.m_bSecure = flags == eServerModeAuthenticationAndSecure;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
data.m_bSecure = !!(flags & k_unServerFlagSecure);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.11);
policy_response_called = true;
}

View File

@ -34,6 +34,7 @@ public ISteamGameServer008,
public ISteamGameServer009,
public ISteamGameServer010,
public ISteamGameServer011,
public ISteamGameServer012,
public ISteamGameServer
{
class Settings *settings;
@ -275,7 +276,8 @@ public:
// Returns the public IP of the server according to Steam, useful when the server is
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
// connect to
uint32 GetPublicIP();
uint32 GetPublicIP_old();
SteamIPAddress_t GetPublicIP();
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
// socket to talk to the master server on, it lets the game use its socket to forward messages

View File

@ -116,7 +116,7 @@ bool Steam_HTTP::SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, c
// header and only do a local cache lookup rather than sending any actual remote request.
bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
{
PRINT_DEBUG("SendHTTPRequest\n");
PRINT_DEBUG("SendHTTPRequest %u %p\n", hRequest, pCallHandle);
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
@ -276,7 +276,12 @@ bool Steam_HTTP::GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *
bool Steam_HTTP::SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen )
{
PRINT_DEBUG("SetHTTPRequestRawPostBody\n");
return false;
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
return true;
}
@ -360,5 +365,11 @@ bool Steam_HTTP::SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, ui
bool Steam_HTTP::GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut )
{
PRINT_DEBUG("GetHTTPRequestWasTimedOut\n");
return false;
Steam_Http_Request *request = get_request(hRequest);
if (!request) {
return false;
}
if (pbWasTimedOut) *pbWasTimedOut = false;
return true;
}

View File

@ -15,10 +15,12 @@
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
#include "base.h" // For SteamItemDef_t
struct Steam_Inventory_Requests {
double timeout = 0.1;
bool done = false;
bool full_query;
SteamInventoryResult_t inventory_result;
std::chrono::system_clock::time_point time_created;
@ -26,7 +28,7 @@ struct Steam_Inventory_Requests {
std::vector<SteamItemInstanceID_t> instance_ids;
bool result_done() {
return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::system_clock::now() - time_created).count() > timeout;
return done;
}
uint32 timestamp() {
@ -34,28 +36,42 @@ struct Steam_Inventory_Requests {
}
};
class Steam_Inventory :
public ISteamInventory001,
public ISteamInventory002,
public ISteamInventory
public ISteamInventory001,
public ISteamInventory002,
public ISteamInventory
{
public:
static constexpr auto items_user_file = "items.json";
static constexpr auto items_default_file = "default_items.json";
private:
class Settings *settings;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
class Local_Storage* local_storage;
std::vector<struct Steam_Inventory_Requests> inventory_requests;
struct Steam_Inventory_Requests *new_inventory_result(const SteamItemInstanceID_t *pInstanceIDs=NULL, uint32 unCountInstanceIDs=0)
nlohmann::json defined_items;
nlohmann::json user_items;
bool inventory_loaded;
bool call_definition_update;
bool item_definitions_loaded;
struct Steam_Inventory_Requests* new_inventory_result(bool full_query=true, const SteamItemInstanceID_t* pInstanceIDs = NULL, uint32 unCountInstanceIDs = 0)
{
static SteamInventoryResult_t result;
++result;
struct Steam_Inventory_Requests request;
request.inventory_result = result;
request.full_query = full_query;
if (pInstanceIDs && unCountInstanceIDs) {
for (int i = 0; i < unCountInstanceIDs; ++i)
request.instance_ids.push_back(pInstanceIDs[i]);
request.instance_ids.reserve(unCountInstanceIDs);
std::copy(pInstanceIDs, pInstanceIDs + unCountInstanceIDs, std::back_inserter(request.instance_ids));
}
request.time_created = std::chrono::system_clock::now();
@ -73,13 +89,53 @@ struct Steam_Inventory_Requests *get_inventory_result(SteamInventoryResult_t res
return &(*request);
}
void read_items_db()
{
std::string items_db_path = Local_Storage::get_game_settings_path() + items_user_file;
PRINT_DEBUG("Items file path: %s\n", items_db_path.c_str());
local_storage->load_json(items_db_path, defined_items);
}
void read_inventory_db()
{
// If we havn't got any inventory
if (!local_storage->load_json_file("", items_user_file, user_items))
{
// Try to load a default one
std::string items_db_path = Local_Storage::get_game_settings_path() + items_default_file;
PRINT_DEBUG("Default items file path: %s\n", items_db_path.c_str());
local_storage->load_json(items_db_path, user_items);
}
}
public:
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks)
static void run_every_runcb_cb(void *object)
{
this->settings = settings;
this->callbacks = callbacks;
this->callback_results = callback_results;
PRINT_DEBUG("Steam_Inventory::run_every_runcb\n");
Steam_Inventory *obj = (Steam_Inventory *)object;
obj->RunCallbacks();
}
Steam_Inventory(class Settings *settings, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb, class Local_Storage *local_storage):
settings(settings),
callback_results(callback_results),
callbacks(callbacks),
run_every_runcb(run_every_runcb),
local_storage(local_storage),
defined_items(nlohmann::json::object()),
user_items(nlohmann::json::object()),
inventory_loaded(false),
call_definition_update(false),
item_definitions_loaded(false)
{
this->run_every_runcb->add(&Steam_Inventory::run_every_runcb_cb, this);
}
~Steam_Inventory()
{
this->run_every_runcb->remove(&Steam_Inventory::run_every_runcb_cb, this);
}
// INVENTORY ASYNC RESULT MANAGEMENT
@ -121,8 +177,64 @@ bool GetResultItems( SteamInventoryResult_t resultHandle,
struct Steam_Inventory_Requests *request = get_inventory_result(resultHandle);
if (!request) return false;
if (!request->result_done()) return false;
if (!inventory_loaded) return false;
if (pOutItemsArray != nullptr)
{
SteamItemDetails_t *items_array_base = pOutItemsArray;
uint32 max_items = *punOutItemsArraySize;
if (request->full_query) {
// We end if we reached the end of items or the end of buffer
for( auto i = user_items.begin(); i != user_items.end() && max_items; ++i, --max_items )
{
pOutItemsArray->m_iDefinition = std::stoi(i.key());
pOutItemsArray->m_itemId = pOutItemsArray->m_iDefinition;
try
{
pOutItemsArray->m_unQuantity = i.value().get<int>();
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
}
} else {
for (auto &itemid : request->instance_ids) {
if (!max_items) break;
auto it = user_items.find(std::to_string(itemid));
if (it != user_items.end()) {
pOutItemsArray->m_iDefinition = itemid;
pOutItemsArray->m_itemId = itemid;
try
{
pOutItemsArray->m_unQuantity = it->get<int>();
}
catch (...)
{
pOutItemsArray->m_unQuantity = 0;
}
pOutItemsArray->m_unFlags = k_ESteamItemNoTrade;
++pOutItemsArray;
--max_items;
}
}
}
*punOutItemsArraySize = pOutItemsArray - items_array_base;
}
else if (punOutItemsArraySize != nullptr)
{
if (request->full_query) {
*punOutItemsArraySize = user_items.size();
} else {
*punOutItemsArraySize = std::count_if(request->instance_ids.begin(), request->instance_ids.end(), [this](SteamItemInstanceID_t item_id){ return user_items.find(std::to_string(item_id)) != user_items.end();});
}
}
if (punOutItemsArraySize) *punOutItemsArraySize = 0;
PRINT_DEBUG("GetResultItems good\n");
return true;
}
@ -206,29 +318,12 @@ bool GetAllItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG("GetAllItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result();
{
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
//TODO: should this always be returned for each get all item calls?
struct SteamInventoryFullUpdate_t data;
data.m_handle = request->inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
struct Steam_Inventory_Requests* request = new_inventory_result();
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
return true;
}
return false;
return true;
}
@ -246,14 +341,7 @@ bool GetItemsByID( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT( unC
PRINT_DEBUG("GetItemsByID\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (pResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result(pInstanceIDs, unCountInstanceIDs);
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
struct Steam_Inventory_Requests *request = new_inventory_result(false, pInstanceIDs, unCountInstanceIDs);
*pResultHandle = request->inventory_result;
return true;
}
@ -330,14 +418,7 @@ bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_C
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO
if (pOutResultHandle) {
struct Steam_Inventory_Requests *request = new_inventory_result();
{
struct SteamInventoryResultReady_t data;
data.m_handle = request->inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), request->timeout);
}
struct Steam_Inventory_Requests *request = new_inventory_result(false);
*pOutResultHandle = request->inventory_result;
return true;
}
@ -359,6 +440,7 @@ bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_C
bool GenerateItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG("GenerateItems\n");
return false;
}
@ -370,6 +452,11 @@ STEAM_METHOD_DESC(GrantPromoItems() checks the list of promotional items for whi
bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
{
PRINT_DEBUG("GrantPromoItems\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result(false);
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
return true;
}
@ -381,13 +468,25 @@ bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
bool AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef )
{
PRINT_DEBUG("AddPromoItem\n");
return false;
//TODO
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result(false);
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
return true;
}
bool AddPromoItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength )
{
PRINT_DEBUG("AddPromoItems\n");
return false;
//TODO
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result(false);
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
return true;
}
@ -398,6 +497,7 @@ STEAM_METHOD_DESC(ConsumeItem() removes items from the inventory permanently.)
bool ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity )
{
PRINT_DEBUG("ConsumeItem\n");
return false;
}
@ -414,6 +514,7 @@ bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
STEAM_ARRAY_COUNT(unArrayDestroyLength) const SteamItemInstanceID_t *pArrayDestroy, STEAM_ARRAY_COUNT(unArrayDestroyLength) const uint32 *punArrayDestroyQuantity, uint32 unArrayDestroyLength )
{
PRINT_DEBUG("ExchangeItems\n");
return false;
}
@ -425,6 +526,7 @@ bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
bool TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest )
{
PRINT_DEBUG("TransferItemQuantity\n");
return false;
}
@ -452,8 +554,13 @@ void SendItemDropHeartbeat()
STEAM_METHOD_DESC(Playtime credit must be consumed and turned into item drops by your game.)
bool TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition )
{
PRINT_DEBUG("TriggerItemDrop\n");
PRINT_DEBUG("TriggerItemDrop %p %i\n", pResultHandle, dropListDefinition);
//TODO: if gameserver return false
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Steam_Inventory_Requests* request = new_inventory_result(false);
if (pResultHandle != nullptr)
*pResultHandle = request->inventory_result;
return true;
}
@ -488,6 +595,14 @@ STEAM_METHOD_DESC(LoadItemDefinitions triggers the automatic load and refresh of
bool LoadItemDefinitions()
{
PRINT_DEBUG("LoadItemDefinitions\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!item_definitions_loaded) {
call_definition_update = true;
}
//real steam launches a SteamInventoryResultReady_t which is why I create a new inventory result
new_inventory_result(false);
return true;
}
@ -501,19 +616,26 @@ bool GetItemDefinitionIDs(
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
{
PRINT_DEBUG("GetItemDefinitionIDs\n");
if (!punItemDefIDsArraySize) {
PRINT_DEBUG("GetItemDefinitionIDs %p\n", pItemDefIDs);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!punItemDefIDsArraySize)
return false;
}
PRINT_DEBUG("array_size %u\n", *punItemDefIDsArraySize);
/*
if (pItemDefIDs) {
*pItemDefIDs = 0;
if (pItemDefIDs == nullptr)
{
*punItemDefIDsArraySize = defined_items.size();
return true;
}
*/
//*punItemDefIDsArraySize = 0;
return false;
if (*punItemDefIDsArraySize < defined_items.size())
return false;
for (auto i = defined_items.begin(); i != defined_items.end(); ++i)
*pItemDefIDs++ = std::stoi(i.key());
return true;
}
@ -529,7 +651,95 @@ bool GetItemDefinitionIDs(
bool GetItemDefinitionProperty( SteamItemDef_t iDefinition, const char *pchPropertyName,
STEAM_OUT_STRING_COUNT(punValueBufferSizeOut) char *pchValueBuffer, uint32 *punValueBufferSizeOut )
{
PRINT_DEBUG("GetItemDefinitionProperty\n");
PRINT_DEBUG("GetItemDefinitionProperty %i %s\n", iDefinition, pchPropertyName);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto item = defined_items.find(std::to_string(iDefinition));
if (item != defined_items.end())
{
if (pchPropertyName != nullptr)
{
// Should I check for punValueBufferSizeOut == nullptr ?
// Try to get the property
auto attr = item.value().find(pchPropertyName);
if (attr != item.value().end())
{
std::string val;
try
{
val = attr.value().get<std::string>();
}
catch (...)
{
pchPropertyName = "";
*punValueBufferSizeOut = 0;
PRINT_DEBUG("Error, item: %d, attr: %s is not a string!", iDefinition, pchPropertyName);
return true;
}
if (pchValueBuffer != nullptr)
{
// copy what we can
strncpy(pchValueBuffer, val.c_str(), *punValueBufferSizeOut);
*punValueBufferSizeOut = std::min(static_cast<uint32>(val.length() + 1), *punValueBufferSizeOut);
}
else
{
// Set punValueBufferSizeOut to the property size
*punValueBufferSizeOut = val.length() + 1;
}
if (pchValueBuffer != nullptr)
{
// Make sure we have a null terminator
pchValueBuffer[*punValueBufferSizeOut-1] = '\0';
}
}
// Property not found
else
{
*punValueBufferSizeOut = 0;
PRINT_DEBUG("Attr %s not found for item %d\n", pchPropertyName, iDefinition);
return false;
}
}
else // Pass a NULL pointer for pchPropertyName to get a comma - separated list of available property names.
{
// If pchValueBuffer is NULL, *punValueBufferSize will contain the suggested buffer size
if (pchValueBuffer == nullptr)
{
// Should I check for punValueBufferSizeOut == nullptr ?
*punValueBufferSizeOut = 0;
for (auto i = item.value().begin(); i != item.value().end(); ++i)
*punValueBufferSizeOut += i.key().length() + 1; // Size of key + comma, and the last is not a comma but null char
}
else
{
// strncat always add the null terminator, so remove 1 to the string length
uint32_t len = *punValueBufferSizeOut-1;
*punValueBufferSizeOut = 0;
memset(pchValueBuffer, 0, len);
for( auto i = item.value().begin(); i != item.value().end() && len > 0; ++i )
{
strncat(pchValueBuffer, i.key().c_str(), len);
// Count how many chars we copied
// Either the string length or the buffer size if its too small
uint32 x = std::min(len, static_cast<uint32>(i.key().length()));
*punValueBufferSizeOut += x;
len -= x;
if (len && std::distance(i, item.value().end()) != 1) // If this is not the last item, add a comma
strncat(pchValueBuffer, ",", len--);
// Always add 1, its a comma or the null terminator
++*punValueBufferSizeOut;
}
}
}
return true;
}
return false;
}
@ -540,6 +750,7 @@ STEAM_CALL_RESULT( SteamInventoryEligiblePromoItemDefIDs_t )
SteamAPICall_t RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID )
{
PRINT_DEBUG("RequestEligiblePromoItemDefinitionsIDs\n");
return 0;
}
@ -552,6 +763,7 @@ bool GetEligiblePromoItemDefinitionIDs(
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
{
PRINT_DEBUG("GetEligiblePromoItemDefinitionIDs\n");
return false;
}
@ -564,6 +776,7 @@ STEAM_CALL_RESULT( SteamInventoryStartPurchaseResult_t )
SteamAPICall_t StartPurchase( STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
{
PRINT_DEBUG("StartPurchase\n");
return 0;
}
@ -572,7 +785,10 @@ STEAM_CALL_RESULT( SteamInventoryRequestPricesResult_t )
SteamAPICall_t RequestPrices()
{
PRINT_DEBUG("RequestPrices\n");
return 0;
SteamInventoryRequestPricesResult_t data;
data.m_result = k_EResultOK;
memcpy(data.m_rgchCurrency, "USD", 4);
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.2);
}
@ -580,6 +796,7 @@ SteamAPICall_t RequestPrices()
uint32 GetNumItemsWithPrices()
{
PRINT_DEBUG("GetNumItemsWithPrices\n");
return 0;
}
bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(pArrayItemDefs, Items with prices) SteamItemDef_t *pArrayItemDefs,
@ -588,6 +805,7 @@ bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(
uint32 unArrayLength )
{
PRINT_DEBUG("GetItemsWithPrices\n");
return false;
}
// Returns item definition ids and their prices in the user's local currency.
@ -603,6 +821,7 @@ bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pCurrentPrice, uint64 *pBasePrice )
{
PRINT_DEBUG("GetItemPrice\n");
return false;
}
// Retrieves the price for the item definition id
@ -618,39 +837,100 @@ bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pPrice )
SteamInventoryUpdateHandle_t StartUpdateProperties()
{
PRINT_DEBUG("StartUpdateProperties\n");
return 0;
}
// Remove the property on the item
bool RemoveProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName )
{
PRINT_DEBUG("RemoveProperty\n");
return false;
}
// Accessor methods to set properties on items
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, const char *pchPropertyValue )
{
PRINT_DEBUG("SetProperty\n");
return false;
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, bool bValue )
{
PRINT_DEBUG("SetProperty\n");
return false;
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, int64 nValue )
{
PRINT_DEBUG("SetProperty\n");
return false;
}
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, float flValue )
{
PRINT_DEBUG("SetProperty\n");
return false;
}
// Submit the update request by handle
bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventoryResult_t * pResultHandle )
{
PRINT_DEBUG("SubmitUpdateProperties\n");
return false;
}
bool InspectItem( SteamInventoryResult_t *pResultHandle, const char *pchItemToken )
{
PRINT_DEBUG("InspectItem\n");
return false;
}
void RunCallbacks()
{
if (call_definition_update || !inventory_requests.empty()) {
if (!item_definitions_loaded) {
read_items_db();
item_definitions_loaded = true;
//only gets called once
//also gets called when getting items
SteamInventoryDefinitionUpdate_t data = {};
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.05);
}
call_definition_update = false;
}
if (!inventory_requests.empty() && !inventory_loaded) {
read_inventory_db();
inventory_loaded = true;
}
if (inventory_loaded)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
for (auto& r : inventory_requests) {
if (!r.done && std::chrono::duration_cast<std::chrono::duration<double>>(now - r.time_created).count() > r.timeout) {
if (r.full_query) {
// SteamInventoryFullUpdate_t callbacks are triggered when GetAllItems
// successfully returns a result which is newer / fresher than the last
// known result.
struct SteamInventoryFullUpdate_t data;
data.m_handle = r.inventory_result;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
{
struct SteamInventoryResultReady_t data;
data.m_handle = r.inventory_result;
data.m_result = k_EResultOK;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
r.done = true;
}
}
}
}
};

View File

@ -88,6 +88,23 @@ public ISteamMatchmaking
std::vector<struct Chat_Entry> chat_entries;
std::vector<struct Data_Requested> data_requested;
std::map<uint64, ::google::protobuf::Map<std::string, std::string>> self_lobby_member_data;
google::protobuf::Map<std::string,std::string>::const_iterator caseinsensitive_find(const ::google::protobuf::Map< ::std::string, ::std::string >& map, std::string key)
{
auto x = map.begin();
while (x != map.end()) {
if (key.size() == x->first.size() && std::equal(x->first.begin(), x->first.end(), key.begin(),
[](char a, char b) {
return tolower(a) == tolower(b);
})) {
break;
}
++x;
}
return x;
}
Lobby *get_lobby(CSteamID id)
{
auto lobby = std::find_if(lobbies.begin(), lobbies.end(), [&id](Lobby const& item) { return item.room_id() == id.ConvertToUint64(); });
@ -112,7 +129,7 @@ void send_lobby_data()
}
}
void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, double cb_timeout=0.0)
void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, double cb_timeout=0.005, bool send_changed_lobby=true)
{
PRINT_DEBUG("Lobby dataupdate %llu %llu\n", lobby.ConvertToUint64(), member.ConvertToUint64());
LobbyDataUpdate_t data;
@ -131,10 +148,12 @@ void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, dou
Lobby *l = get_lobby(lobby);
if (l && l->owner() == settings->get_local_steam_id().ConvertToUint64()) {
Common_Message msg = Common_Message();
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_allocated_lobby(new Lobby(*l));
network->sendToAllIndividuals(&msg, true);
if (send_changed_lobby) {
Common_Message msg = Common_Message();
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_allocated_lobby(new Lobby(*l));
network->sendToAllIndividuals(&msg, true);
}
}
}
@ -239,6 +258,7 @@ void remove_lobbies()
while (g != std::end(lobbies)) {
if (g->members().size() == 0 || (g->deleted() && (g->time_deleted() + LOBBY_DELETED_TIMEOUT < current_time))) {
PRINT_DEBUG("REMOVING LOBBY %llu\n", g->room_id());
self_lobby_member_data.erase(g->room_id());
g = lobbies.erase(g);
} else {
++g;
@ -377,8 +397,7 @@ bool RemoveFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQ
}
*/
//
#define LOBBY_SEARCH_TIMEOUT 3.0 //defined by steam as being 20 seconds max and 3 seconds typically (Should be no less than 3 because or else SGZH doesn't work).
//Sanctum 2 needs this to be 2 seconds for the game to appear in the list though.
#define LOBBY_SEARCH_TIMEOUT 0.2 //Tested on real steam
STEAM_CALL_RESULT( LobbyMatchList_t )
SteamAPICall_t RequestLobbyList()
{
@ -453,7 +472,7 @@ void AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable )
// sets the distance for which we should search for lobbies (based on users IP address to location map on the Steam backed)
void AddRequestLobbyListDistanceFilter( ELobbyDistanceFilter eLobbyDistanceFilter )
{
PRINT_DEBUG("AddRequestLobbyListDistanceFilter\n");
PRINT_DEBUG("AddRequestLobbyListDistanceFilter %i\n", eLobbyDistanceFilter);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
@ -501,12 +520,13 @@ CSteamID GetLobbyByIndex( int iLobby )
return id;
}
static void enter_lobby(Lobby *lobby, CSteamID id)
static bool enter_lobby(Lobby *lobby, CSteamID id)
{
if (get_lobby_member(lobby, id)) return;
if (get_lobby_member(lobby, id)) return false;
Lobby_Member *member = lobby->add_members();
member->set_id(id.ConvertToUint64());
return true;
}
static bool leave_lobby(Lobby *lobby, CSteamID id)
@ -521,7 +541,7 @@ static bool leave_lobby(Lobby *lobby, CSteamID id)
}
#define LOBBY_CREATE_DELAY 0.1 //artificial delay for lobby creation
#define LOBBY_CREATE_DELAY 0.07 //artificial delay for lobby creation
void Create_pending_lobbies()
{
@ -598,35 +618,12 @@ SteamAPICall_t JoinLobby( CSteamID steamIDLobby )
PRINT_DEBUG("JoinLobby %llu\n", steamIDLobby.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto pj = std::find_if(pending_joins.begin(), pending_joins.end(), [&steamIDLobby](Pending_Joins const& item) {return item.lobby_id == steamIDLobby;});
if (pj != pending_joins.end()) return pj->api_id;
Pending_Joins pending_join;
pending_join.api_id = callback_results->reserveCallResult();
pending_join.lobby_id = steamIDLobby;
Lobby *lobby = get_lobby(steamIDLobby);
bool success = true;
if (lobby && lobby->deleted()) {
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseError;
auto api = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return api;
}
if (get_lobby_member(lobby, settings->get_local_steam_id())) {
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
auto api = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
return api;
}
pending_join.joined = std::chrono::high_resolution_clock::now();
pending_joins.push_back(pending_join);
@ -648,6 +645,7 @@ void LeaveLobby( CSteamID steamIDLobby )
if (lobbies.end() != lobby) {
if (!lobby->deleted()) {
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), true);
self_lobby_member_data.erase(lobby->room_id());
if (lobby->owner() != settings->get_local_steam_id().ConvertToUint64()) {
PRINT_DEBUG("LeaveLobby not owner\n");
leave_lobby(&(*lobby), settings->get_local_steam_id());
@ -741,12 +739,13 @@ CSteamID GetLobbyMemberByIndex( CSteamID steamIDLobby, int iMember )
const char *GetLobbyData( CSteamID steamIDLobby, const char *pchKey )
{
PRINT_DEBUG("GetLobbyData %llu %s\n", steamIDLobby.ConvertToUint64(), pchKey);
if (!pchKey) return "";
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Lobby *lobby = get_lobby(steamIDLobby);
const char *ret = "";
if (lobby) {
auto result = lobby->values().find(pchKey);
if (result != lobby->values().end()) ret = lobby->values().find(pchKey)->second.c_str();
auto result = caseinsensitive_find(lobby->values(), pchKey);
if (result != lobby->values().end()) ret = result->second.c_str();
}
PRINT_DEBUG("returned %s\n", ret);
@ -760,7 +759,7 @@ const char *GetLobbyData( CSteamID steamIDLobby, const char *pchKey )
// other users in the lobby will receive notification of the lobby data change via a LobbyDataUpdate_t callback
bool SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchValue )
{
PRINT_DEBUG("SetLobbyData %s %s\n", pchKey, pchValue);
PRINT_DEBUG("SetLobbyData %llu %s %s\n", steamIDLobby.ConvertToUint64(), pchKey, pchValue);
if (!pchKey) return false;
char empty_string[] = "";
if (!pchValue) pchValue = empty_string;
@ -771,12 +770,16 @@ bool SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchVal
return false;
}
auto result = lobby->values().find(pchKey);
if (result == lobby->values().end() || result->second != std::string(pchValue)) {
auto result = caseinsensitive_find(lobby->values(), pchKey);
bool changed = true;
if (result == lobby->values().end()) {
(*lobby->mutable_values())[pchKey] = pchValue;
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
} else {
if (result->second == std::string(pchValue)) changed = false;
(*lobby->mutable_values())[result->first] = pchValue;
}
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true, 0.0, changed);
return true;
}
@ -842,17 +845,27 @@ bool DeleteLobbyData( CSteamID steamIDLobby, const char *pchKey )
const char *GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, const char *pchKey )
{
PRINT_DEBUG("GetLobbyMemberData %s %llu %llu\n", pchKey, steamIDLobby.ConvertToUint64(), steamIDUser.ConvertToUint64());
if (!pchKey) return "";
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct Lobby_Member *member = get_lobby_member(get_lobby(steamIDLobby), steamIDUser);
const char *ret = "";
if (member) {
auto result = member->values().find(std::string(pchKey));
if (result == member->values().end()) return "";
PRINT_DEBUG("GetLobbyMemberData res %s\n", result->second.c_str());
ret = result->second.c_str();
if (steamIDUser == settings->get_local_steam_id()) {
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
if (result != self_lobby_member_data.end()) {
auto value = caseinsensitive_find(result->second, std::string(pchKey));
if (value != result->second.end()) {
ret = value->second.c_str();
}
}
} else {
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
if (result == member->values().end()) return "";
ret = result->second.c_str();
}
}
PRINT_DEBUG("GetLobbyMemberData res %s\n", ret);
return ret;
}
@ -871,7 +884,12 @@ void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *
Lobby_Member *member = get_lobby_member(lobby, settings->get_local_steam_id());
if (member) {
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
(*member->mutable_values())[pchKey] = pchValue;
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
if (result == member->values().end()) {
(*member->mutable_values())[pchKey] = pchValue;
} else {
(*member->mutable_values())[result->first] = pchValue;
}
trigger_lobby_dataupdate(steamIDLobby, (uint64)member->id(), true);
} else {
Lobby_Messages *message = new Lobby_Messages();
@ -879,9 +897,21 @@ void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *
(*message->mutable_map())[pchKey] = pchValue;
send_owner_packet(steamIDLobby, message);
}
}
{
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
if (result != self_lobby_member_data.end()) {
auto value = caseinsensitive_find(result->second, std::string(pchKey));
if (value != result->second.end()) {
self_lobby_member_data[steamIDLobby.ConvertToUint64()][value->first] = pchValue;
} else {
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
}
} else {
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
}
}
}
}
@ -1070,7 +1100,7 @@ bool SetLobbyJoinable( CSteamID steamIDLobby, bool bLobbyJoinable )
// returns the current lobby owner
// you must be a member of the lobby to access this
// you must be a member of the lobby to access this (Mr_Goldberg note: This is a lie)
// there always one lobby owner - if the current owner leaves, another user will become the owner
// it is possible (bur rare) to join a lobby just as the owner is leaving, thus entering a lobby with self as the owner
CSteamID GetLobbyOwner( CSteamID steamIDLobby )
@ -1078,10 +1108,10 @@ CSteamID GetLobbyOwner( CSteamID steamIDLobby )
PRINT_DEBUG("GetLobbyOwner %llu\n", steamIDLobby.ConvertToUint64());
std::lock_guard<std::recursive_mutex> lock(global_mutex);
Lobby *lobby = get_lobby(steamIDLobby);
CSteamID id = k_steamIDNil;
if (lobby) id = (uint64)lobby->owner();
return id;
if (!lobby || lobby->deleted()) return k_steamIDNil;
//TODO: might be better to require the lobby info to be at least requested first.
return (uint64)lobby->owner();
}
@ -1137,7 +1167,7 @@ void RunCallbacks()
PRINT_DEBUG("use lobby: %u, filters: %zu, joinable: %u, type: %u, deleted: %u\n", use, filter_values_copy.size(), l.joinable(), l.type(), l.deleted());
for (auto & f : filter_values_copy) {
PRINT_DEBUG("%s:%s/%i %u %i\n", f.key.c_str(), f.value_string.c_str(), f.value_int, f.is_int, f.eComparisonType);
auto value = l.values().find(f.key);
auto value = caseinsensitive_find(l.values(), f.key);
if (value != l.values().end()) {
//TODO: eComparisonType
if (!f.is_int) {
@ -1152,16 +1182,26 @@ void RunCallbacks()
}
}
} else {
int compare_to = stoi(value->second, 0, 0);
PRINT_DEBUG("Compare Values %i %i\n", compare_to, f.value_int);
if (f.eComparisonType == k_ELobbyComparisonEqual) {
if (compare_to == f.value_int) {
PRINT_DEBUG("Equal\n");
//use = use;
} else {
PRINT_DEBUG("Not Equal\n");
use = false;
try {
PRINT_DEBUG("%s\n", value->second.c_str());
int compare_to = 0;
//TODO: check if this is how real steam behaves
if (value->second.size()) {
compare_to = std::stoll(value->second, 0, 0);
}
PRINT_DEBUG("Compare Values %i %i\n", compare_to, f.value_int);
if (f.eComparisonType == k_ELobbyComparisonEqual) {
if (compare_to == f.value_int) {
PRINT_DEBUG("Equal\n");
//use = use;
} else {
PRINT_DEBUG("Not Equal\n");
use = false;
}
}
} catch (...) {
//Same case as if the key is not in the lobby?
use = false;
}
//TODO: add more comparisons
}
@ -1206,13 +1246,36 @@ void RunCallbacks()
g->message_sent = send_owner_packet(g->lobby_id, message);
}
Lobby *lobby = get_lobby(g->lobby_id);
if (lobby && lobby->deleted()) {
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
g = pending_joins.erase(g);
} else
if (get_lobby_member(lobby, settings->get_local_steam_id())) {
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
g = pending_joins.erase(g);
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
} else
if (check_timedout(g->joined, PENDING_JOIN_TIMEOUT)) {
bool success = false;
LobbyEnter_t data;
data.m_ulSteamIDLobby = g->lobby_id.ConvertToUint64();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
g = pending_joins.erase(g);
@ -1250,7 +1313,7 @@ void Callback(Common_Message *msg)
if (msg->lobby().owner() != settings->get_local_steam_id().ConvertToUint64() && msg->lobby().appid() == settings->get_local_game_id().AppID()) {
Lobby *lobby = get_lobby((uint64)msg->lobby().room_id());
if (!lobby) {
unsigned int old_size = lobbies.size();
size_t old_size = lobbies.size();
lobbies.resize(old_size + 1);
lobbies[old_size].set_room_id(msg->lobby().room_id());
lobby = &(lobbies[old_size]);
@ -1280,20 +1343,26 @@ void Callback(Common_Message *msg)
if (!member) {
if (m.id() == settings->get_local_steam_id().ConvertToUint64()) {
CSteamID id((uint64)lobby->room_id());
auto pd = std::find_if(pending_joins.begin(), pending_joins.end(), [&id](Pending_Joins const& item) { return item.lobby_id == id; });
if (pd != pending_joins.end()) {
bool success = true;
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
callback_results->addCallResult(pd->api_id, data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
pending_joins.erase(pd);
auto pd = pending_joins.begin();
while (pd != pending_joins.end()) {
if (pd->lobby_id == id) {
bool success = true;
LobbyEnter_t data;
data.m_ulSteamIDLobby = lobby->room_id();
data.m_rgfChatPermissions = 0; //Unused - Always 0
data.m_bLocked = false;
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
callback_results->addCallResult(pd->api_id, data.k_iCallback, &data, sizeof(data));
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
pd = pending_joins.erase(pd);
joined = true;
} else {
++pd;
}
}
if (joined) {
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), false);
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
joined = true;
}
} else {
if (we_are_in_lobby) trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)m.id(), false, true);
@ -1334,8 +1403,9 @@ void Callback(Common_Message *msg)
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
if (msg->lobby_messages().type() == Lobby_Messages::JOIN) {
PRINT_DEBUG("LOBBY MESSAGE: JOIN\n");
enter_lobby(lobby, (uint64)msg->source_id());
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), false, true);
if (enter_lobby(lobby, (uint64)msg->source_id())) {
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), false, true, 0.01);
}
}
if (msg->lobby_messages().type() == Lobby_Messages::MEMBER_DATA) {
@ -1343,7 +1413,13 @@ void Callback(Common_Message *msg)
Lobby_Member *member = get_lobby_member(lobby, (uint64)msg->source_id());
if (member) {
for (auto const &p : msg->lobby_messages().map()) {
(*member->mutable_values())[p.first] = p.second;
PRINT_DEBUG("member data %s:%s\n", p.first.c_str(), p.second.c_str());
auto result = caseinsensitive_find(member->values(), p.first);
if (result == member->values().end()) {
(*member->mutable_values())[p.first] = p.second;
} else {
(*member->mutable_values())[result->first] = p.second;
}
}
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)member->id(), true);

View File

@ -338,6 +338,7 @@ HServerQuery Steam_Matchmaking_Servers::PingServer( uint32 unIP, uint16 usPort,
r.ip = unIP;
r.port = usPort;
r.ping_response = pRequestServersResponse;
r.created = std::chrono::high_resolution_clock::now();
direct_ip_requests.push_back(r);
return r.id;
}
@ -352,6 +353,7 @@ HServerQuery Steam_Matchmaking_Servers::PlayerDetails( uint32 unIP, uint16 usPor
r.ip = unIP;
r.port = usPort;
r.players_response = pRequestServersResponse;
r.created = std::chrono::high_resolution_clock::now();
direct_ip_requests.push_back(r);
return r.id;
}
@ -367,6 +369,7 @@ HServerQuery Steam_Matchmaking_Servers::ServerRules( uint32 unIP, uint16 usPort,
r.ip = unIP;
r.port = usPort;
r.rules_response = pRequestServersResponse;
r.created = std::chrono::high_resolution_clock::now();
direct_ip_requests.push_back(r);
return r.id;
}
@ -439,8 +442,16 @@ void Steam_Matchmaking_Servers::RunCallbacks()
}
}
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp = direct_ip_requests;
direct_ip_requests.clear();
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp;
auto dip = std::begin(direct_ip_requests);
while (dip != std::end(direct_ip_requests)) {
if (check_timedout(dip->created, DIRECT_IP_DELAY)) {
direct_ip_requests_temp.push_back(*dip);
dip = direct_ip_requests.erase(dip);
} else {
++dip;
}
}
for (auto &r : direct_ip_requests_temp) {
PRINT_DEBUG("dip request: %lu:%hu\n", r.ip, r.port);
@ -486,6 +497,7 @@ void Steam_Matchmaking_Servers::RunCallbacks()
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
{
if (msg->has_gameserver()) {
PRINT_DEBUG("got SERVER %llu, offline:%u\n", msg->gameserver().id(), msg->gameserver().offline());
if (msg->gameserver().offline()) {
for (auto &g : gameservers) {
if (g.server.id() == msg->gameserver().id()) {

View File

@ -18,12 +18,14 @@
#include "base.h"
#define SERVER_TIMEOUT 10.0
#define DIRECT_IP_DELAY 0.05
struct Steam_Matchmaking_Servers_Direct_IP_Request {
HServerQuery id;
uint32 ip;
uint16 port;
std::chrono::high_resolution_clock::time_point created;
ISteamMatchmakingRulesResponse *rules_response = NULL;
ISteamMatchmakingPlayersResponse *players_response = NULL;
ISteamMatchmakingPingResponse *ping_response = NULL;

View File

@ -20,171 +20,203 @@
// Service Definition
bool Steam_MusicRemote::RegisterSteamMusicRemote( const char *pchName )
{
PRINT_DEBUG("RegisterSteamMusicRemote\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::DeregisterSteamMusicRemote()
{
PRINT_DEBUG("DeregisterSteamMusicRemote\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::BIsCurrentMusicRemote()
{
PRINT_DEBUG("BIsCurrentMusicRemote\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::BActivationSuccess( bool bValue )
{
PRINT_DEBUG("BActivationSuccess\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetDisplayName( const char *pchDisplayName )
{
PRINT_DEBUG("SetDisplayName\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength )
{
PRINT_DEBUG("SetPNGIcon_64x64\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
// Abilities for the user interface
bool Steam_MusicRemote::EnablePlayPrevious(bool bValue)
{
PRINT_DEBUG("EnablePlayPrevious\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::EnablePlayNext( bool bValue )
{
PRINT_DEBUG("EnablePlayNext\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::EnableShuffled( bool bValue )
{
PRINT_DEBUG("EnableShuffled\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::EnableLooped( bool bValue )
{
PRINT_DEBUG("EnableLooped\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::EnableQueue( bool bValue )
{
PRINT_DEBUG("EnableQueue\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::EnablePlaylists( bool bValue )
{
PRINT_DEBUG("EnablePlaylists\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
// Status
bool Steam_MusicRemote::UpdatePlaybackStatus( AudioPlayback_Status nStatus )
{
PRINT_DEBUG("UpdatePlaybackStatus\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateShuffled( bool bValue )
{
PRINT_DEBUG("UpdateShuffled\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateLooped( bool bValue )
{
PRINT_DEBUG("UpdateLooped\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateVolume( float flValue )
{
PRINT_DEBUG("UpdateVolume\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
// volume is between 0.0 and 1.0
// Current Entry
bool Steam_MusicRemote::CurrentEntryWillChange()
{
PRINT_DEBUG("CurrentEntryWillChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::CurrentEntryIsAvailable( bool bAvailable )
{
PRINT_DEBUG("CurrentEntryIsAvailable\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateCurrentEntryText( const char *pchText )
{
PRINT_DEBUG("UpdateCurrentEntryText\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateCurrentEntryElapsedSeconds( int nValue )
{
PRINT_DEBUG("UpdateCurrentEntryElapsedSeconds\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength )
{
PRINT_DEBUG("UpdateCurrentEntryCoverArt\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::CurrentEntryDidChange()
{
PRINT_DEBUG("CurrentEntryDidChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
// Queue
bool Steam_MusicRemote::QueueWillChange()
{
PRINT_DEBUG("QueueWillChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::ResetQueueEntries()
{
PRINT_DEBUG("ResetQueueEntries\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetQueueEntry( int nID, int nPosition, const char *pchEntryText )
{
PRINT_DEBUG("SetQueueEntry\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetCurrentQueueEntry( int nID )
{
PRINT_DEBUG("SetCurrentQueueEntry\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::QueueDidChange()
{
PRINT_DEBUG("QueueDidChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
// Playlist
bool Steam_MusicRemote::PlaylistWillChange()
{
PRINT_DEBUG("PlaylistWillChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::ResetPlaylistEntries()
{
PRINT_DEBUG("ResetPlaylistEntries\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText )
{
PRINT_DEBUG("SetPlaylistEntry\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::SetCurrentPlaylistEntry( int nID )
{
PRINT_DEBUG("SetCurrentPlaylistEntry\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
bool Steam_MusicRemote::PlaylistDidChange()
{
PRINT_DEBUG("PlaylistDidChange\n");
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}

View File

@ -21,6 +21,9 @@
#define ORPHANED_PACKET_TIMEOUT (20)
#define NEW_CONNECTION_TIMEOUT (20.0)
//kingdom 2 crowns doesn't work with a 0.3 delay or lower
#define NEW_CONNECTION_DELAY (0.4)
#define OLD_CHANNEL_NUMBER 1
struct Steam_Networking_Connection {
@ -59,6 +62,7 @@ public ISteamNetworking001,
public ISteamNetworking002,
public ISteamNetworking003,
public ISteamNetworking004,
public ISteamNetworking005,
public ISteamNetworking
{
class Settings *settings;
@ -67,21 +71,28 @@ public ISteamNetworking
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::vector<Common_Message> messages;
std::recursive_mutex messages_mutex;
std::list<Common_Message> messages;
std::list<Common_Message> unprocessed_messages;
std::recursive_mutex connections_edit_mutex;
std::vector<struct Steam_Networking_Connection> connections;
std::vector<struct steam_listen_socket> listen_sockets;
std::vector<struct steam_connection_socket> connection_sockets;
std::map<CSteamID, std::chrono::high_resolution_clock::time_point> new_connection_times;
std::queue<CSteamID> new_connections_to_call_cb;
bool connection_exists(CSteamID id)
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
return std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;}) != connections.end();
}
struct Steam_Networking_Connection *get_or_create_connection(CSteamID id)
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
auto conn = std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;});
if (connections.end() == conn) {
@ -96,23 +107,40 @@ struct Steam_Networking_Connection *get_or_create_connection(CSteamID id)
void remove_connection(CSteamID id)
{
auto conn = std::begin(connections);
while (conn != std::end(connections)) {
if (conn->remote == id) {
{
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
auto conn = std::begin(connections);
while (conn != std::end(connections)) {
if (conn->remote == id) {
conn = connections.erase(conn);
} else {
++conn;
conn = connections.erase(conn);
} else {
++conn;
}
}
}
//pretty sure steam also clears the entire queue of messages for that connection
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = messages.erase(msg);
} else {
++msg;
{
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
auto msg = std::begin(messages);
while (msg != std::end(messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = messages.erase(msg);
} else {
++msg;
}
}
}
{
auto msg = std::begin(unprocessed_messages);
while (msg != std::end(unprocessed_messages)) {
if (msg->source_id() == id.ConvertToUint64()) {
msg = unprocessed_messages.erase(msg);
} else {
++msg;
}
}
}
}
@ -120,8 +148,9 @@ void remove_connection(CSteamID id)
SNetSocket_t create_connection_socket(CSteamID target, int nVirtualPort, uint32 nIP, uint16 nPort, SNetListenSocket_t id=0, enum steam_socket_connection_status status=SOCKET_CONNECTING, SNetSocket_t other_id=0)
{
static SNetSocket_t socket_number = 0;
bool found = 0;
bool found;
do {
found = false;
++socket_number;
for (auto & c: connection_sockets) {
if (c.id == socket_number || socket_number == 0) {
@ -248,16 +277,16 @@ bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData,
Common_Message msg;
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
msg.set_dest_id(steamIDRemote.ConvertToUint64());
msg.set_allocated_network(new Network);
msg.set_allocated_network(new Network_pb);
if (!connection_exists(steamIDRemote)) {
msg.mutable_network()->set_type(Network::NEW_CONNECTION);
msg.mutable_network()->set_type(Network_pb::NEW_CONNECTION);
network->sendTo(&msg, true);
}
msg.mutable_network()->set_channel(nChannel);
msg.mutable_network()->set_data(pubData, cubData);
msg.mutable_network()->set_type(Network::DATA);
msg.mutable_network()->set_type(Network_pb::DATA);
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
new_connection_times.erase(steamIDRemote);
@ -277,7 +306,7 @@ bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData,
bool IsP2PPacketAvailable( uint32 *pcubMsgSize, int nChannel)
{
PRINT_DEBUG("Steam_Networking::IsP2PPacketAvailable channel: %i\n", nChannel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
@ -311,7 +340,7 @@ bool IsP2PPacketAvailable( uint32 *pcubMsgSize)
bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote, int nChannel)
{
PRINT_DEBUG("Steam_Networking::ReadP2PPacket %u %i\n", cubDest, nChannel);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
//this->network->Run();
@ -437,9 +466,8 @@ bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionS
pConnectionState->m_bUsingRelay = false;
pConnectionState->m_nBytesQueuedForSend = 0;
pConnectionState->m_nPacketsQueuedForSend = 0;
//TODO ip?
pConnectionState->m_nRemoteIP = 0;
pConnectionState->m_nRemotePort = 0;
pConnectionState->m_nRemoteIP = network->getIP(steamIDRemote);
pConnectionState->m_nRemotePort = 12345;
}
PRINT_DEBUG("Connection\n");
@ -454,7 +482,7 @@ bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionS
// P2P packet relay is allowed by default
bool AllowP2PPacketRelay( bool bAllow )
{
PRINT_DEBUG("Steam_Networking::AllowP2PPacketRelay\n");
PRINT_DEBUG("Steam_Networking::AllowP2PPacketRelay %u\n", bAllow);
return true;
}
@ -480,7 +508,7 @@ SNetListenSocket_t socket_number = 0;
// pass in 0 if you don't want users to be able to connect via IP/Port, but expect to be always peer-to-peer connections only
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("Steam_Networking::CreateListenSocket %i %u %hu %u\n", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
PRINT_DEBUG("Steam_Networking::CreateListenSocket old %i %u %hu %u\n", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
for (auto & c : listen_sockets) {
if (c.nVirtualP2PPort == nVirtualP2PPort || c.nPort == nPort)
@ -499,6 +527,13 @@ SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 n
return socket.id;
}
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, SteamIPAddress_t nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
{
PRINT_DEBUG("Steam_Networking::CreateListenSocket %i %i %u %hu %u\n", nVirtualP2PPort, nIP.m_eType, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
//TODO: ipv6
return CreateListenSocket(nVirtualP2PPort, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
}
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort )
{
PRINT_DEBUG("Steam_Networking::CreateListenSocket old\n");
@ -525,12 +560,18 @@ SNetSocket_t CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort
SNetSocket_t CreateConnectionSocket( uint32 nIP, uint16 nPort, int nTimeoutSec )
{
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket %u %hu %i\n", nIP, nPort, nTimeoutSec);
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket_old %u %hu %i\n", nIP, nPort, nTimeoutSec);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: nTimeoutSec
return create_connection_socket((uint64)0, 0, nIP, nPort);
}
SNetSocket_t CreateConnectionSocket( SteamIPAddress_t nIP, uint16 nPort, int nTimeoutSec )
{
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket %i %u %hu %i\n", nIP.m_eType, nIP.m_unIPv4, nPort, nTimeoutSec);
//TODO: ipv6
return CreateConnectionSocket(nIP.m_unIPv4, nPort, nTimeoutSec);
}
// disconnects the connection to the socket, if any, and invalidates the handle
// any unread data on the socket will be thrown away
@ -692,7 +733,7 @@ bool RetrieveData( SNetListenSocket_t hListenSocket, void *pubDest, uint32 cubDe
// returns information about the specified socket, filling out the contents of the pointers
bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, uint32 *punIPRemote, uint16 *punPortRemote )
{
PRINT_DEBUG("Steam_Networking::GetSocketInfo\n");
PRINT_DEBUG("Steam_Networking::GetSocketInfo_old\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
struct steam_connection_socket *socket = get_connection_socket(hSocket);
if (!socket) return false;
@ -717,12 +758,28 @@ bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocke
return true;
}
bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, SteamIPAddress_t *punIPRemote, uint16 *punPortRemote )
{
PRINT_DEBUG("Steam_Networking::GetSocketInfo\n");
//TODO: ipv6
uint32 *ip_remote = NULL;
if (punIPRemote) {
ip_remote = &(punIPRemote->m_unIPv4);
}
bool ret = GetSocketInfo(hSocket, pSteamIDRemote, peSocketStatus, ip_remote, punPortRemote );
if (punIPRemote && ret) {
punIPRemote->m_eType = k_ESteamIPTypeIPv4;
}
return ret;
}
// returns which local port the listen socket is bound to
// *pnIP and *pnPort will be 0 if the socket is set to listen for P2P connections only
bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16 *pnPort )
{
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo\n");
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo_old\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
auto conn = std::find_if(listen_sockets.begin(), listen_sockets.end(), [&hListenSocket](struct steam_listen_socket const& conn) { return conn.id == hListenSocket;});
if (conn == listen_sockets.end()) return false;
@ -731,6 +788,22 @@ bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16
return true;
}
bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, SteamIPAddress_t *pnIP, uint16 *pnPort )
{
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo\n");
//TODO: ipv6
uint32 *ip = NULL;
if (pnIP) {
ip = &(pnIP->m_unIPv4);
}
bool ret = GetListenSocketInfo(hListenSocket, ip, pnPort );
if (pnIP && ret) {
pnIP->m_eType = k_ESteamIPTypeIPv4;
}
return ret;
}
// returns true to describe how the socket ended up connecting
ESNetSocketConnectionType GetSocketConnectionType( SNetSocket_t hSocket )
@ -754,24 +827,27 @@ void RunCallbacks()
{
uint64 current_time = std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count();
for (auto &msg : messages) {
CSteamID source_id((uint64)msg.source_id());
if (!msg.network().processed()) {
{
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
{
auto msg = std::begin(unprocessed_messages);
while (msg != std::end(unprocessed_messages)) {
CSteamID source_id((uint64)msg->source_id());
if (!connection_exists(source_id)) {
if (new_connection_times.find(source_id) == new_connection_times.end()) {
P2PSessionRequest_t data;
memset(&data, 0, sizeof(data));
data.m_steamIDRemote = CSteamID(source_id);
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
new_connections_to_call_cb.push(source_id);
new_connection_times[source_id] = std::chrono::high_resolution_clock::now();
}
} else {
struct Steam_Networking_Connection *conn = get_or_create_connection(source_id);
conn->open_channels.insert(msg.network().channel());
conn->open_channels.insert(msg->network().channel());
}
msg.mutable_network()->set_processed(true);
msg.mutable_network()->set_time_processed(current_time);
msg->mutable_network()->set_processed(true);
msg->mutable_network()->set_time_processed(current_time);
messages.push_back(*msg);
msg = unprocessed_messages.erase(msg);
}
}
@ -793,6 +869,27 @@ void RunCallbacks()
}
}
}
while (!new_connections_to_call_cb.empty()) {
CSteamID source_id = new_connections_to_call_cb.front();
auto t = new_connection_times.find(source_id);
if (t == new_connection_times.end()) {
new_connections_to_call_cb.pop();
continue;
}
if (!check_timedout(t->second, NEW_CONNECTION_DELAY)) {
break;
}
P2PSessionRequest_t data;
memset(&data, 0, sizeof(data));
data.m_steamIDRemote = source_id;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
new_connections_to_call_cb.pop();
}
//TODO: not sure if sockets should be wiped right away
remove_killed_connection_sockets();
@ -816,11 +913,12 @@ void Callback(Common_Message *msg)
}PRINT_DEBUG("\n");
#endif
if (msg->network().type() == Network::DATA) {
messages.push_back(Common_Message(*msg));
if (msg->network().type() == Network_pb::DATA) {
unprocessed_messages.push_back(Common_Message(*msg));
}
if (msg->network().type() == Network::NEW_CONNECTION) {
if (msg->network().type() == Network_pb::NEW_CONNECTION) {
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
auto msg_temp = std::begin(messages);
while (msg_temp != std::end(messages)) {
//only delete processed to handle unreliable message arriving at the same time.

View File

@ -0,0 +1,189 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_Networking_Messages :
public ISteamNetworkingMessages
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_networking_messages_callback\n");
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
steam_networking_messages->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_networking_messages_run_every_runcb\n");
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
steam_networking_messages->RunCallbacks();
}
Steam_Networking_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
this->run_every_runcb->add(&Steam_Networking_Messages::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_Networking_Messages()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_Networking_Messages::steam_run_every_runcb, this);
}
/// Sends a message to the specified host. If we don't already have a session with that user,
/// a session is implicitly created. There might be some handshaking that needs to happen
/// before we can actually begin sending message data. If this handshaking fails and we can't
/// get through, an error will be posted via the callback SteamNetworkingMessagesSessionFailed_t.
/// There is no notification when the operation succeeds. (You should have the peer send a reply
/// for this purpose.)
///
/// Sending a message to a host will also implicitly accept any incoming connection from that host.
///
/// nSendFlags is a bitmask of k_nSteamNetworkingSend_xxx options
///
/// nRemoteChannel is a routing number you can use to help route message to different systems.
/// You'll have to call ReceiveMessagesOnChannel() with the same channel number in order to retrieve
/// the data on the other end.
///
/// Using different channels to talk to the same user will still use the same underlying
/// connection, saving on resources. If you don't need this feature, use 0.
/// Otherwise, small integers are the most efficient.
///
/// It is guaranteed that reliable messages to the same host on the same channel
/// will be be received by the remote host (if they are received at all) exactly once,
/// and in the same order that they were send.
///
/// NO other order guarantees exist! In particular, unreliable messages may be dropped,
/// received out of order with respect to each other and with respect to reliable data,
/// or may be received multiple times. Messages on different channels are *not* guaranteed
/// to be received in the order they were sent.
///
/// A note for those familiar with TCP/IP ports, or converting an existing codebase that
/// opened multiple sockets: You might notice that there is only one channel, and with
/// TCP/IP each endpoint has a port number. You can think of the channel number as the
/// *destination* port. If you need each message to also include a "source port" (so the
/// recipient can route the reply), then just put that in your message. That is essentially
/// how UDP works!
///
/// Returns:
/// - k_EREsultOK on success.
/// - k_EResultNoConnection will be returned if the session has failed or was closed by the peer,
/// and k_nSteamNetworkingSend_AutoRestartBrokwnSession is not used. (You can use
/// GetSessionConnectionInfo to get the details.) In order to acknowledge the broken session
/// and start a new one, you must call CloseSessionWithUser
/// - See SendMessageToConnection::SendMessageToConnection for more
EResult SendMessageToUser( const SteamNetworkingIdentity &identityRemote, const void *pubData, uint32 cubData, int nSendFlags, int nRemoteChannel )
{
PRINT_DEBUG("Steam_Networking_Messages::SendMessageToUser\n");
return k_EResultNoConnection;
}
/// Reads the next message that has been sent from another user via SendMessageToUser() on the given channel.
/// Returns number of messages returned into your list. (0 if no message are available on that channel.)
///
/// When you're done with the message object(s), make sure and call Release()!
int ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
{
PRINT_DEBUG("Steam_Networking_Messages::ReceiveMessagesOnChannel\n");
return 0;
}
/// AcceptSessionWithUser() should only be called in response to a SteamP2PSessionRequest_t callback
/// SteamP2PSessionRequest_t will be posted if another user tries to send you a message, and you haven't
/// tried to talk to them. If you don't want to talk to them, just ignore the request.
/// If the user continues to send you messages, SteamP2PSessionRequest_t callbacks will continue to
/// be posted periodically. This may be called multiple times for a single user.
///
/// Calling SendMessage() on the other user, this implicitly accepts any pending session request.
bool AcceptSessionWithUser( const SteamNetworkingIdentity &identityRemote )
{
PRINT_DEBUG("Steam_Networking_Messages::AcceptSessionWithUser\n");
return false;
}
/// Call this when you're done talking to a user to immediately free up resources under-the-hood.
/// If the remote user tries to send data to you again, another P2PSessionRequest_t callback will
/// be posted.
///
/// Note that sessions that go unused for a few minutes are automatically timed out.
bool CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote )
{
PRINT_DEBUG("Steam_Networking_Messages::CloseSessionWithUser\n");
return false;
}
/// Call this when you're done talking to a user on a specific channel. Once all
/// open channels to a user have been closed, the open session to the user will be
/// closed, and any new data from this user will trigger a SteamP2PSessionRequest_t
/// callback
bool CloseChannelWithUser( const SteamNetworkingIdentity &identityRemote, int nLocalChannel )
{
PRINT_DEBUG("Steam_Networking_Messages::CloseChannelWithUser\n");
return false;
}
/// Returns information about the latest state of a connection, if any, with the given peer.
/// Primarily intended for debugging purposes, but can also be used to get more detailed
/// failure information. (See SendMessageToUser and k_nSteamNetworkingSend_AutoRestartBrokwnSession.)
///
/// Returns the value of SteamNetConnectionInfo_t::m_eState, or k_ESteamNetworkingConnectionState_None
/// if no connection exists with specified peer. You may pass nullptr for either parameter if
/// you do not need the corresponding details. Note that sessions time out after a while,
/// so if a connection fails, or SendMessageToUser returns SendMessageToUser, you cannot wait
/// indefinitely to obtain the reason for failure.
ESteamNetworkingConnectionState GetSessionConnectionInfo( const SteamNetworkingIdentity &identityRemote, SteamNetConnectionInfo_t *pConnectionInfo, SteamNetworkingQuickConnectionStatus *pQuickStatus )
{
PRINT_DEBUG("Steam_Networking_Messages::GetSessionConnectionInfo\n");
return k_ESteamNetworkingConnectionState_None;
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
}
};

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,8 @@
class Steam_Networking_Sockets_Serialized :
public ISteamNetworkingSocketsSerialized002,
public ISteamNetworkingSocketsSerialized003
public ISteamNetworkingSocketsSerialized003,
public ISteamNetworkingSocketsSerialized004
{
class Settings *settings;
class Networking *network;
@ -116,6 +117,17 @@ void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::PostConnectionStateMsg\n");
}
bool GetSTUNServer(int dont_know, char *buf, unsigned int len)
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetSTUNServer %i %p %u\n", dont_know, buf, len);
return false;
}
bool BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity)
{
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::BAllowDirectConnectToPeer\n");
return true;
}
void RunCallbacks()
{

View File

@ -18,6 +18,8 @@
#include "base.h"
class Steam_Networking_Utils :
public ISteamNetworkingUtils001,
public ISteamNetworkingUtils002,
public ISteamNetworkingUtils
{
class Settings *settings;
@ -27,6 +29,7 @@ public ISteamNetworkingUtils
class RunEveryRunCB *run_every_runcb;
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function;
bool relay_initialized = false;
public:
static void steam_callback(void *object, Common_Message *msg)
@ -63,46 +66,114 @@ Steam_Networking_Utils(class Settings *settings, class Networking *network, clas
this->run_every_runcb->remove(&Steam_Networking_Utils::steam_run_every_runcb, this);
}
/// Allocate and initialize a message object. Usually the reason
/// you call this is to pass it to ISteamNetworkingSockets::SendMessages.
/// The returned object will have all of the relevant fields cleared to zero.
///
/// Optionally you can also request that this system allocate space to
/// hold the payload itself. If cbAllocateBuffer is nonzero, the system
/// will allocate memory to hold a payload of at least cbAllocateBuffer bytes.
/// m_pData will point to the allocated buffer, m_cbSize will be set to the
/// size, and m_pfnFreeData will be set to the proper function to free up
/// the buffer.
///
/// If cbAllocateBuffer=0, then no buffer is allocated. m_pData will be NULL,
/// m_cbSize will be zero, and m_pfnFreeData will be NULL. You will need to
/// set each of these.
///
/// You can use SteamNetworkingMessage_t::Release to free up the message
/// bookkeeping object and any associated buffer. See
/// ISteamNetworkingSockets::SendMessages for details on reference
/// counting and ownership.
SteamNetworkingMessage_t *AllocateMessage( int cbAllocateBuffer )
{
PRINT_DEBUG("Steam_Networking_Utils::AllocateMessage\n");
//TODO
return NULL;
}
bool InitializeRelayAccess()
{
PRINT_DEBUG("Steam_Networking_Utils::InitializeRelayAccess\n");
relay_initialized = true;
return true;
}
/// Fetch current status of the relay network.
///
/// SteamRelayNetworkStatus_t is also a callback. It will be triggered on
/// both the user and gameserver interfaces any time the status changes, or
/// ping measurement starts or stops.
///
/// SteamRelayNetworkStatus_t::m_eAvail is returned. If you want
/// more details, you can pass a non-NULL value.
ESteamNetworkingAvailability GetRelayNetworkStatus( SteamRelayNetworkStatus_t *pDetails )
{
PRINT_DEBUG("Steam_Networking_Utils::GetRelayNetworkStatus %p\n", pDetails);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
//TODO: check if this is how real steam returns it
SteamRelayNetworkStatus_t data = {};
if (relay_initialized) {
data.m_eAvail = k_ESteamNetworkingAvailability_Current;
data.m_bPingMeasurementInProgress = 0;
data.m_eAvailAnyRelay = k_ESteamNetworkingAvailability_Current;
data.m_eAvailNetworkConfig = k_ESteamNetworkingAvailability_Current;
strcpy(data.m_debugMsg, "OK");
}
if (pDetails) {
*pDetails = data;
}
return k_ESteamNetworkingAvailability_Current;
}
float GetLocalPingLocation( SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG("Steam_Networking_Utils::GetLocalPingLocation\n");
if (relay_initialized) {
result.m_data[2] = 123;
result.m_data[8] = 67;
return 2.0;
}
return -1;
}
int EstimatePingTimeBetweenTwoLocations( const SteamNetworkPingLocation_t &location1, const SteamNetworkPingLocation_t &location2 )
{
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeBetweenTwoLocations\n");
return k_nSteamNetworkingPing_Unknown ;
//return k_nSteamNetworkingPing_Unknown;
return 10;
}
int EstimatePingTimeFromLocalHost( const SteamNetworkPingLocation_t &remoteLocation )
{
PRINT_DEBUG("Steam_Networking_Utils::EstimatePingTimeFromLocalHost\n");
return 10;
}
void ConvertPingLocationToString( const SteamNetworkPingLocation_t &location, char *pszBuf, int cchBufSize )
{
PRINT_DEBUG("Steam_Networking_Utils::ConvertPingLocationToString\n");
strncpy(pszBuf, "fra=10+2", cchBufSize);
}
bool ParsePingLocationString( const char *pszString, SteamNetworkPingLocation_t &result )
{
PRINT_DEBUG("Steam_Networking_Utils::ParsePingLocationString\n");
return true;
}
bool CheckPingDataUpToDate( float flMaxAgeSeconds )
{
PRINT_DEBUG("Steam_Networking_Utils::CheckPingDataUpToDate %f\n", flMaxAgeSeconds);
relay_initialized = true;
return true;
}
@ -110,30 +181,35 @@ bool CheckPingDataUpToDate( float flMaxAgeSeconds )
bool IsPingMeasurementInProgress()
{
PRINT_DEBUG("Steam_Networking_Utils::IsPingMeasurementInProgress\n");
return false;
}
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
{
PRINT_DEBUG("Steam_Networking_Utils::GetPingToDataCenter\n");
return 0;
}
int GetDirectPingToPOP( SteamNetworkingPOPID popID )
{
PRINT_DEBUG("Steam_Networking_Utils::GetDirectPingToPOP\n");
return 0;
}
int GetPOPCount()
{
PRINT_DEBUG("Steam_Networking_Utils::GetPOPCount\n");
return 0;
}
int GetPOPList( SteamNetworkingPOPID *list, int nListSz )
{
PRINT_DEBUG("Steam_Networking_Utils::GetPOPList\n");
return 0;
}
@ -219,6 +295,7 @@ bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigS
ESteamNetworkingConfigDataType eDataType, const void *pArg )
{
PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue\n");
return false;
}
@ -233,6 +310,7 @@ ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue
ESteamNetworkingConfigDataType *pOutDataType, void *pResult, size_t *cbResult )
{
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValue\n");
return k_ESteamNetworkingGetConfigValue_BadValue;
}
@ -243,6 +321,7 @@ ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue
bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
{
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValueInfo\n");
return false;
}
@ -250,6 +329,7 @@ bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutNa
ESteamNetworkingConfigValue GetFirstConfigValue()
{
PRINT_DEBUG("Steam_Networking_Utils::GetFirstConfigValue\n");
return k_ESteamNetworkingConfig_Invalid;
}
@ -258,21 +338,291 @@ ESteamNetworkingConfigValue GetFirstConfigValue()
void SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ToString\n");
if (buf == nullptr || cbBuf == 0)
return;
char buffer[64]; // Its enought for ipv4 & ipv6 + port
std::string str_addr;
if (addr.IsIPv4())
{
in_addr ipv4_addr;
ipv4_addr.s_addr = htonl(addr.GetIPv4());
if (inet_ntop(AF_INET, &ipv4_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
{
if (bWithPort)
{
str_addr = buffer;
str_addr += ':';
str_addr += std::to_string(addr.m_port);
}
else
{
str_addr = buffer;
}
}
}
else
{
in6_addr ipv6_addr;
memcpy(ipv6_addr.s6_addr, addr.m_ipv6, sizeof(addr.m_ipv6));
if (inet_ntop(AF_INET6, &ipv6_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
{
if (bWithPort)
{
str_addr = '[';
str_addr += buffer;
str_addr += "]:";
str_addr += std::to_string(addr.m_port);
}
else
{
str_addr = buffer;
}
}
}
cbBuf = std::min<size_t>(cbBuf, str_addr.length() + 1);
strncpy(buf, str_addr.c_str(), cbBuf);
buf[cbBuf - 1] = '\0';
}
bool SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ParseString\n");
bool valid = false;
if (pAddr == nullptr || pszStr == nullptr)
return valid;
std::string str(pszStr);
size_t pos = str.find(':');
if (pos != std::string::npos)
{// Try ipv4 with port
in_addr ipv4_addr;
std::string tmp(str);
tmp[pos] = 0;
const char* ip = tmp.c_str();
const char* port = &tmp[pos + 1];
if (inet_pton(AF_INET, ip, &ipv4_addr) == 1)
{
valid = true;
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), strtoul(port, nullptr, 10));
}
}
else
{// Try ipv4 without port
in_addr ipv4_addr;
if (inet_pton(AF_INET, str.c_str(), &ipv4_addr) == 1)
{
valid = true;
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), 0);
}
}
if (!valid)
{// Try ipv6
addrinfo* info = nullptr;
addrinfo hints = {};
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
size_t sep_pos = 0;
std::string ip;
int sep_count = 0;
for (int i = 0; i < str.length(); ++i)
{
if (str[i] == ':')
{
sep_pos = i;
++sep_count;
}
}
if (sep_count == 8)
{
ip = std::move(std::string(str.begin(), str.begin() + sep_pos));
}
else
{
ip = str;
}
if (getaddrinfo(ip.c_str(), nullptr, &hints, &info) == 0)
{
sockaddr_in6* maddr = (sockaddr_in6*)info->ai_addr;
size_t pos = str.find(']');
std::string str_port("0");
if (pos != std::string::npos)
{
str_port = std::move(std::string(str.begin() + pos + 2, str.end()));
}
else if (sep_count == 8)
{
str_port = std::move(std::string(str.begin() + sep_pos + 1, str.end()));
}
try
{
int port = std::stoi(str_port);
if (port >= 0 && port <= 65535)
{
pAddr->SetIPv6(maddr->sin6_addr.s6_addr, port);
valid = true;
}
}
catch(...)
{ }
}
if (info)
{
freeaddrinfo(info);
}
}
if (!valid)
{
pAddr->Clear();
}
return valid;
}
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ToString\n");
if (buf == nullptr)
return;
std::string str;
str.reserve(SteamNetworkingIdentity::k_cchMaxString);
switch (identity.m_eType)
{
case k_ESteamNetworkingIdentityType_SteamID:
{
str = "steamid:";
str += std::move(std::to_string(identity.GetSteamID64()));
}
break;
case k_ESteamNetworkingIdentityType_IPAddress:
{
str = "ip:";
char buff[SteamNetworkingIPAddr::k_cchMaxString];
auto& addr = *identity.GetIPAddr();
SteamNetworkingIPAddr_ToString(addr, buff, sizeof(buff), true);
str += buff;
}
break;
case k_ESteamNetworkingIdentityType_GenericBytes:
{
int generic_len;
const uint8* pBuf = identity.GetGenericBytes(generic_len);
str = "gen:";
str.resize(4 + (generic_len * 2));
char* pDest = &str[4];
while(generic_len--)
{
// I don't care for the last char, I've reserved the max string size
snprintf(pDest, 3, "%02x", *pBuf);
++pBuf;
pDest += 2;
}
}
break;
case k_ESteamNetworkingIdentityType_GenericString:
{
str = "str:";
str += identity.GetGenericString();
}
break;
case k_ESteamNetworkingIdentityType_UnknownType:
{
str = identity.m_szUnknownRawString;
}
break;
}
cbBuf = std::min<size_t>(cbBuf, str.length() + 1);
strncpy(buf, str.c_str(), cbBuf);
buf[cbBuf - 1] = '\0';
}
bool SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
{
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ParseString\n");
bool valid = false;
if (pIdentity == nullptr)
{
return valid;
}
if (pszStr != nullptr)
{
const char* end = strchr(pszStr, ':');
if (end != nullptr)
{
++end;
if (strncmp(pszStr, "gen:", end - pszStr) == 0)
{
size_t length = strlen(end);
if (!(length % 2) && length <= (sizeof(pIdentity->m_genericBytes) * 2))
{// Must be even
valid = true;
length /= 2;
pIdentity->m_eType = k_ESteamNetworkingIdentityType_GenericBytes;
pIdentity->m_cbSize = length;
uint8* pBytes = pIdentity->m_genericBytes;
char hex[3] = { 0,0,0 };
while (length)
{
hex[0] = end[0];
hex[1] = end[1];
// Steam doesn't check if wasn't a hex char
*pBytes = strtol(hex, nullptr, 16);
++pBytes;
end += 2;
--length;
}
}
}
else if (strncmp(pszStr, "steamid:", end - pszStr) == 0)
{
CSteamID steam_id(uint64(strtoull(end, nullptr, 10)));
if (steam_id.IsValid())
{
valid = true;
pIdentity->SetSteamID(steam_id);
}
}
else if (strncmp(pszStr, "str:", end - pszStr) == 0)
{
valid = pIdentity->SetGenericString(end);
}
else if (strncmp(pszStr, "ip:", end - pszStr) == 0)
{
SteamNetworkingIPAddr steam_addr;
if (SteamNetworkingIPAddr_ParseString(&steam_addr, end))
{
valid = true;
pIdentity->SetIPAddr(steam_addr);
}
}
}
}
return valid;
}

View File

@ -71,16 +71,19 @@ Steam_Parties(class Settings *settings, class Networking *network, class SteamCa
uint32 GetNumActiveBeacons()
{
PRINT_DEBUG("Steam_Parties::GetNumActiveBeacons\n");
return 0;
}
PartyBeaconID_t GetBeaconByIndex( uint32 unIndex )
{
PRINT_DEBUG("Steam_Parties::GetBeaconByIndex\n");
return k_ulPartyBeaconIdInvalid;
}
bool GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata )
{
PRINT_DEBUG("Steam_Parties::GetBeaconDetails\n");
return false;
}
@ -90,6 +93,7 @@ STEAM_CALL_RESULT( JoinPartyCallback_t )
SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
{
PRINT_DEBUG("Steam_Parties::JoinParty\n");
return 0;
}
@ -100,11 +104,13 @@ SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
bool GetNumAvailableBeaconLocations( uint32 *puNumLocations )
{
PRINT_DEBUG("Steam_Parties::GetNumAvailableBeaconLocations\n");
return false;
}
bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations )
{
PRINT_DEBUG("Steam_Parties::GetAvailableBeaconLocations\n");
return false;
}
@ -116,6 +122,7 @@ STEAM_CALL_RESULT( CreateBeaconCallback_t )
SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata )
{
PRINT_DEBUG("Steam_Parties::CreateBeacon\n");
return 0;
}
@ -143,6 +150,7 @@ STEAM_CALL_RESULT( ChangeNumOpenSlotsCallback_t )
SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots )
{
PRINT_DEBUG("Steam_Parties::ChangeNumOpenSlots\n");
return 0;
}
@ -150,6 +158,7 @@ SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots
bool DestroyBeacon( PartyBeaconID_t ulBeacon )
{
PRINT_DEBUG("Steam_Parties::DestroyBeacon\n");
return false;
}
@ -157,6 +166,7 @@ bool DestroyBeacon( PartyBeaconID_t ulBeacon )
bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut )
{
PRINT_DEBUG("Steam_Parties::GetBeaconLocationData\n");
return false;
}

View File

@ -31,6 +31,10 @@ struct Stream_Write {
std::vector<char> file_data;
};
struct Downloaded_File {
std::string file;
uint64 total_size;
};
class Steam_Remote_Storage :
public ISteamRemoteStorage001,
@ -49,19 +53,23 @@ public ISteamRemoteStorage013,
public ISteamRemoteStorage
{
private:
class Settings *settings;
Local_Storage *local_storage;
class SteamCallResults *callback_results;
bool steam_cloud_enabled;
std::vector<struct Async_Read> async_reads;
std::vector<struct Stream_Write> stream_writes;
std::map<UGCHandle_t, std::string> shared_files;
std::map<UGCHandle_t, struct Downloaded_File> downloaded_files;
public:
Steam_Remote_Storage(Local_Storage *local_storage, class SteamCallResults *callback_results)
Steam_Remote_Storage(class Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results)
{
this->settings = settings;
this->local_storage = local_storage;
this->callback_results = callback_results;
steam_cloud_enabled = true;
local_storage->update_save_filenames(REMOTE_STORAGE_FOLDER);
local_storage->update_save_filenames(Local_Storage::remote_storage_folder);
}
// NOTE
@ -75,8 +83,12 @@ Steam_Remote_Storage(Local_Storage *local_storage, class SteamCallResults *callb
bool FileWrite( const char *pchFile, const void *pvData, int32 cubData )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWrite %s %u\n", pchFile, cubData);
if (!pchFile || cubData <= 0 || cubData > k_unMaxCloudFileChunkSize) {
return false;
}
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int data_stored = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData);
int data_stored = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData);
PRINT_DEBUG("Steam_Remote_Storage::Stored %i, %u\n", data_stored, data_stored == cubData);
return data_stored == cubData;
}
@ -85,7 +97,7 @@ int32 FileRead( const char *pchFile, void *pvData, int32 cubDataToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::FileRead %s %i\n", pchFile, cubDataToRead);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubDataToRead);
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubDataToRead);
if (read_data < 0) read_data = 0;
PRINT_DEBUG("Read %i\n", read_data);
return read_data;
@ -95,12 +107,16 @@ STEAM_CALL_RESULT( RemoteStorageFileWriteAsyncComplete_t )
SteamAPICall_t FileWriteAsync( const char *pchFile, const void *pvData, uint32 cubData )
{
PRINT_DEBUG("Steam_Remote_Storage::FileWriteAsync\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool success = local_storage->store_data(REMOTE_STORAGE_FOLDER, pchFile, (char* )pvData, cubData) == cubData;
RemoteStorageFileWriteAsyncComplete_t data;
data.m_eResult = k_EResultOK;
if (!pchFile || cubData > k_unMaxCloudFileChunkSize || cubData == 0) {
return k_uAPICallInvalid;
}
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
std::lock_guard<std::recursive_mutex> lock(global_mutex);
bool success = local_storage->store_data(Local_Storage::remote_storage_folder, pchFile, (char* )pvData, cubData) == cubData;
RemoteStorageFileWriteAsyncComplete_t data;
data.m_eResult = success ? k_EResultOK : k_EResultFail;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data), 0.01);
}
@ -110,7 +126,7 @@ SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToR
PRINT_DEBUG("Steam_Remote_Storage::FileReadAsync\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
unsigned int size = local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile);
unsigned int size = local_storage->file_size(Local_Storage::remote_storage_folder, pchFile);
RemoteStorageFileReadAsyncComplete_t data;
if (size <= nOffset) {
@ -128,7 +144,7 @@ SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToR
a_read.size = size;
async_reads.push_back(a_read);
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data));
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0);
return data.m_hFileReadAsync;
}
@ -145,7 +161,7 @@ bool FileReadAsyncComplete( SteamAPICall_t hReadCall, void *pvBuffer, uint32 cub
return false;
char *temp = new char[a_read->size];
int read_data = local_storage->get_data(REMOTE_STORAGE_FOLDER, a_read->file_name, (char* )temp, a_read->size);
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, a_read->file_name, (char* )temp, a_read->size);
if (read_data < a_read->to_read + a_read->offset) {
delete[] temp;
return false;
@ -167,7 +183,7 @@ bool FileForget( const char *pchFile )
bool FileDelete( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileDelete\n");
return local_storage->file_delete(REMOTE_STORAGE_FOLDER, pchFile);
return local_storage->file_delete(Local_Storage::remote_storage_folder, pchFile);
}
STEAM_CALL_RESULT( RemoteStorageFileShareResult_t )
@ -176,9 +192,14 @@ SteamAPICall_t FileShare( const char *pchFile )
PRINT_DEBUG("Steam_Remote_Storage::FileShare\n");
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageFileShareResult_t data = {};
data.m_eResult = k_EResultOK;
data.m_hFile = generate_steam_api_call_id();
strncpy(data.m_rgchFilename, pchFile, sizeof(data.m_rgchFilename) - 1);
if (local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile)) {
data.m_eResult = k_EResultOK;
data.m_hFile = generate_steam_api_call_id();
strncpy(data.m_rgchFilename, pchFile, sizeof(data.m_rgchFilename) - 1);
shared_files[data.m_hFile] = pchFile;
} else {
data.m_eResult = k_EResultFileNotFound;
}
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
@ -224,7 +245,7 @@ bool FileWriteStreamClose( UGCFileWriteStreamHandle_t writeHandle )
if (stream_writes.end() == request)
return false;
local_storage->store_data(REMOTE_STORAGE_FOLDER, request->file_name, request->file_data.data(), request->file_data.size());
local_storage->store_data(Local_Storage::remote_storage_folder, request->file_name, request->file_data.data(), request->file_data.size());
stream_writes.erase(request);
return true;
}
@ -245,25 +266,25 @@ bool FileWriteStreamCancel( UGCFileWriteStreamHandle_t writeHandle )
bool FileExists( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FileExists %s\n", pchFile);
return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile);
return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile);
}
bool FilePersisted( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::FilePersisted\n");
return local_storage->file_exists(REMOTE_STORAGE_FOLDER, pchFile);
return local_storage->file_exists(Local_Storage::remote_storage_folder, pchFile);
}
int32 GetFileSize( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileSize %s\n", pchFile);
return local_storage->file_size(REMOTE_STORAGE_FOLDER, pchFile);
return local_storage->file_size(Local_Storage::remote_storage_folder, pchFile);
}
int64 GetFileTimestamp( const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileTimestamp\n");
return local_storage->file_timestamp(REMOTE_STORAGE_FOLDER, pchFile);
return local_storage->file_timestamp(Local_Storage::remote_storage_folder, pchFile);
}
ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile )
@ -277,7 +298,7 @@ ERemoteStoragePlatform GetSyncPlatforms( const char *pchFile )
int32 GetFileCount()
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileCount\n");
int32 num = local_storage->count_files(REMOTE_STORAGE_FOLDER);
int32 num = local_storage->count_files(Local_Storage::remote_storage_folder);
PRINT_DEBUG("Steam_Remote_Storage::File count: %i\n", num);
return num;
}
@ -286,7 +307,7 @@ const char *GetFileNameAndSize( int iFile, int32 *pnFileSizeInBytes )
{
PRINT_DEBUG("Steam_Remote_Storage::GetFileNameAndSize %i\n", iFile);
static char output_filename[MAX_FILENAME_LENGTH];
if (local_storage->iterate_file(REMOTE_STORAGE_FOLDER, iFile, output_filename, pnFileSizeInBytes)) {
if (local_storage->iterate_file(Local_Storage::remote_storage_folder, iFile, output_filename, pnFileSizeInBytes)) {
PRINT_DEBUG("Steam_Remote_Storage::Name: |%s|, size: %i\n", output_filename, pnFileSizeInBytes ? *pnFileSizeInBytes : 0);
return output_filename;
} else {
@ -354,12 +375,28 @@ STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownload( UGCHandle_t hContent, uint32 unPriority )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload\n");
RemoteStorageDownloadUGCResult_t data = {};
if (shared_files.count(hContent)) {
data.m_eResult = k_EResultOK;
data.m_hFile = hContent;
data.m_nAppID = settings->get_local_game_id().AppID();
data.m_nSizeInBytes = local_storage->file_size(Local_Storage::remote_storage_folder, shared_files[hContent]);
shared_files[hContent].copy(data.m_pchFileName, sizeof(data.m_pchFileName) - 1);
data.m_ulSteamIDOwner = settings->get_local_steam_id().ConvertToUint64();
downloaded_files[hContent].file = shared_files[hContent];
downloaded_files[hContent].total_size = data.m_nSizeInBytes;
} else {
data.m_eResult = k_EResultFileNotFound; //TODO: not sure if this is the right result
}
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
}
STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownload( UGCHandle_t hContent )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownload old\n");
return UGCDownload(hContent, 1);
}
@ -368,11 +405,13 @@ SteamAPICall_t UGCDownload( UGCHandle_t hContent )
bool GetUGCDownloadProgress( UGCHandle_t hContent, int32 *pnBytesDownloaded, int32 *pnBytesExpected )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress\n");
return false;
}
bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, uint32 *pnBytesExpected )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress old\n");
return false;
}
@ -380,6 +419,7 @@ bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, ui
bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, STEAM_OUT_STRING() char **ppchName, int32 *pnFileSizeInBytes, STEAM_OUT_STRUCT() CSteamID *pSteamIDOwner )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDetails\n");
return false;
}
@ -392,27 +432,44 @@ bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, STEAM_OUT_STRING() c
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset, EUGCReadAction eAction )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead\n");
if (!downloaded_files.count(hContent) || cubDataToRead < 0) {
return -1; //TODO: is this the right return value?
}
Downloaded_File f = downloaded_files[hContent];
int read_data = local_storage->get_data(Local_Storage::remote_storage_folder, f.file, (char* )pvData, cubDataToRead, cOffset);
if (eAction == k_EUGCRead_Close || (eAction == k_EUGCRead_ContinueReadingUntilFinished && (read_data < cubDataToRead || (cOffset + cubDataToRead) >= f.total_size))) {
downloaded_files.erase(hContent);
}
PRINT_DEBUG("Read %i\n", read_data);
return read_data;
}
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old\n");
return UGCRead( hContent, pvData, cubDataToRead, 0);
}
int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 cOffset)
{
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old\n");
PRINT_DEBUG("Steam_Remote_Storage::UGCRead old2\n");
return UGCRead(hContent, pvData, cubDataToRead, cOffset, k_EUGCRead_ContinueReadingUntilFinished);
}
// Functions to iterate through UGC that has finished downloading but has not yet been read via UGCRead()
int32 GetCachedUGCCount()
{
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCCount\n");
return 0;
}
UGCHandle_t GetCachedUGCHandle( int32 iCachedContent )
{
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCHandle\n");
return k_UGCHandleInvalid;
}
@ -465,62 +522,74 @@ STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags, EWorkshopFileType eWorkshopFileType )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile\n");
return 0;
}
PublishedFileUpdateHandle_t CreatePublishedFileUpdateRequest( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::CreatePublishedFileUpdateRequest\n");
return 0;
}
bool UpdatePublishedFileFile( PublishedFileUpdateHandle_t updateHandle, const char *pchFile )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileFile\n");
return false;
}
SteamAPICall_t PublishFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishFile\n");
return 0;
}
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile old\n");
return 0;
}
SteamAPICall_t UpdatePublishedFile( RemoteStorageUpdatePublishedFileRequest_t updatePublishedFileRequest )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFile\n");
return 0;
}
bool UpdatePublishedFilePreviewFile( PublishedFileUpdateHandle_t updateHandle, const char *pchPreviewFile )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFilePreviewFile\n");
return false;
}
bool UpdatePublishedFileTitle( PublishedFileUpdateHandle_t updateHandle, const char *pchTitle )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTitle\n");
return false;
}
bool UpdatePublishedFileDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchDescription )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileDescription\n");
return false;
}
bool UpdatePublishedFileVisibility( PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileVisibility\n");
return false;
}
bool UpdatePublishedFileTags( PublishedFileUpdateHandle_t updateHandle, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTags\n");
return false;
}
STEAM_CALL_RESULT( RemoteStorageUpdatePublishedFileResult_t )
SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHandle )
{
PRINT_DEBUG("Steam_Remote_Storage::CommitPublishedFileUpdate\n");
return 0;
}
// Gets published file details for the given publishedfileid. If unMaxSecondsOld is greater than 0,
@ -529,19 +598,30 @@ SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHand
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails\n");
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails %llu\n", unPublishedFileId);
//TODO: check what this function really returns
return 0;
/*
std::lock_guard<std::recursive_mutex> lock(global_mutex);
RemoteStorageGetPublishedFileDetailsResult_t data = {};
data.m_eResult = k_EResultFail;
data.m_nPublishedFileId = unPublishedFileId;
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
*/
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails old\n");
return GetPublishedFileDetails(unPublishedFileId, 0);
}
STEAM_CALL_RESULT( RemoteStorageDeletePublishedFileResult_t )
SteamAPICall_t DeletePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::DeletePublishedFile\n");
return 0;
}
// enumerate the files that the current user published with this app
@ -562,6 +642,7 @@ STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
SteamAPICall_t SubscribePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::SubscribePublishedFile\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t )
@ -582,29 +663,34 @@ STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
SteamAPICall_t UnsubscribePublishedFile( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::UnsubscribePublishedFile\n");
return 0;
}
bool UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription\n");
return false;
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
SteamAPICall_t GetPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedItemVoteDetails\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t )
SteamAPICall_t UpdateUserPublishedItemVote( PublishedFileId_t unPublishedFileId, bool bVoteUp )
{
PRINT_DEBUG("Steam_Remote_Storage::UpdateUserPublishedItemVote\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
SteamAPICall_t GetUserPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
{
PRINT_DEBUG("Steam_Remote_Storage::GetUserPublishedItemVoteDetails\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
@ -631,24 +717,28 @@ STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishVideo( EWorkshopVideoProvider eVideoProvider, const char *pchVideoAccount, const char *pchVideoIdentifier, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
SteamAPICall_t PublishVideo(const char *pchFileName, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
{
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo old\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageSetUserPublishedFileActionResult_t )
SteamAPICall_t SetUserPublishedFileAction( PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction )
{
PRINT_DEBUG("Steam_Remote_Storage::SetUserPublishedFileAction\n");
return 0;
}
STEAM_CALL_RESULT( RemoteStorageEnumeratePublishedFilesByUserActionResult_t )
SteamAPICall_t EnumeratePublishedFilesByUserAction( EWorkshopFileAction eAction, uint32 unStartIndex )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedFilesByUserAction\n");
return 0;
}
// this method enumerates the public view of workshop files
@ -656,6 +746,7 @@ STEAM_CALL_RESULT( RemoteStorageEnumerateWorkshopFilesResult_t )
SteamAPICall_t EnumeratePublishedWorkshopFiles( EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, SteamParamStringArray_t *pTags, SteamParamStringArray_t *pUserTags )
{
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedWorkshopFiles\n");
return 0;
}
@ -663,6 +754,7 @@ STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
SteamAPICall_t UGCDownloadToLocation( UGCHandle_t hContent, const char *pchLocation, uint32 unPriority )
{
PRINT_DEBUG("Steam_Remote_Storage::UGCDownloadToLocation\n");
return 0;
}
};

137
dll/steam_remoteplay.h Normal file
View File

@ -0,0 +1,137 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_RemotePlay :
public ISteamRemotePlay
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_remoteplay_callback\n");
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
steam_remoteplay->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_remoteplay_run_every_runcb\n");
Steam_RemotePlay *steam_remoteplay = (Steam_RemotePlay *)object;
steam_remoteplay->RunCallbacks();
}
Steam_RemotePlay(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_RemotePlay::steam_callback, this);
this->run_every_runcb->add(&Steam_RemotePlay::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_RemotePlay()
{
//TODO rm network callbacks
this->run_every_runcb->remove(&Steam_RemotePlay::steam_run_every_runcb, this);
}
// Get the number of currently connected Steam Remote Play sessions
uint32 GetSessionCount()
{
PRINT_DEBUG("Steam_RemotePlay::GetSessionCount\n");
return 0;
}
// Get the currently connected Steam Remote Play session ID at the specified index. Returns zero if index is out of bounds.
uint32 GetSessionID( int iSessionIndex )
{
PRINT_DEBUG("Steam_RemotePlay::GetSessionID\n");
return 0;
}
// Get the SteamID of the connected user
CSteamID GetSessionSteamID( uint32 unSessionID )
{
PRINT_DEBUG("Steam_RemotePlay::GetSessionSteamID\n");
return k_steamIDNil;
}
// Get the name of the session client device
// This returns NULL if the sessionID is not valid
const char *GetSessionClientName( uint32 unSessionID )
{
PRINT_DEBUG("Steam_RemotePlay::GetSessionClientName\n");
return NULL;
}
// Get the form factor of the session client device
ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID )
{
PRINT_DEBUG("Steam_RemotePlay::GetSessionClientFormFactor\n");
return k_ESteamDeviceFormFactorUnknown;
}
// Get the resolution, in pixels, of the session client device
// This is set to 0x0 if the resolution is not available
bool BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY )
{
PRINT_DEBUG("Steam_RemotePlay::BGetSessionClientResolution\n");
return false;
}
// Invite a friend to Remote Play Together
// This returns false if the invite can't be sent
bool BSendRemotePlayTogetherInvite( CSteamID steamIDFriend )
{
PRINT_DEBUG("Steam_RemotePlay::BSendRemotePlayTogetherInvite\n");
return false;
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

View File

@ -17,12 +17,40 @@
#include "steam_screenshots.h"
Steam_Screenshots::Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks) :
local_storage(local_storage),
callbacks(callbacks)
{
}
ScreenshotHandle Steam_Screenshots::create_screenshot_handle()
{
static ScreenshotHandle handle = 100;
return handle++;
}
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight )
{
PRINT_DEBUG("WriteScreenshot\n");
return INVALID_SCREENSHOT_HANDLE;
char buff[128];
auto now = std::chrono::system_clock::now();
time_t now_time;
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
std::string screenshot_name = buff;
screenshot_name += ".png";
if (!local_storage->save_screenshot( screenshot_name, (uint8_t*)pubRGB, nWidth, nHeight, 3))
return INVALID_SCREENSHOT_HANDLE;
auto handle = create_screenshot_handle();
auto& infos = _screenshots[handle];
infos.screenshot_name = buff;
return handle;
}
@ -33,7 +61,30 @@ ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB
ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight )
{
PRINT_DEBUG("AddScreenshotToLibrary\n");
return INVALID_SCREENSHOT_HANDLE;
if (pchFilename == nullptr)
return INVALID_SCREENSHOT_HANDLE;
std::vector<image_pixel_t> pixels(std::move(local_storage->load_image(pchFilename)));
if (pixels.size() != size_t(nWidth) * size_t(nHeight))
return INVALID_SCREENSHOT_HANDLE;
char buff[128];
auto now = std::chrono::system_clock::now();
time_t now_time;
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
std::string screenshot_name = buff;
screenshot_name += ".png";
if (!local_storage->save_screenshot(screenshot_name, (uint8_t*)pixels.data(), nWidth, nHeight, 4))
return INVALID_SCREENSHOT_HANDLE;
auto handle = create_screenshot_handle();
auto& infos = _screenshots[handle];
infos.screenshot_name = buff;
return handle;
}
@ -41,6 +92,16 @@ ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilen
void Steam_Screenshots::TriggerScreenshot()
{
PRINT_DEBUG("TriggerScreenshot\n");
if (hooked)
{
ScreenshotRequested_t data;
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
}
else
{
PRINT_DEBUG("TODO: Make the overlay take a screenshot");
}
}
@ -58,7 +119,15 @@ void Steam_Screenshots::HookScreenshots( bool bHook )
bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation )
{
PRINT_DEBUG("SetLocation\n");
return false;
auto it = _screenshots.find(hScreenshot);
if (it == _screenshots.end())
return false;
it->second.metadatas["locations"].push_back(pchLocation);
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
return true;
}
@ -66,7 +135,15 @@ bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *p
bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID )
{
PRINT_DEBUG("TagUser\n");
return false;
auto it = _screenshots.find(hScreenshot);
if (it == _screenshots.end())
return false;
it->second.metadatas["users"].push_back(uint64_t(steamID.ConvertToUint64()));
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
return true;
}
@ -74,7 +151,15 @@ bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID
bool Steam_Screenshots::TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID )
{
PRINT_DEBUG("TagPublishedFile\n");
return false;
auto it = _screenshots.find(hScreenshot);
if (it == _screenshots.end())
return false;
it->second.metadatas["published_files"].push_back(uint64_t(unPublishedFileID));
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
return true;
}

View File

@ -17,10 +17,25 @@
#include "base.h"
struct screenshot_infos_t
{
std::string screenshot_name;
nlohmann::json metadatas;
};
class Steam_Screenshots : public ISteamScreenshots
{
bool hooked = false;
std::map<ScreenshotHandle, screenshot_infos_t> _screenshots;
class Local_Storage* local_storage;
class SteamCallBacks* callbacks;
ScreenshotHandle create_screenshot_handle();
public:
Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks);
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
ScreenshotHandle WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight );

124
dll/steam_tv.h Normal file
View File

@ -0,0 +1,124 @@
/* Copyright (C) 2019 Mr Goldberg
This file is part of the Goldberg Emulator
The Goldberg Emulator is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
The Goldberg Emulator 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the Goldberg Emulator; if not, see
<http://www.gnu.org/licenses/>. */
#include "base.h"
class Steam_TV :
public ISteamTV
{
class Settings *settings;
class Networking *network;
class SteamCallResults *callback_results;
class SteamCallBacks *callbacks;
class RunEveryRunCB *run_every_runcb;
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
FSteamNetworkingSocketsDebugOutput debug_function;
public:
static void steam_callback(void *object, Common_Message *msg)
{
PRINT_DEBUG("steam_tv_callback\n");
Steam_TV *steam_parties = (Steam_TV *)object;
steam_parties->Callback(msg);
}
static void steam_run_every_runcb(void *object)
{
PRINT_DEBUG("steam_tv_run_every_runcb\n");
Steam_TV *steam_parties = (Steam_TV *)object;
steam_parties->RunCallbacks();
}
Steam_TV(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
{
this->settings = settings;
this->network = network;
this->run_every_runcb = run_every_runcb;
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_TV::steam_callback, this);
// this->run_every_runcb->add(&Steam_TV::steam_run_every_runcb, this);
this->callback_results = callback_results;
this->callbacks = callbacks;
}
~Steam_TV()
{
//TODO rm network callbacks
//this->run_every_runcb->remove(&Steam_TV::steam_run_every_runcb, this);
}
bool IsBroadcasting(int *pnNumViewers)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
return false;
}
void AddBroadcastGameData(const char * pchKey, const char * pchValue)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
}
void RemoveBroadcastGameData(const char * pchKey)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
}
void AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
}
void RemoveTimelineMarker()
{
PRINT_DEBUG("%s\n", __FUNCTION__);
}
uint32 AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
return 0;
}
void RemoveRegion(uint32 unRegionHandle)
{
PRINT_DEBUG("%s\n", __FUNCTION__);
}
void RunCallbacks()
{
}
void Callback(Common_Message *msg)
{
if (msg->has_low_level()) {
if (msg->low_level().type() == Low_Level::CONNECT) {
}
if (msg->low_level().type() == Low_Level::DISCONNECT) {
}
}
if (msg->has_networking_sockets()) {
}
}
};

View File

@ -36,6 +36,8 @@ public ISteamUGC007,
public ISteamUGC008,
public ISteamUGC009,
public ISteamUGC010,
public ISteamUGC012,
public ISteamUGC013,
public ISteamUGC
{
class Settings *settings;
@ -245,6 +247,11 @@ bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, uint32 keyVa
return false;
}
bool GetQueryUGCKeyValueTag( UGCQueryHandle_t handle, uint32 index, const char *pchKey, STEAM_OUT_STRING_COUNT(cchValueSize) char *pchValue, uint32 cchValueSize )
{
PRINT_DEBUG("Steam_UGC::GetQueryUGCKeyValueTag2\n");
return false;
}
// Release the request to free up memory, after retrieving results
@ -268,6 +275,11 @@ bool AddRequiredTag( UGCQueryHandle_t handle, const char *pTagName )
return true;
}
bool AddRequiredTagGroup( UGCQueryHandle_t handle, const SteamParamStringArray_t *pTagGroups )
{
PRINT_DEBUG("Steam_UGC::AddRequiredTagGroup\n");
return true;
}
bool AddExcludedTag( UGCQueryHandle_t handle, const char *pTagName )
{
@ -487,6 +499,13 @@ bool SetAllowLegacyUpload( UGCUpdateHandle_t handle, bool bAllowLegacyUpload )
return false;
}
bool RemoveAllItemKeyValueTags( UGCUpdateHandle_t handle )
{
PRINT_DEBUG("Steam_UGC::RemoveAllItemKeyValueTags\n");
return false;
}
// remove all existing key-value tags (you can add new ones via the AddItemKeyValueTag function)
bool RemoveItemKeyValueTags( UGCUpdateHandle_t handle, const char *pchKey )
{
PRINT_DEBUG("Steam_UGC::RemoveItemKeyValueTags\n");
@ -652,7 +671,7 @@ uint32 GetSubscribedItems( PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEn
// get EItemState flags about item on this client
uint32 GetItemState( PublishedFileId_t nPublishedFileID )
{
PRINT_DEBUG("Steam_UGC::GetItemState\n");
PRINT_DEBUG("Steam_UGC::GetItemState %llu\n", nPublishedFileID);
std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (subscribed.count(nPublishedFileID)) {
if (settings->isModInstalled(nPublishedFileID)) {
@ -693,10 +712,10 @@ bool GetItemDownloadInfo( PublishedFileId_t nPublishedFileID, uint64 *punBytesDo
return false;
}
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, STEAM_OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
{
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo old\n");
return false;
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
}
bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate, bool *pbIsDownloading, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
@ -708,7 +727,7 @@ bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate,
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize ) // returns true if item is installed
{
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo older\n");
return false;
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
}

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