diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9021357 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +# Set the default behavior. +* text=auto + +# Unix shell files should always be LF. +*.sh text eol=lf + +# Windows shell files should always be CRLF. +*.bat text eol=crlf + +# Binary files. +*.jpg binary +*.png binary + diff --git a/.gitignore b/.gitignore index 47acc83..788c645 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,18 @@ -.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 -out/* +.vs/* +.vscode/* +*.bin +*.o +net.pb.* +*steam_api* +release/* +debug/* +/build*/ +*.obj +/dll/net.pb.cc +/dll/net.pb.h +base.exp +base.lib +rtlgenrandom* +steamclient.exp +steamclient.lib +out/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c158841..e054e77 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -59,25 +59,38 @@ build_windows: image: fedora:35 script: - - dnf -y install wine wget p7zip sed dos2unix python + - dnf -y install wine wget p7zip sed dos2unix python cpio - unix2dos *.txt - unix2dos files_example/*.txt files_example/*/*.txt - 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' + - mkdir wine + # Ancient CI version of wine doesn't support the where.exe cmd. (It's a stub.) Use the version from wine-10.1. + # Note: This can use the fedora archive once f40 goes eol (~ 5/28/2025) and wine 10.x gets locked into their archive server. + # (Can't use it before then due to newer wine point releases changing the URL....) + #- wget 'https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/39/Everything/x86_64/os/Packages/w/wine-core-10.0-1.fc41.i686.rpm' + #- echo './usr/lib/wine/i386-windows/where.exe' > extract.txt; rpm2cpio wine-core-10.0-1.fc41.i686.rpm | cpio -ivdE extract.txt; rm -f extract.txt + #- /usr/bin/mv -f ./usr/lib/wine/i386-windows/where.exe wine/; rm -rf ./usr/ + - wget 'https://dl.winehq.org/wine-builds/fedora/41/x86_64/wine-devel-10.0.0-1.1.x86_64.rpm' + - echo './opt/wine-devel/lib64/wine/i386-windows/where.exe' > extract.txt; rpm2cpio wine-devel-10.0.0-1.1.x86_64.rpm | cpio -ivdE extract.txt; rm -f extract.txt + - /usr/bin/mv -f ./opt/wine-devel/lib64/wine/i386-windows/where.exe wine/; rm -rf ./opt/ + - /usr/bin/cp -f wine/where.exe /usr/lib/wine/i386-windows/where.exe; /usr/bin/cp -f wine/where.exe /usr/lib64/wine/x86_64-windows/where.exe - 7za x protobuf_x86-windows-static.7z -oprotobuf_x86-windows-static - 7za x protobuf_x64-windows-static.7z -oprotobuf_x64-windows-static - 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 dll/*.proto | tr "\n" " " | sed "s/.proto/.pb.cc/g")"; sed "s|dll/\*.cc|$DLL_FILES|g" -i *.bat - sed "s| /MP12 | /MP4 |g" -i *.bat - - python generate_build_win_bat.py + # CI can't produce PDBs. Throws a bunch of errors, and skips building the PEs. + - touch CI_BUILD.TAG - export WINEDEBUG=-all - - wine cmd /c build_win_release_test.bat + - WINEPATH=$PWD/wine wine cmd /c build_win_debug_experimental_steamclient.bat 8 + - WINEPATH=$PWD/wine wine cmd /c build_win_release.bat 8 + artifacts: paths: - release/ + - debug/ expire_in: 1 day build_cmake_linux: @@ -156,9 +169,9 @@ deploy_all: - mv linux release/ - shopt -s extglob - rm -rf .g* - - rm -rf !(release) - - mv release/* ./ - - rm -rf release + - rm -rf !(release|debug) +# - mv release/* ./ +# - rm -rf release - echo $CI_JOB_ID > job_id - tree artifacts: diff --git a/build_env_x64.bat b/build_env_x64.bat index 35b701b..21c181d 100755 --- a/build_env_x64.bat +++ b/build_env_x64.bat @@ -12,6 +12,8 @@ if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Bu if exist "%VS_Base_Path%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" goto vs2022 if exist "%VS_Base_Path%\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2022_bt if exist ".\sdk_standalone\set_vars64.bat" goto gitlabci +if exist "vsinstallloc.txt" goto readloc +if exist "%VS_Base_Path%\Microsoft Visual Studio\Installer\vswhere.exe" goto wherevs :vs2022 call "%VS_Base_Path%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" @@ -44,4 +46,14 @@ goto batend :gitlabci call ".\sdk_standalone\set_vars64.bat" goto batend + +:wherevs +call "%VS_Base_Path%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath > vsinstallloc.txt +goto readloc + +:readloc +set /p VS_LOCAL= vsinstallloc.txt +goto readloc + +:readloc +set /p VS_LOCAL=/dev/null +echo "Building debug libsteam_api.so...." +clang++ -shared -fPIC -o libsteam_api.so ../../dll/*.cpp ../../dll/*.cc -g3 -Wno-return-type -fsanitize=address -shared-libasan -lprotobuf-lite -std=c++14 && echo built libsteam_api.so +popd >/dev/null +rm -rf "${PWD}/debug/lobby_connect"/* +pushd "${PWD}/debug/lobby_connect" >/dev/null +echo "Building debug lobby_connect...." +clang++ -DNO_DISK_WRITES=1 -DLOBBY_CONNECT=1 -fPIC -o lobby_connect ../../lobby_connect.cpp ../../dll/*.cpp ../../dll/*.cc -g3 -Wno-return-type -fsanitize=address -shared-libasan -lprotobuf-lite -std=c++14 && echo built lobby_connect +popd >/dev/null diff --git a/build_linux_release.sh b/build_linux_release.sh new file mode 100644 index 0000000..5a79477 --- /dev/null +++ b/build_linux_release.sh @@ -0,0 +1,27 @@ +#!/bin/bash +if [ ! -d "${PWD}/release" ]; then + mkdir "${PWD}/release" +fi +if [ ! -d "${PWD}/release/experimental" ]; then + mkdir "${PWD}/release/experimental" +fi +if [ ! -d "${PWD}/release/lobby_connect" ]; then + mkdir "${PWD}/release/lobby_connect" +fi +protoc -I"${PWD}/dll/" --cpp_out="${PWD}/dll/" "${PWD}"/dll/*.proto +rm -rf "${PWD}/release/experimental"/* +pushd "${PWD}/release/experimental" >/dev/null +echo "Building release libsteam_api.so...." +clang++ -DNDEBUG=1 -DEMU_RELEASE_BUILD=1 -shared -fPIC -o libsteam_api.so ../../dll/*.cpp ../../dll/*.cc -g3 -Wno-return-type -fsanitize=address -shared-libasan -lprotobuf-lite -std=c++14 && echo built libsteam_api.so +cp libsteam_api.so libsteam_api.so.dbg +strip --enable-deterministic-archives --strip-unneeded --remove-section=.comment --remove-section=.note libsteam_api.so +objcopy --add-gnu-debuglink=libsteam_api.so.dbg libsteam_api.so +popd >/dev/null +rm -rf "${PWD}/release/lobby_connect"/* +pushd "${PWD}/release/lobby_connect" >/dev/null +echo "Building release lobby_connect...." +clang++ -DNO_DISK_WRITES=1 -DLOBBY_CONNECT=1 -DNDEBUG=1 -DEMU_RELEASE_BUILD=1 -fPIC -o lobby_connect ../../lobby_connect.cpp ../../dll/*.cpp ../../dll/*.cc -g3 -Wno-return-type -fsanitize=address -shared-libasan -lprotobuf-lite -std=c++14 && echo built lobby_connect +cp lobby_connect lobby_connect.dbg +strip --enable-deterministic-archives --strip-unneeded --remove-section=.comment --remove-section=.note lobby_connect +objcopy --add-gnu-debuglink=lobby_connect.dbg lobby_connect +popd >/dev/null diff --git a/build_set_protobuf_directories.bat b/build_set_protobuf_directories.bat index fb3e63b..8bd16a6 100755 --- a/build_set_protobuf_directories.bat +++ b/build_set_protobuf_directories.bat @@ -1,7 +1,14 @@ @echo off cd /d "%~dp0" -SET PROTOBUF_X86_DIRECTORY=..\vcpkg\installed\x86-windows-static -SET PROTOBUF_X64_DIRECTORY=..\vcpkg\installed\x64-windows-static + +SET TEST_A=%cd% +REM CI doesn't like this var expansion of "%cd%\". +cd ..\vcpkg\installed\x86-windows-static +SET PROTOBUF_X86_DIRECTORY=%cd% +cd %TEST_A% +cd ..\vcpkg\installed\x64-windows-static +SET PROTOBUF_X64_DIRECTORY=%cd% +cd %TEST_A% rem location of protoc in protobuf directories: SET PROTOC_X86_EXE=%PROTOBUF_X86_DIRECTORY%\tools\protobuf\protoc.exe diff --git a/build_win_debug_experimental.bat b/build_win_debug_experimental.bat index 7f3a080..7cbc3c8 100755 --- a/build_win_debug_experimental.bat +++ b/build_win_debug_experimental.bat @@ -1,14 +1,9 @@ @echo off 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 /Z7 /LD /IImGui /I%PROTOBUF_X86_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X86_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll -cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll -"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto -call build_env_x64.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /Z7 /LD /IImGui /I%PROTOBUF_X64_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X64_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll -cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +SET SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD=1 +SET SKIP_STEAMCLIENT_LOADER=1 + +call build_win_debug_experimental_steamclient.bat diff --git a/build_win_debug_experimental_steamclient.bat b/build_win_debug_experimental_steamclient.bat index 1eec4aa..85d7f8d 100644 --- a/build_win_debug_experimental_steamclient.bat +++ b/build_win_debug_experimental_steamclient.bat @@ -1,9 +1,141 @@ +@echo off cd /d "%~dp0" -call build_set_protobuf_directories.bat + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\experimental\steamclient\debug\x86\*.* ( DEL /F /S /Q build\experimental\steamclient\debug\x86\*.* ) +IF EXIST build\experimental\steamclient\debug\x64\*.* ( DEL /F /S /Q build\experimental\steamclient\debug\x64\*.* ) + +IF EXIST debug\experimental_steamclient\*.* ( DEL /F /S /Q debug\experimental_steamclient\*.* ) + +setlocal + +IF DEFINED SKIP_X86 GOTO LK_X64 + "%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 /I%PROTOBUF_X86_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DEMU_OVERLAY /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X86_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient.dll + +REM Non-STEAMCLIENT_DLL debug sc_deps. +cd "%OLD_DIR%\build\experimental_steamclient\debug\x86\sc_deps" +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\SC_DEPS.BLD +IF EXIST %CDS_DIR%\DEBUG_SC_DEPS_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_SC_DEPS_X86.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_SC_DEPS_X86.LKS + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_EXP_STEAMCLIENT_DLL_X86 + +REM Link Non-STEAMCLIENT_DLL debug steam_api.dll. +cd "%OLD_DIR%\build\experimental\debug\x86\" +IF EXIST %CDS_DIR%\DEBUG_STEAMAPI_NON_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMAPI_NON_X86.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental\steam_api.dll > %CDS_DIR%\DEBUG_STEAMAPI_NON_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental\steam_api.pdb >> %CDS_DIR%\DEBUG_STEAMAPI_NON_X86.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\EXPERIMENTAL.BLD @%CDS_DIR%\EXPERIMENTAL.LKS @%CDS_DIR%\DEBUG_ALL_DEPS_X86.LKS @%CDS_DIR%\DEBUG_SC_DEPS_X86.LKS @%CDS_DIR%\DEBUG_STEAMAPI_NON_X86.LKS + +:LK_EXP_STEAMCLIENT_DLL_X86 + +IF DEFINED SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD GOTO LK_STEAMCLIENT_DLL_X86 + +REM Link STEAMCLIENT_DLL debug steamclient.dll +cd "%OLD_DIR%\build\experimental_steamclient\debug\x86" +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_X86.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental_steamclient\steamclient.dll > %CDS_DIR%\DEBUG_STEAMCLIENT_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental_steamclient\steamclient.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_X86.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS @%CDS_DIR%\DEBUG_ALL_DEPS_X86.LKS @%CDS_DIR%\DEBUG_SC_DEPS_X86.LKS @%CDS_DIR%\DEBUG_STEAMCLIENT_X86.LKS + +:LK_STEAMCLIENT_DLL_X86 + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_STEAMCLIENT_LOADER_X86 + +REM Link Non-STEAMCLIENT_DLL debug steamclient.dll. +cd "%OLD_DIR%\build\experimental\debug\x86\" +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X86.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental\steamclient.dll > %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental\steamclient.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X86.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\STEAMCLIENT.BLD @%CDS_DIR%\DEBUG_STEAMCLIENT_NON_X86.LKS + +:LK_STEAMCLIENT_LOADER_X86 + +IF DEFINED SKIP_STEAMCLIENT_LOADER GOTO LK_X64 + +REM Build steamclient_loader debug x86. +cd "%OLD_DIR%\build\experimental_steamclient\steamclient_loader\debug\x86" +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\STEAMCLIENT_LOADER.BLD +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS +echo /link /OUT:%OLD_DIR%\debug\experimental_steamclient\steamclient_loader_x32.exe >> %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental_steamclient\steamclient_loader_x32.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS ) +cl @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\STEAMCLIENT_LOADER.LKS @%CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X86.LKS +cd %OLD_DIR% + +:LK_X64 + +endlocal + +setlocal + +IF DEFINED SKIP_X64 GOTO LK_END + +"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x64.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /IImGui /I%PROTOBUF_X64_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DEMU_OVERLAY /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X64_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient64.dll + +REM Non-STEAMCLIENT_DLL debug sc_deps. +cd "%OLD_DIR%\build\experimental_steamclient\debug\x64\sc_deps" +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\SC_DEPS.BLD +IF EXIST %CDS_DIR%\DEBUG_SC_DEPS_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_SC_DEPS_X64.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_SC_DEPS_X64.LKS + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_EXP_STEAMCLIENT_DLL_X64 + +REM Link Non-STEAMCLIENT_DLL debug steam_api64.dll. +cd "%OLD_DIR%\build\experimental\debug\x64\" +IF EXIST %CDS_DIR%\DEBUG_STEAMAPI_NON_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMAPI_NON_X64.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental\steam_api64.dll > %CDS_DIR%\DEBUG_STEAMAPI_NON_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental\steam_api64.pdb >> %CDS_DIR%\DEBUG_STEAMAPI_NON_X64.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\EXPERIMENTAL.BLD @%CDS_DIR%\EXPERIMENTAL.LKS @%CDS_DIR%\DEBUG_ALL_DEPS_X64.LKS @%CDS_DIR%\DEBUG_SC_DEPS_X64.LKS @%CDS_DIR%\DEBUG_STEAMAPI_NON_X64.LKS + +:LK_EXP_STEAMCLIENT_DLL_X64 + +IF DEFINED SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD GOTO LK_STEAMCLIENT_DLL_X64 + +REM Link STEAMCLIENT_DLL debug steamclient64.dll +cd "%OLD_DIR%\build\experimental_steamclient\debug\x64" +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_X64.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental_steamclient\steamclient64.dll > %CDS_DIR%\DEBUG_STEAMCLIENT_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental_steamclient\steamclient64.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_X64.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS @%CDS_DIR%\DEBUG_ALL_DEPS_X64.LKS @%CDS_DIR%\DEBUG_SC_DEPS_X64.LKS @%CDS_DIR%\DEBUG_STEAMCLIENT_X64.LKS + +:LK_STEAMCLIENT_DLL_X64 + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_STEAMCLIENT_LOADER_X64 + +REM Link Non-STEAMCLIENT_DLL debug steamclient64.dll. +cd "%OLD_DIR%\build\experimental\debug\x64\" +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X64.LKS ) +echo /link /OUT:%OLD_DIR%\debug\experimental\steamclient64.dll > %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental\steamclient64.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_NON_X64.LKS ) +cl /LD @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\STEAMCLIENT.BLD @%CDS_DIR%\DEBUG_STEAMCLIENT_NON_X64.LKS + +:LK_STEAMCLIENT_LOADER_X64 + +IF DEFINED SKIP_STEAMCLIENT_LOADER GOTO LK_END + +REM Build steamclient_loader debug x64. +cd "%OLD_DIR%\build\experimental_steamclient\steamclient_loader\debug\x64" +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\STEAMCLIENT_LOADER.BLD +IF EXIST %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS +echo /link /OUT:%OLD_DIR%\debug\experimental_steamclient\steamclient_loader_x64.exe >> %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\experimental_steamclient\steamclient_loader_x64.pdb >> %CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS ) +cl @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\STEAMCLIENT_LOADER.LKS @%CDS_DIR%\DEBUG_STEAMCLIENT_LOADER_X64.LKS +cd %OLD_DIR% + +:LK_END + +endlocal + +copy Readme_experimental.txt debug\experimental\Readme.txt +copy steamclient_loader\ColdClientLoader.ini debug\experimental_steamclient\ +copy Readme_experimental_steamclient.txt debug\experimental_steamclient\Readme.txt diff --git a/build_win_debug_lobby_connect.bat b/build_win_debug_lobby_connect.bat new file mode 100644 index 0000000..7ad250e --- /dev/null +++ b/build_win_debug_lobby_connect.bat @@ -0,0 +1,46 @@ +@echo off +cd /d "%~dp0" + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\debug\lobby_connect\x86\*.* ( DEL /F /S /Q build\debug\lobby_connect\x86\*.* ) +IF EXIST build\debug\lobby_connect\x64\*.* ( DEL /F /S /Q build\debug\lobby_connect\x64\*.* ) + +IF EXIST debug\lobby_connect\*.* ( DEL /F /S /Q debug\lobby_connect\*.* ) + +setlocal +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x86.bat +cd %OLD_DIR%\build\lobby_connect\debug\x86 + +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\LOBBY_CONNECT.BLD +IF EXIST %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS +echo /link /OUT:%OLD_DIR%\debug\lobby_connect\lobby_connect_x32.exe >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS +echo /link /IMPLIB:%cd%\lobby_connect_x32.lib >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\lobby_connect\lobby_connect_x32.pdb >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS ) + +cl @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\LOBBY_CONNECT.LKS @%CDS_DIR%\DEBUG_LOBBY_CONNECT_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x64.bat +cd %OLD_DIR%\build\lobby_connect\debug\x64 + +cl /c @%CDS_DIR%\DEBUG.BLD @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\LOBBY_CONNECT.BLD +IF EXIST %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS +echo /link /OUT:%OLD_DIR%\debug\lobby_connect\lobby_connect_x64.exe >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS +echo /link /IMPLIB:%cd%\lobby_connect_x64.lib >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\debug\lobby_connect\lobby_connect_x64.pdb >> %CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS ) + +cl @%CDS_DIR%\DEBUG.LKS @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\LOBBY_CONNECT.LKS @%CDS_DIR%\DEBUG_LOBBY_CONNECT_X64.LKS +cd %OLD_DIR% +endlocal +copy Readme_lobby_connect.txt debug\lobby_connect\Readme.txt diff --git a/build_win_find_interfaces.bat b/build_win_find_interfaces.bat index cb1cb48..c2cab0d 100755 --- a/build_win_find_interfaces.bat +++ b/build_win_find_interfaces.bat @@ -1,9 +1,39 @@ @echo off cd /d "%~dp0" -mkdir release\tools -del /Q release\tools\* + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\tools\release\x86\*.* ( DEL /F /S /Q build\tools\release\x86\*.* ) +IF EXIST build\tools\release\x64\*.* ( DEL /F /S /Q build\tools\release\x64\*.* ) + +IF EXIST release\tools\*.* ( DEL /F /S /Q release\tools\*.* ) + +setlocal call build_env_x86.bat -cl generate_interfaces_file.cpp /EHsc /MP12 /Ox /link /debug:none /OUT:release\tools\generate_interfaces_file.exe -del /Q /S release\tools\*.lib -del /Q /S release\tools\*.exp +cd %OLD_DIR%\build\tools\release\x86 +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\GENERATE_INTERFACES_FILE.BLD +IF EXIST %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS ) +where "generate_interfaces_file.obj" > %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS +echo /link /OUT:%OLD_DIR%\release\tools\generate_interfaces_file_x32.exe >> %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\tools\generate_interfaces_file_x32.pdb >> %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS ) +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\GENERATE_INTERFACES_FILE.LKS @%CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal +call build_env_x64.bat +cd %OLD_DIR%\build\tools\release\x64 +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\GENERATE_INTERFACES_FILE.BLD +IF EXIST %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS ) +where "generate_interfaces_file.obj" > %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS +echo /link /OUT:%OLD_DIR%\release\tools\generate_interfaces_file_x64.exe >> %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\tools\generate_interfaces_file_x64.pdb >> %CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS ) +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\GENERATE_INTERFACES_FILE.LKS @%CDS_DIR%\RELEASE_GENERATE_INTERFACES_FILE_X64.LKS +cd %OLD_DIR% +endlocal + copy Readme_generate_interfaces.txt release\tools\Readme_generate_interfaces.txt diff --git a/build_win_lobby_connect.bat b/build_win_lobby_connect.bat index 27c83e5..09f194b 100755 --- a/build_win_lobby_connect.bat +++ b/build_win_lobby_connect.bat @@ -1,12 +1,46 @@ @echo off cd /d "%~dp0" -mkdir release\lobby_connect -del /Q release\lobby_connect\* -call build_set_protobuf_directories.bat + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\release\lobby_connect\x86\*.* ( DEL /F /S /Q build\release\lobby_connect\x86\*.* ) +IF EXIST build\release\lobby_connect\x64\*.* ( DEL /F /S /Q build\release\lobby_connect\x64\*.* ) + +IF EXIST release\lobby_connect\*.* ( DEL /F /S /Q release\lobby_connect\*.* ) + +setlocal "%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x86.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -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 rtlgenrandom.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 +cd %OLD_DIR%\build\lobby_connect\release\x86 + +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\LOBBY_CONNECT.BLD +IF EXIST %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS +echo /link /OUT:%OLD_DIR%\release\lobby_connect\lobby_connect_x32.exe >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS +echo /link /IMPLIB:%cd%\lobby_connect_x32.lib >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\lobby_connect\lobby_connect_x32.pdb >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS ) + +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\LOBBY_CONNECT.LKS @%CDS_DIR%\RELEASE_LOBBY_CONNECT_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x64.bat +cd %OLD_DIR%\build\lobby_connect\release\x64 + +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\LOBBY_CONNECT.BLD +IF EXIST %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS +echo /link /OUT:%OLD_DIR%\release\lobby_connect\lobby_connect_x64.exe >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS +echo /link /IMPLIB:%cd%\lobby_connect_x64.lib >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\lobby_connect\lobby_connect_x64.pdb >> %CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS ) + +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\LOBBY_CONNECT.LKS @%CDS_DIR%\RELEASE_LOBBY_CONNECT_X64.LKS +cd %OLD_DIR% +endlocal copy Readme_lobby_connect.txt release\lobby_connect\Readme.txt diff --git a/build_win_release.bat b/build_win_release.bat index 79e7804..31e3854 100755 --- a/build_win_release.bat +++ b/build_win_release.bat @@ -1,24 +1,51 @@ @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 + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\release\x86\*.* ( DEL /F /S /Q build\release\x86\*.* ) +IF EXIST build\release\x64\*.* ( DEL /F /S /Q build\release\x64\*.* ) + +IF EXIST release\steam_settings.EXAMPLE ( DEL /F /S /Q release\steam_settings.EXAMPLE ) +IF EXIST release\*.dll ( DEL /F /Q release\*.dll ) +IF EXIST release\*.txt ( DEL /F /Q release\*.txt ) + +setlocal "%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 /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api.dll +cd %OLD_DIR%\build\release\x86 +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\DLL_MAIN_CPP.BLD +IF EXIST %CDS_DIR%\RELEASE_BASE_DLL_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_BASE_DLL_X86.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_BASE_DLL_X86.LKS +echo /link /OUT:%OLD_DIR%\release\steam_api.dll >> %CDS_DIR%\RELEASE_BASE_DLL_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\steam_api.pdb >> %CDS_DIR%\RELEASE_BASE_DLL_X86.LKS ) + +cl /LD @%CDS_DIR%/RELEASE.LKS @%CDS_DIR%/PROTOBUF_X86.LKS @%CDS_DIR%/DLL_MAIN_CPP.LKS @%CDS_DIR%\RELEASE_BASE_DLL_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal "%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto call build_env_x64.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /DEMU_RELEASE_BUILD /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\steam_api64.dll +cd %OLD_DIR%\build\release\x64 + +cl /c @%CDS_DIR%/RELEASE.BLD @%CDS_DIR%/PROTOBUF_X64.BLD @%CDS_DIR%/DLL_MAIN_CPP.BLD +IF EXIST %CDS_DIR%\RELEASE_BASE_DLL_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_BASE_DLL_X64.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_BASE_DLL_X64.LKS +echo /link /OUT:%OLD_DIR%\release\steam_api64.dll >> %CDS_DIR%\RELEASE_BASE_DLL_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\steam_api64.pdb >> %CDS_DIR%\RELEASE_BASE_DLL_X64.LKS ) + +cl /LD @%CDS_DIR%/RELEASE.LKS @%CDS_DIR%/PROTOBUF_X64.LKS @%CDS_DIR%/DLL_MAIN_CPP.LKS @%CDS_DIR%\RELEASE_BASE_DLL_X64.LKS +cd %OLD_DIR% + +endlocal 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 diff --git a/build_win_release_experimental.bat b/build_win_release_experimental.bat index 5c3ff84..faa8230 100755 --- a/build_win_release_experimental.bat +++ b/build_win_release_experimental.bat @@ -1,17 +1,9 @@ @echo off cd /d "%~dp0" -mkdir release\experimental -del /Q release\experimental\* -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 /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /I%PROTOBUF_X86_DIRECTORY%\include\ /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X86_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.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 /MP4 /Ox /link /OUT:release\experimental\steamclient.dll -"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto -call build_env_x64.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /I%PROTOBUF_X64_DIRECTORY%\include\ /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X64_LIBRARY%" opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.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 /MP4 /Ox /link /OUT:release\experimental\steamclient64.dll -copy Readme_experimental.txt release\experimental\Readme.txt +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +SET SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD=1 +SET SKIP_STEAMCLIENT_LOADER=1 + +call build_win_release_experimental_steamclient.bat diff --git a/build_win_release_experimental_steamclient.bat b/build_win_release_experimental_steamclient.bat index cd55557..800c195 100644 --- a/build_win_release_experimental_steamclient.bat +++ b/build_win_release_experimental_steamclient.bat @@ -1,16 +1,141 @@ @echo off cd /d "%~dp0" -mkdir release\experimental_steamclient -del /Q release\experimental_steamclient\* -call build_set_protobuf_directories.bat + +SET OLD_DIR=%cd% + +IF NOT "%1" == "" ( SET JOB_COUNT=%~1 ) + +IF NOT DEFINED BUILT_ALL_DEPS ( call generate_all_deps.bat ) + +IF EXIST build\experimental\steamclient\release\x86\*.* ( DEL /F /S /Q build\experimental\steamclient\release\x86\*.* ) +IF EXIST build\experimental\steamclient\release\x64\*.* ( DEL /F /S /Q build\experimental\steamclient\release\x64\*.* ) + +IF EXIST release\experimental_steamclient\*.* ( DEL /F /S /Q release\experimental_steamclient\*.* ) + +setlocal + +IF DEFINED SKIP_X86 GOTO LK_X64 + "%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 /IImGui /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X86_LIBRARY%" 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 + +REM Non-STEAMCLIENT_DLL release sc_deps. +cd "%OLD_DIR%\build\experimental_steamclient\release\x86\sc_deps" +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\PROTOBUF_X86.BLD @%CDS_DIR%\SC_DEPS.BLD +IF EXIST %CDS_DIR%\RELEASE_SC_DEPS_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_SC_DEPS_X86.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_SC_DEPS_X86.LKS + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_EXP_STEAMCLIENT_DLL_X86 + +REM Link Non-STEAMCLIENT_DLL release steam_api.dll. +cd "%OLD_DIR%\build\experimental\release\x86\" +IF EXIST %CDS_DIR%\RELEASE_STEAMAPI_NON_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMAPI_NON_X86.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental\steam_api.dll > %CDS_DIR%\RELEASE_STEAMAPI_NON_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental\steam_api.pdb >> %CDS_DIR%\RELEASE_STEAMAPI_NON_X86.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\EXPERIMENTAL.BLD @%CDS_DIR%\EXPERIMENTAL.LKS @%CDS_DIR%\RELEASE_ALL_DEPS_X86.LKS @%CDS_DIR%\RELEASE_SC_DEPS_X86.LKS @%CDS_DIR%\RELEASE_STEAMAPI_NON_X86.LKS + +:LK_EXP_STEAMCLIENT_DLL_X86 + +IF DEFINED SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD GOTO LK_STEAMCLIENT_DLL_X86 + +REM Link STEAMCLIENT_DLL release steamclient.dll +cd "%OLD_DIR%\build\experimental_steamclient\release\x86" +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_X86.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental_steamclient\steamclient.dll > %CDS_DIR%\RELEASE_STEAMCLIENT_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental_steamclient\steamclient.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_X86.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X86.LKS @%CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS @%CDS_DIR%\RELEASE_ALL_DEPS_X86.LKS @%CDS_DIR%\RELEASE_SC_DEPS_X86.LKS @%CDS_DIR%\RELEASE_STEAMCLIENT_X86.LKS + +:LK_STEAMCLIENT_DLL_X86 + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_STEAMCLIENT_LOADER_X86 + +REM Link Non-STEAMCLIENT_DLL release steamclient.dll. +cd "%OLD_DIR%\build\experimental\release\x86\" +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X86.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental\steamclient.dll > %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental\steamclient.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X86.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\STEAMCLIENT.BLD @%CDS_DIR%\RELEASE_STEAMCLIENT_NON_X86.LKS + +:LK_STEAMCLIENT_LOADER_X86 + +IF DEFINED SKIP_STEAMCLIENT_LOADER GOTO LK_X64 + +REM Build steamclient_loader release x86. +cd "%OLD_DIR%\build\experimental_steamclient\steamclient_loader\release\x86" +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\STEAMCLIENT_LOADER.BLD +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS +echo /link /OUT:%OLD_DIR%\release\experimental_steamclient\steamclient_loader_x32.exe >> %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental_steamclient\steamclient_loader_x32.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS ) +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\STEAMCLIENT_LOADER.LKS @%CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X86.LKS +cd %OLD_DIR% + +:LK_X64 + +endlocal + +setlocal + +IF DEFINED SKIP_X64 GOTO LK_END + "%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 /IImGui /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ /Ioverlay_experimental dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/backends/imgui_impl_dx*.cpp ImGui/backends/imgui_impl_win32.cpp ImGui/backends/imgui_impl_vulkan.cpp ImGui/backends/imgui_impl_opengl3.cpp ImGui/backends/imgui_win_shader_blobs.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp overlay_experimental/System/*.cpp "%PROTOBUF_X64_LIBRARY%" 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 + +REM Non-STEAMCLIENT_DLL release sc_deps. +cd "%OLD_DIR%\build\experimental_steamclient\release\x64\sc_deps" +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\PROTOBUF_X64.BLD @%CDS_DIR%\SC_DEPS.BLD +IF EXIST %CDS_DIR%\RELEASE_SC_DEPS_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_SC_DEPS_X64.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_SC_DEPS_X64.LKS + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_EXP_STEAMCLIENT_DLL_X64 + +REM Link Non-STEAMCLIENT_DLL release steam_api64.dll. +cd "%OLD_DIR%\build\experimental\release\x64\" +IF EXIST %CDS_DIR%\RELEASE_STEAMAPI_NON_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMAPI_NON_X64.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental\steam_api64.dll > %CDS_DIR%\RELEASE_STEAMAPI_NON_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental\steam_api64.pdb >> %CDS_DIR%\RELEASE_STEAMAPI_NON_X64.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\EXPERIMENTAL.BLD @%CDS_DIR%\EXPERIMENTAL.LKS @%CDS_DIR%\RELEASE_ALL_DEPS_X64.LKS @%CDS_DIR%\RELEASE_SC_DEPS_X64.LKS @%CDS_DIR%\RELEASE_STEAMAPI_NON_X64.LKS + +:LK_EXP_STEAMCLIENT_DLL_X64 + +IF DEFINED SKIP_EXPERIMENTAL_STEAMCLIENT_BUILD GOTO LK_STEAMCLIENT_DLL_X64 + +REM Link STEAMCLIENT_DLL release steamclient64.dll +cd "%OLD_DIR%\build\experimental_steamclient\release\x64" +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_X64.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental_steamclient\steamclient64.dll > %CDS_DIR%\RELEASE_STEAMCLIENT_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental_steamclient\steamclient64.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_X64.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\PROTOBUF_X64.LKS @%CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS @%CDS_DIR%\RELEASE_ALL_DEPS_X64.LKS @%CDS_DIR%\RELEASE_SC_DEPS_X64.LKS @%CDS_DIR%\RELEASE_STEAMCLIENT_X64.LKS + +:LK_STEAMCLIENT_DLL_X64 + +IF DEFINED SKIP_EXPERIMENTAL_BUILD GOTO LK_STEAMCLIENT_LOADER_X64 + +REM Link Non-STEAMCLIENT_DLL release steamclient64.dll. +cd "%OLD_DIR%\build\experimental\release\x64\" +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X64.LKS ) +echo /link /OUT:%OLD_DIR%\release\experimental\steamclient64.dll > %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental\steamclient64.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_NON_X64.LKS ) +cl /LD @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\STEAMCLIENT.BLD @%CDS_DIR%\RELEASE_STEAMCLIENT_NON_X64.LKS + +:LK_STEAMCLIENT_LOADER_X64 + +IF DEFINED SKIP_STEAMCLIENT_LOADER GOTO LK_END + +REM Build steamclient_loader release x64. +cd "%OLD_DIR%\build\experimental_steamclient\steamclient_loader\release\x64" +cl /c @%CDS_DIR%\RELEASE.BLD @%CDS_DIR%\STEAMCLIENT_LOADER.BLD +IF EXIST %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS +echo /link /OUT:%OLD_DIR%\release\experimental_steamclient\steamclient_loader_x64.exe >> %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /PDB:%OLD_DIR%\release\experimental_steamclient\steamclient_loader_x64.pdb >> %CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS ) +cl @%CDS_DIR%\RELEASE.LKS @%CDS_DIR%\STEAMCLIENT_LOADER.LKS @%CDS_DIR%\RELEASE_STEAMCLIENT_LOADER_X64.LKS +cd %OLD_DIR% + +:LK_END + +endlocal + +copy Readme_experimental.txt release\experimental\Readme.txt +copy steamclient_loader\ColdClientLoader.ini release\experimental_steamclient\ copy Readme_experimental_steamclient.txt release\experimental_steamclient\Readme.txt diff --git a/dll/appticket.h b/dll/appticket.h index 94f92e7..1768278 100644 --- a/dll/appticket.h +++ b/dll/appticket.h @@ -1,4 +1,7 @@ +#ifndef APPTICKET_H +#define APPTICKET_H + struct AppTicketV1 { // Total ticket size - 16 @@ -297,4 +300,6 @@ public: return buffer; } -}; \ No newline at end of file +}; + +#endif // APPTICKET_H diff --git a/dll/base.cpp b/dll/base.cpp index 507e034..3499f2d 100644 --- a/dll/base.cpp +++ b/dll/base.cpp @@ -19,14 +19,37 @@ #ifdef __WINDOWS__ +HMODULE hadvapi32 = NULL; +BOOLEAN (NTAPI *real_RtlGenRandom)(PVOID,ULONG) = NULL; + static void randombytes(char * const buf, const size_t size) { - while (!RtlGenRandom((PVOID) buf, (ULONG) size)) { - PRINT_DEBUG("RtlGenRandom ERROR\n"); - Sleep(100); + PRINT_DEBUG("%s %p %" PRI_ZU ".\n", "mine_RtlGenRandom() called.", buf, size); + if (hadvapi32 == NULL) { + hadvapi32 = GetModuleHandleW(L"advapi32.dll"); + if (hadvapi32 == NULL) { + PRINT_DEBUG("%s.\n", "GetModuleHandle() failed for advapi32.dll"); + } + PRINT_DEBUG("advapi32.dll handle: 0x%p.\n", hadvapi32); } - + if (hadvapi32 != NULL && + real_RtlGenRandom == NULL) { + real_RtlGenRandom = (BOOLEAN(NTAPI *)(PVOID,ULONG))GetProcAddress(hadvapi32, "SystemFunction036"); + if (real_RtlGenRandom == NULL) { + PRINT_DEBUG("%s.\n", "GetProcAddress() failed for RtlGenRandom()"); + } + PRINT_DEBUG("real_RtlGenRandom address: 0x%p.\n", real_RtlGenRandom); + } + if (real_RtlGenRandom != NULL) { + PRINT_DEBUG("%s.\n", "Calling real_RtlGenRandom"); + while (!real_RtlGenRandom((PVOID) buf, (ULONG) size)) { + PRINT_DEBUG("RtlGenRandom ERROR\n"); + Sleep(100); + } + PRINT_DEBUG("%s.\n", "real_RtlGenRandom returned"); + } + return; } std::string get_env_variable(std::string name) @@ -818,16 +841,55 @@ HINTERNET WINAPI Mine_WinHttpOpenRequest( return Real_WinHttpOpenRequest(hConnect, pwszVerb, pwszObjectName, pwszVersion, pwszReferrer, ppwszAcceptTypes, dwFlags); } - - static bool network_functions_attached = false; +std::vector detours_threads; + BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) { switch ( dwReason ) { case DLL_PROCESS_ATTACH: if (!file_exists(get_full_program_path() + "disable_lan_only.txt") && !file_exists(get_full_program_path() + "\\steam_settings\\disable_lan_only.txt")) { PRINT_DEBUG("Hooking lan only functions\n"); DetourTransactionBegin(); - DetourUpdateThread( GetCurrentThread() ); + HANDLE toolHelp = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (toolHelp != INVALID_HANDLE_VALUE) { + THREADENTRY32 te; + te.dwSize = sizeof(THREADENTRY32); + if (Thread32First(toolHelp, &te)) { + bool bUpdatedThread = false; + DWORD myPID = GetCurrentProcessId(); + DWORD myTID = GetCurrentThreadId(); + HANDLE tHandle; + do { + if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) { + if (te.th32OwnerProcessID == myPID) { + if (te.th32ThreadID != myTID) { + tHandle = OpenThread(THREAD_ALL_ACCESS, false, te.th32ThreadID); + if (tHandle != NULL) { + PRINT_DEBUG("Hooking thread %d\n", te.th32ThreadID); + detours_threads.push_back( tHandle ); + DetourUpdateThread( tHandle ); + bUpdatedThread = true; + } else { + PRINT_DEBUG("Unable to hook thread %d\n", te.th32ThreadID); + } + } else {//hooking non-pseudo current thread handle is unsupported. + PRINT_DEBUG("Hooking thread %d\n", myTID); + DetourUpdateThread( GetCurrentThread() ); + bUpdatedThread = true; + } + } + } + te.dwSize = sizeof(THREADENTRY32); + } while (Thread32Next(toolHelp, &te)); + } else { + PRINT_DEBUG("Unable to iterate thread list, only hooking current thread\n"); + DetourUpdateThread( GetCurrentThread() ); + } + CloseHandle(toolHelp); + } else { + PRINT_DEBUG("Unable to get thread list, only hooking current thread\n"); + DetourUpdateThread( GetCurrentThread() ); + } DetourAttach( &(PVOID &)Real_SendTo, Mine_SendTo ); DetourAttach( &(PVOID &)Real_Connect, Mine_Connect ); DetourAttach( &(PVOID &)Real_WSAConnect, Mine_WSAConnect ); @@ -848,8 +910,12 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) { break; case DLL_PROCESS_DETACH: + std::vector::iterator it; if (network_functions_attached) { DetourTransactionBegin(); + for (it = detours_threads.begin(); it != detours_threads.end(); it++) { + DetourUpdateThread( *it ); + } DetourUpdateThread( GetCurrentThread() ); DetourDetach( &(PVOID &)Real_SendTo, Mine_SendTo ); DetourDetach( &(PVOID &)Real_Connect, Mine_Connect ); diff --git a/dll/base.h b/dll/base.h index fe59cba..6fa5cfe 100644 --- a/dll/base.h +++ b/dll/base.h @@ -325,12 +325,12 @@ public: if (std::find(callbacks[iCallback].callbacks.begin(), callbacks[iCallback].callbacks.end(), cb) == callbacks[iCallback].callbacks.end()) { callbacks[iCallback].callbacks.push_back(cb); - CCallbackMgr::SetRegister(cb, iCallback); for (auto & res: callbacks[iCallback].results) { //TODO: timeout? SteamAPICall_t api_id = results->addCallResult(iCallback, &(res[0]), res.size(), 0.0, false); results->addCallBack(api_id, cb); } + CCallbackMgr::SetRegister(cb, iCallback); } } diff --git a/dll/common_includes.h b/dll/common_includes.h index 5de14c4..a00cc04 100644 --- a/dll/common_includes.h +++ b/dll/common_includes.h @@ -65,6 +65,18 @@ #include #undef SystemFunction036 + #ifdef _MSC_VER + #ifndef PRIu64 + #define PRIu64 "I64u" + #endif + #ifndef PRIuPTR + #define PRIuPTR "Iu" + #endif + #define PRI_ZU "Iu" // Format specifier for size_t. + #else + #include + #endif + #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 @@ -74,6 +86,7 @@ #ifdef EMU_EXPERIMENTAL_BUILD #include + #include #include "../detours/detours.h" #endif @@ -127,6 +140,15 @@ inline void reset_LastError() #include #include + #include + #ifndef PRIu64 + #define PRIu64 "I64u" + #endif + #ifndef PRIuPTR + #define PRIuPTR "Iu" + #endif + #define PRI_ZU "zu" + #define PATH_MAX_STRING_SIZE 512 #ifndef EMU_RELEASE_BUILD @@ -152,6 +174,7 @@ inline void reset_LastError() #include #include #include +#include #include #include @@ -203,6 +226,10 @@ inline std::string ascii_to_lowercase(std::string data) { #define DEFAULT_LANGUAGE "english" +#define DEFAULT_UI_NOTIFICATION_POSITION "top right" + #define LOBBY_CONNECT_APPID ((uint32)-2) -#endif//__INCLUDED_COMMON_INCLUDES__ \ No newline at end of file +#define FRIEND_AVATAR_MAX_IMAGE_LENGTH (5 * 1024 * 1024) + +#endif//__INCLUDED_COMMON_INCLUDES__ diff --git a/dll/dll.cpp b/dll/dll.cpp index 4fef715..c90dee9 100644 --- a/dll/dll.cpp +++ b/dll/dll.cpp @@ -17,7 +17,9 @@ #define STEAM_API_FUNCTIONS_IMPL #include "dll.h" - +#ifdef STEAM_WIN32 +#include +#endif static char old_client[128] = "SteamClient017"; static char old_gameserver[128] = "SteamGameServer012"; @@ -140,6 +142,16 @@ Steam_Client *get_steam_client() return steamclient_instance; } +Steam_Client *try_get_steam_client() +{ + Steam_Client * ret = NULL; + if (global_mutex.try_lock() == true) { + ret = steamclient_instance; + global_mutex.unlock(); + } + return ret; +} + void destroy_client() { std::lock_guard lock(global_mutex); @@ -234,6 +246,40 @@ STEAMAPI_API void * S_CALLTYPE SteamInternal_ContextInit( void *pContextInitData return &contextInitData->ctx; } +static std::mutex minidump_global_mutex; +static bool minidump_enable = true; +static bool minidump_comment_enable = true; +static std::string minidump_comment; +static std::string minidump_path; +static std::string start_time; + +bool get_minidump_enable() { + std::lock_guard lock(minidump_global_mutex); + return minidump_enable; +} + +bool get_minidump_comment_enable() { + std::lock_guard lock(minidump_global_mutex); + return minidump_comment_enable; +} + +void set_minidump_enable(bool enable) { + std::lock_guard lock(minidump_global_mutex); + minidump_enable = enable; + return; +} + +void set_minidump_comment_enable(bool enable) { + std::lock_guard lock(minidump_global_mutex); + minidump_comment_enable = enable; + return; +} + +std::string get_start_time() { + std::lock_guard lock(minidump_global_mutex); + return start_time; +} + //steam_api.h // SteamAPI_Init must be called before using any other API functions. If it fails, an // error message will be output to the debugger (or stderr) with further information. @@ -242,6 +288,11 @@ STEAMAPI_API steam_bool S_CALLTYPE SteamAPI_Init() { PRINT_DEBUG("SteamAPI_Init called\n"); if (user_steam_pipe) return true; + { + std::lock_guard lock(minidump_global_mutex); + if (start_time.length() <= 0) start_time = std::to_string((std::chrono::duration_cast(((std::chrono::system_clock::now()).time_since_epoch()))).count()); + if (minidump_path.length() <= 0) minidump_path = Local_Storage::get_user_appdata_path().append(PATH_SEPARATOR).append("minidumps").append(PATH_SEPARATOR).append("steam_minidump_").append(start_time).append(".mdmp"); + } #ifdef EMU_EXPERIMENTAL_BUILD crack_SteamAPI_Init(); #endif @@ -330,6 +381,11 @@ STEAMAPI_API void S_CALLTYPE SteamAPI_Shutdown() STEAMAPI_API steam_bool S_CALLTYPE SteamAPI_RestartAppIfNecessary( uint32 unOwnAppID ) { PRINT_DEBUG("SteamAPI_RestartAppIfNecessary %u\n", unOwnAppID); + { + std::lock_guard lock(minidump_global_mutex); + if (start_time.length() <= 0) start_time = std::to_string((std::chrono::duration_cast(((std::chrono::system_clock::now()).time_since_epoch()))).count()); + if (minidump_path.length() <= 0) minidump_path = Local_Storage::get_user_appdata_path().append(PATH_SEPARATOR).append("minidumps").append(PATH_SEPARATOR).append("steam_minidump_").append(start_time).append(".mdmp"); + } #ifdef EMU_EXPERIMENTAL_BUILD crack_SteamAPI_RestartAppIfNecessary(unOwnAppID); #endif @@ -350,11 +406,93 @@ STEAMAPI_API void S_CALLTYPE SteamAPI_ReleaseCurrentThreadMemory() STEAMAPI_API void S_CALLTYPE SteamAPI_WriteMiniDump( uint32 uStructuredExceptionCode, void* pvExceptionInfo, uint32 uBuildID ) { PRINT_DEBUG("SteamAPI_WriteMiniDump\n"); +#ifdef STEAM_WIN32 + if (!get_minidump_enable()) + return; + + std::lock_guard lock(minidump_global_mutex); + + MINIDUMP_EXCEPTION_INFORMATION mei; + memset(&mei, '\0', sizeof(MINIDUMP_EXCEPTION_INFORMATION)); + mei.ThreadId = GetCurrentThreadId(); + mei.ExceptionPointers = (EXCEPTION_POINTERS*)pvExceptionInfo; + mei.ClientPointers = false; + + char expCode[40] = {'S', 't', 'r', 'u', 'c', 't', 'u', 'r', 'e', 'd', ' ', + 'E', 'x', 'c', 'e', 'p', 't', 'i', 'o', 'n', ' ', + 'C', 'o', 'd', 'e', ':', ' ', + '4', '2', '9', '4', '9', '6', '7', '2', '9', '5', '\0', '\0'}; + char * printExpCode = &expCode[26]; + snprintf(printExpCode, (39 - 26), "%u", uStructuredExceptionCode); + + char buildId[25] = { 'B', 'u', 'i', 'l', 'd', ' ', + 'I', 'D', ':', ' ', + '4', '2', '9', '4', '9', '6', '7', '2', '9', '5', '\0', '\0', '\0', '\0', '\0'}; + char * printBuildId = &expCode[10]; + snprintf(printBuildId, (24 - 10), "%u", uBuildID); + + MINIDUMP_USER_STREAM mus[3]; + memset(&mus, '\0', sizeof(MINIDUMP_USER_STREAM) * 3); + + mus[0].Type = CommentStreamA; + mus[0].BufferSize = 40; + mus[0].Buffer = (void*)&expCode; + + mus[1].Type = CommentStreamA; + mus[1].BufferSize = 25; + mus[1].Buffer = (void*)&buildId; + + mus[2].BufferSize = minidump_comment.length(); + if (minidump_comment.length() > 0) { + mus[2].Type = CommentStreamA; + mus[2].BufferSize = minidump_comment.length(); + if (mus[2].BufferSize > 0) { + mus[2].Buffer = (void*)minidump_comment.c_str(); + } + } + + MINIDUMP_USER_STREAM_INFORMATION musi; + memset(&musi, '\0', sizeof(MINIDUMP_USER_STREAM_INFORMATION)); + musi.UserStreamCount = (minidump_comment.length() > 0) ? 3 : 2; + musi.UserStreamArray = (MINIDUMP_USER_STREAM*)&mus; + if (minidump_path.length() > 0) { + HANDLE hFile = CreateFile(minidump_path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hFile != INVALID_HANDLE_VALUE) { + MiniDumpWriteDump(GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithProcessThreadData | MiniDumpWithThreadInfo | MiniDumpWithUnloadedModules | MiniDumpWithTokenInformation), + ((pvExceptionInfo != NULL) ? &mei : NULL), + &musi, + NULL); + CloseHandle(hFile); + } + } +#endif + return; } STEAMAPI_API void S_CALLTYPE SteamAPI_SetMiniDumpComment( const char *pchMsg ) { - PRINT_DEBUG("SteamAPI_SetMiniDumpComment: %s\n", pchMsg); + if (get_minidump_enable() == false || get_minidump_comment_enable() == false) + return; + + std::lock_guard lock(minidump_global_mutex); + + if (pchMsg != NULL) { + PRINT_DEBUG("SteamAPI_SetMiniDumpComment: %s\n", pchMsg); + minidump_comment = std::string(pchMsg); + } else { + PRINT_DEBUG("SteamAPI_SetMiniDumpComment: Empty string.\n"); + minidump_comment.clear(); + } + return; } //----------------------------------------------------------------------------------------------------------------------------------------------------------// diff --git a/dll/dll.h b/dll/dll.h index 1b34715..72ed264 100644 --- a/dll/dll.h +++ b/dll/dll.h @@ -27,8 +27,149 @@ Steam_Client *get_steam_client(); bool steamclient_has_ipv6_functions(); +Steam_Client *try_get_steam_client(); + +bool get_minidump_enable(); +bool get_minidump_comment_enable(); +void set_minidump_enable(bool enable); +void set_minidump_comment_enable(bool enable); +std::string get_start_time(); HSteamUser flat_hsteamuser(); HSteamPipe flat_hsteampipe(); HSteamUser flat_gs_hsteamuser(); HSteamPipe flat_gs_hsteampipe(); + +#define GOLDBERG_CALLBACK_INTERNAL(parent, fname, cb_type) \ + struct GB_CCallbackInternal_ ## fname : private GB_CCallbackInterImp< sizeof(cb_type) > { \ + public: \ + GB_CCallbackInternal_ ## fname () { \ + PRINT_DEBUG("GB_CCallbackInternal_%s::%s %s 0x%p.\n", \ + #fname, \ + #fname, \ + "Default constructor", \ + this); \ + this->reg_thread_has_run = false; \ + std::thread th = std::thread(GB_CCallbackInterImp::_register, this, cb_type::k_iCallback); \ + th.detach(); \ + } \ + GB_CCallbackInternal_ ## fname ( const GB_CCallbackInternal_ ## fname & a ) { \ + PRINT_DEBUG("GB_CCallbackInternal_%s::%s %s 0x%p.\n", \ + #fname, \ + #fname, \ + "Copy constructor", \ + this); \ + this->reg_thread_has_run = false; \ + std::thread th = std::thread(GB_CCallbackInterImp::_register, this, cb_type::k_iCallback); \ + th.detach(); \ + } \ + GB_CCallbackInternal_ ## fname & operator=(const GB_CCallbackInternal_ ## fname &) { \ + PRINT_DEBUG("GB_CCallbackInternal_%s::%s %s 0x%p.\n", \ + #fname, \ + #fname, \ + "Assignment = operator", \ + this); \ + return *this; \ + } \ + virtual void Run(void *callback) { \ + PRINT_DEBUG("GB_CCallbackInternal_%s::Run 0x%p Callback argument: 0x%p.\n", \ + #fname, \ + this, \ + callback); \ + if (m_nCallbackFlags & k_ECallbackFlagsRegistered) { \ + parent *obj = reinterpret_cast(reinterpret_cast(this) - offsetof(parent, m_steamcallback_ ## fname)); \ + obj->fname(reinterpret_cast(callback)); \ + } \ + } \ + bool isRegistered() { \ + return ( m_nCallbackFlags & k_ECallbackFlagsRegistered ); \ + } \ + private: \ + } m_steamcallback_ ## fname ; \ + void fname(cb_type *callback) ; \ + bool cb_type ## _is_registered() { \ + return m_steamcallback_ ## fname.isRegistered(); \ + } + +template +class GB_CCallbackInterImp : protected CCallbackBase +{ + public: + virtual ~GB_CCallbackInterImp() { + _unregister(this); + return; + } + void SetGameserverFlag() { + m_nCallbackFlags |= k_ECallbackFlagsGameServer; + return; + } + protected: + friend class CCallbackMgr; + std::atomic reg_thread_has_run; + virtual void Run(void *callback) = 0; + virtual void Run(void *callback, bool io_failure, SteamAPICall_t api_fp) { + Run(callback); + return; + } + virtual int GetCallbackSizeBytes() { + return sizeof_cb_type; + } + static void _register(void * arg, int callback) { + GB_CCallbackInterImp * gb = (GB_CCallbackInterImp *)arg; + if (gb != NULL) { + PRINT_DEBUG("GB_CCallbackInterImp::_register Begin registration thread for 0x%p callback %d.\n", + gb, + callback); + if (!(gb->m_nCallbackFlags & k_ECallbackFlagsRegistered)) { + bool ready = false; + do { + if (global_mutex.try_lock() == true) { + Steam_Client * client = try_get_steam_client(); + if (client != NULL) { + client->RegisterCallback(gb, callback); + ready = true; + gb->reg_thread_has_run = true; + PRINT_DEBUG("GB_CCallbackInterImp::_register Registration complete for 0x%p callback %d.\n", + gb, + callback); + } + global_mutex.unlock(); + } + } while (!ready); + } + PRINT_DEBUG("GB_CCallbackInterImp::_register Exiting registration thread for 0x%p callback %d.\n", + gb, + callback); + } + return; + } + static void _unregister(void * arg) { + GB_CCallbackInterImp * gb = (GB_CCallbackInterImp *)arg; + if (gb != NULL) { + PRINT_DEBUG("GB_CCallbackInterImp::_unregister Begin deregistration thread for 0x%p.\n", + gb); + bool can_dereg = false; + do { + can_dereg = gb->reg_thread_has_run; + } while (!can_dereg); + if (gb->m_nCallbackFlags & k_ECallbackFlagsRegistered) { + bool ready = false; + do { + if (global_mutex.try_lock() == true) { + Steam_Client * client = try_get_steam_client(); + if (client != NULL) { + client->UnregisterCallback(gb); + ready = true; + PRINT_DEBUG("GB_CCallbackInterImp::_unregister Deregistration complete for 0x%p.\n", + gb); + } + global_mutex.unlock(); + } + } while (!ready); + PRINT_DEBUG("GB_CCallbackInterImp::_unregister Exiting deregistration thread for 0x%p.\n", + gb); + } + } + return; + } +}; diff --git a/dll/local_storage.cpp b/dll/local_storage.cpp index f6ab1fc..7cb0bc6 100644 --- a/dll/local_storage.cpp +++ b/dll/local_storage.cpp @@ -34,18 +34,180 @@ struct File_Data { std::string name; }; +std::string convert_vector_image_pixel_t_to_std_string(std::vector in) { + std::string out; + + for (auto i : in) { + out += i.channels.r; + out += i.channels.g; + out += i.channels.b; + out += i.channels.a; + } + + return out; +} + +struct our_stbi_buffer { + uint8 * target; + size_t target_length; + size_t current_offset; +}; + +void our_stbi_write_func(void *context, void *data, int size) { + struct our_stbi_buffer * con = (struct our_stbi_buffer *)context; + uint8 * in_data = (uint8 *)data; + if (in_data != NULL && + con != NULL && + con->target != NULL && + con->target_length > 0 && + con->current_offset < con->target_length && + (con->current_offset + size) < con->target_length) { + for (size_t x = 0; (x < size && (con->current_offset + x) < con->target_length); x++) { + con->target[(con->current_offset + x)] = in_data[x]; + } + con->current_offset += size; + } + return; +} + +std::string convert_raw_uint8_to_png_std_string(uint8 * in, int width, int height, int components) { + struct our_stbi_buffer buf; + std::string out; + out.clear(); + + if (in != NULL && width > 0 && height > 0 && components > 0 && components <= 4) { + buf.target_length = (width * height * components); + buf.current_offset = 0; + buf.target = new uint8[(width * height * components)]; + if (buf.target != NULL) { + if (stbi_write_png_to_func(our_stbi_write_func, &buf, width, height, components, in, 0) == 1) { + for (size_t x = 0; x < (width * height * components); x++) { + char a = (char)buf.target[x]; + out += a; + } + } + + delete buf.target; + buf.target = NULL; + } + buf.target_length = 0; + buf.current_offset = 0; + } + + return out; +} + +std::string convert_raw_uint8_to_jpg_std_string(uint8 * in, int width, int height, int components) { + struct our_stbi_buffer buf; + std::string out; + out.clear(); + + if (in != NULL && width > 0 && height > 0 && components > 0 && components <= 4) { + buf.target_length = (width * height * components); + buf.current_offset = 0; + buf.target = new uint8[(width * height * components)]; + if (buf.target != NULL) { + if (stbi_write_jpg_to_func(our_stbi_write_func, &buf, width, height, components, in, 0) == 1) { + for (size_t x = 0; x < (width * height * components); x++) { + char a = (char)buf.target[x]; + out += a; + } + } + + delete buf.target; + buf.target = NULL; + } + buf.target_length = 0; + buf.current_offset = 0; + } + + return out; +} + +std::string convert_imgbuf_std_string_to_std_string_uint8(std::string in, + int * out_width, + int * out_height, + int * out_components, + int desired_components) { + std::string out; + out.clear(); + int w = 0; + int h = 0; + int c = 0; + + if (in.length() > 0 && + desired_components > 0 && + desired_components <= 4) { + uint8 * buf = (uint8*)stbi_load_from_memory((stbi_uc *)in.c_str(), in.length(), &w, &h, &c, desired_components); + if (buf != NULL) { + if (w > 0 && h > 0 && desired_components > 0) { + for (size_t x = 0; x < (w * h * desired_components); x++) { + char a = buf[x]; + out += a; + } + } + if (out_width != NULL) { + *out_width = w; + } + if (out_height != NULL) { + *out_height = h; + } + if (out_components != NULL) { + *out_components = c; + } + stbi_image_free(buf); + } else { + out.clear(); + if (out_width != NULL) { + *out_width = 0; + } + if (out_height != NULL) { + *out_height = 0; + } + if (out_components != NULL) { + *out_components = 0; + } + PRINT_DEBUG("%s %p. reason: %s\n", "Failed to decode image at", &in, stbi_failure_reason()); + } + } + + return out; +} + +std::string convert_png_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components) { + return convert_imgbuf_std_string_to_std_string_uint8(in, width, height, components, desired_components); +} + +std::string convert_jpg_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components) { + return convert_imgbuf_std_string_to_std_string_uint8(in, width, height, components, desired_components); +} + #ifdef NO_DISK_WRITES std::string Local_Storage::get_program_path() { return " "; } +std::string Local_Storage::get_user_pictures_path() +{ + return " "; +} std::string Local_Storage::get_user_appdata_path() { return " "; } +bool Local_Storage::is_directory(std::string &path) +{ + return false; +} + +std::string Local_Storage::get_parent_directory(std::string &path) +{ + return " "; +} + std::string Local_Storage::get_game_settings_path() { return " "; @@ -71,7 +233,7 @@ void Local_Storage::setAppId(uint32 appid) } -int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length) +int Local_Storage::store_file_data(std::string folder, std::string file, const char *data, unsigned int length) { return -1; } @@ -81,7 +243,7 @@ int Local_Storage::store_data(std::string folder, std::string file, char *data, return -1; } -int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length) +int Local_Storage::store_data_settings(std::string file, const char *data, unsigned int length) { return -1; } @@ -111,6 +273,11 @@ bool Local_Storage::file_exists(std::string folder, std::string file) return false; } +bool Local_Storage::data_settings_exists(std::string file) +{ + return false; +} + unsigned int Local_Storage::file_size(std::string folder, std::string file) { return 0; @@ -121,6 +288,11 @@ bool Local_Storage::file_delete(std::string folder, std::string file) return false; } +bool Local_Storage::delete_data_settings(std::string file) +{ + return false; +} + uint64_t Local_Storage::file_timestamp(std::string folder, std::string file) { return 0; @@ -156,7 +328,7 @@ std::vector Local_Storage::get_filenames_path(std::string path) return std::vector(); } -std::vector Local_Storage::load_image(std::string const& image_path) +std::vector Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height) { return std::vector(); } @@ -272,6 +444,29 @@ static std::vector get_filenames_recursive(std::string base_pa #else +static bool DirectoryExists(const char *path) { + char tmp[PATH_MAX_STRING_SIZE]; + struct stat sb; + size_t len; + + /* copy path */ + len = strnlen (path, PATH_MAX_STRING_SIZE); + if (len == 0 || len == PATH_MAX_STRING_SIZE) { + return false; + } + memcpy (tmp, path, len); + tmp[len] = '\0'; + + /* check if path exists and is a directory */ + if (stat (tmp, &sb) == 0) { + if (S_ISDIR (sb.st_mode)) { + return true; + } + } + + return false; +} + /* recursive mkdir */ static int mkdir_p(const char *dir, const mode_t mode) { char tmp[PATH_MAX_STRING_SIZE]; @@ -410,6 +605,26 @@ std::string Local_Storage::get_game_settings_path() return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR); } +std::string Local_Storage::get_user_pictures_path() { + std::string user_pictures_path = "Pictures"; +#if defined(STEAM_WIN32) + WCHAR szPath[MAX_PATH] = {}; + + HRESULT hr = SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, 0, szPath); + + if (SUCCEEDED(hr)) { + user_pictures_path = utf8_encode(szPath); + } + +#else + char *datadir = getenv("HOME"); + if (datadir) { + user_pictures_path = datadir; + } +#endif + return user_pictures_path; +} + std::string Local_Storage::get_user_appdata_path() { std::string user_appdata_path = "SAVE"; @@ -492,12 +707,77 @@ Local_Storage::Local_Storage(std::string save_directory) } } +std::string Local_Storage::get_parent_directory(std::string &path) +{ + std::string ret = ""; + std::string temp = ""; + + ret = sanitize_file_name(path); + if (ret.length() > 1) { + temp = ret[(ret.length() - 1)]; + if (temp == PATH_SEPARATOR) { + ret.erase((ret.length() - 1), 1); + } + } + + if (ret.length() > 1) { + size_t last_pos = 0; + size_t count = 0; + for (auto i : ret) { + temp = i; + if (temp == PATH_SEPARATOR) { + last_pos = count; + } + count = count + 1; + } + if ((last_pos > 0) && (last_pos < (count - 1))) { + ret.erase(last_pos, (count - 1)); + } + } + + ret = desanitize_file_name(ret); + + return ret; +} + +bool Local_Storage::is_directory(std::string &path) +{ + bool ret = false; +#if defined(STEAM_WIN32) + std::wstring strPath = utf8_decode(path); + ret = DirectoryExists(strPath.c_str()); +#else + ret = DirectoryExists(path.c_str()); +#endif + return ret; +} + +std::vector Local_Storage::get_drive_list() +{ + std::vector ret; +#if defined(STEAM_WIN32) + DWORD drives = GetLogicalDrives(); + if (drives != 0) { + for (unsigned int x = 0; x < 26; x++) { + if (drives & (((DWORD)0x1) << x)) { + char tmp[2] = { 'A' + (char)x, '\0' }; + ret.push_back(std::string(tmp) + ":"); + } + } + } +#else + //TODO: Parse /proc/self/mountinfo + ret.push_back("/"); +#endif + return ret; +} + void Local_Storage::setAppId(uint32 appid) { this->appid = std::to_string(appid) + PATH_SEPARATOR; } -int Local_Storage::store_file_data(std::string folder, std::string file, char *data, unsigned int length) +int Local_Storage::store_file_data(std::string folder, std::string file, const char *data, unsigned int length) { if (folder.back() != *PATH_SEPARATOR) { folder.append(PATH_SEPARATOR); @@ -556,11 +836,62 @@ int Local_Storage::store_data(std::string folder, std::string file, char *data, return store_file_data(save_directory + appid + folder, file, data, length); } -int Local_Storage::store_data_settings(std::string file, char *data, unsigned int length) +int Local_Storage::store_data_settings(std::string file, const char *data, unsigned int length) { return store_file_data(get_global_settings_path(), file, data, length); } +int Local_Storage::copy_file_data(std::string src_full_path, std::string dest_full_path) +{ + std::ifstream srcfile; + std::ofstream destfile; + char * buf = NULL; + size_t readcount = 0; + const size_t bufsize = 1024; + bool fail = false; + + buf = new char[bufsize]; + if (buf == NULL) return -1; + + srcfile.open(utf8_decode(src_full_path), std::ios::binary | std::ios::in); + if (!srcfile.is_open()) { + delete buf; + buf = NULL; + return -1; + } + + destfile.open(utf8_decode(dest_full_path), std::ios::binary | std::ios::out); + if (!destfile.is_open()) { + delete buf; + buf = NULL; + srcfile.close(); + return -1; + } + + srcfile.seekg(0, std::ios::beg); + destfile.seekp(0, std::ios::beg); + + do { + buf[readcount] = srcfile.get(); + if (srcfile.eof() || (readcount + 1) == bufsize) { + destfile.write(buf, (readcount + 1)); + readcount = 0; + } else { + readcount++; + } + if (srcfile.fail() || destfile.fail()) { + fail = true; + } + } while (srcfile.eof() == false && srcfile.fail() == false && destfile.fail() == false); + + srcfile.close(); + destfile.close(); + reset_LastError(); + delete buf; + buf = NULL; + return (!fail) ? 1 : 0; +} + int Local_Storage::get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset) { std::ifstream myfile; @@ -612,6 +943,14 @@ bool Local_Storage::file_exists(std::string folder, std::string file) return file_exists_(full_path); } +bool Local_Storage::data_settings_exists(std::string file) +{ + file = sanitize_file_name(file); + + std::string full_path = get_global_settings_path() + file; + return file_exists_(full_path); +} + unsigned int Local_Storage::file_size(std::string folder, std::string file) { file = sanitize_file_name(file); @@ -623,6 +962,23 @@ unsigned int Local_Storage::file_size(std::string folder, std::string file) return file_size_(full_path); } +unsigned int Local_Storage::data_settings_size(std::string file) +{ + file = sanitize_file_name(file); + + std::string full_path = get_global_settings_path() + file; + return file_size_(full_path); +} + +bool _internal_file_delete(std::string & full_path) +{ +#if defined(STEAM_WIN32) + return _wremove(utf8_decode(full_path).c_str()) == 0; +#else + return remove(full_path.c_str()) == 0; +#endif +} + bool Local_Storage::file_delete(std::string folder, std::string file) { file = sanitize_file_name(file); @@ -631,11 +987,15 @@ bool Local_Storage::file_delete(std::string folder, std::string file) } std::string full_path = save_directory + appid + folder + file; -#if defined(STEAM_WIN32) - return _wremove(utf8_decode(full_path).c_str()) == 0; -#else - return remove(full_path.c_str()) == 0; -#endif + return _internal_file_delete(full_path); +} + +bool Local_Storage::delete_data_settings(std::string file) +{ + file = sanitize_file_name(file); + + std::string full_path = get_global_settings_path() + file; + return _internal_file_delete(full_path); } uint64_t Local_Storage::file_timestamp(std::string folder, std::string file) @@ -717,7 +1077,7 @@ bool Local_Storage::load_json(std::string full_path, nlohmann::json& json) try { json = std::move(nlohmann::json::parse(buffer)); - PRINT_DEBUG("Loaded json \"%s\". Loaded %u items.\n", full_path.c_str(), json.size()); + PRINT_DEBUG("Loaded json \"%s\". Loaded %" PRI_ZU " 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()); @@ -766,10 +1126,11 @@ bool Local_Storage::write_json_file(std::string folder, std::string const&file, return false; } -std::vector Local_Storage::load_image(std::string const& image_path) +std::vector Local_Storage::load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height) { std::vector res; - int width, height; + int32_t width = 0; + int32_t height = 0; image_pixel_t* img = (image_pixel_t*)stbi_load(image_path.c_str(), &width, &height, nullptr, 4); if (img != nullptr) { @@ -777,12 +1138,76 @@ std::vector Local_Storage::load_image(std::string const& image_pa std::copy(img, img + width * height, res.begin()); stbi_image_free(img); + } else { + width = 0; + height = 0; + PRINT_DEBUG("%s %s. reason: %s\n", "Failed to load image at", image_path.c_str(), stbi_failure_reason()); + } + if (out_width != nullptr) { + if (width > 0) { + *out_width = static_cast(width); + } else { + *out_width = 0; + } + } + if (out_height != nullptr) { + if (height > 0) { + *out_height = static_cast(height); + } else { + *out_height = 0; + } } reset_LastError(); return res; } +int32_t Local_Storage::save_avatar_image(int32_t eAvatarSize, int32_t width, int32_t height, uint8_t * img_ptr) +{ + int32_t ret = 0; + std::string image_path = ""; + switch (eAvatarSize) { + case k_EAvatarSize32x32: + if (width > 0 && + width <= 32 && + height > 0 && + height <= 32) { + image_path += "avatar_small."; + } + break; + case k_EAvatarSize64x64: + if (width > 32 && + width <= 64 && + height > 32 && + height <= 64) { + image_path += "avatar_medium."; + } + break; + case k_EAvatarSize184x184: + if (width > 64 && + width <= 184 && + height > 64 && + height <= 184) { + image_path += "avatar_large."; + } + break; + case k_EAvatarSizeMAX: + default: + image_path.clear(); + break; + }; + + if (image_path.length() > 0 && img_ptr != NULL) { + delete_data_settings(image_path + "jpg"); + image_path += "png"; + delete_data_settings(image_path); + + image_path = get_global_settings_path() + image_path; + ret = (stbi_write_png(image_path.c_str(), width, height, 4, img_ptr, 0) == 1); + } + return ret; +} + 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); diff --git a/dll/local_storage.h b/dll/local_storage.h index ddb1c33..301a177 100644 --- a/dll/local_storage.h +++ b/dll/local_storage.h @@ -41,6 +41,13 @@ struct image_t std::vector pix_map; }; +std::string convert_vector_image_pixel_t_to_std_string(std::vector in); +std::string convert_raw_uint8_to_png_std_string(uint8 * in, int width, int height, int components); +std::string convert_raw_uint8_to_jpg_std_string(uint8 * in, int width, int height, int components); + +std::string convert_png_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components); +std::string convert_jpg_buffer_std_string_to_std_string_uint8(std::string in, int * width, int * height, int * components, int desired_components); + class Local_Storage { public: static constexpr auto inventory_storage_folder = "inventory"; @@ -58,22 +65,30 @@ private: public: static std::string get_program_path(); static std::string get_game_settings_path(); + static std::string get_user_pictures_path(); static std::string get_user_appdata_path(); + static std::string get_parent_directory(std::string &path); + static std::vector get_drive_list(); + static bool is_directory(std::string &path); Local_Storage(std::string save_directory); static int get_file_data(std::string full_path, char *data, unsigned int max_length, unsigned int offset=0); + static int copy_file_data(std::string src_full_path, std::string dest_full_path); void setAppId(uint32 appid); - static int store_file_data(std::string folder, std::string file, char *data, unsigned int length); + static int store_file_data(std::string folder, std::string file, const char *data, unsigned int length); static std::vector 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 store_data_settings(std::string file, const char *data, unsigned int 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); bool file_exists(std::string folder, std::string file); + bool data_settings_exists(std::string file); unsigned int file_size(std::string folder, std::string file); + unsigned int data_settings_size(std::string file); bool file_delete(std::string folder, std::string file); + bool delete_data_settings(std::string file); uint64_t file_timestamp(std::string folder, std::string file); std::string get_global_settings_path(); std::string get_path(std::string folder); @@ -84,7 +99,8 @@ public: 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 load_image(std::string const& image_path); + std::vector load_image(std::string const& image_path, uint32_t * out_width, uint32_t * out_height); + int32_t save_avatar_image(int32_t eAvatarSize, int32_t width, int32_t height, uint8_t * img_ptr); bool save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels); }; diff --git a/dll/net.proto b/dll/net.proto index 32e7852..cc1318f 100644 --- a/dll/net.proto +++ b/dll/net.proto @@ -179,6 +179,31 @@ message Friend { uint64 lobby_id = 5; } +message Image { + enum MessageTypes { + REQUEST = 0; + NOTIFY = 1; + } + + enum ImgTypes { + RAW = 0; + JPG = 1; + PNG = 2; + GIF = 3; + } + + MessageTypes type = 1; + ImgTypes img_type = 2; + uint32 img_width = 3; + uint32 img_height = 4; + uint32 img_components = 5; + bytes img_data = 6; +} + +message Friend_Avatar { + Image img = 1; +} + message Auth_Ticket { uint32 number = 1; @@ -230,6 +255,7 @@ message Common_Message { Networking_Sockets networking_sockets = 13; Steam_Messages steam_messages = 14; Networking_Messages networking_messages = 15; + Friend_Avatar friend_avatar = 16; } uint32 source_ip = 128; diff --git a/dll/network.cpp b/dll/network.cpp index c7323c6..370e279 100644 --- a/dll/network.cpp +++ b/dll/network.cpp @@ -513,7 +513,7 @@ std::set Networking::resolve_ip(std::string dns) if (getaddrinfo(dns.c_str(), NULL, NULL, &result) == 0) { for (struct addrinfo *res = result; res != NULL; res = res->ai_next) { - PRINT_DEBUG("%u %u\n", res->ai_addrlen, res->ai_family); + PRINT_DEBUG("%" PRI_ZU " %u\n", res->ai_addrlen, res->ai_family); if (res->ai_family == AF_INET) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr; uint32 ip; @@ -524,6 +524,7 @@ std::set Networking::resolve_ip(std::string dns) ips.insert(addr); } } + freeaddrinfo(result); } return ips; @@ -580,6 +581,11 @@ void Networking::do_callbacks_message(Common_Message *msg) PRINT_DEBUG("has_networking_messages\n"); run_callbacks(CALLBACK_ID_NETWORKING_MESSAGES, msg); } + + if (msg->has_friend_avatar()) { + PRINT_DEBUG("has_friend_avatar\n"); + run_callbacks(CALLBACK_ID_FRIEND_AVATAR, msg); + } } bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket) diff --git a/dll/network.h b/dll/network.h index 72e9f39..8baaab7 100644 --- a/dll/network.h +++ b/dll/network.h @@ -61,6 +61,7 @@ enum Callback_Ids { CALLBACK_ID_NETWORKING_SOCKETS, CALLBACK_ID_STEAM_MESSAGES, CALLBACK_ID_NETWORKING_MESSAGES, + CALLBACK_ID_FRIEND_AVATAR, CALLBACK_IDS_MAX }; diff --git a/dll/rtlgenrandom.c b/dll/rtlgenrandom.c deleted file mode 100644 index e160da4..0000000 --- a/dll/rtlgenrandom.c +++ /dev/null @@ -1,4 +0,0 @@ -#include -#define RtlGenRandom SystemFunction036 -#define DLLEXPORT __declspec(dllexport) -DLLEXPORT BOOLEAN WINAPI RtlGenRandom(PVOID in, ULONG len) {} \ No newline at end of file diff --git a/dll/rtlgenrandom.def b/dll/rtlgenrandom.def deleted file mode 100644 index 3f23d20..0000000 --- a/dll/rtlgenrandom.def +++ /dev/null @@ -1,3 +0,0 @@ -LIBRARY advapi32.dll -EXPORTS -SystemFunction036 \ No newline at end of file diff --git a/dll/settings.cpp b/dll/settings.cpp index f347c0a..872555a 100644 --- a/dll/settings.cpp +++ b/dll/settings.cpp @@ -16,7 +16,7 @@ . */ #include "settings.h" - +#include "dll.h" std::string Settings::sanitize(std::string name) { @@ -45,6 +45,10 @@ Settings::Settings(CSteamID steam_id, CGameID game_id, std::string name, std::st this->name = this->name + " "; } + this->settings_parser_done = false; + + this->ui_notification_position = ""; + auto lang = sanitize(language); std::transform(lang.begin(), lang.end(), lang.begin(), ::tolower); lang.erase(std::remove(lang.begin(), lang.end(), ' '), lang.end()); @@ -54,6 +58,28 @@ Settings::Settings(CSteamID steam_id, CGameID game_id, std::string name, std::st this->offline = offline; this->create_unknown_leaderboards = true; + + this->next_free = 1; + this->preferred_network_image_type = Image::JPG; + + this->background_thread_exit = false; + PRINT_DEBUG("%s.\n", "Settings::Settings Creating new background_monitor thread"); + background_monitor_thread = std::thread(Settings::background_monitor_entry, this); +} + +Settings::~Settings() +{ + bool wait = false; + { + std::lock_guard lock(background_thread_mutex); + if (background_monitor_thread.joinable()) { + background_thread_exit = true; + wait = true; + } + } + if (wait) { + background_monitor_thread.join(); + } } CSteamID Settings::get_local_steam_id() @@ -214,13 +240,305 @@ void Settings::setLeaderboard(std::string leaderboard, enum ELeaderboardSortMeth leaderboards[leaderboard] = leader; } +int Settings::find_next_free_image_ref() { + int ret = 0; + + std::lock_guard lock(images_mutex); + + if (images.size() == 0) { + ret = 1; + } else { + for (auto x : images) { + auto y = images.upper_bound(x.first); + if (y == images.end()) { + if ((x.first + 1) == 0) { + ret = 1; + break; + } else { + ret = x.first + 1; + break; + } + } else { + if ((x.first + 1) < y->first) { + if ((x.first + 1) != 0) { + ret = x.first + 1; + break; + } + } + } + } + } + + next_free = ret; + + return ret; +} + +int Settings::remove_image(int ref) { + int ret = 0; + + std::lock_guard lock(images_mutex); + + auto x = images.find(ref); + if (x != images.end()) { + images.erase(x); + ret = find_next_free_image_ref(); + } else { + ret = next_free; + } + + return ret; +} + +int Settings::replace_image(int ref, std::string data, uint32 width, uint32 height) +{ + std::lock_guard lock(images_mutex); + int ret = 0; + if (ref == 0) { + ret = add_image(data, width, height); + } else { + auto t = images.find(ref); + if (t != images.end()) { + ret = t->first; + t->second.data = data; + t->second.width = width; + t->second.height = height; + } else { + ret = add_image(data, width, height); + } + } + return ret; +} + 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; + int last = 0; + std::lock_guard lock(images_mutex); + if (next_free != 0) { + last = next_free; + struct Image_Data dt; + dt.width = width; + dt.height = height; + dt.data = data; + images[last] = dt; + find_next_free_image_ref(); + } else { + PRINT_DEBUG("%s.\n", + "Settings::add_image failed. Buffer is full"); + } return last; } + +int Settings::get_image(int ref, std::string * data, uint32 * width, uint32 * height) +{ + std::lock_guard lock(images_mutex); + int ret = 0; + auto t = images.find(ref); + if (t != images.end()) { + ret = t->first; + if (data != NULL) { + *data = t->second.data; + } + if (width != NULL) { + *width = t->second.width; + } + if (height != NULL) { + *height = t->second.height; + } + } else { + ret = 0; + if (width != NULL) { + *width = 0; + } + if (height != NULL) { + *height = 0; + } + } + return ret; +} + +int Settings::get_profile_image(int eAvatarSize) +{ + std::lock_guard lock(images_mutex); + int ret = 0; + for (auto i : profile_images) { + if (i.first == eAvatarSize) { + ret = i.second; + break; + } + } + return ret; +} + +void Settings::background_monitor_entry(Settings * settings) { + PRINT_DEBUG("%s.\n", "Settings::background_monitor_entry thread starting"); + if (settings != NULL) { + settings->background_monitor(); + } + PRINT_DEBUG("%s.\n", "Settings::background_monitor_entry thread exiting"); + return; +} + +#define BACKGROUND_MONITOR_RATE 200 + +void Settings::background_monitor() { + bool exit = false; + do { + { + std::lock_guard lock(background_thread_mutex); + exit = background_thread_exit; + if (!exit) { + if (background_tasks.size() > 0) { + for (auto x = background_tasks.begin(); x != background_tasks.end(); x++) { + bool task_done = false; + Steam_Client * client = try_get_steam_client(); + if (client != NULL) { + switch (x->id) { + case Settings_Background_Task_IDs::NOTIFY_AVATAR_IMAGE: + { + PRINT_DEBUG("%s.\n", "Settings::background_monitor Got NOTIFY_AVATAR_IMAGE task"); + + if (client != NULL && client->steam_friends != NULL) { + if (disable_overlay == true || + (client->steam_overlay != NULL && + client->steam_overlay->RegisteredInternalCallbacks() == true)) { + client->steam_friends->GetFriendAvatar(this->steam_id, k_EAvatarSize32x32); + client->steam_friends->GetFriendAvatar(this->steam_id, k_EAvatarSize64x64); + client->steam_friends->GetFriendAvatar(this->steam_id, k_EAvatarSize184x184); + task_done = true; + } + } + } + break; + default: + PRINT_DEBUG("%s %d.\n", "Settings::background_monitor Unknown task", x->id); + task_done = true; + break; + }; + if (task_done) { + background_tasks.erase(x); + break; + } + } + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(BACKGROUND_MONITOR_RATE)); + } + } + } while(!exit); + return; +} + +void Settings::create_background_notify_task(Settings_Background_Task_IDs id, void *arg = NULL) { + std::lock_guard lock(background_thread_mutex); + background_tasks.push_back(Settings_Background_Task{id, arg}); + return; +} + +int Settings::set_profile_image(int eAvatarSize, Image_Data * image) +{ + bool size_ok = false; + int ref = 0; + + if (image != NULL) { + if (eAvatarSize == k_EAvatarSize32x32 && image->width > 0 && image->width <= 32 && image->height > 0 && image->height <= 32) + size_ok = true; + if (eAvatarSize == k_EAvatarSize64x64 && image->width > 32 && image->width <= 64 && image->height > 32 && image->height <= 64) + size_ok = true; + if (eAvatarSize == k_EAvatarSize184x184 && image->width > 64 && image->width <= 184 && image->height > 64 && image->height <= 184) + size_ok = true; + + if (size_ok == true && image->data.length() > 0) { + ref = this->add_image(image->data, image->width, image->height); + PRINT_DEBUG("Settings::set_profile_image %d -> %d.\n", eAvatarSize, ref); + std::lock_guard lock(images_mutex); + profile_images[eAvatarSize] = ref; + create_background_notify_task(Settings_Background_Task_IDs::NOTIFY_AVATAR_IMAGE, NULL); + } else { + PRINT_DEBUG("%s %d %dx%d %" PRI_ZU ".\n", + "Settings::set_profile_image failed", + eAvatarSize, + image->width, + image->height, + image->data.length()); + } + } + + return ref; +} + +int Settings::set_profile_image(int eAvatarSize, int reference, bool notify = true) +{ + bool size_ok = false; + int ref = 0; + std::lock_guard lock(images_mutex); + auto t = images.find(reference); + if (reference != 0 && + t != images.end()) { + ref = t->first; + if (eAvatarSize == k_EAvatarSize32x32 && t->second.width > 0 && t->second.width <= 32 && t->second.height > 0 && t->second.height <= 32) + size_ok = true; + if (eAvatarSize == k_EAvatarSize64x64 && t->second.width > 32 && t->second.width <= 64 && t->second.height > 32 && t->second.height <= 64) + size_ok = true; + if (eAvatarSize == k_EAvatarSize184x184 && t->second.width > 64 && t->second.width <= 184 && t->second.height > 64 && t->second.height <= 184) + size_ok = true; + + if (size_ok == true) { + PRINT_DEBUG("Settings::set_profile_image %d -> %d.\n", eAvatarSize, ref); + profile_images[eAvatarSize] = ref; + if (notify == true) { + create_background_notify_task(Settings_Background_Task_IDs::NOTIFY_AVATAR_IMAGE, NULL); + } + } else { + PRINT_DEBUG("%s %d ref: %d.\n", + "Settings::set_profile_image failed invalid size. size:", + eAvatarSize, + reference); + } + } else { + PRINT_DEBUG("%s %d ref: %d.\n", + "Settings::set_profile_image failed invalid reference. size:", + eAvatarSize, + reference); + } + + return ref; +} + +void Settings::set_preferred_network_image_type(int new_type) +{ + switch (new_type) { + case Image::RAW: + case Image::PNG: + case Image::JPG: + this->preferred_network_image_type = new_type; + break; + default: + PRINT_DEBUG("%s %d.\n", + "Settings::set_preferred_network_image_type failed. Requested type", + new_type); + break; + }; + return; +} + +std::string Settings::get_ui_notification_position() +{ + return this->ui_notification_position; +} + +void Settings::set_ui_notification_position(char * pos) +{ + PRINT_DEBUG("%s 0x%p.\n", + "Settings::set_ui_notification_position", + pos); + if (pos != NULL) { + size_t len = strlen(pos); + if (len > 0) { + this->ui_notification_position = ""; + for (size_t x = 0; x < len && pos[x] != '\0'; x++) { + this->ui_notification_position += pos[x]; + } + } + } +} diff --git a/dll/settings.h b/dll/settings.h index a7b2aa9..0f5f211 100644 --- a/dll/settings.h +++ b/dll/settings.h @@ -65,21 +65,45 @@ struct Controller_Settings { std::map, std::string>>> action_set_layers; }; +enum Settings_Background_Task_IDs { + NOTIFY_AVATAR_IMAGE = 0 +}; + +struct Settings_Background_Task { + enum Settings_Background_Task_IDs id; + void * arg; +}; + class Settings { CSteamID steam_id; CGameID game_id; - std::string name, language; + std::string name, language, ui_notification_position; CSteamID lobby_id; + uint32 preferred_network_image_type; + bool background_thread_exit; + std::vector background_tasks; + std::thread background_monitor_thread; + std::recursive_mutex background_thread_mutex; + std::atomic settings_parser_done; bool unlockAllDLCs; bool offline; + bool showAchievementDescOnUnlock; + bool showAchievementHiddenUnearned; std::vector DLCs; std::vector mods; std::map app_paths; std::map leaderboards; std::map stats; + std::map profile_images; bool create_unknown_leaderboards; uint16 port; + int next_free; + + int find_next_free_image_ref(); + void create_background_notify_task(Settings_Background_Task_IDs id, void *arg); + static void background_monitor_entry(Settings * settings); + void background_monitor(); public: #ifdef LOBBY_CONNECT @@ -87,8 +111,10 @@ public: #else static const bool is_lobby_connect = false; #endif + static std::string sanitize(std::string name); Settings(CSteamID steam_id, CGameID game_id, std::string name, std::string language, bool offline); + ~Settings(); CSteamID get_local_steam_id(); CGameID get_local_game_id(); const char *get_local_name(); @@ -103,6 +129,9 @@ public: uint16 get_port() {return port;} void set_port(uint16 port) { this->port = port;} + bool is_settings_parser_done() { return this->settings_parser_done; } + void set_settings_parser_done(const bool done) { this->settings_parser_done = done; } + //DLC stuff void unlockAllDLC(bool value); void addDLC(AppId_t appID, std::string name, bool available); @@ -132,6 +161,9 @@ public: //custom broadcasts std::set custom_broadcasts; + //custom master server + std::set custom_master_server; + //stats std::map getStats() { return stats; } void setStatDefiniton(std::string name, struct Stat_config stat_config) {stats[ascii_to_lowercase(name)] = stat_config; } @@ -140,8 +172,17 @@ public: std::set subscribed_groups; //images + std::recursive_mutex images_mutex; std::map images; + int remove_image(int ref); + int replace_image(int ref, std::string data, uint32 width, uint32 height); int add_image(std::string data, uint32 width, uint32 height); + int get_image(int ref, std::string * data, uint32 * width, uint32 * height); + int get_profile_image(int eAvatarSize); + int set_profile_image(int eAvatarSize, Image_Data * image); + int set_profile_image(int eAvatarSize, int reference, bool notify); + int get_preferred_network_image_type() { return this->preferred_network_image_type; } + void set_preferred_network_image_type(int new_type); //controller struct Controller_Settings controller_settings; @@ -167,6 +208,16 @@ public: //warn people who use local save bool warn_local_save = false; + + //achievements + bool get_show_achievement_desc_on_unlock() { return showAchievementDescOnUnlock; } + void set_show_achievement_desc_on_unlock(bool set) { this->showAchievementDescOnUnlock = set; } + bool get_show_achievement_hidden_unearned() { return showAchievementHiddenUnearned; } + void set_show_achievement_hidden_unearned(bool set) { this->showAchievementHiddenUnearned = set; } + + //UI + std::string get_ui_notification_position(); + void set_ui_notification_position(char * pos); }; #endif diff --git a/dll/settings_parser.cpp b/dll/settings_parser.cpp index 22dad2e..79ceec4 100644 --- a/dll/settings_parser.cpp +++ b/dll/settings_parser.cpp @@ -110,7 +110,7 @@ static void load_gamecontroller_settings(Settings *settings) } 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()); + PRINT_DEBUG("Added %" PRI_ZU " action names to %s\n", button_pairs.size(), action_set_name.c_str()); } } @@ -211,10 +211,45 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s 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); + // Custom master server + std::set custom_master_server; + load_custom_broadcasts(local_storage->get_global_settings_path() + "custom_master_server.txt", custom_master_server); + load_custom_broadcasts(Local_Storage::get_game_settings_path() + "custom_master_server.txt", custom_master_server); + // Acount name - char name[32] = {}; + char name[32] = { '\0' }; if (local_storage->get_data_settings("account_name.txt", name, sizeof(name) - 1) <= 0) { - strcpy(name, DEFAULT_NAME); + PRINT_DEBUG("%s.\n", "Attempting to set steam user name from system user name"); +#if defined(STEAM_WIN32) + DWORD username_dword = 32; + wchar_t username[32] = { '\0' }; + if (GetUserNameW((wchar_t*)&username, &username_dword) == TRUE) { + std::wstring username_wstr(username); + std::string username_str = utf8_encode(username_wstr); + size_t username_len = username_str.length(); + if (username_len > 0 && + username_len < 31) { + memcpy(&name, username_str.c_str(), username_len); + name[31] = '\0'; + } + } +#else + char * env_username = getenv("USER"); + if (env_username != NULL) { + size_t username_len = strlen(env_username); + if (username_len > 0 && + username_len < 31) { + memcpy(&name, env_username, username_len); + name[31] = '\0'; + } + } +#endif + char empty_name[32] = { '\0' }; + if (memcmp(name, empty_name, 32) == 0) { + PRINT_DEBUG("%s %s.\n", "Setting steam user name to", DEFAULT_NAME); + strcpy(name, DEFAULT_NAME); + } + PRINT_DEBUG("Username: %s.\n", name); local_storage->store_data_settings("account_name.txt", name, strlen(name)); } @@ -225,6 +260,13 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s local_storage->store_data_settings("language.txt", language, strlen(language)); } + // UI Notification Position. + char notification_position[32] = {}; + if (local_storage->get_data_settings("ui_notification_position.txt", notification_position, sizeof(notification_position) - 1) <= 0) { + strcpy(notification_position, DEFAULT_UI_NOTIFICATION_POSITION); + local_storage->store_data_settings("ui_notification_position.txt", notification_position, strlen(notification_position)); + } + // Steam ID char array_steam_id[32] = {}; CSteamID user_id; @@ -260,6 +302,12 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text)); } + if (Local_Storage::is_directory(Local_Storage::get_user_appdata_path().append(PATH_SEPARATOR).append("minidumps")) == false) { + local_storage->store_data_settings(std::string("minidumps").append(PATH_SEPARATOR).append("dummy.txt"), + " ", + sizeof(" ") / sizeof(char)); + } + std::set supported_languages; { @@ -299,10 +347,42 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s bool disable_networking = false; bool disable_overlay = false; bool disable_lobby_creation = false; + bool enable_achievement_desc_on_unlock = false; + bool enable_displaying_hidden_achievements = false; int build_id = 10; bool warn_forced = false; + Image_Data profile_small {0, 0, std::string()}; + Image_Data profile_medium {0, 0, std::string()}; + Image_Data profile_large {0, 0, std::string()}; + + { + std::string steam_settings_path = local_storage->get_global_settings_path(); + + std::vector paths = local_storage->get_filenames_path(steam_settings_path); + for (auto & p: paths) { + PRINT_DEBUG("global settings path %s\n", p.c_str()); + if (p == "enable_achievement_desc_on_unlock.txt") { + enable_achievement_desc_on_unlock = true; + } else if (p == "enable_displaying_hidden_achievements.txt") { + enable_displaying_hidden_achievements = true; + } else if (p == "avatar_small.jpg") { + profile_small.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_small.width, &profile_small.height)); + } else if (p == "avatar_medium.jpg") { + profile_medium.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_medium.width, &profile_medium.height)); + } else if (p == "avatar_large.jpg") { + profile_large.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_large.width, &profile_large.height)); + } else if (p == "avatar_small.png") { + profile_small.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_small.width, &profile_small.height)); + } else if (p == "avatar_medium.png") { + profile_medium.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_medium.width, &profile_medium.height)); + } else if (p == "avatar_large.png") { + profile_large.data = convert_vector_image_pixel_t_to_std_string(local_storage->load_image(steam_settings_path + p, &profile_large.width, &profile_large.height)); + } + } + } + { std::string steam_settings_path = Local_Storage::get_game_settings_path(); @@ -317,12 +397,22 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s disable_overlay = true; } else if (p == "disable_lobby_creation.txt") { disable_lobby_creation = true; + } else if (p == "enable_achievement_desc_on_unlock.txt") { + enable_achievement_desc_on_unlock = true; + } else if (p == "enable_displaying_hidden_achievements.txt") { + enable_displaying_hidden_achievements = 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; warn_forced = true; } + } else if (p == "force_ui_notification_position.txt") { + int len = Local_Storage::get_file_data(steam_settings_path + "force_ui_notification_position.txt", notification_position, sizeof(notification_position) - 1); + if (len > 0) { + notification_position[len] = 0; + warn_forced = true; + } } 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) { @@ -359,6 +449,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s settings_server->set_port(port); settings_client->custom_broadcasts = custom_broadcasts; settings_server->custom_broadcasts = custom_broadcasts; + settings_client->custom_master_server = custom_master_server; + settings_server->custom_master_server = custom_master_server; settings_client->disable_networking = disable_networking; settings_server->disable_networking = disable_networking; settings_client->disable_overlay = disable_overlay; @@ -373,6 +465,30 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s settings_server->warn_local_save = local_save; settings_client->supported_languages = supported_languages; settings_server->supported_languages = supported_languages; + settings_client->set_show_achievement_desc_on_unlock(enable_achievement_desc_on_unlock); + settings_server->set_show_achievement_desc_on_unlock(enable_achievement_desc_on_unlock); + settings_client->set_show_achievement_hidden_unearned(enable_displaying_hidden_achievements); + settings_server->set_show_achievement_hidden_unearned(enable_displaying_hidden_achievements); + settings_client->set_ui_notification_position(notification_position); + settings_server->set_ui_notification_position(notification_position); + if (profile_small.data.length() > 0 && profile_small.width > 0 && profile_small.height > 0) { + settings_client->set_profile_image(k_EAvatarSize32x32, &profile_small); + settings_server->set_profile_image(k_EAvatarSize32x32, &profile_small); + } else { + PRINT_DEBUG("%s %" PRI_ZU " %d %d\n", "Small user avatar image not defined.", profile_small.data.length(), profile_small.width, profile_small.height); + } + if (profile_medium.data.length() > 0 && profile_medium.width > 0 && profile_medium.height > 0) { + settings_client->set_profile_image(k_EAvatarSize64x64, &profile_medium); + settings_server->set_profile_image(k_EAvatarSize64x64, &profile_medium); + } else { + PRINT_DEBUG("%s %" PRI_ZU " %d %d\n", "Medium user avatar image not defined.", profile_medium.data.length(), profile_medium.width, profile_medium.height); + } + if (profile_large.data.length() > 0 && profile_large.width > 0 && profile_large.height > 0) { + settings_client->set_profile_image(k_EAvatarSize184x184, &profile_large); + settings_server->set_profile_image(k_EAvatarSize184x184, &profile_large); + } else { + PRINT_DEBUG("%s %" PRI_ZU " %d %d\n", "Large user avatar image not defined.", profile_large.data.length(), profile_large.width, profile_large.height); + } { std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt"; @@ -621,6 +737,9 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s load_gamecontroller_settings(settings_client); + settings_client->set_settings_parser_done(true); + settings_server->set_settings_parser_done(true); + *settings_client_out = settings_client; *settings_server_out = settings_server; *local_storage_out = local_storage; @@ -629,8 +748,33 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s return appid; } -void save_global_settings(Local_Storage *local_storage, char *name, char *language) +void save_global_settings(Local_Storage *local_storage, Settings * client_settings) { - local_storage->store_data_settings("account_name.txt", name, strlen(name)); - local_storage->store_data_settings("language.txt", language, strlen(language)); + if ((local_storage != nullptr) && (client_settings != nullptr)) { + std::string name = client_settings->get_local_name(); + std::string language = client_settings->get_language(); + std::string ui_notif_pos = client_settings->get_ui_notification_position(); + + local_storage->store_data_settings("account_name.txt", (char*)name.c_str(), name.length()); + local_storage->store_data_settings("language.txt", (char*)language.c_str(), language.length()); + local_storage->store_data_settings("ui_notification_position.txt", (char*)ui_notif_pos.c_str(), ui_notif_pos.length()); + if (client_settings->get_show_achievement_desc_on_unlock()) { + if (local_storage->data_settings_exists("enable_achievement_desc_on_unlock.txt") != true) { + local_storage->store_data_settings("enable_achievement_desc_on_unlock.txt", " ", sizeof(" ")); + } + } else { + if (local_storage->data_settings_exists("enable_achievement_desc_on_unlock.txt") == true) { + local_storage->delete_data_settings("enable_achievement_desc_on_unlock.txt"); + } + } + if (client_settings->get_show_achievement_hidden_unearned()) { + if (local_storage->data_settings_exists("enable_displaying_hidden_achievements.txt") != true) { + local_storage->store_data_settings("enable_displaying_hidden_achievements.txt", " ", sizeof(" ")); + } + } else { + if (local_storage->data_settings_exists("enable_displaying_hidden_achievements.txt") == true) { + local_storage->delete_data_settings("enable_displaying_hidden_achievements.txt"); + } + } + } } diff --git a/dll/settings_parser.h b/dll/settings_parser.h index 38b926c..cfa9f6c 100644 --- a/dll/settings_parser.h +++ b/dll/settings_parser.h @@ -22,6 +22,6 @@ //returns appid uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out); -void save_global_settings(Local_Storage *local_storage, char *name, char *language); +void save_global_settings(Local_Storage *local_storage, Settings *client_settings); #endif diff --git a/dll/steam_HTMLsurface.h b/dll/steam_HTMLsurface.h index 3b10b26..a35df54 100644 --- a/dll/steam_HTMLsurface.h +++ b/dll/steam_HTMLsurface.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_HTMLSURFACE_H +#define INCLUDED_STEAM_HTMLSURFACE_H + class Steam_HTMLsurface : public ISteamHTMLSurface001, public ISteamHTMLSurface002, @@ -337,3 +340,5 @@ void FileLoadDialogResponse( HHTMLBrowser unBrowserHandle, const char **pchSelec } }; + +#endif // INCLUDED_STEAM_HTMLSURFACE_H diff --git a/dll/steam_applist.h b/dll/steam_applist.h index 10f872e..1feb871 100644 --- a/dll/steam_applist.h +++ b/dll/steam_applist.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_APPLIST_H +#define INCLUDED_STEAM_APPLIST_H + class Steam_Applist : public ISteamAppList { public: @@ -28,3 +31,5 @@ public: int GetAppBuildId( AppId_t nAppID ); // return the buildid of this app, may change at any time based on backend updates to the game }; + +#endif // INCLUDED_STEAM_APPLIST_H diff --git a/dll/steam_apps.h b/dll/steam_apps.h index 1a4f206..eab3756 100644 --- a/dll/steam_apps.h +++ b/dll/steam_apps.h @@ -1,5 +1,8 @@ #include "base.h" +#ifndef INCLUDED_STEAM_APPS_H +#define INCLUDED_STEAM_APPS_H + class Steam_Apps : public ISteamApps001, public ISteamApps002, @@ -110,3 +113,5 @@ public: // set current DLC AppID being played (or 0 if none). Allows Steam to track usage of major DLC extensions bool SetDlcContext( AppId_t nAppID ); }; + +#endif // INCLUDED_STEAM_APPS_H diff --git a/dll/steam_client.cpp b/dll/steam_client.cpp index 17af33e..37c2365 100644 --- a/dll/steam_client.cpp +++ b/dll/steam_client.cpp @@ -50,16 +50,16 @@ static void background_thread(Steam_Client *client) Steam_Client::Steam_Client() { - uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage); - - 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(); callbacks_client = new SteamCallBacks(callback_results_client); callbacks_server = new SteamCallBacks(callback_results_server); run_every_runcb = new RunEveryRunCB(); + uint32 appid = create_localstorage_settings(&settings_client, &settings_server, &local_storage); + + network = new Networking(settings_server->get_local_steam_id(), appid, settings_server->get_port(), &(settings_server->custom_broadcasts), settings_server->disable_networking); + 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()); if (appid) { @@ -81,6 +81,7 @@ Steam_Client::Steam_Client() 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_unified_messages = new Steam_Unified_Messages(settings_client, network, callback_results_client, callbacks_client, run_every_runcb); 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(); @@ -95,7 +96,6 @@ Steam_Client::Steam_Client() 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); @@ -1433,9 +1433,22 @@ void Steam_Client::RegisterCallback( class CCallbackBase *pCallback, int iCallba PRINT_DEBUG("Unknown callback base %i\n", base_callback); }; + std::lock_guard lock(global_mutex); if (isGameServer) { + if (callback_results_server == nullptr) { + callback_results_server = new SteamCallResults(); + } + if (callbacks_server == nullptr) { + callbacks_server = new SteamCallBacks(callback_results_server); + } callbacks_server->addCallBack(iCallback, pCallback); } else { + if (callback_results_client == nullptr) { + callback_results_client = new SteamCallResults(); + } + if (callbacks_client == nullptr) { + callbacks_client = new SteamCallBacks(callback_results_client); + } callbacks_client->addCallBack(iCallback, pCallback); } } @@ -1657,10 +1670,15 @@ void Steam_Client::UnregisterCallback( class CCallbackBase *pCallback) PRINT_DEBUG("Unknown callback base %i\n", base_callback); }; + std::lock_guard lock(global_mutex); if (isGameServer) { - callbacks_server->rmCallBack(iCallback, pCallback); + if (callbacks_server != nullptr) { + callbacks_server->rmCallBack(iCallback, pCallback); + } } else { - callbacks_client->rmCallBack(iCallback, pCallback); + if (callbacks_client != nullptr) { + callbacks_client->rmCallBack(iCallback, pCallback); + } } } @@ -1670,7 +1688,6 @@ void Steam_Client::RegisterCallResult( class CCallbackBase *pCallback, SteamAPIC std::lock_guard lock(global_mutex); callback_results_client->addCallBack(hAPICall, pCallback); callback_results_server->addCallBack(hAPICall, pCallback); - } void Steam_Client::UnregisterCallResult( class CCallbackBase *pCallback, SteamAPICall_t hAPICall) diff --git a/dll/steam_client.h b/dll/steam_client.h index d00cb6a..9de5f95 100644 --- a/dll/steam_client.h +++ b/dll/steam_client.h @@ -53,6 +53,9 @@ #include "../overlay_experimental/steam_overlay.h" +#ifndef INCLUDED_STEAM_CLIENT_H +#define INCLUDED_STEAM_CLIENT_H + enum Steam_Pipe { NO_USER, CLIENT, @@ -75,6 +78,7 @@ public ISteamClient018, public ISteamClient019, public ISteamClient { +// Some games (SAO:FB) use pointer math to access the class so member order is important. public: Networking *network; SteamCallResults *callback_results_server, *callback_results_client; @@ -94,6 +98,7 @@ public: Steam_Remote_Storage *steam_remote_storage; Steam_Screenshots *steam_screenshots; Steam_HTTP *steam_http; + Steam_Unified_Messages *steam_unified_messages; Steam_Controller *steam_controller; Steam_UGC *steam_ugc; Steam_Applist *steam_applist; @@ -108,7 +113,6 @@ public: 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; @@ -302,3 +306,5 @@ public: void DestroyAllInterfaces(); }; + +#endif // INCLUDED_STEAM_CLIENT_H diff --git a/dll/steam_controller.h b/dll/steam_controller.h index db04d60..b192b1c 100644 --- a/dll/steam_controller.h +++ b/dll/steam_controller.h @@ -16,6 +16,10 @@ . */ #include "base.h" + +#ifndef INCLUDED_STEAM_CONTROLLER_H +#define INCLUDED_STEAM_CONTROLLER_H + #ifndef CONTROLLER_SUPPORT inline void GamepadInit(void) {} inline void GamepadShutdown(void) {} @@ -1224,3 +1228,5 @@ void RunCallbacks() } }; + +#endif // INCLUDED_STEAM_CONTROLLER_H diff --git a/dll/steam_friends.cpp b/dll/steam_friends.cpp new file mode 100644 index 0000000..689ba53 --- /dev/null +++ b/dll/steam_friends.cpp @@ -0,0 +1,425 @@ +/* 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 + . */ + +#include "steam_friends.h" +#include "dll.h" + +void Steam_Friends::Callback(Common_Message *msg) +{ + if (msg->has_low_level()) { + if (msg->low_level().type() == Low_Level::DISCONNECT) { + PRINT_DEBUG("Steam_Friends::Callback Disconnect\n"); + uint64 id = msg->source_id(); + 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); + if ((uint64)f->id() != settings->get_local_steam_id().ConvertToUint64()) { + auto nums = avatars.find((uint64)f->id()); + if (nums != avatars.end()) { + if (nums->second.smallest != 0) { + settings->remove_image(nums->second.smallest); + } + if (nums->second.medium != 0) { + settings->remove_image(nums->second.medium); + } + if (nums->second.large != 0) { + settings->remove_image(nums->second.large); + } + avatars.erase(nums); + } + friends.erase(f); + } + } + } + + if (msg->low_level().type() == Low_Level::CONNECT) { + PRINT_DEBUG("Steam_Friends::Callback Connect\n"); + Common_Message msg_; + msg_.set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg_.set_dest_id(msg->source_id()); + Friend *f = new Friend(us); + f->set_id(settings->get_local_steam_id().ConvertToUint64()); + f->set_name(settings->get_local_name()); + f->set_appid(settings->get_local_game_id().AppID()); + f->set_lobby_id(settings->get_lobby().ConvertToUint64()); + msg_.set_allocated_friend_(f); + network->sendTo(&msg_, true); + } + } + + if (msg->has_friend_()) { + PRINT_DEBUG("Steam_Friends::Callback Friend %llu %llu\n", msg->friend_().id(), msg->friend_().lobby_id()); + Friend *f = find_friend((uint64)msg->friend_().id()); + 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 { + std::map map1(f->rich_presence().begin(), f->rich_presence().end()); + std::map map2(msg->friend_().rich_presence().begin(), msg->friend_().rich_presence().end()); + + if (map1 != map2) { + //The App ID of the game. This should always be the current game. + if (isAppIdCompatible(f)) { + rich_presence_updated((uint64)msg->friend_().id(), (uint64)msg->friend_().appid()); + } + } + //TODO: callbacks? + *f = msg->friend_(); + } + } + + if (msg->has_friend_messages()) { + if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) { + PRINT_DEBUG("Steam_Friends::Callback Got Lobby Invite\n"); + 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(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::Callback 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 + if (overlay->Ready()) + { + // Then we will handle it ! + overlay->SetRichInvite(*find_friend(static_cast(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)); + } + } + } + + if (msg->has_friend_avatar()) { + CSteamID userID((uint64)msg->source_id()); + Friend *f = find_friend(userID.ConvertToUint64()); + if (f) { + if (msg->friend_avatar().img().type() == Image::NOTIFY) { + PRINT_DEBUG("%s %" PRIu64 ".\n", "Steam_Friends::Callback Got Friend_Avatar NOTIFY for", userID.ConvertToUint64()); + + std::string raw_image = msg->friend_avatar().img().img_data(); + if (raw_image.length() > 0 && raw_image.length() < FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + uint32_t width = (uint32_t)msg->friend_avatar().img().img_width(); + uint32_t height = (uint32_t)msg->friend_avatar().img().img_height(); + uint32_t img_type = (uint32_t)msg->friend_avatar().img().img_type(); + int eAvatarSize = k_EAvatarSizeMAX; + + auto nums = avatars.find(userID.ConvertToUint64()); + if (nums == avatars.end()) { + struct Avatar_Numbers n; + generate_avatar_numbers(n); + n.last_update_time = std::chrono::steady_clock::now(); + avatars[userID.ConvertToUint64()] = n; + nums = avatars.find(userID.ConvertToUint64()); + } + + int num_replace = 0; + if (nums != avatars.end()) { + if (width > 0 && width <= 32 && height > 0 && height <= 32) { + eAvatarSize = k_EAvatarSize32x32; + num_replace = nums->second.smallest; + } else { + if (width > 32 && width <= 64 && height > 32 && height <= 64) { + eAvatarSize = k_EAvatarSize64x64; + num_replace = nums->second.medium; + } else { + if (width > 64 && width <= 184 && height > 64 && height <= 184) { + eAvatarSize = k_EAvatarSize184x184; + num_replace = nums->second.large; + } + } + } + + if (eAvatarSize != k_EAvatarSizeMAX) { + + switch (img_type) { + case Image::RAW: + PRINT_DEBUG("%s %" PRIu64 " %s %d %s.\n", + "Steam_Friends::Callback Got Friend_Avatar NOTIFY for", + userID.ConvertToUint64(), + "size", + eAvatarSize, + "image type RAW"); + break; + case Image::JPG: + { + std::string convert; + int n_width = 0; + int n_height = 0; + PRINT_DEBUG("%s %" PRIu64 " %s %d %s.\n", + "Steam_Friends::Callback Got Friend_Avatar NOTIFY for", + userID.ConvertToUint64(), + "size", + eAvatarSize, + "image type JPG"); + convert = convert_jpg_buffer_std_string_to_std_string_uint8(raw_image, &n_width, &n_height, NULL, 4); + if (convert.length() > 0 && n_width == width && n_height == height) { + raw_image = convert; + convert.clear(); + } else { + raw_image.clear(); + } + } + break; + case Image::PNG: + { + std::string convert; + int n_width = 0; + int n_height = 0; + PRINT_DEBUG("%s %" PRIu64 " %s %d %s.\n", + "Steam_Friends::Callback Got Friend_Avatar NOTIFY for", + userID.ConvertToUint64(), + "size", + eAvatarSize, + "image type PNG"); + convert = convert_png_buffer_std_string_to_std_string_uint8(raw_image, &n_width, &n_height, NULL, 4); + if (convert.length() > 0 && n_width == width && n_height == height) { + raw_image = convert; + convert.clear(); + } else { + raw_image.clear(); + } + } + break; + default: + raw_image.clear(); + PRINT_DEBUG("%s %" PRIu64 " %s %d %s %d.\n", + "Steam_Friends::Callback Got Friend_Avatar NOTIFY for", + userID.ConvertToUint64(), + "size", + eAvatarSize, + "with unsupported image type", + img_type); + break; + }; + if (raw_image.length() > 0) { + int ref = settings->replace_image(num_replace, raw_image, width, height); + if (ref != 0) { + nums->second.last_update_time = std::chrono::steady_clock::now(); + + AvatarImageLoaded_t ail_data = {}; + ail_data.m_steamID = userID.ConvertToUint64(); + ail_data.m_iImage = ref; + ail_data.m_iWide = width; + ail_data.m_iTall = height; + callbacks->addCBResult(ail_data.k_iCallback, &ail_data, sizeof(ail_data)); + persona_change(userID, k_EPersonaChangeAvatar); + } + } + } + } + } + } + + if (msg->friend_avatar().img().type() == Image::REQUEST) { + CSteamID requestID((uint64)msg->dest_id()); + if (settings->get_local_steam_id() == requestID) { + PRINT_DEBUG("%s %" PRIu64 ".\n", "Steam_Friends::Callback Got Friend_Avatar REQUEST from", userID.ConvertToUint64()); + + uint32_t width = (uint32_t)msg->friend_avatar().img().img_width(); + uint32_t height = (uint32_t)msg->friend_avatar().img().img_height(); + uint32_t img_type = (uint32_t)msg->friend_avatar().img().img_type(); + + int eAvatarSize = k_EAvatarSizeMAX; + if (width > 0 && width <= 32 && height > 0 && height <= 32) { + eAvatarSize = k_EAvatarSize32x32; + } else { + if (width > 32 && width <= 64 && height > 32 && height <= 64) { + eAvatarSize = k_EAvatarSize64x64; + } else { + if (width > 64 && width <= 184 && height > 64 && height <= 184) { + eAvatarSize = k_EAvatarSize184x184; + } + } + } + + int ref = GetFriendAvatar(settings->get_local_steam_id(), eAvatarSize); + if (ref != 0) { + + uint32 n_width = 0; + uint32 n_height = 0; + Steam_Utils* steamUtils = get_steam_client()->steam_utils; + if ((steamUtils->GetImageSize(ref, &n_width, &n_height) == true) && + (n_width > 0) && (n_height > 0)) { + uint8 * raw_image = new uint8[(n_width * n_height * sizeof(uint32))]; + if (raw_image != NULL) { + if (steamUtils->GetImageRGBA(ref, + raw_image, + (n_width * n_height * sizeof(uint32))) == true) { + uint32_t img_type = (uint32_t)msg->friend_avatar().img().img_type(); + PRINT_DEBUG("%s %" PRIu64 " %s %d %s %d.\n", + "Steam_Friends::Callback Got Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for image type", + img_type, + "size", + eAvatarSize); + + std::string pixdata = ""; + if (img_type == Image::PNG) { + pixdata = convert_raw_uint8_to_png_std_string(raw_image, n_width, n_height, 4); + if (pixdata.length() <= 0 || pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + if (pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s %" PRI_ZU " %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for PNG image size", + eAvatarSize, + "Image is over maximum size by ", + pixdata.length() - FRIEND_AVATAR_MAX_IMAGE_LENGTH, + "bytes"); + } else { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for PNG image size", + eAvatarSize, + "Could not convert image to requested type"); + } + + // Try again using JPG. + img_type = Image::JPG; + pixdata.clear(); + } + } + + if (img_type == Image::JPG) { + pixdata = convert_raw_uint8_to_jpg_std_string(raw_image, n_width, n_height, 4); + if (pixdata.length() <= 0 || pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + // Try again using RAW. + if (pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s %" PRI_ZU " %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for JPG image size", + eAvatarSize, + "Image is over maximum size by ", + pixdata.length() - FRIEND_AVATAR_MAX_IMAGE_LENGTH, + "bytes"); + } else { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for JPG image size", + eAvatarSize, + "Could not convert image to requested type"); + } + img_type = Image::RAW; + pixdata.clear(); + } + } + + if (img_type == Image::RAW) { + for (size_t x = 0; x < (n_width * n_height * sizeof(uint32)); x++) { + char a = (char)(raw_image[x]); + pixdata += a; + } + if (pixdata.length() <= 0 || pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + // No more attempts. + if (pixdata.length() >= FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s %" PRI_ZU " %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for RAW image size", + eAvatarSize, + "Image is over maximum size by ", + pixdata.length() - FRIEND_AVATAR_MAX_IMAGE_LENGTH, + "bytes"); + } else { + PRINT_DEBUG("%s %" PRIu64 " %s %d. %s.\n", + "Steam_Friends::Callback Cannot complete Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for RAW image size", + eAvatarSize, + "Could not convert image to requested type"); + } + pixdata.clear(); + } + } + + if (img_type != Image::PNG && img_type != Image::JPG && img_type != Image::RAW) { + pixdata.clear(); + PRINT_DEBUG("%s %" PRIu64 " %s %d %s %d.\n", + "Steam_Friends::Callback Got Friend_Avatar REQUEST from", + userID.ConvertToUint64(), + "for unsupported image type", + img_type, + "size", + eAvatarSize); + } + + if (pixdata.length() > 0 && pixdata.length() < FRIEND_AVATAR_MAX_IMAGE_LENGTH) { + PRINT_DEBUG("Steam_Friends::Callback Sending Friend_Avatar NOTIFY size %d type %d\n", + eAvatarSize, + img_type); + Common_Message msg_; + msg_.set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg_.set_dest_id(msg->source_id()); + + Image *img = new Image(); + img->set_type(Image::NOTIFY); + img->set_img_type(static_cast(img_type)); + img->set_img_width(n_width); + img->set_img_height(n_height); + img->set_img_data(pixdata); + + Friend_Avatar *friend_avatar = new Friend_Avatar(); + friend_avatar->set_allocated_img(img); + + msg_.set_allocated_friend_avatar(friend_avatar); + network->sendTo(&msg_, true); + } + } + + delete [] raw_image; + raw_image = NULL; + } + } + } + } + } + } + } +} diff --git a/dll/steam_friends.h b/dll/steam_friends.h index 5f28c95..c37a285 100644 --- a/dll/steam_friends.h +++ b/dll/steam_friends.h @@ -27,6 +27,7 @@ struct Avatar_Numbers { int smallest; int medium; int large; + std::chrono::steady_clock::time_point last_update_time; }; class Steam_Friends : @@ -94,25 +95,181 @@ bool isAppIdCompatible(Friend *f) return settings->get_local_game_id().AppID() == f->appid(); } +void generate_avatar_numbers(struct Avatar_Numbers & nums) { + static struct Avatar_Numbers blanks; + + if (blanks.smallest == 0) { + std::string small_avatar(32 * 32 * 4, 0); + blanks.smallest = settings->add_image(small_avatar, 32, 32); + } + if (blanks.medium == 0) { + std::string medium_avatar(64 * 64 * 4, 0); + blanks.medium = settings->add_image(medium_avatar, 64, 64); + } + if (blanks.large == 0) { + std::string large_avatar(184 * 184 * 4, 0); + blanks.large = settings->add_image(large_avatar, 184, 184); + } + + nums.smallest = blanks.smallest; + nums.medium = blanks.medium; + nums.large = blanks.large; + return; +} + +STEAM_CALL_RESULT( AvatarImageLoaded_t ) 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; + bool generate = true; + struct Avatar_Numbers avatar_numbers; + + if (settings->is_settings_parser_done() == false) { + do { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } while (settings->is_settings_parser_done() == false); } - //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); + if (settings->get_local_steam_id().ConvertToUint64() == steam_id) { + avatar_numbers.smallest = settings->get_profile_image(k_EAvatarSize32x32); + avatar_numbers.medium = settings->get_profile_image(k_EAvatarSize64x64); + avatar_numbers.large = settings->get_profile_image(k_EAvatarSize184x184); - 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); + if (avatar_numbers.smallest != 0 && + avatar_numbers.medium != 0 && + avatar_numbers.large != 0) { + generate = false; + } + + if (avatar_ids != avatars.end()) { + // Check for updated entry. + if (avatar_numbers.smallest == avatar_ids->second.smallest && + avatar_numbers.medium == avatar_ids->second.medium && + avatar_numbers.large == avatar_ids->second.large) { + return avatar_ids->second; + } + } + } else { + if (avatar_ids != avatars.end()) { + return avatar_ids->second; + } else { + // Request avatar data. + PRINT_DEBUG("Steam_Friends::add_friend_avatars sending Friend_Avatar small request for %" PRIu64 ".\n", steam_id); + Common_Message * msg_ = new Common_Message(); + msg_->set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg_->set_dest_id(steam_id); + + Image *img = new Image(); + img->set_type(Image::REQUEST); + img->set_img_type(static_cast(settings->get_preferred_network_image_type())); + img->set_img_width(32); + img->set_img_height(32); + img->set_img_data(""); + + Friend_Avatar *friend_avatar = new Friend_Avatar(); + friend_avatar->set_allocated_img(img); + + msg_->set_allocated_friend_avatar(friend_avatar); + network->sendTo(msg_, true); + + PRINT_DEBUG("Steam_Friends::add_friend_avatars sending Friend_Avatar medium request for %" PRIu64 ".\n", steam_id); + msg_ = new Common_Message(); + msg_->set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg_->set_dest_id(steam_id); + + img = new Image(); + img->set_type(Image::REQUEST); + img->set_img_type(static_cast(settings->get_preferred_network_image_type())); + img->set_img_width(64); + img->set_img_height(64); + img->set_img_data(""); + + friend_avatar = new Friend_Avatar(); + friend_avatar->set_allocated_img(img); + + msg_->set_allocated_friend_avatar(friend_avatar); + network->sendTo(msg_, true); + + PRINT_DEBUG("Steam_Friends::add_friend_avatars sending Friend_Avatar large request for %" PRIu64 ".\n", steam_id); + msg_ = new Common_Message(); + msg_->set_source_id(settings->get_local_steam_id().ConvertToUint64()); + msg_->set_dest_id(steam_id); + + img = new Image(); + img->set_type(Image::REQUEST); + img->set_img_type(static_cast(settings->get_preferred_network_image_type())); + img->set_img_width(184); + img->set_img_height(184); + img->set_img_data(""); + + friend_avatar = new Friend_Avatar(); + friend_avatar->set_allocated_img(img); + + msg_->set_allocated_friend_avatar(friend_avatar); + network->sendTo(msg_, true); + } + } + + PRINT_DEBUG("%s %s %s %" PRIu64 ".\n", + "Steam_Friends::add_friend_avatars", + (generate == true) ? "Generating empty" : "Notifying changed", + "avatar image for", + steam_id); + if (generate == true) { + generate_avatar_numbers(avatar_numbers); + if (settings->get_local_steam_id().ConvertToUint64() == steam_id) { + settings->set_profile_image(k_EAvatarSize32x32, avatar_numbers.smallest, false); + settings->set_profile_image(k_EAvatarSize64x64, avatar_numbers.medium, false); + settings->set_profile_image(k_EAvatarSize184x184, avatar_numbers.large, false); + } + } + + avatar_numbers.last_update_time = std::chrono::steady_clock::now(); avatars[steam_id] = avatar_numbers; + + // Generate callbacks. + uint32 width = 0; + uint32 height = 0; + AvatarImageLoaded_t ail_data = {}; + bool sent_ail = false; + + auto image = settings->get_image(avatar_numbers.smallest, NULL, &width, &height); + + if (image == avatar_numbers.smallest) { + ail_data.m_steamID = steam_id; + ail_data.m_iImage = avatar_numbers.smallest; + ail_data.m_iWide = width; + ail_data.m_iTall = height; + callbacks->addCBResult(ail_data.k_iCallback, &ail_data, sizeof(ail_data)); + sent_ail = true; + } + + image = settings->get_image(avatar_numbers.medium, NULL, &width, &height); + + if (image == avatar_numbers.medium) { + ail_data.m_steamID = steam_id; + ail_data.m_iImage = avatar_numbers.medium; + ail_data.m_iWide = width; + ail_data.m_iTall = height; + callbacks->addCBResult(ail_data.k_iCallback, &ail_data, sizeof(ail_data)); + sent_ail = true; + } + + image = settings->get_image(avatar_numbers.large, NULL, &width, &height); + + if (image == avatar_numbers.large) { + ail_data.m_steamID = steam_id; + ail_data.m_iImage = avatar_numbers.large; + ail_data.m_iWide = width; + ail_data.m_iTall = height; + callbacks->addCBResult(ail_data.k_iCallback, &ail_data, sizeof(ail_data)); + sent_ail = true; + } + if (sent_ail) { + persona_change(id, k_EPersonaChangeAvatar); + } return avatar_numbers; } @@ -147,6 +304,7 @@ Steam_Friends(Settings* settings, Networking* network, SteamCallResults* callbac overlay(overlay) { this->network->setCallback(CALLBACK_ID_FRIEND, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this); + this->network->setCallback(CALLBACK_ID_FRIEND_AVATAR, 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); this->run_every_runcb->add(&Steam_Friends::steam_friends_run_every_runcb, this); @@ -157,6 +315,20 @@ Steam_Friends(Settings* settings, Networking* network, SteamCallResults* callbac { //TODO rm network callbacks this->run_every_runcb->remove(&Steam_Friends::steam_friends_run_every_runcb, this); + + for (auto x : avatars) { + if (x.first != settings->get_local_steam_id().ConvertToUint64()) { + if (x.second.smallest != 0) { + settings->remove_image(x.second.smallest); + } + if (x.second.medium != 0) { + settings->remove_image(x.second.medium); + } + if (x.second.large != 0) { + settings->remove_image(x.second.large); + } + } + } } static bool ok_friend_flags(int iFriendFlags) @@ -594,10 +766,10 @@ void ActivateGameOverlayInviteDialog( CSteamID 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"); //IMPORTANT NOTE: don't change friend avatar numbers for the same friend or else some games endlessly allocate stuff. std::lock_guard lock(global_mutex); struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + PRINT_DEBUG("Steam_Friends::GetSmallFriendAvatar %" PRIu64 " -> %d.\n", steamIDFriend.ConvertToUint64(), numbers.smallest); return numbers.smallest; } @@ -605,9 +777,9 @@ int GetSmallFriendAvatar( CSteamID steamIDFriend ) // gets the medium (64x64) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set int GetMediumFriendAvatar( CSteamID steamIDFriend ) { - PRINT_DEBUG("Steam_Friends::GetMediumFriendAvatar\n"); std::lock_guard lock(global_mutex); struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + PRINT_DEBUG("Steam_Friends::GetMediumFriendAvatar %" PRIu64 " -> %d.\n", steamIDFriend.ConvertToUint64(), numbers.medium); return numbers.medium; } @@ -616,9 +788,9 @@ int GetMediumFriendAvatar( CSteamID steamIDFriend ) // returns -1 if this image has yet to be loaded, in this case wait for a AvatarImageLoaded_t callback and then call this again int GetLargeFriendAvatar( CSteamID steamIDFriend ) { - PRINT_DEBUG("Steam_Friends::GetLargeFriendAvatar\n"); std::lock_guard lock(global_mutex); struct Avatar_Numbers numbers = add_friend_avatars(steamIDFriend); + PRINT_DEBUG("Steam_Friends::GetLargeFriendAvatar %" PRIu64 " -> %d.\n", steamIDFriend.ConvertToUint64(), numbers.large); return numbers.large; } @@ -1077,105 +1249,7 @@ void RunCallbacks() } } -void Callback(Common_Message *msg) -{ - if (msg->has_low_level()) { - if (msg->low_level().type() == Low_Level::DISCONNECT) { - PRINT_DEBUG("Steam_Friends Disconnect\n"); - uint64 id = msg->source_id(); - 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); - } - } - - if (msg->low_level().type() == Low_Level::CONNECT) { - PRINT_DEBUG("Steam_Friends Connect\n"); - Common_Message msg_; - msg_.set_source_id(settings->get_local_steam_id().ConvertToUint64()); - msg_.set_dest_id(msg->source_id()); - Friend *f = new Friend(us); - f->set_id(settings->get_local_steam_id().ConvertToUint64()); - f->set_name(settings->get_local_name()); - f->set_appid(settings->get_local_game_id().AppID()); - f->set_lobby_id(settings->get_lobby().ConvertToUint64()); - msg_.set_allocated_friend_(f); - network->sendTo(&msg_, true); - } - } - - if (msg->has_friend_()) { - PRINT_DEBUG("Steam_Friends Friend %llu %llu\n", msg->friend_().id(), msg->friend_().lobby_id()); - Friend *f = find_friend((uint64)msg->friend_().id()); - 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 { - std::map map1(f->rich_presence().begin(), f->rich_presence().end()); - std::map map2(msg->friend_().rich_presence().begin(), msg->friend_().rich_presence().end()); - - if (map1 != map2) { - //The App ID of the game. This should always be the current game. - if (isAppIdCompatible(f)) { - rich_presence_updated((uint64)msg->friend_().id(), (uint64)msg->friend_().appid()); - } - } - //TODO: callbacks? - *f = msg->friend_(); - } - } - - if (msg->has_friend_messages()) { - if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) { - PRINT_DEBUG("Steam_Friends Got Lobby Invite\n"); - 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(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 - if (overlay->Ready()) - { - // Then we will handle it ! - overlay->SetRichInvite(*find_friend(static_cast(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)); - } - } - } -} +void Callback(Common_Message *msg); }; diff --git a/dll/steam_game_coordinator.h b/dll/steam_game_coordinator.h index 1ba2b88..79f0edb 100644 --- a/dll/steam_game_coordinator.h +++ b/dll/steam_game_coordinator.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_GAME_COORDINATOR_H +#define INCLUDED_STEAM_GAME_COORDINATOR_H + class Steam_Game_Coordinator : public ISteamGameCoordinator { @@ -152,3 +155,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_GAME_COORDINATOR_H diff --git a/dll/steam_gamesearch.h b/dll/steam_gamesearch.h index 2254f01..dd06b6c 100644 --- a/dll/steam_gamesearch.h +++ b/dll/steam_gamesearch.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_GAMESEARCH_H +#define INCLUDED_STEAM_GAMESEARCH_H + class Steam_Game_Search : public ISteamGameSearch { @@ -213,3 +216,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_GAMESEARCH_H diff --git a/dll/steam_gameserver.h b/dll/steam_gameserver.h index 6cd9b70..8f385f2 100644 --- a/dll/steam_gameserver.h +++ b/dll/steam_gameserver.h @@ -16,7 +16,10 @@ . */ #include "base.h" - + +#ifndef INCLUDED_STEAM_GAMESERVER_H +#define INCLUDED_STEAM_GAMESERVER_H + //----------------------------------------------------------------------------- // Purpose: Functions for authenticating users via Steam to play on a game server //----------------------------------------------------------------------------- @@ -353,3 +356,5 @@ public: // void RunCallbacks(); }; + +#endif // INCLUDED_STEAM_GAMESERVER_H diff --git a/dll/steam_gameserverstats.h b/dll/steam_gameserverstats.h index 8efde54..b11e94a 100644 --- a/dll/steam_gameserverstats.h +++ b/dll/steam_gameserverstats.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_GAMESERVERSTATS_H +#define INCLUDED_STEAM_GAMESERVERSTATS_H + //----------------------------------------------------------------------------- // Purpose: Functions for authenticating users via Steam to play on a game server //----------------------------------------------------------------------------- @@ -61,3 +64,5 @@ public: STEAM_CALL_RESULT( GSStatsStored_t ) SteamAPICall_t StoreUserStats( CSteamID steamIDUser ); }; + +#endif // INCLUDED_STEAM_GAMESERVERSTATS_H diff --git a/dll/steam_http.h b/dll/steam_http.h index 7e33f2c..c30d399 100644 --- a/dll/steam_http.h +++ b/dll/steam_http.h @@ -17,6 +17,8 @@ #include "base.h" +#ifndef INCLUDED_STEAM_HTTP_H +#define INCLUDED_STEAM_HTTP_H struct Steam_Http_Request { HTTPRequestHandle handle; @@ -152,3 +154,5 @@ public: // Check if the reason the request failed was because we timed it out (rather than some harder failure) bool GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut ); }; + +#endif // INCLUDED_STEAM_HTTP_H diff --git a/dll/steam_inventory.h b/dll/steam_inventory.h index 4d30f20..699649c 100644 --- a/dll/steam_inventory.h +++ b/dll/steam_inventory.h @@ -17,6 +17,9 @@ #include "base.h" // For SteamItemDef_t +#ifndef INCLUDED_STEAM_INVENTORY_H +#define INCLUDED_STEAM_INVENTORY_H + struct Steam_Inventory_Requests { double timeout = 0.1; bool done = false; @@ -964,3 +967,5 @@ void RunCallbacks() } }; + +#endif // INCLUDED_STEAM_INVENTORY_H diff --git a/dll/steam_masterserver_updater.h b/dll/steam_masterserver_updater.h index d55e63e..c08a6b6 100644 --- a/dll/steam_masterserver_updater.h +++ b/dll/steam_masterserver_updater.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_MASTERSERVER_UPDATER_H +#define INCLUDED_STEAM_MASTERSERVER_UPDATER_H + class Steam_Masterserver_Updater : public ISteamMasterServerUpdater { @@ -173,12 +176,64 @@ void ForceHeartbeat() bool AddMasterServer( const char *pServerAddress ) { PRINT_DEBUG("Steam_Masterserver_Updater::AddMasterServer\n"); + + IP_PORT addr; + + if (pServerAddress) + { + addr.ip = (uint32)*pServerAddress; + if (pServerAddress[(sizeof(uint32))] != 0) + { + addr.port = (uint16)*(pServerAddress + sizeof(uint32)); + } + else + { + addr.port = 27016; + } + PRINT_DEBUG("Steam_Masterserver_Updater::AddMasterServer pServerAddress IP: %d, PORT: %d", addr.ip, addr.port); + this->settings->custom_master_server.insert(addr); + } + return true; } bool RemoveMasterServer( const char *pServerAddress ) { PRINT_DEBUG("Steam_Masterserver_Updater::RemoveMasterServer\n"); + + std::set::iterator iter; + IP_PORT addr; + IP_PORT list; + + if (pServerAddress) + { + addr.ip = (uint32)*pServerAddress; + if (pServerAddress[(sizeof(uint32))] != 0) + { + addr.port = (uint16)*(pServerAddress + sizeof(uint32)); + } + else + { + addr.port = 27016; + } + PRINT_DEBUG("Steam_Masterserver_Updater::RemoveMasterServer pServerAddress IP: %d, PORT: %d", addr.ip, addr.port); + + iter = this->settings->custom_master_server.begin(); + while (iter != this->settings->custom_master_server.end()) + { + list = (*iter); + if (addr.ip == list.ip && + (addr.port == list.port || (list.port == 0 && addr.port == 27016))) + { + iter = this->settings->custom_master_server.erase(iter); + } + else + { + iter++; + } + } + } + return true; } @@ -186,7 +241,7 @@ bool RemoveMasterServer( const char *pServerAddress ) int GetNumMasterServers() { PRINT_DEBUG("Steam_Masterserver_Updater::GetNumMasterServers\n"); - return 0; + return this->settings->custom_master_server.size(); } @@ -194,7 +249,38 @@ int GetNumMasterServers() int GetMasterServerAddress( int iServer, char *pOut, int outBufferSize ) { PRINT_DEBUG("Steam_Masterserver_Updater::GetMasterServerAddress\n"); - return 0; + + size_t written_bytes = 0; + char * byte_cpy = NULL; + std::set::iterator iter; + IP_PORT addr; + + if (pOut && outBufferSize >= sizeof(uint32) && this->settings->custom_master_server.size() > 0) + { + iter = this->settings->custom_master_server.begin(); + while (written_bytes < outBufferSize && iter != this->settings->custom_master_server.end()) + { + addr = (*iter); + byte_cpy = (char*)&(addr.ip); + for (size_t x = 0; x < sizeof(addr.ip) && written_bytes < outBufferSize; x++) + { + memcpy(pOut + x, byte_cpy + x, 1); + written_bytes++; + } + if (addr.port != 0 && addr.port != 27016) // Default updater port. + { + byte_cpy = (char*)&(addr.port); + for (size_t x = 0; x < sizeof(addr.port) && written_bytes < outBufferSize; x++) + { + memcpy(pOut + x, byte_cpy + x, 1); + written_bytes++; + } + } + iter++; + } + } + + return written_bytes; } @@ -217,3 +303,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_MASTERSERVER_UPDATER_H diff --git a/dll/steam_matchmaking.h b/dll/steam_matchmaking.h index 94bd026..d3e1103 100644 --- a/dll/steam_matchmaking.h +++ b/dll/steam_matchmaking.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_MATCHMAKING_H +#define INCLUDED_STEAM_MATCHMAKING_H + #define SEND_LOBBY_RATE 5.0 #define PENDING_JOIN_TIMEOUT 10.0 @@ -1547,3 +1550,5 @@ void Callback(Common_Message *msg) }; + +#endif // INCLUDED_STEAM_MATCHMAKING_H diff --git a/dll/steam_matchmaking_servers.cpp b/dll/steam_matchmaking_servers.cpp index 23441f3..c436ee6 100644 --- a/dll/steam_matchmaking_servers.cpp +++ b/dll/steam_matchmaking_servers.cpp @@ -33,7 +33,7 @@ Steam_Matchmaking_Servers::Steam_Matchmaking_Servers(class Settings *settings, c this->network->setCallback(CALLBACK_ID_GAMESERVER, (uint64) 0, &network_callback, this); } -static int server_list_request; +static size_t server_list_request; // Request a new list of servers of a particular type. These calls each correspond to one of the EMatchMakingType values. // Each call allocates a new asynchronous request object. @@ -297,7 +297,7 @@ gameserveritem_t *Steam_Matchmaking_Servers::GetServerDetails( HServerListReques PRINT_DEBUG("equal? %p %p\n", hRequest, g->id); if (g->id == hRequest) { gameservers_filtered = g->gameservers_filtered; - PRINT_DEBUG("found %u\n", gameservers_filtered.size()); + PRINT_DEBUG("found %" PRI_ZU "\n", gameservers_filtered.size()); break; } diff --git a/dll/steam_matchmaking_servers.h b/dll/steam_matchmaking_servers.h index 21a643f..0650467 100644 --- a/dll/steam_matchmaking_servers.h +++ b/dll/steam_matchmaking_servers.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_MATCHMAKING_SERVERS_H +#define INCLUDED_STEAM_MATCHMAKING_SERVERS_H + #define SERVER_TIMEOUT 10.0 #define DIRECT_IP_DELAY 0.05 @@ -223,3 +226,5 @@ public: void Callback(Common_Message *msg); void server_details(Gameserver *g, gameserveritem_t *server); }; + +#endif // INCLUDED_STEAM_MATCHMAKING_SERVERS_H diff --git a/dll/steam_music.h b/dll/steam_music.h index 37ab94c..db2a6a8 100644 --- a/dll/steam_music.h +++ b/dll/steam_music.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_MUSIC_H +#define INCLUDED_STEAM_MUSIC_H + class Steam_Music : public ISteamMusic { int playing; @@ -41,3 +44,5 @@ public: void SetVolume( float flVolume ); float GetVolume(); }; + +#endif // INCLUDED_STEAM_MUSIC_H diff --git a/dll/steam_musicremote.h b/dll/steam_musicremote.h index 4ad84c2..1cbfe85 100644 --- a/dll/steam_musicremote.h +++ b/dll/steam_musicremote.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_MUSICREMOTE_H +#define INCLUDED_STEAM_MUSICREMOTE_H + class Steam_MusicRemote : public ISteamMusicRemote { public: @@ -65,3 +68,5 @@ public: bool SetCurrentPlaylistEntry( int nID ); bool PlaylistDidChange(); }; + +#endif // INCLUDED_STEAM_MUSICREMOTE_H diff --git a/dll/steam_networking.h b/dll/steam_networking.h index 4a19845..6b23acc 100644 --- a/dll/steam_networking.h +++ b/dll/steam_networking.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_NETWORKING_H +#define INCLUDED_STEAM_NETWORKING_H + //packet timeout in seconds for non connections #define ORPHANED_PACKET_TIMEOUT (20) #define NEW_CONNECTION_TIMEOUT (20.0) @@ -1026,3 +1029,5 @@ void Callback(Common_Message *msg) } } }; + +#endif // INCLUDED_STEAM_NETWORKING_H diff --git a/dll/steam_networking_messages.h b/dll/steam_networking_messages.h index 38df49a..ed7a7e8 100644 --- a/dll/steam_networking_messages.h +++ b/dll/steam_networking_messages.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_NETWORKING_MESSAGES +#define INCLUDED_STEAM_NETWORKING_MESSAGES + #define NETWORKING_MESSAGES_TIMEOUT 30.0 struct Steam_Message_Connection { @@ -444,3 +447,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_NETWORKING_MESSAGES diff --git a/dll/steam_networking_sockets.h b/dll/steam_networking_sockets.h index 235e7bc..8c11a08 100644 --- a/dll/steam_networking_sockets.h +++ b/dll/steam_networking_sockets.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_NETWORKING_SOCKETS_H +#define INCLUDED_STEAM_NETWORKING_SOCKETS_H + struct Listen_Socket { HSteamListenSocket socket_id; @@ -881,7 +884,7 @@ SteamNetworkingMessage_t *get_steam_message_connection(HSteamNetConnection hConn pMsg->m_pfnRelease = &delete_steam_message; pMsg->m_nChannel = 0; connect_socket->second.data.pop(); - PRINT_DEBUG("get_steam_message_connection %u %u, %u\n", hConn, size, pMsg->m_nMessageNumber); + PRINT_DEBUG("get_steam_message_connection %u, %u, %" PRIu64 "\n", hConn, size, pMsg->m_nMessageNumber); return pMsg; } @@ -2086,13 +2089,13 @@ void Callback(Common_Message *msg) auto connect_socket = s->connect_sockets.find(msg->networking_sockets().connection_id()); if (connect_socket != s->connect_sockets.end()) { if (connect_socket->second.remote_identity.GetSteamID64() == msg->source_id() && (connect_socket->second.status == CONNECT_SOCKET_CONNECTED)) { - PRINT_DEBUG("Steam_Networking_Sockets: got data len %u, num %u on connection %u\n", msg->networking_sockets().data().size(), msg->networking_sockets().message_number(), connect_socket->first); + PRINT_DEBUG("Steam_Networking_Sockets: got data len %" PRI_ZU ", num %" PRIu64 " on connection %u\n", msg->networking_sockets().data().size(), msg->networking_sockets().message_number(), connect_socket->first); connect_socket->second.data.push(msg->networking_sockets()); } } else { connect_socket = std::find_if(s->connect_sockets.begin(), s->connect_sockets.end(), [msg](const auto &in) {return in.second.remote_identity.GetSteamID64() == msg->source_id() && (in.second.status == CONNECT_SOCKET_NOT_ACCEPTED || in.second.status == CONNECT_SOCKET_CONNECTED) && in.second.remote_id == msg->networking_sockets().connection_id_from();}); if (connect_socket != s->connect_sockets.end()) { - PRINT_DEBUG("Steam_Networking_Sockets: got data len %u, num %u on not accepted connection %u\n", msg->networking_sockets().data().size(), msg->networking_sockets().message_number(), connect_socket->first); + PRINT_DEBUG("Steam_Networking_Sockets: got data len %" PRI_ZU ", num %" PRIu64 " on not accepted connection %u\n", msg->networking_sockets().data().size(), msg->networking_sockets().message_number(), connect_socket->first); connect_socket->second.data.push(msg->networking_sockets()); } } @@ -2110,3 +2113,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_NETWORKING_SOCKETS_H diff --git a/dll/steam_networking_socketsserialized.h b/dll/steam_networking_socketsserialized.h index 8b55292..30b5a2e 100644 --- a/dll/steam_networking_socketsserialized.h +++ b/dll/steam_networking_socketsserialized.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_NETWORKING_SOCKETS_SERIALIZED_H +#define INCLUDED_STEAM_NETWORKING_SOCKETS_SERIALIZED_H + class Steam_Networking_Sockets_Serialized : public ISteamNetworkingSocketsSerialized002, public ISteamNetworkingSocketsSerialized003, @@ -154,3 +157,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_NETWORKING_SOCKETS_SERIALIZED_H diff --git a/dll/steam_networking_utils.h b/dll/steam_networking_utils.h index 0e5388e..322adde 100644 --- a/dll/steam_networking_utils.h +++ b/dll/steam_networking_utils.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_NETWORKING_UTILS_H +#define INCLUDED_STEAM_NETWORKING_UTILS_H + class Steam_Networking_Utils : public ISteamNetworkingUtils001, public ISteamNetworkingUtils002, @@ -358,7 +361,7 @@ bool SetConnectionConfigValueString( HSteamNetConnection hConn, ESteamNetworking bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, intptr_t scopeObj, ESteamNetworkingConfigDataType eDataType, const void *pArg ) { - PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue %i %i %p %i %p\n", eValue, eScopeType, scopeObj, eDataType, pArg); + PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue %i %i %" PRIuPTR " %i %p\n", eValue, eScopeType, scopeObj, eDataType, pArg); return true; } @@ -749,3 +752,5 @@ void Callback(Common_Message *msg) }; + +#endif // INCLUDED_STEAM_NETWORKING_UTILS_H diff --git a/dll/steam_parental.h b/dll/steam_parental.h index 5505c61..5048541 100644 --- a/dll/steam_parental.h +++ b/dll/steam_parental.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_PARENTAL_H +#define INCLUDED_STEAM_PARENTAL_H + class Steam_Parental : public ISteamParentalSettings { public: @@ -29,3 +32,5 @@ public: bool BIsFeatureBlocked( EParentalFeature eFeature ); bool BIsFeatureInBlockList( EParentalFeature eFeature ); }; + +#endif // INCLUDED_STEAM_PARENTAL_H diff --git a/dll/steam_parties.h b/dll/steam_parties.h index 8d282be..4dc1028 100644 --- a/dll/steam_parties.h +++ b/dll/steam_parties.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_PARTIES_H +#define INCLUDED_STEAM_PARTIES_H + class Steam_Parties : public ISteamParties { @@ -192,3 +195,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_PARTIES_H diff --git a/dll/steam_remote_storage.h b/dll/steam_remote_storage.h index 032bc5f..de6bf79 100644 --- a/dll/steam_remote_storage.h +++ b/dll/steam_remote_storage.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_REMOTE_STORAGE_H +#define INCLUDED_STEAM_REMOTE_STORAGE_H + struct Async_Read { SteamAPICall_t api_call; uint32 offset; @@ -791,3 +794,5 @@ bool EndFileWriteBatch() }; + +#endif // INCLUDED_STEAM_REMOTE_STORAGE_H diff --git a/dll/steam_remoteplay.cpp b/dll/steam_remoteplay.cpp new file mode 100644 index 0000000..c182b76 --- /dev/null +++ b/dll/steam_remoteplay.cpp @@ -0,0 +1,202 @@ +/* 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 + . */ + +#include "steam_remoteplay.h" +#include +#include + +typedef struct remote_play_session_info_T { + RemotePlaySessionID_t session_id; + CSteamID connected_user; + const char * client_name; + ESteamDeviceFormFactor client_form_factor; + int client_resolution_x; + int client_resolution_y; +} remote_play_session_info; +//TODO: NOT thread safe!!! +static std::vector remote_play_sessions; + +int create_remote_play_session_info( RemotePlaySessionID_t session_id, CSteamID connected_user, const char * client_name, ESteamDeviceFormFactor client_form_factor, int client_resolution_x, int client_resolution_y ) { + remote_play_session_info session_info; + size_t buffer_length = 0; + char * buffer = NULL; + + if ((remote_play_sessions.size() < UINT_MAX) && (client_name != NULL)) { + session_info.session_id = session_id; + session_info.connected_user = connected_user; + session_info.client_form_factor = client_form_factor; + session_info.client_resolution_x = client_resolution_x; + session_info.client_resolution_y = client_resolution_y; + + buffer_length = strlen( client_name ); + if (buffer_length > 0) { + buffer = new char[buffer_length + 1]; + if (buffer != NULL) { + memcpy(buffer, client_name, buffer_length); + session_info.client_name = buffer; + remote_play_sessions.push_back( (const remote_play_session_info)session_info ); + return 0; + } + } + } + return -1; +} + +int destroy_remote_play_session_info( size_t index ) { + if (remote_play_sessions.size() < index) { + delete remote_play_sessions[index].client_name; + remote_play_sessions.erase(remote_play_sessions.begin() + index); + return 0; + } + return -1; +} + +uint32 get_number_of_remote_play_sessions() { + return (uint32)remote_play_sessions.size(); +} + +int get_remote_play_session_id( size_t index, RemotePlaySessionID_t * session_id ) { + if ((session_id != NULL) && (index >= 0) && (remote_play_sessions.size() < index)) { + *session_id = remote_play_sessions[index].session_id; + return 0; + } + return -1; +} + +int get_remote_play_session_index( RemotePlaySessionID_t session_id, size_t * index ) { + size_t count = 0; + + if ((index != NULL) && (remote_play_sessions.size() > 0)) { + for (std::vector::iterator iter = remote_play_sessions.begin(); iter != remote_play_sessions.end(); iter++) { + if (iter->session_id == session_id) { + *index = count; + return 0; + } + count++; + } + } + return -1; +} + +int get_remote_play_session_connected_user( size_t index, CSteamID * connected_user ) { + if ((connected_user != NULL) && (index >= 0) && (remote_play_sessions.size() < index)) { + *connected_user = remote_play_sessions[index].connected_user; + return 0; + } + return -1; +} + +int get_remote_play_session_client_name( size_t index, const char ** client_name ) { + if ((client_name != NULL) && (index >= 0) && (remote_play_sessions.size() < index)) { + *client_name = remote_play_sessions[index].client_name; + return 0; + } + return -1; +} + +int get_remote_play_session_client_form_factor( size_t index, ESteamDeviceFormFactor * client_form_factor ) { + if ((client_form_factor != NULL) && (index >= 0) && (remote_play_sessions.size() < index)) { + *client_form_factor = remote_play_sessions[index].client_form_factor; + return 0; + } + return -1; +} + +int get_remote_play_session_client_resolutions( size_t index, int * client_resolution_x, int * client_resolution_y ) { + if ((client_resolution_x != NULL) && (client_resolution_y != NULL) && (index >= 0) && (remote_play_sessions.size() < index)) { + *client_resolution_x = remote_play_sessions[index].client_resolution_x; + *client_resolution_y = remote_play_sessions[index].client_resolution_y; + return 0; + } + return -1; +} + +uint32 Steam_RemotePlay::GetSessionCount() +{ + PRINT_DEBUG("Steam_RemotePlay::GetSessionCount\n"); + return get_number_of_remote_play_sessions(); +} + +uint32 Steam_RemotePlay::GetSessionID( int iSessionIndex ) +{ + RemotePlaySessionID_t session_id; + + PRINT_DEBUG("Steam_RemotePlay::GetSessionID\n"); + return ((get_remote_play_session_id( iSessionIndex, &session_id ) == 0) ? (session_id) : (0)); +} + +CSteamID Steam_RemotePlay::GetSessionSteamID( uint32 unSessionID ) +{ + CSteamID steam_id = k_steamIDNil; + size_t index = 0; + + PRINT_DEBUG("Steam_RemotePlay::GetSessionSteamID\n"); + if (get_remote_play_session_index( unSessionID, &index ) == 0) { + if (get_remote_play_session_connected_user( index, &steam_id ) == 0) { + return steam_id; + } + } + return k_steamIDNil; +} + +const char * Steam_RemotePlay::GetSessionClientName( uint32 unSessionID ) +{ + const char * client_name = NULL; + size_t index = 0; + + PRINT_DEBUG("Steam_RemotePlay::GetSessionClientName\n"); + if (get_remote_play_session_index( unSessionID, &index ) == 0) { + if (get_remote_play_session_client_name( index, &client_name ) == 0) { + return client_name; + } + } + return NULL; +} + +ESteamDeviceFormFactor Steam_RemotePlay::GetSessionClientFormFactor( uint32 unSessionID ) +{ + ESteamDeviceFormFactor form_factor = k_ESteamDeviceFormFactorUnknown; + size_t index = 0; + + PRINT_DEBUG("Steam_RemotePlay::GetSessionClientFormFactor\n"); + if (get_remote_play_session_index( unSessionID, &index ) == 0) { + if (get_remote_play_session_client_form_factor( index, &form_factor ) == 0) { + return form_factor; + } + } + return k_ESteamDeviceFormFactorUnknown; +} + +bool Steam_RemotePlay::BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY ) +{ + int x = 0; + int y = 0; + size_t index = 0; + + PRINT_DEBUG("Steam_RemotePlay::BGetSessionClientResolution\n"); + if ((pnResolutionX != NULL) && (pnResolutionY != NULL)) { + if (get_remote_play_session_index( unSessionID, &index ) == 0) { + if (get_remote_play_session_client_resolutions( index, &x, &y ) == 0) { + *pnResolutionX = x; + *pnResolutionY = y; + return true; + } + } + } + return false; +} + diff --git a/dll/steam_remoteplay.h b/dll/steam_remoteplay.h index 7bd97ff..c03ea4f 100644 --- a/dll/steam_remoteplay.h +++ b/dll/steam_remoteplay.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_REMOTEPLAY_H +#define INCLUDED_STEAM_REMOTEPLAY_H + class Steam_RemotePlay : public ISteamRemotePlay { @@ -62,48 +65,24 @@ Steam_RemotePlay(class Settings *settings, class Networking *network, class Stea } // Get the number of currently connected Steam Remote Play sessions -uint32 GetSessionCount() -{ - PRINT_DEBUG("Steam_RemotePlay::GetSessionCount\n"); - return 0; -} +uint32 GetSessionCount(); // 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; -} +uint32 GetSessionID( int iSessionIndex ); // Get the SteamID of the connected user -CSteamID GetSessionSteamID( uint32 unSessionID ) -{ - PRINT_DEBUG("Steam_RemotePlay::GetSessionSteamID\n"); - return k_steamIDNil; -} +CSteamID GetSessionSteamID( uint32 unSessionID ); // 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; -} +const char *GetSessionClientName( uint32 unSessionID ); // Get the form factor of the session client device -ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID ) -{ - PRINT_DEBUG("Steam_RemotePlay::GetSessionClientFormFactor\n"); - return k_ESteamDeviceFormFactorUnknown; -} +ESteamDeviceFormFactor GetSessionClientFormFactor( uint32 unSessionID ); // 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; -} +bool BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *pnResolutionY ); // Invite a friend to Remote Play Together // This returns false if the invite can't be sent @@ -135,3 +114,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_REMOTEPLAY_H diff --git a/dll/steam_screenshots.cpp b/dll/steam_screenshots.cpp index f4e0d45..5eca0d2 100644 --- a/dll/steam_screenshots.cpp +++ b/dll/steam_screenshots.cpp @@ -65,7 +65,7 @@ ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilen if (pchFilename == nullptr) return INVALID_SCREENSHOT_HANDLE; - std::vector pixels(std::move(local_storage->load_image(pchFilename))); + std::vector pixels(std::move(local_storage->load_image(pchFilename, NULL, NULL))); if (pixels.size() != size_t(nWidth) * size_t(nHeight)) return INVALID_SCREENSHOT_HANDLE; diff --git a/dll/steam_screenshots.h b/dll/steam_screenshots.h index 745be9f..9acbe1f 100644 --- a/dll/steam_screenshots.h +++ b/dll/steam_screenshots.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_SCREENSHOTS_H +#define INCLUDED_STEAM_SCREENSHOTS_H + struct screenshot_infos_t { std::string screenshot_name; @@ -73,3 +76,5 @@ public: // JPEG, TGA, and PNG formats are supported. ScreenshotHandle AddVRScreenshotToLibrary( EVRScreenshotType eType, const char *pchFilename, const char *pchVRFilename ); }; + +#endif // INCLUDED_STEAM_SCREENSHOTS_H diff --git a/dll/steam_tv.h b/dll/steam_tv.h index 6ea7857..52738e3 100644 --- a/dll/steam_tv.h +++ b/dll/steam_tv.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_TV_H +#define INCLUDED_STEAM_TV_H + class Steam_TV : public ISteamTV { @@ -122,3 +125,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_TV_H diff --git a/dll/steam_ugc.h b/dll/steam_ugc.h index b1ada44..0b2785c 100644 --- a/dll/steam_ugc.h +++ b/dll/steam_ugc.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_UGC_H +#define INCLUDED_STEAM_UGC_H + struct UGC_query { UGCQueryHandle_t handle; std::set return_only; @@ -600,13 +603,13 @@ bool RemoveItemPreview( UGCUpdateHandle_t handle, uint32 index ) bool AddContentDescriptor( UGCUpdateHandle_t handle, EUGCContentDescriptorID descid ) { - PRINT_DEBUG("Steam_UGC::AddContentDescriptor %llu %u\n", handle, index); + PRINT_DEBUG("Steam_UGC::AddContentDescriptor %llu %u\n", handle, descid); return false; } bool RemoveContentDescriptor( UGCUpdateHandle_t handle, EUGCContentDescriptorID descid ) { - PRINT_DEBUG("Steam_UGC::RemoveContentDescriptor %llu %u\n", handle, index); + PRINT_DEBUG("Steam_UGC::RemoveContentDescriptor %llu %u\n", handle, descid); return false; } @@ -903,3 +906,5 @@ SteamAPICall_t GetWorkshopEULAStatus() }; + +#endif // INCLUDED_STEAM_UGC_H diff --git a/dll/steam_unified_messages.h b/dll/steam_unified_messages.h index fb34fb7..b9169a1 100644 --- a/dll/steam_unified_messages.h +++ b/dll/steam_unified_messages.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_UNIFIED_MESSAGES_H +#define INCLUDED_STEAM_UNIFIED_MESSAGES_H + class Steam_Unified_Messages : public ISteamUnifiedMessages { @@ -121,3 +124,5 @@ void Callback(Common_Message *msg) } }; + +#endif // INCLUDED_STEAM_UNIFIED_MESSAGES_H diff --git a/dll/steam_user.h b/dll/steam_user.h index 0f21124..a316e17 100644 --- a/dll/steam_user.h +++ b/dll/steam_user.h @@ -19,6 +19,9 @@ #include "appticket.h" +#ifndef INCLUDED_STEAM_USER_H +#define INCLUDED_STEAM_USER_H + class Steam_User : public ISteamUser009, public ISteamUser010, @@ -532,3 +535,5 @@ bool BSetDurationControlOnlineState( EDurationControlOnlineState eNewState ) } }; + +#endif // INCLUDED_STEAM_USER_H diff --git a/dll/steam_user_stats.h b/dll/steam_user_stats.h index 4ff4163..50352a4 100644 --- a/dll/steam_user_stats.h +++ b/dll/steam_user_stats.h @@ -85,6 +85,8 @@ private: nlohmann::json defined_achievements; nlohmann::json user_achievements; std::vector sorted_achievement_names; + std::map achievement_image_data_normal; + std::map achievement_image_data_gray; std::map stats_cache_int; std::map stats_cache_float; @@ -112,10 +114,136 @@ nlohmann::detail::iter_impl defined_achievements_find(std::strin }); } +int32 achievement_image_data_normal_find(std::string key) { + for (auto & it : achievement_image_data_normal) { + try { + if (it.first == key) { + return it.second; + } + } catch (...) {} + } + return 0; +} + +int32 achievement_image_data_gray_find(std::string key) { + for (auto & it : achievement_image_data_gray) { + try { + if (it.first == key) { + return it.second; + } + } catch (...) {} + } + return 0; +} + void load_achievements_db() { + uint64 count = 0; + uint64 bad_count = 0; + uint64 corrupt_count = 0; + uint64 missing_normal_images = 0; + uint64 missing_gray_images = 0; + uint64 unreadable_normal_images = 0; + uint64 unreadable_gray_images = 0; + std::string name = ""; std::string file_path = Local_Storage::get_game_settings_path() + achievements_user_file; local_storage->load_json(file_path, defined_achievements); + for (auto & it : defined_achievements) { + name = ""; + try { + auto name_it = it.find("name"); + if (name_it != it.end()) { + name = static_cast(*name_it); + } + if (name.length() > 0) { + auto normal_it = it.find("icon"); + auto gray_it = it.find("icon_gray"); + std::string normal = ""; + std::string gray = ""; + if (normal_it != it.end()) { + normal = Local_Storage::get_game_settings_path() + static_cast(*normal_it); + } + if (gray_it != it.end()) { + gray = Local_Storage::get_game_settings_path() + static_cast(*gray_it); + } + if (normal.length() > 0 && gray.length() > 0) { + uint32 normal_height = 0; + uint32 normal_width = 0; + uint32 gray_height = 0; + uint32 gray_width = 0; + std::vector normal_pixels(local_storage->load_image(normal, &normal_width, &normal_height)); + std::vector gray_pixels(local_storage->load_image(gray, &gray_width, &gray_height)); + if (normal_width > 0 && normal_height > 0 && + gray_width > 0 && gray_height > 0) { + std::string normalStr; + std::string grayStr; + normalStr.reserve(sizeof(uint32_t) * normal_width * normal_height); + grayStr.reserve(sizeof(uint32_t) * gray_width * gray_height); + for (auto & pix : normal_pixels) { + normalStr += pix.channels.r; + normalStr += pix.channels.g; + normalStr += pix.channels.b; + normalStr += pix.channels.a; + } + for (auto & pix : gray_pixels) { + grayStr += pix.channels.r; + grayStr += pix.channels.g; + grayStr += pix.channels.b; + grayStr += pix.channels.a; + } + achievement_image_data_normal[name] = settings->add_image(normalStr, normal_width, normal_height); + achievement_image_data_gray[name] = settings->add_image(grayStr, gray_width, gray_height); + count++; + } else { + corrupt_count++; + if (normal_width <= 0 || normal_height <= 0) + unreadable_normal_images++; + if (gray_width <= 0 || gray_height <= 0) + unreadable_gray_images++; + PRINT_DEBUG("%s %s %s.\n", + "Unable to load images for achievement", + name.c_str(), + "unable to get resolution from images."); + } + } else { + corrupt_count++; + if (normal.length() <= 0) + missing_normal_images++; + if (gray.length() <= 0) + missing_gray_images++; + PRINT_DEBUG("%s %s %s.\n", + "Unable to load images for achievement", + name.c_str(), + "an image path is not configured"); + } + } else { + bad_count++; + PRINT_DEBUG("%s %s %s %" PRIu64 " %s.\n", + "Invalid achievement json at", + file_path.c_str(), + "for achievement number", + count + corrupt_count + bad_count + 1, + "is invalid"); + } + } catch (std::exception &e) { + bad_count++; + PRINT_DEBUG("Loading image data for achievement %s failed. Exception type: %s Reason: %s.\n", + (name.length() > 0) ? name.c_str() : "ERROR: NAME PARSING FROM JSON FAILED", + typeid(e).name(), + e.what()); + } + } + + PRINT_DEBUG("loaded %" PRIu64 " achievement images.\npartial loaded %" PRIu64 " achievements.\nskipped %" PRIu64 " invalid achievements.\n", + count, + corrupt_count, + bad_count); + PRINT_DEBUG("ignored %" PRIu64 " missing normal achievement images.\nignored %" PRIu64 " missing gray achievement images.\n", + missing_normal_images, + missing_gray_images); + PRINT_DEBUG("ignored %" PRIu64 " unreadable normal achievement images.\nignored %" PRIu64 " unreadable gray achievement images.\n", + unreadable_normal_images, + unreadable_gray_images); } void load_achievements() @@ -590,10 +718,18 @@ bool StoreStats() int GetAchievementIcon( const char *pchName ) { PRINT_DEBUG("GetAchievementIcon\n"); - if (pchName == nullptr) return 0; + int ret = 0; + if (pchName == nullptr) return ret; std::lock_guard lock(global_mutex); - return 0; + bool achieved = false; + if ((GetAchievementAndUnlockTime(pchName, &achieved, NULL) == true) && (achieved == true)) { + ret = achievement_image_data_normal_find(pchName); + } else { + ret = achievement_image_data_gray_find(pchName); + } + + return ret; } diff --git a/dll/steam_utils.h b/dll/steam_utils.h index d229cd3..b65b554 100644 --- a/dll/steam_utils.h +++ b/dll/steam_utils.h @@ -19,6 +19,9 @@ #include "local_storage.h" #include "../overlay_experimental/steam_overlay.h" +#ifndef INCLUDED_STEAM_UTILS_H +#define INCLUDED_STEAM_UTILS_H + static std::chrono::time_point app_initialized_time = std::chrono::steady_clock::now(); @@ -113,7 +116,10 @@ bool GetImageRGBA( int iImage, uint8 *pubDest, int nDestBufferSize ) if (image == settings->images.end()) return false; unsigned size = image->second.data.size(); - if (nDestBufferSize < size) size = nDestBufferSize; + if (nDestBufferSize < size) { + PRINT_DEBUG("GetImageRGBA %i Given buffer too small. Got 0x%x bytes. Need 0x%x bytes.\n", iImage, nDestBufferSize, size); + size = nDestBufferSize; + } image->second.data.copy((char *)pubDest, nDestBufferSize); return true; } @@ -442,3 +448,5 @@ bool DismissFloatingGamepadTextInput() } }; + +#endif // INCLUDED_STEAM_UTILS_H diff --git a/dll/steam_video.h b/dll/steam_video.h index 9a1282d..0126ef5 100644 --- a/dll/steam_video.h +++ b/dll/steam_video.h @@ -17,6 +17,9 @@ #include "base.h" +#ifndef INCLUDED_STEAM_VIDEO_H +#define INCLUDED_STEAM_VIDEO_H + class Steam_Video : public ISteamVideo { public: @@ -32,3 +35,5 @@ public: void GetOPFSettings( AppId_t unVideoAppID ); bool GetOPFStringForApp( AppId_t unVideoAppID, char *pchBuffer, int32 *pnBufferSize ); }; + +#endif // INCLUDED_STEAM_VIDEO_H diff --git a/dll/wrap.cpp b/dll/wrap.cpp index 50defc7..7cabf7c 100644 --- a/dll/wrap.cpp +++ b/dll/wrap.cpp @@ -25,6 +25,7 @@ // Nothing to be done here #else #define STEAM_API_FUNCTIONS_IMPL + #include "base.h" #include "dll.h" @@ -34,6 +35,26 @@ const char *STEAM_PATH; size_t STEAM_PATH_SIZE; +#ifndef __x86_64__ +# define _STAT_VER_LINUX_OLD 1 +# define _STAT_VER_KERNEL 1 +# define _STAT_VER_SVR4 2 +# define _STAT_VER_LINUX 3 +# define _MKNOD_VER_LINUX 1 +# define _MKNOD_VER_SVR4 2 +#else +# define _STAT_VER_KERNEL 0 +# define _STAT_VER_LINUX 1 +# define _MKNOD_VER_LINUX 0 +#endif +#define _STAT_VER _STAT_VER_LINUX +#define _MKNOD_VER _MKNOD_VER_LINUX + +/* From kernel_stat.h It help me save some condition */ +#define XSTAT_IS_XSTAT64 1 +#define STATFS_IS_STATFS64 __STATFS_MATCHES_STATFS64 +#define STAT_IS_KERNEL_STAT 1 + // Returns a '/' terminated absolute path to the steam folder in user's home, // root is returned if env home is not set const char *get_steam_path() @@ -290,7 +311,16 @@ STEAMAPI_API int __wrap_access(const char *path, int mode) STEAMAPI_API int __wrap___xstat(int ver, const char * path, struct stat * stat_buf) { const char *path_lowercased = lowercase_path(path, false, false); - int result = __xstat(ver, path_lowercased, stat_buf); + int result; + + switch (ver) { + case _STAT_VER_KERNEL: + result = stat(path_lowercased, stat_buf); + break; + default: + result = EINVAL; + } + if (path_lowercased != path) { free((void *)path_lowercased); } @@ -305,7 +335,16 @@ STEAMAPI_API int __wrap_stat(const char * path, struct stat * stat_buf) STEAMAPI_API int __wrap___lxstat(int ver, const char * path, struct stat * stat_buf) { const char *path_lowercased = lowercase_path(path, false, false); - int result = __lxstat(ver, path_lowercased, stat_buf); + int result; + + switch (ver) { + case _STAT_VER_KERNEL: + result = lstat(path_lowercased, stat_buf); + break; + default: + result = EINVAL; + } + if (path_lowercased != path) { free((void *)path_lowercased); } @@ -350,7 +389,16 @@ STEAMAPI_API DIR *__wrap_opendir(const char *path) STEAMAPI_API int __wrap___xstat64(int ver, const char *path, struct stat64 *stat_buf) { const char *path_lowercased = lowercase_path(path, false, false); - int result = __xstat64(ver, path_lowercased, stat_buf); + int result; + + switch (ver) { + case _STAT_VER_KERNEL: + result = stat64(path_lowercased, stat_buf); + break; + default: + result = EINVAL; + } + if (path_lowercased != path) { free((void *)path_lowercased); } @@ -360,7 +408,16 @@ STEAMAPI_API int __wrap___xstat64(int ver, const char *path, struct stat64 *stat STEAMAPI_API int __wrap___lxstat64(int ver, const char *path, struct stat64 *stat_buf) { const char *path_lowercased = lowercase_path(path, false, false); - int result = __lxstat64(ver, path_lowercased, stat_buf); + int result; + + switch (ver) { + case _STAT_VER_KERNEL: + result = lstat64(path_lowercased, stat_buf); + break; + default: + result = EINVAL; + } + if (path_lowercased != path) { free((void *)path_lowercased); } @@ -448,7 +505,7 @@ STEAMAPI_API int __wrap_link(const char *path1, const char *path2) STEAMAPI_API int __wrap_mknod(const char *path, mode_t mode, dev_t dev) { const char *path_lowercased = lowercase_path(path, true, true); - int result = __xmknod(1, path_lowercased, mode, &dev); + int result = mknod(path_lowercased, mode, dev); if (path_lowercased != path) { free((void *)path_lowercased); } diff --git a/files_example/steam_settings.EXAMPLE/custom_master_server.EXAMPLE.txt b/files_example/steam_settings.EXAMPLE/custom_master_server.EXAMPLE.txt new file mode 100644 index 0000000..2837580 --- /dev/null +++ b/files_example/steam_settings.EXAMPLE/custom_master_server.EXAMPLE.txt @@ -0,0 +1,2 @@ +192.168.7.99 +removethis.test.domain.com diff --git a/generate_all_deps.bat b/generate_all_deps.bat new file mode 100644 index 0000000..974ec06 --- /dev/null +++ b/generate_all_deps.bat @@ -0,0 +1,57 @@ +@echo off +cd /d "%~dp0" + +call generate_build_win.bat + +IF EXIST build\all_deps\debug\x86\*.* ( DEL /F /S /Q build\all_deps\debug\x86\*.* ) +IF EXIST build\all_deps\debug\x64\*.* ( DEL /F /S /Q build\all_deps\debug\x64\*.* ) +IF EXIST build\all_deps\release\x86\*.* ( DEL /F /S /Q build\all_deps\release\x86\*.* ) +IF EXIST build\all_deps\release\x64\*.* ( DEL /F /S /Q build\all_deps\release\x64\*.* ) + +call build_set_protobuf_directories.bat + +setlocal +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x86.bat +SET OLD_DIR=%cd% +cd "build\all_deps\debug\x86" +cl /c @%CDS_DIR%/DEBUG.BLD @%CDS_DIR%/PROTOBUF_X86.BLD @%CDS_DIR%\ALL_DEPS.BLD +IF EXIST %CDS_DIR%\DEBUG_ALL_DEPS_X86.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_ALL_DEPS_X86.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_ALL_DEPS_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal +"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x64.bat +SET OLD_DIR=%cd% +cd "build\all_deps\debug\x64" +cl /c @%CDS_DIR%/DEBUG.BLD @%CDS_DIR%/PROTOBUF_X64.BLD @%CDS_DIR%\ALL_DEPS.BLD +IF EXIST %CDS_DIR%\DEBUG_ALL_DEPS_X64.LKS ( DEL /F /Q %CDS_DIR%\DEBUG_ALL_DEPS_X64.LKS ) +where "*.obj" > %CDS_DIR%\DEBUG_ALL_DEPS_X64.LKS +cd %OLD_DIR% +endlocal + +setlocal +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x86.bat +SET OLD_DIR=%cd% +cd "build\all_deps\release\x86" +cl /c @%CDS_DIR%/RELEASE.BLD @%CDS_DIR%/PROTOBUF_X86.BLD @%CDS_DIR%\ALL_DEPS.BLD +IF EXIST %CDS_DIR%\RELEASE_ALL_DEPS_X86.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_ALL_DEPS_X86.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_ALL_DEPS_X86.LKS +cd %OLD_DIR% +endlocal + +setlocal +"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto +call build_env_x64.bat +SET OLD_DIR=%cd% +cd "build\all_deps\release\x64" +cl /c @%CDS_DIR%/RELEASE.BLD @%CDS_DIR%/PROTOBUF_X64.BLD @%CDS_DIR%\ALL_DEPS.BLD +IF EXIST %CDS_DIR%\RELEASE_ALL_DEPS_X64.LKS ( DEL /F /Q %CDS_DIR%\RELEASE_ALL_DEPS_X64.LKS ) +where "*.obj" > %CDS_DIR%\RELEASE_ALL_DEPS_X64.LKS +cd %OLD_DIR% +endlocal + +SET BUILT_ALL_DEPS=1 diff --git a/generate_build_win.bat b/generate_build_win.bat new file mode 100644 index 0000000..3b7b231 --- /dev/null +++ b/generate_build_win.bat @@ -0,0 +1,406 @@ +@echo off +REM Should be called from the root of the repo. + +REM Make build and output dirs. +IF NOT EXIST build ( mkdir build ) +IF NOT EXIST build\cmds ( mkdir build\cmds ) +IF NOT EXIST build\debug ( mkdir build\debug ) +IF NOT EXIST build\release ( mkdir build\release ) +IF NOT EXIST build\all_deps ( mkdir build\all_deps ) +IF NOT EXIST build\all_deps\debug ( mkdir build\all_deps\debug ) +IF NOT EXIST build\all_deps\release ( mkdir build\all_deps\release ) +IF NOT EXIST build\experimental ( mkdir build\experimental ) +IF NOT EXIST build\experimental\debug ( mkdir build\experimental\debug ) +IF NOT EXIST build\experimental\release ( mkdir build\experimental\release ) +IF NOT EXIST build\experimental_steamclient ( mkdir build\experimental_steamclient ) +IF NOT EXIST build\experimental_steamclient\debug ( mkdir build\experimental_steamclient\debug ) +IF NOT EXIST build\experimental_steamclient\release ( mkdir build\experimental_steamclient\release ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader ( mkdir build\experimental_steamclient\steamclient_loader ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader\debug ( mkdir build\experimental_steamclient\steamclient_loader\debug ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader\release ( mkdir build\experimental_steamclient\steamclient_loader\release ) +IF NOT EXIST build\lobby_connect\debug ( mkdir build\lobby_connect\debug ) +IF NOT EXIST build\lobby_connect\release ( mkdir build\lobby_connect\release ) +IF NOT EXIST build\tools ( mkdir build\tools ) +IF NOT EXIST build\tools\debug ( mkdir build\tools\debug ) +IF NOT EXIST build\tools\release ( mkdir build\tools\release ) + +IF NOT EXIST build\debug\x86 ( mkdir build\debug\x86 ) +IF NOT EXIST build\debug\x64 ( mkdir build\debug\x64 ) +IF NOT EXIST build\release\x86 ( mkdir build\release\x86 ) +IF NOT EXIST build\release\x64 ( mkdir build\release\x64 ) + +IF NOT EXIST build\all_deps\debug\x86 ( mkdir build\all_deps\debug\x86 ) +IF NOT EXIST build\all_deps\debug\x64 ( mkdir build\all_deps\debug\x64 ) +IF NOT EXIST build\all_deps\release\x86 ( mkdir build\all_deps\release\x86 ) +IF NOT EXIST build\all_deps\release\x64 ( mkdir build\all_deps\release\x64 ) + +IF NOT EXIST build\experimental\debug\x86 ( mkdir build\experimental\debug\x86 ) +IF NOT EXIST build\experimental\debug\x64 ( mkdir build\experimental\debug\x64 ) +IF NOT EXIST build\experimental\release\x86 ( mkdir build\experimental\release\x86 ) +IF NOT EXIST build\experimental\release\x64 ( mkdir build\experimental\release\x64 ) + +IF NOT EXIST build\experimental_steamclient\debug\x86 ( mkdir build\experimental_steamclient\debug\x86 ) +IF NOT EXIST build\experimental_steamclient\debug\x64 ( mkdir build\experimental_steamclient\debug\x64 ) +IF NOT EXIST build\experimental_steamclient\release\x86 ( mkdir build\experimental_steamclient\release\x86 ) +IF NOT EXIST build\experimental_steamclient\release\x64 ( mkdir build\experimental_steamclient\release\x64 ) +IF NOT EXIST build\experimental_steamclient\debug\x86\deps ( mkdir build\experimental_steamclient\debug\x86\deps ) +IF NOT EXIST build\experimental_steamclient\debug\x64\deps ( mkdir build\experimental_steamclient\debug\x64\deps ) +IF NOT EXIST build\experimental_steamclient\release\x86\deps ( mkdir build\experimental_steamclient\release\x86\deps ) +IF NOT EXIST build\experimental_steamclient\release\x64\deps ( mkdir build\experimental_steamclient\release\x64\deps ) +IF NOT EXIST build\experimental_steamclient\debug\x86\sc_deps ( mkdir build\experimental_steamclient\debug\x86\sc_deps ) +IF NOT EXIST build\experimental_steamclient\debug\x64\sc_deps ( mkdir build\experimental_steamclient\debug\x64\sc_deps ) +IF NOT EXIST build\experimental_steamclient\release\x86\sc_deps ( mkdir build\experimental_steamclient\release\x86\sc_deps ) +IF NOT EXIST build\experimental_steamclient\release\x64\sc_deps ( mkdir build\experimental_steamclient\release\x64\sc_deps ) + +IF NOT EXIST build\experimental_steamclient\steamclient_loader\debug\x86 ( mkdir build\experimental_steamclient\steamclient_loader\debug\x86 ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader\debug\x64 ( mkdir build\experimental_steamclient\steamclient_loader\debug\x64 ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader\release\x86 ( mkdir build\experimental_steamclient\steamclient_loader\release\x86 ) +IF NOT EXIST build\experimental_steamclient\steamclient_loader\release\x64 ( mkdir build\experimental_steamclient\steamclient_loader\release\x64 ) + +IF NOT EXIST build\lobby_connect\debug\x86 ( mkdir build\lobby_connect\debug\x86 ) +IF NOT EXIST build\lobby_connect\debug\x64 ( mkdir build\lobby_connect\debug\x64 ) +IF NOT EXIST build\lobby_connect\release\x86 ( mkdir build\lobby_connect\release\x86 ) +IF NOT EXIST build\lobby_connect\release\x64 ( mkdir build\lobby_connect\release\x64 ) +IF NOT EXIST build\tools\debug\x86 ( mkdir build\tools\debug\x86 ) +IF NOT EXIST build\tools\debug\x64 ( mkdir build\tools\debug\x64 ) +IF NOT EXIST build\tools\release\x86 ( mkdir build\tools\release\x86 ) +IF NOT EXIST build\tools\release\x64 ( mkdir build\tools\release\x64 ) + +IF NOT EXIST debug ( mkdir debug ) +IF NOT EXIST debug\experimental ( mkdir debug\experimental ) +IF NOT EXIST debug\experimental_steamclient ( mkdir debug\experimental_steamclient ) +IF NOT EXIST debug\lobby_connect ( mkdir debug\lobby_connect ) +IF NOT EXIST debug\tools ( mkdir debug\tools ) + +IF NOT EXIST release ( mkdir release ) +IF NOT EXIST release\experimental ( mkdir release\experimental ) +IF NOT EXIST release\experimental_steamclient ( mkdir release\experimental_steamclient ) +IF NOT EXIST release\lobby_connect ( mkdir release\lobby_connect ) +IF NOT EXIST release\tools ( mkdir release\tools ) + +SET CDS_DIR=%cd%\build\cmds + +REM +REM Arguments. +REM + +REM normal_args. +IF EXIST %CDS_DIR%\NORMAL_ARGS.ARG ( DEL /F /S /Q %CDS_DIR%\NORMAL_ARGS.ARG ) +echo /EHsc > %CDS_DIR%\NORMAL_ARGS.ARG +echo /Ox >> %CDS_DIR%\NORMAL_ARGS.ARG + +REM JOB ARGS. +IF "%JOB_COUNT%" == "" ( echo /MP1 >> %CDS_DIR%\NORMAL_ARGS.ARG ) +IF NOT "%JOB_COUNT%" == "" ( echo /MP%JOB_COUNT% >> %CDS_DIR%\NORMAL_ARGS.ARG ) + +REM Debug args. +IF EXIST %CDS_DIR%\DEBUG.BLD ( DEL /F /S /Q %CDS_DIR%\DEBUG.BLD ) +IF EXIST %CDS_DIR%\DEBUG.LKS ( DEL /F /S /Q %CDS_DIR%\DEBUG.LKS ) + +REM Create empty file. (No BUILD time args currently.) +type NUL >> %CDS_DIR%\DEBUG.BLD + +REM DISABLE the PDB builds if we are running on the CI. (It can't build them currently.) +IF EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /DEBUG:NONE > %CDS_DIR%\DEBUG.LKS ) +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /DEBUG:FULL /OPT:REF /OPT:ICF > %CDS_DIR%\DEBUG.LKS ) + +REM Release args. +IF EXIST %CDS_DIR%\RELEASE.ARG ( DEL /F /S /Q %CDS_DIR%\RELEASE.ARG ) +IF EXIST %CDS_DIR%\RELEASE.LKS ( DEL /F /S /Q %CDS_DIR%\RELEASE.LKS ) +REM Release mode Flags. +echo /DEMU_RELEASE_BUILD > %CDS_DIR%\RELEASE.ARG +echo /DNDEBUG >> %CDS_DIR%\RELEASE.ARG +copy %CDS_DIR%\RELEASE.ARG %CDS_DIR%\RELEASE.BLD +type %CDS_DIR%\RELEASE.ARG > %CDS_DIR%\RELEASE.LKS + +REM DISABLE the PDB builds if we are running on the CI. (It can't build them currently.) +IF EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /DEBUG:NONE >> %CDS_DIR%\RELEASE.LKS ) +IF NOT EXIST %OLD_DIR%\CI_BUILD.TAG ( echo /link /DEBUG:FULL /OPT:REF /OPT:ICF >> %CDS_DIR%\RELEASE.LKS ) + +REM BASE DLL Flags. +IF EXIST %CDS_DIR%\DLL_MAIN_CPP.ARG ( DEL /F /S /Q %CDS_DIR%\DLL_MAIN_CPP.ARG ) + +REM EXPERIMENTAL Flags. +IF EXIST %CDS_DIR%\EXPERIMENTAL.ARG ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL.ARG ) +echo /DEMU_EXPERIMENTAL_BUILD > %CDS_DIR%\EXPERIMENTAL.ARG +echo /DCONTROLLER_SUPPORT >> %CDS_DIR%\EXPERIMENTAL.ARG +echo /DEMU_OVERLAY >> %CDS_DIR%\EXPERIMENTAL.ARG + +REM EXPERIMENTAL_STEAMCLIENT Flags. +IF EXIST %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.ARG ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.ARG ) +echo /DSTEAMCLIENT_DLL > %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.ARG + +REM lobby_connect Flags. +IF EXIST %CDS_DIR%\LOBBY_CONNECT.ARG ( DEL /F /S /Q %CDS_DIR%\LOBBY_CONNECT.ARG ) +echo /DNO_DISK_WRITES > %CDS_DIR%\LOBBY_CONNECT.ARG +echo /DLOBBY_CONNECT >> %CDS_DIR%\LOBBY_CONNECT.ARG + +REM +REM Includes. +REM + +REM protobuf. +call build_set_protobuf_directories.bat +IF EXIST %CDS_DIR%\PROTOBUF_X86.ICD ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X86.ICD ) +IF EXIST %CDS_DIR%\PROTOBUF_X64.ICD ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X64.ICD ) +setlocal +SET TEST_A=%cd% +cd %PROTOBUF_X86_DIRECTORY% +SET TEST_B=%cd%\include +cd %TEST_A% +echo /I%TEST_B% > %CDS_DIR%\PROTOBUF_X86.ICD +endlocal +setlocal +SET TEST_A=%cd% +cd %PROTOBUF_X64_DIRECTORY% +SET TEST_B=%cd%\include +cd %TEST_A% +echo /I%TEST_B% > %CDS_DIR%\PROTOBUF_X64.ICD +endlocal + +REM OVERLAY_EXPERIMENTAL. +IF EXIST %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD ( DEL /F /S /Q %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD ) +echo /I%cd%\overlay_experimental > %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD + +REM IMGUI. +IF EXIST %CDS_DIR%\IMGUI.ICD ( DEL /F /S /Q %CDS_DIR%\IMGUI.ICD ) +echo /I%cd%\ImGui > %CDS_DIR%\IMGUI.ICD + +REM +REM Link Libraries. +REM + +REM protobuf. +IF EXIST %CDS_DIR%\PROTOBUF_X86.OS ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X86.OS ) +IF EXIST %CDS_DIR%\PROTOBUF_X64.OS ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X64.OS ) +dir /b /s %PROTOBUF_X86_LIBRARY% > %CDS_DIR%\PROTOBUF_X86.OS +dir /b /s %PROTOBUF_X64_LIBRARY% > %CDS_DIR%\PROTOBUF_X64.OS + +REM BASE DLL. +IF EXIST %CDS_DIR%\DLL_MAIN_CPP.OS ( DEL /F /S /Q %CDS_DIR%\DLL_MAIN_CPP.OS ) +echo dbghelp.lib > %CDS_DIR%\DLL_MAIN_CPP.OS +echo Iphlpapi.lib >> %CDS_DIR%\DLL_MAIN_CPP.OS +echo Ws2_32.lib >> %CDS_DIR%\DLL_MAIN_CPP.OS +echo Shell32.lib >> %CDS_DIR%\DLL_MAIN_CPP.OS +echo advapi32.lib >> %CDS_DIR%\DLL_MAIN_CPP.OS + +REM EXPERIMENTAL. +IF EXIST %CDS_DIR%\EXPERIMENTAL.OS ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL.OS ) +echo Faultrep.lib > %CDS_DIR%\EXPERIMENTAL.OS +echo opengl32.lib >> %CDS_DIR%\EXPERIMENTAL.OS +echo Winmm.lib >> %CDS_DIR%\EXPERIMENTAL.OS + +REM steamclient_loader. +IF EXIST %CDS_DIR%\STEAMCLIENT_LOADER.OS ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT_LOADER.OS ) +echo advapi32.lib > %CDS_DIR%\STEAMCLIENT_LOADER.OS +echo user32.lib >> %CDS_DIR%\STEAMCLIENT_LOADER.OS + +REM lobby_connect. +IF EXIST %CDS_DIR%\LOBBY_CONNECT.OS ( DEL /F /S /Q %CDS_DIR%\LOBBY_CONNECT.OS ) +echo Comdlg32.lib > %CDS_DIR%\LOBBY_CONNECT.OS + +REM +REM Files. +REM + +REM Protobuf. +REM Needs to be compiled here (really just needs to exist), as we include it below. +"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto + +REM OVERLAY_EXPERIMENTAL. +IF EXIST %CDS_DIR%\OVERLAY_EXPERIMENTAL.FLS ( DEL /F /S /Q %CDS_DIR%\OVERLAY_EXPERIMENTAL.FLS ) +IF EXIST %CDS_DIR%\OVERLAY_EXPERIMENTAL_SYSTEM.FLS ( DEL /F /S /Q %CDS_DIR%\OVERLAY_EXPERIMENTAL_SYSTEM.FLS ) +where "%cd%\overlay_experimental\:*.cpp" > %CDS_DIR%\OVERLAY_EXPERIMENTAL.FLS +where "%cd%\overlay_experimental\windows\:*.cpp" >> %CDS_DIR%\OVERLAY_EXPERIMENTAL.FLS +where "%cd%\overlay_experimental\System\:*.cpp" >> %CDS_DIR%\OVERLAY_EXPERIMENTAL_SYSTEM.FLS + +REM IMGUI. +IF EXIST %CDS_DIR%\IMGUI.FLS ( DEL /F /S /Q %CDS_DIR%\IMGUI.FLS ) +where "%cd%\ImGui\:*.cpp" > %CDS_DIR%\IMGUI.FLS +where "%cd%\ImGui\backends\:imgui_impl_dx*.cpp" >> %CDS_DIR%\IMGUI.FLS +where "%cd%\ImGui\backends\:imgui_impl_win32.cpp" >> %CDS_DIR%\IMGUI.FLS +where "%cd%\ImGui\backends\:imgui_impl_vulkan.cpp" >> %CDS_DIR%\IMGUI.FLS +where "%cd%\ImGui\backends\:imgui_impl_opengl3.cpp" >> %CDS_DIR%\IMGUI.FLS +where "%cd%\ImGui\backends\:imgui_win_shader_blobs.cpp" >> %CDS_DIR%\IMGUI.FLS + +REM DETOURS. +IF EXIST %CDS_DIR%\DETOURS.FLS ( DEL /F /S /Q %CDS_DIR%\DETOURS.FLS ) +where "%cd%\detours\:*.cpp" > %CDS_DIR%\DETOURS.FLS + +REM CONTROLLER. +IF EXIST %CDS_DIR%\CONTROLLER.FLS ( DEL /F /S /Q CONTROLLER.FLS ) +where "%cd%\controller\:gamepad.c" > %CDS_DIR%\CONTROLLER.FLS + +REM sc_different_deps. +IF EXIST %CDS_DIR%\SC_DIFFERENT_DEPS.FLS ( DEL /F /S /Q %CDS_DIR%\SC_DIFFERENT_DEPS.FLS ) +where "%cd%\dll\:flat.cpp" > %CDS_DIR%\SC_DIFFERENT_DEPS.FLS +where "%cd%\dll\:dll.cpp" >> %CDS_DIR%\SC_DIFFERENT_DEPS.FLS + +REM BASE DLL. +IF EXIST %CDS_DIR%\DLL_MAIN_CPP.FLS ( DEL /F /S /Q %CDS_DIR%\DLL_MAIN_CPP.FLS ) +move %cd%\dll\flat.cpp %cd%\dll\flat.cpp.tmp +move %cd%\dll\dll.cpp %cd%\dll\dll.cpp.tmp +where "%cd%\dll\:*.cpp" > %CDS_DIR%\DLL_MAIN_CPP.FLS +move %cd%\dll\flat.cpp.tmp %cd%\dll\flat.cpp +move %cd%\dll\dll.cpp.tmp %cd%\dll\dll.cpp +where "%cd%\dll\:*.cc" >> %CDS_DIR%\DLL_MAIN_CPP.FLS + +REM SteamClient. +IF EXIST %CDS_DIR%\STEAMCLIENT.FLS ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT.FLS ) +where "%cd%\:steamclient.cpp" > %CDS_DIR%\STEAMCLIENT.FLS + +REM steamclient_loader. +IF EXIST %CDS_DIR%\STEAMCLIENT_LOADER.FLS ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT_LOADER.FLS ) +where "%cd%\steamclient_loader\:*.cpp" > %CDS_DIR%\STEAMCLIENT_LOADER.FLS + +REM lobby_connect. +IF EXIST %CDS_DIR%\LOBBY_CONNECT.FLS ( DEL /F /S /Q %CDS_DIR%\LOBBY_CONNECT.FLS ) +where "%cd%\:lobby_connect.cpp" > %CDS_DIR%\LOBBY_CONNECT.FLS + +REM generate_interfaces_file. +IF EXIST %CDS_DIR%\GENERATE_INTERFACES_FILE.FLS ( DEL /F /S /Q %CDS_DIR%\GENERATE_INTERFACES_FILE.FLS ) +where "%cd%\:generate_interfaces_file.cpp" > %CDS_DIR%\GENERATE_INTERFACES_FILE.FLS + +REM +REM Build and link cmd script files. +REM + +REM protobuf. +IF EXIST %CDS_DIR%\PROTOBUF_X86.BLD ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X86.BLD ) +IF EXIST %CDS_DIR%\PROTOBUF_X86.LKS ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X86.LKS ) +type %CDS_DIR%\PROTOBUF_X86.ICD > %CDS_DIR%\PROTOBUF_X86.BLD +type %CDS_DIR%\PROTOBUF_X86.BLD > %CDS_DIR%\PROTOBUF_X86.LKS +type %CDS_DIR%\PROTOBUF_X86.OS >> %CDS_DIR%\PROTOBUF_X86.LKS + +IF EXIST %CDS_DIR%\PROTOBUF_X64.BLD ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X64.BLD ) +IF EXIST %CDS_DIR%\PROTOBUF_X64.LKS ( DEL /F /S /Q %CDS_DIR%\PROTOBUF_X64.LKS ) +type %CDS_DIR%\PROTOBUF_X64.ICD > %CDS_DIR%\PROTOBUF_X64.BLD +type %CDS_DIR%\PROTOBUF_X64.BLD > %CDS_DIR%\PROTOBUF_X64.LKS +type %CDS_DIR%\PROTOBUF_X64.OS >> %CDS_DIR%\PROTOBUF_X64.LKS + +REM SC_DEPS +IF EXIST %CDS_DIR%\SC_DEPS.BLD ( DEL /F /S /Q %CDS_DIR%\SC_DEPS.BLD ) +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\SC_DEPS.BLD +type %CDS_DIR%\EXPERIMENTAL.ARG >> %CDS_DIR%\SC_DEPS.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD >> %CDS_DIR%\SC_DEPS.BLD +type %CDS_DIR%\IMGUI.ICD >> %CDS_DIR%\SC_DEPS.BLD +type %CDS_DIR%\DLL_MAIN_CPP.FLS >> %CDS_DIR%\SC_DEPS.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.FLS >> %CDS_DIR%\SC_DEPS.BLD + +REM DEPS +IF EXIST %CDS_DIR%\DEPS.BLD ( DEL /F /S /Q %CDS_DIR%\DEPS.BLD ) +type %CDS_DIR%\DETOURS.FLS >> %CDS_DIR%\DEPS.BLD +type %CDS_DIR%\CONTROLLER.FLS >> %CDS_DIR%\DEPS.BLD +type %CDS_DIR%\IMGUI.FLS >> %CDS_DIR%\DEPS.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL_SYSTEM.FLS >> %CDS_DIR%\DEPS.BLD + +REM all_deps. +IF EXIST %CDS_DIR%\ALL_DEPS.BLD ( DEL /F /S /Q %CDS_DIR%\ALL_DEPS.BLD ) +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD >> %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\IMGUI.ICD >> %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\DETOURS.FLS >> %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\CONTROLLER.FLS >> %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\IMGUI.FLS >> %CDS_DIR%\ALL_DEPS.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL_SYSTEM.FLS >> %CDS_DIR%\ALL_DEPS.BLD + +REM BASE DLL. +IF EXIST %CDS_DIR%\DLL_MAIN_CPP.BLD ( DEL /F /S /Q %CDS_DIR%\DLL_MAIN_CPP.BLD ) +IF EXIST %CDS_DIR%\DLL_MAIN_CPP.LKS ( DEL /F /S /Q %CDS_DIR%\DLL_MAIN_CPP.LKS ) +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\DLL_MAIN_CPP.BLD +type %CDS_DIR%\DLL_MAIN_CPP.FLS >> %CDS_DIR%\DLL_MAIN_CPP.BLD +type %CDS_DIR%\SC_DIFFERENT_DEPS.FLS >> %CDS_DIR%\DLL_MAIN_CPP.BLD +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\DLL_MAIN_CPP.LKS +type %CDS_DIR%\DLL_MAIN_CPP.OS >> %CDS_DIR%\DLL_MAIN_CPP.LKS + +REM EXPERIMENTAL. +IF EXIST %CDS_DIR%\EXPERIMENTAL.BLD ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL.BLD ) +IF EXIST %CDS_DIR%\EXPERIMENTAL.LKS ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL.LKS ) + +REM Note the order and repeats. cl will complain if this gets messed up. +REM OLD SCRIPT. +REM type NORMAL_ARGS.ARG > EXPERIMENTAL.BLD +REM type EXPERIMENTAL.ARG >> EXPERIMENTAL.BLD +REM type EXPERIMENTAL.ICD >> EXPERIMENTAL.BLD +REM type DLL_MAIN_CPP.FLS >> EXPERIMENTAL.BLD +REM type SC_DIFFERENT_DEPS.FLS >> EXPERIMENTAL.BLD +REM type OVERLAY_EXPERIMENTAL.FLS >> EXPERIMENTAL.BLD +REM type OVERLAY_EXPERIMENTAL_SYSTEM.FLS >> EXPERIMENTAL.BLD +REM type DETOURS.FLS >> EXPERIMENTAL.BLD +REM type CONTROLLER.FLS >> EXPERIMENTAL.BLD +REM type IMGUI.FLS >> EXPERIMENTAL.BLD +REM type NORMAL_ARGS.ARG > EXPERIMENTAL.LKS +REM type EXPERIMENTAL.ARG >> EXPERIMENTAL.LKS +REM type EXPERIMENTAL.ICD >> EXPERIMENTAL.LKS +REM type DLL_MAIN_CPP.OS >> EXPERIMENTAL.LKS +REM type EXPERIMENTAL.OS >> EXPERIMENTAL.LKS +REM NEW Combined experimental && experimental_steamclient SCRIPT. +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\EXPERIMENTAL.BLD +type %CDS_DIR%\EXPERIMENTAL.ARG >> %CDS_DIR%\EXPERIMENTAL.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD >> %CDS_DIR%\EXPERIMENTAL.BLD +type %CDS_DIR%\IMGUI.ICD >> %CDS_DIR%\EXPERIMENTAL.BLD +type %CDS_DIR%\SC_DIFFERENT_DEPS.FLS >> %CDS_DIR%\EXPERIMENTAL.BLD + +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\EXPERIMENTAL.LKS +type %CDS_DIR%\EXPERIMENTAL.ARG >> %CDS_DIR%\EXPERIMENTAL.LKS +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD >> %CDS_DIR%\EXPERIMENTAL.LKS +type %CDS_DIR%\IMGUI.ICD >> %CDS_DIR%\EXPERIMENTAL.LKS +type %CDS_DIR%\DLL_MAIN_CPP.OS >> %CDS_DIR%\EXPERIMENTAL.LKS +type %CDS_DIR%\EXPERIMENTAL.OS >> %CDS_DIR%\EXPERIMENTAL.LKS + +REM SteamClient. +IF EXIST %CDS_DIR%\STEAMCLIENT.BLD ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT.BLD ) +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\STEAMCLIENT.BLD +type %CDS_DIR%\EXPERIMENTAL.ARG >> %CDS_DIR%\STEAMCLIENT.BLD +type %CDS_DIR%\OVERLAY_EXPERIMENTAL.ICD >> %CDS_DIR%\STEAMCLIENT.BLD +type %CDS_DIR%\IMGUI.ICD >> %CDS_DIR%\STEAMCLIENT.BLD +type %CDS_DIR%\STEAMCLIENT.FLS >> %CDS_DIR%\STEAMCLIENT.BLD + +REM EXPERIMENTAL_STEAMCLIENT. +IF EXIST %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.BLD ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.BLD ) +IF EXIST %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS ( DEL /F /S /Q %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS ) + +REM Note the order and repeats. cl will complain if this gets messed up. + + + + +REM FULL +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS +type %CDS_DIR%\EXPERIMENTAL.ARG >> %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS +type %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.ARG >> %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS +type %CDS_DIR%\SC_DIFFERENT_DEPS.FLS >> %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS +type %CDS_DIR%\DLL_MAIN_CPP.OS >> %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS +type %CDS_DIR%\EXPERIMENTAL.OS >> %CDS_DIR%\EXPERIMENTAL_STEAMCLIENT.LKS + +REM steamclient_loader. +IF EXIST %CDS_DIR%\STEAMCLIENT_LOADER.BLD ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT_LOADER.BLD ) +IF EXIST %CDS_DIR%\STEAMCLIENT_LOADER.LKS ( DEL /F /S /Q %CDS_DIR%\STEAMCLIENT_LOADER.LKS ) + +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\STEAMCLIENT_LOADER.BLD +type %CDS_DIR%\STEAMCLIENT_LOADER.FLS >> %CDS_DIR%\STEAMCLIENT_LOADER.BLD + +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\STEAMCLIENT_LOADER.LKS +type %CDS_DIR%\STEAMCLIENT_LOADER.OS >> %CDS_DIR%\STEAMCLIENT_LOADER.LKS + +REM lobby_connect. +IF EXIST %CDS_DIR%\LOBBY_CONNECT.BLD ( DEL /F /S /Q %CDS_DIR%\LOBBY_CONNECT.BLD ) +IF EXIST %CDS_DIR%\LOBBY_CONNECT.LKS ( DEL /F /S /Q %CDS_DIR%\LOBBY_CONNECT.LKS ) +type %CDS_DIR%\LOBBY_CONNECT.ARG > %CDS_DIR%\LOBBY_CONNECT.BLD +type %CDS_DIR%\NORMAL_ARGS.ARG >> %CDS_DIR%\LOBBY_CONNECT.BLD +type %CDS_DIR%\LOBBY_CONNECT.FLS >> %CDS_DIR%\LOBBY_CONNECT.BLD +type %CDS_DIR%\DLL_MAIN_CPP.FLS >> %CDS_DIR%\LOBBY_CONNECT.BLD +type %CDS_DIR%\SC_DIFFERENT_DEPS.FLS >> %CDS_DIR%\LOBBY_CONNECT.BLD + +type %CDS_DIR%\LOBBY_CONNECT.ARG > %CDS_DIR%\LOBBY_CONNECT.LKS +type %CDS_DIR%\NORMAL_ARGS.ARG >> %CDS_DIR%\LOBBY_CONNECT.LKS +type %CDS_DIR%\DLL_MAIN_CPP.OS >> %CDS_DIR%\LOBBY_CONNECT.LKS +type %CDS_DIR%\LOBBY_CONNECT.OS >> %CDS_DIR%\LOBBY_CONNECT.LKS + +REM GENERATE_INTERFACES_FILE +IF EXIST %CDS_DIR%\GENERATE_INTERFACES_FILE.BLD ( DEL /F /S /Q %CDS_DIR%\GENERATE_INTERFACES_FILE.BLD ) +IF EXIST %CDS_DIR%\GENERATE_INTERFACES_FILE.LKS ( DEL /F /S /Q %CDS_DIR%\GENERATE_INTERFACES_FILE.LKS ) +type %CDS_DIR%\NORMAL_ARGS.ARG >> %CDS_DIR%\GENERATE_INTERFACES_FILE.BLD +type %CDS_DIR%\GENERATE_INTERFACES_FILE.FLS >> %CDS_DIR%\GENERATE_INTERFACES_FILE.BLD + +type %CDS_DIR%\NORMAL_ARGS.ARG > %CDS_DIR%\GENERATE_INTERFACES_FILE.LKS diff --git a/generate_build_win_bat.py b/generate_build_win_bat.py deleted file mode 100644 index f24b6bc..0000000 --- a/generate_build_win_bat.py +++ /dev/null @@ -1,127 +0,0 @@ -import os - -def files_from_dir(dir, extension, filter=[]): - out = [] - for f in os.listdir(dir): - if f.endswith(extension) and f not in filter: - out.append(os.path.join(dir, f)) - return out - -def convert_to_obj(files, obj_dir): - out = [] - for f in files: - out.append(os.path.join(obj_dir, os.path.splitext(os.path.basename(f))[0] + ".obj")) - return out - -def cl_line_obj(arguments, out_dir): - return "rmdir /S /Q {0}\nmkdir {0}\ncl /Fo:{0}/ /c {1}\n".format(out_dir, ' '.join(arguments)) - -def cl_line_link(arguments, linker_arguments): - return "cl /LD {} /link {}\n".format(' '.join(arguments), ' '.join(linker_arguments)) - -def cl_line_exe(arguments, linker_arguments): - return "cl {} /link {}\n".format(' '.join(arguments), ' '.join(linker_arguments)) - -jobs = 4 -normal_build_args = ["/EHsc", "/Ox", "/MP{}".format(jobs)] - -includes = ["ImGui", "overlay_experimental"] -includes_32 = list(map(lambda a: '/I{}'.format(a), ["%PROTOBUF_X86_DIRECTORY%\\include\\"] + includes)) -includes_64 = list(map(lambda a: '/I{}'.format(a), ["%PROTOBUF_X64_DIRECTORY%\\include\\"] + includes)) - -debug_build_args = [] -release_build_args = ["/DEMU_RELEASE_BUILD", "/DNDEBUG"] -steamclient_build_args = ["/DSTEAMCLIENT_DLL"] - -experimental_build_args = ["/DEMU_EXPERIMENTAL_BUILD", "/DCONTROLLER_SUPPORT", "/DEMU_OVERLAY"] -steamclient_experimental_build_args = experimental_build_args + steamclient_build_args - -normal_linker_libs = ["Iphlpapi.lib", "Ws2_32.lib", "rtlgenrandom.lib", "Shell32.lib"] -experimental_linker_libs = ["opengl32.lib", "Winmm.lib"] + normal_linker_libs -linker_32 = ['"%PROTOBUF_X86_LIBRARY%"'] -linker_64 = ['"%PROTOBUF_X64_LIBRARY%"'] - -controller_deps = ["controller/gamepad.c"] -imgui_deps = files_from_dir("ImGui", ".cpp") + ["ImGui/backends/imgui_impl_dx9.cpp", "ImGui/backends/imgui_impl_dx10.cpp", "ImGui/backends/imgui_impl_dx11.cpp", "ImGui/backends/imgui_impl_dx12.cpp", "ImGui/backends/imgui_impl_win32.cpp", "ImGui/backends/imgui_impl_vulkan.cpp", "ImGui/backends/imgui_impl_opengl3.cpp", "ImGui/backends/imgui_win_shader_blobs.cpp"] -proto_deps = list(map(lambda a: a.replace(".proto", ".pb.cc"), files_from_dir("dll", ".proto"))) -all_deps = proto_deps + files_from_dir("detours", ".cpp") + controller_deps + imgui_deps + files_from_dir("overlay_experimental/System", ".cpp") - -sc_different_deps = ["flat.cpp", "dll.cpp"] -steam_deps = files_from_dir("dll", ".cpp", sc_different_deps) -overlay_deps = files_from_dir("overlay_experimental", ".cpp") + files_from_dir("overlay_experimental/windows", ".cpp") -experimental_steam_deps = steam_deps + overlay_deps -sc_different_deps = list(map(lambda a: "dll/" + a, sc_different_deps)) - -regular_files = [] - -head = """@echo off -cd /d "%~dp0" -rmdir /S /Q release -mkdir release -mkdir release\experimental -mkdir release\experimental_steamclient -mkdir release\debug_experimental -mkdir release\debug_experimental_steamclient -call build_set_protobuf_directories.bat -""" - -head_32bit = """"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\\net.proto -call build_env_x86.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -""" - -head_64bit = """"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\\net.proto -call build_env_x64.bat -cl dll/rtlgenrandom.c dll/rtlgenrandom.def -""" - -footer = """ -copy Readme_release.txt release\Readme.txt -xcopy /s files_example\* release\\ -copy Readme_experimental.txt release\experimental\Readme.txt -copy Readme_debug.txt release\debug_experimental\Readme.txt -copy steamclient_loader\ColdClientLoader.ini release\experimental_steamclient\\ -call build_win_lobby_connect.bat -call build_win_find_interfaces.bat -""" - -out = head -out += head_32bit - -deps_folder = "deps" -sc_deps_folder = "deps_sc" - -def generate_common(include_arch, linker_arch, steam_api_name, steamclient_name): - out = "" - out += cl_line_obj(normal_build_args + release_build_args + include_arch + all_deps, deps_folder) - out += cl_line_link(normal_build_args + release_build_args + include_arch + steam_deps + sc_different_deps + ["deps/net.pb.obj"] + linker_arch + normal_linker_libs, ["/debug:none", "/OUT:release\\{}".format(steam_api_name)]) - - debug_full_args = normal_build_args + debug_build_args + experimental_build_args + include_arch - out += cl_line_obj(debug_full_args + experimental_steam_deps, sc_deps_folder) - - debug_full_dll_args = debug_full_args + sc_different_deps + convert_to_obj(all_deps, deps_folder) + convert_to_obj(experimental_steam_deps, sc_deps_folder) + linker_arch + experimental_linker_libs - out += cl_line_link(debug_full_dll_args, ["/OUT:release\debug_experimental\\{}".format(steam_api_name)]) - out += cl_line_link(steamclient_build_args + debug_full_dll_args, ["/OUT:release\debug_experimental_steamclient\\{}".format(steamclient_name)]) - - release_full_args = normal_build_args + release_build_args + experimental_build_args + include_arch - out += cl_line_obj(release_full_args + experimental_steam_deps, sc_deps_folder) - - release_full_dll_args = release_full_args + sc_different_deps + convert_to_obj(all_deps, deps_folder) + convert_to_obj(experimental_steam_deps, sc_deps_folder) + linker_arch + experimental_linker_libs - out += cl_line_link(release_full_dll_args, ["/debug:none", "/OUT:release\experimental\\{}".format(steam_api_name)]) - out += cl_line_link(steamclient_build_args + release_full_dll_args, ["/debug:none", "/OUT:release\experimental_steamclient\\{}".format(steamclient_name)]) - out += cl_line_link(release_build_args + experimental_build_args + ["steamclient.cpp"] + normal_build_args, ["/debug:none", "/OUT:release\experimental\\{}".format(steamclient_name)]) - return out - -out += generate_common(includes_32, linker_32, "steam_api.dll", "steamclient.dll") - -out += cl_line_exe(files_from_dir("steamclient_loader", ".cpp") + ["advapi32.lib", "user32.lib"] + normal_build_args, ["/debug:none", "/OUT:release\experimental_steamclient\steamclient_loader.exe"]) - -out += head_64bit -out += generate_common(includes_64, linker_64, "steam_api64.dll", "steamclient64.dll") - - -out += footer - - -with open("build_win_release_test.bat", "w") as f: - f.write(out) diff --git a/lobby_connect.cpp b/lobby_connect.cpp index 36f3327..3af6dad 100644 --- a/lobby_connect.cpp +++ b/lobby_connect.cpp @@ -32,93 +32,113 @@ #else #endif -int main() { +int main(int argc, char ** argv) { + bool broadcast_relay_daemon = false; + + if (argc > 1) { + for (int x = 1; x < argc; x++) { + if (strcmp(argv[x], "-d") == 0) { + broadcast_relay_daemon = true; + } + } + } + if (SteamAPI_Init()) { //Set appid to: LOBBY_CONNECT_APPID SteamAPI_RestartAppIfNecessary(LOBBY_CONNECT_APPID); - std::cout << "This is a program to find lobbies and run the game with lobby connect parameters" << std::endl; - std::cout << "Api initialized, "; + if (broadcast_relay_daemon) { + std::cout << "Running in relay daemon mode. Press Ctrl+C to exit...." << std::endl; + std::cout.flush(); + + while (1) { + SteamAPI_RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + } else { + std::cout << "This is a program to find lobbies and run the game with lobby connect parameters" << std::endl; + std::cout << "Api initialized, "; top: - std::cout << "waiting a few seconds for connections:" << std::endl; - for (int i = 0; i < 10; ++i) { - SteamAPI_RunCallbacks(); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } + std::cout << "waiting a few seconds for connections:" << std::endl; + for (int i = 0; i < 10; ++i) { + SteamAPI_RunCallbacks(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } - int friend_count = SteamFriends()->GetFriendCount(k_EFriendFlagAll); - std::cout << "People on the network: " << friend_count << std::endl; - for (int i = 0; i < friend_count; ++i) { - CSteamID id = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagAll); - const char *name = SteamFriends()->GetFriendPersonaName(id); + int friend_count = SteamFriends()->GetFriendCount(k_EFriendFlagAll); + std::cout << "People on the network: " << friend_count << std::endl; + for (int i = 0; i < friend_count; ++i) { + CSteamID id = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagAll); + const char *name = SteamFriends()->GetFriendPersonaName(id); - FriendGameInfo_t friend_info = {}; - SteamFriends()->GetFriendGamePlayed(id, &friend_info); - std::cout << name << " is playing: " << friend_info.m_gameID.AppID() << std::endl; - } + FriendGameInfo_t friend_info = {}; + SteamFriends()->GetFriendGamePlayed(id, &friend_info); + std::cout << name << " is playing: " << friend_info.m_gameID.AppID() << std::endl; + } - std::cout << std::endl << "--------------Menu-------------" << std::endl << "\tappid\tname\tcommand line" << std::endl; + std::cout << std::endl << "--------------Menu-------------" << std::endl << "\tappid\tname\tcommand line" << std::endl; - std::vector arguments; - for (int i = 0; i < friend_count; ++i) { - CSteamID id = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagAll); - const char *name = SteamFriends()->GetFriendPersonaName(id); - const char *connect = SteamFriends()->GetFriendRichPresence( id, "connect"); - FriendGameInfo_t friend_info = {}; - SteamFriends()->GetFriendGamePlayed(id, &friend_info); + std::vector arguments; + for (int i = 0; i < friend_count; ++i) { + CSteamID id = SteamFriends()->GetFriendByIndex(i, k_EFriendFlagAll); + const char *name = SteamFriends()->GetFriendPersonaName(id); + const char *connect = SteamFriends()->GetFriendRichPresence( id, "connect"); + FriendGameInfo_t friend_info = {}; + SteamFriends()->GetFriendGamePlayed(id, &friend_info); - if (strlen(connect) > 0) { - std::cout << arguments.size() << "\t" << friend_info.m_gameID.AppID() << "\t" << name << "\t" << connect << std::endl; - arguments.push_back(connect); - } else { - if (friend_info.m_steamIDLobby != k_steamIDNil) { - std::string connect = "+connect_lobby " + std::to_string(friend_info.m_steamIDLobby.ConvertToUint64()); + if (strlen(connect) > 0) { std::cout << arguments.size() << "\t" << friend_info.m_gameID.AppID() << "\t" << name << "\t" << connect << std::endl; arguments.push_back(connect); + } else { + if (friend_info.m_steamIDLobby != k_steamIDNil) { + std::string connect = "+connect_lobby " + std::to_string(friend_info.m_steamIDLobby.ConvertToUint64()); + std::cout << arguments.size() << "\t" << friend_info.m_gameID.AppID() << "\t" << name << "\t" << connect << std::endl; + arguments.push_back(connect); + } } } - } - std::cout << arguments.size() << ": Retry." << std::endl; - std::cout << std::endl << "Enter the number corresponding to your choice then press Enter." << std::endl; - unsigned int choice; - std::cin >> choice; + std::cout << arguments.size() << ": Retry." << std::endl; + std::cout << std::endl << "Enter the number corresponding to your choice then press Enter." << std::endl; + unsigned int choice; + std::cin >> choice; - if (choice >= arguments.size()) goto top; + if (choice >= arguments.size()) goto top; #ifdef _WIN32 - std::cout << "starting the game with: " << arguments[choice] << std::endl << "Please select the game exe" << std::endl; + std::cout << "starting the game with: " << arguments[choice] << std::endl << "Please select the game exe" << std::endl; - OPENFILENAMEA ofn; - char szFileName[MAX_PATH] = ""; - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = 0; - ofn.lpstrFilter = "Exe Files (*.exe)\0*.exe\0All Files (*.*)\0*.*\0"; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; - ofn.lpstrDefExt = "txt"; - if(GetOpenFileNameA(&ofn)) - { - std::string filename = szFileName; - filename = "\"" + filename + "\" " + arguments[choice]; - std::cout << filename << std::endl; - STARTUPINFOA lpStartupInfo; - PROCESS_INFORMATION lpProcessInfo; + OPENFILENAMEA ofn; + char szFileName[MAX_PATH] = ""; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = 0; + ofn.lpstrFilter = "Exe Files (*.exe)\0*.exe\0All Files (*.*)\0*.*\0"; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + ofn.lpstrDefExt = "txt"; + if(GetOpenFileNameA(&ofn)) + { + std::string filename = szFileName; + filename = "\"" + filename + "\" " + arguments[choice]; + std::cout << filename << std::endl; + STARTUPINFOA lpStartupInfo; + PROCESS_INFORMATION lpProcessInfo; - ZeroMemory( &lpStartupInfo, sizeof( lpStartupInfo ) ); - lpStartupInfo.cb = sizeof( lpStartupInfo ); - ZeroMemory( &lpProcessInfo, sizeof( lpProcessInfo ) ); + ZeroMemory( &lpStartupInfo, sizeof( lpStartupInfo ) ); + lpStartupInfo.cb = sizeof( lpStartupInfo ); + ZeroMemory( &lpProcessInfo, sizeof( lpProcessInfo ) ); - CreateProcessA( NULL, - const_cast(filename.c_str()), NULL, NULL, - NULL, NULL, NULL, NULL, - &lpStartupInfo, - &lpProcessInfo - ); - } + CreateProcessA( NULL, + const_cast(filename.c_str()), NULL, NULL, + NULL, NULL, NULL, NULL, + &lpStartupInfo, + &lpProcessInfo + ); + } #else - std::cout << "Please launch the game with these arguments: " << arguments[choice] << std::endl; + std::cout << "Please launch the game with these arguments: " << arguments[choice] << std::endl; #endif + } } } diff --git a/overlay_experimental/Renderer_Detector.cpp b/overlay_experimental/Renderer_Detector.cpp index 687b12f..abaacc2 100644 --- a/overlay_experimental/Renderer_Detector.cpp +++ b/overlay_experimental/Renderer_Detector.cpp @@ -48,6 +48,12 @@ #undef GetModuleHandle #endif + // Minimal timeout for detecting the real rendering API when multiple are used. + #define MIN_RENDER_DETECT_TIMEOUT 1 + // Variance is in milliseconds. 1000 (one second) / 24 (FPS) = Aprox. 41.66 Milliseconds between frames.) + #define MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE 42 + #define GET_RENDERER_DETECT_TIMEOUT ((MAX_RENDERER_API_DETECT_TIMEOUT > MIN_RENDER_DETECT_TIMEOUT) ? MAX_RENDERER_API_DETECT_TIMEOUT : MIN_RENDER_DETECT_TIMEOUT) * 1000 + #elif defined(__linux__) || defined(linux) #define RENDERERDETECTOR_OS_LINUX @@ -105,6 +111,15 @@ private: decltype(::SwapBuffers)* wglSwapBuffers; decltype(::vkQueuePresentKHR)* vkQueuePresentKHR; + std::mutex timing_detect_mutex; + std::chrono::time_point initial_hook_detect_time; + std::chrono::time_point d3d_hook_detect_time; + std::chrono::time_point ogl_hook_detect_time; + std::chrono::time_point vkn_hook_detect_time; + std::chrono::steady_clock::duration d3d_frame_duration; + std::chrono::steady_clock::duration ogl_frame_duration; + std::chrono::steady_clock::duration vkn_frame_duration; + bool dxgi_hooked; bool dxgi1_2_hooked; bool dx12_hooked; @@ -113,6 +128,7 @@ private: bool dx9_hooked; bool opengl_hooked; bool vulkan_hooked; + bool wine_found; DX12_Hook* dx12_hook; DX11_Hook* dx11_hook; @@ -135,6 +151,7 @@ private: dx9_hooked(false), opengl_hooked(false), vulkan_hooked(false), + wine_found(false), renderer_hook(nullptr), dx9_hook(nullptr), dx10_hook(nullptr), @@ -257,6 +274,8 @@ private: void DeduceDXVersionFromSwapChain(IDXGISwapChain* pSwapChain) { + PRINT_DEBUG("%s.\n", + "Renderer_Detector DeduceDXVersionFromSwapChain"); IUnknown* pDevice = nullptr; if (Inst()->dx12_hooked) { @@ -294,21 +313,154 @@ private: } } + static bool CheckD3DHookDetectTimings() + { + auto inst = Inst(); + bool ret = false; + + // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. + // + // ---BUT--- + // + // Wine and it's derivatives will -always- pass the OpenGL or Vulkan renderer + // detection, depending on what backend they are set to use for D3D. + // (wined3d == OpenGL. vkd3d || vkd3d-proton || DXVK || etc. == Vulkan.) + // + // So pass this check if, under Windows, D3D's frame interval timing isn't close to + // OpenGL's or Vulkan's. (I.e. Multiple frames have passed and only D3D has kept pace.) + // --OR-- + // Invert the result under Windows to cover the NVIDIA case. + // --OR-- + // Fail this check under Wine if OpenGL's / Vulkan's timing is the only regularly + // incrementing value. (I.e. Multiple frames have passed and only OpenGL OR Vulkan is + // keeping pace.) + // --OR-- + // Pass this check under Wine if D3D's frame interval timing is keeping up with + // OpenGL's / Vulkan's. (I.e. Multiple frames have passed, D3D and either OpenGL or + // Vulkan has kept pace.) + { + if (inst->d3d_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->d3d_frame_duration != std::chrono::steady_clock::duration()) + { + if (inst->ogl_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->ogl_frame_duration != std::chrono::steady_clock::duration()) + { + std::chrono::duration duration_diff(0); + if (inst->ogl_frame_duration > inst->d3d_frame_duration) + { + duration_diff = std::chrono::duration_cast(inst->ogl_frame_duration - inst->d3d_frame_duration); + } + else + { + duration_diff = std::chrono::duration_cast(inst->d3d_frame_duration - inst->ogl_frame_duration); + } + + if (duration_diff < std::chrono::duration(MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE)) + { + ret = true; + } + + if (!inst->wine_found) + ret = !ret; + } + else + { + if (inst->vkn_hook_detect_time != std::chrono::steady_clock::time_point() && + inst->vkn_frame_duration != std::chrono::steady_clock::duration()) + { + std::chrono::duration duration_diff(0); + if (inst->vkn_frame_duration > inst->d3d_frame_duration) + { + duration_diff = std::chrono::duration_cast(inst->vkn_frame_duration - inst->d3d_frame_duration); + } + else + { + duration_diff = std::chrono::duration_cast(inst->d3d_frame_duration - inst->vkn_frame_duration); + } + + if (duration_diff < std::chrono::duration(MAX_RENDER_DETECT_FRAME_TIMING_VARIANCE)) + { + ret = true; + } + + if (!inst->wine_found) + ret = !ret; + } + else + { + ret = true; + } + } + } + } + + PRINT_DEBUG("%s %s.\n", + "Renderer_Detector CheckD3DHookDetectTimings Correct API:", + ((ret == true) ? "D3D" : "OpenGL / Vulkan")); + + return ret; + } + static HRESULT STDMETHODCALLTYPE MyIDXGISwapChain_Present(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags) { auto inst = Inst(); HRESULT res; bool locked; - std::unique_lock lk(inst->renderer_mutex, std::defer_lock); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyIDXGISwapChain_Present current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. // So only lock when OpenGL or Vulkan hasn't already locked the mutex. - locked = lk.try_lock(); - res = (_this->*inst->IDXGISwapChainPresent)(SyncInterval, Flags); - if (!locked || inst->detection_done) - return res; - inst->DeduceDXVersionFromSwapChain(_this); + res = (_this->*inst->IDXGISwapChainPresent)(SyncInterval, Flags); + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass) + inst->DeduceDXVersionFromSwapChain(_this); + } return res; } @@ -318,16 +470,60 @@ private: auto inst = Inst(); HRESULT res; bool locked; - std::unique_lock lk(inst->renderer_mutex, std::defer_lock); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyIDXGISwapChain_Present1 current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } // It appears that (NVidia at least) calls IDXGISwapChain when calling OpenGL or Vulkan SwapBuffers. // So only lock when OpenGL or Vulkan hasn't already locked the mutex. - locked = lk.try_lock(); - res = (_this->*inst->IDXGISwapChainPresent1)(SyncInterval, Flags, pPresentParameters); - if (!locked || inst->detection_done) - return res; - inst->DeduceDXVersionFromSwapChain(_this); + res = (_this->*inst->IDXGISwapChainPresent1)(SyncInterval, Flags, pPresentParameters); + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass) + inst->DeduceDXVersionFromSwapChain(_this); + } return res; } @@ -335,13 +531,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9Present(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9Present Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9Present current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DDevice9Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -349,13 +590,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9PresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9PresentEx Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9PresentEx current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DDevice9ExPresentEx)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -363,13 +649,58 @@ private: static HRESULT STDMETHODCALLTYPE MyDX9SwapChainPresent(IDirect3DSwapChain9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + + { + std::lock_guard lk(inst->renderer_mutex); + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent Initial time"); + } + if (inst->d3d_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyDX9SwapChainPresent Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->d3d_frame_duration = std::chrono::duration_cast(current - inst->d3d_hook_detect_time); + inst->d3d_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyDX9SwapChainPresent current frame duration:", + (std::chrono::duration_cast(inst->d3d_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = CheckD3DHookDetectTimings(); + } + } + } + } auto res = (_this->*inst->IDirect3DSwapChain9Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->dx9_hook); + if (detect_pass) + inst->HookDetected(inst->dx9_hook); + } return res; } @@ -377,15 +708,57 @@ private: static BOOL WINAPI MywglSwapBuffers(HDC hDC) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers Initial time"); + } + if (inst->ogl_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->ogl_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MywglSwapBuffers Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->ogl_frame_duration = std::chrono::duration_cast(current - inst->ogl_hook_detect_time); + inst->ogl_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MywglSwapBuffers current frame duration:", + (std::chrono::duration_cast(inst->ogl_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = !(CheckD3DHookDetectTimings()); + } + } + } + } auto res = inst->wglSwapBuffers(hDC); - if (inst->detection_done) - return res; - - if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1)) { - inst->HookDetected(inst->opengl_hook); + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; + + if (detect_pass == true && gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1)) + inst->HookDetected(inst->opengl_hook); } return res; @@ -394,13 +767,58 @@ private: static VkResult VKAPI_CALL MyvkQueuePresentKHR(VkQueue Queue, const VkPresentInfoKHR* pPresentInfo) { auto inst = Inst(); - std::lock_guard lk(inst->renderer_mutex); + bool detect_pass = false; + { + std::lock_guard lk(inst->renderer_mutex); + + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR"); + + if (!inst->detection_done) + { + auto current = std::chrono::steady_clock::now(); + std::lock_guard t_lk(inst->timing_detect_mutex); + if (inst->initial_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->initial_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR Initial time"); + } + if (inst->vkn_hook_detect_time == std::chrono::steady_clock::time_point()) + { + inst->vkn_hook_detect_time = current; + PRINT_DEBUG("%s.\n", + "Renderer_Detector MyvkQueuePresentKHR Initial detect"); + } + else + { + if (std::chrono::duration_cast(current - inst->initial_hook_detect_time) < std::chrono::milliseconds{GET_RENDERER_DETECT_TIMEOUT}) + { + inst->vkn_frame_duration = std::chrono::duration_cast(current - inst->vkn_hook_detect_time); + inst->vkn_hook_detect_time = current; + PRINT_DEBUG("%s %" PRIu64 " %s %" PRIu64 "ms.\n", + "Renderer_Detector MyvkQueuePresentKHR current frame duration:", + (std::chrono::duration_cast(inst->vkn_frame_duration)).count(), + "ms. time since first detect:", + (std::chrono::duration_cast(current - inst->initial_hook_detect_time)).count()); + } + else + { + detect_pass = !(CheckD3DHookDetectTimings()); + } + } + } + } auto res = inst->vkQueuePresentKHR(Queue, pPresentInfo); - if (inst->detection_done) - return res; + { + std::lock_guard lk(inst->renderer_mutex); + if (inst->detection_done) + return res; - inst->HookDetected(inst->vulkan_hook); + if (detect_pass) + inst->HookDetected(inst->vulkan_hook); + } return res; } @@ -583,6 +1001,23 @@ private: { if (!dx10_hooked) { + //HACK: Remove when wine _finally_ implements the NULL renderer.... + bool found_wine = false; + std::string ntdll_path = FindPreferedModulePath("ntdll.dll"); + if (!ntdll_path.empty()) + { + HMODULE hNTdll = GetModuleHandleA(ntdll_path.c_str()); + if (hNTdll != nullptr) + { + auto wine_get_version = (const char*(*)())GetProcAddress(hNTdll, "wine_get_version"); + if (wine_get_version != nullptr) + { + found_wine = true; + SPDLOG_WARN("Found WINE version: %s.\n", wine_get_version()); + } + } + } + System::Library::Library libD3d10; if (!libD3d10.OpenLibrary(library_path, false)) { @@ -607,7 +1042,7 @@ private: if (D3D10CreateDevice != nullptr && CreateDXGIFactory1 != nullptr) { - D3D10CreateDevice(NULL, D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &pDevice); + D3D10CreateDevice(NULL, (found_wine) ? D3D10_DRIVER_TYPE_WARP : D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &pDevice); if (pDevice != nullptr) { CreateDXGIFactory1(IID_PPV_ARGS(&pDXGIFactory)); @@ -627,6 +1062,24 @@ private: pDXGIFactory->CreateSwapChainForHwnd(pDevice, dummyWindow, &SwapChainDesc, NULL, NULL, reinterpret_cast(&pSwapChain)); } + else + { + SPDLOG_WARN("Failed to instanciate IDXGIFactory2, fallback to pure DX10 detection"); + } + } + else + { + SPDLOG_WARN("Failed to instanciate ID3D10Device, fallback to pure DX10 detection"); + } + } + else + { + if (D3D10CreateDevice == nullptr) + { + SPDLOG_WARN("Failed to get address of D3D10CreateDevice, fallback to pure DX10 detection"); + } else + { + SPDLOG_WARN("Failed to get address of CreateDXGIFactory1, fallback to pure DX10 detection"); } } @@ -660,7 +1113,11 @@ private: SwapChainDesc.SampleDesc.Quality = 0; SwapChainDesc.Windowed = TRUE; - D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice); + D3D10CreateDeviceAndSwapChain(NULL, (found_wine) ? D3D10_DRIVER_TYPE_WARP : D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice); + } + else + { + SPDLOG_WARN("Failed to get address of D3D10CreateDeviceAndSwapChain, DX10 API failed."); } } @@ -698,6 +1155,23 @@ private: { if (!dx11_hooked) { + //HACK: Remove when wine _finally_ implements the NULL renderer.... + bool found_wine = false; + std::string ntdll_path = FindPreferedModulePath("ntdll.dll"); + if (!ntdll_path.empty()) + { + HMODULE hNTdll = GetModuleHandleA(ntdll_path.c_str()); + if (hNTdll != nullptr) + { + auto wine_get_version = (const char*(*)())GetProcAddress(hNTdll, "wine_get_version"); + if (wine_get_version != nullptr) + { + found_wine = true; + SPDLOG_WARN("Found WINE version: %s.\n", wine_get_version()); + } + } + } + System::Library::Library libD3d11; if (!libD3d11.OpenLibrary(library_path, false)) { @@ -722,7 +1196,7 @@ private: if (D3D11CreateDevice != nullptr && CreateDXGIFactory1 != nullptr) { - D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &pDevice, NULL, NULL); + D3D11CreateDevice(NULL, (found_wine) ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &pDevice, NULL, NULL); if (pDevice != nullptr) { CreateDXGIFactory1(IID_PPV_ARGS(&pDXGIFactory)); @@ -741,7 +1215,17 @@ private: SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; pDXGIFactory->CreateSwapChainForHwnd(pDevice, dummyWindow, &SwapChainDesc, NULL, NULL, reinterpret_cast(&pSwapChain)); + } else { + SPDLOG_WARN("Failed to instanciate IDXGIFactory2, fallback to pure DX11 detection"); } + } else { + SPDLOG_WARN("Failed to instanciate ID3D11Device, fallback to pure DX11 detection"); + } + } else { + if (D3D11CreateDevice == nullptr) { + SPDLOG_WARN("Failed to get address of D3D11CreateDevice, fallback to pure DX11 detection"); + } else { + SPDLOG_WARN("Failed to get address of CreateDXGIFactory1, fallback to pure DX11 detection"); } } @@ -775,7 +1259,9 @@ private: SwapChainDesc.SampleDesc.Quality = 0; SwapChainDesc.Windowed = TRUE; - D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice, NULL, NULL); + D3D11CreateDeviceAndSwapChain(NULL, (found_wine) ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice, NULL, NULL); + } else { + SPDLOG_WARN("Failed to get address of D3D11CreateDeviceAndSwapChain, DX11 API failed."); } } @@ -1045,6 +1531,20 @@ private: bool EnterDetection() { + std::string ntdll_path = FindPreferedModulePath("ntdll.dll"); + if (!ntdll_path.empty()) + { + HMODULE hNTdll = GetModuleHandleA(ntdll_path.c_str()); + if (hNTdll != nullptr) + { + auto wine_get_version = (const char*(*)())GetProcAddress(hNTdll, "wine_get_version"); + if (wine_get_version != nullptr) + { + wine_found = true; + PRINT_DEBUG("Found WINE version: %s.\n", wine_get_version()); + } + } + } return CreateHWND() != nullptr; } @@ -1063,6 +1563,7 @@ private: dx9_hooked = false; opengl_hooked = false; vulkan_hooked = false; + wine_found = false; delete dx9_hook ; dx9_hook = nullptr; delete dx10_hook ; dx10_hook = nullptr; @@ -1070,6 +1571,15 @@ private: delete dx12_hook ; dx12_hook = nullptr; delete opengl_hook; opengl_hook = nullptr; delete vulkan_hook; vulkan_hook = nullptr; + + initial_hook_detect_time = std::chrono::time_point(); + d3d_hook_detect_time = std::chrono::time_point(); + ogl_hook_detect_time = std::chrono::time_point(); + vkn_hook_detect_time = std::chrono::time_point(); + + d3d_frame_duration = std::chrono::steady_clock::duration(); + ogl_frame_duration = std::chrono::steady_clock::duration(); + vkn_frame_duration = std::chrono::steady_clock::duration(); } #elif defined(RENDERERDETECTOR_OS_LINUX) @@ -1312,6 +1822,22 @@ public: { std::lock_guard lk(stop_detection_mutex); + if (timeout != std::chrono::milliseconds{ -1 }) + { + if (timeout < std::chrono::milliseconds{ GET_RENDERER_DETECT_TIMEOUT * 2 }) + {// Need enough time to determine API if multiple are used. + timeout = std::chrono::milliseconds{ GET_RENDERER_DETECT_TIMEOUT * 2 }; + } + PRINT_DEBUG("%s %" PRIu64 ".\n", + "Renderer_Detector Detection timeout set to:", + timeout.count()); + } + else + { + PRINT_DEBUG("%s.\n", + "Renderer_Detector Detection timeout set to: infinite"); + } + if (detection_count == 0) {// If we have no detections in progress, restart detection. detection_cancelled = false; @@ -1401,7 +1927,7 @@ public: } stop_detection_cv.notify_all(); - SPDLOG_TRACE("Renderer detection done {}.", (void*)renderer_hook); + SPDLOG_TRACE("Renderer detection done %p.", (void*)renderer_hook); return renderer_hook; }); diff --git a/overlay_experimental/Renderer_Detector.h b/overlay_experimental/Renderer_Detector.h index bec8368..34a534b 100644 --- a/overlay_experimental/Renderer_Detector.h +++ b/overlay_experimental/Renderer_Detector.h @@ -27,6 +27,21 @@ #include "Renderer_Hook.h" +/* Timeout is in seconds. + Used for determining which rendering API is the real one used by + the app when multiple APIs are used. (NVIDIA, Wine, etc.) + + This is an internally used value, separate from the timeout given + to DetectRenderer(). + + If you define it below the minimal level (one second), the minimal level + will be used. + + Any timeout given to DetectRenderer() less than this value will be upgraded + to it. +*/ +#define MAX_RENDERER_API_DETECT_TIMEOUT 1 + namespace ingame_overlay { std::future DetectRenderer(std::chrono::milliseconds timeout = std::chrono::milliseconds{ -1 }); diff --git a/overlay_experimental/internal_includes.h b/overlay_experimental/internal_includes.h index 16b3291..c764fa9 100644 --- a/overlay_experimental/internal_includes.h +++ b/overlay_experimental/internal_includes.h @@ -38,6 +38,8 @@ #include #endif +#define PRINT_DEBUG_NO_LINENUM(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, a, __VA_ARGS__); fclose(t);} while (0) + #ifndef SPDLOG_TRACE #define SPDLOG_TRACE(x, ...) PRINT_DEBUG(x "\n", __VA_ARGS__) #endif @@ -47,6 +49,9 @@ #ifndef SPDLOG_INFO #define SPDLOG_INFO(x, ...) PRINT_DEBUG(x "\n", __VA_ARGS__) #endif +#ifndef SPDLOG_INFO_NO_NEWLINE +#define SPDLOG_INFO_NO_NEWLINE(x, ...) PRINT_DEBUG_NO_LINENUM(x, __VA_ARGS__) +#endif #ifndef SPDLOG_WARN #define SPDLOG_WARN(x, ...) PRINT_DEBUG(x "\n", __VA_ARGS__) #endif diff --git a/overlay_experimental/steam_overlay.cpp b/overlay_experimental/steam_overlay.cpp index a0205c2..75b59c9 100644 --- a/overlay_experimental/steam_overlay.cpp +++ b/overlay_experimental/steam_overlay.cpp @@ -50,8 +50,26 @@ static constexpr char *valid_languages[] = { "vietnamese" }; +static constexpr char *valid_ui_notification_position_labels[] = { + "top left", + "top right", + "bottom left", + "bottom right" +}; + #define URL_WINDOW_NAME "URL Window" +class Steam_Overlay_CCallback +{ + private: + GOLDBERG_CALLBACK_INTERNAL(Steam_Overlay_CCallback, OnAvatarImageLoaded, AvatarImageLoaded_t); + + public: + bool is_ready() { + return (AvatarImageLoaded_t_is_registered()); + } +}; + int find_free_id(std::vector & ids, int base) { std::sort(ids.begin(), ids.end()); @@ -94,12 +112,366 @@ int find_free_notification_id(std::vector const& notifications) return find_free_id(ids, base_friend_window_id); } +bool operator<(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return ((a.id.ConvertToUint64() < b.id.ConvertToUint64()) && (a.eAvatarSize < b.eAvatarSize)); +} + +bool operator>(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return (b < a); +} + +bool operator<=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a > b); +} + +bool operator>=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a < b); +} + +bool operator==(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return ((a.id.ConvertToUint64() == b.id.ConvertToUint64()) && (a.eAvatarSize == b.eAvatarSize)); +} + +bool operator!=(const Profile_Image_ID &a, const Profile_Image_ID &b) { + return !(a == b); +} + +void Steam_Overlay::populate_initial_profile_images(CSteamID id = k_steamIDNil) { + bool found = false; + if (id == k_steamIDNil) { + id = settings->get_local_steam_id().ConvertToUint64(); + } + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + found = true; + break; + } + } + if (!found) { + profile_images[id] = Profile_Image_Set(); + } + return; +} + #ifdef __WINDOWS__ #include "windows/Windows_Hook.h" #endif #include "notification.h" +bool Steam_Overlay::LoadProfileImage(const CSteamID & id, const int eAvatarSize) { + bool ret = false; + Profile_Image_Set new_images; + memset(&new_images, '\0', sizeof(Profile_Image_Set)); + + ret = LoadProfileImage(id, eAvatarSize, new_images); + if (ret == true) { + std::lock_guard lock(overlay_mutex); + auto entry = profile_images.find(id); + if (entry != profile_images.end()) { + if (eAvatarSize == k_EAvatarSize32x32) { + profile_images[id].small = new_images.small; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + profile_images[id].medium = new_images.medium; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + profile_images[id].large = new_images.large; + } + } + } + } else { + profile_images[id] = new_images; + } + } + + return ret; +} + +bool Steam_Overlay::LoadProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %" PRIu64 " size %d.\n", id.ConvertToUint64(), eAvatarSize); + + bool ret = false; + Profile_Image * image = NULL; + uint32 width = 0; + uint32 height = 0; + Steam_Utils* steamUtils = get_steam_client()->steam_utils; + Steam_Friends* steamFriends = get_steam_client()->steam_friends; + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (image != NULL) { + int image_handle = steamFriends->GetFriendAvatar(id, eAvatarSize); + if (image_handle != 0) { + if (steamUtils->GetImageSize(image_handle, &width, &height) == true && + width > 0 && + height > 0) { + + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %" PRIu64 " size %d image_handle %d width %d height %d.\n", + id.ConvertToUint64(), eAvatarSize, image_handle, width, height); + + if (image->raw_image != NULL) { + delete image->raw_image; + image->raw_image = NULL; + } + image->width = 0; + image->height = 0; + DestroyProfileImageResource(id, eAvatarSize); + + uint8 * raw_image = new uint8[(width * height * sizeof(uint32))]; + if (raw_image != NULL) { + if (steamUtils->GetImageRGBA(image_handle, raw_image, (width * height * sizeof(uint32))) == true) { + image->raw_image = raw_image; + image->width = width; + image->height = height; + + ret = true; + } else { + delete raw_image; + raw_image = NULL; + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %" PRIu64 " size %d could not get pixel data.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + } else { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %" PRIu64 " size %d pixel data has invalid size.\n", + id.ConvertToUint64(), eAvatarSize); + } + } else { + PRINT_DEBUG("Steam_Overlay::LoadProfileImage() profile id %" PRIu64 " size %d profile pixel data not loaded.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + + return ret; +} + +void Steam_Overlay::DestroyProfileImage(const CSteamID & id, const int eAvatarSize) { + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + DestroyProfileImage(id, eAvatarSize, x.second); + break; + } + } + + return; +} + +void Steam_Overlay::DestroyProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::DestroyProfileImage() %" PRIu64 " size %d.\n", id.ConvertToUint64(), eAvatarSize); + + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (image != NULL) { + if (image->raw_image != NULL) { + delete image->raw_image; + image->raw_image = NULL; + } + image->width = 0; + image->height = 0; + } + + return; +} + +bool Steam_Overlay::CreateProfileImageResource(const CSteamID & id, const int eAvatarSize) { + bool ret = false; + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + ret = CreateProfileImageResource(id, eAvatarSize, x.second); + break; + } + } + + return ret; +} + +bool Steam_Overlay::CreateProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() %" PRIu64 " size %d.\n", id.ConvertToUint64(), eAvatarSize); + + bool ret = false; + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (_renderer) { + if (image->raw_image != NULL && image->width > 0 && image->height > 0 && + image->image_resource.expired() == true) { + std::weak_ptr test; + test = _renderer->CreateImageResource(image->raw_image, + image->width, + image->height); + std::shared_ptr test2; + test2 = test.lock(); + if (!test2) { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() Unable to create resource for profile id %" PRIu64 " size %d.\n", + id.ConvertToUint64(), eAvatarSize); + } else { + image->image_resource = test; + ret = true; + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() created resource for profile id %" PRIu64 " size %d -> %" PRIu64 ".\n", + id.ConvertToUint64(), eAvatarSize, *test2); + } + } else { + PRINT_DEBUG("Steam_Overlay::CreateProfileImageResource() invalid raw data for profile id %" PRIu64 " size %d.\n", + id.ConvertToUint64(), eAvatarSize); + } + } + + return ret; +} + +void Steam_Overlay::DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize) { + std::lock_guard lock(overlay_mutex); + + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == id.ConvertToUint64()) { + DestroyProfileImageResource(id, eAvatarSize, x.second); + break; + } + } + + return; +} + +void Steam_Overlay::DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images) +{ + PRINT_DEBUG("Steam_Overlay::DestroyProfileImageResource() %" PRIu64 " size %d.\n", id.ConvertToUint64(), eAvatarSize); + + Profile_Image * image = NULL; + std::lock_guard lock(overlay_mutex); + + if (eAvatarSize == k_EAvatarSize32x32) + image = &images.small; + if (eAvatarSize == k_EAvatarSize64x64) + image = &images.medium; + if (eAvatarSize == k_EAvatarSize184x184) + image = &images.large; + + if (_renderer != NULL && image != NULL && image->image_resource.expired() == false) { + _renderer->ReleaseImageResource(image->image_resource); + image->image_resource.reset(); + } + + return; +} + +void Steam_Overlay::LoadAchievementImage(Overlay_Achievement & ach) +{ + PRINT_DEBUG("LoadAchievementImage() %s.\n", ach.name.c_str()); + + // Image load. + Steam_Utils* steamUtils = get_steam_client()->steam_utils; + Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats; + int32 image_handle = steamUserStats->GetAchievementIcon(ach.name.c_str()); + if (image_handle != 0) { + uint32 width = 0; + uint32 height = 0; + if ((steamUtils->GetImageSize(image_handle, &width, &height) == true) && + (width > 0) && (height > 0)) { + + PRINT_DEBUG("LoadAchievementImage() %d %d %d.\n", image_handle, width, height); + + { + std::lock_guard lock(overlay_mutex); + if (ach.raw_image != NULL) { + delete ach.raw_image; + ach.raw_image = NULL; + } + ach.raw_image_width = 0; + ach.raw_image_height = 0; + DestroyAchievementImageResource(ach); + } + + uint8 * raw_image = new uint8[(width * height * sizeof(uint32))]; + if (raw_image != NULL) { + if (steamUtils->GetImageRGBA(image_handle, + raw_image, + (width * height * sizeof(uint32))) == true) { + PRINT_DEBUG("LoadAchievementImage() %d -> %p.\n", image_handle, raw_image); + { + std::lock_guard lock(overlay_mutex); + ach.raw_image = raw_image; + ach.raw_image_width = width; + ach.raw_image_height = height; + } + } else { + delete raw_image; + PRINT_DEBUG("LoadAchievementImage() Achievement %s could not get pixel data.\n", ach.name.c_str()); + } + } else { + PRINT_DEBUG("LoadAchievementImage() Achievement %s could not allocate memory for pixel data.\n", ach.name.c_str()); + } + } else { + PRINT_DEBUG("LoadAchievementImage() Achievement %s image pixel data has an invalid size.\n", ach.name.c_str()); + } + } else { + PRINT_DEBUG("LoadAchievementImage() Achievement %s is not loaded.\n", ach.name.c_str()); + } +} + +void Steam_Overlay::CreateAchievementImageResource(Overlay_Achievement & ach) +{ + PRINT_DEBUG("CreateAchievementImageResource() %s. %d x %d -> %p\n", ach.name.c_str(), ach.raw_image_width, ach.raw_image_height, ach.raw_image); + + if (_renderer) { + std::lock_guard lock(overlay_mutex); + if (ach.raw_image != NULL && ach.raw_image_width > 0 && ach.raw_image_height > 0 && ach.image_resource.expired() == true) { + std::weak_ptr test; + test = _renderer->CreateImageResource(ach.raw_image, + ach.raw_image_width, + ach.raw_image_height); + ach.image_resource = test; + std::shared_ptr test2; + test2 = test.lock(); + if (!test2) { + PRINT_DEBUG("CreateAchievementImageResource() Unable to create resource for %s.\n", ach.name.c_str()); + } else { + PRINT_DEBUG("CreateAchievementImageResource() created resource for %s -> %PRIu64.\n", ach.name.c_str(), *test2); + } + + } else { + PRINT_DEBUG("CreateAchievementImageResource() invalid raw data for %s.\n", ach.name.c_str()); + } + } +} + +void Steam_Overlay::DestroyAchievementImageResource(Overlay_Achievement & ach) +{ + PRINT_DEBUG("DestroyAchievementImageResource() %s.\n", ach.name.c_str()); + + if (_renderer) { + std::lock_guard lock(overlay_mutex); + if (ach.image_resource.expired() == false) { + _renderer->ReleaseImageResource(ach.image_resource); + ach.image_resource.reset(); + } + } +} + void Steam_Overlay::steam_overlay_run_every_runcb(void* object) { Steam_Overlay* _this = reinterpret_cast(object); @@ -121,18 +493,31 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu setup_overlay_called(false), show_overlay(false), is_ready(false), - notif_position(ENotificationPosition::k_EPositionBottomLeft), + notif_position(ENotificationPosition::k_EPositionTopRight), + current_ui_notification_position_selection(1), // valid_ui_notification_position_labels[1] == "top right" h_inset(0), v_inset(0), overlay_state_changed(false), i_have_lobby(false), show_achievements(false), show_settings(false), + show_profile_image_select(false), + show_drive_list(false), + tried_load_new_profile_image(false), + cleared_new_profile_images_struct(true), _renderer(nullptr), - fonts_atlas(nullptr) + fonts_atlas(nullptr), + earned_achievement_count(0) { strncpy(username_text, settings->get_local_name(), sizeof(username_text)); + show_achievement_desc_on_unlock = settings->get_show_achievement_desc_on_unlock(); + show_achievement_hidden_unearned = settings->get_show_achievement_hidden_unearned(); + + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[1] = false; + radio_btn_new_profile_image_size[2] = true; + if (settings->warn_forced) { this->disable_forced = true; this->warning_forced = true; @@ -147,10 +532,53 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu this->local_save = false; } + int i = 0; + std::string ui_notif_pos = settings->get_ui_notification_position(); + if (ui_notif_pos.length() > 0) { + for (auto n : valid_ui_notification_position_labels) { + if (strcmp(n, ui_notif_pos.c_str()) == 0) { + this->current_ui_notification_position_selection = i; + break; + } + ++i; + } + switch (i) { + case 0: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionTopLeft"); + this->notif_position = k_EPositionTopLeft; + break; + case 1: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionTopRight"); + this->notif_position = k_EPositionTopRight; + break; + case 2: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionBottomLeft"); + this->notif_position = k_EPositionBottomLeft; + break; + case 3: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Got ui notification position from Settings: k_EPositionBottomRight"); + this->notif_position = k_EPositionBottomRight; + break; + default: + PRINT_DEBUG("%s %s. %s.\n", + "Steam_Overlay::Steam_Overlay Unrecognized ui notification position received from Settings: ", + ui_notif_pos.c_str(), + "Defaulting to k_EPositionTopRight"); + break; + }; + } else { + PRINT_DEBUG("%s.\n", + "Steam_Overlay::Steam_Overlay Settings does not have a ui notification position defined. Defaulting to k_EPositionTopRight"); + } + current_language = 0; const char *language = settings->get_language(); - int i = 0; + i = 0; for (auto l : valid_languages) { if (strcmp(l, language) == 0) { current_language = i; @@ -160,13 +588,67 @@ Steam_Overlay::Steam_Overlay(Settings* settings, SteamCallResults* callback_resu ++i; } + populate_initial_profile_images(); + run_every_runcb->add(&Steam_Overlay::steam_overlay_run_every_runcb, this); this->network->setCallback(CALLBACK_ID_STEAM_MESSAGES, settings->get_local_steam_id(), &Steam_Overlay::steam_overlay_callback, this); + this->overlay_CCallback = new Steam_Overlay_CCallback(); } Steam_Overlay::~Steam_Overlay() { + std::lock_guard lock(overlay_mutex); run_every_runcb->remove(&Steam_Overlay::steam_overlay_run_every_runcb, this); + if (this->overlay_CCallback != NULL) { + delete this->overlay_CCallback; + this->overlay_CCallback = NULL; + } + + if (achievements.size()) { + for (auto & x : achievements) { + if (x.raw_image != NULL) { + delete x.raw_image; + x.raw_image = NULL; + x.raw_image_width = 0; + x.raw_image_height = 0; + } + DestroyAchievementImageResource(x); + } + } + + if (profile_images.size()) { + for (auto & x : profile_images) { + if (x.second.small.raw_image != NULL) { + delete x.second.small.raw_image; + x.second.small.raw_image = NULL; + } + x.second.small.width = 0; + x.second.small.height = 0; + + if (x.second.medium.raw_image != NULL) { + delete x.second.medium.raw_image; + x.second.medium.raw_image = NULL; + } + x.second.medium.width = 0; + x.second.medium.height = 0; + + if (x.second.large.raw_image != NULL) { + delete x.second.large.raw_image; + x.second.large.raw_image = NULL; + } + x.second.large.width = 0; + x.second.large.height = 0; + } + } + + DestroyProfileImageResources(); + + new_profile_image_handles.small.raw_image = NULL; // Handle + new_profile_image_handles.medium.raw_image = NULL; // Handle + new_profile_image_handles.large.raw_image = NULL; // Handle + + DestroyTemporaryImageResources(); + DestroyTemporaryImages(); } bool Steam_Overlay::Ready() const @@ -181,7 +663,37 @@ bool Steam_Overlay::NeedPresent() const void Steam_Overlay::SetNotificationPosition(ENotificationPosition eNotificationPosition) { - notif_position = eNotificationPosition; + switch (eNotificationPosition) { + case k_EPositionTopLeft: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionTopLeft"); + this->notif_position = k_EPositionTopLeft; + this->current_ui_notification_position_selection = 0; + break; + case k_EPositionTopRight: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionTopRight"); + this->notif_position = k_EPositionTopRight; + this->current_ui_notification_position_selection = 1; + break; + case k_EPositionBottomLeft: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionBottomLeft"); + this->notif_position = k_EPositionBottomLeft; + this->current_ui_notification_position_selection = 2; + break; + case k_EPositionBottomRight: + PRINT_DEBUG("%s.\n", + "Steam_Overlay::SetNotificationPosition Got ui notification position: k_EPositionBottomRight"); + this->notif_position = k_EPositionBottomRight; + this->current_ui_notification_position_selection = 3; + break; + default: + PRINT_DEBUG("%s %d.\n", + "Steam_Overlay::SetNotificationPosition Unrecognized ui notification position received: ", + eNotificationPosition); + break; + }; } void Steam_Overlay::SetNotificationInset(int nHorizontalInset, int nVerticalInset) @@ -206,7 +718,7 @@ void Steam_Overlay::UnSetupOverlay() { ingame_overlay::StopRendererDetection(); if (!Ready() && future_renderer.valid()) { - if (future_renderer.wait_for(std::chrono::milliseconds{500}) == std::future_status::ready) { + if (future_renderer.wait_for(std::chrono::milliseconds{500} + std::chrono::seconds{MAX_RENDERER_API_DETECT_TIMEOUT}) == std::future_status::ready) { future_renderer.get(); ingame_overlay::FreeDetector(); } @@ -395,7 +907,7 @@ void Steam_Overlay::FriendDisconnect(Friend _friend) friends.erase(it); } -void Steam_Overlay::AddMessageNotification(std::string const& message) +void Steam_Overlay::AddMessageNotification(std::string const& message, CSteamID frd = k_steamIDNil) { std::lock_guard lock(notifications_mutex); int id = find_free_notification_id(notifications); @@ -405,6 +917,7 @@ void Steam_Overlay::AddMessageNotification(std::string const& message) notif.id = id; notif.type = notification_type_message; notif.message = message; + notif.steam_id = frd; notif.start_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); have_notifications = true; @@ -422,8 +935,28 @@ void Steam_Overlay::AddAchievementNotification(nlohmann::json const& ach) Notification notif; notif.id = id; notif.type = notification_type_achievement; - // Load achievement image - notif.message = ach["displayName"].get() + "\n" + ach["description"].get(); + // Achievement image + if (achievements.size() > 0) { + for (auto & x : achievements) { + if (x.name == ach["name"].get()) { + // Reload the image due to the pop. + LoadAchievementImage(x); + DestroyAchievementImageResource(x); + // Cannot call CreateAchievementImageResource(x) here. OpenGL displays bad texture. + notif.ach_name = x.name; + } + } + } + // Achievement count. + if (total_achievement_count > earned_achievement_count) { + earned_achievement_count++; + } + notif.message = "Achievement Unlocked!\n"; + if (show_achievement_desc_on_unlock) { + notif.message += ach["displayName"].get() + "\n" + ach["description"].get(); + } else { + notif.message += "\n" + ach["displayName"].get(); + } notif.start_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); have_notifications = true; @@ -454,6 +987,7 @@ void Steam_Overlay::AddInviteNotification(std::pair(std::chrono::system_clock::now().time_since_epoch()); notifications.emplace_back(notif); have_notifications = true; @@ -546,6 +1080,35 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st std::string friend_window_id = std::move("###" + std::to_string(state.id)); if (ImGui::Begin((state.window_title + friend_window_id).c_str(), &show)) { + ImGuiStyle currentStyle = ImGui::GetStyle(); + ImVec2 image_offset(((ImGui::GetWindowWidth() * 0.04f) + currentStyle.FramePadding.x), + ((ImGui::GetWindowHeight() * 0.04f) + currentStyle.FramePadding.y + + ImGui::GetFontSize() + currentStyle.ItemSpacing.y)); // calc border. + ImVec2 image_max_resolution((ImGui::GetWindowWidth() - image_offset.x), (ImGui::GetWindowHeight() - image_offset.y)); // calc total space for image. + if (image_max_resolution.x > image_max_resolution.y) { // fix image aspect ratio. (square) + image_max_resolution.x = image_max_resolution.x - (image_max_resolution.x - image_max_resolution.y); + } else { + image_max_resolution.y = image_max_resolution.y - (image_max_resolution.y - image_max_resolution.x); + } + ImVec2 image_scale(image_max_resolution.x * 0.3f, + image_max_resolution.y * 0.3f); + ImVec2 text_offset(image_offset.x + image_scale.x + currentStyle.ItemSpacing.x, + image_offset.y + (image_scale.y * 0.2f) + currentStyle.ItemSpacing.y); + ImVec2 next_line_offset(currentStyle.FramePadding.x + currentStyle.ItemInnerSpacing.x, + image_offset.y + image_scale.y + (currentStyle.ItemSpacing.y * 2.0f)); + ImGui::SetCursorPos(image_offset); + + display_imgui_avatar(image_scale.x, + image_scale.y, + 1.0f, 1.0f, 1.0f, 1.0f, + CSteamID((uint64)frd.id()), + k_EAvatarSize184x184, + 0x1); + ImGui::SetCursorPos(text_offset); + + ImGui::TextWrapped(std::string(frd.name() + " (" + std::to_string((uint64)frd.id()) + ")").c_str()); + ImGui::SetCursorPos(next_line_offset); + if (state.window_state & window_state_need_attention && ImGui::IsWindowFocused()) { state.window_state &= ~window_state_need_attention; @@ -590,8 +1153,7 @@ void Steam_Overlay::BuildFriendWindow(Friend const& frd, friend_window_state& st // | [__chat line__] [send] | // |------------------------------| float wnd_width = ImGui::GetWindowContentRegionWidth(); - ImGuiStyle &style = ImGui::GetStyle(); - wnd_width -= ImGui::CalcTextSize("Send").x + style.FramePadding.x * 2 + style.ItemSpacing.x + 1; + wnd_width -= ImGui::CalcTextSize("Send").x + currentStyle.FramePadding.x * 2 + currentStyle.ItemSpacing.x + 1; ImGui::PushItemWidth(wnd_width); if (ImGui::InputText("##chat_line", state.chat_input, max_chat_len, ImGuiInputTextFlags_EnterReturnsTrue)) @@ -636,46 +1198,112 @@ void Steam_Overlay::BuildNotifications(int width, int height) std::queue friend_actions_temp; + ImVec2 notification_rez(((Notification::width >= 1.0f) ? (width / Notification::width) : (width * Notification::width)), + ((Notification::height >= 1.0f) ? (height / Notification::height) : (height * Notification::height))); // calc notification screen size. + { std::lock_guard lock(notifications_mutex); for (auto it = notifications.begin(); it != notifications.end(); ++it, ++i) { auto elapsed_notif = now - it->start_time; - + float alpha = 0; + ImGuiStyle currentStyle = ImGui::GetStyle(); if ( elapsed_notif < Notification::fade_in) { - float alpha = Notification::max_alpha * (elapsed_notif.count() / static_cast(Notification::fade_in.count())); + alpha = Notification::max_alpha * (elapsed_notif.count() / static_cast(Notification::fade_in.count())); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2)); } else if ( elapsed_notif > Notification::fade_out_start) { - float alpha = Notification::max_alpha * ((Notification::show_time - elapsed_notif).count() / static_cast(Notification::fade_out.count())); + alpha = Notification::max_alpha * ((Notification::show_time - elapsed_notif).count() / static_cast(Notification::fade_out.count())); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, alpha)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, alpha*2)); } else { + alpha = Notification::max_alpha; ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, Notification::max_alpha)); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Notification::r, Notification::g, Notification::b, Notification::max_alpha)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 255, 255, Notification::max_alpha*2)); } - - ImGui::SetNextWindowPos(ImVec2((float)width - width * Notification::width, Notification::height * font_size * i )); + + ImVec2 window_pos((float)width - width * Notification::width, Notification::height * font_size * i); + + ImGuiViewport * viewport = ImGui::GetMainViewport(); + if (viewport != NULL) { + if (notif_position == ENotificationPosition::k_EPositionBottomLeft || + notif_position == ENotificationPosition::k_EPositionBottomRight) { + window_pos.y = ((viewport->Pos.y + viewport->Size.y) - Notification::height * font_size) - window_pos.y; + } + } + if (notif_position == ENotificationPosition::k_EPositionTopLeft || + notif_position == ENotificationPosition::k_EPositionBottomLeft) { + window_pos.x = 0; + } + + // Avoid overlay titlebar. + if (show_overlay == true && + (notif_position == ENotificationPosition::k_EPositionTopLeft || + notif_position == ENotificationPosition::k_EPositionTopRight)) { + window_pos.x += currentStyle.FramePadding.x + currentStyle.ItemSpacing.x; + window_pos.y += currentStyle.FramePadding.y + ImGui::GetFontSize() + currentStyle.ItemSpacing.y; + } + + ImGui::SetNextWindowPos(window_pos); ImGui::SetNextWindowSize(ImVec2( width * Notification::width, Notification::height * font_size )); ImGui::Begin(std::to_string(it->id).c_str(), nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoDecoration); + // Notification image. + ImVec4 image_color_multipler(1, 1, 1, (alpha*2)); // fade in / out. + ImVec2 image_offset(((notification_rez.x * 0.04f) + currentStyle.FramePadding.x), ((notification_rez.y * 0.04f) + currentStyle.FramePadding.y)); // calc border. + ImVec2 image_max_resolution((notification_rez.x - image_offset.x), (notification_rez.y - image_offset.y)); // calc total space for image. + if (image_max_resolution.x > image_max_resolution.y) { // fix image aspect ratio. (square) + image_max_resolution.x = image_max_resolution.x - (image_max_resolution.x - image_max_resolution.y); + } else { + image_max_resolution.y = image_max_resolution.y - (image_max_resolution.y - image_max_resolution.x); + } + ImVec2 image_scale(image_max_resolution.x * 0.4f, + image_max_resolution.y * 0.4f); + ImVec2 text_offset(image_offset.x + currentStyle.ItemSpacing.x + image_scale.x, image_offset.y); + ImGui::SetCursorPos(image_offset); + switch (it->type) { case notification_type_achievement: + for (auto &a : achievements) { + if (a.name == it->ach_name) { + CreateAchievementImageResource(a); + if ((a.image_resource.expired() == false) && + (a.raw_image_width > 0) && + (a.raw_image_height > 0)) { + std::shared_ptr s_ptr = a.image_resource.lock(); + ImGui::Image((ImTextureID)(intptr_t)*s_ptr, image_scale, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), image_color_multipler); + } + break; + } + } + ImGui::SetCursorPos(text_offset); + ImGui::PushTextWrapPos(0.0f); ImGui::TextWrapped("%s", it->message.c_str()); + ImGui::PopTextWrapPos(); break; case notification_type_invite: { + display_imgui_avatar(image_scale.x, + image_scale.y, + image_color_multipler.x, // r + image_color_multipler.y, // g + image_color_multipler.z, // b + image_color_multipler.w, // a + it->steam_id, + k_EAvatarSize184x184, + 0x1); + ImGui::SetCursorPos(text_offset); ImGui::TextWrapped("%s", it->message.c_str()); if (ImGui::Button("Join")) { @@ -686,7 +1314,18 @@ void Steam_Overlay::BuildNotifications(int width, int height) } break; case notification_type_message: - ImGui::TextWrapped("%s", it->message.c_str()); break; + display_imgui_avatar(image_scale.x, + image_scale.y, + image_color_multipler.x, // r + image_color_multipler.y, // g + image_color_multipler.z, // b + image_color_multipler.w, // a + it->steam_id, + k_EAvatarSize184x184, + 0x1); + ImGui::SetCursorPos(text_offset); + ImGui::TextWrapped("%s", it->message.c_str()); + break; } ImGui::End(); @@ -709,6 +1348,238 @@ void Steam_Overlay::BuildNotifications(int width, int height) } } +#if defined(__WINDOWS__) +struct cb_font_str +{ + float font_size; + ImFontConfig fontcfg; + ImFontAtlas * atlas; + ImFont * defaultFont; + HDC hDevice; + int foundBits; + std::recursive_mutex mutex; +}; + +static cb_font_str CBSTR; + +DWORD LoadWindowsFontFromMem_GetSize_Helper(const LOGFONT *lf, DWORD * type) { + DWORD ret = 0; + + if (lf != NULL && type != NULL) { + ret = GetFontData(CBSTR.hDevice, 0x66637474, 0, NULL, 0); + if (ret != GDI_ERROR && ret > 4) { + *type = 0x66637474; + } else { + ret = GetFontData(CBSTR.hDevice, 0, 0, NULL, 0); + if (ret != GDI_ERROR && ret > 4) { + *type = 0x0; + } else { + *type = ~((DWORD)0); + } + } + PRINT_DEBUG("%s %s %d 0x%x\n", "LoadWindowsFontFromMem_GetSize_Helper Result for font", lf->lfFaceName, ret, *type); + } + + return ret; +} + +int LoadWindowsFontFromMem(const LOGFONT *lf) +{ + int ret = 0; + HFONT hFont = NULL; + HGDIOBJ oldFont = NULL; + + if (lf != NULL) { + hFont = CreateFontIndirect(lf); + if (hFont != NULL) { + oldFont = SelectObject(CBSTR.hDevice, hFont); + uint8_t metsize = GetOutlineTextMetrics(CBSTR.hDevice, 0, NULL); + if (metsize > 0) { + OUTLINETEXTMETRIC * metric = (OUTLINETEXTMETRIC*)malloc(metsize); + if (metric != NULL) { + memset(metric, '\0', metsize); + if (GetOutlineTextMetrics(CBSTR.hDevice, metsize, metric) != 0) { + // otmfsType: Bit 1 (May not be embedded if set.) Bit 2 (Read-Only embedding.) + // See also: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-outlinetextmetrica + if ((((UINT)(metric->otmfsType)) & ((UINT)0x1) << 1) == 0) { + DWORD type = 0; + DWORD fontDataSize = LoadWindowsFontFromMem_GetSize_Helper(lf, &type); + if (fontDataSize != GDI_ERROR && fontDataSize > 4 && type != ~((DWORD)0)) { + uint8_t * fontData = (uint8*)malloc(fontDataSize); + if (fontData != NULL) { + ImFont * font = NULL; + DWORD fontDataRet = GetFontData(CBSTR.hDevice, type, 0, fontData, fontDataSize); + if (fontDataRet != GDI_ERROR && fontDataRet == fontDataSize) { + + if (memcmp("\0\0\0\0", fontData, sizeof("\0\0\0\0")) == 0) { + PRINT_DEBUG("%s\n", "Invalid font tag. Skipping."); + } else { + + if (type == 0x66637474) { + if (memcmp(fontData, "ttcf", sizeof("ttcf")) != 0) { + PRINT_DEBUG("TAG %x %x %x %x %s\n", fontData[0], fontData[1], fontData[2], fontData[3], "NOT A TrueType Collection file, despite detection result."); + memset(fontData, '\0', fontDataSize); + free(fontData); + fontData = NULL; + } else { + PRINT_DEBUG("%s\n", "Found TrueType Collection file."); + } + } + + if (fontData != NULL) { + PRINT_DEBUG("%s %s %s\n", "Font considered valid. Attempting to import", lf->lfFaceName, "into ImGUI."); + if ((CBSTR.foundBits & 0xF) != 0x0) { + CBSTR.fontcfg.MergeMode = true; + PRINT_DEBUG("%s\n", "Merging fonts."); + } + + font = CBSTR.atlas->AddFontFromMemoryTTF(fontData, fontDataSize, CBSTR.font_size, &(CBSTR.fontcfg)); + if (font != NULL) { + PRINT_DEBUG("%s %s %s\n", "ImGUI loaded font", lf->lfFaceName, "successfully."); + if (CBSTR.defaultFont == NULL) { + CBSTR.defaultFont = font; + } + ret = 1; + } else { + PRINT_DEBUG("%s %s.\n", "ImGUI failed to load font", lf->lfFaceName); + } + } + + } + + } else { + PRINT_DEBUG("%s %d.\n", "GetFontData() failed. Ret: ", fontDataRet); + } + + if (font == NULL) { + if (fontData != NULL) { + memset(fontData, '\0', fontDataSize); + free(fontData); + fontData = NULL; + } + } + } + } else { + PRINT_DEBUG("%s %d.\n", "GetFontData() failed. Unable to get initial size of font data. Ret: ", fontDataSize); + } + } else { + PRINT_DEBUG("%s %s. otmfsType data: %d.\n", "Licensing failure. Cannot use font", lf->lfFaceName, metric->otmfsType); + } + } else { + PRINT_DEBUG("%s %s.\n", + "Steam_Overlay::LoadWindowsFontFromMem GetOutlineTextMetrics() (fill font metric struct) failed for", + lf->lfFaceName); + } + + free(metric); + metric = NULL; + } + } else { + PRINT_DEBUG("%s %s.\n", + "Steam_Overlay::LoadWindowsFontFromMem GetOutlineTextMetrics() (get struct size) failed for", + lf->lfFaceName); + } + SelectObject(CBSTR.hDevice, oldFont); + DeleteObject(hFont); + } else { + PRINT_DEBUG("%s %s.\n", + "Steam_Overlay::LoadWindowsFontFromMem CreateFontIndirect() failed for", + lf->lfFaceName); + } + } + return ret; +} + +int CALLBACK cb_enumfonts(const LOGFONT *lf, const TEXTMETRIC *tm, DWORD fontType, LPARAM UNUSED) +{ + int ret = 1; // Continue iteration. + cb_font_str * cbStr = &CBSTR; + std::lock_guard lock(cbStr->mutex); + if (CBSTR.atlas != NULL && lf != NULL && fontType == TRUETYPE_FONTTYPE) { + /* + foundBits: + Bit 1: Loaded ANSI font. + Bit 2: Loaded Japanese font. + Bit 4: Loaded Chinese font. + Bit 8: Loaded Korean font. + Bit 10: Loaded preferred ANSI font. + Bit 20: Loaded preferred CJ font. + Bit 40: Loaded preferred K font. + Bit 80: Searched for preferred fonts. + */ + if ((CBSTR.foundBits & 0x80) == 0) { + // Load ANSI first, or the other fonts will define the glyphs for english text. + if ((CBSTR.foundBits & 0x1) == 0) { + // preferred ANSI font. + if ((strncmp(lf->lfFaceName, "Microsoft Sans Serif", sizeof("Microsoft Sans Serif")) == 0) && ((CBSTR.foundBits & 0x10) == 0)) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x10 | 0x1; + ret = 0; + PRINT_DEBUG("%s %s.\n", "Loaded preferred font:", lf->lfFaceName); + } + } + } else { + // preferred CJ font. + if ((strncmp(lf->lfFaceName, "SimSun", sizeof("SimSun")) == 0) && ((CBSTR.foundBits & 0x20) == 0)) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x20 | 0x2 | 0x4; + PRINT_DEBUG("%s %s.\n", "Loaded preferred font:", lf->lfFaceName); + } + } + // preferred K font. + if ((strncmp(lf->lfFaceName, "Malgun Gothic", sizeof("Malgun Gothic")) == 0) && ((CBSTR.foundBits & 0x40) == 0)) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x40 | 0x8; + PRINT_DEBUG("%s %s.\n", "Loaded preferred font:", lf->lfFaceName); + } + } + } + } else { + // Load ANSI first, or the other fonts will define the glyphs for english text. + if ((CBSTR.foundBits & 0x1) == 0) { + if (lf->lfCharSet == ANSI_CHARSET) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x1; + ret = 0; + PRINT_DEBUG("%s %s.\n", "Loaded ANSI_CHARSET:", lf->lfFaceName); + } + } + } else { + if ((CBSTR.foundBits & 0x2) == 0) { + if (lf->lfCharSet == SHIFTJIS_CHARSET) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x2; + PRINT_DEBUG("%s %s.\n", "Loaded SHIFTJIS_CHARSET:", lf->lfFaceName); + } + } + } + if ((CBSTR.foundBits & 0x4) == 0) { + if (lf->lfCharSet == CHINESEBIG5_CHARSET) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x4; + PRINT_DEBUG("%s %s.\n", "Loaded CHINESEBIG5_CHARSET:", lf->lfFaceName); + } + } + } + if ((CBSTR.foundBits & 0x8) == 0) { + if (lf->lfCharSet == HANGUL_CHARSET) { + if (LoadWindowsFontFromMem(lf) == 1) { + CBSTR.foundBits = CBSTR.foundBits | 0x8; + PRINT_DEBUG("%s %s.\n", "Loaded HANGUL_CHARSET:", lf->lfFaceName); + } + } + } + } + } + } + if ((CBSTR.foundBits & 0xF) == 0xF) { + PRINT_DEBUG("%s\n", "Loaded all needed fonts."); + ret = 0; // Stop iteration. + } + return ret; +} +#endif + void Steam_Overlay::CreateFonts() { if (fonts_atlas) return; @@ -745,7 +1616,100 @@ void Steam_Overlay::CreateFonts() ImFont *font = NULL; #if defined(__WINDOWS__) - font = Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\micross.ttf", font_size, &fontcfg); + // Do this instead of calling Fonts->AddFontFromFileTTF(). As *some* Windows implementations don't ship anything in C:\Windows\Fonts. + HDC oDC = GetDC(NULL); + HWND oWND = WindowFromDC(oDC); + int caps = GetDeviceCaps(oDC, RASTERCAPS); + if (caps != 0) { + int width = GetDeviceCaps(oDC, HORZRES); + int height = GetDeviceCaps(oDC, VERTRES); + HBITMAP hBitmap = CreateCompatibleBitmap(oDC, width, height); + + LOGFONT lf; + memset(&lf, '\0', sizeof(LOGFONT)); + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = font_size; + lf.lfQuality = ANTIALIASED_QUALITY; + HGDIOBJ hOldBitmap; + { + std::lock_guard lock(CBSTR.mutex); + CBSTR.font_size = font_size; + CBSTR.fontcfg = fontcfg; + CBSTR.atlas = Fonts; + CBSTR.defaultFont = NULL; + CBSTR.hDevice = CreateCompatibleDC(oDC); + CBSTR.foundBits = 0; + hOldBitmap = SelectObject(CBSTR.hDevice, hBitmap); + DeleteObject(hOldBitmap); + } + + PRINT_DEBUG("%s\n", "Atempting to load preferred ANSI font from Win32 API."); + EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); + bool resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0x1) != 0x1) { + CBSTR.foundBits = CBSTR.foundBits | 0x80; + resume_search = true; + } + } + if (resume_search) { + PRINT_DEBUG("%s\n", "Atempting to load generic ANSI font from Win32 API."); + EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0x1) != 0x1) { + PRINT_DEBUG("%s\n", "Falling back to built in ImGUI ANSI font."); + CBSTR.defaultFont = Fonts->AddFontDefault(&fontcfg); + if (CBSTR.defaultFont == NULL) { + PRINT_DEBUG("%s\n", "Built in ImGUI ANSI font failed to load. Weird text will probably happen."); + } + CBSTR.foundBits = CBSTR.foundBits | 0x1; + } + CBSTR.foundBits = CBSTR.foundBits ^ 0x80; + } + } + resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + if ((CBSTR.foundBits & 0xE) != 0xE) { + resume_search = true; + } + } + if (resume_search) { + PRINT_DEBUG("%s\n", "Atempting to load preferred CJK fonts from Win32 API."); + EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); + } + resume_search = false; + { + std::lock_guard lock(CBSTR.mutex); + CBSTR.foundBits = CBSTR.foundBits | 0x80; + if ((CBSTR.foundBits & 0xF) != 0xF) { + resume_search = true; + } + } + if (resume_search) { + PRINT_DEBUG("%s\n", "Loading generic CJK fonts from Win32 API."); + EnumFontFamiliesExA(CBSTR.hDevice, &lf, cb_enumfonts, 0, 0); + } + { + std::lock_guard lock(CBSTR.mutex); + if (CBSTR.defaultFont != NULL) { + font = CBSTR.defaultFont; + } + if (need_extra_fonts == true) { + if ((CBSTR.foundBits & 0xF) == 0xF) { + need_extra_fonts = false; + } + } + + DeleteDC(CBSTR.hDevice); // Order is important. + DeleteObject(hBitmap); + } + } + ReleaseDC(oWND, oDC); +#else + font = Fonts->AddFontFromFileTTF("/usr/share/fonts/truetype/roboto/unhinted/RobotoCondensed-Regular.ttf", font_size, &fontcfg); #endif if (!font) { @@ -755,11 +1719,12 @@ void Steam_Overlay::CreateFonts() font_notif = font_default = font; if (need_extra_fonts) { - PRINT_DEBUG("loading extra fonts\n"); - fontcfg.MergeMode = true; #if defined(__WINDOWS__) - Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\simsun.ttc", font_size, &fontcfg); - Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\malgun.ttf", font_size, &fontcfg); + PRINT_DEBUG("%s\n", "Unable to load extra fonts! Some text may not be readable!"); +#else + PRINT_DEBUG("%s\n", "loading extra fonts."); + fontcfg.MergeMode = true; + Fonts->AddFontFromFileTTF("/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", font_size, &fontcfg); #endif } @@ -771,6 +1736,647 @@ void Steam_Overlay::CreateFonts() reset_LastError(); } +void Steam_Overlay::ReturnTemporaryImage(Profile_Image & imageData) +{ + if (imageData.raw_image != NULL) { + delete imageData.raw_image; + imageData.raw_image = NULL; + } + imageData.width = 0; + imageData.height = 0; + return; +} + +Profile_Image Steam_Overlay::GetTemporaryImage(uint8 * imageData) +{ + Profile_Image ret; + + if (imageData != NULL) { + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.first == imageData && + x.second.image_data.raw_image != NULL && + x.second.image_data.width > 0 && + x.second.image_data.height > 0) { + size_t buffer_size = x.second.image_data.width * x.second.image_data.height * sizeof(uint32_t); + ret.raw_image = new uint8[buffer_size]; + if (ret.raw_image != NULL) { + for (size_t y = 0; y < buffer_size; y++) { + uint8 a = x.second.image_data.raw_image[y]; + ret.raw_image[y] = a; + } + ret.width = x.second.image_data.width; + ret.height = x.second.image_data.height; + x.second.last_display_time = std::chrono::steady_clock::now(); + } + break; + } + } + } + + return ret; +} + +void Steam_Overlay::PruneTemporaryImages() +{ + std::lock_guard lock(overlay_mutex); + if (temp_display_images.size() > 0) { + for (auto & x : temp_display_images) { + if (x.second.last_display_time < (std::chrono::steady_clock::now() - std::chrono::minutes(1))) { + if (x.second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x.second.image_data.image_resource); + x.second.image_data.image_resource.reset(); + } + } + if (x.second.image_data.raw_image != NULL) { + delete x.second.image_data.raw_image; + x.second.image_data.raw_image = NULL; + } + x.second.image_data.width = 0; + x.second.image_data.height = 0; + } + } + + bool done = false; + do { + if (temp_display_images.size() > 0) { + for (std::map::iterator x = temp_display_images.begin(); + x != temp_display_images.end(); + x++) { + std::map::iterator y = x; + y++; + if (y == temp_display_images.end()) { + done = true; + } + if ((x->second.image_data.image_resource.expired() == true) && + x->second.image_data.raw_image == NULL && + x->second.image_data.width <= 0 && + x->second.image_data.height <= 0) { + temp_display_images.erase(x); + break; + } + } + } else { + done = true; + } + } while (!done); + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImageResources() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x.second.image_data.image_resource); + x.second.image_data.image_resource.reset(); + } + } + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImageResource(uint8 * imageData) +{ + std::lock_guard lock(overlay_mutex); + auto x = temp_display_images.find(imageData); + if (x != temp_display_images.end()) { + if (x->second.image_data.image_resource.expired() == false) { + if (_renderer) { + _renderer->ReleaseImageResource(x->second.image_data.image_resource); + x->second.image_data.image_resource.reset(); + } + } + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImages() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : temp_display_images) { + if (x.second.image_data.raw_image != NULL) { + delete x.second.image_data.raw_image; + x.second.image_data.raw_image = NULL; + } + x.second.image_data.width = 0; + x.second.image_data.height = 0; + } + + return; +} + +void Steam_Overlay::DestroyTemporaryImage(uint8 * imageData) +{ + std::lock_guard lock(overlay_mutex); + DestroyTemporaryImageResource(imageData); + auto x = temp_display_images.find(imageData); + if (x != temp_display_images.end()) { + if (x->second.image_data.raw_image != NULL) { + delete x->second.image_data.raw_image; + x->second.image_data.raw_image = NULL; + } + x->second.image_data.width = 0; + x->second.image_data.height = 0; + temp_display_images.erase(x); + } + + return; +} + +void Steam_Overlay::DestroyProfileImageResources() +{ + std::lock_guard lock(overlay_mutex); + for (auto & x : profile_images) { + if (x.second.small.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize32x32, x.second); + } + } + if (x.second.medium.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize64x64, x.second); + } + } + if (x.second.large.image_resource.expired() == false) { + if (_renderer) { + DestroyProfileImageResource(x.first, k_EAvatarSize184x184, x.second); + } + } + } + + return; +} + +void Steam_Overlay::DestroyAchievementImageResources() +{ + for (auto & x : achievements) { + if (x.image_resource.expired() == false) { + DestroyAchievementImageResource(x); + } + } + + return; +} + +int Steam_Overlay::display_imgui_achievement(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + uint32_t loadType = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeAchievement, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + achName, + k_steamIDNil, + k_EAvatarSizeMAX, + NULL, + 0x0, + 0x0, + 0x0, + loadType); +} + +int Steam_Overlay::display_imgui_avatar(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + CSteamID userID = k_steamIDNil, + int eAvatarSize = k_EAvatarSizeMAX, + uint32_t loadType = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeAvatar, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + "", + userID, + eAvatarSize, + NULL, + 0x0, + 0x0, + 0x0, + loadType); +} + +int Steam_Overlay::display_imgui_custom_image(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + uint8 * imageData = NULL, + uint32_t imageDataLength = 0x0, + uint32_t imageDataWidth = 0x0, + uint32_t imageDataHeight = 0x0) +{ + return Steam_Overlay::display_imgui_image(displayImageTypeCustom, + xSize, + ySize, + image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a, + "", + k_steamIDNil, + k_EAvatarSizeMAX, + imageData, + imageDataLength, + imageDataWidth, + imageDataHeight, + 0x0); +} + +int Steam_Overlay::display_imgui_image(uint32_t displayImageType, + float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName = "", + CSteamID userID = k_steamIDNil, + int eAvatarSize = k_EAvatarSizeMAX, + uint8 * imageData = NULL, + uint32_t imageDataLength = 0x0, + uint32_t imageDataWidth = 0x0, + uint32_t imageDataHeight = 0x0, + uint32_t loadType = 0x0) +{ + int ret = 0; + bool valid_args = false; + ImVec2 image_size(xSize, ySize); + ImVec4 image_color_multipler(image_color_multipler_r, + image_color_multipler_g, + image_color_multipler_b, + image_color_multipler_a); + + switch (displayImageType) { + case displayImageTypeAchievement: + // Achievements + if (achName != "") { + valid_args = true; + for (auto & x : achievements) { + if (x.name == achName) { + if (loadType == 0x0) { + if (x.raw_image == NULL) { + LoadAchievementImage(x); + } + } + + if (x.image_resource.expired() == true) { + CreateAchievementImageResource(x); + } + + if ((x.image_resource.expired() == false) && + (x.raw_image_width > 0) && + (x.raw_image_height > 0)) { + std::shared_ptr s_ptr = x.image_resource.lock(); + ImGui::Image((ImTextureID)(intptr_t)*s_ptr, + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + break; + } + } + } + if (ret != 1) { + ImGui::Dummy(image_size); + } + break; + case displayImageTypeAvatar: + // User Avatars + if (userID != k_steamIDNil) { + bool found_profile_images = false; + Profile_Image image; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + + if (found_profile_images == false) { + populate_initial_profile_images(userID); + } else { + if (valid_args) { + bool reload = false; + if (image.raw_image == NULL || + image.width == 0 || + image.height == 0) { + if (loadType == 0x0) { + if (LoadProfileImage(userID, eAvatarSize) == true) { + reload = true; + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Got avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Load OK."); + } else { + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Load Failure."); + } + } else { + uint32_t type = 0x0; + switch (eAvatarSize) { + case k_EAvatarSize32x32: + type |= 0x1; + break; + case k_EAvatarSize64x64: + type |= 0x2; + break; + case k_EAvatarSize184x184: + type |= 0x4; + break; + default: + type = 0x0; + break; + }; + + if (type != 0x0) { + PRINT_DEBUG("%s %" PRIu64 " %s %d.\n", + "Steam_Overlay::display_imgui_image Queuing Lazy Load avatar image for", + userID.ConvertToUint64(), + "size", + eAvatarSize); + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + auto ld = lazy_load_avatar_images.find(userID); + if (ld == lazy_load_avatar_images.end()) { + type |= lazy_load_avatar_images[userID] & 0x7; + lazy_load_avatar_images[userID] = type; + } else { + lazy_load_avatar_images[userID] = type; + } + } + } + } + + // Re-get list in case the map buffer was reallocated. + if (reload) { + found_profile_images = false; + valid_args = false; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + } + + reload = false; + if (found_profile_images && valid_args && image.raw_image != NULL && image.width > 0 && image.height > 0) { + //if (!image.image_resource) { + if (image.image_resource.expired() == true) { + + if (CreateProfileImageResource(userID, eAvatarSize) == true) { + reload = true; + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Got profile image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource OK."); + } else { + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource creation failure."); + } + } + + // Re-get list in case the image resource was reallocated. + if (reload) { + found_profile_images = false; + valid_args = false; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + for (auto & x : profile_images) { + if (x.first.ConvertToUint64() == userID.ConvertToUint64()) { + if (eAvatarSize == k_EAvatarSize32x32) { + image = x.second.small; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize64x64) { + image = x.second.medium; + valid_args = true; + } else { + if (eAvatarSize == k_EAvatarSize184x184) { + image = x.second.large; + valid_args = true; + } else { + valid_args = false; + } + } + } + found_profile_images = true; + break; + } + } + } + } + + if (valid_args && found_profile_images && image.image_resource.expired() == false) { + std::shared_ptr test2; + test2 = image.image_resource.lock(); + if (test2) { + ImGui::Image((ImTextureID)(intptr_t)*(test2), + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + } else { + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Unable to get avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Resource Invalid."); + } + } else { + if (loadType == 0x0) { + PRINT_DEBUG("%s %d %s %" PRIu64 " %s\n", + "Steam_Overlay::display_imgui_image Unable to display avatar image size", + eAvatarSize, + "for user", + userID.ConvertToUint64(), + ". Could not find buffer after pixel data load."); + } + } + } + } + } + break; + case displayImageTypeCustom: + // Custom image. + if (imageData != NULL) { + valid_args = true; + { + Temporary_Image new_temp_image; + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end()) { + result->second.last_display_time = std::chrono::steady_clock::now(); + } else { + if (imageDataLength > 0) { + uint8 * new_pixels = new uint8[(imageDataWidth * imageDataHeight * sizeof(uint32_t))]; + if (new_pixels != NULL) { + for (size_t x = 0; x < (imageDataWidth * imageDataHeight * sizeof(uint32_t)) && x < imageDataLength; x++) { + new_pixels[x] = imageData[x]; + } + new_temp_image.image_data.raw_image = new_pixels; + new_temp_image.image_data.width = imageDataWidth; + new_temp_image.image_data.height = imageDataHeight; + new_temp_image.last_display_time = std::chrono::steady_clock::now(); + temp_display_images[imageData] = new_temp_image; + } else { + valid_args = false; + PRINT_DEBUG("%s %p. %s.\n", + "Steam_Overlay::display_imgui_image Unable to display custom image", + imageData, + "Could not allocate memory"); + } + } else { + valid_args = false; + PRINT_DEBUG("%s %p. %s.\n", + "Steam_Overlay::display_imgui_image Unable to display custom image", + imageData, + "Image length not given, and image is not registered in temporary mappings"); + } + } + } + if (valid_args == true) { + { + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end()) { + if (result->second.image_data.image_resource.expired() == true) { + if (_renderer && + result->second.image_data.raw_image != NULL && + result->second.image_data.width > 0 && + result->second.image_data.height > 0) { + std::weak_ptr test; + test = _renderer->CreateImageResource(result->second.image_data.raw_image, + result->second.image_data.width, + result->second.image_data.height); + std::shared_ptr test2; + test2 = test.lock(); + if (!test2) { + PRINT_DEBUG("Steam_Overlay::display_imgui_image Unable to create resource for custom image %p.\n", + imageData); + _renderer->ReleaseImageResource(test); + } else { + PRINT_DEBUG("Steam_Overlay::display_imgui_image created resource for custom image %p -> %" PRIu64 ".\n", + imageData, + *test2); + result->second.image_data.image_resource = test; + } + } + } + } + } + { + std::lock_guard lock(overlay_mutex); + auto result = temp_display_images.find(imageData); + if (result != temp_display_images.end() && + result->second.image_data.raw_image != NULL && + result->second.image_data.width > 0 && + result->second.image_data.height > 0 && + result->second.image_data.image_resource.expired() == false) { + std::shared_ptr test2; + test2 = result->second.image_data.image_resource.lock(); + if (test2) { + ImGui::Image((ImTextureID)(intptr_t)*(test2), + image_size, + ImVec2(0.0f, 0.0f), + ImVec2(1.0f, 1.0f), + image_color_multipler); + ret = 1; + } + } + } + } + } + break; + case displayImageTypeEND: + default: + PRINT_DEBUG("%s %d.\n", + "Steam_Overlay::display_imgui_image Unrecognized displayImageType: ", + displayImageType); + break; + }; + + return ret; +} + // Try to make this function as short as possible or it might affect game's fps. void Steam_Overlay::OverlayProc() { @@ -801,86 +2407,157 @@ void Steam_Overlay::OverlayProc() if (ImGui::Begin("SteamOverlay", &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus)) { - ImGui::LabelText("##label", "Username: %s(%llu) playing %u", - settings->get_local_name(), - settings->get_local_steam_id().ConvertToUint64(), - settings->get_local_game_id().AppID()); - ImGui::SameLine(); + bool user_image_displayed = false; + CSteamID local_user_id = settings->get_local_steam_id(); + ImGuiStyle currentStyle = ImGui::GetStyle(); - ImGui::LabelText("##label", "Renderer: %s", (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str())); + user_image_displayed = (display_imgui_avatar(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize184x184) == 1); - ImGui::Spacing(); - if (ImGui::Button("Show Achievements")) { - show_achievements = true; - } - - ImGui::SameLine(); - - if (ImGui::Button("Settings")) { - show_settings = true; - } - - ImGui::Spacing(); - ImGui::Spacing(); - - ImGui::LabelText("##label", "Friends"); - - std::lock_guard lock(overlay_mutex); - if (!friends.empty()) { - if (ImGui::ListBoxHeader("##label", friends.size())) + std::lock_guard lock(overlay_mutex); + + ImGui::LabelText("##label", "Username: %s(%llu) playing %u", + settings->get_local_name(), + local_user_id.ConvertToUint64(), + settings->get_local_game_id().AppID()); + ImGui::SameLine(); + + ImGui::LabelText("##label", "Renderer: %s", (_renderer == nullptr ? "Unknown" : _renderer->GetLibraryName().c_str())); + + if (total_achievement_count > 0) { + ImGui::Text("Achievements earned: %d / %d", earned_achievement_count, total_achievement_count); + ImGui::SameLine(); + ImGui::ProgressBar((earned_achievement_count / total_achievement_count), ImVec2((io.DisplaySize.x * 0.20f),0)); + } else { + ImGui::Text("Achievements not loaded. Unable to display statistics."); + } + + ImGui::Spacing(); + if (total_achievement_count <= 0) { + ImGui::BeginDisabled(); + } + if (ImGui::Button("Show Achievements")) { + show_achievements = true; + } + if (total_achievement_count <= 0) { + ImGui::EndDisabled(); + } + + ImGui::SameLine(); + + if (ImGui::Button("Settings")) { + show_settings = true; + } + + ImGui::Spacing(); + ImGui::Spacing(); + + ImGui::LabelText("##label", "Friends"); + + if (!friends.empty()) { - std::for_each(friends.begin(), friends.end(), [this](std::pair &i) + if (ImGui::ListBoxHeader("##label", friends.size())) { - ImGui::PushID(i.second.id-base_friend_window_id+base_friend_item_id); - - ImGui::Selectable(i.second.window_title.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick); - BuildContextMenu(i.first, i.second); - if (ImGui::IsItemClicked() && ImGui::IsMouseDoubleClicked(0)) + std::for_each(friends.begin(), friends.end(), [this](std::pair &i) { - i.second.window_state |= window_state_show; - } - ImGui::PopID(); + ImGui::PushID(i.second.id-base_friend_window_id+base_friend_item_id); - BuildFriendWindow(i.first, i.second); - }); - ImGui::ListBoxFooter(); - } - } + // Selectable "" + SameLine + Image + Text == all highlighted. + // See also: https://github.com/ocornut/imgui/issues/1658#issuecomment-373731255 + ImGui::Selectable("", false, ImGuiSelectableFlags_AllowDoubleClick); + BuildContextMenu(i.first, i.second); + if (ImGui::IsItemClicked() && ImGui::IsMouseDoubleClicked(0)) + { + i.second.window_state |= window_state_show; + } + ImGui::PopID(); - if (show_achievements && achievements.size()) { - ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 32, ImGui::GetFontSize() * 32), ImVec2(8192, 8192)); - bool show = show_achievements; - if (ImGui::Begin("Achievement Window", &show)) { - ImGui::Text("List of achievements"); - ImGui::BeginChild("Achievements"); - for (auto & x : achievements) { - bool achieved = x.achieved; - bool hidden = x.hidden && !achieved; + ImGui::SameLine(); - ImGui::Separator(); - ImGui::Text("%s", x.title.c_str()); - if (hidden) { - ImGui::Text("hidden achievement"); - } else { - ImGui::TextWrapped("%s", x.description.c_str()); - } + if (display_imgui_avatar(32 * 0.4f, 32 * 0.4f, + 1.0f, 1.0f, 1.0f, 1.0f, + CSteamID((uint64)i.first.id()), + k_EAvatarSize32x32, + 0x1) != 1) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(32 * 0.4f, 32 * 0.4f)); + PRINT_DEBUG("%s %" PRIu64 ".\n", + "SteamOverlay::OverlayProc Failed to display friend avatar for", + (uint64)i.first.id()); + } + ImGui::SameLine(); - if (achieved) { - char buffer[80] = {}; - time_t unlock_time = (time_t)x.unlock_time; - std::strftime(buffer, 80, "%Y-%m-%d at %H:%M:%S", std::localtime(&unlock_time)); + ImGui::Text(i.second.window_title.c_str()); - ImGui::TextColored(ImVec4(0, 255, 0, 255), "achieved on %s", buffer); - } else { - ImGui::TextColored(ImVec4(255, 0, 0, 255), "not achieved"); - } - ImGui::Separator(); + BuildFriendWindow(i.first, i.second); + }); + ImGui::ListBoxFooter(); } - ImGui::EndChild(); } - ImGui::End(); - show_achievements = show; + + if (show_achievements && achievements.size()) { + ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 32, ImGui::GetFontSize() * 32), ImVec2(8192, 8192)); + bool show = show_achievements; + if (ImGui::Begin("Achievement Window", &show)) { + ImGui::Text("List of achievements"); + ImGui::BeginChild("Achievements"); + float window_x_offset = (ImGui::GetWindowWidth() > currentStyle.ScrollbarSize) ? (ImGui::GetWindowWidth() - currentStyle.ScrollbarSize) : 0; + for (auto & x : achievements) { + bool achieved = x.achieved; + bool hidden = x.hidden && !achieved; + + if (!hidden || show_achievement_hidden_unearned) { + + ImGui::Separator(); + + // Anchor the image to the right side of the list. + ImVec2 target = ImGui::GetCursorPos(); + target.x = window_x_offset; + if (target.x > (256 * 0.4f)) { + target.x = target.x - (256 * 0.4f); + } else { + target.x = 0; + } + + ImGui::PushTextWrapPos(target.x); + ImGui::Text("%s", x.title.c_str()); + if (hidden) { + ImGui::Text("Hidden Achievement"); + } else { + ImGui::Text("%s", x.description.c_str()); + } + ImGui::PopTextWrapPos(); + + if (achieved) { + char buffer[80] = {}; + time_t unlock_time = (time_t)x.unlock_time; + std::strftime(buffer, 80, "%Y-%m-%d at %H:%M:%S", std::localtime(&unlock_time)); + + ImGui::TextColored(ImVec4(0, 255, 0, 255), "achieved on %s", buffer); + } else { + ImGui::TextColored(ImVec4(255, 0, 0, 255), "not achieved"); + } + + // Set cursor for image output. + if (target.x != 0) { + ImGui::SetCursorPos(target); + } + + display_imgui_achievement(256 * 0.4f, 256 * 0.4f, + 1.0f, 1.0f, 1.0f, 1.0f, + x.name); + + ImGui::Separator(); + } + } + ImGui::EndChild(); + } + ImGui::End(); + show_achievements = show; + } + } if (show_settings) { @@ -889,36 +2566,530 @@ void Steam_Overlay::OverlayProc() ImGui::Separator(); - ImGui::Text("Username:"); - ImGui::SameLine(); - ImGui::InputText("##username", username_text, sizeof(username_text), disable_forced ? ImGuiInputTextFlags_ReadOnly : 0); + { + std::lock_guard lock(overlay_mutex); - ImGui::Separator(); + ImGui::Text("Username:"); + ImGui::SameLine(); + ImGui::InputText("##username", username_text, sizeof(username_text), disable_forced ? ImGuiInputTextFlags_ReadOnly : 0); - ImGui::Text("Language:"); + ImGui::Separator(); - if (ImGui::ListBox("##language", ¤t_language, valid_languages, sizeof(valid_languages) / sizeof(char *), 7)) { + ImGui::Text("Language:"); - } + if (ImGui::ListBox("##language", ¤t_language, valid_languages, sizeof(valid_languages) / sizeof(char *), 7)) { - ImGui::Text("Selected Language: %s", valid_languages[current_language]); - - ImGui::Separator(); - - if (!disable_forced) { - ImGui::Text("You may have to restart the game for these to apply."); - if (ImGui::Button("Save")) { - save_settings = true; - show_settings = false; } - } else { - ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); - ImGui::TextWrapped("Some steam_settings/force_*.txt files have been detected. Please delete them if you want this menu to work."); - ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + + ImGui::Text("Selected Language: %s", valid_languages[current_language]); + + ImGui::Separator(); + + ImGui::LabelText("##notificationpositions", "Notification Positions"); + + if (ImGui::BeginListBox("##notificationpositions", ImVec2(-0.1f, ImGui::GetTextLineHeightWithSpacing() * 4.25f + currentStyle.FramePadding.y * 2.0f))) { + for (size_t x = 0; x < (sizeof(valid_ui_notification_position_labels) / sizeof(char*)); x++) { + bool sel = false; + sel = ImGui::Selectable(valid_ui_notification_position_labels[x], + (current_ui_notification_position_selection == x), + ImGuiSelectableFlags_AllowDoubleClick); + if (sel) { + PRINT_DEBUG("%s %s.\n", + "SteamOverlay::OverlayProc click on notification positions item", + valid_ui_notification_position_labels[x]); + switch (x) { + case 0: + notif_position = k_EPositionTopLeft; + current_ui_notification_position_selection = 0; + break; + case 1: + notif_position = k_EPositionTopRight; + current_ui_notification_position_selection = 1; + break; + case 2: + notif_position = k_EPositionBottomLeft; + current_ui_notification_position_selection = 2; + break; + case 3: + notif_position = k_EPositionBottomRight; + current_ui_notification_position_selection = 3; + break; + default: + break; + }; + } + } + + ImGui::EndListBox(); + } + + ImGui::Text("Selected notification position: %s", valid_ui_notification_position_labels[current_ui_notification_position_selection]); + + ImGui::Separator(); + + ImGui::Checkbox("Show achievement descriptions on unlock", &show_achievement_desc_on_unlock); + ImGui::Checkbox("Show unearned hidden achievements", &show_achievement_hidden_unearned); + + ImGui::Separator(); + + if (ImGui::Button("Set User Profile Image")) { + show_profile_image_select = true; + } + + ImGui::Separator(); + + if (!disable_forced) { + ImGui::Text("You may have to restart the game for these to apply."); + if (ImGui::Button("Save")) { + save_settings = true; + show_settings = false; + show_profile_image_select = false; + } + } else { + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + ImGui::TextWrapped("Some steam_settings/force_*.txt files have been detected. Please delete them if you want this menu to work."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + } } } ImGui::End(); + + if (show_profile_image_select) { + bool shown_drive_list = false; + { + std::lock_guard lock(overlay_mutex); + + cleared_new_profile_images_struct = false; + shown_drive_list = show_drive_list; + + if (shown_drive_list) { + std::vector temp = Local_Storage::get_drive_list(); + for (auto x : temp) { + filesystem_list[x] = false; + } + } else { + if (current_path.length() <= 0) { + filesystem_list.clear(); + if (future_path.length() > 0) { + current_path = future_path; + future_path.clear(); + } else { + current_path = Local_Storage::get_user_pictures_path(); + } + } + + if (filesystem_list.size() <= 0) { + std::vector temp = Local_Storage::get_filenames_path(current_path); + for (auto x : temp) { + filesystem_list[x] = false; + } + filesystem_list[".."] = false; + } + } + } + + if (ImGui::Begin("Set User Profile Image", &show_profile_image_select, ImGuiWindowFlags_AlwaysAutoResize)) { + + std::string status_message = ""; + bool small_image_displayed = false; + bool medium_image_displayed = false; + bool large_image_displayed = false; + user_image_displayed = false; + bool found_profile_images = false; + + ImGui::Text("Currently Used Profile Images:"); + small_image_displayed = (display_imgui_avatar(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize32x32) == 1); + if (small_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(50.0f, 50.0f)); + } + ImGui::SameLine(); + + medium_image_displayed = (display_imgui_avatar(70.0f, 70.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize64x64) == 1); + if (medium_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(70.0f, 70.0f)); + } + ImGui::SameLine(); + + large_image_displayed = (display_imgui_avatar(90.0f, 90.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + local_user_id, + k_EAvatarSize184x184) == 1); + if (large_image_displayed == false) { + ImGui::ColorButton("test", ImVec4(255, 0, 0, 255), 0, ImVec2(90.0f, 90.0f)); + } + ImGui::SameLine(); + user_image_displayed = (small_image_displayed && medium_image_displayed && large_image_displayed); + ImGui::Separator(); + + ImGui::TextWrapped("Select image size to configure:"); + ImGui::SameLine(); + + { + std::lock_guard lock(overlay_mutex); + if (ImGui::RadioButton("32x32 (Small)", (radio_btn_new_profile_image_size[0] == true)) == true) { + radio_btn_new_profile_image_size[0] = true; + radio_btn_new_profile_image_size[1] = false; + radio_btn_new_profile_image_size[2] = false; + } + ImGui::SameLine(); + if (ImGui::RadioButton("64x64 (Medium)", (radio_btn_new_profile_image_size[1] == true)) == true) { + radio_btn_new_profile_image_size[1] = true; + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[2] = false; + } + ImGui::SameLine(); + if (ImGui::RadioButton("184x184 (Large)", (radio_btn_new_profile_image_size[2] == true)) == true) { + radio_btn_new_profile_image_size[2] = true; + radio_btn_new_profile_image_size[0] = false; + radio_btn_new_profile_image_size[1] = false; + } + } + ImGui::TextWrapped("Note: Selected image must have a resolution bigger than the previous option and less than or equal to the selected option."); + ImGui::Separator(); + + bool load_image_file = false; + ImGui::TextWrapped("Please select a JPG or PNG file"); + { + std::lock_guard lock(overlay_mutex); + if (new_profile_image_path.length() > 0) { + load_image_file = true; + } + std::string path_message = "Current Path: " + current_path; + ImGui::TextWrapped(path_message.c_str()); + } + if (ImGui::Button("Up")) { + std::string temp = Local_Storage::get_parent_directory(current_path); + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Image Select Up button."); + if (Local_Storage::is_directory(temp) == true) { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", temp.c_str(), " is a directory"); + std::lock_guard lock(overlay_mutex); + current_path.clear(); + future_path = temp; + show_drive_list = false; + } else { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", temp.c_str(), " is NOT a directory"); + } + } + ImGui::SameLine(); + if (ImGui::Button("Home")) { + PRINT_DEBUG("%s.\n", "SteamOverlay::OverlayProc Image Select Home button"); + std::lock_guard lock(overlay_mutex); + future_path = Local_Storage::get_user_pictures_path(); + if (future_path.length() > 0) { + current_path.clear(); + show_drive_list = false; + } + } + ImGui::SameLine(); + if (ImGui::Button("Drives")) { + PRINT_DEBUG("%s.\n", "SteamOverlay::OverlayProc Image Select Drives button"); + std::lock_guard lock(overlay_mutex); + show_drive_list = true; + current_path.clear(); + future_path.clear(); + filesystem_list.clear(); + } + if (ImGui::BeginListBox("##FileSelect")) { + { + std::lock_guard lock(overlay_mutex); + for (auto x : filesystem_list) { + if (x.first.length() > 0) { + x.second = ImGui::Selectable(x.first.c_str(), x.second, ImGuiSelectableFlags_AllowDoubleClick); + + if (x.second) { + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc click on filesystem list item", x.first.c_str()); + if (shown_drive_list) { + future_path = ((x.first.find_last_of((char)PATH_SEPARATOR) == (x.first.length())) ? (x.first) : (x.first + PATH_SEPARATOR)); + if (Local_Storage::is_directory(future_path) == true) { + current_path.clear(); + show_drive_list = false; + PRINT_DEBUG("%s %s %" PRI_ZU " %" PRI_ZU ".\n", "SteamOverlay::OverlayProc Next directory will be", future_path.c_str(), x.first.find_last_of((char)PATH_SEPARATOR), x.first.length()); + } else { + future_path.clear(); + } + } else { + if (x.first == "..") { + future_path = Local_Storage::get_parent_directory(current_path); + PRINT_DEBUG("%s %s is %s.\n", "SteamOverlay::OverlayProc parent directory of", current_path.c_str(), future_path.c_str()); + if (Local_Storage::is_directory(future_path) == true) { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", future_path.c_str(), "is a directory"); + current_path.clear(); + } else { + PRINT_DEBUG("%s %s %s.\n", "SteamOverlay::OverlayProc ", future_path.c_str(), "is NOT a directory"); + future_path.clear(); + } + } else { + std::string temp_path = ((x.first.find_last_of((char)PATH_SEPARATOR) == (x.first.length())) ? (current_path + x.first) : (current_path + PATH_SEPARATOR + x.first)); + if (Local_Storage::is_directory(temp_path) == true) { + future_path = temp_path; + current_path.clear(); + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Next directory will be", future_path.c_str()); + } else { + // Load file. + new_profile_image_path = temp_path; + status_message.clear(); + tried_load_new_profile_image = false; + load_image_file = true; + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Load file at", new_profile_image_path.c_str()); + } + } + } + } + } + } + } + + ImGui::EndListBox(); + + ImGui::Separator(); + } + + if (load_image_file == true) { + Profile_Image image; + bool loaded_new_profile_image = false; + int new_image_size = k_EAvatarSizeMAX; + int new_image_pix_size_max = 0; + int new_image_pix_size_min = 0; + uint32_t new_profile_image_width = 0; + uint32_t new_profile_image_height = 0; + std::string image_path = ""; + { + std::lock_guard lock(overlay_mutex); + if (radio_btn_new_profile_image_size[0]) { + new_image_size = k_EAvatarSize32x32; + new_image_pix_size_max = 32; + new_image_pix_size_min = 0; + image = new_profile_image_handles.small; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_image_size = k_EAvatarSize64x64; + new_image_pix_size_max = 64; + new_image_pix_size_min = 32; + image = new_profile_image_handles.medium; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_image_size = k_EAvatarSize184x184; + new_image_pix_size_max = 184; + new_image_pix_size_min = 64; + image = new_profile_image_handles.large; + } else { + new_image_size = k_EAvatarSizeMAX; + new_image_pix_size_max = 0; + new_image_pix_size_min = 0; + image = Profile_Image(); + } + } + } + image_path = new_profile_image_path; + loaded_new_profile_image = tried_load_new_profile_image; + } + + if (image_path.length() > 0 && new_image_size != k_EAvatarSizeMAX) { + if (loaded_new_profile_image == false) { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc New image load stage 1."); + { + std::lock_guard lock(overlay_mutex); + DestroyTemporaryImage(image.raw_image); + image.width = 0; + image.height = 0; + if (radio_btn_new_profile_image_size[0]) { + new_profile_image_handles.small = image; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_profile_image_handles.medium = image; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_profile_image_handles.large = image; + } + } + } + } + + std::string new_profile_image_str = ""; + PRINT_DEBUG("%s %s.\n", "SteamOverlay::OverlayProc Loading new image pixel data from", image_path.c_str()); + new_profile_image_str = convert_vector_image_pixel_t_to_std_string(get_steam_client()->local_storage->load_image(image_path, &new_profile_image_width, &new_profile_image_height)); + if ((new_profile_image_str.length() > 0) && + (new_profile_image_width > new_image_pix_size_min) && + (new_profile_image_width <= new_image_pix_size_max) && + (new_profile_image_height > new_image_pix_size_min) && + (new_profile_image_height <= new_image_pix_size_max)) { + uint8 * temp_raw_image = NULL; + temp_raw_image = new uint8[(new_profile_image_width * new_profile_image_height * sizeof(uint32_t))]; + if (temp_raw_image != NULL) { + size_t y = 0; + for (auto & x : new_profile_image_str) { + if (y < new_profile_image_str.length() && y < (new_profile_image_width * new_profile_image_height * sizeof(uint32_t))) { + temp_raw_image[y] = x; + y++; + } + } + image.width = new_profile_image_width; + image.height = new_profile_image_height; + image.raw_image = temp_raw_image; + + status_message = "Loaded selected image.\n"; + ImGui::TextWrapped("Selected new avatar image:"); + if (display_imgui_custom_image(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + image.raw_image, + (image.width * image.height * sizeof(uint32_t)), + image.width, + image.height) == 1) { + std::lock_guard lock(overlay_mutex); + if (radio_btn_new_profile_image_size[0]) { + new_profile_image_handles.small = image; + } else { + if (radio_btn_new_profile_image_size[1]) { + new_profile_image_handles.medium = image; + } else { + if (radio_btn_new_profile_image_size[2]) { + new_profile_image_handles.large = image; + } + } + } + } + + delete temp_raw_image; + temp_raw_image = NULL; + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Unable to allocate memory for new image."); + new_profile_image_str.clear(); + new_profile_image_width = 0; + new_profile_image_height = 0; + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Unable to load new image. Invalid size, or invalid image."); + new_profile_image_str.clear(); + new_profile_image_width = 0; + new_profile_image_height = 0; + status_message = "Failed to load selected image. Invalid size, or invalid image.\n"; + } + { + std::lock_guard lock(overlay_mutex); + tried_load_new_profile_image = true; + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc New image load stage 2."); + if (image.raw_image != NULL && image.width > 0 && image.height > 0) { + status_message = "Loaded selected image.\n"; + ImGui::TextWrapped("Selected new avatar image:"); + display_imgui_custom_image(50.0f, 50.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + image.raw_image, + 0, + image.width, + image.height); + if (ImGui::Button("Apply")) { + Profile_Image temp_pi = GetTemporaryImage(image.raw_image); + if (temp_pi.raw_image != NULL && temp_pi.width > 0 && temp_pi.height > 0) { + Image_Data id; + //id.data = (char*)temp_pi.raw_image; Not a deep copy.... + id.data.clear(); + for (size_t x = 0; x < (temp_pi.width * temp_pi.height * sizeof(uint32_t)); x++) { + char a = (char)(temp_pi.raw_image[x]); + id.data += a; + } + id.width = temp_pi.width; + id.height = temp_pi.height; + PRINT_DEBUG("%s %d %s %" PRIu64 ".\n", + "SteamOverlay::OverlayProc Attempting to set new avatar image size", + new_image_size, + "for profile", + local_user_id.ConvertToUint64()); + if (new_image_size == k_EAvatarSize32x32) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize32x32, &id)) { + new_profile_image_path.clear(); + save_small_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + if (new_image_size == k_EAvatarSize64x64) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize64x64, &id)) { + new_profile_image_path.clear(); + save_medium_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + if (new_image_size == k_EAvatarSize184x184) { + std::lock_guard glock(global_mutex); + std::lock_guard lock(overlay_mutex); + if (settings->set_profile_image(k_EAvatarSize184x184, &id)) { + new_profile_image_path.clear(); + save_large_profile_image = true; + show_profile_image_select = false; + } else { + status_message = "Failed to set active avatar image.\n"; + } + } + DestroyProfileImageResource(local_user_id, new_image_size); + DestroyProfileImage(local_user_id, new_image_size); + PRINT_DEBUG("%s\n", "Steam_Overlay::OverlayProc End apply new avatar image."); + } else { + PRINT_DEBUG("%s.\n", + "Steam_Overlay::OverlayProc Unable to set new avatar image. Image data expired"); + status_message = "Loaded image data expired. Please reselect the image file in the file chooser above."; + } + ReturnTemporaryImage(temp_pi); + } + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Invalid image data after load cannot display."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + ImGui::TextWrapped("Could not load file as image. Image file must be either a PNG or JPG and must be less than or equal to selected resolution."); + ImGui::TextColored(ImVec4(255, 0, 0, 255), "WARNING WARNING WARNING"); + status_message = "Selected file is not an image, or image has an invalid resolution."; + } + } + } + } + if (ImGui::Button("Cancel")) { + std::lock_guard lock(overlay_mutex); + show_profile_image_select = false; + } + ImGui::Separator(); + + ImGui::TextWrapped("Status"); + ImGui::TextWrapped(status_message.c_str()); + ImGui::Separator(); + + { + std::lock_guard lock(overlay_mutex); + current_status_message = status_message; + } + } + + ImGui::End(); + } else { + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc Begin new avatar image select cleanup."); + std::lock_guard lock(overlay_mutex); + new_profile_image_path.clear(); + new_profile_image_handles.small.raw_image = NULL; // Handle + new_profile_image_handles.small.width = 0; + new_profile_image_handles.small.height = 0; + new_profile_image_handles.medium.raw_image = NULL; // Handle + new_profile_image_handles.medium.width = 0; + new_profile_image_handles.medium.height = 0; + new_profile_image_handles.large.raw_image = NULL; // Handle + new_profile_image_handles.large.width = 0; + new_profile_image_handles.large.height = 0; + current_status_message.clear(); + cleared_new_profile_images_struct = true; + PRINT_DEBUG("%s\n", "SteamOverlay::OverlayProc End new avatar image select cleanup."); + } } std::string url = show_url; @@ -994,7 +3165,8 @@ void Steam_Overlay::Callback(Common_Message *msg) friend_info->second.window_state |= window_state_need_attention; } - AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message()); + AddMessageNotification(friend_info->first.name() + " says: " + steam_message.message(), + CSteamID((uint64)friend_info->first.id())); NotifyUser(friend_info->second); } } @@ -1004,15 +3176,16 @@ void Steam_Overlay::RunCallbacks() { if (!achievements.size()) { Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats; - uint32 achievements_num = steamUserStats->GetNumAchievements(); - if (achievements_num) { + total_achievement_count = steamUserStats->GetNumAchievements(); + if (total_achievement_count) { PRINT_DEBUG("POPULATE OVERLAY ACHIEVEMENTS\n"); - for (unsigned i = 0; i < achievements_num; ++i) { + for (unsigned i = 0; i < total_achievement_count; ++i) { Overlay_Achievement ach; ach.name = steamUserStats->GetAchievementName(i); ach.title = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "name"); ach.description = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "desc"); const char *hidden = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "hidden"); + if (strlen(hidden) && hidden[0] == '1') { ach.hidden = true; } else { @@ -1024,11 +3197,19 @@ void Steam_Overlay::RunCallbacks() if (steamUserStats->GetAchievementAndUnlockTime(ach.name.c_str(), &achieved, &unlock_time)) { ach.achieved = achieved; ach.unlock_time = unlock_time; + if ((achieved == true) && (total_achievement_count > earned_achievement_count)) { + earned_achievement_count++; + } } else { ach.achieved = false; ach.unlock_time = 0; } + ach.raw_image = nullptr; + ach.raw_image_width = 0; + ach.raw_image_height = 0; + LoadAchievementImage(ach); + achievements.push_back(ach); } @@ -1055,6 +3236,83 @@ void Steam_Overlay::RunCallbacks() PRINT_DEBUG("tried to start renderer %u\n", started); } + if (Ready() && _renderer) { + bool done = false; + std::map temp_lazy; + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + temp_lazy = lazy_load_avatar_images; + } + if (temp_lazy.size() > 0) { + for (auto x : temp_lazy) { + uint32_t types = x.second & 0x7; + uint32_t attempts = x.second & 0x70; + if (x.first != k_steamIDNil && attempts < 0x70 && types > 0x0) { + switch (attempts) { + case 0x0: + attempts |= 0x10; + break; + case 0x10: + attempts |= 0x20; + break; + case 0x20: + attempts |= 0x40; + break; + default: + attempts = 0x70; + break; + }; + uint32_t retry = 0x0; + PRINT_DEBUG("%s %" PRIu64 ".\n", + "Steam_Overlay::RunCallbacks Lazy loading Friend Avatars for", + x.first.ConvertToUint64()); + if (x.second & 0x1) { + if (LoadProfileImage(x.first, k_EAvatarSize32x32) == false) { + retry |= 0x1; + } + } + if (x.second & 0x2) { + if (LoadProfileImage(x.first, k_EAvatarSize64x64) == false) { + retry |= 0x2; + } + } + if (x.second & 0x4) { + if (LoadProfileImage(x.first, k_EAvatarSize184x184) == false) { + retry |= 0x4; + } + } + + { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + if ((lazy_load_avatar_images[x.first] & 0x7) != types) { + // New type requested during load. + retry = ((lazy_load_avatar_images[x.first] & 0x7) ^ types) | retry; + attempts = 0x0; + } + if (retry != 0x0) { + lazy_load_avatar_images[x.first] = retry | attempts; + } else { + auto found = lazy_load_avatar_images.find(x.first); + if (found != lazy_load_avatar_images.end()) { + lazy_load_avatar_images.erase(found); + } + } + } + } else { + std::lock_guard lock(overlay_mutex); // Can't hold this when calling LoadProfileImage(). + auto found = lazy_load_avatar_images.find(x.first); + if (found != lazy_load_avatar_images.end()) { + lazy_load_avatar_images.erase(found); + } + } + } + } + } + + if (!show_overlay) { + PruneTemporaryImages(); + } + if (overlay_state_changed) { GameOverlayActivated_t data = { 0 }; @@ -1062,7 +3320,6 @@ void Steam_Overlay::RunCallbacks() data.m_bUserInitiated = true; data.m_nAppID = settings->get_local_game_id().AppID(); callbacks->addCBResult(data.k_iCallback, &data, sizeof(data)); - overlay_state_changed = false; } @@ -1070,12 +3327,92 @@ void Steam_Overlay::RunCallbacks() Steam_Matchmaking* steamMatchmaking = get_steam_client()->steam_matchmaking; if (save_settings) { - char *language_text = valid_languages[current_language]; - save_global_settings(get_steam_client()->local_storage, username_text, language_text); + bool temp_show_achievement_desc_on_unlock = false; + bool temp_show_achievement_hidden_unearned = false; + char *language_text = NULL; + char *notification_position_text = NULL; + int temp_notif_position = 0; + { + std::lock_guard lock(overlay_mutex); + temp_show_achievement_desc_on_unlock = show_achievement_desc_on_unlock; + temp_show_achievement_hidden_unearned = show_achievement_hidden_unearned; + temp_notif_position = notif_position; + language_text = valid_languages[current_language]; + } + switch (temp_notif_position) { + case k_EPositionTopLeft: + notification_position_text = valid_ui_notification_position_labels[0]; + break; + case k_EPositionTopRight: + notification_position_text = valid_ui_notification_position_labels[1]; + break; + case k_EPositionBottomLeft: + notification_position_text = valid_ui_notification_position_labels[2]; + break; + case k_EPositionBottomRight: + notification_position_text = valid_ui_notification_position_labels[3]; + break; + default: + notification_position_text = NULL; + break; + }; get_steam_client()->settings_client->set_local_name(username_text); - get_steam_client()->settings_server->set_local_name(username_text); get_steam_client()->settings_client->set_language(language_text); + if (notification_position_text != NULL) { + get_steam_client()->settings_client->set_ui_notification_position(notification_position_text); + } + get_steam_client()->settings_client->set_show_achievement_desc_on_unlock(temp_show_achievement_desc_on_unlock); + get_steam_client()->settings_client->set_show_achievement_hidden_unearned(temp_show_achievement_hidden_unearned); + get_steam_client()->settings_server->set_local_name(username_text); get_steam_client()->settings_server->set_language(language_text); + if (notification_position_text != NULL) { + get_steam_client()->settings_server->set_ui_notification_position(notification_position_text); + } + get_steam_client()->settings_server->set_show_achievement_desc_on_unlock(temp_show_achievement_desc_on_unlock); + get_steam_client()->settings_server->set_show_achievement_hidden_unearned(temp_show_achievement_hidden_unearned); + save_global_settings(get_steam_client()->local_storage, get_steam_client()->settings_client); + bool save_small = false; + bool save_medium = false; + bool save_large = false; + { + std::lock_guard lock(overlay_mutex); + if (save_small_profile_image) save_small = true; + if (save_medium_profile_image) save_medium = true; + if (save_large_profile_image) save_large = true; + } + if (save_small || save_medium || save_large) { + CSteamID local_steam_id = settings->get_local_steam_id(); + if (save_small) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize32x32) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize32x32, + profile_images[local_steam_id].small.width, + profile_images[local_steam_id].small.height, + profile_images[local_steam_id].small.raw_image); + save_small_profile_image = false; + } + } + if (save_medium) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize64x64) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize64x64, + profile_images[local_steam_id].medium.width, + profile_images[local_steam_id].medium.height, + profile_images[local_steam_id].medium.raw_image); + save_medium_profile_image = false; + } + } + if (save_large) { + if (LoadProfileImage(local_steam_id, k_EAvatarSize184x184) == true) { + std::lock_guard lock(overlay_mutex); + get_steam_client()->local_storage->save_avatar_image(k_EAvatarSize184x184, + profile_images[local_steam_id].large.width, + profile_images[local_steam_id].large.height, + profile_images[local_steam_id].large.raw_image); + save_large_profile_image = false; + } + } + } steamFriends->resend_friend_data(); save_settings = false; } @@ -1182,4 +3519,43 @@ void Steam_Overlay::RunCallbacks() } } +bool Steam_Overlay::RegisteredInternalCallbacks() +{ + return (overlay_CCallback != NULL && + overlay_CCallback->is_ready() == true); +} + +void Steam_Overlay::OnAvatarImageLoaded(AvatarImageLoaded_t *pParam) +{ + if (pParam != NULL) { + if (pParam->m_steamID != k_steamIDNil) { + PRINT_DEBUG("%s %" PRIu64 ". %s %d.\n", + "Steam_Overlay::OnAvatarImageLoaded Destroy avatar images for", + pParam->m_steamID.ConvertToUint64(), + "New reference", + pParam->m_iImage); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize32x32); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize32x32); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize64x64); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize64x64); + DestroyProfileImageResource(pParam->m_steamID, k_EAvatarSize184x184); + DestroyProfileImage(pParam->m_steamID, k_EAvatarSize184x184); + } + } + return; +} + +void Steam_Overlay_CCallback::OnAvatarImageLoaded(AvatarImageLoaded_t * pParam) +{ + auto client = try_get_steam_client(); + if (client != NULL && client->steam_overlay != NULL) { + PRINT_DEBUG("%s 0x%p sent to overlay 0x%p.\n", + "Steam_Overlay_CCallback::OnAvatarImageLoaded", + pParam, + client->steam_overlay); + client->steam_overlay->OnAvatarImageLoaded(pParam); + } + return; +} + #endif diff --git a/overlay_experimental/steam_overlay.h b/overlay_experimental/steam_overlay.h index ca33e04..13fc581 100644 --- a/overlay_experimental/steam_overlay.h +++ b/overlay_experimental/steam_overlay.h @@ -68,6 +68,8 @@ struct Notification std::chrono::seconds start_time; std::string message; std::pair* frd; + std::string ach_name; + CSteamID steam_id; }; struct Overlay_Achievement @@ -78,11 +80,67 @@ struct Overlay_Achievement bool hidden; bool achieved; uint32 unlock_time; + uint8 * raw_image; + uint32 raw_image_width; + uint32 raw_image_height; + std::weak_ptr image_resource; +}; + +struct Profile_Image_ID +{ + CSteamID id; + int eAvatarSize; +}; + +bool operator<(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator>(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator<=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator>=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator==(const Profile_Image_ID &a, const Profile_Image_ID &b); + +bool operator!=(const Profile_Image_ID &a, const Profile_Image_ID &b); + +struct Profile_Image +{ + uint32 width; + uint32 height; + uint8 * raw_image; + std::weak_ptr image_resource; +}; + +#ifdef small +#undef small +#endif + +struct Profile_Image_Set +{ + Profile_Image small; + Profile_Image medium; + Profile_Image large; +}; + +struct Temporary_Image +{ + Profile_Image image_data; + std::chrono::steady_clock::time_point last_display_time; +}; + +enum DisplayImageType { + displayImageTypeAchievement = 0, + displayImageTypeAvatar = 1, + displayImageTypeCustom = 2, + displayImageTypeEND = 3 }; #ifdef EMU_OVERLAY #include #include "Renderer_Hook.h" +class Steam_Overlay_CCallback; + class Steam_Overlay { Settings* settings; @@ -90,6 +148,7 @@ class Steam_Overlay SteamCallBacks* callbacks; RunEveryRunCB* run_every_runcb; Networking* network; + Steam_Overlay_CCallback* overlay_CCallback; // friend id, show client window (to chat and accept invite maybe) std::map friends; @@ -101,16 +160,33 @@ class Steam_Overlay int h_inset, v_inset; std::string show_url; std::vector achievements; - bool show_achievements, show_settings; + bool show_achievements, show_settings, show_profile_image_select; void *fonts_atlas; - bool disable_forced, local_save, warning_forced; - uint32_t appid; + bool disable_forced, local_save, warning_forced, show_achievement_desc_on_unlock, show_achievement_hidden_unearned; + uint32_t appid, total_achievement_count, earned_achievement_count; char username_text[256]; - std::atomic_bool save_settings; + std::atomic_bool save_settings, save_small_profile_image, save_medium_profile_image, save_large_profile_image; + + // Set Avatar Image filesystem chooser + bool show_drive_list; + std::map filesystem_list; + std::string current_path; + std::string future_path; + + // Avatar Images + std::map profile_images; + std::maplazy_load_avatar_images; + + std::string new_profile_image_path; + std::string current_status_message; + bool tried_load_new_profile_image, cleared_new_profile_images_struct; + Profile_Image_Set new_profile_image_handles; + bool radio_btn_new_profile_image_size[3]; int current_language; + int current_ui_notification_position_selection; std::string warning_message; @@ -149,6 +225,82 @@ class Steam_Overlay void BuildFriendWindow(Friend const& frd, friend_window_state &state); // Notifications like achievements, chat and invitations void BuildNotifications(int width, int height); + + // ImGui image. + std::map temp_display_images; + + int display_imgui_achievement(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + uint32_t loadType); + + int display_imgui_avatar(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + CSteamID userID, + int eAvatarSize, + uint32_t loadType); + + int display_imgui_custom_image(float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + uint8 * imageData, + uint32_t imageDataLength, + uint32_t imageDataWidth, + uint32_t imageDataHeight); + + int display_imgui_image(uint32_t displayImageType, + float xSize, + float ySize, + float image_color_multipler_r, + float image_color_multipler_g, + float image_color_multipler_b, + float image_color_multipler_a, + std::string achName, + CSteamID userID, + int eAvatarSize, + uint8 * imageData, + uint32_t imageDataLength, + uint32_t imageDataWidth, + uint32_t imageDataHeight, + uint32_t loadType); + + void LoadAchievementImage(Overlay_Achievement & ach); + void CreateAchievementImageResource(Overlay_Achievement & ach); + void DestroyAchievementImageResource(Overlay_Achievement & ach); + void DestroyAchievementImageResources(); + + // Profile images + void populate_initial_profile_images(CSteamID id); + bool LoadProfileImage(const CSteamID & id, const int eAvatarSize); + bool LoadProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + bool CreateProfileImageResource(const CSteamID & id, const int eAvatarSize); + bool CreateProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImage(const CSteamID & id, const int eAvatarSize); + void DestroyProfileImage(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize); + void DestroyProfileImageResource(const CSteamID & id, const int eAvatarSize, Profile_Image_Set & images); + void DestroyProfileImageResources(); + + // Temporary images + void ReturnTemporaryImage(Profile_Image & imageData); + Profile_Image GetTemporaryImage(uint8 * imageData); + void DestroyTemporaryImageResource(uint8 * imageData); + void DestroyTemporaryImageResources(); + void DestroyTemporaryImage(uint8 * imageData); + void DestroyTemporaryImages(); + void PruneTemporaryImages(); + public: Steam_Overlay(Settings* settings, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Networking *network); @@ -163,6 +315,7 @@ public: void SetNotificationInset(int nHorizontalInset, int nVerticalInset); void SetupOverlay(); void UnSetupOverlay(); + bool RegisteredInternalCallbacks(); void HookReady(bool ready); @@ -183,9 +336,10 @@ public: void FriendConnect(Friend _friend); void FriendDisconnect(Friend _friend); - void AddMessageNotification(std::string const& message); + void AddMessageNotification(std::string const& message, CSteamID frd); void AddAchievementNotification(nlohmann::json const& ach); void AddInviteNotification(std::pair &wnd_state); + void OnAvatarImageLoaded(AvatarImageLoaded_t *pParam); }; #else @@ -205,6 +359,7 @@ public: void SetNotificationInset(int nHorizontalInset, int nVerticalInset) {} void SetupOverlay() {} void UnSetupOverlay() {} + bool RegisteredInternalCallbacks() const { return true; } void HookReady(bool ready) {} @@ -225,9 +380,10 @@ public: void FriendConnect(Friend _friend) {} void FriendDisconnect(Friend _friend) {} - void AddMessageNotification(std::string const& message) {} + void AddMessageNotification(std::string const& message, CSteamID frd) {} void AddAchievementNotification(nlohmann::json const& ach) {} void AddInviteNotification(std::pair &wnd_state) {} + void OnAvatarImageLoaded(AvatarImageLoaded_t *pParam) {} }; #endif diff --git a/overlay_experimental/windows/DX11_Hook.cpp b/overlay_experimental/windows/DX11_Hook.cpp index 14111fe..9607dfd 100644 --- a/overlay_experimental/windows/DX11_Hook.cpp +++ b/overlay_experimental/windows/DX11_Hook.cpp @@ -207,6 +207,7 @@ DX11_Hook::DX11_Hook(): _Hooked(false), _WindowsHooked(false), pContext(nullptr), + pDevice(nullptr), mainRenderTargetView(nullptr), Present(nullptr), ResizeBuffers(nullptr), @@ -263,6 +264,11 @@ void DX11_Hook::LoadFunctions( std::weak_ptr DX11_Hook::CreateImageResource(const void* image_data, uint32_t width, uint32_t height) { + if (pDevice == nullptr) { + SPDLOG_INFO("DX11 CreateImageResource %p pDevice is NULL.\n", image_data); + return std::shared_ptr(); + } + ID3D11ShaderResourceView** resource = new ID3D11ShaderResourceView*(nullptr); // Create texture @@ -273,6 +279,7 @@ std::weak_ptr DX11_Hook::CreateImageResource(const void* image_data, u desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.CPUAccessFlags = 0; @@ -282,6 +289,7 @@ std::weak_ptr DX11_Hook::CreateImageResource(const void* image_data, u subResource.pSysMem = image_data; subResource.SysMemPitch = desc.Width * 4; subResource.SysMemSlicePitch = 0; + pDevice->CreateTexture2D(&desc, &subResource, &pTexture); if (pTexture != nullptr) diff --git a/overlay_experimental/windows/OpenGL_Hook.cpp b/overlay_experimental/windows/OpenGL_Hook.cpp index e88de9d..43e4050 100644 --- a/overlay_experimental/windows/OpenGL_Hook.cpp +++ b/overlay_experimental/windows/OpenGL_Hook.cpp @@ -163,12 +163,7 @@ std::weak_ptr OpenGL_Hook::CreateImageResource(const void* image_data, { GLuint* texture = new GLuint(0); glGenTextures(1, texture); - if (glGetError() != GL_NO_ERROR) - { - delete texture; - return std::shared_ptr(nullptr); - } - + // Save old texture id GLint oldTex; glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTex); @@ -177,13 +172,17 @@ std::weak_ptr OpenGL_Hook::CreateImageResource(const void* image_data, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Upload pixels into texture +#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); - glBindTexture(GL_TEXTURE_2D, oldTex); - + glFlush(); auto ptr = std::shared_ptr((uint64_t*)texture, [](uint64_t* handle) { if (handle != nullptr) diff --git a/steamclient_loader/ColdClientLoader.cpp b/steamclient_loader/ColdClientLoader.cpp index 6e790ab..f322ff6 100644 --- a/steamclient_loader/ColdClientLoader.cpp +++ b/steamclient_loader/ColdClientLoader.cpp @@ -32,6 +32,9 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance WCHAR ExeRunDir[MAX_PATH] = { 0 }; WCHAR ExeCommandLine[4096] = { 0 }; WCHAR AppId[128] = { 0 }; + HANDLE SharedMemFileMap = 0; + HANDLE SharedMemFileView = 0; + HANDLE SharedMemFileLock = 0; STARTUPINFOW info = { sizeof(info) }; PROCESS_INFORMATION processInfo; @@ -100,14 +103,45 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance return 0; } + // Create shared mem map. + SharedMemFileMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"Local\\SteamStart_SharedMemFile"); + if (!SharedMemFileMap) + { + MessageBoxA(NULL, "Unable to create shared memory mapping.", "ColdClientLoader", MB_ICONERROR); + return 0; + } + SharedMemFileView = MapViewOfFile(SharedMemFileMap, SECTION_ALL_ACCESS, 0, 0, 0); + if (!SharedMemFileView) + { + MessageBoxA(NULL, "Unable to create view of shared memory mapping.", "ColdClientLoader", MB_ICONERROR); + CloseHandle(SharedMemFileMap); + return 0; + } + SharedMemFileLock = CreateEventW(NULL, FALSE, FALSE, L"Local\\SteamStart_SharedMemLock"); + if (!SharedMemFileLock) + { + MessageBoxA(NULL, "Unable to create lock for shared memory mapping.", "ColdClientLoader", MB_ICONERROR); + CloseHandle(SharedMemFileView); + CloseHandle(SharedMemFileMap); + return 0; + } + SetEvent(SharedMemFileLock); + + // Create env. + SetEnvironmentVariableW(L"SteamAppPath", ExeRunDir); + WCHAR CommandLine[8192]; _snwprintf(CommandLine, _countof(CommandLine), L"\"%ls\" %ls", ExeFile, ExeCommandLine); if (!ExeFile[0] || !CreateProcessW(ExeFile, CommandLine, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, ExeRunDir, &info, &processInfo)) { MessageBoxA(NULL, "Unable to load the requested EXE file.", "ColdClientLoader", MB_ICONERROR); + CloseHandle(SharedMemFileLock); + CloseHandle(SharedMemFileView); + CloseHandle(SharedMemFileMap); return 0; } HKEY Registrykey; + HKEY Registrykey_32BitView; // Declare some variables to be used for Steam registry. DWORD UserId = 0x03100004771F810D & 0xffffffff; DWORD ProcessID = GetCurrentProcessId(); @@ -116,8 +150,12 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance DWORD keyType = REG_SZ; WCHAR OrgSteamCDir[MAX_PATH] = { 0 }; WCHAR OrgSteamCDir64[MAX_PATH] = { 0 }; + WCHAR OrgSteamCDir_32BitView[MAX_PATH] = { 0 }; + WCHAR OrgSteamCDir64_32BitView[MAX_PATH] = { 0 }; DWORD Size1 = MAX_PATH; DWORD Size2 = MAX_PATH; + DWORD Size3 = MAX_PATH; + DWORD Size4 = MAX_PATH; if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS, &Registrykey) == ERROR_SUCCESS) { orig_steam = true; @@ -127,6 +165,24 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance } else { if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &Registrykey, NULL) != ERROR_SUCCESS) + { + MessageBoxA(NULL, "Unable to patch Steam process informations on the Windows registry.", "ColdClientLoader", MB_ICONERROR); + TerminateProcess(processInfo.hProcess, NULL); + CloseHandle(SharedMemFileLock); + CloseHandle(SharedMemFileView); + CloseHandle(SharedMemFileMap); + return 0; + } + } + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &Registrykey_32BitView) == ERROR_SUCCESS) + { + orig_steam = true; + // Get original values to restore later. + RegQueryValueExW(Registrykey_32BitView, L"SteamClientDll", 0, &keyType, (LPBYTE)& OrgSteamCDir_32BitView, &Size3); + RegQueryValueExW(Registrykey_32BitView, L"SteamClientDll64", 0, &keyType, (LPBYTE)& OrgSteamCDir64_32BitView, &Size4); + } else { + if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, 0, REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &Registrykey_32BitView, NULL) != ERROR_SUCCESS) { MessageBoxA(NULL, "Unable to patch Steam process informations on the Windows registry.", "ColdClientLoader", MB_ICONERROR); TerminateProcess(processInfo.hProcess, NULL); @@ -137,26 +193,34 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance // Set values to Windows registry. RegSetValueExA(Registrykey, "ActiveUser", NULL, REG_DWORD, (LPBYTE)& UserId, sizeof(DWORD)); RegSetValueExA(Registrykey, "pid", NULL, REG_DWORD, (LPBYTE)& ProcessID, sizeof(DWORD)); + RegSetValueExA(Registrykey_32BitView, "ActiveUser", NULL, REG_DWORD, (LPBYTE)& UserId, sizeof(DWORD)); + RegSetValueExA(Registrykey_32BitView, "pid", NULL, REG_DWORD, (LPBYTE)& ProcessID, sizeof(DWORD)); { // Before saving to the registry check again if the path was valid and if the file exist if (GetFileAttributesW(ClientPath) != INVALID_FILE_ATTRIBUTES) { RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)ClientPath, (DWORD)(lstrlenW(ClientPath) * sizeof(WCHAR)) + 1); + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)ClientPath, (DWORD)(lstrlenW(ClientPath) * sizeof(WCHAR)) + 1); } else { RegSetValueExW(Registrykey, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)"", 0); + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)"", 0); } if (GetFileAttributesW(Client64Path) != INVALID_FILE_ATTRIBUTES) { RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)Client64Path, (DWORD)(lstrlenW(Client64Path) * sizeof(WCHAR)) + 1); + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)Client64Path, (DWORD)(lstrlenW(Client64Path) * sizeof(WCHAR)) + 1); } else { RegSetValueExW(Registrykey, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)"", 0); + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)"", 0); } } RegSetValueExA(Registrykey, "Universe", NULL, REG_SZ, (LPBYTE)"Public", (DWORD)lstrlenA("Public") + 1); + RegSetValueExA(Registrykey_32BitView, "Universe", NULL, REG_SZ, (LPBYTE)"Public", (DWORD)lstrlenA("Public") + 1); // Close the HKEY Handle. RegCloseKey(Registrykey); + RegCloseKey(Registrykey_32BitView); ResumeThread(processInfo.hThread); WaitForSingleObject(processInfo.hThread, INFINITE); @@ -173,7 +237,21 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance // Close the HKEY Handle. RegCloseKey(Registrykey); } + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &Registrykey_32BitView) == ERROR_SUCCESS) + { + // Restore the values. + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll", NULL, REG_SZ, (LPBYTE)OrgSteamCDir_32BitView, Size3); + RegSetValueExW(Registrykey_32BitView, L"SteamClientDll64", NULL, REG_SZ, (LPBYTE)OrgSteamCDir64_32BitView, Size4); + + // Close the HKEY Handle. + RegCloseKey(Registrykey_32BitView); + } } + // Close the SharedMem handles. + CloseHandle(SharedMemFileLock); + CloseHandle(SharedMemFileView); + CloseHandle(SharedMemFileMap); + return 0; }