Merge branch 'master' into 'source_query_mr'
# Conflicts: # dll/settings.hmerge-requests/25/head
|
@ -62,7 +62,7 @@ build_windows:
|
|||
- dnf -y install wine wget p7zip sed dos2unix
|
||||
- unix2dos *.txt
|
||||
- unix2dos files_example/*.txt files_example/*/*.txt
|
||||
- sed -i 's/..\\vcpkg\\packages\\/.\\/g' build_set_protobuf_directories.bat
|
||||
- sed -i 's/..\\vcpkg\\installed\\/.\\protobuf_/g' build_set_protobuf_directories.bat
|
||||
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
|
||||
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/0119304e030098b4821d73170fe52084/protobuf_x64-windows-static.7z'
|
||||
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/4185a97ab363ddc1859127e59ec68581/protobuf_x86-windows-static.7z'
|
||||
|
@ -71,13 +71,23 @@ build_windows:
|
|||
- 7za x sdk_standalone.7z -osdk_standalone
|
||||
- DLL_FILES="$(ls dll/*.cpp | tr "\n" " ")"; sed "s|dll/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls detours/*.cpp | tr "\n" " ")"; sed "s|detours/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls overlay_experimental/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls overlay_experimental/windows/*.cpp | tr "\n" " ")"; sed "s|overlay_experimental/windows/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls ImGui/*.cpp | tr "\n" " ")"; sed "s|ImGui/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls ImGui/impls/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls ImGui/impls/windows/*.cpp | tr "\n" " ")"; sed "s|ImGui/impls/windows/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls dll/*.proto | tr "\n" " " | sed "s/.proto/.pb.cc/g")"; sed "s|dll/\*.cc|$DLL_FILES|g" -i *.bat
|
||||
- DLL_FILES="$(ls steamclient_loader/*.cpp | tr "\n" " ")"; sed "s|steamclient_loader/\*.cpp|$DLL_FILES|g" -i *.bat
|
||||
- sed "s| /MP12 | /MP4 |g" -i *.bat
|
||||
- export WINEDEBUG=-all
|
||||
- wine cmd /c build_win_debug_experimental.bat
|
||||
- wine cmd /c build_win_release.bat
|
||||
- mkdir release/debug_experimental
|
||||
- mv steam_api.dll steam_api64.dll release/debug_experimental/
|
||||
- rm -f steamclient.dll steamclient64.dll
|
||||
- wine cmd /c build_win_debug_experimental_steamclient.bat
|
||||
- mkdir release/debug_experimental_steamclient
|
||||
- mv steamclient.dll steamclient64.dll release/debug_experimental_steamclient/
|
||||
- cp Readme_debug.txt release/debug_experimental/Readme.txt
|
||||
artifacts:
|
||||
paths:
|
||||
|
@ -86,9 +96,11 @@ build_windows:
|
|||
|
||||
build_cmake_linux:
|
||||
stage: build
|
||||
image: ubuntu:disco
|
||||
image: ubuntu
|
||||
when: manual
|
||||
|
||||
before_script:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
- apt update -y
|
||||
- apt install build-essential cmake libprotobuf-dev protobuf-compiler ninja-build -y
|
||||
|
||||
|
@ -109,9 +121,10 @@ build_cmake_linux:
|
|||
build_cmake_windows:
|
||||
stage: build
|
||||
image: fedora:29
|
||||
when: manual
|
||||
|
||||
before_script:
|
||||
- dnf -y install wine wget p7zip sed dos2unix
|
||||
- dnf -y install wine wget p7zip sed dos2unix unzip
|
||||
- wget 'https://gitlab.com/Mr_Goldberg/goldberg_emulator/uploads/48db8f434a193aae872279dc4f5dde6a/sdk_standalone.7z'
|
||||
- 7za x sdk_standalone.7z -osdk_standalone
|
||||
- wget 'https://github.com/Kitware/CMake/releases/download/v3.15.0-rc1/cmake-3.15.0-rc1-win64-x64.zip'
|
||||
|
@ -131,7 +144,7 @@ build_cmake_windows:
|
|||
- cd ..
|
||||
- mkdir x64-experimental-release && cd x64-experimental-release
|
||||
- echo call .\\..\\..\\sdk_standalone\\set_vars64.bat >> cmake-build.bat
|
||||
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
|
||||
- echo .\\..\\..\\cmake-3.15.0-rc1-win64-x64\\bin\\cmake.exe ..\\.. -G \"NMake Makefiles\" -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DEMU_EXPERIMENTAL_BUILD=ON -DEMU_OVERLAY=ON -DCMAKE_PREFIX_PATH="protobuf_x64-windows-static" -DProtobuf_PROTOC_EXECUTABLE:STRING="./../../protobuf_x64-windows-static/tools/protobuf/protoc.exe" >> cmake-build.bat
|
||||
- echo nmake.exe >> cmake-build.bat
|
||||
- wine cmd /c cmake-build.bat
|
||||
- cd ..
|
||||
|
@ -150,6 +163,10 @@ deploy_all:
|
|||
|
||||
script:
|
||||
- ls -lah
|
||||
- dnf -y install git tree
|
||||
- mkdir -p release/source_code
|
||||
- git bundle create release/source_code/source_code.bundle --all
|
||||
- "echo \"This is a git bundle of the full repo, to use: git clone source_code.bundle --branch master\" > release/source_code/Readme.txt"
|
||||
- mv linux release/
|
||||
- shopt -s extglob
|
||||
- rm -rf .g*
|
||||
|
@ -157,10 +174,11 @@ deploy_all:
|
|||
- mv release/* ./
|
||||
- rm -rf release
|
||||
- echo $CI_JOB_ID > job_id
|
||||
- tree
|
||||
artifacts:
|
||||
name: "Goldberg_Lan_Steam_Emu_$CI_COMMIT_REF_NAME-$CI_COMMIT_TAG-$CI_COMMIT_SHORT_SHA"
|
||||
paths:
|
||||
- .
|
||||
- ./
|
||||
|
||||
pages:
|
||||
image: fedora
|
||||
|
|
|
@ -77,6 +77,26 @@ file(GLOB DETOURS_SRC_SHARED
|
|||
detours/*.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
|
||||
overlay_experimental/*.cpp
|
||||
overlay_experimental/windows/*.cpp
|
||||
ImGui/*.cpp
|
||||
ImGui/impls/*.cpp
|
||||
ImGui/impls/windows/*.cpp
|
||||
glew/glew.c
|
||||
)
|
||||
elseif(UNIX)
|
||||
file(GLOB OVERLAY_EXPERIMENTAL_SRC_SHARED
|
||||
overlay_experimental/*.cpp
|
||||
overlay_experimental/linux/*.cpp
|
||||
ImGui/*.cpp
|
||||
ImGui/impls/*.cpp
|
||||
ImGui/impls/linux/*.cpp
|
||||
glew/glew.c
|
||||
)
|
||||
endif()
|
||||
|
||||
###################################################
|
||||
# Setup for the steam_api(64).dll / libsteam_api.so
|
||||
###################################################
|
||||
|
@ -85,6 +105,7 @@ file(GLOB DETOURS_SRC_SHARED
|
|||
add_library(${LIB_STEAM_API}
|
||||
SHARED
|
||||
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:${DETOURS_SRC_SHARED}>
|
||||
$<$<AND:$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:${OVERLAY_EXPERIMENTAL_SRC_SHARED}>
|
||||
${DLL_SRC_SHARED}
|
||||
${PROTO_SRCS}
|
||||
${PROTO_HDRS}
|
||||
|
@ -95,6 +116,8 @@ target_include_directories(${LIB_STEAM_API}
|
|||
PRIVATE
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ImGui
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/glew/include
|
||||
)
|
||||
|
||||
# Link the required libraries
|
||||
|
@ -103,6 +126,8 @@ target_link_libraries(${LIB_STEAM_API}
|
|||
protobuf::libprotobuf
|
||||
$<$<BOOL:${WIN32}>:ws2_32>
|
||||
$<$<BOOL:${WIN32}>:iphlpapi>
|
||||
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:opengl32.lib>
|
||||
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:Winmm.lib>
|
||||
)
|
||||
|
||||
# Add target compile definitions
|
||||
|
@ -113,6 +138,8 @@ target_compile_definitions(${LIB_STEAM_API}
|
|||
$<$<CONFIG:RelWithDebInfo>:EMU_RELEASE_BUILD>
|
||||
$<$<CONFIG:MinSizeRel>:EMU_RELEASE_BUILD>
|
||||
$<$<BOOL:${EMU_EXPERIMENTAL_BUILD}>:EMU_EXPERIMENTAL_BUILD>
|
||||
$<$<BOOL:${EMU_OVERLAY}>:EMU_OVERLAY>
|
||||
$<$<AND:$<BOOL:${WIN32}>,$<BOOL:${EMU_EXPERIMENTAL_BUILD}>,$<BOOL:${EMU_OVERLAY}>>:GLEW_STATIC>
|
||||
)
|
||||
|
||||
# Install the target
|
||||
|
@ -230,6 +257,7 @@ target_link_libraries(${BIN_LOBBY_CONNECT}
|
|||
$<$<BOOL:${WIN32}>:ws2_32>
|
||||
$<$<BOOL:${WIN32}>:iphlpapi>
|
||||
$<$<BOOL:${WIN32}>:comdlg32>
|
||||
${CMAKE_DL_LIBS}
|
||||
-debug:none
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
|
||||
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
|
||||
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
|
||||
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
|
||||
//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty)
|
||||
// It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp.
|
||||
#define IMGUI_DISABLE_DEMO_WINDOWS
|
||||
#define IMGUI_DISABLE_METRICS_WINDOW
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
|
||||
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||
//#define IMGUI_DISABLE_OSX_FUNCTIONS // [OSX] Won't use and link with any OSX function (clipboard).
|
||||
//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf.
|
||||
//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
|
||||
//---- Using 32-bits vertex indices (default is 16-bits) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bits indices).
|
||||
// Another way to allow large meshes while keeping 16-bits indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools
|
||||
// Use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
// Have the Item Picker break in the ItemAdd() function instead of ItemHoverable() - which is earlier in the code, will catch a few extra items, allow picking items other than Hovered one.
|
||||
// This adds a small runtime cost which is why it is not enabled by default.
|
||||
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
*/
|
||||
|
||||
#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
|
|
@ -0,0 +1,5 @@
|
|||
namespace ImGui
|
||||
{
|
||||
IMGUI_API bool ColoredInputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
IMGUI_API bool ColoredInputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||
}
|
|
@ -0,0 +1,634 @@
|
|||
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
||||
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
||||
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
||||
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
||||
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
||||
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
||||
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
||||
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
||||
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
||||
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
||||
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
||||
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
||||
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
||||
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
||||
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
||||
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
||||
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
||||
|
||||
//----------------------------------------
|
||||
// OpenGL GLSL GLSL
|
||||
// version version string
|
||||
//----------------------------------------
|
||||
// 2.0 110 "#version 110"
|
||||
// 2.1 120 "#version 120"
|
||||
// 3.0 130 "#version 130"
|
||||
// 3.1 140 "#version 140"
|
||||
// 3.2 150 "#version 150"
|
||||
// 3.3 330 "#version 330 core"
|
||||
// 4.0 400 "#version 400 core"
|
||||
// 4.1 410 "#version 410 core"
|
||||
// 4.2 420 "#version 410 core"
|
||||
// 4.3 430 "#version 430 core"
|
||||
// ES 2.0 100 "#version 100" = WebGL 1.0
|
||||
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
||||
//----------------------------------------
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "../imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||
#include <stddef.h> // intptr_t
|
||||
#else
|
||||
#include <stdint.h> // intptr_t
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
|
||||
// Auto-detect GL version
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
|
||||
#else
|
||||
#include <GLES3/gl3.h> // Use GL ES 3
|
||||
#endif
|
||||
#else
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code
|
||||
#else
|
||||
#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Desktop GL has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 0
|
||||
#else
|
||||
#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1
|
||||
#endif
|
||||
|
||||
// OpenGL Data
|
||||
static char g_GlslVersionString[32] = "";
|
||||
static GLuint g_FontTexture = 0;
|
||||
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
|
||||
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
|
||||
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
|
||||
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_opengl3";
|
||||
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
#endif
|
||||
|
||||
// Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 100";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 300 es";
|
||||
#else
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 130";
|
||||
#endif
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
|
||||
strcpy(g_GlslVersionString, glsl_version);
|
||||
strcat(g_GlslVersionString, "\n");
|
||||
|
||||
// Make a dummy GL call (we don't actually need the result)
|
||||
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
|
||||
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
|
||||
GLint current_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_NewFrame()
|
||||
{
|
||||
if (!g_FontTexture)
|
||||
return ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
{
|
||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(g_ShaderHandle);
|
||||
glUniform1i(g_AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
|
||||
#endif
|
||||
|
||||
(void)vertex_array_object;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxPos);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxUV);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxColor);
|
||||
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
|
||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
// Backup GL state
|
||||
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
|
||||
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
|
||||
#endif
|
||||
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
|
||||
#endif
|
||||
#ifdef GL_POLYGON_MODE
|
||||
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||
#endif
|
||||
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
||||
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
|
||||
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
|
||||
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
|
||||
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
|
||||
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
|
||||
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
|
||||
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
|
||||
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
|
||||
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
|
||||
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
|
||||
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
|
||||
bool clip_origin_lower_left = true;
|
||||
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
|
||||
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
|
||||
if (last_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
#endif
|
||||
|
||||
// Setup desired GL state
|
||||
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
|
||||
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
|
||||
GLuint vertex_array_object = 0;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
// Upload vertex/index buffers
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec4 clip_rect;
|
||||
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
|
||||
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
|
||||
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
|
||||
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
|
||||
|
||||
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
if (clip_origin_lower_left)
|
||||
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
|
||||
else
|
||||
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||
|
||||
// Bind texture, Draw
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
#if IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||
#else
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the temporary VAO
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Restore modified GL state
|
||||
glUseProgram(last_program);
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
glBindSampler(0, last_sampler);
|
||||
#endif
|
||||
glActiveTexture(last_active_texture);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array_object);
|
||||
#endif
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
|
||||
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
|
||||
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
|
||||
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
|
||||
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
|
||||
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
||||
#endif
|
||||
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
|
||||
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
|
||||
// Upload texture to graphics system
|
||||
GLint last_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGenTextures(1, &g_FontTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
#ifdef GL_UNPACK_ROW_LENGTH
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
#endif
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
|
||||
|
||||
// Restore state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
if (g_FontTexture)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glDeleteTextures(1, &g_FontTexture);
|
||||
io.Fonts->TexID = 0;
|
||||
g_FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||
static bool CheckShader(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||
static bool CheckProgram(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||
{
|
||||
// Backup GL state
|
||||
GLint last_texture, last_array_buffer;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLint last_vertex_array;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||
#endif
|
||||
|
||||
// Parse GLSL version string
|
||||
int glsl_version = 130;
|
||||
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
|
||||
|
||||
const GLchar* vertex_shader_glsl_120 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"attribute vec2 Position;\n"
|
||||
"attribute vec2 UV;\n"
|
||||
"attribute vec4 Color;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_130 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_410_core =
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_120 =
|
||||
"#ifdef GL_ES\n"
|
||||
" precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_130 =
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_410_core =
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
// Select shaders matching our GLSL versions
|
||||
const GLchar* vertex_shader = NULL;
|
||||
const GLchar* fragment_shader = NULL;
|
||||
if (glsl_version < 130)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_120;
|
||||
fragment_shader = fragment_shader_glsl_120;
|
||||
}
|
||||
else if (glsl_version >= 410)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_410_core;
|
||||
fragment_shader = fragment_shader_glsl_410_core;
|
||||
}
|
||||
else if (glsl_version == 300)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_300_es;
|
||||
fragment_shader = fragment_shader_glsl_300_es;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_130;
|
||||
fragment_shader = fragment_shader_glsl_130;
|
||||
}
|
||||
|
||||
// Create shaders
|
||||
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
|
||||
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
|
||||
glCompileShader(g_VertHandle);
|
||||
CheckShader(g_VertHandle, "vertex shader");
|
||||
|
||||
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
|
||||
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
|
||||
glCompileShader(g_FragHandle);
|
||||
CheckShader(g_FragHandle, "fragment shader");
|
||||
|
||||
g_ShaderHandle = glCreateProgram();
|
||||
glAttachShader(g_ShaderHandle, g_VertHandle);
|
||||
glAttachShader(g_ShaderHandle, g_FragHandle);
|
||||
glLinkProgram(g_ShaderHandle);
|
||||
CheckProgram(g_ShaderHandle, "shader program");
|
||||
|
||||
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
|
||||
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
|
||||
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
|
||||
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
|
||||
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
|
||||
|
||||
// Create buffers
|
||||
glGenBuffers(1, &g_VboHandle);
|
||||
glGenBuffers(1, &g_ElementsHandle);
|
||||
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
|
||||
// Restore modified GL state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
||||
{
|
||||
if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle);
|
||||
if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle);
|
||||
g_VboHandle = g_ElementsHandle = 0;
|
||||
|
||||
if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle);
|
||||
if (g_VertHandle) glDeleteShader(g_VertHandle);
|
||||
g_VertHandle = 0;
|
||||
|
||||
if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle);
|
||||
if (g_FragHandle) glDeleteShader(g_FragHandle);
|
||||
g_FragHandle = 0;
|
||||
|
||||
if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle);
|
||||
g_ShaderHandle = 0;
|
||||
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
|
||||
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Specific OpenGL versions
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
|
||||
|
||||
// Set default OpenGL3 loader to be gl3w
|
||||
#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
|
||||
#endif
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
@ -0,0 +1,338 @@
|
|||
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [?] Platform: Clipboard support
|
||||
// [?] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [?] Platform: Keyboard arrays indexed using
|
||||
// [?] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_x11.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-08-31: Initial X11 implementation
|
||||
|
||||
// X11 Data
|
||||
static Display* g_Display = nullptr;
|
||||
static Window g_Window = 0;
|
||||
static uint64_t g_Time = 0;
|
||||
static uint64_t g_TicksPerSecond = 0;
|
||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
static bool g_HasGamepad = false;
|
||||
static bool g_WantUpdateHasGamepad = true;
|
||||
|
||||
bool GetKeyState(int keysym, char keys[32])
|
||||
{
|
||||
int keycode = XKeysymToKeycode(g_Display, keysym);
|
||||
return keys[keycode/8] & (1<<keycode%8);
|
||||
}
|
||||
|
||||
bool IsKeySys(int key)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case XK_Shift_L : case XK_Shift_R :
|
||||
case XK_Control_L: case XK_Control_R :
|
||||
case XK_Alt_L : case XK_Alt_R :
|
||||
case XK_Super_L : case XK_Super_R :
|
||||
case XK_Caps_Lock: case XK_Shift_Lock:
|
||||
case XK_BackSpace: case XK_Delete :
|
||||
case XK_Left : case XK_Right :
|
||||
case XK_Up : case XK_Down :
|
||||
case XK_Prior : case XK_Next :
|
||||
case XK_Home : case XK_End :
|
||||
case XK_Insert : case XK_Return :
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplX11_Init(void *display, void *window)
|
||||
{
|
||||
timespec ts, tsres;
|
||||
clock_getres(CLOCK_MONOTONIC_RAW, &tsres);
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
|
||||
g_TicksPerSecond = 1000000000.0f / (static_cast<uint64_t>(tsres.tv_nsec) + static_cast<uint64_t>(tsres.tv_sec)*1000000000);
|
||||
g_Time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
|
||||
|
||||
//if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
|
||||
// return false;
|
||||
//if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
|
||||
// return false;
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
g_Display = reinterpret_cast<Display*>(display);
|
||||
g_Window = reinterpret_cast<Window>(window);
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
io.BackendPlatformName = "imgui_impl_x11";
|
||||
io.ImeWindowHandle = nullptr;
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
|
||||
io.KeyMap[ImGuiKey_Tab] = XKeysymToKeycode(g_Display, XK_Tab);
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = XKeysymToKeycode(g_Display, XK_Left);
|
||||
io.KeyMap[ImGuiKey_RightArrow] = XKeysymToKeycode(g_Display, XK_Right);
|
||||
io.KeyMap[ImGuiKey_UpArrow] = XKeysymToKeycode(g_Display, XK_Up);
|
||||
io.KeyMap[ImGuiKey_DownArrow] = XKeysymToKeycode(g_Display, XK_Down);
|
||||
io.KeyMap[ImGuiKey_PageUp] = XKeysymToKeycode(g_Display, XK_Prior);
|
||||
io.KeyMap[ImGuiKey_PageDown] = XKeysymToKeycode(g_Display, XK_Next);
|
||||
io.KeyMap[ImGuiKey_Home] = XKeysymToKeycode(g_Display, XK_Home);
|
||||
io.KeyMap[ImGuiKey_End] = XKeysymToKeycode(g_Display, XK_End);
|
||||
io.KeyMap[ImGuiKey_Insert] = XKeysymToKeycode(g_Display, XK_Insert);
|
||||
io.KeyMap[ImGuiKey_Delete] = XKeysymToKeycode(g_Display, XK_Delete);
|
||||
io.KeyMap[ImGuiKey_Backspace] = XKeysymToKeycode(g_Display, XK_BackSpace);
|
||||
io.KeyMap[ImGuiKey_Space] = XKeysymToKeycode(g_Display, XK_space);
|
||||
io.KeyMap[ImGuiKey_Enter] = XKeysymToKeycode(g_Display, XK_Return);
|
||||
io.KeyMap[ImGuiKey_Escape] = XKeysymToKeycode(g_Display, XK_Escape);
|
||||
io.KeyMap[ImGuiKey_A] = XKeysymToKeycode(g_Display, XK_A);
|
||||
io.KeyMap[ImGuiKey_C] = XKeysymToKeycode(g_Display, XK_C);
|
||||
io.KeyMap[ImGuiKey_V] = XKeysymToKeycode(g_Display, XK_V);
|
||||
io.KeyMap[ImGuiKey_X] = XKeysymToKeycode(g_Display, XK_X);
|
||||
io.KeyMap[ImGuiKey_Y] = XKeysymToKeycode(g_Display, XK_Y);
|
||||
io.KeyMap[ImGuiKey_Z] = XKeysymToKeycode(g_Display, XK_Z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplX11_Shutdown()
|
||||
{
|
||||
g_Display = nullptr;
|
||||
g_Window = 0;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplX11_UpdateMouseCursor()
|
||||
{
|
||||
/*
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return false;
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
::SetCursor(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
LPTSTR win32_cursor = IDC_ARROW;
|
||||
switch (imgui_cursor)
|
||||
{
|
||||
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||
}
|
||||
::SetCursor(::LoadCursor(NULL, win32_cursor));
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ImGui_ImplX11_UpdateMousePos()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
// POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||
// ::ClientToScreen(g_hWnd, &pos);
|
||||
// ::SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
|
||||
// Set mouse position
|
||||
Window unused_window;
|
||||
int rx, ry, x, y;
|
||||
unsigned int mask;
|
||||
|
||||
XQueryPointer(g_Display, g_Window, &unused_window, &unused_window, &rx, &ry, &x, &y, &mask);
|
||||
|
||||
io.MousePos = ImVec2((float)x, (float)y);
|
||||
}
|
||||
|
||||
// Gamepad navigation mapping
|
||||
static void ImGui_ImplX11_UpdateGamepads()
|
||||
{
|
||||
/* TODO: support linux gamepad ?
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
|
||||
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
|
||||
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
|
||||
if (g_WantUpdateHasGamepad)
|
||||
{
|
||||
XINPUT_CAPABILITIES caps;
|
||||
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
|
||||
g_WantUpdateHasGamepad = false;
|
||||
}
|
||||
|
||||
XINPUT_STATE xinput_state;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
|
||||
{
|
||||
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
|
||||
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
|
||||
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
|
||||
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
|
||||
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
|
||||
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
|
||||
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
|
||||
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
|
||||
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
|
||||
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
|
||||
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void ImGui_ImplX11_NewFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||
|
||||
unsigned int width, height;
|
||||
Window unused_window;
|
||||
int unused_int;
|
||||
unsigned int unused_unsigned_int;
|
||||
|
||||
XGetGeometry(g_Display, (Window)g_Window, &unused_window, &unused_int, &unused_int, &width, &height, &unused_unsigned_int, &unused_unsigned_int);
|
||||
|
||||
io.DisplaySize.x = width;
|
||||
io.DisplaySize.y = height;
|
||||
|
||||
timespec ts, tsres;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
|
||||
uint64_t current_time = static_cast<uint64_t>(ts.tv_nsec) + static_cast<uint64_t>(ts.tv_sec)*1000000000;
|
||||
|
||||
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
|
||||
g_Time = current_time;
|
||||
|
||||
// Read keyboard modifiers inputs
|
||||
char keys[32];
|
||||
XQueryKeymap(g_Display, keys);
|
||||
|
||||
io.KeyCtrl = GetKeyState(XK_Control_L, keys);
|
||||
io.KeyShift = GetKeyState(XK_Shift_L, keys);
|
||||
io.KeyAlt = GetKeyState(XK_Alt_L, keys);
|
||||
io.KeySuper = false;
|
||||
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
|
||||
|
||||
// Update OS mouse position
|
||||
ImGui_ImplX11_UpdateMousePos();
|
||||
/*
|
||||
// Update OS mouse cursor with the cursor requested by imgui
|
||||
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||
if (g_LastMouseCursor != mouse_cursor)
|
||||
{
|
||||
g_LastMouseCursor = mouse_cursor;
|
||||
ImGui_ImplX11_UpdateMouseCursor();
|
||||
}
|
||||
*/
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplX11_UpdateGamepads();
|
||||
}
|
||||
|
||||
// Process X11 mouse/keyboard inputs.
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent &event)
|
||||
{
|
||||
if (ImGui::GetCurrentContext() == NULL)
|
||||
return 0;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
switch (event.type)
|
||||
{
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
switch(event.xbutton.button)
|
||||
{
|
||||
case Button1:
|
||||
io.MouseDown[0] = event.type == ButtonPress;
|
||||
break;
|
||||
|
||||
case Button2:
|
||||
io.MouseDown[2] = event.type == ButtonPress;
|
||||
break;
|
||||
|
||||
case Button3:
|
||||
io.MouseDown[1] = event.type == ButtonPress;
|
||||
break;
|
||||
|
||||
case Button4: // Mouse wheel up
|
||||
if( event.type == ButtonPress )
|
||||
io.MouseWheel += 1;
|
||||
return 0;
|
||||
|
||||
case Button5: // Mouse wheel down
|
||||
if( event.type == ButtonPress )
|
||||
io.MouseWheel -= 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
{
|
||||
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
|
||||
if( IsKeySys(key) )
|
||||
io.KeysDown[event.xkey.keycode] = true;
|
||||
else
|
||||
io.AddInputCharacter(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case KeyRelease:
|
||||
{
|
||||
int key = XKeycodeToKeysym(g_Display, event.xkey.keycode, event.xkey.state & ShiftMask ? 1 : 0);
|
||||
if( IsKeySys(key) )
|
||||
io.KeysDown[event.xkey.keycode] = false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
case WM_DEVICECHANGE:
|
||||
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
|
||||
g_WantUpdateHasGamepad = true;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
#pragma once
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplX11_Init(void* display, void* window);
|
||||
IMGUI_IMPL_API void ImGui_ImplX11_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplX11_NewFrame();
|
||||
|
||||
// Handler for Win32 messages, update mouse/keyboard data.
|
||||
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
|
||||
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
|
||||
/*
|
||||
IMGUI_IMPL_API int ImGui_ImplX11_EventHandler(XEvent *event);
|
||||
*/
|
|
@ -0,0 +1,557 @@
|
|||
// dear imgui: Renderer for DirectX10
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-29: DirectX10: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX10: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example.
|
||||
// 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-04-09: Misc: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) on other back-ends.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2016-05-07: DirectX10: Disabling depth-write.
|
||||
|
||||
#include "../../imgui.h"
|
||||
#include "imgui_impl_dx10.h"
|
||||
|
||||
// DirectX
|
||||
#include <stdio.h>
|
||||
#include <d3d10_1.h>
|
||||
#include <d3d10.h>
|
||||
|
||||
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static ID3DBlob* g_pVertexShaderBlob = NULL;
|
||||
static ID3DBlob* g_pPixelShaderBlob = NULL;
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static ID3D10Device* g_pd3dDevice = NULL;
|
||||
static IDXGIFactory* g_pFactory = NULL;
|
||||
static ID3D10Buffer* g_pVB = NULL;
|
||||
static ID3D10Buffer* g_pIB = NULL;
|
||||
static ID3D10VertexShader* g_pVertexShader = NULL;
|
||||
static ID3D10InputLayout* g_pInputLayout = NULL;
|
||||
static ID3D10Buffer* g_pVertexConstantBuffer = NULL;
|
||||
static ID3D10PixelShader* g_pPixelShader = NULL;
|
||||
static ID3D10SamplerState* g_pFontSampler = NULL;
|
||||
static ID3D10ShaderResourceView*g_pFontTextureView = NULL;
|
||||
static ID3D10RasterizerState* g_pRasterizerState = NULL;
|
||||
static ID3D10BlendState* g_pBlendState = NULL;
|
||||
static ID3D10DepthStencilState* g_pDepthStencilState = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
|
||||
{
|
||||
// Setup viewport
|
||||
D3D10_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D10_VIEWPORT));
|
||||
vp.Width = (UINT)draw_data->DisplaySize.x;
|
||||
vp.Height = (UINT)draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Bind shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(g_pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(g_pVertexShader);
|
||||
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
|
||||
ctx->PSSetShader(g_pPixelShader);
|
||||
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
|
||||
|
||||
// Setup render state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
|
||||
ctx->RSSetState(g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ID3D10Device* ctx = g_pd3dDevice;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D10_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (ctx->CreateBuffer(&desc, NULL, &g_pVB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D10_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
if (ctx->CreateBuffer(&desc, NULL, &g_pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer
|
||||
ImDrawVert* vtx_dst = NULL;
|
||||
ImDrawIdx* idx_dst = NULL;
|
||||
g_pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
|
||||
g_pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
g_pVB->Unmap();
|
||||
g_pIB->Unmap();
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
{
|
||||
void* mapped_resource;
|
||||
if (g_pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||
g_pVertexConstantBuffer->Unmap();
|
||||
}
|
||||
|
||||
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||
struct BACKUP_DX10_STATE
|
||||
{
|
||||
UINT ScissorRectsCount, ViewportsCount;
|
||||
D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
ID3D10RasterizerState* RS;
|
||||
ID3D10BlendState* BlendState;
|
||||
FLOAT BlendFactor[4];
|
||||
UINT SampleMask;
|
||||
UINT StencilRef;
|
||||
ID3D10DepthStencilState* DepthStencilState;
|
||||
ID3D10ShaderResourceView* PSShaderResource;
|
||||
ID3D10SamplerState* PSSampler;
|
||||
ID3D10PixelShader* PS;
|
||||
ID3D10VertexShader* VS;
|
||||
D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology;
|
||||
ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
|
||||
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
|
||||
DXGI_FORMAT IndexBufferFormat;
|
||||
ID3D10InputLayout* InputLayout;
|
||||
};
|
||||
BACKUP_DX10_STATE old;
|
||||
old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
ctx->RSGetState(&old.RS);
|
||||
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
ctx->PSGetShader(&old.PS);
|
||||
ctx->VSGetShader(&old.VS);
|
||||
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
ctx->IAGetInputLayout(&old.InputLayout);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D10_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y)};
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->TextureId;
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
// Restore modified DX state
|
||||
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
|
||||
ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
|
||||
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX10_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D10_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Usage = D3D10_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D10Texture2D *pTexture = NULL;
|
||||
D3D10_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
|
||||
// Create texture view
|
||||
D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
|
||||
ZeroMemory(&srv_desc, sizeof(srv_desc));
|
||||
srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srv_desc.Texture2D.MostDetailedMip = 0;
|
||||
g_pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &g_pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
|
||||
|
||||
// Create texture sampler
|
||||
{
|
||||
D3D10_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX10_CreateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return false;
|
||||
if (g_pFontSampler)
|
||||
ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX10 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
|
||||
if (D3DCompile == nullptr)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &g_pVertexShaderBlob, NULL);
|
||||
|
||||
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
|
||||
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pVertexShader) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
if (g_pd3dDevice->CreateVertexShader(ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pVertexShader) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the input layout
|
||||
D3D10_INPUT_ELEMENT_DESC local_layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D10_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D10_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D10_INPUT_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
#ifdef USE_D3DCOMPILE
|
||||
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX10, ImGui_vertexShaderDX10_len, &g_pInputLayout) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D10_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
sampler sampler0;\
|
||||
Texture2D texture0;\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &g_pPixelShaderBlob, NULL);
|
||||
|
||||
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), &g_pPixelShader) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
if (g_pd3dDevice->CreatePixelShader(ImGui_pixelShaderDX10, ImGui_pixelShaderDX10_len, &g_pPixelShader) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
unload_d3dcompile();
|
||||
#endif
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D10_BLEND_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.BlendEnable[0] = true;
|
||||
desc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
|
||||
desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
|
||||
desc.BlendOp = D3D10_BLEND_OP_ADD;
|
||||
desc.SrcBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
|
||||
desc.DestBlendAlpha = D3D10_BLEND_ZERO;
|
||||
desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
|
||||
desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
|
||||
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D10_RASTERIZER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.FillMode = D3D10_FILL_SOLID;
|
||||
desc.CullMode = D3D10_CULL_NONE;
|
||||
desc.ScissorEnable = true;
|
||||
desc.DepthClipEnable = true;
|
||||
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D10_DEPTH_STENCIL_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D10_COMPARISON_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
|
||||
}
|
||||
|
||||
ImGui_ImplDX10_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX10_InvalidateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
|
||||
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
|
||||
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
|
||||
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
|
||||
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
|
||||
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
|
||||
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
|
||||
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
|
||||
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
|
||||
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
|
||||
#ifdef USE_D3DCOMPILE
|
||||
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
|
||||
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX10_Init(ID3D10Device* device)
|
||||
{
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_dx10";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = NULL;
|
||||
IDXGIAdapter* pDXGIAdapter = NULL;
|
||||
IDXGIFactory* pFactory = NULL;
|
||||
|
||||
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||
{
|
||||
g_pd3dDevice = device;
|
||||
g_pFactory = pFactory;
|
||||
}
|
||||
if (pDXGIDevice) pDXGIDevice->Release();
|
||||
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||
g_pd3dDevice->AddRef();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX10_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
|
||||
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX10_NewFrame()
|
||||
{
|
||||
if (!g_pFontSampler)
|
||||
return ImGui_ImplDX10_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// dear imgui: Renderer for DirectX10
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ID3D10Device;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();
|
|
@ -0,0 +1,684 @@
|
|||
// dear imgui: Renderer for DirectX11
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
|
||||
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
|
||||
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2016-05-07: DirectX11: Disabling depth-write.
|
||||
|
||||
#include "../../imgui.h"
|
||||
#include "imgui_impl_dx11.h"
|
||||
|
||||
// DirectX
|
||||
#include <stdio.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static ID3DBlob* g_pVertexShaderBlob = NULL;
|
||||
static ID3DBlob* g_pPixelShaderBlob = NULL;
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static ID3D11Device* g_pd3dDevice = NULL;
|
||||
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
|
||||
static IDXGIFactory* g_pFactory = NULL;
|
||||
static ID3D11Buffer* g_pVB = NULL;
|
||||
static ID3D11Buffer* g_pIB = NULL;
|
||||
static ID3D11VertexShader* g_pVertexShader = NULL;
|
||||
static ID3D11InputLayout* g_pInputLayout = NULL;
|
||||
static ID3D11Buffer* g_pVertexConstantBuffer = NULL;
|
||||
static ID3D11PixelShader* g_pPixelShader = NULL;
|
||||
static ID3D11SamplerState* g_pFontSampler = NULL;
|
||||
static ID3D11ShaderResourceView*g_pFontTextureView = NULL;
|
||||
static ID3D11RasterizerState* g_pRasterizerState = NULL;
|
||||
static ID3D11BlendState* g_pBlendState = NULL;
|
||||
static ID3D11DepthStencilState* g_pDepthStencilState = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
// Setup viewport
|
||||
D3D11_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(g_pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &g_pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(g_pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(g_pVertexShader, NULL, 0);
|
||||
ctx->VSSetConstantBuffers(0, 1, &g_pVertexConstantBuffer);
|
||||
ctx->PSSetShader(g_pPixelShader, NULL, 0);
|
||||
ctx->PSSetSamplers(0, 1, &g_pFontSampler);
|
||||
|
||||
// Setup blend state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(g_pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(g_pDepthStencilState, 0);
|
||||
ctx->RSSetState(g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ID3D11DeviceContext* ctx = g_pd3dDeviceContext;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVB) < 0)
|
||||
return;
|
||||
}
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = g_IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
if (g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||
if (ctx->Map(g_pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (ctx->Map(g_pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
ctx->Unmap(g_pVB, 0);
|
||||
ctx->Unmap(g_pIB, 0);
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||
if (ctx->Map(g_pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||
ctx->Unmap(g_pVertexConstantBuffer, 0);
|
||||
}
|
||||
|
||||
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||
struct BACKUP_DX11_STATE
|
||||
{
|
||||
UINT ScissorRectsCount, ViewportsCount;
|
||||
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
|
||||
ID3D11RasterizerState* RS;
|
||||
ID3D11BlendState* BlendState;
|
||||
FLOAT BlendFactor[4];
|
||||
UINT SampleMask;
|
||||
UINT StencilRef;
|
||||
ID3D11DepthStencilState* DepthStencilState;
|
||||
ID3D11ShaderResourceView* PSShaderResource;
|
||||
ID3D11SamplerState* PSSampler;
|
||||
ID3D11PixelShader* PS;
|
||||
ID3D11VertexShader* VS;
|
||||
UINT PSInstancesCount, VSInstancesCount;
|
||||
ID3D11ClassInstance* PSInstances[256], *VSInstances[256]; // 256 is max according to PSSetShader documentation
|
||||
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
|
||||
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
|
||||
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
|
||||
DXGI_FORMAT IndexBufferFormat;
|
||||
ID3D11InputLayout* InputLayout;
|
||||
};
|
||||
BACKUP_DX11_STATE old;
|
||||
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
ctx->RSGetState(&old.RS);
|
||||
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
old.PSInstancesCount = old.VSInstancesCount = 256;
|
||||
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
ctx->IAGetInputLayout(&old.InputLayout);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_idx_offset = 0;
|
||||
int global_vtx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D11_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->TextureId;
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
// Restore modified DX state
|
||||
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
|
||||
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
|
||||
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX11_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D11Texture2D *pTexture = NULL;
|
||||
D3D11_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
g_pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
|
||||
// Create texture view
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &g_pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)g_pFontTextureView;
|
||||
|
||||
// Create texture sampler
|
||||
{
|
||||
D3D11_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
g_pd3dDevice->CreateSamplerState(&desc, &g_pFontSampler);
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return false;
|
||||
if (g_pFontSampler)
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
|
||||
if (D3DCompile == nullptr)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
const char* target;
|
||||
switch (g_pd3dDevice->GetFeatureLevel())
|
||||
{
|
||||
case D3D_FEATURE_LEVEL_9_1: target = "vs_4_0_level_9_1"; break;
|
||||
case D3D_FEATURE_LEVEL_9_2: target = "vs_4_0_level_9_2"; break;
|
||||
case D3D_FEATURE_LEVEL_9_3: target = "vs_4_0_level_9_3"; break;
|
||||
case D3D_FEATURE_LEVEL_10_0: target = "vs_4_0"; break;
|
||||
case D3D_FEATURE_LEVEL_10_1: target = "vs_4_1"; break;
|
||||
case D3D_FEATURE_LEVEL_11_0: target = "vs_5_0"; break;
|
||||
case D3D_FEATURE_LEVEL_11_1: target = "vs_5_0"; break;
|
||||
default: target = "vs_4_0";
|
||||
}
|
||||
|
||||
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pVertexShaderBlob, NULL);
|
||||
|
||||
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
if (g_pd3dDevice->CreateVertexShader((DWORD*)g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), NULL, &g_pVertexShader) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
|
||||
unsigned char* byteCode;
|
||||
SIZE_T byteCodeSize;
|
||||
|
||||
switch (g_pd3dDevice->GetFeatureLevel())
|
||||
{
|
||||
case D3D_FEATURE_LEVEL_9_1:
|
||||
byteCode = ImGui_vertexShaderDX11_9_1;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_9_1_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_9_2:
|
||||
byteCode = ImGui_vertexShaderDX11_9_2;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_9_2_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_9_3:
|
||||
byteCode = ImGui_vertexShaderDX11_9_3;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_9_3_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_10_0:
|
||||
byteCode = ImGui_vertexShaderDX11_10_0;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_10_0_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_10_1:
|
||||
byteCode = ImGui_vertexShaderDX11_10_1;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_10_1_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_0:
|
||||
byteCode = ImGui_vertexShaderDX11_11_0;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_11_0_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_1:
|
||||
byteCode = ImGui_vertexShaderDX11_11_1;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_11_1_len;
|
||||
break;
|
||||
|
||||
default:
|
||||
byteCode = ImGui_vertexShaderDX11;
|
||||
byteCodeSize = ImGui_vertexShaderDX11_len;
|
||||
}
|
||||
|
||||
auto x = g_pd3dDevice->CreateVertexShader(byteCode, byteCodeSize, NULL, &g_pVertexShader);
|
||||
if (x != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the input layout
|
||||
D3D11_INPUT_ELEMENT_DESC local_layout[] =
|
||||
{
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)(&((ImDrawVert*)0)->uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)(&((ImDrawVert*)0)->col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
#ifdef USE_D3DCOMPILE
|
||||
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize(), &g_pInputLayout) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
if (g_pd3dDevice->CreateInputLayout(local_layout, 3, ImGui_vertexShaderDX11, ImGui_vertexShaderDX11_len, &g_pInputLayout) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D11_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER);
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
g_pd3dDevice->CreateBuffer(&desc, NULL, &g_pVertexConstantBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
sampler sampler0;\
|
||||
Texture2D texture0;\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
const char* target;
|
||||
switch (g_pd3dDevice->GetFeatureLevel())
|
||||
{
|
||||
case D3D_FEATURE_LEVEL_9_1: target = "ps_4_0_level_9_1"; break;
|
||||
case D3D_FEATURE_LEVEL_9_2: target = "ps_4_0_level_9_2"; break;
|
||||
case D3D_FEATURE_LEVEL_9_3: target = "ps_4_0_level_9_3"; break;
|
||||
case D3D_FEATURE_LEVEL_10_0: target = "ps_4_0"; break;
|
||||
case D3D_FEATURE_LEVEL_10_1: target = "ps_4_1"; break;
|
||||
case D3D_FEATURE_LEVEL_11_0: target = "ps_5_0"; break;
|
||||
case D3D_FEATURE_LEVEL_11_1: target = "ps_5_0"; break;
|
||||
default: target = "ps_4_0";
|
||||
}
|
||||
|
||||
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", target, 0, 0, &g_pPixelShaderBlob, NULL);
|
||||
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
if (g_pd3dDevice->CreatePixelShader((DWORD*)g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize(), NULL, &g_pPixelShader) != S_OK)
|
||||
return false;
|
||||
#else
|
||||
|
||||
unsigned char* byteCode;
|
||||
SIZE_T byteCodeSize;
|
||||
|
||||
switch (g_pd3dDevice->GetFeatureLevel())
|
||||
{
|
||||
case D3D_FEATURE_LEVEL_9_1:
|
||||
byteCode = ImGui_pixelShaderDX11_9_1;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_9_1_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_9_2:
|
||||
byteCode = ImGui_pixelShaderDX11_9_2;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_9_2_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_9_3:
|
||||
byteCode = ImGui_pixelShaderDX11_9_3;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_9_3_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_10_0:
|
||||
byteCode = ImGui_pixelShaderDX11_10_0;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_10_0_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_10_1:
|
||||
byteCode = ImGui_pixelShaderDX11_10_1;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_10_1_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_0:
|
||||
byteCode = ImGui_pixelShaderDX11_11_0;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_11_0_len;
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_1:
|
||||
byteCode = ImGui_pixelShaderDX11_11_1;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_11_1_len;
|
||||
break;
|
||||
|
||||
default:
|
||||
byteCode = ImGui_pixelShaderDX11;
|
||||
byteCodeSize = ImGui_pixelShaderDX11_len;
|
||||
}
|
||||
|
||||
if (g_pd3dDevice->CreatePixelShader(byteCode, byteCodeSize, NULL, &g_pPixelShader) != S_OK)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
unload_d3dcompile();
|
||||
#endif
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D11_BLEND_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.RenderTarget[0].BlendEnable = true;
|
||||
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
|
||||
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
g_pd3dDevice->CreateBlendState(&desc, &g_pBlendState);
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D11_RASTERIZER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.FillMode = D3D11_FILL_SOLID;
|
||||
desc.CullMode = D3D11_CULL_NONE;
|
||||
desc.ScissorEnable = true;
|
||||
desc.DepthClipEnable = true;
|
||||
g_pd3dDevice->CreateRasterizerState(&desc, &g_pRasterizerState);
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D11_DEPTH_STENCIL_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
g_pd3dDevice->CreateDepthStencilState(&desc, &g_pDepthStencilState);
|
||||
}
|
||||
|
||||
ImGui_ImplDX11_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
|
||||
if (g_pFontSampler) { g_pFontSampler->Release(); g_pFontSampler = NULL; }
|
||||
if (g_pFontTextureView) { g_pFontTextureView->Release(); g_pFontTextureView = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
|
||||
if (g_pBlendState) { g_pBlendState->Release(); g_pBlendState = NULL; }
|
||||
if (g_pDepthStencilState) { g_pDepthStencilState->Release(); g_pDepthStencilState = NULL; }
|
||||
if (g_pRasterizerState) { g_pRasterizerState->Release(); g_pRasterizerState = NULL; }
|
||||
if (g_pPixelShader) { g_pPixelShader->Release(); g_pPixelShader = NULL; }
|
||||
if (g_pVertexConstantBuffer) { g_pVertexConstantBuffer->Release(); g_pVertexConstantBuffer = NULL; }
|
||||
if (g_pInputLayout) { g_pInputLayout->Release(); g_pInputLayout = NULL; }
|
||||
if (g_pVertexShader) { g_pVertexShader->Release(); g_pVertexShader = NULL; }
|
||||
#ifdef USE_D3DCOMPILE
|
||||
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
|
||||
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
|
||||
{
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_dx11";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = NULL;
|
||||
IDXGIAdapter* pDXGIAdapter = NULL;
|
||||
IDXGIFactory* pFactory = NULL;
|
||||
|
||||
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
|
||||
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
|
||||
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
|
||||
{
|
||||
g_pd3dDevice = device;
|
||||
g_pd3dDeviceContext = device_context;
|
||||
g_pFactory = pFactory;
|
||||
}
|
||||
if (pDXGIDevice) pDXGIDevice->Release();
|
||||
if (pDXGIAdapter) pDXGIAdapter->Release();
|
||||
g_pd3dDevice->AddRef();
|
||||
g_pd3dDeviceContext->AddRef();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
if (g_pFactory) { g_pFactory->Release(); g_pFactory = NULL; }
|
||||
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
|
||||
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; }
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_NewFrame()
|
||||
{
|
||||
if (!g_pFontSampler)
|
||||
return ImGui_ImplDX11_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// dear imgui: Renderer for DirectX11
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
#pragma once
|
||||
|
||||
struct ID3D11Device;
|
||||
struct ID3D11DeviceContext;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
|
@ -0,0 +1,677 @@
|
|||
// dear imgui: Renderer for DirectX12
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// Issues:
|
||||
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: Misc: Various minor tidying up.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData().
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example.
|
||||
// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport).
|
||||
// 2018-02-22: Merged into master with all Win32 code synchronized to other examples.
|
||||
|
||||
#include "../../imgui.h"
|
||||
#include "imgui_impl_dx12.h"
|
||||
|
||||
// DirectX
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_4.h>
|
||||
|
||||
#include "../../../overlay_experimental/windows/ImGui_ShaderBlobs.h"
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static ID3DBlob* g_pVertexShaderBlob = NULL;
|
||||
static ID3DBlob* g_pPixelShaderBlob = NULL;
|
||||
#endif
|
||||
|
||||
// DirectX data
|
||||
static ID3D12Device* g_pd3dDevice = NULL;
|
||||
static ID3D12RootSignature* g_pRootSignature = NULL;
|
||||
static ID3D12PipelineState* g_pPipelineState = NULL;
|
||||
static DXGI_FORMAT g_RTVFormat = DXGI_FORMAT_UNKNOWN;
|
||||
static ID3D12Resource* g_pFontTextureResource = NULL;
|
||||
static D3D12_CPU_DESCRIPTOR_HANDLE g_hFontSrvCpuDescHandle = {};
|
||||
static D3D12_GPU_DESCRIPTOR_HANDLE g_hFontSrvGpuDescHandle = {};
|
||||
|
||||
struct FrameResources
|
||||
{
|
||||
ID3D12Resource* IndexBuffer;
|
||||
ID3D12Resource* VertexBuffer;
|
||||
int IndexBufferSize;
|
||||
int VertexBufferSize;
|
||||
};
|
||||
static FrameResources* g_pFrameResources = NULL;
|
||||
static UINT g_numFramesInFlight = 0;
|
||||
static UINT g_frameIndex = UINT_MAX;
|
||||
|
||||
struct VERTEX_CONSTANT_BUFFER
|
||||
{
|
||||
float mvp[4][4];
|
||||
};
|
||||
|
||||
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, FrameResources* fr)
|
||||
{
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
||||
VERTEX_CONSTANT_BUFFER vertex_constant_buffer;
|
||||
{
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
float mvp[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, 0.5f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp));
|
||||
}
|
||||
|
||||
// Setup viewport
|
||||
D3D12_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0.0f;
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Bind shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
D3D12_VERTEX_BUFFER_VIEW vbv;
|
||||
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
|
||||
vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
|
||||
vbv.SizeInBytes = fr->VertexBufferSize * stride;
|
||||
vbv.StrideInBytes = stride;
|
||||
ctx->IASetVertexBuffers(0, 1, &vbv);
|
||||
D3D12_INDEX_BUFFER_VIEW ibv;
|
||||
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
|
||||
ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
|
||||
ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
|
||||
ctx->IASetIndexBuffer(&ibv);
|
||||
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->SetPipelineState(g_pPipelineState);
|
||||
ctx->SetGraphicsRootSignature(g_pRootSignature);
|
||||
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
|
||||
|
||||
// Setup blend factor
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendFactor(blend_factor);
|
||||
}
|
||||
|
||||
// Render function
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// FIXME: I'm assuming that this only gets called once per frame!
|
||||
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
||||
g_frameIndex = g_frameIndex + 1;
|
||||
FrameResources* fr = &g_pFrameResources[g_frameIndex % g_numFramesInFlight];
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (fr->VertexBuffer == NULL || fr->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (fr->VertexBuffer != NULL) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
|
||||
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->VertexBuffer)) < 0)
|
||||
return;
|
||||
}
|
||||
if (fr->IndexBuffer == NULL || fr->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (fr->IndexBuffer != NULL) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
|
||||
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
if (g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&fr->IndexBuffer)) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
void* vtx_resource, *idx_resource;
|
||||
D3D12_RANGE range;
|
||||
memset(&range, 0, sizeof(D3D12_RANGE));
|
||||
if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
fr->VertexBuffer->Unmap(0, &range);
|
||||
fr->IndexBuffer->Unmap(0, &range);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply Scissor, Bind texture, Draw
|
||||
const D3D12_RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
|
||||
ctx->SetGraphicsRootDescriptorTable(1, *(D3D12_GPU_DESCRIPTOR_HANDLE*)&pcmd->TextureId);
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX12_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
ID3D12Resource* pTexture = NULL;
|
||||
g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture));
|
||||
|
||||
UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
|
||||
UINT uploadSize = height * uploadPitch;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = uploadSize;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
|
||||
ID3D12Resource* uploadBuffer = NULL;
|
||||
HRESULT hr = g_pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
void* mapped = NULL;
|
||||
D3D12_RANGE range = { 0, uploadSize };
|
||||
hr = uploadBuffer->Map(0, &range, &mapped);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4);
|
||||
uploadBuffer->Unmap(0, &range);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
|
||||
srcLocation.pResource = uploadBuffer;
|
||||
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srcLocation.PlacedFootprint.Footprint.Width = width;
|
||||
srcLocation.PlacedFootprint.Footprint.Height = height;
|
||||
srcLocation.PlacedFootprint.Footprint.Depth = 1;
|
||||
srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
|
||||
dstLocation.pResource = pTexture;
|
||||
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLocation.SubresourceIndex = 0;
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = pTexture;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
ID3D12Fence* fence = NULL;
|
||||
hr = g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
HANDLE event = CreateEvent(0, 0, 0, 0);
|
||||
IM_ASSERT(event != NULL);
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.NodeMask = 1;
|
||||
|
||||
ID3D12CommandQueue* cmdQueue = NULL;
|
||||
hr = g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
ID3D12CommandAllocator* cmdAlloc = NULL;
|
||||
hr = g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
ID3D12GraphicsCommandList* cmdList = NULL;
|
||||
hr = g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
|
||||
cmdList->ResourceBarrier(1, &barrier);
|
||||
|
||||
hr = cmdList->Close();
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*) &cmdList);
|
||||
hr = cmdQueue->Signal(fence, 1);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
fence->SetEventOnCompletion(1, event);
|
||||
WaitForSingleObject(event, INFINITE);
|
||||
|
||||
cmdList->Release();
|
||||
cmdAlloc->Release();
|
||||
cmdQueue->Release();
|
||||
CloseHandle(event);
|
||||
fence->Release();
|
||||
uploadBuffer->Release();
|
||||
|
||||
// Create texture view
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
g_pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, g_hFontSrvCpuDescHandle);
|
||||
if (g_pFontTextureResource != NULL)
|
||||
g_pFontTextureResource->Release();
|
||||
g_pFontTextureResource = pTexture;
|
||||
}
|
||||
|
||||
// Store our identifier
|
||||
static_assert(sizeof(ImTextureID) >= sizeof(g_hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
|
||||
io.Fonts->TexID = (ImTextureID)g_hFontSrvGpuDescHandle.ptr;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return false;
|
||||
if (g_pPipelineState)
|
||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
|
||||
// Create the root signature
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE descRange = {};
|
||||
descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
descRange.NumDescriptors = 1;
|
||||
descRange.BaseShaderRegister = 0;
|
||||
descRange.RegisterSpace = 0;
|
||||
descRange.OffsetInDescriptorsFromTableStart = 0;
|
||||
|
||||
D3D12_ROOT_PARAMETER param[2] = {};
|
||||
|
||||
param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
|
||||
param[0].Constants.ShaderRegister = 0;
|
||||
param[0].Constants.RegisterSpace = 0;
|
||||
param[0].Constants.Num32BitValues = 16;
|
||||
param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
|
||||
|
||||
param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
param[1].DescriptorTable.NumDescriptorRanges = 1;
|
||||
param[1].DescriptorTable.pDescriptorRanges = &descRange;
|
||||
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
|
||||
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.MipLODBias = 0.f;
|
||||
staticSampler.MaxAnisotropy = 0;
|
||||
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
staticSampler.MinLOD = 0.f;
|
||||
staticSampler.MaxLOD = 0.f;
|
||||
staticSampler.ShaderRegister = 0;
|
||||
staticSampler.RegisterSpace = 0;
|
||||
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC desc = {};
|
||||
desc.NumParameters = _countof(param);
|
||||
desc.pParameters = param;
|
||||
desc.NumStaticSamplers = 1;
|
||||
desc.pStaticSamplers = &staticSampler;
|
||||
desc.Flags =
|
||||
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
|
||||
|
||||
ID3DBlob* blob = NULL;
|
||||
|
||||
static decltype(D3D12SerializeRootSignature)* D3D12SerializeRootSignature = (decltype(D3D12SerializeRootSignature))GetProcAddress(GetModuleHandle("d3d12.dll"), "D3D12SerializeRootSignature");
|
||||
if (D3D12SerializeRootSignature(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, NULL) != S_OK)
|
||||
return false;
|
||||
|
||||
g_pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&g_pRootSignature));
|
||||
blob->Release();
|
||||
}
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX12 sample code but remove this dependency you can:
|
||||
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
|
||||
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
|
||||
psoDesc.NodeMask = 1;
|
||||
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
||||
psoDesc.pRootSignature = g_pRootSignature;
|
||||
psoDesc.SampleMask = UINT_MAX;
|
||||
psoDesc.NumRenderTargets = 1;
|
||||
psoDesc.RTVFormats[0] = g_RTVFormat;
|
||||
psoDesc.SampleDesc.Count = 1;
|
||||
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
decltype(D3DCompile)* D3DCompile = load_d3dcompile();
|
||||
if (D3DCompile == nullptr)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Create the vertex shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* vertexShader =
|
||||
"cbuffer vertexBuffer : register(b0) \
|
||||
{\
|
||||
float4x4 ProjectionMatrix; \
|
||||
};\
|
||||
struct VS_INPUT\
|
||||
{\
|
||||
float2 pos : POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
\
|
||||
PS_INPUT main(VS_INPUT input)\
|
||||
{\
|
||||
PS_INPUT output;\
|
||||
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
|
||||
output.col = input.col;\
|
||||
output.uv = input.uv;\
|
||||
return output;\
|
||||
}";
|
||||
|
||||
D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &g_pVertexShaderBlob, NULL);
|
||||
if (g_pVertexShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
psoDesc.VS = { g_pVertexShaderBlob->GetBufferPointer(), g_pVertexShaderBlob->GetBufferSize() };
|
||||
#else
|
||||
psoDesc.VS = { ImGui_vertexShaderDX12, ImGui_vertexShaderDX12_len };
|
||||
#endif
|
||||
|
||||
|
||||
// Create the input layout
|
||||
static D3D12_INPUT_ELEMENT_DESC local_layout[] = {
|
||||
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
|
||||
};
|
||||
psoDesc.InputLayout = { local_layout, 3 };
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
{
|
||||
#ifdef USE_D3DCOMPILE
|
||||
static const char* pixelShader =
|
||||
"struct PS_INPUT\
|
||||
{\
|
||||
float4 pos : SV_POSITION;\
|
||||
float4 col : COLOR0;\
|
||||
float2 uv : TEXCOORD0;\
|
||||
};\
|
||||
SamplerState sampler0 : register(s0);\
|
||||
Texture2D texture0 : register(t0);\
|
||||
\
|
||||
float4 main(PS_INPUT input) : SV_Target\
|
||||
{\
|
||||
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
|
||||
return out_col; \
|
||||
}";
|
||||
|
||||
D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &g_pPixelShaderBlob, NULL);
|
||||
if (g_pPixelShaderBlob == NULL) // NB: Pass ID3D10Blob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
|
||||
return false;
|
||||
psoDesc.PS = { g_pPixelShaderBlob->GetBufferPointer(), g_pPixelShaderBlob->GetBufferSize() };
|
||||
#else
|
||||
|
||||
psoDesc.PS = { ImGui_pixelShaderDX12, ImGui_pixelShaderDX12_len };
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_D3DCOMPILE
|
||||
unload_d3dcompile();
|
||||
#endif
|
||||
|
||||
// Create the blending setup
|
||||
{
|
||||
D3D12_BLEND_DESC& desc = psoDesc.BlendState;
|
||||
desc.AlphaToCoverageEnable = false;
|
||||
desc.RenderTarget[0].BlendEnable = true;
|
||||
desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
|
||||
desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_ZERO;
|
||||
desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
{
|
||||
D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState;
|
||||
desc.FillMode = D3D12_FILL_MODE_SOLID;
|
||||
desc.CullMode = D3D12_CULL_MODE_NONE;
|
||||
desc.FrontCounterClockwise = FALSE;
|
||||
desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
|
||||
desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
|
||||
desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
|
||||
desc.DepthClipEnable = true;
|
||||
desc.MultisampleEnable = FALSE;
|
||||
desc.AntialiasedLineEnable = FALSE;
|
||||
desc.ForcedSampleCount = 0;
|
||||
desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
|
||||
}
|
||||
|
||||
// Create depth-stencil State
|
||||
{
|
||||
D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState;
|
||||
desc.DepthEnable = false;
|
||||
desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
|
||||
desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
desc.StencilEnable = false;
|
||||
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
|
||||
desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
desc.BackFace = desc.FrontFace;
|
||||
}
|
||||
|
||||
if (g_pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&g_pPipelineState)) != S_OK)
|
||||
return false;
|
||||
|
||||
ImGui_ImplDX12_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_InvalidateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
#ifdef USE_D3DCOMPILE
|
||||
if (g_pVertexShaderBlob) { g_pVertexShaderBlob->Release(); g_pVertexShaderBlob = NULL; }
|
||||
if (g_pPixelShaderBlob) { g_pPixelShaderBlob->Release(); g_pPixelShaderBlob = NULL; }
|
||||
#endif
|
||||
if (g_pRootSignature) { g_pRootSignature->Release(); g_pRootSignature = NULL; }
|
||||
if (g_pPipelineState) { g_pPipelineState->Release(); g_pPipelineState = NULL; }
|
||||
if (g_pFontTextureResource) { g_pFontTextureResource->Release(); g_pFontTextureResource = NULL; io.Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
for (UINT i = 0; i < g_numFramesInFlight; i++)
|
||||
{
|
||||
FrameResources* fr = &g_pFrameResources[i];
|
||||
if (fr->IndexBuffer) { fr->IndexBuffer->Release(); fr->IndexBuffer = NULL; }
|
||||
if (fr->VertexBuffer) { fr->VertexBuffer->Release(); fr->VertexBuffer = NULL; }
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
|
||||
{
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_dx12";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
g_pd3dDevice = device;
|
||||
g_RTVFormat = rtv_format;
|
||||
g_hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
|
||||
g_hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
|
||||
g_pFrameResources = new FrameResources[num_frames_in_flight];
|
||||
g_numFramesInFlight = num_frames_in_flight;
|
||||
g_frameIndex = UINT_MAX;
|
||||
|
||||
// Create buffers with a default size (they will later be grown as needed)
|
||||
for (int i = 0; i < num_frames_in_flight; i++)
|
||||
{
|
||||
FrameResources* fr = &g_pFrameResources[i];
|
||||
fr->IndexBuffer = NULL;
|
||||
fr->VertexBuffer = NULL;
|
||||
fr->IndexBufferSize = 10000;
|
||||
fr->VertexBufferSize = 5000;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
delete[] g_pFrameResources;
|
||||
g_pFrameResources = NULL;
|
||||
g_pd3dDevice = NULL;
|
||||
g_hFontSrvCpuDescHandle.ptr = 0;
|
||||
g_hFontSrvGpuDescHandle.ptr = 0;
|
||||
g_numFramesInFlight = 0;
|
||||
g_frameIndex = UINT_MAX;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_NewFrame()
|
||||
{
|
||||
if (!g_pPipelineState)
|
||||
return ImGui_ImplDX12_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// dear imgui: Renderer for DirectX12
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
// Issues:
|
||||
// [ ] 64-bit only for now! (Because sizeof(ImTextureId) == sizeof(void*)). See github.com/ocornut/imgui/pull/301
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
#pragma once
|
||||
|
||||
enum DXGI_FORMAT;
|
||||
struct ID3D12Device;
|
||||
struct ID3D12GraphicsCommandList;
|
||||
struct D3D12_CPU_DESCRIPTOR_HANDLE;
|
||||
struct D3D12_GPU_DESCRIPTOR_HANDLE;
|
||||
|
||||
// cmd_list is the command list that the implementation will use to render imgui draw lists.
|
||||
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
|
||||
// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
|
||||
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX12_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
|
|
@ -0,0 +1,286 @@
|
|||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
|
||||
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
|
||||
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
|
||||
#include "../../imgui.h"
|
||||
#include "imgui_impl_dx9.h"
|
||||
|
||||
// DirectX
|
||||
#include <d3d9.h>
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
// DirectX data
|
||||
static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
|
||||
static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL;
|
||||
static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL;
|
||||
static LPDIRECT3DTEXTURE9 g_FontTexture = NULL;
|
||||
static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000;
|
||||
|
||||
struct CUSTOMVERTEX
|
||||
{
|
||||
float pos[3];
|
||||
D3DCOLOR col;
|
||||
float uv[2];
|
||||
};
|
||||
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
|
||||
|
||||
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
|
||||
{
|
||||
// Setup viewport
|
||||
D3DVIEWPORT9 vp;
|
||||
vp.X = vp.Y = 0;
|
||||
vp.Width = (DWORD)draw_data->DisplaySize.x;
|
||||
vp.Height = (DWORD)draw_data->DisplaySize.y;
|
||||
vp.MinZ = 0.0f;
|
||||
vp.MaxZ = 1.0f;
|
||||
g_pd3dDevice->SetViewport(&vp);
|
||||
|
||||
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient)
|
||||
g_pd3dDevice->SetPixelShader(NULL);
|
||||
g_pd3dDevice->SetVertexShader(NULL);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, true);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, false);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
|
||||
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
|
||||
|
||||
// Setup orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
|
||||
{
|
||||
float L = draw_data->DisplayPos.x + 0.5f;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
|
||||
float T = draw_data->DisplayPos.y + 0.5f;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
|
||||
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
|
||||
D3DMATRIX mat_projection =
|
||||
{ { {
|
||||
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.5f, 0.0f,
|
||||
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
|
||||
} } };
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
|
||||
}
|
||||
}
|
||||
|
||||
// Render function.
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// Create and grow buffers if needed
|
||||
if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
g_VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
g_IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup the DX9 state
|
||||
IDirect3DStateBlock9* d3d9_state_block = NULL;
|
||||
if (g_pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
|
||||
return;
|
||||
|
||||
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
|
||||
D3DMATRIX last_world, last_view, last_projection;
|
||||
g_pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
|
||||
// FIXME-OPT: This is a waste of resource, the ideal is to use imconfig.h and
|
||||
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
|
||||
CUSTOMVERTEX* vtx_dst;
|
||||
ImDrawIdx* idx_dst;
|
||||
if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
|
||||
return;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data;
|
||||
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
|
||||
{
|
||||
vtx_dst->pos[0] = vtx_src->pos.x;
|
||||
vtx_dst->pos[1] = vtx_src->pos.y;
|
||||
vtx_dst->pos[2] = 0.0f;
|
||||
vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000) >> 16) | ((vtx_src->col & 0xFF) << 16); // RGBA --> ARGB for DirectX9
|
||||
vtx_dst->uv[0] = vtx_src->uv.x;
|
||||
vtx_dst->uv[1] = vtx_src->uv.y;
|
||||
vtx_dst++;
|
||||
vtx_src++;
|
||||
}
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
g_pVB->Unlock();
|
||||
g_pIB->Unlock();
|
||||
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX));
|
||||
g_pd3dDevice->SetIndices(g_pIB);
|
||||
g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX9_SetupRenderState(draw_data);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) };
|
||||
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->TextureId;
|
||||
g_pd3dDevice->SetTexture(0, texture);
|
||||
g_pd3dDevice->SetScissorRect(&r);
|
||||
g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount/3);
|
||||
}
|
||||
}
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
|
||||
// Restore the DX9 transform
|
||||
g_pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
|
||||
g_pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
|
||||
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
|
||||
|
||||
// Restore the DX9 state
|
||||
d3d9_state_block->Apply();
|
||||
d3d9_state_block->Release();
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
|
||||
{
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_dx9";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
|
||||
g_pd3dDevice = device;
|
||||
g_pd3dDevice->AddRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX9_InvalidateDeviceObjects();
|
||||
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }
|
||||
}
|
||||
|
||||
static bool ImGui_ImplDX9_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height, bytes_per_pixel;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
|
||||
|
||||
// Upload texture to graphics system
|
||||
g_FontTexture = NULL;
|
||||
if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0)
|
||||
return false;
|
||||
D3DLOCKED_RECT tex_locked_rect;
|
||||
if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
|
||||
return false;
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy((unsigned char *)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel));
|
||||
g_FontTexture->UnlockRect(0);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)g_FontTexture;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_CreateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return false;
|
||||
if (!ImGui_ImplDX9_CreateFontsTexture())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplDX9_InvalidateDeviceObjects()
|
||||
{
|
||||
if (!g_pd3dDevice)
|
||||
return;
|
||||
if (g_pVB) { g_pVB->Release(); g_pVB = NULL; }
|
||||
if (g_pIB) { g_pIB->Release(); g_pIB = NULL; }
|
||||
if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->TexID = NULL; } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX9_NewFrame()
|
||||
{
|
||||
if (!g_FontTexture)
|
||||
return ImGui_ImplDX9_CreateDeviceObjects();
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// dear imgui: Renderer for DirectX9
|
||||
// This needs to be used along with a Platform Binding (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp.
|
||||
// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bits indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
#pragma once
|
||||
|
||||
struct IDirect3DDevice9;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();
|
|
@ -0,0 +1,324 @@
|
|||
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
#include "../../imgui.h"
|
||||
#include "imgui_impl_win32.h"
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <XInput.h>
|
||||
#include <tchar.h>
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2019-05-11: Inputs: Don't filter value from WM_CHAR before calling AddInputCharacter().
|
||||
// 2019-01-17: Misc: Using GetForegroundWindow()+IsChild() instead of GetActiveWindow() to be compatible with windows created in a different thread or parent.
|
||||
// 2019-01-17: Inputs: Added support for mouse buttons 4 and 5 via WM_XBUTTON* messages.
|
||||
// 2019-01-15: Inputs: Added support for XInput gamepads (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
|
||||
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||
// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads).
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples.
|
||||
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
|
||||
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||
// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||
// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert.
|
||||
// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag.
|
||||
// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read.
|
||||
// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging.
|
||||
// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set.
|
||||
|
||||
// Win32 Data
|
||||
static HWND g_hWnd = 0;
|
||||
static INT64 g_Time = 0;
|
||||
static INT64 g_TicksPerSecond = 0;
|
||||
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT;
|
||||
static bool g_HasGamepad = false;
|
||||
static bool g_WantUpdateHasGamepad = true;
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplWin32_Init(void* hwnd)
|
||||
{
|
||||
if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond))
|
||||
return false;
|
||||
if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time))
|
||||
return false;
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
g_hWnd = (HWND)hwnd;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
io.BackendPlatformName = "imgui_impl_win32";
|
||||
io.ImeWindowHandle = hwnd;
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime.
|
||||
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
|
||||
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
|
||||
io.KeyMap[ImGuiKey_Home] = VK_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = VK_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
|
||||
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
io.KeyMap[ImGuiKey_V] = 'V';
|
||||
io.KeyMap[ImGuiKey_X] = 'X';
|
||||
io.KeyMap[ImGuiKey_Y] = 'Y';
|
||||
io.KeyMap[ImGuiKey_Z] = 'Z';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplWin32_Shutdown()
|
||||
{
|
||||
g_hWnd = (HWND)0;
|
||||
}
|
||||
|
||||
static bool ImGui_ImplWin32_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
|
||||
return false;
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
::SetCursor(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
LPTSTR win32_cursor = IDC_ARROW;
|
||||
switch (imgui_cursor)
|
||||
{
|
||||
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
|
||||
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
|
||||
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
|
||||
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
|
||||
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
|
||||
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
|
||||
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
|
||||
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
|
||||
}
|
||||
::SetCursor(::LoadCursor(NULL, win32_cursor));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ImGui_ImplWin32_UpdateMousePos()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
|
||||
::ClientToScreen(g_hWnd, &pos);
|
||||
::SetCursorPos(pos.x, pos.y);
|
||||
}
|
||||
|
||||
// Set mouse position
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
POINT pos;
|
||||
if (HWND active_window = ::GetForegroundWindow())
|
||||
if (active_window == g_hWnd || ::IsChild(active_window, g_hWnd))
|
||||
if (::GetCursorPos(&pos) && ::ScreenToClient(g_hWnd, &pos))
|
||||
io.MousePos = ImVec2((float)pos.x, (float)pos.y);
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "xinput")
|
||||
#endif
|
||||
|
||||
// Gamepad navigation mapping
|
||||
static void ImGui_ImplWin32_UpdateGamepads()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
|
||||
// Calling XInputGetState() every frame on disconnected gamepads is unfortunately too slow.
|
||||
// Instead we refresh gamepad availability by calling XInputGetCapabilities() _only_ after receiving WM_DEVICECHANGE.
|
||||
if (g_WantUpdateHasGamepad)
|
||||
{
|
||||
XINPUT_CAPABILITIES caps;
|
||||
g_HasGamepad = (XInputGetCapabilities(0, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS);
|
||||
g_WantUpdateHasGamepad = false;
|
||||
}
|
||||
|
||||
XINPUT_STATE xinput_state;
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
if (g_HasGamepad && XInputGetState(0, &xinput_state) == ERROR_SUCCESS)
|
||||
{
|
||||
const XINPUT_GAMEPAD& gamepad = xinput_state.Gamepad;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
|
||||
#define MAP_BUTTON(NAV_NO, BUTTON_ENUM) { io.NavInputs[NAV_NO] = (gamepad.wButtons & BUTTON_ENUM) ? 1.0f : 0.0f; }
|
||||
#define MAP_ANALOG(NAV_NO, VALUE, V0, V1) { float vn = (float)(VALUE - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
|
||||
MAP_BUTTON(ImGuiNavInput_Activate, XINPUT_GAMEPAD_A); // Cross / A
|
||||
MAP_BUTTON(ImGuiNavInput_Cancel, XINPUT_GAMEPAD_B); // Circle / B
|
||||
MAP_BUTTON(ImGuiNavInput_Menu, XINPUT_GAMEPAD_X); // Square / X
|
||||
MAP_BUTTON(ImGuiNavInput_Input, XINPUT_GAMEPAD_Y); // Triangle / Y
|
||||
MAP_BUTTON(ImGuiNavInput_DpadLeft, XINPUT_GAMEPAD_DPAD_LEFT); // D-Pad Left
|
||||
MAP_BUTTON(ImGuiNavInput_DpadRight, XINPUT_GAMEPAD_DPAD_RIGHT); // D-Pad Right
|
||||
MAP_BUTTON(ImGuiNavInput_DpadUp, XINPUT_GAMEPAD_DPAD_UP); // D-Pad Up
|
||||
MAP_BUTTON(ImGuiNavInput_DpadDown, XINPUT_GAMEPAD_DPAD_DOWN); // D-Pad Down
|
||||
MAP_BUTTON(ImGuiNavInput_FocusPrev, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_FocusNext, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakSlow, XINPUT_GAMEPAD_LEFT_SHOULDER); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakFast, XINPUT_GAMEPAD_RIGHT_SHOULDER); // R1 / RB
|
||||
MAP_ANALOG(ImGuiNavInput_LStickLeft, gamepad.sThumbLX, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32768);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickRight, gamepad.sThumbLX, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickUp, gamepad.sThumbLY, +XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, +32767);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickDown, gamepad.sThumbLY, -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, -32767);
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void ImGui_ImplWin32_NewFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
RECT rect;
|
||||
::GetClientRect(g_hWnd, &rect);
|
||||
io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top));
|
||||
|
||||
// Setup time step
|
||||
INT64 current_time;
|
||||
::QueryPerformanceCounter((LARGE_INTEGER *)¤t_time);
|
||||
io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond;
|
||||
g_Time = current_time;
|
||||
|
||||
// Read keyboard modifiers inputs
|
||||
io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||
io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
io.KeySuper = false;
|
||||
// io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below.
|
||||
|
||||
// Update OS mouse position
|
||||
ImGui_ImplWin32_UpdateMousePos();
|
||||
|
||||
// Update OS mouse cursor with the cursor requested by imgui
|
||||
ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
|
||||
if (g_LastMouseCursor != mouse_cursor)
|
||||
{
|
||||
g_LastMouseCursor = mouse_cursor;
|
||||
ImGui_ImplWin32_UpdateMouseCursor();
|
||||
}
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
//ImGui_ImplWin32_UpdateGamepads();
|
||||
}
|
||||
|
||||
// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions.
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x020E
|
||||
#endif
|
||||
#ifndef DBT_DEVNODES_CHANGED
|
||||
#define DBT_DEVNODES_CHANGED 0x0007
|
||||
#endif
|
||||
|
||||
// Process Win32 mouse/keyboard inputs.
|
||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||
// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinates when dragging mouse outside of our window bounds.
|
||||
// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag.
|
||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (ImGui::GetCurrentContext() == NULL)
|
||||
return 0;
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
switch (msg)
|
||||
{
|
||||
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
|
||||
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
|
||||
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
|
||||
case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK:
|
||||
{
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; }
|
||||
if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; }
|
||||
if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; }
|
||||
if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||
// if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL)
|
||||
// ::SetCapture(hwnd);
|
||||
io.MouseDown[button] = true;
|
||||
return 0;
|
||||
}
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_XBUTTONUP:
|
||||
{
|
||||
int button = 0;
|
||||
if (msg == WM_LBUTTONUP) { button = 0; }
|
||||
if (msg == WM_RBUTTONUP) { button = 1; }
|
||||
if (msg == WM_MBUTTONUP) { button = 2; }
|
||||
if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; }
|
||||
io.MouseDown[button] = false;
|
||||
// if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd)
|
||||
// ::ReleaseCapture();
|
||||
return 0;
|
||||
}
|
||||
case WM_MOUSEWHEEL:
|
||||
io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||
return 0;
|
||||
case WM_MOUSEHWHEEL:
|
||||
io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
|
||||
return 0;
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
if (wParam < 256)
|
||||
io.KeysDown[wParam] = 1;
|
||||
return 0;
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if (wParam < 256)
|
||||
io.KeysDown[wParam] = 0;
|
||||
return 0;
|
||||
case WM_CHAR:
|
||||
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
|
||||
io.AddInputCharacter((unsigned int)wParam);
|
||||
return 0;
|
||||
case WM_SETCURSOR:
|
||||
if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor())
|
||||
return 1;
|
||||
return 0;
|
||||
case WM_DEVICECHANGE:
|
||||
if ((UINT)wParam == DBT_DEVNODES_CHANGED)
|
||||
g_WantUpdateHasGamepad = true;
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// dear imgui: Platform Binding for Windows (standard windows API for 32 and 64 bits applications)
|
||||
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// [X] Platform: Keyboard arrays indexed using VK_* Virtual Key Codes, e.g. ImGui::IsKeyPressed(VK_SPACE).
|
||||
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
|
||||
#pragma once
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd);
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame();
|
||||
|
||||
// Handler for Win32 messages, update mouse/keyboard data.
|
||||
// You may or not need this for your implementation, but it can serve as reference for handling inputs.
|
||||
// Intentionally commented out to avoid dragging dependencies on <windows.h> types. You can COPY this line into your .cpp code instead.
|
||||
/*
|
||||
IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
*/
|
|
@ -0,0 +1,630 @@
|
|||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 0.99.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Added STBRP__CDECL
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
|
||||
// stb_rect_pack.h - v0.99 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
typedef int stbrp_coord;
|
||||
#else
|
||||
typedef unsigned short stbrp_coord;
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||
#endif
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
context->extra[1].y = (1<<30);
|
||||
#else
|
||||
context->extra[1].y = 65535;
|
||||
#endif
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height < c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
#define STBRP__MAXVAL 0xffffffff
|
||||
#else
|
||||
#define STBRP__MAXVAL 0xffff
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
2
Makefile
|
@ -2,7 +2,7 @@
|
|||
|
||||
CXX=clang++
|
||||
CXX_FLAGS += -fPIC -std=c++11
|
||||
LD_FLAGS += -shared -lprotobuf-lite -Wl,--no-undefined
|
||||
LD_FLAGS += -shared -lprotobuf-lite -ldl -Wl,--no-undefined
|
||||
LIBRARY_NAME=libsteam_api.so
|
||||
RM = rm -f
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ Note that these are global so you won't have to change them for each game. For g
|
|||
If you want to change your steam_id on a per game basis, simply create a settings folder in the game unique directory (Full path: C:\Users\<Your windows user name>\AppData\Roaming\Goldberg SteamEmu Saves\<appid>\settings)
|
||||
In that settings folder create a user_steam_id.txt file that contains the valid steam id that you want to use for that game only.
|
||||
|
||||
You can also make the emu ignore certain global settings by using a force_account_name.txt, force_language.txt or force_steamid.txt that you put in the <path where my emu lib is>\steam_settings\ folder.
|
||||
See the steam_settings.EXAMPLE folder for an example.
|
||||
|
||||
If for some reason you want it to save in the game directory you can create a file named local_save.txt right beside steam_api(64).dll (libsteam_api.so on linux)
|
||||
The only thing that file should contain is the name of the save directory. This can be useful if you want to use different global settings like a different account name or steam id for a particular game.
|
||||
Note that this save directory will be beside where the emu dll (or .so) is which may not be the same as the game path.
|
||||
|
@ -41,6 +44,14 @@ If the DLC file is present, the emulator will only unlock the DLCs in that file.
|
|||
The contents of this file are: appid=DLC name
|
||||
See the steam_settings.EXAMPLE folder for an example.
|
||||
|
||||
Depots:
|
||||
This is pretty rare but some games might use depot ids to see if dlcs are installed. You can provide a list of installed depots to the game with a steam_settings\depots.txt file.
|
||||
See the steam_settings.EXAMPLE folder for an example.
|
||||
|
||||
Subscribed Groups:
|
||||
Some games like payday 2 check which groups you are subscribed in and unlock things based on that. You can provide a list of subscribed groups to the game with a steam_settings\subscribed_groups.txt file.
|
||||
See steam_settings.EXAMPLE\subscribed_groups.EXAMPLE.txt for an example for payday 2.
|
||||
|
||||
App paths:
|
||||
Some rare games might need to be provided one or more paths to app ids. For example the path to where a dlc is installed. This sets the paths returned by the Steam_Apps::GetAppInstallDir function.
|
||||
See steam_settings.EXAMPLE\app_paths.EXAMPLE.txt for an example.
|
||||
|
@ -102,8 +113,11 @@ You must all be on the same LAN for it to work.
|
|||
IMPORTANT:
|
||||
Do not run more than one steam game with the same appid at the same time on the same computer with my emu or there might be network issues (dedicated servers should be fine though).
|
||||
|
||||
Overlay (Note: at the moment this feature is only enabled in the windows experimental builds):
|
||||
The overlay can be disabled by putting a file named disable_overlay.txt in the steam_settings folder. This is for games that depend on the steam overlay to let people join multiplayer games.
|
||||
Use SHIFT-TAB to open the overlay.
|
||||
|
||||
Controller (Note: at the moment this feature is only enabled in the windows experimental builds):
|
||||
Controller (Note: at the moment this feature is only enabled in the windows experimental builds and the linux builds):
|
||||
SteamController/SteamInput support is limited to XInput controllers. If your controller is not XInput, there are many tools (at least for windows) that you can use to make it emulate an XInput one.
|
||||
Steam uses things called action sets for controller configuration. An action set is a group of action names. Action names are bound to buttons, triggers or joysticks.
|
||||
The emulator needs to know for each action set, which button is linked to which action name. Create a ACTION_SET_NAME.txt file in the steam_settings\controller folder for every action set the game uses.
|
||||
|
@ -120,6 +134,9 @@ If you look at: steamcontrollerconfigdetails, you will see something like: 17796
|
|||
1779660455 refers to a file id that you can dl using your favorite steam workshop downloader site.
|
||||
The url would be: https://steamcommunity.com/sharedfiles/filedetails/?id=1779660455
|
||||
|
||||
The glyphs directory contains some glyphs for the controller buttons for the games that use the GetGlyphForActionOrigin function.
|
||||
If you want to use the real steam glyphs instead of the free ones in the example directory copy them from: <Steam Directory>\tenfoot\resource\images\library\controller\api folder.
|
||||
|
||||
Valid digital button names:
|
||||
DUP
|
||||
DDOWN
|
||||
|
|
|
@ -7,6 +7,8 @@ set VS_Base_Path=C:\Program Files (x86)
|
|||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" goto vs2019
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" goto vs2017
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat" goto vs14
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2019_bt
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" goto vs2017_bt
|
||||
if exist ".\sdk_standalone\set_vars64.bat" goto gitlabci
|
||||
|
||||
:vs2019
|
||||
|
@ -21,6 +23,14 @@ goto batend
|
|||
call "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64\vcvars64.bat"
|
||||
goto batend
|
||||
|
||||
:vs2019_bt
|
||||
call "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
|
||||
goto batend
|
||||
|
||||
:vs2017_bt
|
||||
call "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
|
||||
goto batend
|
||||
|
||||
:gitlabci
|
||||
call ".\sdk_standalone\set_vars64.bat"
|
||||
goto batend
|
||||
|
|
|
@ -7,6 +7,8 @@ set VS_Base_Path=C:\Program Files (x86)
|
|||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" goto vs2019
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" goto vs2017
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64_x86\vcvarsamd64_x86.bat" goto vs14
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2019_bt
|
||||
if exist "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat" goto vs2017_bt
|
||||
if exist ".\sdk_standalone\set_vars32.bat" goto gitlabci
|
||||
|
||||
:vs2019
|
||||
|
@ -21,6 +23,15 @@ goto batend
|
|||
call "%VS_Base_Path%\Microsoft Visual Studio 14.0\VC\bin\amd64_x86\vcvarsamd64_x86.bat"
|
||||
goto batend
|
||||
|
||||
:vs2019_bt
|
||||
call "%VS_Base_Path%\Microsoft Visual Studio\2019\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
|
||||
goto batend
|
||||
|
||||
:vs2017_bt
|
||||
call "%VS_Base_Path%\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars32.bat"
|
||||
goto batend
|
||||
|
||||
|
||||
:gitlabci
|
||||
call ".\sdk_standalone\set_vars32.bat"
|
||||
goto batend
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@echo off
|
||||
cd /d "%~dp0"
|
||||
SET PROTOBUF_X86_DIRECTORY=..\vcpkg\packages\protobuf_x86-windows-static
|
||||
SET PROTOBUF_X64_DIRECTORY=..\vcpkg\packages\protobuf_x64-windows-static
|
||||
SET PROTOBUF_X86_DIRECTORY=..\vcpkg\installed\x86-windows-static
|
||||
SET PROTOBUF_X64_DIRECTORY=..\vcpkg\installed\x64-windows-static
|
||||
|
||||
rem location of protoc in protobuf directories:
|
||||
SET PROTOC_X86_EXE=%PROTOBUF_X86_DIRECTORY%\tools\protobuf\protoc.exe
|
||||
|
|
|
@ -5,9 +5,12 @@ mkdir -p linux/x86_64
|
|||
mkdir -p linux/lobby_connect
|
||||
mkdir -p linux/tools
|
||||
cp scripts/find_interfaces.sh linux/tools/
|
||||
cp scripts/steamclient_loader.sh linux/tools/
|
||||
../protobuf/prefix_x86/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
|
||||
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -std=c++11 && echo built32
|
||||
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect32
|
||||
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built32
|
||||
g++ -m32 -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x86 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built_lobby_connect32
|
||||
g++ -m32 -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix_x86/include/ -L../protobuf/prefix_x86/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built32_steamclient
|
||||
../protobuf/prefix/bin/protoc -I./dll/ --cpp_out=./dll/ ./dll/*.proto
|
||||
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -std=c++11 && echo built64
|
||||
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -std=c++11 && echo built_lobby_connect64
|
||||
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/libsteam_api.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built64
|
||||
g++ -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DNDEBUG -DNO_DISK_WRITES -DLOBBY_CONNECT -s -o linux/lobby_connect/lobby_connect_x64 lobby_connect.cpp dll/*.cpp dll/*.cc -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built_lobby_connect64
|
||||
g++ -shared -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -DGNUC -DEMU_RELEASE_BUILD -DSTEAMCLIENT_DLL -DNDEBUG -DCONTROLLER_SUPPORT -s -o linux/x86_64/steamclient.so dll/*.cpp dll/*.cc controller/*.c -Wno-return-type -I../protobuf/prefix/include/ -L../protobuf/prefix/lib/ -lprotobuf-lite -lpthread -ldl -std=c++11 && echo built64_steamclient
|
||||
|
|
|
@ -4,13 +4,13 @@ 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 /I%PROTOBUF_X86_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api.dll
|
||||
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api.dll
|
||||
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient.dll
|
||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
|
||||
|
||||
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||
call build_env_x64.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
cl /LD /I%PROTOBUF_X64_DIRECTORY%\include\ /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /link /OUT:steam_api64.dll
|
||||
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DGLEW_STATIC /DEMU_EXPERIMENTAL_BUILD /DCONTROLLER_SUPPORT /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steam_api64.dll
|
||||
cl /LD steamclient.cpp /EHsc /MP12 /link /OUT:steamclient64.dll
|
||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
cd /d "%~dp0"
|
||||
call build_set_protobuf_directories.bat
|
||||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||
call build_env_x86.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient.dll
|
||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets.dll
|
||||
call build_env_x64.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
cl /LD /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DEMU_OVERLAY dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /link /OUT:steamclient64.dll
|
||||
cl /LD steamnetworkingsockets.cpp /EHsc /MP12 /link /OUT:steamnetworkingsockets64.dll
|
|
@ -11,7 +11,7 @@ call build_set_protobuf_directories.bat
|
|||
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
|
||||
call build_set_protobuf_directories.bat
|
||||
|
||||
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||
call build_env_x64.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
|
|
|
@ -6,11 +6,11 @@ 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 /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api.dll
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib 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 /MP12 /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 /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental\steam_api64.dll
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DGLEW_STATIC /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DNDEBUG /IImGui /Iglew\include /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib 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 /MP12 /Ox /link /OUT:release\experimental\steamclient64.dll
|
||||
copy Readme_experimental.txt release\experimental\Readme.txt
|
||||
|
|
|
@ -6,11 +6,11 @@ call build_set_protobuf_directories.bat
|
|||
"%PROTOC_X86_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||
call build_env_x86.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X86_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient.dll
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DGLEW_STATIC /IImGui /Iglew\include /DNDEBUG /I%PROTOBUF_X86_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X86_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient.dll
|
||||
"%PROTOC_X64_EXE%" -I.\dll\ --cpp_out=.\dll\ .\dll\net.proto
|
||||
cl steamclient_loader/*.cpp advapi32.lib user32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient_loader.exe
|
||||
copy steamclient_loader\ColdClientLoader.ini release\experimental_steamclient\
|
||||
call build_env_x64.bat
|
||||
cl dll/rtlgenrandom.c dll/rtlgenrandom.def
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c "%PROTOBUF_X64_LIBRARY%" Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient64.dll
|
||||
cl /LD /DEMU_RELEASE_BUILD /DEMU_EXPERIMENTAL_BUILD /DSTEAMCLIENT_DLL /DCONTROLLER_SUPPORT /DEMU_OVERLAY /DGLEW_STATIC /IImGui /Iglew\include /DNDEBUG /I%PROTOBUF_X64_DIRECTORY%\include\ dll/*.cpp dll/*.cc detours/*.cpp controller/gamepad.c ImGui/*.cpp ImGui/impls/*.cpp ImGui/impls/windows/*.cpp overlay_experimental/*.cpp overlay_experimental/windows/*.cpp "%PROTOBUF_X64_LIBRARY%" glew\glew.c opengl32.lib Iphlpapi.lib Ws2_32.lib rtlgenrandom.lib Shell32.lib Winmm.lib /EHsc /MP12 /Ox /link /debug:none /OUT:release\experimental_steamclient\steamclient64.dll
|
||||
copy Readme_experimental_steamclient.txt release\experimental_steamclient\Readme.txt
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
# include <stdio.h>
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
# include <libudev.h>
|
||||
# include <dirent.h>
|
||||
# include <sys/stat.h>
|
||||
# include <time.h>
|
||||
#else
|
||||
# error "Unknown platform in gamepad.c"
|
||||
#endif
|
||||
|
@ -60,6 +62,8 @@ struct GAMEPAD_STATE {
|
|||
char* device;
|
||||
int fd;
|
||||
int effect;
|
||||
double axis_min[ABS_MAX];
|
||||
double axis_max[ABS_MAX];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -126,7 +130,8 @@ void GamepadShutdown(void) {
|
|||
/* no Win32 shutdown required */
|
||||
}
|
||||
|
||||
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) {
|
||||
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right, unsigned int rumble_length_ms) {
|
||||
//TODO: rumble_length_ms
|
||||
if ((STATE[gamepad].flags & FLAG_RUMBLE) != 0) {
|
||||
XINPUT_VIBRATION vib;
|
||||
ZeroMemory(&vib, sizeof(vib));
|
||||
|
@ -137,14 +142,103 @@ void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) {
|
|||
}
|
||||
|
||||
#elif defined(__linux__)
|
||||
#define test_bit(nr, addr) \
|
||||
(((1UL << ((nr) % (sizeof(long) * 8))) & ((addr)[(nr) / (sizeof(long) * 8)])) != 0)
|
||||
#define NBITS(x) ((((x)-1)/(sizeof(long) * 8))+1)
|
||||
|
||||
/* UDev handles */
|
||||
static struct udev* UDEV = NULL;
|
||||
static struct udev_monitor* MON = NULL;
|
||||
|
||||
static int IsGamepad(int fd, char *namebuf, const size_t namebuflen)
|
||||
{
|
||||
struct input_id inpid;
|
||||
//uint16_t *guid16 = (uint16_t *)guid->data;
|
||||
|
||||
/* When udev is enabled we only get joystick devices here, so there's no need to test them */
|
||||
unsigned long evbit[NBITS(EV_MAX)] = { 0 };
|
||||
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
|
||||
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
|
||||
|
||||
if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
|
||||
(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
|
||||
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
|
||||
test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(fd, EVIOCGNAME(namebuflen), namebuf) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(fd, EVIOCGID, &inpid) < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//printf("Joystick: %s, bustype = %d, vendor = 0x%.4x, product = 0x%.4x, version = %d\n", namebuf, inpid.bustype, inpid.vendor, inpid.product, inpid.version);
|
||||
|
||||
//memset(guid->data, 0, sizeof(guid->data));
|
||||
|
||||
/* We only need 16 bits for each of these; space them out to fill 128. */
|
||||
/* Byteswap so devices get same GUID on little/big endian platforms. */
|
||||
/*
|
||||
*guid16++ = SDL_SwapLE16(inpid.bustype);
|
||||
*guid16++ = 0;
|
||||
|
||||
if (inpid.vendor && inpid.product) {
|
||||
*guid16++ = SDL_SwapLE16(inpid.vendor);
|
||||
*guid16++ = 0;
|
||||
*guid16++ = SDL_SwapLE16(inpid.product);
|
||||
*guid16++ = 0;
|
||||
*guid16++ = SDL_SwapLE16(inpid.version);
|
||||
*guid16++ = 0;
|
||||
} else {
|
||||
strlcpy((char*)guid16, namebuf, sizeof(guid->data) - 4);
|
||||
}
|
||||
|
||||
if (SDL_ShouldIgnoreJoystick(namebuf, *guid)) {
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void GamepadAddDevice(const char* devPath);
|
||||
static void GamepadRemoveDevice(const char* devPath);
|
||||
|
||||
static void GamepadDetect()
|
||||
{
|
||||
DIR *folder;
|
||||
struct dirent *dent;
|
||||
|
||||
folder = opendir("/dev/input");
|
||||
if (folder) {
|
||||
while ((dent = readdir(folder))) {
|
||||
int len = strlen(dent->d_name);
|
||||
if (len > 5 && strncmp(dent->d_name, "event", 5) == 0) {
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/dev/input/%s", dent->d_name);
|
||||
GamepadAddDevice(path);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(folder);
|
||||
}
|
||||
|
||||
for (int i = 0; i != GAMEPAD_COUNT; ++i) {
|
||||
if ((STATE[i].flags & FLAG_CONNECTED) && STATE[i].device) {
|
||||
struct stat sb;
|
||||
//printf("%s\n", STATE[i].device);
|
||||
if (stat(STATE[i].device, &sb) == -1) {
|
||||
GamepadRemoveDevice(STATE[i].device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Helper to add a new device */
|
||||
static void GamepadAddDevice(const char* devPath) {
|
||||
int i;
|
||||
|
@ -154,11 +248,25 @@ static void GamepadAddDevice(const char* devPath) {
|
|||
if ((STATE[i].flags & FLAG_CONNECTED) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (STATE[i].device && strcmp(devPath, STATE[i].device) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == GAMEPAD_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fd = open(devPath, O_RDWR, 0);
|
||||
if (fd < 0) return;
|
||||
char namebuf[128];
|
||||
int is_gamepad = IsGamepad(fd, namebuf, sizeof (namebuf));
|
||||
if (!is_gamepad) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy the device path */
|
||||
STATE[i].device = strdup(devPath);
|
||||
if (STATE[i].device == NULL) {
|
||||
|
@ -168,25 +276,87 @@ static void GamepadAddDevice(const char* devPath) {
|
|||
/* reset device state */
|
||||
GamepadResetState((GAMEPAD_DEVICE)i);
|
||||
|
||||
/* attempt to open the device in read-write mode, which we need fo rumble */
|
||||
STATE[i].fd = open(STATE[i].device, O_RDWR|O_NONBLOCK);
|
||||
if (STATE[i].fd != -1) {
|
||||
STATE[i].flags = FLAG_CONNECTED|FLAG_RUMBLE;
|
||||
return;
|
||||
}
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
STATE[i].fd = fd;
|
||||
STATE[i].flags |= FLAG_CONNECTED;
|
||||
|
||||
/* attempt to open in read-only mode if access was denied */
|
||||
if (errno == EACCES) {
|
||||
STATE[i].fd = open(STATE[i].device, O_RDONLY|O_NONBLOCK);
|
||||
if (STATE[i].fd != -1) {
|
||||
STATE[i].flags = FLAG_CONNECTED;
|
||||
return;
|
||||
int controller = i;
|
||||
{
|
||||
int i, t;
|
||||
unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
|
||||
unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
|
||||
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
|
||||
unsigned long ffbit[NBITS(FF_MAX)] = { 0 };
|
||||
|
||||
/* See if this device uses the new unified event API */
|
||||
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
|
||||
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
|
||||
(ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0)) {
|
||||
|
||||
/* Get the number of buttons, axes, and other thingamajigs */
|
||||
for (i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
|
||||
if (test_bit(i, keybit)) {
|
||||
//printf("Joystick has button: 0x%x\n", i);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < BTN_JOYSTICK; ++i) {
|
||||
if (test_bit(i, keybit)) {
|
||||
//printf("Joystick has button: 0x%x\n", i);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ABS_MAX; ++i) {
|
||||
/* Skip hats */
|
||||
if (i == ABS_HAT0X) {
|
||||
i = ABS_HAT3Y;
|
||||
continue;
|
||||
}
|
||||
if (test_bit(i, absbit)) {
|
||||
struct input_absinfo absinfo;
|
||||
|
||||
if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
printf("Joystick has absolute axis: 0x%.2x\n", i);
|
||||
printf("Values = { %d, %d, %d, %d, %d }\n",
|
||||
absinfo.value, absinfo.minimum, absinfo.maximum,
|
||||
absinfo.fuzz, absinfo.flat);
|
||||
*/
|
||||
STATE[controller].axis_min[i] = absinfo.minimum;
|
||||
STATE[controller].axis_max[i] = absinfo.maximum;
|
||||
}
|
||||
}
|
||||
for (i = ABS_HAT0X; i <= ABS_HAT3Y; i += 2) {
|
||||
if (test_bit(i, absbit) || test_bit(i + 1, absbit)) {
|
||||
struct input_absinfo absinfo;
|
||||
int hat_index = (i - ABS_HAT0X) / 2;
|
||||
|
||||
if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
printf("Joystick has hat %d\n", hat_index);
|
||||
printf("Values = { %d, %d, %d, %d, %d }\n",
|
||||
absinfo.value, absinfo.minimum, absinfo.maximum,
|
||||
absinfo.fuzz, absinfo.flat);
|
||||
*/
|
||||
//joystick->hwdata->hats_indices[joystick->nhats++] = hat_index;
|
||||
}
|
||||
}
|
||||
if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
|
||||
//++joystick->nballs;
|
||||
}
|
||||
}
|
||||
|
||||
/* could not open the device at all */
|
||||
free(STATE[i].device);
|
||||
STATE[i].device = NULL;
|
||||
if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) {
|
||||
if (test_bit(FF_RUMBLE, ffbit)) {
|
||||
STATE[controller].flags |= FLAG_RUMBLE;
|
||||
}
|
||||
if (test_bit(FF_SINE, ffbit)) {
|
||||
//printf("sine\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to remove a device */
|
||||
|
@ -218,159 +388,145 @@ void GamepadInit(void) {
|
|||
STATE[i].fd = STATE[i].effect = -1;
|
||||
}
|
||||
|
||||
/* open the udev handle */
|
||||
UDEV = udev_new();
|
||||
if (UDEV == NULL) {
|
||||
/* FIXME: flag error? */
|
||||
return;
|
||||
}
|
||||
|
||||
/* open monitoring device (safe to fail) */
|
||||
MON = udev_monitor_new_from_netlink(UDEV, "udev");
|
||||
/* FIXME: flag error if hot-plugging can't be supported? */
|
||||
if (MON != NULL) {
|
||||
udev_monitor_enable_receiving(MON);
|
||||
udev_monitor_filter_add_match_subsystem_devtype(MON, "input", NULL);
|
||||
}
|
||||
|
||||
/* enumerate joypad devices */
|
||||
enu = udev_enumerate_new(UDEV);
|
||||
udev_enumerate_add_match_subsystem(enu, "input");
|
||||
udev_enumerate_scan_devices(enu);
|
||||
devices = udev_enumerate_get_list_entry(enu);
|
||||
|
||||
udev_list_entry_foreach(item, devices) {
|
||||
const char* name;
|
||||
const char* sysPath;
|
||||
const char* devPath;
|
||||
struct udev_device* dev;
|
||||
|
||||
name = udev_list_entry_get_name(item);
|
||||
dev = udev_device_new_from_syspath(UDEV, name);
|
||||
sysPath = udev_device_get_syspath(dev);
|
||||
devPath = udev_device_get_devnode(dev);
|
||||
|
||||
if (sysPath != NULL && devPath != NULL && strstr(sysPath, "/js") != 0) {
|
||||
GamepadAddDevice(devPath);
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
udev_enumerate_unref(enu);
|
||||
GamepadDetect();
|
||||
}
|
||||
|
||||
void GamepadUpdate(void) {
|
||||
if (MON != NULL) {
|
||||
fd_set r;
|
||||
struct timeval tv;
|
||||
int fd = udev_monitor_get_fd(MON);
|
||||
static unsigned long last = 0;
|
||||
unsigned long cur = time(NULL);
|
||||
|
||||
/* set up a poll on the udev device */
|
||||
FD_ZERO(&r);
|
||||
FD_SET(fd, &r);
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
select(fd + 1, &r, 0, 0, &tv);
|
||||
|
||||
/* test if we have a device change */
|
||||
if (FD_ISSET(fd, &r)) {
|
||||
struct udev_device* dev = udev_monitor_receive_device(MON);
|
||||
if (dev) {
|
||||
const char* devNode = udev_device_get_devnode(dev);
|
||||
const char* sysPath = udev_device_get_syspath(dev);
|
||||
const char* action = udev_device_get_action(dev);
|
||||
sysPath = udev_device_get_syspath(dev);
|
||||
action = udev_device_get_action(dev);
|
||||
|
||||
if (strstr(sysPath, "/js") != 0) {
|
||||
if (strcmp(action, "remove") == 0) {
|
||||
GamepadRemoveDevice(devNode);
|
||||
} else if (strcmp(action, "add") == 0) {
|
||||
GamepadAddDevice(devNode);
|
||||
}
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
if (last + 2 < cur) {
|
||||
GamepadDetect();
|
||||
last = cur;
|
||||
}
|
||||
|
||||
GamepadUpdateCommon();
|
||||
}
|
||||
|
||||
static int adjust_values_trigger(double min, double max, double value)
|
||||
{
|
||||
return (((value + (0 - min)) / (max - min)) * 255.0);
|
||||
}
|
||||
|
||||
static int adjust_values_stick(double min, double max, double value)
|
||||
{
|
||||
return (((value + (0 - min)) / (max - min)) * (65535.0)) - 32768.0;
|
||||
}
|
||||
|
||||
static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) {
|
||||
if (STATE[gamepad].flags & FLAG_CONNECTED) {
|
||||
struct js_event je;
|
||||
while (read(STATE[gamepad].fd, &je, sizeof(je)) > 0) {
|
||||
int button;
|
||||
switch (je.type) {
|
||||
case JS_EVENT_BUTTON:
|
||||
/* determine which button the event is for */
|
||||
switch (je.number) {
|
||||
case 0: button = BUTTON_A; break;
|
||||
case 1: button = BUTTON_B; break;
|
||||
case 2: button = BUTTON_X; break;
|
||||
case 3: button = BUTTON_Y; break;
|
||||
case 4: button = BUTTON_LEFT_SHOULDER; break;
|
||||
case 5: button = BUTTON_RIGHT_SHOULDER; break;
|
||||
case 6: button = BUTTON_BACK; break;
|
||||
case 7: button = BUTTON_START; break;
|
||||
case 8: button = 0; break; /* XBOX button */
|
||||
case 9: button = BUTTON_LEFT_THUMB; break;
|
||||
case 10: button = BUTTON_RIGHT_THUMB; break;
|
||||
struct input_event events[32];
|
||||
int i, len;
|
||||
int code;
|
||||
|
||||
while ((len = read(STATE[gamepad].fd, events, (sizeof events))) > 0) {
|
||||
len /= sizeof(events[0]);
|
||||
for (i = 0; i < len; ++i) {
|
||||
int button = 0;
|
||||
code = events[i].code;
|
||||
switch (events[i].type) {
|
||||
case EV_KEY:
|
||||
//printf("EV_KEY %i\n", code);
|
||||
switch (code) {
|
||||
case BTN_SOUTH: button = BUTTON_A; break;
|
||||
case BTN_EAST: button = BUTTON_B; break;
|
||||
case BTN_NORTH: button = BUTTON_X; break;
|
||||
case BTN_WEST: button = BUTTON_Y; break;
|
||||
case BTN_TL: button = BUTTON_LEFT_SHOULDER; break;
|
||||
case BTN_TR: button = BUTTON_RIGHT_SHOULDER; break;
|
||||
case BTN_SELECT: button = BUTTON_BACK; break;
|
||||
case BTN_START: button = BUTTON_START; break;
|
||||
case BTN_MODE: button = 0; break; /* XBOX button */
|
||||
case BTN_THUMBL: button = BUTTON_LEFT_THUMB; break;
|
||||
case BTN_THUMBR: button = BUTTON_RIGHT_THUMB; break;
|
||||
default: button = 0; break;
|
||||
}
|
||||
|
||||
/* set or unset the button */
|
||||
if (je.value) {
|
||||
if (events[i].value) {
|
||||
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(button);
|
||||
} else {
|
||||
STATE[gamepad].bCurrent ^= BUTTON_TO_FLAG(button);
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(button);
|
||||
}
|
||||
|
||||
break;
|
||||
case JS_EVENT_AXIS:
|
||||
/* normalize and store the axis */
|
||||
switch (je.number) {
|
||||
case 0: STATE[gamepad].stick[STICK_LEFT].x = je.value; break;
|
||||
case 1: STATE[gamepad].stick[STICK_LEFT].y = -je.value; break;
|
||||
case 2: STATE[gamepad].trigger[TRIGGER_LEFT].value = (je.value + 32768) >> 8; break;
|
||||
case 3: STATE[gamepad].stick[STICK_RIGHT].x = je.value; break;
|
||||
case 4: STATE[gamepad].stick[STICK_RIGHT].y = -je.value; break;
|
||||
case 5: STATE[gamepad].trigger[TRIGGER_RIGHT].value = (je.value + 32768) >> 8; break;
|
||||
case 6:
|
||||
if (je.value == -32767) {
|
||||
case EV_ABS:
|
||||
switch (code) {
|
||||
case ABS_HAT0X:
|
||||
case ABS_HAT0Y:
|
||||
case ABS_HAT1X:
|
||||
case ABS_HAT1Y:
|
||||
case ABS_HAT2X:
|
||||
case ABS_HAT2Y:
|
||||
case ABS_HAT3X:
|
||||
case ABS_HAT3Y:
|
||||
//code -= ABS_HAT0X;
|
||||
//printf("ABS_HAT %i\n", code);
|
||||
switch(code) {
|
||||
case ABS_HAT0X:
|
||||
if (events[i].value < 0) {
|
||||
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_LEFT);
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
|
||||
} else if (je.value == 32767) {
|
||||
} else if (events[i].value > 0) {
|
||||
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT);
|
||||
} else {
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_LEFT) & ~BUTTON_TO_FLAG(BUTTON_DPAD_RIGHT);
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if (je.value == -32767) {
|
||||
case ABS_HAT0Y:
|
||||
if (events[i].value < 0) {
|
||||
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_UP);
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
|
||||
} else if (je.value == 32767) {
|
||||
} else if (events[i].value > 0) {
|
||||
STATE[gamepad].bCurrent |= BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP);
|
||||
} else {
|
||||
STATE[gamepad].bCurrent &= ~BUTTON_TO_FLAG(BUTTON_DPAD_UP) & ~BUTTON_TO_FLAG(BUTTON_DPAD_DOWN);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
//printf("EV_ABS %i %i\n", code, events[i].value);
|
||||
if (code == ABS_Z || code == ABS_RZ) {
|
||||
int value = adjust_values_trigger(STATE[gamepad].axis_min[code], STATE[gamepad].axis_max[code], events[i].value);
|
||||
switch(code) {
|
||||
case ABS_Z : STATE[gamepad].trigger[TRIGGER_LEFT].value = value; break;
|
||||
case ABS_RZ: STATE[gamepad].trigger[TRIGGER_RIGHT].value = value; break;
|
||||
}
|
||||
} else {
|
||||
int value = adjust_values_stick(STATE[gamepad].axis_min[code], STATE[gamepad].axis_max[code], events[i].value);
|
||||
switch(code) {
|
||||
case ABS_X : STATE[gamepad].stick[STICK_LEFT].x = value; break;
|
||||
case ABS_Y : STATE[gamepad].stick[STICK_LEFT].y = -value; break;
|
||||
case ABS_RX: STATE[gamepad].stick[STICK_RIGHT].x = value; break;
|
||||
case ABS_RY: STATE[gamepad].stick[STICK_RIGHT].y = -value; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EV_REL:
|
||||
switch (code) {
|
||||
case REL_X:
|
||||
case REL_Y:
|
||||
code -= REL_X;
|
||||
//printf("EV_REL %i %i\n", code, events[i].value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EV_SYN:
|
||||
switch (code) {
|
||||
case SYN_DROPPED :
|
||||
//printf("Event SYN_DROPPED detected\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,10 +534,6 @@ static void GamepadUpdateDevice(GAMEPAD_DEVICE gamepad) {
|
|||
void GamepadShutdown(void) {
|
||||
int i;
|
||||
|
||||
/* cleanup udev */
|
||||
udev_monitor_unref(MON);
|
||||
udev_unref(UDEV);
|
||||
|
||||
/* cleanup devices */
|
||||
for (i = 0; i != GAMEPAD_COUNT; ++i) {
|
||||
if (STATE[i].device != NULL) {
|
||||
|
@ -394,33 +546,17 @@ void GamepadShutdown(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) {
|
||||
void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right, unsigned int rumble_length_ms) {
|
||||
if (STATE[gamepad].fd != -1) {
|
||||
struct input_event play;
|
||||
|
||||
/* delete any existing effect */
|
||||
if (STATE[gamepad].effect != -1) {
|
||||
/* stop the effect */
|
||||
play.type = EV_FF;
|
||||
play.code = STATE[gamepad].effect;
|
||||
play.value = 0;
|
||||
|
||||
write(STATE[gamepad].fd, (const void*)&play, sizeof(play));
|
||||
|
||||
/* delete the effect */
|
||||
ioctl(STATE[gamepad].fd, EVIOCRMFF, STATE[gamepad].effect);
|
||||
}
|
||||
|
||||
/* if rumble parameters are non-zero, start the new effect */
|
||||
if (left != 0.f || right != 0.f) {
|
||||
struct ff_effect ff;
|
||||
|
||||
/* define an effect for this rumble setting */
|
||||
ff.type = FF_RUMBLE;
|
||||
ff.id = -1;
|
||||
ff.id = STATE[gamepad].effect;
|
||||
ff.u.rumble.strong_magnitude = (unsigned short)(left * 65535);
|
||||
ff.u.rumble.weak_magnitude = (unsigned short)(right * 65535);
|
||||
ff.replay.length = 5;
|
||||
ff.replay.length = rumble_length_ms;
|
||||
ff.replay.delay = 0;
|
||||
|
||||
/* upload the effect */
|
||||
|
@ -435,7 +571,6 @@ void GamepadSetRumble(GAMEPAD_DEVICE gamepad, float left, float right) {
|
|||
|
||||
write(STATE[gamepad].fd, (const void*)&play, sizeof(play));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !defined(_WIN32) && !defined(__linux__) */
|
||||
|
|
|
@ -232,8 +232,9 @@ GAMEPAD_API GAMEPAD_BOOL GamepadTriggerReleased(GAMEPAD_DEVICE device, GAMEPAD_T
|
|||
* \param device The device to update.
|
||||
* \param left Left motor strengh (0 to 1).
|
||||
* \param right Right motor strengh (0 to 1).
|
||||
* \param rumble_length_ms rumble time in ms (0 = unlimited).
|
||||
*/
|
||||
GAMEPAD_API void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right);
|
||||
GAMEPAD_API void GamepadSetRumble(GAMEPAD_DEVICE device, float left, float right, unsigned int rumble_length_ms);
|
||||
|
||||
/**
|
||||
* Query the position of an analog stick as raw values.
|
||||
|
|
|
@ -7,34 +7,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
#include <windows.h>
|
||||
#include <stddef.h>
|
||||
#pragma warning(push)
|
||||
#if _MSC_VER > 1400
|
||||
#pragma warning(disable:6102 6103) // /analyze warnings
|
||||
#endif
|
||||
#include <strsafe.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
// #define DETOUR_DEBUG 1
|
||||
#define DETOURS_INTERNAL
|
||||
|
||||
#include "detours.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
|
||||
#error detours.h version mismatch
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
|
||||
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
|
||||
|
|
|
@ -7,33 +7,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma warning(disable:4068) // unknown pragma (suppress)
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
#include <windows.h>
|
||||
|
||||
#if (_MSC_VER < 1299)
|
||||
#pragma warning(disable: 4710)
|
||||
#endif
|
||||
|
||||
//#define DETOUR_DEBUG 1
|
||||
#define DETOURS_INTERNAL
|
||||
|
||||
#include "detours.h"
|
||||
|
||||
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
|
||||
#error detours.h version mismatch
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define NOTHROW
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -880,7 +862,8 @@ struct _DETOUR_TRAMPOLINE
|
|||
{
|
||||
// An ARM64 instruction is 4 bytes long.
|
||||
//
|
||||
// The overwrite is always 2 instructions plus a literal, so 16 bytes, 4 instructions.
|
||||
// The overwrite is always composed of 3 instructions (12 bytes) which perform an indirect jump
|
||||
// using _DETOUR_TRAMPOLINE::pbDetour as the address holding the target location.
|
||||
//
|
||||
// Copied instructions can expand.
|
||||
//
|
||||
|
@ -915,7 +898,7 @@ struct _DETOUR_TRAMPOLINE
|
|||
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 184);
|
||||
|
||||
enum {
|
||||
SIZE_OF_JMP = 16
|
||||
SIZE_OF_JMP = 12
|
||||
};
|
||||
|
||||
inline ULONG fetch_opcode(PBYTE pbCode)
|
||||
|
@ -929,6 +912,79 @@ inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
|
|||
pbCode += 4;
|
||||
}
|
||||
|
||||
struct ARM64_INDIRECT_JMP {
|
||||
struct {
|
||||
ULONG Rd : 5;
|
||||
ULONG immhi : 19;
|
||||
ULONG iop : 5;
|
||||
ULONG immlo : 2;
|
||||
ULONG op : 1;
|
||||
} ardp;
|
||||
|
||||
struct {
|
||||
ULONG Rt : 5;
|
||||
ULONG Rn : 5;
|
||||
ULONG imm : 12;
|
||||
ULONG opc : 2;
|
||||
ULONG iop1 : 2;
|
||||
ULONG V : 1;
|
||||
ULONG iop2 : 3;
|
||||
ULONG size : 2;
|
||||
} ldr;
|
||||
|
||||
ULONG br;
|
||||
};
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201)
|
||||
|
||||
union ARM64_INDIRECT_IMM {
|
||||
struct {
|
||||
ULONG64 pad : 12;
|
||||
ULONG64 adrp_immlo : 2;
|
||||
ULONG64 adrp_immhi : 19;
|
||||
};
|
||||
|
||||
LONG64 value;
|
||||
};
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
PBYTE detour_gen_jmp_indirect(BYTE *pbCode, ULONG64 *pbJmpVal)
|
||||
{
|
||||
// adrp x17, [jmpval]
|
||||
// ldr x17, [x17, jmpval]
|
||||
// br x17
|
||||
|
||||
struct ARM64_INDIRECT_JMP *pIndJmp;
|
||||
union ARM64_INDIRECT_IMM jmpIndAddr;
|
||||
|
||||
jmpIndAddr.value = (((LONG64)pbJmpVal) & 0xFFFFFFFFFFFFF000) -
|
||||
(((LONG64)pbCode) & 0xFFFFFFFFFFFFF000);
|
||||
|
||||
pIndJmp = (struct ARM64_INDIRECT_JMP *)pbCode;
|
||||
pbCode = (BYTE *)(pIndJmp + 1);
|
||||
|
||||
pIndJmp->ardp.Rd = 17;
|
||||
pIndJmp->ardp.immhi = jmpIndAddr.adrp_immhi;
|
||||
pIndJmp->ardp.iop = 0x10;
|
||||
pIndJmp->ardp.immlo = jmpIndAddr.adrp_immlo;
|
||||
pIndJmp->ardp.op = 1;
|
||||
|
||||
pIndJmp->ldr.Rt = 17;
|
||||
pIndJmp->ldr.Rn = 17;
|
||||
pIndJmp->ldr.imm = (((ULONG64)pbJmpVal) & 0xFFF) / 8;
|
||||
pIndJmp->ldr.opc = 1;
|
||||
pIndJmp->ldr.iop1 = 1;
|
||||
pIndJmp->ldr.V = 0;
|
||||
pIndJmp->ldr.iop2 = 7;
|
||||
pIndJmp->ldr.size = 3;
|
||||
|
||||
pIndJmp->br = 0xD61F0220;
|
||||
|
||||
return pbCode;
|
||||
}
|
||||
|
||||
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
|
||||
{
|
||||
PBYTE pbLiteral;
|
||||
|
@ -995,7 +1051,7 @@ inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
|
|||
the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the
|
||||
calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or
|
||||
a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address
|
||||
within ±4GB of the current PC.
|
||||
within +/- 4GB of the current PC.
|
||||
|
||||
PC-rel. addressing
|
||||
This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are
|
||||
|
@ -1062,7 +1118,10 @@ inline void detour_find_jmp_bounds(PBYTE pbCode,
|
|||
PDETOUR_TRAMPOLINE *ppLower,
|
||||
PDETOUR_TRAMPOLINE *ppUpper)
|
||||
{
|
||||
// We have to place trampolines within +/- 2GB of code.
|
||||
// The encoding used by detour_gen_jmp_indirect actually enables a
|
||||
// displacement of +/- 4GiB. In the future, this could be changed to
|
||||
// reflect that. For now, just reuse the x86 logic which is plenty.
|
||||
|
||||
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
|
||||
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
|
||||
DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi));
|
||||
|
@ -1250,6 +1309,65 @@ static PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static PVOID detour_alloc_trampoline_allocate_new(PBYTE pbTarget,
|
||||
PDETOUR_TRAMPOLINE pLo,
|
||||
PDETOUR_TRAMPOLINE pHi)
|
||||
{
|
||||
PVOID pbTry = NULL;
|
||||
|
||||
// NB: We must always also start the search at an offset from pbTarget
|
||||
// in order to maintain ASLR entropy.
|
||||
|
||||
#if defined(DETOURS_64BIT)
|
||||
// Try looking 1GB below or lower.
|
||||
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
|
||||
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);
|
||||
}
|
||||
// Try looking 1GB above or higher.
|
||||
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);
|
||||
}
|
||||
// Try looking 1GB below or higher.
|
||||
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);
|
||||
}
|
||||
// Try looking 1GB above or lower.
|
||||
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
|
||||
pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Try anything below.
|
||||
if (pbTry == NULL) {
|
||||
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);
|
||||
}
|
||||
// try anything above.
|
||||
if (pbTry == NULL) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);
|
||||
}
|
||||
|
||||
return pbTry;
|
||||
}
|
||||
|
||||
PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget,
|
||||
_Out_ PDWORD pcbAllocatedSize)
|
||||
{
|
||||
PDETOUR_TRAMPOLINE pLo;
|
||||
PDETOUR_TRAMPOLINE pHi;
|
||||
detour_find_jmp_bounds((PBYTE)pbTarget, &pLo, &pHi);
|
||||
|
||||
PVOID pbNewlyAllocated =
|
||||
detour_alloc_trampoline_allocate_new((PBYTE)pbTarget, pLo, pHi);
|
||||
if (pbNewlyAllocated == NULL) {
|
||||
DETOUR_TRACE(("Couldn't find available memory region!\n"));
|
||||
*pcbAllocatedSize = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*pcbAllocatedSize = DETOUR_REGION_SIZE;
|
||||
return pbNewlyAllocated;
|
||||
}
|
||||
|
||||
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
|
||||
{
|
||||
// We have to place trampolines within +/- 2GB of target.
|
||||
|
@ -1294,41 +1412,10 @@ static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
|
|||
// Round pbTarget down to 64KB block.
|
||||
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
|
||||
|
||||
PVOID pbTry = NULL;
|
||||
|
||||
// NB: We must always also start the search at an offset from pbTarget
|
||||
// in order to maintain ASLR entropy.
|
||||
|
||||
#if defined(DETOURS_64BIT)
|
||||
// Try looking 1GB below or lower.
|
||||
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
|
||||
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);
|
||||
}
|
||||
// Try looking 1GB above or higher.
|
||||
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);
|
||||
}
|
||||
// Try looking 1GB below or higher.
|
||||
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);
|
||||
}
|
||||
// Try looking 1GB above or lower.
|
||||
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
|
||||
pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Try anything below.
|
||||
if (pbTry == NULL) {
|
||||
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);
|
||||
}
|
||||
// try anything above.
|
||||
if (pbTry == NULL) {
|
||||
pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);
|
||||
}
|
||||
|
||||
if (pbTry != NULL) {
|
||||
s_pRegion = (DETOUR_REGION*)pbTry;
|
||||
PVOID pbNewlyAllocated =
|
||||
detour_alloc_trampoline_allocate_new(pbTarget, pLo, pHi);
|
||||
if (pbNewlyAllocated != NULL) {
|
||||
s_pRegion = (DETOUR_REGION*)pbNewlyAllocated;
|
||||
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
|
||||
s_pRegion->pFree = NULL;
|
||||
s_pRegion->pNext = s_pRegions;
|
||||
|
@ -1655,7 +1742,7 @@ LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)
|
|||
#endif // DETOURS_ARM
|
||||
|
||||
#ifdef DETOURS_ARM64
|
||||
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
|
||||
PBYTE pbCode = detour_gen_jmp_indirect(o->pbTarget, (ULONG64*)&(o->pTrampoline->pbDetour));
|
||||
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
|
||||
*o->ppbPointer = o->pTrampoline->rbCode;
|
||||
UNREFERENCED_PARAMETER(pbCode);
|
||||
|
|
|
@ -16,6 +16,34 @@
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
#ifdef DETOURS_INTERNAL
|
||||
|
||||
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
|
||||
#pragma warning(disable:4068) // unknown pragma (suppress)
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#if (_MSC_VER < 1310)
|
||||
#else
|
||||
#pragma warning(push)
|
||||
#if _MSC_VER > 1400
|
||||
#pragma warning(disable:6102 6103) // /analyze warnings
|
||||
#endif
|
||||
#include <strsafe.h>
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // DETOURS_INTERNAL
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
#undef DETOURS_X64
|
||||
#undef DETOURS_X86
|
||||
#undef DETOURS_IA64
|
||||
|
@ -61,7 +89,12 @@
|
|||
//#define DETOURS_OPTION_BITS 32
|
||||
#endif
|
||||
|
||||
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
|
||||
/////////////////////////////////////////////////////////////// Helper Macros.
|
||||
//
|
||||
#define DETOURS_STRINGIFY_(x) #x
|
||||
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
|
||||
|
||||
#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
@ -387,7 +420,6 @@ typedef struct _DETOUR_EXE_RESTORE
|
|||
#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC // some environments do not have this
|
||||
BYTE raw[sizeof(IMAGE_NT_HEADERS64) +
|
||||
sizeof(IMAGE_SECTION_HEADER) * 32];
|
||||
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108);
|
||||
#else
|
||||
BYTE raw[0x108 + sizeof(IMAGE_SECTION_HEADER) * 32];
|
||||
#endif
|
||||
|
@ -396,6 +428,10 @@ typedef struct _DETOUR_EXE_RESTORE
|
|||
|
||||
} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE;
|
||||
|
||||
#ifdef IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
||||
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == 0x108);
|
||||
#endif
|
||||
|
||||
// The size can change, but assert for clarity due to the muddying #ifdefs.
|
||||
#ifdef _WIN64
|
||||
C_ASSERT(sizeof(DETOUR_EXE_RESTORE) == 0x688);
|
||||
|
@ -431,11 +467,6 @@ typedef struct _DETOUR_EXE_HELPER
|
|||
0,\
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////// Helper Macros.
|
||||
//
|
||||
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
|
||||
#define DETOURS_STRINGIFY_(x) #x
|
||||
|
||||
///////////////////////////////////////////////////////////// Binary Typedefs.
|
||||
//
|
||||
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)(
|
||||
|
@ -523,6 +554,8 @@ PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,
|
|||
_Out_opt_ LONG *plExtra);
|
||||
BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule,
|
||||
_In_ BOOL fLimitReferencesToModule);
|
||||
PVOID WINAPI DetourAllocateRegionWithinJumpBounds(_In_ LPCVOID pbTarget,
|
||||
_Out_ PDWORD pcbAllocatedSize);
|
||||
|
||||
///////////////////////////////////////////////////// Loaded Binary Functions.
|
||||
//
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include <detours.h>
|
||||
#else
|
||||
#ifndef DETOURS_STRINGIFY
|
||||
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
|
||||
#define DETOURS_STRINGIFY_(x) #x
|
||||
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
|
||||
#endif
|
||||
|
||||
#define VER_FILEFLAGSMASK 0x3fL
|
||||
|
@ -24,4 +24,4 @@
|
|||
#define VER_FILETYPE 0x00000002L
|
||||
#define VER_FILESUBTYPE 0x00000000L
|
||||
#endif
|
||||
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
|
||||
#define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS)
|
||||
|
|
|
@ -7,28 +7,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
#include <windows.h>
|
||||
#include <limits.h>
|
||||
|
||||
// #define DETOUR_DEBUG 1
|
||||
#define DETOURS_INTERNAL
|
||||
|
||||
#include "detours.h"
|
||||
#include <limits.h>
|
||||
|
||||
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
|
||||
#error detours.h version mismatch
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#undef ASSERT
|
||||
#define ASSERT(x)
|
||||
|
||||
|
@ -260,6 +247,11 @@ class CDetourDis
|
|||
#define ENTRY_CopyFF ENTRY_DataIgnored &CDetourDis::CopyFF
|
||||
#define ENTRY_CopyVex2 ENTRY_DataIgnored &CDetourDis::CopyVex2
|
||||
#define ENTRY_CopyVex3 ENTRY_DataIgnored &CDetourDis::CopyVex3
|
||||
#define ENTRY_CopyEvex ENTRY_DataIgnored &CDetourDis::CopyEvex // 62, 3 byte payload, then normal with implied prefixes like vex
|
||||
#define ENTRY_CopyXop ENTRY_DataIgnored &CDetourDis::CopyXop // 0x8F ... POP /0 or AMD XOP
|
||||
#define ENTRY_CopyBytesXop 5, 5, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm
|
||||
#define ENTRY_CopyBytesXop1 6, 6, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm8
|
||||
#define ENTRY_CopyBytesXop4 9, 9, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm32
|
||||
#define ENTRY_Invalid ENTRY_DataIgnored &CDetourDis::Invalid
|
||||
#define ENTRY_End ENTRY_DataIgnored NULL
|
||||
|
||||
|
@ -289,6 +281,9 @@ class CDetourDis
|
|||
PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
||||
PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
||||
PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc);
|
||||
PBYTE CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p);
|
||||
PBYTE CopyEvex(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
||||
PBYTE CopyXop(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
||||
|
||||
protected:
|
||||
static const COPYENTRY s_rceCopyTable[257];
|
||||
|
@ -303,6 +298,7 @@ class CDetourDis
|
|||
BOOL m_bAddressOverride;
|
||||
BOOL m_bRaxOverride; // AMD64 only
|
||||
BOOL m_bVex;
|
||||
BOOL m_bEvex;
|
||||
BOOL m_bF2;
|
||||
BOOL m_bF3; // x86 only
|
||||
BYTE m_nSegmentOverride;
|
||||
|
@ -337,6 +333,7 @@ CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra)
|
|||
m_bF2 = FALSE;
|
||||
m_bF3 = FALSE;
|
||||
m_bVex = FALSE;
|
||||
m_bEvex = FALSE;
|
||||
|
||||
m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget;
|
||||
m_plExtra = plExtra ? plExtra : &m_lScratchExtra;
|
||||
|
@ -368,8 +365,11 @@ PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
|
|||
{
|
||||
UINT nBytesFixed;
|
||||
|
||||
ASSERT(!m_bVex || pEntry->nFlagBits == 0);
|
||||
ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16);
|
||||
if (m_bVex || m_bEvex)
|
||||
{
|
||||
ASSERT(pEntry->nFlagBits == 0);
|
||||
ASSERT(pEntry->nFixedSize == pEntry->nFixedSize16);
|
||||
}
|
||||
|
||||
UINT const nModOffset = pEntry->nModOffset;
|
||||
UINT const nFlagBits = pEntry->nFlagBits;
|
||||
|
@ -748,33 +748,42 @@ PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
|
|||
return pbOut;
|
||||
}
|
||||
|
||||
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
|
||||
PBYTE CDetourDis::CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p)
|
||||
// m is first instead of last in the hopes of pbDst/pbSrc being
|
||||
// passed along efficiently in the registers they were already in.
|
||||
{
|
||||
static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod };
|
||||
static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 };
|
||||
static const COPYENTRY Invalid = { 0xC4, ENTRY_Invalid };
|
||||
static const COPYENTRY ceInvalid = { 0xC4, ENTRY_Invalid };
|
||||
|
||||
m_bVex = TRUE;
|
||||
REFCOPYENTRY pEntry;
|
||||
switch (m) {
|
||||
default: pEntry = &Invalid; break;
|
||||
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]]; break;
|
||||
case 2: pEntry = &ceF38; break;
|
||||
case 3: pEntry = &ceF3A; break;
|
||||
}
|
||||
|
||||
switch (pbSrc[-1] & 3) { // p in last byte
|
||||
switch (p & 3) {
|
||||
case 0: break;
|
||||
case 1: m_bOperandOverride = TRUE; break;
|
||||
case 2: m_bF3 = TRUE; break;
|
||||
case 3: m_bF2 = TRUE; break;
|
||||
}
|
||||
|
||||
REFCOPYENTRY pEntry;
|
||||
|
||||
switch (m) {
|
||||
default: return Invalid(&ceInvalid, pbDst, pbSrc);
|
||||
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]];
|
||||
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
|
||||
case 2: return CopyBytes(&ceF38, pbDst, pbSrc);
|
||||
case 3: return CopyBytes(&ceF3A, pbDst, pbSrc);
|
||||
}
|
||||
}
|
||||
|
||||
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
|
||||
// m is first instead of last in the hopes of pbDst/pbSrc being
|
||||
// passed along efficiently in the registers they were already in.
|
||||
{
|
||||
m_bVex = TRUE;
|
||||
BYTE const p = (BYTE)(pbSrc[-1] & 3); // p in last byte
|
||||
return CopyVexEvexCommon(m, pbDst, pbSrc, p);
|
||||
}
|
||||
|
||||
|
||||
PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
||||
// 3 byte VEX prefix 0xC4
|
||||
{
|
||||
|
@ -835,6 +844,78 @@ PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
|||
return CopyVexCommon(1, pbDst + 2, pbSrc + 2);
|
||||
}
|
||||
|
||||
PBYTE CDetourDis::CopyEvex(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
||||
// 62, 3 byte payload, x86 with implied prefixes like Vex
|
||||
// for 32bit, mode 0xC0 else fallback to bound /r
|
||||
{
|
||||
// NOTE: Intel and Wikipedia number these differently.
|
||||
// Intel says 0-2, Wikipedia says 1-3.
|
||||
|
||||
BYTE const p0 = pbSrc[1];
|
||||
|
||||
#ifdef DETOURS_X86
|
||||
const static COPYENTRY ceBound = { 0x62, ENTRY_CopyBytes2Mod };
|
||||
if ((p0 & 0xC0) != 0xC0) {
|
||||
return CopyBytes(&ceBound, pbDst, pbSrc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const COPYENTRY ceInvalid = { 0x62, ENTRY_Invalid };
|
||||
|
||||
if ((p0 & 0x0C) != 0)
|
||||
return Invalid(&ceInvalid, pbDst, pbSrc);
|
||||
|
||||
BYTE const p1 = pbSrc[2];
|
||||
|
||||
if ((p1 & 0x04) != 0x04)
|
||||
return Invalid(&ceInvalid, pbDst, pbSrc);
|
||||
|
||||
// Copy 4 byte prefix.
|
||||
*(UNALIGNED ULONG *)pbDst = *(UNALIGNED ULONG*)pbSrc;
|
||||
|
||||
m_bEvex = TRUE;
|
||||
|
||||
#ifdef DETOURS_X64
|
||||
m_bRaxOverride |= !!(p1 & 0x80); // w
|
||||
#endif
|
||||
|
||||
return CopyVexEvexCommon(p0 & 3u, pbDst + 4, pbSrc + 4, p1 & 3u);
|
||||
}
|
||||
|
||||
PBYTE CDetourDis::CopyXop(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
||||
/* 3 byte AMD XOP prefix 0x8F
|
||||
byte0: 0x8F
|
||||
byte1: RXBmmmmm
|
||||
byte2: WvvvvLpp
|
||||
byte3: opcode
|
||||
mmmmm >= 8, else pop
|
||||
mmmmm only otherwise defined for 8, 9, A.
|
||||
pp is like VEX but only instructions with 0 are defined
|
||||
*/
|
||||
{
|
||||
const static COPYENTRY cePop = { 0x8F, ENTRY_CopyBytes2Mod };
|
||||
const static COPYENTRY ceXop = { 0x8F, ENTRY_CopyBytesXop };
|
||||
const static COPYENTRY ceXop1 = { 0x8F, ENTRY_CopyBytesXop1 };
|
||||
const static COPYENTRY ceXop4 = { 0x8F, ENTRY_CopyBytesXop4 };
|
||||
|
||||
BYTE const m = (BYTE)(pbSrc[1] & 0x1F);
|
||||
ASSERT(m <= 10);
|
||||
switch (m)
|
||||
{
|
||||
default:
|
||||
return CopyBytes(&cePop, pbDst, pbSrc);
|
||||
|
||||
case 8: // modrm with 8bit immediate
|
||||
return CopyBytes(&ceXop1, pbDst, pbSrc);
|
||||
|
||||
case 9: // modrm with no immediate
|
||||
return CopyBytes(&ceXop, pbDst, pbSrc);
|
||||
|
||||
case 10: // modrm with 32bit immediate
|
||||
return CopyBytes(&ceXop4, pbDst, pbSrc);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
PBYTE CDetourDis::s_pbModuleBeg = NULL;
|
||||
|
@ -1030,11 +1111,11 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
|
|||
#ifdef DETOURS_X64
|
||||
{ 0x60, ENTRY_Invalid }, // Invalid
|
||||
{ 0x61, ENTRY_Invalid }, // Invalid
|
||||
{ 0x62, ENTRY_Invalid }, // Invalid (not yet implemented Intel EVEX support)
|
||||
{ 0x62, ENTRY_CopyEvex }, // EVEX / AVX512
|
||||
#else
|
||||
{ 0x60, ENTRY_CopyBytes1 }, // PUSHAD
|
||||
{ 0x61, ENTRY_CopyBytes1 }, // POPAD
|
||||
{ 0x62, ENTRY_CopyBytes2Mod }, // BOUND /r
|
||||
{ 0x62, ENTRY_CopyEvex }, // BOUND /r and EVEX / AVX512
|
||||
#endif
|
||||
{ 0x63, ENTRY_CopyBytes2Mod }, // 32bit ARPL /r, 64bit MOVSXD
|
||||
{ 0x64, ENTRY_CopyBytesSegment }, // FS prefix
|
||||
|
@ -1084,7 +1165,7 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
|
|||
{ 0x8C, ENTRY_CopyBytes2Mod }, // MOV /r
|
||||
{ 0x8D, ENTRY_CopyBytes2Mod }, // LEA /r
|
||||
{ 0x8E, ENTRY_CopyBytes2Mod }, // MOV /r
|
||||
{ 0x8F, ENTRY_CopyBytes2Mod }, // POP /0
|
||||
{ 0x8F, ENTRY_CopyXop }, // POP /0 or AMD XOP
|
||||
{ 0x90, ENTRY_CopyBytes1 }, // NOP
|
||||
{ 0x91, ENTRY_CopyBytes1 }, // XCHG
|
||||
{ 0x92, ENTRY_CopyBytes1 }, // XCHG
|
||||
|
|
|
@ -9,39 +9,18 @@
|
|||
// Used for for payloads, byways, and imports.
|
||||
//
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
#include <windows.h>
|
||||
#if _MSC_VER >= 1310
|
||||
#pragma warning(push)
|
||||
#if _MSC_VER > 1400
|
||||
#pragma warning(disable:6102 6103) // /analyze warnings
|
||||
#endif
|
||||
#include <strsafe.h>
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if (_MSC_VER < 1299)
|
||||
#if _MSC_VER < 1299
|
||||
#pragma warning(disable: 4710)
|
||||
#endif
|
||||
|
||||
// #define DETOUR_DEBUG 1
|
||||
#define DETOURS_INTERNAL
|
||||
|
||||
#include "detours.h"
|
||||
|
||||
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
|
||||
#error detours.h version mismatch
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace Detour
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1714,17 +1693,13 @@ BOOL CImage::Write(HANDLE hFile)
|
|||
m_nNextFileAddr = Max(m_SectionHeaders[n].PointerToRawData +
|
||||
m_SectionHeaders[n].SizeOfRawData,
|
||||
m_nNextFileAddr);
|
||||
#if 0
|
||||
m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +
|
||||
m_SectionHeaders[n].Misc.VirtualSize,
|
||||
m_nNextVirtAddr);
|
||||
#else
|
||||
// Old images have VirtualSize == 0 as a matter of course, e.g. NT 3.1.
|
||||
// In which case, use SizeOfRawData instead.
|
||||
m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +
|
||||
(m_SectionHeaders[n].Misc.VirtualSize
|
||||
? m_SectionHeaders[n].Misc.VirtualSize
|
||||
: SectionAlign(m_SectionHeaders[n].SizeOfRawData)),
|
||||
m_nNextVirtAddr);
|
||||
#endif
|
||||
|
||||
m_nExtraOffset = Max(m_nNextFileAddr, m_nExtraOffset);
|
||||
|
||||
|
@ -1857,7 +1832,7 @@ BOOL CImage::Write(HANDLE hFile)
|
|||
for (CImageImportFile *pImportFile = m_pImportFiles;
|
||||
pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {
|
||||
|
||||
ZeroMemory(piidDst, sizeof(piidDst));
|
||||
ZeroMemory(piidDst, sizeof(*piidDst));
|
||||
nameTable.Allocate(pImportFile->m_pszName, (DWORD *)&piidDst->Name);
|
||||
piidDst->TimeDateStamp = 0;
|
||||
piidDst->ForwarderChain = pImportFile->m_nForwarderChain;
|
||||
|
@ -1899,7 +1874,7 @@ BOOL CImage::Write(HANDLE hFile)
|
|||
}
|
||||
piidDst++;
|
||||
}
|
||||
ZeroMemory(piidDst, sizeof(piidDst));
|
||||
ZeroMemory(piidDst, sizeof(*piidDst));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
|
@ -9,27 +9,6 @@
|
|||
// Module enumeration functions.
|
||||
//
|
||||
|
||||
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
|
||||
|
||||
#pragma warning(disable:4068) // unknown pragma (suppress)
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4091) // empty typedef
|
||||
#endif
|
||||
|
||||
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
||||
#include <windows.h>
|
||||
#if (_MSC_VER < 1310)
|
||||
#else
|
||||
#pragma warning(push)
|
||||
#if _MSC_VER > 1400
|
||||
#pragma warning(disable:6102 6103) // /analyze warnings
|
||||
#endif
|
||||
#include <strsafe.h>
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// #define DETOUR_DEBUG 1
|
||||
#define DETOURS_INTERNAL
|
||||
#include "detours.h"
|
||||
|
@ -38,10 +17,6 @@
|
|||
#error detours.h version mismatch
|
||||
#endif
|
||||
|
||||
#if _MSC_VER >= 1900
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
|
||||
#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
|
||||
|
||||
|
@ -164,8 +139,8 @@ PDETOUR_SYM_INFO DetourLoadImageHlp(VOID)
|
|||
return pSymInfo;
|
||||
}
|
||||
|
||||
PVOID WINAPI DetourFindFunction(_In_ PCSTR pszModule,
|
||||
_In_ PCSTR pszFunction)
|
||||
PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule,
|
||||
_In_ LPCSTR pszFunction)
|
||||
{
|
||||
/////////////////////////////////////////////// First, try GetProcAddress.
|
||||
//
|
||||
|
|
127
dll/base.cpp
|
@ -17,13 +17,7 @@
|
|||
|
||||
#include "base.h"
|
||||
|
||||
#ifdef STEAM_WIN32
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
|
||||
#define SystemFunction036 NTAPI SystemFunction036
|
||||
#include <ntsecapi.h>
|
||||
#undef SystemFunction036
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
static void
|
||||
randombytes(char * const buf, const size_t size)
|
||||
|
@ -46,11 +40,6 @@ std::string get_env_variable(std::string name)
|
|||
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int fd = -1;
|
||||
|
||||
static void randombytes(char *buf, size_t size)
|
||||
|
@ -126,32 +115,42 @@ static unsigned generate_account_id()
|
|||
|
||||
CSteamID generate_steam_id_user()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeIndividual);
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeIndividual);
|
||||
}
|
||||
|
||||
static CSteamID generate_steam_anon_user()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonUser);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_server()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeGameServer);
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeGameServer);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_anonserver()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance, k_EUniversePublic, k_EAccountTypeAnonGameServer);
|
||||
}
|
||||
|
||||
CSteamID generate_steam_id_lobby()
|
||||
{
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDesktopInstance | k_EChatInstanceFlagLobby, k_EUniversePublic, k_EAccountTypeChat);
|
||||
return CSteamID(generate_account_id(), k_unSteamUserDefaultInstance | k_EChatInstanceFlagLobby, k_EUniversePublic, k_EAccountTypeChat);
|
||||
}
|
||||
|
||||
#ifndef STEAM_WIN32
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
|
||||
{
|
||||
if (timeout == 0.0) return true;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::duration<double>>(now - old).count() > timeout) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __LINUX__
|
||||
std::string get_lib_path() {
|
||||
std::string dir = "/proc/self/map_files";
|
||||
DIR *dp;
|
||||
|
@ -191,20 +190,35 @@ std::string get_lib_path() {
|
|||
}
|
||||
#endif
|
||||
|
||||
std::string get_full_program_path()
|
||||
std::string get_full_lib_path()
|
||||
{
|
||||
std::string program_path;
|
||||
#if defined(STEAM_WIN32)
|
||||
#if defined(__WINDOWS__)
|
||||
char DllPath[MAX_PATH] = {0};
|
||||
GetModuleFileName((HINSTANCE)&__ImageBase, DllPath, _countof(DllPath));
|
||||
program_path = DllPath;
|
||||
#else
|
||||
program_path = get_lib_path();
|
||||
#endif
|
||||
program_path = program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
|
||||
return program_path;
|
||||
}
|
||||
|
||||
std::string get_full_program_path()
|
||||
{
|
||||
std::string env_program_path = get_env_variable("SteamAppPath");
|
||||
if (env_program_path.length()) {
|
||||
if (env_program_path.back() != PATH_SEPARATOR[0]) {
|
||||
env_program_path = env_program_path.append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
return env_program_path;
|
||||
}
|
||||
|
||||
std::string program_path;
|
||||
program_path = get_full_lib_path();
|
||||
return program_path.substr(0, program_path.rfind(PATH_SEPARATOR)).append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
std::string get_current_path()
|
||||
{
|
||||
std::string path;
|
||||
|
@ -459,8 +473,46 @@ void Auth_Ticket_Manager::Callback(Common_Message *msg)
|
|||
}
|
||||
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
#ifdef STEAM_WIN32
|
||||
#include "../detours/detours.h"
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
struct ips_test {
|
||||
uint32_t ip_from;
|
||||
uint32_t ip_to;
|
||||
};
|
||||
|
||||
static std::vector<struct ips_test> adapter_ips;
|
||||
|
||||
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
adapter_ips.clear();
|
||||
for (unsigned i = 0; i < num_ips; ++i) {
|
||||
struct ips_test ip_a;
|
||||
PRINT_DEBUG("from: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&from[i])[0], ((unsigned char *)&from[i])[1], ((unsigned char *)&from[i])[2], ((unsigned char *)&from[i])[3]);
|
||||
PRINT_DEBUG("to: %hhu.%hhu.%hhu.%hhu\n", ((unsigned char *)&to[i])[0], ((unsigned char *)&to[i])[1], ((unsigned char *)&to[i])[2], ((unsigned char *)&to[i])[3]);
|
||||
ip_a.ip_from = ntohl(from[i]);
|
||||
ip_a.ip_to = ntohl(to[i]);
|
||||
if (ip_a.ip_to < ip_a.ip_from) continue;
|
||||
if ((ip_a.ip_to - ip_a.ip_from) > (1 << 25)) continue;
|
||||
PRINT_DEBUG("added\n");
|
||||
adapter_ips.push_back(ip_a);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_adapter_ip(unsigned char *ip)
|
||||
{
|
||||
uint32_t ip_temp = 0;
|
||||
memcpy(&ip_temp, ip, sizeof(ip_temp));
|
||||
ip_temp = ntohl(ip_temp);
|
||||
|
||||
for (auto &i : adapter_ips) {
|
||||
if (i.ip_from <= ip_temp && ip_temp <= i.ip_to) {
|
||||
PRINT_DEBUG("ADAPTER IP %hhu.%hhu.%hhu.%hhu\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_lan_ip(const sockaddr *addr, int namelen)
|
||||
{
|
||||
|
@ -471,6 +523,7 @@ static bool is_lan_ip(const sockaddr *addr, int namelen)
|
|||
unsigned char ip[4];
|
||||
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
|
||||
PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu:%u\n", ip[0], ip[1], ip[2], ip[3], ntohs(addr_in->sin_port));
|
||||
if (is_adapter_ip(ip)) return true;
|
||||
if (ip[0] == 127) return true;
|
||||
if (ip[0] == 10) return true;
|
||||
if (ip[0] == 192 && ip[1] == 168) return true;
|
||||
|
@ -541,13 +594,6 @@ inline bool file_exists (const std::string& name) {
|
|||
return (stat (name.c_str(), &buffer) == 0);
|
||||
}
|
||||
|
||||
#ifdef DETOURS_64BIT
|
||||
#define DLL_NAME "steam_api64.dll"
|
||||
#else
|
||||
#define DLL_NAME "steam_api.dll"
|
||||
#endif
|
||||
|
||||
|
||||
HMODULE (WINAPI *Real_GetModuleHandleA)(LPCSTR lpModuleName) = GetModuleHandleA;
|
||||
HMODULE WINAPI Mine_GetModuleHandleA(LPCSTR lpModuleName)
|
||||
{
|
||||
|
@ -593,12 +639,6 @@ static void load_dll()
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DETOURS_64BIT
|
||||
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x64.dll"
|
||||
#else
|
||||
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x86.dll"
|
||||
#endif
|
||||
|
||||
static void load_lumaCEG()
|
||||
{
|
||||
std::string path = get_full_program_path();
|
||||
|
@ -645,7 +685,6 @@ bool crack_SteamAPI_Init()
|
|||
|
||||
return false;
|
||||
}
|
||||
#include <winhttp.h>
|
||||
|
||||
HINTERNET (WINAPI *Real_WinHttpConnect)(
|
||||
IN HINTERNET hSession,
|
||||
|
@ -746,6 +785,16 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD dwReason, LPVOID ) {
|
|||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
71
dll/base.h
|
@ -18,57 +18,14 @@
|
|||
#ifndef BASE_INCLUDE
|
||||
#define BASE_INCLUDE
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define STEAM_WIN32
|
||||
#pragma warning( disable : 4716)
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define STEAM_API_EXPORTS
|
||||
#include "../sdk_includes/steam_gameserver.h"
|
||||
#include "../sdk_includes/steamdatagram_tickets.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
//#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
|
||||
#ifdef STEAM_WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <processthreadsapi.h>
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
#define PATH_SEPARATOR "\\"
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
|
||||
#endif
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#define PATH_SEPARATOR "/"
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
|
||||
#endif
|
||||
#endif
|
||||
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
|
||||
#ifdef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...)
|
||||
#endif
|
||||
|
||||
#include "settings.h"
|
||||
#include "local_storage.h"
|
||||
#include "network.h"
|
||||
|
||||
#include "defines.h"
|
||||
#include "common_includes.h"
|
||||
|
||||
#define PUSH_BACK_IF_NOT_IN(vector, element) { if(std::find(vector.begin(), vector.end(), element) == vector.end()) vector.push_back(element); }
|
||||
|
||||
extern std::recursive_mutex global_mutex;
|
||||
|
||||
std::string get_env_variable(std::string name);
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
|
||||
|
||||
class CCallbackMgr
|
||||
{
|
||||
|
@ -89,6 +46,7 @@ public:
|
|||
};
|
||||
|
||||
#define STEAM_CALLRESULT_TIMEOUT 120.0
|
||||
#define STEAM_CALLRESULT_WAIT_FOR_CB 0.01
|
||||
struct Steam_Call_Result {
|
||||
Steam_Call_Result(SteamAPICall_t a, int icb, void *r, unsigned int s, double r_in, bool run_cc_cb) {
|
||||
api_call = a;
|
||||
|
@ -115,7 +73,7 @@ struct Steam_Call_Result {
|
|||
}
|
||||
|
||||
bool can_execute() {
|
||||
return (!to_delete) && call_completed();
|
||||
return (!to_delete) && call_completed() && (has_cb() || check_timedout(created, STEAM_CALLRESULT_WAIT_FOR_CB));
|
||||
}
|
||||
|
||||
bool has_cb() {
|
||||
|
@ -139,10 +97,13 @@ CSteamID generate_steam_id_user();
|
|||
CSteamID generate_steam_id_server();
|
||||
CSteamID generate_steam_id_anonserver();
|
||||
CSteamID generate_steam_id_lobby();
|
||||
std::string get_full_lib_path();
|
||||
std::string get_full_program_path();
|
||||
std::string get_current_path();
|
||||
std::string canonical_path(std::string path);
|
||||
|
||||
#define DEFAULT_CB_TIMEOUT 0.002
|
||||
|
||||
class SteamCallResults {
|
||||
std::vector<struct Steam_Call_Result> callresults;
|
||||
std::vector<class CCallbackBase *> completed_callbacks;
|
||||
|
@ -216,13 +177,15 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
|
||||
SteamAPICall_t addCallResult(SteamAPICall_t api_call, int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
|
||||
auto cb_result = std::find_if(callresults.begin(), callresults.end(), [api_call](struct Steam_Call_Result const& item) { return item.api_call == api_call; });
|
||||
if (cb_result != callresults.end()) {
|
||||
if (cb_result->reserved) {
|
||||
std::chrono::high_resolution_clock::time_point created = cb_result->created;
|
||||
std::vector<class CCallbackBase *> temp_cbs = cb_result->callbacks;
|
||||
*cb_result = Steam_Call_Result(api_call, iCallback, result, size, timeout, run_call_completed_cb);
|
||||
cb_result->callbacks = temp_cbs;
|
||||
cb_result->created = created;
|
||||
return cb_result->api_call;
|
||||
}
|
||||
} else {
|
||||
|
@ -242,7 +205,7 @@ public:
|
|||
return callresults.back().api_call;
|
||||
}
|
||||
|
||||
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=0.0, bool run_call_completed_cb=true) {
|
||||
SteamAPICall_t addCallResult(int iCallback, void *result, unsigned int size, double timeout=DEFAULT_CB_TIMEOUT, bool run_call_completed_cb=true) {
|
||||
return addCallResult(generate_steam_api_call_id(), iCallback, result, size, timeout, run_call_completed_cb);
|
||||
}
|
||||
|
||||
|
@ -395,11 +358,11 @@ public:
|
|||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size) {
|
||||
addCBResult(iCallback, result, size, 0.0, false);
|
||||
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, false);
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size, bool dont_post_if_already) {
|
||||
addCBResult(iCallback, result, size, 0.0, dont_post_if_already);
|
||||
addCBResult(iCallback, result, size, DEFAULT_CB_TIMEOUT, dont_post_if_already);
|
||||
}
|
||||
|
||||
void addCBResult(int iCallback, void *result, unsigned int size, double timeout) {
|
||||
|
@ -423,14 +386,7 @@ public:
|
|||
|
||||
void runCallBacks() {
|
||||
for (auto & c : callbacks) {
|
||||
std::vector<std::vector<char>> res_back = c.second.results;
|
||||
c.second.results.clear();
|
||||
for (auto r : res_back) {
|
||||
for (auto cb: c.second.callbacks) {
|
||||
//PRINT_DEBUG("Calling callback %i\n", cb->GetICallback());
|
||||
//cb->Run(&(r[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -498,6 +454,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void set_adapter_ips(uint32_t *from, uint32_t *to, unsigned num_ips);
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
bool crack_SteamAPI_RestartAppIfNecessary(uint32 unOwnAppID);
|
||||
bool crack_SteamAPI_Init();
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __INCLUDED_COMMON_INCLUDES__
|
||||
#define __INCLUDED_COMMON_INCLUDES__
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
|
||||
#define __WINDOWS_64__
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
|
||||
#define __WINDOWS_32__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(linux)
|
||||
#if defined(__x86_64__)
|
||||
#define __LINUX_64__
|
||||
#else
|
||||
#define __LINUX_32__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__LINUX_32__) || defined(__LINUX_64__)
|
||||
#define __LINUX__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#define STEAM_WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define STEAM_API_EXPORTS
|
||||
|
||||
#if defined(__WINDOWS__)
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#include <iphlpapi.h> // Include winsock2 before this, or winsock2 iphlpapi will be unavailable
|
||||
#include <shlobj.h>
|
||||
|
||||
#define MSG_NOSIGNAL 0
|
||||
|
||||
#define SystemFunction036 NTAPI SystemFunction036
|
||||
#include <ntsecapi.h>
|
||||
#undef SystemFunction036
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(a, ...) do {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, "%u " a, GetCurrentThreadId(), __VA_ARGS__); fclose(t); WSASetLastError(0);} while (0)
|
||||
#endif
|
||||
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
#define PATH_SEPARATOR "\\"
|
||||
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
#include <winhttp.h>
|
||||
|
||||
#include "../detours/detours.h"
|
||||
|
||||
#ifdef DETOURS_64BIT
|
||||
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x64.dll"
|
||||
#define DLL_NAME "steam_api64.dll"
|
||||
#else
|
||||
#define LUMA_CEG_DLL_NAME "LumaCEG_Plugin_x86.dll"
|
||||
#define DLL_NAME "steam_api.dll"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__LINUX__)
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <netdb.h>
|
||||
#include <dlfcn.h>
|
||||
#include <utime.h>
|
||||
|
||||
#define PATH_MAX_STRING_SIZE 512
|
||||
|
||||
#ifndef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...) {FILE *t = fopen("STEAM_LOG.txt", "a"); fprintf(t, __VA_ARGS__); fclose(t);}
|
||||
#endif
|
||||
#define PATH_SEPARATOR "/"
|
||||
#endif
|
||||
//#define PRINT_DEBUG(...) fprintf(stdout, __VA_ARGS__)
|
||||
#ifdef EMU_RELEASE_BUILD
|
||||
#define PRINT_DEBUG(...)
|
||||
#endif
|
||||
|
||||
// C/C++ includes
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <list>
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Other libs includes
|
||||
#include "../json/json.hpp"
|
||||
#include "../controller/gamepad.h"
|
||||
|
||||
// Steamsdk includes
|
||||
#include "../sdk_includes/steam_api.h"
|
||||
#include "../sdk_includes/steam_gameserver.h"
|
||||
#include "../sdk_includes/steamdatagram_tickets.h"
|
||||
|
||||
// Emulator includes
|
||||
#include "net.pb.h"
|
||||
#include "settings.h"
|
||||
#include "local_storage.h"
|
||||
#include "network.h"
|
||||
|
||||
// Emulator defines
|
||||
#define CLIENT_HSTEAMUSER 1
|
||||
#define SERVER_HSTEAMUSER 1
|
||||
|
||||
#define DEFAULT_NAME "Goldberg"
|
||||
#define PROGRAM_NAME "Goldberg SteamEmu"
|
||||
#define DEFAULT_LANGUAGE "english"
|
||||
|
||||
#define LOBBY_CONNECT_APPID ((uint32)-2)
|
||||
|
||||
#endif//__INCLUDED_COMMON_INCLUDES__
|
|
@ -1,12 +0,0 @@
|
|||
//TODO: put these in a common .h
|
||||
#define CLIENT_HSTEAMUSER 12
|
||||
#define SERVER_HSTEAMUSER 13
|
||||
|
||||
#define CLIENT_STEAM_PIPE 5
|
||||
#define SERVER_STEAM_PIPE 6
|
||||
|
||||
#define DEFAULT_NAME "Goldberg"
|
||||
#define PROGRAM_NAME "Goldberg SteamEmu"
|
||||
#define DEFAULT_LANGUAGE "english"
|
||||
|
||||
#define LOBBY_CONNECT_APPID ((uint32)-2)
|
416
dll/dll.cpp
|
@ -15,6 +15,7 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#define STEAM_API_FUNCTIONS_IMPL
|
||||
#include "dll.h"
|
||||
|
||||
|
||||
|
@ -43,7 +44,6 @@ static char old_inventory[128] = "STEAMINVENTORY_INTERFACE_V001";
|
|||
static char old_video[128] = "STEAMVIDEO_INTERFACE_V001";
|
||||
static char old_masterserver_updater[128] = "SteamMasterServerUpdater001";
|
||||
|
||||
#include <fstream>
|
||||
static void load_old_interface_versions()
|
||||
{
|
||||
static bool loaded = false;
|
||||
|
@ -133,6 +133,12 @@ Steam_Client *get_steam_clientserver_old()
|
|||
return get_steam_client();
|
||||
}
|
||||
|
||||
static bool steamclient_has_ipv6_functions_flag;
|
||||
bool steamclient_has_ipv6_functions()
|
||||
{
|
||||
return steamclient_has_ipv6_functions_flag;
|
||||
}
|
||||
|
||||
static void *create_client_interface(const char *ver)
|
||||
{
|
||||
if (strstr(ver, "SteamClient") == ver) {
|
||||
|
@ -162,10 +168,14 @@ static void *create_client_interface(const char *ver)
|
|||
steam_client = (ISteamClient017 *)get_steam_client();
|
||||
} else if (strcmp(ver, "SteamClient018") == 0) {
|
||||
steam_client = (ISteamClient018 *)get_steam_client();
|
||||
} else if (strcmp(ver, "SteamClient019") == 0) {
|
||||
steam_client = (ISteamClient019 *)get_steam_client();
|
||||
} else if (strcmp(ver, STEAMCLIENT_INTERFACE_VERSION) == 0) {
|
||||
steam_client = (ISteamClient *)get_steam_client();
|
||||
steamclient_has_ipv6_functions_flag = true;
|
||||
} else {
|
||||
steam_client = (ISteamClient *)get_steam_client();
|
||||
steamclient_has_ipv6_functions_flag = true;
|
||||
}
|
||||
|
||||
return steam_client;
|
||||
|
@ -182,17 +192,17 @@ STEAMAPI_API void * S_CALLTYPE SteamInternal_CreateInterface( const char *ver )
|
|||
return create_client_interface(ver);
|
||||
}
|
||||
|
||||
static uintp global_counter;
|
||||
struct ContextInitData { void (*pFn)(void* pCtx); uintp counter; CSteamAPIContext ctx; };
|
||||
|
||||
STEAMAPI_API void * S_CALLTYPE SteamInternal_ContextInit( void *pContextInitData )
|
||||
{
|
||||
//PRINT_DEBUG("SteamInternal_ContextInit\n");
|
||||
struct ContextInitData *contextInitData = (struct ContextInitData *)pContextInitData;
|
||||
if (!contextInitData->counter) {
|
||||
if (contextInitData->counter != global_counter) {
|
||||
PRINT_DEBUG("SteamInternal_ContextInit initializing\n");
|
||||
contextInitData->pFn(&contextInitData->ctx);
|
||||
//this is hackish but whatever.
|
||||
if (contextInitData->ctx.SteamUser()) contextInitData->counter = 1;
|
||||
contextInitData->counter = global_counter;
|
||||
}
|
||||
|
||||
return &contextInitData->ctx;
|
||||
|
@ -201,14 +211,19 @@ STEAMAPI_API void * S_CALLTYPE SteamInternal_ContextInit( void *pContextInitData
|
|||
//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.
|
||||
static HSteamPipe user_steam_pipe;
|
||||
STEAMAPI_API bool S_CALLTYPE SteamAPI_Init()
|
||||
{
|
||||
PRINT_DEBUG("SteamAPI_Init called\n");
|
||||
if (user_steam_pipe) return true;
|
||||
#ifdef EMU_EXPERIMENTAL_BUILD
|
||||
crack_SteamAPI_Init();
|
||||
#endif
|
||||
load_old_interface_versions();
|
||||
get_steam_client()->userLogIn();
|
||||
Steam_Client* client = get_steam_client();
|
||||
user_steam_pipe = client->CreateSteamPipe();
|
||||
client->ConnectToGlobalUser(user_steam_pipe);
|
||||
global_counter++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -219,11 +234,57 @@ STEAMAPI_API bool S_CALLTYPE SteamAPI_InitAnonymousUser()
|
|||
return SteamAPI_Init();
|
||||
}
|
||||
|
||||
static ISteamUser *old_user_instance;
|
||||
static ISteamFriends *old_friends_interface;
|
||||
static ISteamUtils *old_utils_interface;
|
||||
static ISteamMatchmaking *old_matchmaking_instance;
|
||||
static ISteamUserStats *old_userstats_instance;
|
||||
static ISteamApps *old_apps_instance;
|
||||
static ISteamMatchmakingServers *old_matchmakingservers_instance;
|
||||
static ISteamNetworking *old_networking_instance;
|
||||
static ISteamRemoteStorage *old_remotestorage_instance;
|
||||
static ISteamScreenshots *old_screenshots_instance;
|
||||
static ISteamHTTP *old_http_instance;
|
||||
static ISteamController *old_controller_instance;
|
||||
static ISteamUGC *old_ugc_instance;
|
||||
static ISteamAppList *old_applist_instance;
|
||||
static ISteamMusic *old_music_instance;
|
||||
static ISteamMusicRemote *old_musicremote_instance;
|
||||
static ISteamHTMLSurface *old_htmlsurface_instance;
|
||||
static ISteamInventory *old_inventory_instance;
|
||||
static ISteamVideo *old_video_instance;
|
||||
static ISteamParentalSettings *old_parental_instance;
|
||||
static ISteamUnifiedMessages *old_unified_instance;
|
||||
|
||||
// SteamAPI_Shutdown should be called during process shutdown if possible.
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_Shutdown()
|
||||
{
|
||||
PRINT_DEBUG("SteamAPI_Shutdown\n");
|
||||
get_steam_client()->clientShutdown();
|
||||
get_steam_client()->BReleaseSteamPipe(user_steam_pipe);
|
||||
user_steam_pipe = 0;
|
||||
--global_counter;
|
||||
old_user_instance = NULL;
|
||||
old_friends_interface = NULL;
|
||||
old_utils_interface = NULL;
|
||||
old_matchmaking_instance = NULL;
|
||||
old_userstats_instance = NULL;
|
||||
old_apps_instance = NULL;
|
||||
old_matchmakingservers_instance = NULL;
|
||||
old_networking_instance = NULL;
|
||||
old_remotestorage_instance = NULL;
|
||||
old_screenshots_instance = NULL;
|
||||
old_http_instance = NULL;
|
||||
old_controller_instance = NULL;
|
||||
old_ugc_instance = NULL;
|
||||
old_applist_instance = NULL;
|
||||
old_music_instance = NULL;
|
||||
old_musicremote_instance = NULL;
|
||||
old_htmlsurface_instance = NULL;
|
||||
old_inventory_instance = NULL;
|
||||
old_video_instance = NULL;
|
||||
old_parental_instance = NULL;
|
||||
old_unified_instance = NULL;
|
||||
}
|
||||
|
||||
// SteamAPI_RestartAppIfNecessary ensures that your executable was launched through Steam.
|
||||
|
@ -296,7 +357,7 @@ STEAMAPI_API void S_CALLTYPE SteamAPI_RunCallbacks()
|
|||
{
|
||||
PRINT_DEBUG("SteamAPI_RunCallbacks\n");
|
||||
get_steam_client()->RunCallbacks(true, false);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1)); //fixes resident evil revelations lagging.
|
||||
//std::this_thread::sleep_for(std::chrono::microseconds(1)); //fixes resident evil revelations lagging. (Seems to work fine without this right now, commenting out)
|
||||
}
|
||||
|
||||
|
||||
|
@ -414,8 +475,7 @@ STEAMAPI_API const char *SteamAPI_GetSteamInstallPath()
|
|||
STEAMAPI_API HSteamPipe SteamAPI_GetHSteamPipe()
|
||||
{
|
||||
PRINT_DEBUG("SteamAPI_GetHSteamPipe\n");
|
||||
if (!get_steam_client()->user_logged_in) return 0;
|
||||
return CLIENT_STEAM_PIPE;
|
||||
return user_steam_pipe;
|
||||
}
|
||||
|
||||
// sets whether or not Steam_RunCallbacks() should do a try {} catch (...) {} around calls to issuing callbacks
|
||||
|
@ -451,27 +511,30 @@ STEAMAPI_API ISteamClient *SteamClient() {
|
|||
load_old_interface_versions();
|
||||
return (ISteamClient *)SteamInternal_CreateInterface(old_client);
|
||||
}
|
||||
STEAMAPI_API ISteamUser *SteamUser() { PRINT_DEBUG("SteamUser()\n");return get_steam_client_old()->GetISteamUser(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_user); }
|
||||
STEAMAPI_API ISteamFriends *SteamFriends() { PRINT_DEBUG("SteamFriends()\n");return get_steam_client_old()->GetISteamFriends(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_friends ); }
|
||||
STEAMAPI_API ISteamUtils *SteamUtils() { PRINT_DEBUG("SteamUtils()\n");return get_steam_client_old()->GetISteamUtils(SteamAPI_GetHSteamPipe(), old_utils); }
|
||||
STEAMAPI_API ISteamMatchmaking *SteamMatchmaking() { PRINT_DEBUG("SteamMatchmaking()\n");return get_steam_client_old()->GetISteamMatchmaking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking); }
|
||||
STEAMAPI_API ISteamUserStats *SteamUserStats() { PRINT_DEBUG("SteamUserStats()\n");return get_steam_client_old()->GetISteamUserStats(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_userstats); }
|
||||
STEAMAPI_API ISteamApps *SteamApps() { PRINT_DEBUG("SteamApps()\n");return get_steam_client_old()->GetISteamApps(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_apps); }
|
||||
STEAMAPI_API ISteamMatchmakingServers *SteamMatchmakingServers() { PRINT_DEBUG("SteamMatchmakingServers()\n");return get_steam_client_old()->GetISteamMatchmakingServers(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking_servers); }
|
||||
STEAMAPI_API ISteamNetworking *SteamNetworking() { PRINT_DEBUG("SteamNetworking()\n");return get_steam_client_old()->GetISteamNetworking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_networking); }
|
||||
STEAMAPI_API ISteamRemoteStorage *SteamRemoteStorage() { PRINT_DEBUG("SteamRemoteStorage()\n");return get_steam_client_old()->GetISteamRemoteStorage(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_remote_storage_interface); }
|
||||
STEAMAPI_API ISteamScreenshots *SteamScreenshots() { PRINT_DEBUG("SteamScreenshots()\n");return get_steam_client_old()->GetISteamScreenshots(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_screenshots); }
|
||||
STEAMAPI_API ISteamHTTP *SteamHTTP() { PRINT_DEBUG("SteamHTTP()\n");return get_steam_client_old()->GetISteamHTTP(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_http); }
|
||||
STEAMAPI_API ISteamController *SteamController() { PRINT_DEBUG("SteamController()\n");return get_steam_client_old()->GetISteamController(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_controller); }
|
||||
STEAMAPI_API ISteamUGC *SteamUGC() { PRINT_DEBUG("SteamUGC()\n");return get_steam_client_old()->GetISteamUGC(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_ugc_interface ); }
|
||||
STEAMAPI_API ISteamAppList *SteamAppList() { PRINT_DEBUG("SteamAppList()\n");return get_steam_client_old()->GetISteamAppList(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_applist); }
|
||||
STEAMAPI_API ISteamMusic *SteamMusic() { PRINT_DEBUG("SteamMusic()\n");return get_steam_client_old()->GetISteamMusic(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music); }
|
||||
STEAMAPI_API ISteamMusicRemote *SteamMusicRemote() { PRINT_DEBUG("SteamMusicRemote()\n");return get_steam_client_old()->GetISteamMusicRemote(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music_remote); }
|
||||
STEAMAPI_API ISteamHTMLSurface *SteamHTMLSurface() { PRINT_DEBUG("SteamHTMLSurface()\n");return get_steam_client_old()->GetISteamHTMLSurface(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_html_surface); }
|
||||
STEAMAPI_API ISteamInventory *SteamInventory() { PRINT_DEBUG("SteamInventory()\n");return get_steam_client_old()->GetISteamInventory(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_inventory); }
|
||||
STEAMAPI_API ISteamVideo *SteamVideo() { PRINT_DEBUG("SteamVideo()\n");return get_steam_client_old()->GetISteamVideo(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_video); }
|
||||
STEAMAPI_API ISteamParentalSettings *SteamParentalSettings() { PRINT_DEBUG("SteamParentalSettings()\n");return get_steam_client_old()->GetISteamParentalSettings(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), ""); }
|
||||
STEAMAPI_API ISteamUnifiedMessages *SteamUnifiedMessages() { PRINT_DEBUG("SteamUnifiedMessages()\n");return get_steam_client_old()->GetISteamUnifiedMessages(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_unified_messages); }
|
||||
|
||||
#define CACHE_OLDSTEAM_INSTANCE(variable, get_func) { if (variable) return variable; else return variable = (get_func); }
|
||||
|
||||
STEAMAPI_API ISteamUser *SteamUser() { PRINT_DEBUG("SteamUser()\n"); CACHE_OLDSTEAM_INSTANCE(old_user_instance, get_steam_client_old()->GetISteamUser(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_user)) }
|
||||
STEAMAPI_API ISteamFriends *SteamFriends() { PRINT_DEBUG("SteamFriends()\n"); CACHE_OLDSTEAM_INSTANCE(old_friends_interface, get_steam_client_old()->GetISteamFriends(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_friends )) }
|
||||
STEAMAPI_API ISteamUtils *SteamUtils() { PRINT_DEBUG("SteamUtils()\n"); CACHE_OLDSTEAM_INSTANCE(old_utils_interface, get_steam_client_old()->GetISteamUtils(SteamAPI_GetHSteamPipe(), old_utils)) }
|
||||
STEAMAPI_API ISteamMatchmaking *SteamMatchmaking() { PRINT_DEBUG("SteamMatchmaking()\n"); CACHE_OLDSTEAM_INSTANCE(old_matchmaking_instance, get_steam_client_old()->GetISteamMatchmaking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking)) }
|
||||
STEAMAPI_API ISteamUserStats *SteamUserStats() { PRINT_DEBUG("SteamUserStats()\n"); CACHE_OLDSTEAM_INSTANCE(old_userstats_instance, get_steam_client_old()->GetISteamUserStats(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_userstats)) }
|
||||
STEAMAPI_API ISteamApps *SteamApps() { PRINT_DEBUG("SteamApps()\n"); CACHE_OLDSTEAM_INSTANCE(old_apps_instance, get_steam_client_old()->GetISteamApps(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_apps)) }
|
||||
STEAMAPI_API ISteamMatchmakingServers *SteamMatchmakingServers() { PRINT_DEBUG("SteamMatchmakingServers()\n"); CACHE_OLDSTEAM_INSTANCE(old_matchmakingservers_instance, get_steam_client_old()->GetISteamMatchmakingServers(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_matchmaking_servers)) }
|
||||
STEAMAPI_API ISteamNetworking *SteamNetworking() { PRINT_DEBUG("SteamNetworking()\n"); CACHE_OLDSTEAM_INSTANCE(old_networking_instance, get_steam_client_old()->GetISteamNetworking(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_networking)) }
|
||||
STEAMAPI_API ISteamRemoteStorage *SteamRemoteStorage() { PRINT_DEBUG("SteamRemoteStorage()\n"); CACHE_OLDSTEAM_INSTANCE(old_remotestorage_instance, get_steam_client_old()->GetISteamRemoteStorage(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_remote_storage_interface)) }
|
||||
STEAMAPI_API ISteamScreenshots *SteamScreenshots() { PRINT_DEBUG("SteamScreenshots()\n"); CACHE_OLDSTEAM_INSTANCE(old_screenshots_instance, get_steam_client_old()->GetISteamScreenshots(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_screenshots)) }
|
||||
STEAMAPI_API ISteamHTTP *SteamHTTP() { PRINT_DEBUG("SteamHTTP()\n"); CACHE_OLDSTEAM_INSTANCE(old_http_instance, get_steam_client_old()->GetISteamHTTP(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_http)) }
|
||||
STEAMAPI_API ISteamController *SteamController() { PRINT_DEBUG("SteamController()\n"); CACHE_OLDSTEAM_INSTANCE(old_controller_instance, get_steam_client_old()->GetISteamController(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_controller)) }
|
||||
STEAMAPI_API ISteamUGC *SteamUGC() { PRINT_DEBUG("SteamUGC()\n"); CACHE_OLDSTEAM_INSTANCE(old_ugc_instance, get_steam_client_old()->GetISteamUGC(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_ugc_interface )) }
|
||||
STEAMAPI_API ISteamAppList *SteamAppList() { PRINT_DEBUG("SteamAppList()\n"); CACHE_OLDSTEAM_INSTANCE(old_applist_instance, get_steam_client_old()->GetISteamAppList(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_applist)) }
|
||||
STEAMAPI_API ISteamMusic *SteamMusic() { PRINT_DEBUG("SteamMusic()\n"); CACHE_OLDSTEAM_INSTANCE(old_music_instance, get_steam_client_old()->GetISteamMusic(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music)) }
|
||||
STEAMAPI_API ISteamMusicRemote *SteamMusicRemote() { PRINT_DEBUG("SteamMusicRemote()\n"); CACHE_OLDSTEAM_INSTANCE(old_musicremote_instance, get_steam_client_old()->GetISteamMusicRemote(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_music_remote)) }
|
||||
STEAMAPI_API ISteamHTMLSurface *SteamHTMLSurface() { PRINT_DEBUG("SteamHTMLSurface()\n"); CACHE_OLDSTEAM_INSTANCE(old_htmlsurface_instance, get_steam_client_old()->GetISteamHTMLSurface(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_html_surface)) }
|
||||
STEAMAPI_API ISteamInventory *SteamInventory() { PRINT_DEBUG("SteamInventory()\n"); CACHE_OLDSTEAM_INSTANCE(old_inventory_instance, get_steam_client_old()->GetISteamInventory(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_inventory)) }
|
||||
STEAMAPI_API ISteamVideo *SteamVideo() { PRINT_DEBUG("SteamVideo()\n"); CACHE_OLDSTEAM_INSTANCE(old_video_instance, get_steam_client_old()->GetISteamVideo(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_video)) }
|
||||
STEAMAPI_API ISteamParentalSettings *SteamParentalSettings() { PRINT_DEBUG("SteamParentalSettings()\n"); CACHE_OLDSTEAM_INSTANCE(old_parental_instance, get_steam_client_old()->GetISteamParentalSettings(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), "")) }
|
||||
STEAMAPI_API ISteamUnifiedMessages *SteamUnifiedMessages() { PRINT_DEBUG("SteamUnifiedMessages()\n"); CACHE_OLDSTEAM_INSTANCE(old_unified_instance, get_steam_client_old()->GetISteamUnifiedMessages(SteamAPI_GetHSteamUser(), SteamAPI_GetHSteamPipe(), old_unified_messages)) }
|
||||
|
||||
|
||||
//Gameserver stuff
|
||||
|
@ -482,11 +545,11 @@ STEAMAPI_API void * S_CALLTYPE SteamGameServerInternal_CreateInterface( const ch
|
|||
return SteamInternal_CreateInterface(ver);
|
||||
}
|
||||
|
||||
static HSteamPipe server_steam_pipe;
|
||||
STEAMAPI_API HSteamPipe S_CALLTYPE SteamGameServer_GetHSteamPipe()
|
||||
{
|
||||
PRINT_DEBUG("SteamGameServer_GetHSteamPipe\n");
|
||||
if (!get_steam_client()->server_init) return 0;
|
||||
return SERVER_STEAM_PIPE;
|
||||
return server_steam_pipe;
|
||||
}
|
||||
|
||||
STEAMAPI_API HSteamUser S_CALLTYPE SteamGameServer_GetHSteamUser()
|
||||
|
@ -531,10 +594,13 @@ STEAMAPI_API bool S_CALLTYPE SteamInternal_GameServer_Init( uint32 unIP, uint16
|
|||
{
|
||||
PRINT_DEBUG("SteamInternal_GameServer_Init %u %hu %hu %hu %u %s\n", unIP, usPort, usGamePort, usQueryPort, eServerMode, pchVersionString);
|
||||
load_old_interface_versions();
|
||||
get_steam_client()->serverInit();
|
||||
get_steam_client()->CreateLocalUser(&server_steam_pipe, k_EAccountTypeGameServer);
|
||||
++global_counter;
|
||||
//g_pSteamClientGameServer is only used in pre 1.37 (where the interface versions are not provided by the game)
|
||||
g_pSteamClientGameServer = SteamGameServerClient();
|
||||
return get_steam_client()->steam_gameserver->InitGameServer(unIP, usGamePort, usQueryPort, eServerMode, 0, pchVersionString);
|
||||
uint32 unFlags = 0;
|
||||
if (eServerMode == eServerModeAuthenticationAndSecure) unFlags = k_unServerFlagSecure;
|
||||
return get_steam_client()->steam_gameserver->InitGameServer(unIP, usGamePort, usQueryPort, unFlags, 0, pchVersionString);
|
||||
}
|
||||
|
||||
//SteamGameServer004 and before:
|
||||
|
@ -570,11 +636,33 @@ STEAMAPI_API bool SteamGameServer_Init( uint32 unIP, uint16 usSteamPort, uint16
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ISteamGameServer *old_gameserver_instance;
|
||||
static ISteamUtils *old_gamserver_utils_instance;
|
||||
static ISteamNetworking *old_gamserver_networking_instance;
|
||||
static ISteamGameServerStats *old_gamserver_stats_instance;
|
||||
static ISteamHTTP *old_gamserver_http_instance;
|
||||
static ISteamInventory *old_gamserver_inventory_instance;
|
||||
static ISteamUGC *old_gamserver_ugc_instance;
|
||||
static ISteamApps *old_gamserver_apps_instance;
|
||||
static ISteamMasterServerUpdater *old_gamserver_masterupdater_instance;
|
||||
|
||||
STEAMAPI_API void SteamGameServer_Shutdown()
|
||||
{
|
||||
PRINT_DEBUG("SteamGameServer_Shutdown\n");
|
||||
get_steam_clientserver_old()->serverShutdown();
|
||||
get_steam_client()->serverShutdown();
|
||||
get_steam_client()->BReleaseSteamPipe(server_steam_pipe);
|
||||
server_steam_pipe = 0;
|
||||
--global_counter;
|
||||
g_pSteamClientGameServer = NULL; //TODO: check if this actually gets nulled when SteamGameServer_Shutdown is called
|
||||
old_gameserver_instance = NULL;
|
||||
old_gamserver_utils_instance = NULL;
|
||||
old_gamserver_networking_instance = NULL;
|
||||
old_gamserver_stats_instance = NULL;
|
||||
old_gamserver_http_instance = NULL;
|
||||
old_gamserver_inventory_instance = NULL;
|
||||
old_gamserver_ugc_instance = NULL;
|
||||
old_gamserver_apps_instance = NULL;
|
||||
old_gamserver_masterupdater_instance = NULL;
|
||||
}
|
||||
|
||||
STEAMAPI_API void SteamGameServer_RunCallbacks()
|
||||
|
@ -601,16 +689,16 @@ STEAMAPI_API ISteamClient *SteamGameServerClient() {
|
|||
return (ISteamClient *)SteamInternal_CreateInterface(old_client);
|
||||
}
|
||||
|
||||
STEAMAPI_API ISteamGameServer *SteamGameServer() { PRINT_DEBUG("SteamGameServer()\n"); return get_steam_clientserver_old()->GetISteamGameServer(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver ); }
|
||||
STEAMAPI_API ISteamUtils *SteamGameServerUtils() { PRINT_DEBUG("SteamGameServerUtils()\n"); return get_steam_clientserver_old()->GetISteamUtils(SteamGameServer_GetHSteamPipe(), old_utils ); }
|
||||
STEAMAPI_API ISteamNetworking *SteamGameServerNetworking() { PRINT_DEBUG("SteamGameServerNetworking()\n"); return get_steam_clientserver_old()->GetISteamNetworking(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_networking ); }
|
||||
STEAMAPI_API ISteamGameServerStats *SteamGameServerStats() { PRINT_DEBUG("SteamGameServerStats()\n"); return get_steam_clientserver_old()->GetISteamGameServerStats(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver_stats ); }
|
||||
STEAMAPI_API ISteamHTTP *SteamGameServerHTTP() { PRINT_DEBUG("SteamGameServerHTTP()\n"); return get_steam_clientserver_old()->GetISteamHTTP(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_http ); }
|
||||
STEAMAPI_API ISteamInventory *SteamGameServerInventory() { PRINT_DEBUG("SteamGameServerInventory()\n"); return get_steam_clientserver_old()->GetISteamInventory(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_inventory ); }
|
||||
STEAMAPI_API ISteamUGC *SteamGameServerUGC() { PRINT_DEBUG("SteamGameServerUGC()\n"); return get_steam_clientserver_old()->GetISteamUGC(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_ugc_interface ); }
|
||||
STEAMAPI_API ISteamApps *SteamGameServerApps() { PRINT_DEBUG("SteamGameServerApps()\n"); return get_steam_clientserver_old()->GetISteamApps(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_apps ); }
|
||||
STEAMAPI_API ISteamGameServer *SteamGameServer() { PRINT_DEBUG("SteamGameServer()\n"); CACHE_OLDSTEAM_INSTANCE(old_gameserver_instance, get_steam_clientserver_old()->GetISteamGameServer(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver )) }
|
||||
STEAMAPI_API ISteamUtils *SteamGameServerUtils() { PRINT_DEBUG("SteamGameServerUtils()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_utils_instance, get_steam_clientserver_old()->GetISteamUtils(SteamGameServer_GetHSteamPipe(), old_utils )) }
|
||||
STEAMAPI_API ISteamNetworking *SteamGameServerNetworking() { PRINT_DEBUG("SteamGameServerNetworking()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_networking_instance, get_steam_clientserver_old()->GetISteamNetworking(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_networking )) }
|
||||
STEAMAPI_API ISteamGameServerStats *SteamGameServerStats() { PRINT_DEBUG("SteamGameServerStats()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_stats_instance, get_steam_clientserver_old()->GetISteamGameServerStats(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_gameserver_stats )) }
|
||||
STEAMAPI_API ISteamHTTP *SteamGameServerHTTP() { PRINT_DEBUG("SteamGameServerHTTP()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_http_instance, get_steam_clientserver_old()->GetISteamHTTP(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_http )) }
|
||||
STEAMAPI_API ISteamInventory *SteamGameServerInventory() { PRINT_DEBUG("SteamGameServerInventory()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_inventory_instance, get_steam_clientserver_old()->GetISteamInventory(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_inventory )) }
|
||||
STEAMAPI_API ISteamUGC *SteamGameServerUGC() { PRINT_DEBUG("SteamGameServerUGC()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_ugc_instance, get_steam_clientserver_old()->GetISteamUGC(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_ugc_interface )) }
|
||||
STEAMAPI_API ISteamApps *SteamGameServerApps() { PRINT_DEBUG("SteamGameServerApps()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_apps_instance, get_steam_clientserver_old()->GetISteamApps(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_apps )) }
|
||||
|
||||
STEAMAPI_API ISteamMasterServerUpdater *SteamMasterServerUpdater() {PRINT_DEBUG("SteamMasterServerUpdater()\n"); return get_steam_clientserver_old()->GetISteamMasterServerUpdater(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_masterserver_updater); }
|
||||
STEAMAPI_API ISteamMasterServerUpdater *SteamMasterServerUpdater() {PRINT_DEBUG("SteamMasterServerUpdater()\n"); CACHE_OLDSTEAM_INSTANCE(old_gamserver_masterupdater_instance, get_steam_clientserver_old()->GetISteamMasterServerUpdater(SteamGameServer_GetHSteamUser(), SteamGameServer_GetHSteamPipe(), old_masterserver_updater)) }
|
||||
|
||||
STEAMAPI_API uint32 SteamGameServer_GetIPCCallCount()
|
||||
{
|
||||
|
@ -620,12 +708,161 @@ STEAMAPI_API uint32 SteamGameServer_GetIPCCallCount()
|
|||
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_UseBreakpadCrashHandler( char const *pchVersion, char const *pchDate, char const *pchTime, bool bFullMemoryDumps, void *pvContext, PFNPreMinidumpCallback m_pfnPreMinidumpCallback )
|
||||
{
|
||||
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_SetBreakpadAppID( uint32 unAppID )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
struct cb_data {
|
||||
int cb_id;
|
||||
std::vector<char> result;
|
||||
};
|
||||
static std::queue<struct cb_data> client_cb;
|
||||
static std::queue<struct cb_data> server_cb;
|
||||
|
||||
static void cb_add_queue_server(std::vector<char> result, int callback)
|
||||
{
|
||||
struct cb_data cb;
|
||||
cb.cb_id = callback;
|
||||
cb.result = result;
|
||||
server_cb.push(cb);
|
||||
}
|
||||
|
||||
static void cb_add_queue_client(std::vector<char> result, int callback)
|
||||
{
|
||||
struct cb_data cb;
|
||||
cb.cb_id = callback;
|
||||
cb.result = result;
|
||||
client_cb.push(cb);
|
||||
}
|
||||
|
||||
/// Inform the API that you wish to use manual event dispatch. This must be called after SteamAPI_Init, but before
|
||||
/// you use any of the other manual dispatch functions below.
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_ManualDispatch_Init()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
steam_client->callback_results_server->setCbAll(&cb_add_queue_server);
|
||||
steam_client->callback_results_client->setCbAll(&cb_add_queue_client);
|
||||
}
|
||||
|
||||
/// Perform certain periodic actions that need to be performed.
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_ManualDispatch_RunFrame( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s %i\n", __FUNCTION__, hSteamPipe);
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
if (!steam_client->steam_pipes.count(hSteamPipe)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_client->RunCallbacks(false, true);
|
||||
} else if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::CLIENT) {
|
||||
steam_client->RunCallbacks(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch the next pending callback on the given pipe, if any. If a callback is available, true is returned
|
||||
/// and the structure is populated. In this case, you MUST call SteamAPI_ManualDispatch_FreeLastCallback
|
||||
/// (after dispatching the callback) before calling SteamAPI_ManualDispatch_GetNextCallback again.
|
||||
STEAMAPI_API bool S_CALLTYPE SteamAPI_ManualDispatch_GetNextCallback( HSteamPipe hSteamPipe, CallbackMsg_t *pCallbackMsg )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
std::queue<struct cb_data> *q = NULL;
|
||||
HSteamUser m_hSteamUser = 0;
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
if (!steam_client->steamclient_server_inited) {
|
||||
while(!server_cb.empty()) server_cb.pop();
|
||||
}
|
||||
|
||||
if (!steam_client->steam_pipes.count(hSteamPipe)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
q = &server_cb;
|
||||
m_hSteamUser = SERVER_HSTEAMUSER;
|
||||
} else if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::CLIENT) {
|
||||
q = &client_cb;
|
||||
m_hSteamUser = CLIENT_HSTEAMUSER;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (q->empty()) return false;
|
||||
if (pCallbackMsg) {
|
||||
pCallbackMsg->m_hSteamUser = m_hSteamUser;
|
||||
pCallbackMsg->m_iCallback = q->front().cb_id;
|
||||
pCallbackMsg->m_pubParam = (uint8 *)&(q->front().result[0]);
|
||||
pCallbackMsg->m_cubParam = q->front().result.size();
|
||||
PRINT_DEBUG("Steam_BGetCallback cb number %i\n", q->front().cb_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// You must call this after dispatching the callback, if SteamAPI_ManualDispatch_GetNextCallback returns true.
|
||||
STEAMAPI_API void S_CALLTYPE SteamAPI_ManualDispatch_FreeLastCallback( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s %i\n", __FUNCTION__, hSteamPipe);
|
||||
std::queue<struct cb_data> *q = NULL;
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
if (!steam_client->steam_pipes.count(hSteamPipe)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
q = &server_cb;
|
||||
} else if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::CLIENT) {
|
||||
q = &client_cb;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!q->empty()) q->pop();
|
||||
}
|
||||
|
||||
/// Return the call result for the specified call on the specified pipe. You really should
|
||||
/// only call this in a handler for SteamAPICallCompleted_t callback.
|
||||
STEAMAPI_API bool S_CALLTYPE SteamAPI_ManualDispatch_GetAPICallResult( HSteamPipe hSteamPipe, SteamAPICall_t hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed )
|
||||
{
|
||||
PRINT_DEBUG("SteamAPI_ManualDispatch_GetAPICallResult %i %llu %i %i\n", hSteamPipe, hSteamAPICall, cubCallback, iCallbackExpected);
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
if (!steam_client->steam_pipes.count(hSteamPipe)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
return get_steam_client()->steam_gameserver_utils->GetAPICallResult(hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
} else if (steam_client->steam_pipes[hSteamPipe] == Steam_Pipe::CLIENT) {
|
||||
return get_steam_client()->steam_utils->GetAPICallResult(hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HSteamUser flat_hsteamuser()
|
||||
{
|
||||
return SteamAPI_GetHSteamUser();
|
||||
}
|
||||
|
||||
HSteamPipe flat_hsteampipe()
|
||||
{
|
||||
return SteamAPI_GetHSteamPipe();
|
||||
}
|
||||
|
||||
HSteamUser flat_gs_hsteamuser()
|
||||
{
|
||||
return SteamGameServer_GetHSteamUser();
|
||||
}
|
||||
|
||||
HSteamPipe flat_gs_hsteampipe()
|
||||
{
|
||||
return SteamGameServer_GetHSteamPipe();
|
||||
}
|
||||
|
||||
//VR stuff
|
||||
|
@ -722,80 +959,26 @@ SteamMasterServerUpdater
|
|||
|
||||
*/
|
||||
|
||||
struct cb_data {
|
||||
int cb_id;
|
||||
std::vector<char> result;
|
||||
};
|
||||
static std::queue<struct cb_data> client_cb;
|
||||
static std::queue<struct cb_data> server_cb;
|
||||
|
||||
static void cb_add_queue_server(std::vector<char> result, int callback)
|
||||
{
|
||||
struct cb_data cb;
|
||||
cb.cb_id = callback;
|
||||
cb.result = result;
|
||||
server_cb.push(cb);
|
||||
}
|
||||
|
||||
static void cb_add_queue_client(std::vector<char> result, int callback)
|
||||
{
|
||||
struct cb_data cb;
|
||||
cb.cb_id = callback;
|
||||
cb.result = result;
|
||||
client_cb.push(cb);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BGetCallback( HSteamPipe hSteamPipe, CallbackMsg_t *pCallbackMsg )
|
||||
{
|
||||
PRINT_DEBUG("%s %i\n", __FUNCTION__, hSteamPipe);
|
||||
std::queue<struct cb_data> *q = NULL;
|
||||
HSteamUser m_hSteamUser = 0;
|
||||
get_steam_client()->callback_results_server->setCbAll(&cb_add_queue_server);
|
||||
get_steam_client()->callback_results_client->setCbAll(&cb_add_queue_client);
|
||||
get_steam_client()->RunCallbacks(true, true);
|
||||
if (hSteamPipe == SERVER_STEAM_PIPE) {
|
||||
q = &server_cb;
|
||||
m_hSteamUser = SERVER_HSTEAMUSER;
|
||||
} else {
|
||||
q = &client_cb;
|
||||
m_hSteamUser = CLIENT_HSTEAMUSER;
|
||||
}
|
||||
|
||||
if (q->empty()) return false;
|
||||
if (pCallbackMsg) {
|
||||
pCallbackMsg->m_hSteamUser = m_hSteamUser;
|
||||
pCallbackMsg->m_iCallback = q->front().cb_id;
|
||||
pCallbackMsg->m_pubParam = (uint8 *)&(q->front().result[0]);
|
||||
pCallbackMsg->m_cubParam = q->front().result.size();
|
||||
PRINT_DEBUG("Steam_BGetCallback cb number %i\n", q->front().cb_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
SteamAPI_ManualDispatch_Init();
|
||||
Steam_Client *steam_client = get_steam_client();
|
||||
steam_client->RunCallbacks(true, true);
|
||||
return SteamAPI_ManualDispatch_GetNextCallback( hSteamPipe, pCallbackMsg );
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_FreeLastCallback( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s %i\n", __FUNCTION__, hSteamPipe);
|
||||
std::queue<struct cb_data> *q = NULL;
|
||||
if (hSteamPipe == SERVER_STEAM_PIPE) {
|
||||
q = &server_cb;
|
||||
} else {
|
||||
q = &client_cb;
|
||||
}
|
||||
|
||||
if (!q->empty()) q->pop();
|
||||
SteamAPI_ManualDispatch_FreeLastCallback( hSteamPipe );
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GetAPICallResult( HSteamPipe hSteamPipe, SteamAPICall_t hSteamAPICall, void* pCallback, int cubCallback, int iCallbackExpected, bool* pbFailed )
|
||||
{
|
||||
PRINT_DEBUG("Steam_GetAPICallResult %i %llu %i %i\n", hSteamPipe, hSteamAPICall, cubCallback, iCallbackExpected);
|
||||
if (!hSteamPipe) return false;
|
||||
if (hSteamPipe == SERVER_STEAM_PIPE) {
|
||||
return get_steam_client()->steam_gameserver_utils->GetAPICallResult(hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
} else {
|
||||
return get_steam_client()->steam_utils->GetAPICallResult(hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
}
|
||||
return SteamAPI_ManualDispatch_GetAPICallResult(hSteamPipe, hSteamAPICall, pCallback, cubCallback, iCallbackExpected, pbFailed);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void *CreateInterface( const char *pName, int *pReturnCode )
|
||||
|
@ -832,56 +1015,67 @@ STEAMCLIENT_API void Breakpad_SteamWriteMiniDumpUsingExceptionInfoWithBuildId( i
|
|||
STEAMCLIENT_API bool Steam_BConnected( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BLoggedOn( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_BReleaseSteamPipe( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API HSteamUser Steam_ConnectToGlobalUser( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API HSteamUser Steam_CreateGlobalUser( HSteamPipe *phSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API HSteamUser Steam_CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API HSteamPipe Steam_CreateSteamPipe()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSBLoggedOn( void *phSteamHandle )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSBSecure( void *phSteamHandle)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSGetSteam2GetEncryptionKeyToSendToNewClient( void *phSteamHandle, void *pvEncryptionKey, uint32 *pcbEncryptionKey, uint32 cbMaxEncryptionKey )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API uint64 Steam_GSGetSteamID()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_GSLogOff( void *phSteamHandle )
|
||||
|
@ -897,31 +1091,37 @@ STEAMCLIENT_API void Steam_GSLogOn( void *phSteamHandle )
|
|||
STEAMCLIENT_API bool Steam_GSRemoveUserConnect( void *phSteamHandle, uint32 unUserID )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendSteam2UserConnect( void *phSteamHandle, uint32 unUserID, const void *pvRawKey, uint32 unKeyLen, uint32 unIPPublic, uint16 usPort, const void *pvCookie, uint32 cubCookie )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendSteam3UserConnect( void *phSteamHandle, uint64 steamID, uint32 unIPPublic, const void *pvCookie, uint32 cubCookie )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendUserDisconnect( void *phSteamHandle, uint64 ulSteamID, uint32 unUserID )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSendUserStatusResponse( void *phSteamHandle, uint64 ulSteamID, int nSecondsConnected, int nSecondsSinceLast )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API bool Steam_GSSetServerType( void *phSteamHandle, int32 nAppIdServed, uint32 unServerFlags, uint32 unGameIP, uint32 unGamePort, const char *pchGameDir, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_GSSetSpawnCount( void *phSteamHandle, uint32 ucSpawn )
|
||||
|
@ -932,16 +1132,19 @@ STEAMCLIENT_API void Steam_GSSetSpawnCount( void *phSteamHandle, uint32 ucSpawn
|
|||
STEAMCLIENT_API bool Steam_GSUpdateStatus( void *phSteamHandle, int cPlayers, int cPlayersMax, int cBotPlayers, const char *pchServerName, const char *pchMapName )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void* Steam_GetGSHandle( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API int Steam_InitiateGameConnection( HSteamUser hUser, HSteamPipe hSteamPipe, void *pBlob, int cbMaxBlob, uint64 steamID, int nGameAppID, uint32 unIPServer, uint16 usPortServer, bool bSecure )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_LogOff( HSteamUser hUser, HSteamPipe hSteamPipe )
|
||||
|
@ -954,6 +1157,11 @@ STEAMCLIENT_API void Steam_LogOn( HSteamUser hUser, HSteamPipe hSteamPipe, uint6
|
|||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_ReleaseThreadLocalMemory(bool thread_exit)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
STEAMCLIENT_API void Steam_ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser )
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
|
|
10
dll/dll.h
|
@ -19,10 +19,16 @@
|
|||
|
||||
#ifdef STEAMCLIENT_DLL
|
||||
#define STEAMAPI_API static
|
||||
#define STEAMCLIENT_API S_API
|
||||
#define STEAMCLIENT_API S_API_EXPORT
|
||||
#else
|
||||
#define STEAMAPI_API S_API
|
||||
#define STEAMAPI_API S_API_EXPORT
|
||||
#define STEAMCLIENT_API static
|
||||
#endif
|
||||
|
||||
Steam_Client *get_steam_client();
|
||||
bool steamclient_has_ipv6_functions();
|
||||
|
||||
HSteamUser flat_hsteamuser();
|
||||
HSteamPipe flat_hsteampipe();
|
||||
HSteamUser flat_gs_hsteamuser();
|
||||
HSteamPipe flat_gs_hsteampipe();
|
||||
|
|
3753
dll/flat.cpp
|
@ -17,10 +17,15 @@
|
|||
|
||||
#include "local_storage.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <iomanip>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_ONLY_JPEG
|
||||
#include "../stb/stb_image.h"
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#include "../stb/stb_image_write.h"
|
||||
|
||||
struct File_Data {
|
||||
std::string name;
|
||||
|
@ -148,9 +153,18 @@ std::vector<std::string> Local_Storage::get_filenames_path(std::string path)
|
|||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
|
||||
{
|
||||
return std::vector<image_pixel_t>();
|
||||
}
|
||||
|
||||
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#if defined(__WINDOWS__)
|
||||
|
||||
static BOOL DirectoryExists(LPCSTR szPath)
|
||||
{
|
||||
|
@ -176,11 +190,6 @@ static void create_directory(std::string strPath)
|
|||
createDirectoryRecursively(strPath);
|
||||
}
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
static std::vector<struct File_Data> get_filenames(std::string strPath)
|
||||
{
|
||||
std::vector<struct File_Data> output;
|
||||
|
@ -251,13 +260,6 @@ static std::vector<struct File_Data> get_filenames_recursive(std::string base_pa
|
|||
}
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#define PATH_MAX_STRING_SIZE 512
|
||||
|
||||
/* recursive mkdir */
|
||||
static int mkdir_p(const char *dir, const mode_t mode) {
|
||||
|
@ -397,11 +399,6 @@ std::string Local_Storage::get_game_settings_path()
|
|||
return get_program_path().append(game_settings_folder).append(PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
#if defined(STEAM_WIN32)
|
||||
#include <shlobj.h>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
std::string Local_Storage::get_user_appdata_path()
|
||||
{
|
||||
std::string user_appdata_path = "SAVE";
|
||||
|
@ -760,4 +757,32 @@ bool Local_Storage::write_json_file(std::string folder, std::string const&file,
|
|||
return false;
|
||||
}
|
||||
|
||||
std::vector<image_pixel_t> Local_Storage::load_image(std::string const& image_path)
|
||||
{
|
||||
std::vector<image_pixel_t> res;
|
||||
FILE* hFile = fopen(image_path.c_str(), "r");
|
||||
if (hFile != nullptr)
|
||||
{
|
||||
int width, height;
|
||||
image_pixel_t* img = (image_pixel_t*)stbi_load_from_file(hFile, &width, &height, nullptr, 4);
|
||||
if (img != nullptr)
|
||||
{
|
||||
res.resize(width*height);
|
||||
std::copy(img, img + width * height, res.begin());
|
||||
|
||||
stbi_image_free(img);
|
||||
}
|
||||
fclose(hFile);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Local_Storage::save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels)
|
||||
{
|
||||
std::string screenshot_path = std::move(save_directory + appid + screenshots_folder + PATH_SEPARATOR);
|
||||
create_directory(screenshot_path);
|
||||
screenshot_path += image_path;
|
||||
return stbi_write_png(screenshot_path.c_str(), width, height, channels, img_ptr, 0) == 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,17 +15,32 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include <vector>
|
||||
|
||||
#ifndef LOCAL_STORAGE_INCLUDE
|
||||
#define LOCAL_STORAGE_INCLUDE
|
||||
|
||||
#include <string>
|
||||
#include "../json/json.hpp"
|
||||
#include "base.h"
|
||||
|
||||
#define MAX_FILENAME_LENGTH 300
|
||||
|
||||
union image_pixel_t
|
||||
{
|
||||
uint32_t pixel;
|
||||
struct pixel_channels_t
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
} channels;
|
||||
};
|
||||
|
||||
struct image_t
|
||||
{
|
||||
size_t width;
|
||||
size_t height;
|
||||
std::vector<image_pixel_t> pix_map;
|
||||
};
|
||||
|
||||
class Local_Storage {
|
||||
public:
|
||||
static constexpr auto inventory_storage_folder = "inventory";
|
||||
|
@ -33,6 +48,7 @@ public:
|
|||
static constexpr auto remote_storage_folder = "remote";
|
||||
static constexpr auto stats_storage_folder = "stats";
|
||||
static constexpr auto user_data_storage = "local";
|
||||
static constexpr auto screenshots_folder = "screenshots";
|
||||
static constexpr auto game_settings_folder = "steam_settings";
|
||||
|
||||
private:
|
||||
|
@ -66,6 +82,9 @@ public:
|
|||
bool load_json(std::string full_path, nlohmann::json& json);
|
||||
bool load_json_file(std::string folder, std::string const& file, nlohmann::json& json);
|
||||
bool write_json_file(std::string folder, std::string const& file, nlohmann::json const& json);
|
||||
|
||||
std::vector<image_pixel_t> load_image(std::string const& image_path);
|
||||
bool save_screenshot(std::string const& image_path, uint8_t* img_ptr, int32_t width, int32_t height, int32_t channels);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -79,7 +79,7 @@ message Low_Level {
|
|||
Types type = 1;
|
||||
}
|
||||
|
||||
message Network {
|
||||
message Network_pb {
|
||||
uint32 channel = 1;
|
||||
bytes data = 2;
|
||||
|
||||
|
@ -185,6 +185,17 @@ message Friend_Messages {
|
|||
}
|
||||
}
|
||||
|
||||
message Steam_Messages {
|
||||
enum Types {
|
||||
FRIEND_CHAT = 0;
|
||||
}
|
||||
|
||||
Types type = 1;
|
||||
oneof message_data {
|
||||
bytes message = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Common_Message {
|
||||
uint64 source_id = 1;
|
||||
uint64 dest_id = 2;
|
||||
|
@ -193,13 +204,14 @@ message Common_Message {
|
|||
Low_Level low_level = 4;
|
||||
Lobby lobby = 5;
|
||||
Lobby_Messages lobby_messages = 6;
|
||||
Network network = 7;
|
||||
Network_pb network = 7;
|
||||
Gameserver gameserver = 8;
|
||||
Friend friend = 9;
|
||||
Auth_Ticket auth_ticket = 10;
|
||||
Friend_Messages friend_messages = 11;
|
||||
Network_Old network_old = 12;
|
||||
Networking_Sockets networking_sockets = 13;
|
||||
Steam_Messages steam_messages = 14;
|
||||
}
|
||||
|
||||
uint32 source_ip = 128;
|
||||
|
|
100
dll/network.cpp
|
@ -18,25 +18,11 @@
|
|||
#include "network.h"
|
||||
#include "dll.h"
|
||||
|
||||
#if defined(STEAM_WIN32)
|
||||
|
||||
#define MSG_NOSIGNAL 0
|
||||
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BROADCASTS 16
|
||||
static int number_broadcasts = -1;
|
||||
static IP_PORT broadcasts[MAX_BROADCASTS];
|
||||
static uint32_t lower_range_ips[MAX_BROADCASTS];
|
||||
static uint32_t upper_range_ips[MAX_BROADCASTS];
|
||||
|
||||
#define BROADCAST_INTERVAL 5.0
|
||||
#define HEARTBEAT_TIMEOUT 20.0
|
||||
|
@ -44,7 +30,44 @@ static IP_PORT broadcasts[MAX_BROADCASTS];
|
|||
|
||||
#if defined(STEAM_WIN32)
|
||||
|
||||
#include <iphlpapi.h>
|
||||
//windows xp support
|
||||
static int
|
||||
inet_pton4(const char *src, uint32_t *dst)
|
||||
{
|
||||
static const char digits[] = "0123456789";
|
||||
int saw_digit, octets, ch;
|
||||
u_char tmp[sizeof(uint32_t)], *tp;
|
||||
|
||||
saw_digit = 0;
|
||||
octets = 0;
|
||||
*(tp = tmp) = 0;
|
||||
while ((ch = *src++) != '\0') {
|
||||
const char *pch;
|
||||
|
||||
if ((pch = strchr(digits, ch)) != NULL) {
|
||||
size_t nx = *tp * 10 + (pch - digits);
|
||||
|
||||
if (nx > 255)
|
||||
return (0);
|
||||
*tp = (u_char) nx;
|
||||
if (! saw_digit) {
|
||||
if (++octets > 4)
|
||||
return (0);
|
||||
saw_digit = 1;
|
||||
}
|
||||
} else if (ch == '.' && saw_digit) {
|
||||
if (octets == 4)
|
||||
return (0);
|
||||
*++tp = 0;
|
||||
saw_digit = 0;
|
||||
} else
|
||||
return (0);
|
||||
}
|
||||
if (octets < 4)
|
||||
return (0);
|
||||
memcpy(dst, tmp, sizeof(uint32_t));
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void get_broadcast_info(uint16 port)
|
||||
{
|
||||
|
@ -72,16 +95,16 @@ static void get_broadcast_info(uint16 port)
|
|||
IP_ADAPTER_INFO *pAdapter = pAdapterInfo;
|
||||
|
||||
while (pAdapter) {
|
||||
unsigned long iface_ip = 0, subnet_mask = 0;
|
||||
uint32_t iface_ip = 0, subnet_mask = 0;
|
||||
|
||||
|
||||
if (inet_pton(AF_INET, pAdapter->IpAddressList.IpMask.String, &subnet_mask) == 1
|
||||
&& inet_pton(AF_INET, pAdapter->IpAddressList.IpAddress.String, &iface_ip) == 1) {
|
||||
if (inet_pton4(pAdapter->IpAddressList.IpMask.String, &subnet_mask) == 1
|
||||
&& inet_pton4(pAdapter->IpAddressList.IpAddress.String, &iface_ip) == 1) {
|
||||
IP_PORT *ip_port = &broadcasts[number_broadcasts];
|
||||
//ip_port->ip.family = AF_INET;
|
||||
uint32 broadcast_ip = iface_ip | ~subnet_mask;
|
||||
ip_port->ip = broadcast_ip;
|
||||
ip_port->port = port;
|
||||
lower_range_ips[number_broadcasts] = iface_ip & subnet_mask;
|
||||
upper_range_ips[number_broadcasts] = broadcast_ip;
|
||||
number_broadcasts++;
|
||||
|
||||
if (number_broadcasts >= MAX_BROADCASTS) {
|
||||
|
@ -223,6 +246,11 @@ static void run_at_startup()
|
|||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
//hack: the game Full Mojo Rampage calls WSACleanup on startup so we call WSAStartup a few times so it doesn't get deallocated.
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
}
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
@ -288,6 +316,7 @@ static bool send_broadcasts(sock_t sock, uint16 port, char *data, unsigned long
|
|||
if (number_broadcasts < 0 || check_timedout(last_get_broadcast_info, 60.0)) {
|
||||
PRINT_DEBUG("get_broadcast_info\n");
|
||||
get_broadcast_info(port);
|
||||
set_adapter_ips(lower_range_ips, upper_range_ips, number_broadcasts);
|
||||
last_get_broadcast_info = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
|
@ -444,18 +473,6 @@ static bool recv_tcp(struct TCP_Socket &socket)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout)
|
||||
{
|
||||
if (timeout == 0.0) return true;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::duration<double>>(now - old).count() > timeout) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void socket_timeouts(struct TCP_Socket &socket, double extra_time)
|
||||
{
|
||||
if (check_timedout(socket.last_heartbeat_sent, HEARTBEAT_TIMEOUT / 2.0)) {
|
||||
|
@ -534,6 +551,11 @@ void Networking::do_callbacks_message(Common_Message *msg)
|
|||
PRINT_DEBUG("has_networking_sockets\n");
|
||||
run_callbacks(CALLBACK_ID_NETWORKING_SOCKETS, msg);
|
||||
}
|
||||
|
||||
if (msg->has_steam_messages()) {
|
||||
PRINT_DEBUG("has_steam_messages\n");
|
||||
run_callbacks(CALLBACK_ID_STEAM_MESSAGES, msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool Networking::handle_tcp(Common_Message *msg, struct TCP_Socket &socket)
|
||||
|
@ -1112,6 +1134,16 @@ bool Networking::sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32 Networking::getIP(CSteamID id)
|
||||
{
|
||||
Connection *conn = find_connection(id, this->appid);
|
||||
if (conn) {
|
||||
return ntohl(conn->tcp_ip_port.ip);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Networking::sendTo(Common_Message *msg, bool reliable, Connection *conn)
|
||||
{
|
||||
if (!enabled) return false;
|
||||
|
|
|
@ -15,13 +15,10 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#ifndef NETWORK_INCLUDE
|
||||
#define NETWORK_INCLUDE
|
||||
|
||||
#include "net.pb.h"
|
||||
#include <chrono>
|
||||
#include "base.h"
|
||||
|
||||
inline bool protobuf_message_equal(const google::protobuf::MessageLite& msg_a,
|
||||
const google::protobuf::MessageLite& msg_b) {
|
||||
|
@ -38,8 +35,6 @@ typedef unsigned int sock_t;
|
|||
typedef int sock_t;
|
||||
#endif
|
||||
|
||||
bool check_timedout(std::chrono::high_resolution_clock::time_point old, double timeout);
|
||||
|
||||
struct IP_PORT {
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
|
@ -60,6 +55,7 @@ enum Callback_Ids {
|
|||
CALLBACK_ID_AUTH_TICKET,
|
||||
CALLBACK_ID_FRIEND_MESSAGES,
|
||||
CALLBACK_ID_NETWORKING_SOCKETS,
|
||||
CALLBACK_ID_STEAM_MESSAGES,
|
||||
|
||||
CALLBACK_IDS_MAX
|
||||
};
|
||||
|
@ -135,6 +131,7 @@ public:
|
|||
bool sendToIPPort(Common_Message *msg, uint32 ip, uint16 port, bool reliable);
|
||||
|
||||
bool setCallback(Callback_Ids id, CSteamID steam_id, void (*message_callback)(void *object, Common_Message *msg), void *object);
|
||||
uint32 getIP(CSteamID id);
|
||||
uint32 getOwnIP();
|
||||
|
||||
void shutDown();
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include <set>
|
||||
|
||||
#ifndef SETTINGS_INCLUDE
|
||||
#define SETTINGS_INCLUDE
|
||||
|
||||
#include "base.h"
|
||||
|
||||
struct DLC_entry {
|
||||
AppId_t appID;
|
||||
std::string name;
|
||||
|
@ -106,6 +105,9 @@ public:
|
|||
bool hasDLC(AppId_t appID);
|
||||
bool getDLC(unsigned int index, AppId_t &appID, bool &available, std::string &name);
|
||||
|
||||
//Depots
|
||||
std::vector<DepotId_t> depots;
|
||||
|
||||
//App Install paths
|
||||
void setAppInstallPath(AppId_t appID, std::string path);
|
||||
std::string getAppInstallPath(AppId_t appID);
|
||||
|
@ -129,18 +131,25 @@ public:
|
|||
std::map<std::string, Stat_config> getStats() { return stats; }
|
||||
void setStatDefiniton(std::string name, struct Stat_config stat_config) {stats[name] = stat_config; }
|
||||
|
||||
//subscribed lobby/group ids
|
||||
std::set<uint64> subscribed_groups;
|
||||
|
||||
//images
|
||||
std::map<int, struct Image_Data> images;
|
||||
int add_image(std::string data, uint32 width, uint32 height);
|
||||
|
||||
//controller
|
||||
struct Controller_Settings controller_settings;
|
||||
std::string glyphs_directory;
|
||||
|
||||
//networking
|
||||
bool disable_networking = false;
|
||||
|
||||
//gameserver
|
||||
//gameserver source query
|
||||
bool disable_source_query = false;
|
||||
|
||||
//overlay
|
||||
bool disable_overlay = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,15 +16,23 @@
|
|||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "settings_parser.h"
|
||||
#include <fstream>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
|
||||
static void consume_bom(std::ifstream &input)
|
||||
{
|
||||
int bom[3];
|
||||
bom[0] = input.get();
|
||||
bom[1] = input.get();
|
||||
bom[2] = input.get();
|
||||
if (bom[0] != 0xEF || bom[1] != 0xBB || bom[2] != 0xBF) {
|
||||
input.seekg(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void load_custom_broadcasts(std::string broadcasts_filepath, std::set<uint32> &custom_broadcasts)
|
||||
{
|
||||
std::ifstream broadcasts_file(broadcasts_filepath);
|
||||
PRINT_DEBUG("Broadcasts file path: %s\n", broadcasts_filepath.c_str());
|
||||
std::ifstream broadcasts_file(broadcasts_filepath);
|
||||
consume_bom(broadcasts_file);
|
||||
if (broadcasts_file.is_open()) {
|
||||
std::string line;
|
||||
while (std::getline(broadcasts_file, line)) {
|
||||
|
@ -63,6 +71,7 @@ static void load_gamecontroller_settings(Settings *settings)
|
|||
std::string controller_config_path = path + PATH_SEPARATOR + p;
|
||||
std::ifstream input( controller_config_path );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
std::map<std::string, std::pair<std::set<std::string>, std::string>> button_pairs;
|
||||
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
|
@ -104,6 +113,8 @@ static void load_gamecontroller_settings(Settings *settings)
|
|||
PRINT_DEBUG("Added %u action names to %s\n", button_pairs.size(), action_set_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
settings->glyphs_directory = path + (PATH_SEPARATOR "glyphs" PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
uint32 create_localstorage_settings(Settings **settings_client_out, Settings **settings_server_out, Local_Storage **local_storage_out)
|
||||
|
@ -248,6 +259,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
|
||||
bool steam_offline_mode = false;
|
||||
bool disable_networking = false;
|
||||
bool disable_overlay = false;
|
||||
{
|
||||
std::string steam_settings_path = Local_Storage::get_game_settings_path();
|
||||
|
||||
|
@ -258,6 +270,22 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
steam_offline_mode = true;
|
||||
} else if (p == "disable_networking.txt") {
|
||||
disable_networking = true;
|
||||
} else if (p == "disable_overlay.txt") {
|
||||
disable_overlay = true;
|
||||
} else if (p == "force_language.txt") {
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "force_language.txt", language, sizeof(language) - 1);
|
||||
if (len > 0) language[len] = 0;
|
||||
} else if (p == "force_steamid.txt") {
|
||||
char steam_id_text[32] = {};
|
||||
if (Local_Storage::get_file_data(steam_settings_path + "force_steamid.txt", steam_id_text, sizeof(steam_id_text) - 1) > 0) {
|
||||
CSteamID temp_id = CSteamID((uint64)std::atoll(steam_id_text));
|
||||
if (temp_id.IsValid()) {
|
||||
user_id = temp_id;
|
||||
}
|
||||
}
|
||||
} else if (p == "force_account_name.txt") {
|
||||
int len = Local_Storage::get_file_data(steam_settings_path + "force_account_name.txt", name, sizeof(name) - 1);
|
||||
if (len > 0) name[len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -270,11 +298,14 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
settings_server->custom_broadcasts = custom_broadcasts;
|
||||
settings_client->disable_networking = disable_networking;
|
||||
settings_server->disable_networking = disable_networking;
|
||||
settings_client->disable_overlay = disable_overlay;
|
||||
settings_server->disable_overlay = disable_overlay;
|
||||
|
||||
{
|
||||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";
|
||||
std::ifstream input( dlc_config_path );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
settings_client->unlockAllDLC(false);
|
||||
settings_server->unlockAllDLC(false);
|
||||
PRINT_DEBUG("Locking all DLC\n");
|
||||
|
@ -315,7 +346,9 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
{
|
||||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "app_paths.txt";
|
||||
std::ifstream input( dlc_config_path );
|
||||
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
|
@ -349,6 +382,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "leaderboards.txt";
|
||||
std::ifstream input( dlc_config_path );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
settings_client->setCreateUnknownLeaderboards(false);
|
||||
settings_server->setCreateUnknownLeaderboards(false);
|
||||
|
||||
|
@ -390,7 +424,7 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
std::string stats_config_path = Local_Storage::get_game_settings_path() + "stats.txt";
|
||||
std::ifstream input( stats_config_path );
|
||||
if (input.is_open()) {
|
||||
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
|
@ -451,6 +485,50 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
|
|||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string depots_config_path = Local_Storage::get_game_settings_path() + "depots.txt";
|
||||
std::ifstream input( depots_config_path );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
DepotId_t depot_id = stoul(line);
|
||||
settings_client->depots.push_back(depot_id);
|
||||
settings_server->depots.push_back(depot_id);
|
||||
PRINT_DEBUG("Added depot %u\n", depot_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string depots_config_path = Local_Storage::get_game_settings_path() + "subscribed_groups.txt";
|
||||
std::ifstream input( depots_config_path );
|
||||
if (input.is_open()) {
|
||||
consume_bom(input);
|
||||
for( std::string line; getline( input, line ); ) {
|
||||
if (!line.empty() && line[line.length()-1] == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (!line.empty() && line[line.length()-1] == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
uint64 source_id = stoull(line);
|
||||
settings_client->subscribed_groups.insert(source_id);
|
||||
settings_server->subscribed_groups.insert(source_id);
|
||||
PRINT_DEBUG("Added source %llu\n", source_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::string mod_path = Local_Storage::get_game_settings_path() + "mods";
|
||||
std::vector<std::string> paths = Local_Storage::get_filenames_path(mod_path);
|
||||
|
|
|
@ -20,27 +20,32 @@
|
|||
uint32 Steam_Applist::GetNumInstalledApps()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetNumInstalledApps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 Steam_Applist::GetInstalledApps( AppId_t *pvecAppID, uint32 unMaxAppIDs )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetInstalledApps\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns -1 if no name was found
|
||||
int Steam_Applist::GetAppName( AppId_t nAppID, STEAM_OUT_STRING() char *pchName, int cchNameMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppName\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// returns -1 if no dir was found
|
||||
int Steam_Applist::GetAppInstallDir( AppId_t nAppID, char *pchDirectory, int cchNameMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppInstallDir\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return the buildid of this app, may change at any time based on backend updates to the game
|
||||
int Steam_Applist::GetAppBuildId( AppId_t nAppID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Applist::GetAppBuildId\n");
|
||||
return 10;
|
||||
}
|
||||
|
|
|
@ -181,41 +181,27 @@ bool Steam_Apps::MarkContentCorrupt( bool bMissingFilesOnly )
|
|||
// return installed depots in mount order
|
||||
uint32 Steam_Apps::GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots )
|
||||
{
|
||||
PRINT_DEBUG("GetInstalledDepots %u\n", appID);
|
||||
PRINT_DEBUG("GetInstalledDepots %u, %u\n", appID, cMaxDepots);
|
||||
//TODO not sure about the behavior of this function, I didn't actually test this.
|
||||
if (!pvecDepots) return 0;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
if (appID == settings->get_local_game_id().AppID()) {
|
||||
unsigned int count = settings->DLCCount();
|
||||
unsigned int count = settings->depots.size();
|
||||
if (cMaxDepots < count) count = cMaxDepots;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
AppId_t appid;
|
||||
bool available;
|
||||
std::string name;
|
||||
if (settings->getDLC(i, appid, available, name)) {
|
||||
pvecDepots[i] = appid;
|
||||
}
|
||||
}
|
||||
|
||||
std::copy(settings->depots.begin(), settings->depots.begin() + count, pvecDepots);
|
||||
return count;
|
||||
} else {
|
||||
if (cMaxDepots) {
|
||||
*pvecDepots = appID;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
uint32 Steam_Apps::GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots )
|
||||
{
|
||||
PRINT_DEBUG("GetInstalledDepots old\n");
|
||||
return GetInstalledDepots( settings->get_local_game_id().AppID(), pvecDepots, cMaxDepots );
|
||||
}
|
||||
|
||||
// returns current app install folder for AppID, returns folder name length
|
||||
uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize )
|
||||
{
|
||||
PRINT_DEBUG("GetAppInstallDir %u %u\n", appID, cchFolderBufferSize);
|
||||
PRINT_DEBUG("GetAppInstallDir %u %p %u\n", appID, pchFolder, cchFolderBufferSize);
|
||||
//TODO return real path instead of dll path
|
||||
if (!pchFolder || !cchFolderBufferSize) return 0;
|
||||
std::string installed_path = settings->getAppInstallPath(appID);
|
||||
|
||||
if (installed_path.size() == 0) {
|
||||
|
@ -232,8 +218,11 @@ uint32 Steam_Apps::GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchF
|
|||
}
|
||||
|
||||
PRINT_DEBUG("path %s\n", installed_path.c_str());
|
||||
if (cchFolderBufferSize && pchFolder) {
|
||||
snprintf(pchFolder, cchFolderBufferSize, "%s", installed_path.c_str());
|
||||
return strlen(pchFolder);
|
||||
}
|
||||
|
||||
return installed_path.length(); //Real steam always returns the actual path length, not the copied one.
|
||||
}
|
||||
|
||||
// returns true if that app is installed (not necessarily owned)
|
||||
|
@ -314,3 +303,10 @@ bool Steam_Apps::BIsSubscribedFromFamilySharing()
|
|||
PRINT_DEBUG("BIsSubscribedFromFamilySharing\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if game is a timed trial with limited playtime
|
||||
bool Steam_Apps::BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed )
|
||||
{
|
||||
PRINT_DEBUG("BIsTimedTrial\n");
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
#include "base.h"
|
||||
|
||||
class Steam_Apps : public ISteamApps
|
||||
class Steam_Apps :
|
||||
public ISteamApps002,
|
||||
public ISteamApps003,
|
||||
public ISteamApps004,
|
||||
public ISteamApps005,
|
||||
public ISteamApps006,
|
||||
public ISteamApps007,
|
||||
public ISteamApps
|
||||
{
|
||||
Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
|
@ -49,6 +56,7 @@ public:
|
|||
bool GetCurrentBetaName( char *pchName, int cchNameBufferSize ); // returns current beta branch name, 'public' is the default branch
|
||||
bool MarkContentCorrupt( bool bMissingFilesOnly ); // signal Steam that game files seems corrupt or missing
|
||||
uint32 GetInstalledDepots( AppId_t appID, DepotId_t *pvecDepots, uint32 cMaxDepots ); // return installed depots in mount order
|
||||
uint32 GetInstalledDepots( DepotId_t *pvecDepots, uint32 cMaxDepots );
|
||||
|
||||
// returns current app install folder for AppID, returns folder name length
|
||||
uint32 GetAppInstallDir( AppId_t appID, char *pchFolder, uint32 cchFolderBufferSize );
|
||||
|
@ -89,4 +97,7 @@ public:
|
|||
|
||||
// Check if user borrowed this game via Family Sharing, If true, call GetAppOwner() to get the lender SteamID
|
||||
bool BIsSubscribedFromFamilySharing();
|
||||
|
||||
// check if game is a timed trial with limited playtime
|
||||
bool BIsTimedTrial( uint32* punSecondsAllowed, uint32* punSecondsPlayed );
|
||||
};
|
||||
|
|
|
@ -18,30 +18,36 @@
|
|||
#include "steam_client.h"
|
||||
#include "settings_parser.h"
|
||||
|
||||
|
||||
static std::condition_variable kill_background_thread_cv;
|
||||
static std::atomic_bool kill_background_thread;
|
||||
static void background_thread(Steam_Client *client)
|
||||
{
|
||||
PRINT_DEBUG("background thread starting\n");
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
|
||||
while (1) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
global_mutex.lock();
|
||||
if (!client->network->isAlive()) {
|
||||
global_mutex.unlock();
|
||||
//delete network;
|
||||
if (kill_background_thread || kill_background_thread_cv.wait_for(lck, std::chrono::seconds(1)) != std::cv_status::timeout) {
|
||||
if (kill_background_thread) {
|
||||
PRINT_DEBUG("background thread exit\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long time = std::chrono::duration_cast<std::chrono::duration<unsigned long long>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
if (time > client->last_cb_run + 1) {
|
||||
global_mutex.lock();
|
||||
PRINT_DEBUG("background thread run\n");
|
||||
client->network->Run();
|
||||
client->steam_matchmaking->RunBackground();
|
||||
client->run_every_runcb->run();
|
||||
global_mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -54,16 +60,19 @@ Steam_Client::Steam_Client()
|
|||
|
||||
PRINT_DEBUG("steam client init: id: %llu server id: %llu appid: %u port: %u \n", settings_client->get_local_steam_id().ConvertToUint64(), settings_server->get_local_steam_id().ConvertToUint64(), appid, settings_server->get_port());
|
||||
|
||||
steam_overlay = new Steam_Overlay(settings_client, callback_results_client, callbacks_client, run_every_runcb, network);
|
||||
|
||||
steam_user = new Steam_User(settings_client, local_storage, network, callback_results_client, callbacks_client);
|
||||
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_utils = new Steam_Utils(settings_client, callback_results_client);
|
||||
steam_friends = new Steam_Friends(settings_client, network, callback_results_client, callbacks_client, run_every_runcb, steam_overlay);
|
||||
steam_utils = new Steam_Utils(settings_client, callback_results_client, steam_overlay);
|
||||
|
||||
steam_matchmaking = new Steam_Matchmaking(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_matchmaking_servers = new Steam_Matchmaking_Servers(settings_client, network);
|
||||
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client);
|
||||
steam_user_stats = new Steam_User_Stats(settings_client, local_storage, callback_results_client, callbacks_client, steam_overlay);
|
||||
steam_apps = new Steam_Apps(settings_client, callback_results_client);
|
||||
steam_networking = new Steam_Networking(settings_client, network, callbacks_client, run_every_runcb);
|
||||
steam_remote_storage = new Steam_Remote_Storage(settings_client, local_storage, callback_results_client);
|
||||
steam_screenshots = new Steam_Screenshots();
|
||||
steam_screenshots = new Steam_Screenshots(local_storage, callbacks_client);
|
||||
steam_http = new Steam_HTTP(settings_client, network, callback_results_client, callbacks_client);
|
||||
steam_controller = new Steam_Controller(settings_client, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_ugc = new Steam_UGC(settings_client, callback_results_client, callbacks_client);
|
||||
|
@ -76,16 +85,18 @@ Steam_Client::Steam_Client()
|
|||
steam_parental = new Steam_Parental();
|
||||
steam_networking_sockets = new Steam_Networking_Sockets(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_networking_sockets_serialized = new Steam_Networking_Sockets_Serialized(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_networking_messages = new Steam_Networking_Messages(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_game_coordinator = new Steam_Game_Coordinator(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_networking_utils = new Steam_Networking_Utils(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_unified_messages = new Steam_Unified_Messages(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_game_search = new Steam_Game_Search(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_parties = new Steam_Parties(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_remoteplay = new Steam_RemotePlay(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
steam_tv = new Steam_TV(settings_client, network, callback_results_client, callbacks_client, run_every_runcb);
|
||||
|
||||
PRINT_DEBUG("client init gameserver\n");
|
||||
steam_gameserver = new Steam_GameServer(settings_server, network, callbacks_server);
|
||||
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server);
|
||||
steam_gameserver_utils = new Steam_Utils(settings_server, callback_results_server, steam_overlay);
|
||||
steam_gameserverstats = new Steam_GameServerStats(settings_server, network, callback_results_server, callbacks_server);
|
||||
steam_gameserver_networking = new Steam_Networking(settings_server, network, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_http = new Steam_HTTP(settings_server, network, callback_results_server, callbacks_server);
|
||||
|
@ -94,9 +105,11 @@ Steam_Client::Steam_Client()
|
|||
steam_gameserver_apps = new Steam_Apps(settings_server, callback_results_server);
|
||||
steam_gameserver_networking_sockets = new Steam_Networking_Sockets(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_networking_sockets_serialized = new Steam_Networking_Sockets_Serialized(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_networking_messages = new Steam_Networking_Messages(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
steam_gameserver_game_coordinator = new Steam_Game_Coordinator(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
steam_masterserver_updater = new Steam_Masterserver_Updater(settings_server, network, callback_results_server, callbacks_server, run_every_runcb);
|
||||
|
||||
last_cb_run = 0;
|
||||
PRINT_DEBUG("client init end\n");
|
||||
}
|
||||
|
||||
|
@ -154,14 +167,24 @@ void Steam_Client::setAppID(uint32 appid)
|
|||
HSteamPipe Steam_Client::CreateSteamPipe()
|
||||
{
|
||||
PRINT_DEBUG("CreateSteamPipe\n");
|
||||
return CLIENT_STEAM_PIPE;
|
||||
HSteamPipe pipe = steam_pipe_counter++;
|
||||
PRINT_DEBUG("creating pipe %i\n", pipe);
|
||||
|
||||
steam_pipes[pipe] = Steam_Pipe::NO_USER;
|
||||
return pipe;
|
||||
}
|
||||
|
||||
// Releases a previously created communications pipe
|
||||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
bool Steam_Client::BReleaseSteamPipe( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("BReleaseSteamPipe\n");
|
||||
PRINT_DEBUG("BReleaseSteamPipe %i\n", hSteamPipe);
|
||||
if (steam_pipes.count(hSteamPipe)) {
|
||||
steam_pipes.erase(hSteamPipe);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// connects to an existing global user, failing if none exists
|
||||
|
@ -169,7 +192,17 @@ bool Steam_Client::BReleaseSteamPipe( HSteamPipe hSteamPipe )
|
|||
// NOT THREADSAFE - ensure that no other threads are accessing Steamworks API when calling
|
||||
HSteamUser Steam_Client::ConnectToGlobalUser( HSteamPipe hSteamPipe )
|
||||
{
|
||||
PRINT_DEBUG("ConnectToGlobalUser\n");
|
||||
PRINT_DEBUG("ConnectToGlobalUser %i\n", hSteamPipe);
|
||||
if (!steam_pipes.count(hSteamPipe)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
userLogIn();
|
||||
#ifdef EMU_OVERLAY
|
||||
if(!settings_client->disable_overlay)
|
||||
steam_overlay->SetupOverlay();
|
||||
#endif
|
||||
steam_pipes[hSteamPipe] = Steam_Pipe::CLIENT;
|
||||
return CLIENT_HSTEAMUSER;
|
||||
}
|
||||
|
||||
|
@ -178,13 +211,19 @@ HSteamUser Steam_Client::ConnectToGlobalUser( HSteamPipe hSteamPipe )
|
|||
HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe, EAccountType eAccountType )
|
||||
{
|
||||
PRINT_DEBUG("CreateLocalUser %p %i\n", phSteamPipe, eAccountType);
|
||||
if (eAccountType == k_EAccountTypeIndividual) {
|
||||
if (phSteamPipe) *phSteamPipe = CLIENT_STEAM_PIPE;
|
||||
return CLIENT_HSTEAMUSER;
|
||||
} else {
|
||||
if (phSteamPipe) *phSteamPipe = SERVER_STEAM_PIPE;
|
||||
//if (eAccountType == k_EAccountTypeIndividual) {
|
||||
//Is this actually used?
|
||||
//if (phSteamPipe) *phSteamPipe = CLIENT_STEAM_PIPE;
|
||||
//return CLIENT_HSTEAMUSER;
|
||||
//} else { //k_EAccountTypeGameServer
|
||||
serverInit();
|
||||
|
||||
HSteamPipe pipe = CreateSteamPipe();
|
||||
if (phSteamPipe) *phSteamPipe = pipe;
|
||||
steam_pipes[pipe] = Steam_Pipe::SERVER;
|
||||
steamclient_server_inited = true;
|
||||
return SERVER_HSTEAMUSER;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe )
|
||||
|
@ -197,13 +236,16 @@ HSteamUser Steam_Client::CreateLocalUser( HSteamPipe *phSteamPipe )
|
|||
void Steam_Client::ReleaseUser( HSteamPipe hSteamPipe, HSteamUser hUser )
|
||||
{
|
||||
PRINT_DEBUG("ReleaseUser\n");
|
||||
if (hUser == SERVER_HSTEAMUSER && steam_pipes.count(hSteamPipe)) {
|
||||
steamclient_server_inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
// retrieves the ISteamUser interface associated with the handle
|
||||
ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamUser %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "SteamUser009") == 0) {
|
||||
return (ISteamUser *)(void *)(ISteamUser009 *)steam_user;
|
||||
|
@ -227,6 +269,8 @@ ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hStea
|
|||
return (ISteamUser *)(void *)(ISteamUser018 *)steam_user;
|
||||
} else if (strcmp(pchVersion, "SteamUser019") == 0) {
|
||||
return (ISteamUser *)(void *)(ISteamUser019 *)steam_user;
|
||||
} else if (strcmp(pchVersion, "SteamUser020") == 0) {
|
||||
return (ISteamUser *)(void *)(ISteamUser020 *)steam_user;
|
||||
} else if (strcmp(pchVersion, STEAMUSER_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamUser *)(void *)(ISteamUser *)steam_user;
|
||||
} else {
|
||||
|
@ -240,7 +284,7 @@ ISteamUser *Steam_Client::GetISteamUser( HSteamUser hSteamUser, HSteamPipe hStea
|
|||
ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamGameServer %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "SteamGameServer005") == 0) {
|
||||
return (ISteamGameServer *)(void *)(ISteamGameServer005 *)steam_gameserver;
|
||||
|
@ -256,6 +300,8 @@ ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSte
|
|||
return (ISteamGameServer *)(void *)(ISteamGameServer010 *)steam_gameserver;
|
||||
} else if (strcmp(pchVersion, "SteamGameServer011") == 0) {
|
||||
return (ISteamGameServer *)(void *)(ISteamGameServer011 *)steam_gameserver;
|
||||
} else if (strcmp(pchVersion, "SteamGameServer012") == 0) {
|
||||
return (ISteamGameServer *)(void *)(ISteamGameServer012 *)steam_gameserver;
|
||||
} else if (strcmp(pchVersion, STEAMGAMESERVER_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamGameServer *)(void *)(ISteamGameServer *)steam_gameserver;
|
||||
} else {
|
||||
|
@ -269,14 +315,19 @@ ISteamGameServer *Steam_Client::GetISteamGameServer( HSteamUser hSteamUser, HSte
|
|||
// this must be set before CreateLocalUser()
|
||||
void Steam_Client::SetLocalIPBinding( uint32 unIP, uint16 usPort )
|
||||
{
|
||||
PRINT_DEBUG("SetLocalIPBinding %u %hu\n", unIP, usPort);
|
||||
PRINT_DEBUG("SetLocalIPBinding old %u %hu\n", unIP, usPort);
|
||||
}
|
||||
|
||||
void Steam_Client::SetLocalIPBinding( const SteamIPAddress_t &unIP, uint16 usPort )
|
||||
{
|
||||
PRINT_DEBUG("SetLocalIPBinding %i %u %hu\n", unIP.m_eType, unIP.m_unIPv4, usPort);
|
||||
}
|
||||
|
||||
// returns the ISteamFriends interface
|
||||
ISteamFriends *Steam_Client::GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamFriends %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "SteamFriends004") == 0) {
|
||||
return (ISteamFriends *)(void *)(ISteamFriends004 *)steam_friends;
|
||||
|
@ -317,11 +368,11 @@ ISteamFriends *Steam_Client::GetISteamFriends( HSteamUser hSteamUser, HSteamPipe
|
|||
ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamUtils %s\n", pchVersion);
|
||||
if (!hSteamPipe) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe)) return NULL;
|
||||
|
||||
Steam_Utils *steam_utils_temp;
|
||||
|
||||
if (hSteamPipe == SERVER_STEAM_PIPE) {
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_utils_temp = steam_gameserver_utils;
|
||||
} else {
|
||||
steam_utils_temp = steam_utils;
|
||||
|
@ -341,6 +392,8 @@ ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pc
|
|||
return (ISteamUtils *)(void *)(ISteamUtils007 *)steam_utils_temp;
|
||||
} else if (strcmp(pchVersion, "SteamUtils008") == 0) {
|
||||
return (ISteamUtils *)(void *)(ISteamUtils008 *)steam_utils_temp;
|
||||
} else if (strcmp(pchVersion, "SteamUtils009") == 0) {
|
||||
return (ISteamUtils *)(void *)(ISteamUtils009 *)steam_utils_temp;
|
||||
} else if (strcmp(pchVersion, STEAMUTILS_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamUtils *)(void *)(ISteamUtils *)steam_utils_temp;
|
||||
} else {
|
||||
|
@ -354,7 +407,7 @@ ISteamUtils *Steam_Client::GetISteamUtils( HSteamPipe hSteamPipe, const char *pc
|
|||
ISteamMatchmaking *Steam_Client::GetISteamMatchmaking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamMatchmaking %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "SteamMatchMaking001") == 0) {
|
||||
//TODO
|
||||
|
@ -391,7 +444,7 @@ ISteamMatchmaking *Steam_Client::GetISteamMatchmaking( HSteamUser hSteamUser, HS
|
|||
ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamMatchmakingServers %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
return steam_matchmaking_servers;
|
||||
}
|
||||
|
||||
|
@ -399,10 +452,10 @@ ISteamMatchmakingServers *Steam_Client::GetISteamMatchmakingServers( HSteamUser
|
|||
void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamGenericInterface %s\n", pchVersion);
|
||||
if (!hSteamPipe) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe)) return NULL;
|
||||
|
||||
bool server = false;
|
||||
if (hSteamUser == SERVER_HSTEAMUSER) {
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
server = true;
|
||||
} else {
|
||||
if ((strstr(pchVersion, "SteamNetworkingUtils") != pchVersion) && (strstr(pchVersion, "SteamUtils") != pchVersion)) {
|
||||
|
@ -422,8 +475,10 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
|
|||
return (void *)(ISteamNetworkingSocketsSerialized002 *)steam_networking_sockets_serialized_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSocketsSerialized003") == 0) {
|
||||
return (void *)(ISteamNetworkingSocketsSerialized003 *)steam_networking_sockets_serialized_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSocketsSerialized004") == 0) {
|
||||
return (void *)(ISteamNetworkingSocketsSerialized004 *)steam_networking_sockets_serialized_temp;
|
||||
} else {
|
||||
return (void *)(ISteamNetworkingSocketsSerialized003 *)steam_networking_sockets_serialized_temp;
|
||||
return (void *)(ISteamNetworkingSocketsSerialized004 *)steam_networking_sockets_serialized_temp;
|
||||
}
|
||||
} else if (strstr(pchVersion, "SteamNetworkingSockets") == pchVersion) {
|
||||
Steam_Networking_Sockets *steam_networking_sockets_temp;
|
||||
|
@ -437,9 +492,24 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
|
|||
return (void *)(ISteamNetworkingSockets001 *) steam_networking_sockets_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSockets002") == 0) {
|
||||
return (void *)(ISteamNetworkingSockets002 *) steam_networking_sockets_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSockets003") == 0) {
|
||||
return (void *)(ISteamNetworkingSockets003 *) steam_networking_sockets_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSockets006") == 0) {
|
||||
return (void *)(ISteamNetworkingSockets006 *) steam_networking_sockets_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingSockets008") == 0) {
|
||||
return (void *)(ISteamNetworkingSockets008 *) steam_networking_sockets_temp;
|
||||
} else {
|
||||
return (void *)(ISteamNetworkingSockets *) steam_networking_sockets_temp;
|
||||
}
|
||||
} else if (strstr(pchVersion, "SteamNetworkingMessages") == pchVersion) {
|
||||
Steam_Networking_Messages *steam_networking_messages_temp;
|
||||
if (server) {
|
||||
steam_networking_messages_temp = steam_gameserver_networking_messages;
|
||||
} else {
|
||||
steam_networking_messages_temp = steam_networking_messages;
|
||||
}
|
||||
|
||||
return (void *)(ISteamNetworkingMessages *)steam_networking_messages_temp;
|
||||
} else if (strstr(pchVersion, "SteamGameCoordinator") == pchVersion) {
|
||||
Steam_Game_Coordinator *steam_game_coordinator_temp;
|
||||
if (server) {
|
||||
|
@ -449,9 +519,13 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
|
|||
}
|
||||
|
||||
return (void *)(ISteamGameCoordinator *)steam_game_coordinator_temp;
|
||||
} else if (strstr(pchVersion, STEAMTV_INTERFACE_VERSION) == pchVersion) {
|
||||
return (void *)(ISteamTV *)steam_tv;
|
||||
} else if (strstr(pchVersion, "SteamNetworkingUtils") == pchVersion) {
|
||||
if (strcmp(pchVersion, "SteamNetworkingUtils001") == 0) {
|
||||
return (void *)(ISteamNetworkingUtils001 *)steam_networking_utils;
|
||||
} else if (strcmp(pchVersion, "SteamNetworkingUtils002") == 0) {
|
||||
return (void *)(ISteamNetworkingUtils002 *)steam_networking_utils;
|
||||
} else if (strcmp(pchVersion, STEAMNETWORKINGUTILS_INTERFACE_VERSION) == 0) {
|
||||
return (void *)(ISteamNetworkingUtils *)steam_networking_utils;
|
||||
} else {
|
||||
|
@ -524,7 +598,7 @@ void *Steam_Client::GetISteamGenericInterface( HSteamUser hSteamUser, HSteamPipe
|
|||
ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamUserStats %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION001") == 0) {
|
||||
//TODO
|
||||
|
@ -548,6 +622,8 @@ ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteam
|
|||
return (ISteamUserStats *)(void *)(ISteamUserStats009 *)steam_user_stats;
|
||||
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION010") == 0) {
|
||||
return (ISteamUserStats *)(void *)(ISteamUserStats010 *)steam_user_stats;
|
||||
} else if (strcmp(pchVersion, "STEAMUSERSTATS_INTERFACE_VERSION011") == 0) {
|
||||
return (ISteamUserStats *)(void *)(ISteamUserStats011 *)steam_user_stats;
|
||||
} else if (strcmp(pchVersion, STEAMUSERSTATS_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamUserStats *)(void *)(ISteamUserStats *)steam_user_stats;
|
||||
} else {
|
||||
|
@ -561,7 +637,7 @@ ISteamUserStats *Steam_Client::GetISteamUserStats( HSteamUser hSteamUser, HSteam
|
|||
ISteamGameServerStats *Steam_Client::GetISteamGameServerStats( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamGameServerStats %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_gameserverstats;
|
||||
}
|
||||
|
||||
|
@ -569,9 +645,31 @@ ISteamGameServerStats *Steam_Client::GetISteamGameServerStats( HSteamUser hSteam
|
|||
ISteamApps *Steam_Client::GetISteamApps( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamApps %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (hSteamUser == SERVER_HSTEAMUSER) {
|
||||
return steam_gameserver_apps;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
Steam_Apps *steam_apps_temp;
|
||||
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_apps_temp = steam_gameserver_apps;
|
||||
} else {
|
||||
steam_apps_temp = steam_apps;
|
||||
}
|
||||
if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION002") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps002 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION003") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps003 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION004") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps004 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION005") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps005 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION006") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps006 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMAPPS_INTERFACE_VERSION007") == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps007 *)steam_apps_temp;
|
||||
} else if (strcmp(pchVersion, STEAMAPPS_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamApps *)(void *)(ISteamApps *)steam_apps_temp;
|
||||
} else {
|
||||
return (ISteamApps *)(void *)(ISteamApps *)steam_apps_temp;
|
||||
}
|
||||
|
||||
return steam_apps;
|
||||
|
@ -581,11 +679,11 @@ ISteamApps *Steam_Client::GetISteamApps( HSteamUser hSteamUser, HSteamPipe hStea
|
|||
ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamNetworking %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
Steam_Networking *steam_networking_temp;
|
||||
|
||||
if (hSteamUser == SERVER_HSTEAMUSER) {
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_networking_temp = steam_gameserver_networking;
|
||||
} else {
|
||||
steam_networking_temp = steam_networking;
|
||||
|
@ -599,7 +697,9 @@ ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSte
|
|||
return (ISteamNetworking *)(void *)(ISteamNetworking003 *)steam_networking_temp;
|
||||
} else if (strcmp(pchVersion, "SteamNetworking004") == 0) {
|
||||
return (ISteamNetworking *)(void *)(ISteamNetworking004 *)steam_networking_temp;
|
||||
} else if (strcmp(pchVersion, STEAMUGC_INTERFACE_VERSION) == 0) {
|
||||
} else if (strcmp(pchVersion, "SteamNetworking005") == 0) {
|
||||
return (ISteamNetworking *)(void *)(ISteamNetworking005 *)steam_networking_temp;
|
||||
} else if (strcmp(pchVersion, STEAMNETWORKING_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamNetworking *)(void *)(ISteamNetworking *)steam_networking_temp;
|
||||
} else {
|
||||
return (ISteamNetworking *)(void *)(ISteamNetworking *)steam_networking_temp;
|
||||
|
@ -612,7 +712,7 @@ ISteamNetworking *Steam_Client::GetISteamNetworking( HSteamUser hSteamUser, HSte
|
|||
ISteamRemoteStorage *Steam_Client::GetISteamRemoteStorage( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamRemoteStorage %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "STEAMREMOTESTORAGE_INTERFACE_VERSION001") == 0) {
|
||||
return (ISteamRemoteStorage *)(void *)(ISteamRemoteStorage001 *)steam_remote_storage;
|
||||
|
@ -653,7 +753,7 @@ ISteamRemoteStorage *Steam_Client::GetISteamRemoteStorage( HSteamUser hSteamuser
|
|||
ISteamScreenshots *Steam_Client::GetISteamScreenshots( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamScreenshots %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_screenshots;
|
||||
}
|
||||
|
||||
|
@ -661,7 +761,7 @@ ISteamScreenshots *Steam_Client::GetISteamScreenshots( HSteamUser hSteamuser, HS
|
|||
// Deprecated. Applications should use SteamAPI_RunCallbacks() or SteamGameServer_RunCallbacks() instead.
|
||||
void Steam_Client::RunFrame()
|
||||
{
|
||||
PRINT_DEBUG("RunFrame\n");
|
||||
PRINT_DEBUG("Steam_Client::RunFrame\n");
|
||||
}
|
||||
|
||||
// returns the number of IPC calls made since the last time this function was called
|
||||
|
@ -687,15 +787,26 @@ void Steam_Client::SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction
|
|||
bool Steam_Client::BShutdownIfAllPipesClosed()
|
||||
{
|
||||
PRINT_DEBUG("BShutdownIfAllPipesClosed\n");
|
||||
if (!steam_pipes.size()) {
|
||||
if (background_keepalive.joinable()) {
|
||||
kill_background_thread = true;
|
||||
kill_background_thread_cv.notify_one();
|
||||
background_keepalive.join();
|
||||
}
|
||||
|
||||
PRINT_DEBUG("all pipes closed\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Expose HTTP interface
|
||||
ISteamHTTP *Steam_Client::GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamHTTP %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (hSteamuser == SERVER_HSTEAMUSER) {
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
return steam_gameserver_http;
|
||||
}
|
||||
|
||||
|
@ -706,14 +817,14 @@ ISteamHTTP *Steam_Client::GetISteamHTTP( HSteamUser hSteamuser, HSteamPipe hStea
|
|||
void *Steam_Client::DEPRECATED_GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("DEPRECATED_GetISteamUnifiedMessages %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return (void *)(ISteamUnifiedMessages *)steam_unified_messages;
|
||||
}
|
||||
|
||||
ISteamUnifiedMessages *Steam_Client::GetISteamUnifiedMessages( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamUnifiedMessages %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_unified_messages;
|
||||
}
|
||||
|
||||
|
@ -721,7 +832,7 @@ ISteamUnifiedMessages *Steam_Client::GetISteamUnifiedMessages( HSteamUser hSteam
|
|||
ISteamController *Steam_Client::GetISteamController( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamController %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "STEAMCONTROLLER_INTERFACE_VERSION") == 0) {
|
||||
return (ISteamController *)(void *)(ISteamController001 *)steam_controller;
|
||||
|
@ -749,10 +860,10 @@ ISteamController *Steam_Client::GetISteamController( HSteamUser hSteamUser, HSte
|
|||
ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamUGC %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
Steam_UGC *steam_ugc_temp;
|
||||
|
||||
if (hSteamUser == SERVER_HSTEAMUSER) {
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_ugc_temp = steam_gameserver_ugc;
|
||||
} else {
|
||||
steam_ugc_temp = steam_ugc;
|
||||
|
@ -786,6 +897,8 @@ ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamP
|
|||
return (ISteamUGC *)(void *)(ISteamUGC012 *)steam_ugc_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION012") == 0) {
|
||||
return (ISteamUGC *)(void *)(ISteamUGC012 *)steam_ugc_temp;
|
||||
} else if (strcmp(pchVersion, "STEAMUGC_INTERFACE_VERSION013") == 0) {
|
||||
return (ISteamUGC *)(void *)(ISteamUGC013 *)steam_ugc_temp;
|
||||
} else if (strcmp(pchVersion, STEAMUGC_INTERFACE_VERSION) == 0) {
|
||||
return (ISteamUGC *)(void *)(ISteamUGC *)steam_ugc_temp;
|
||||
} else {
|
||||
|
@ -799,7 +912,7 @@ ISteamUGC *Steam_Client::GetISteamUGC( HSteamUser hSteamUser, HSteamPipe hSteamP
|
|||
ISteamAppList *Steam_Client::GetISteamAppList( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamAppList %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
return steam_applist;
|
||||
}
|
||||
|
||||
|
@ -807,7 +920,7 @@ ISteamAppList *Steam_Client::GetISteamAppList( HSteamUser hSteamUser, HSteamPipe
|
|||
ISteamMusic *Steam_Client::GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamMusic %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_music;
|
||||
}
|
||||
|
||||
|
@ -815,7 +928,7 @@ ISteamMusic *Steam_Client::GetISteamMusic( HSteamUser hSteamuser, HSteamPipe hSt
|
|||
ISteamMusicRemote *Steam_Client::GetISteamMusicRemote(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion)
|
||||
{
|
||||
PRINT_DEBUG("GetISteamMusicRemote %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_musicremote;
|
||||
}
|
||||
|
||||
|
@ -823,7 +936,7 @@ ISteamMusicRemote *Steam_Client::GetISteamMusicRemote(HSteamUser hSteamuser, HSt
|
|||
ISteamHTMLSurface *Steam_Client::GetISteamHTMLSurface(HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion)
|
||||
{
|
||||
PRINT_DEBUG("GetISteamHTMLSurface %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
|
||||
if (strcmp(pchVersion, "STEAMHTMLSURFACE_INTERFACE_VERSION_001") == 0) {
|
||||
return (ISteamHTMLSurface *)(void *)(ISteamHTMLSurface001 *)steam_HTMLsurface;
|
||||
|
@ -872,13 +985,13 @@ void Steam_Client::Remove_SteamAPI_CPostAPIResultInProcess( SteamAPI_PostAPIResu
|
|||
ISteamInventory *Steam_Client::GetISteamInventory( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamInventory %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
Steam_Inventory *steam_inventory_temp;
|
||||
Settings *settings_temp;
|
||||
SteamCallBacks *callbacks_temp;
|
||||
SteamCallResults *callback_results_temp;
|
||||
|
||||
if (hSteamuser == SERVER_HSTEAMUSER) {
|
||||
if (steam_pipes[hSteamPipe] == Steam_Pipe::SERVER) {
|
||||
steam_inventory_temp = steam_gameserver_inventory;
|
||||
} else {
|
||||
steam_inventory_temp = steam_inventory;
|
||||
|
@ -901,7 +1014,7 @@ ISteamInventory *Steam_Client::GetISteamInventory( HSteamUser hSteamuser, HSteam
|
|||
ISteamVideo *Steam_Client::GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamVideo %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_video;
|
||||
}
|
||||
|
||||
|
@ -909,21 +1022,21 @@ ISteamVideo *Steam_Client::GetISteamVideo( HSteamUser hSteamuser, HSteamPipe hSt
|
|||
ISteamParentalSettings *Steam_Client::GetISteamParentalSettings( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamParentalSettings %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
return steam_parental;
|
||||
}
|
||||
|
||||
ISteamMasterServerUpdater *Steam_Client::GetISteamMasterServerUpdater( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamMasterServerUpdater %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
return steam_masterserver_updater;
|
||||
}
|
||||
|
||||
ISteamContentServer *Steam_Client::GetISteamContentServer( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamContentServer %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -931,7 +1044,7 @@ ISteamContentServer *Steam_Client::GetISteamContentServer( HSteamUser hSteamUser
|
|||
ISteamGameSearch *Steam_Client::GetISteamGameSearch( HSteamUser hSteamuser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamGameSearch %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamuser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamuser) return NULL;
|
||||
|
||||
return steam_game_search;
|
||||
}
|
||||
|
@ -940,7 +1053,7 @@ ISteamGameSearch *Steam_Client::GetISteamGameSearch( HSteamUser hSteamuser, HSte
|
|||
ISteamInput *Steam_Client::GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamInput %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
return steam_controller;
|
||||
}
|
||||
|
@ -949,7 +1062,7 @@ ISteamInput *Steam_Client::GetISteamInput( HSteamUser hSteamUser, HSteamPipe hSt
|
|||
ISteamParties *Steam_Client::GetISteamParties( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamParties %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
return steam_parties;
|
||||
}
|
||||
|
@ -957,7 +1070,7 @@ ISteamParties *Steam_Client::GetISteamParties( HSteamUser hSteamUser, HSteamPipe
|
|||
ISteamRemotePlay *Steam_Client::GetISteamRemotePlay( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion )
|
||||
{
|
||||
PRINT_DEBUG("GetISteamRemotePlay %s\n", pchVersion);
|
||||
if (!hSteamPipe || !hSteamUser) return NULL;
|
||||
if (!steam_pipes.count(hSteamPipe) || !hSteamUser) return NULL;
|
||||
|
||||
return steam_remoteplay;
|
||||
}
|
||||
|
@ -1452,6 +1565,11 @@ void Steam_Client::RunCallbacks(bool runClientCB, bool runGameserverCB)
|
|||
callbacks_server->runCallBacks();
|
||||
PRINT_DEBUG("Steam_Client::RunCallbacks callbacks_client\n");
|
||||
callbacks_client->runCallBacks();
|
||||
last_cb_run = std::chrono::duration_cast<std::chrono::duration<unsigned long long>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
PRINT_DEBUG("Steam_Client::RunCallbacks done\n");
|
||||
|
||||
}
|
||||
|
||||
void Steam_Client::DestroyAllInterfaces()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Client::DestroyAllInterfaces\n");
|
||||
}
|
|
@ -43,17 +43,25 @@
|
|||
#include "steam_game_coordinator.h"
|
||||
#include "steam_networking_socketsserialized.h"
|
||||
#include "steam_networking_sockets.h"
|
||||
#include "steam_networking_messages.h"
|
||||
#include "steam_networking_utils.h"
|
||||
#include "steam_unified_messages.h"
|
||||
#include "steam_gamesearch.h"
|
||||
#include "steam_parties.h"
|
||||
#include "steam_remoteplay.h"
|
||||
#include "steam_tv.h"
|
||||
|
||||
#include "steam_gameserver.h"
|
||||
#include "steam_gameserverstats.h"
|
||||
#include "steam_masterserver_updater.h"
|
||||
|
||||
#include <thread>
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
enum Steam_Pipe {
|
||||
NO_USER,
|
||||
CLIENT,
|
||||
SERVER
|
||||
};
|
||||
|
||||
class Steam_Client :
|
||||
public ISteamClient007,
|
||||
|
@ -68,6 +76,7 @@ public ISteamClient015,
|
|||
public ISteamClient016,
|
||||
public ISteamClient017,
|
||||
public ISteamClient018,
|
||||
public ISteamClient019,
|
||||
public ISteamClient
|
||||
{
|
||||
public:
|
||||
|
@ -100,12 +109,14 @@ public:
|
|||
Steam_Parental *steam_parental;
|
||||
Steam_Networking_Sockets *steam_networking_sockets;
|
||||
Steam_Networking_Sockets_Serialized *steam_networking_sockets_serialized;
|
||||
Steam_Networking_Messages *steam_networking_messages;
|
||||
Steam_Game_Coordinator *steam_game_coordinator;
|
||||
Steam_Networking_Utils *steam_networking_utils;
|
||||
Steam_Unified_Messages *steam_unified_messages;
|
||||
Steam_Game_Search *steam_game_search;
|
||||
Steam_Parties *steam_parties;
|
||||
Steam_RemotePlay *steam_remoteplay;
|
||||
Steam_TV *steam_tv;
|
||||
|
||||
Steam_GameServer *steam_gameserver;
|
||||
Steam_Utils *steam_gameserver_utils;
|
||||
|
@ -117,12 +128,20 @@ public:
|
|||
Steam_Apps *steam_gameserver_apps;
|
||||
Steam_Networking_Sockets *steam_gameserver_networking_sockets;
|
||||
Steam_Networking_Sockets_Serialized *steam_gameserver_networking_sockets_serialized;
|
||||
Steam_Networking_Messages *steam_gameserver_networking_messages;
|
||||
Steam_Game_Coordinator *steam_gameserver_game_coordinator;
|
||||
Steam_Masterserver_Updater *steam_masterserver_updater;
|
||||
|
||||
Steam_Overlay* steam_overlay;
|
||||
|
||||
bool user_logged_in = false;
|
||||
bool server_init = false;
|
||||
std::thread background_keepalive;
|
||||
bool steamclient_server_inited = false;
|
||||
std::atomic<unsigned long long> last_cb_run;
|
||||
|
||||
unsigned steam_pipe_counter = 1;
|
||||
std::map<HSteamPipe, enum Steam_Pipe> steam_pipes;
|
||||
|
||||
Steam_Client();
|
||||
~Steam_Client();
|
||||
|
@ -157,6 +176,7 @@ public:
|
|||
// set the local IP and Port to bind to
|
||||
// this must be set before CreateLocalUser()
|
||||
void SetLocalIPBinding( uint32 unIP, uint16 usPort );
|
||||
void SetLocalIPBinding( const SteamIPAddress_t &unIP, uint16 usPort );
|
||||
|
||||
// returns the ISteamFriends interface
|
||||
ISteamFriends *GetISteamFriends( HSteamUser hSteamUser, HSteamPipe hSteamPipe, const char *pchVersion );
|
||||
|
@ -281,4 +301,6 @@ public:
|
|||
void clientShutdown();
|
||||
bool IsServerInit();
|
||||
bool IsUserLogIn();
|
||||
|
||||
void DestroyAllInterfaces();
|
||||
};
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include "../controller/gamepad.h"
|
||||
#include <cctype>
|
||||
|
||||
struct Controller_Map {
|
||||
std::map<ControllerDigitalActionHandle_t, std::set<int>> active_digital;
|
||||
|
@ -129,7 +127,11 @@ public ISteamInput
|
|||
std::map<ControllerActionSetHandle_t, struct Controller_Map> controller_maps;
|
||||
std::map<ControllerHandle_t, struct Controller_Action> controllers;
|
||||
|
||||
std::map<EInputActionOrigin, std::string> steaminput_glyphs;
|
||||
std::map<EControllerActionOrigin, std::string> steamcontroller_glyphs;
|
||||
|
||||
bool disabled;
|
||||
bool initialized;
|
||||
|
||||
void set_handles(std::map<std::string, std::map<std::string, std::pair<std::set<std::string>, std::string>>> action_sets) {
|
||||
uint64 handle_num = 1;
|
||||
|
@ -211,6 +213,7 @@ Steam_Controller(class Settings *settings, class SteamCallResults *callback_resu
|
|||
|
||||
set_handles(settings->controller_settings.action_sets);
|
||||
disabled = !action_handles.size();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
~Steam_Controller()
|
||||
|
@ -242,6 +245,7 @@ bool Init()
|
|||
controllers.insert(std::pair<ControllerHandle_t, struct Controller_Action>(i, cont_action));
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -274,7 +278,7 @@ void SetOverrideMode( const char *pchMode )
|
|||
void RunFrame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::RunFrame()\n");
|
||||
if (disabled) {
|
||||
if (disabled || !initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -658,8 +662,8 @@ int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t ac
|
|||
if (a == map->second.active_analog.end()) return 0;
|
||||
|
||||
int count = 0;
|
||||
for (auto a: a->second.first) {
|
||||
switch (a) {
|
||||
for (auto b: a->second.first) {
|
||||
switch (b) {
|
||||
case TRIGGER_LEFT:
|
||||
originsOut[count] = k_EInputActionOrigin_XBox360_LeftTrigger_Pull;
|
||||
break;
|
||||
|
@ -689,7 +693,7 @@ int GetAnalogActionOrigins( InputHandle_t inputHandle, InputActionSetHandle_t ac
|
|||
|
||||
void StopAnalogActionMomentum( ControllerHandle_t controllerHandle, ControllerAnalogActionHandle_t eAction )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::StopAnalogActionMomentum\n");
|
||||
PRINT_DEBUG("Steam_Controller::StopAnalogActionMomentum %llu %llu\n", controllerHandle, eAction);
|
||||
}
|
||||
|
||||
|
||||
|
@ -720,7 +724,12 @@ void TriggerVibration( ControllerHandle_t controllerHandle, unsigned short usLef
|
|||
auto controller = controllers.find(controllerHandle);
|
||||
if (controller == controllers.end()) return;
|
||||
|
||||
GamepadSetRumble((GAMEPAD_DEVICE)(controllerHandle - 1), ((double)usLeftSpeed) / 65535.0, ((double)usRightSpeed) / 65535.0);
|
||||
unsigned int rumble_length_ms = 0;
|
||||
#if defined(__linux__)
|
||||
//FIXME: shadow of the tomb raider on linux doesn't seem to turn off the rumble so I made it expire after 100ms. Need to check if this is how linux steam actually behaves.
|
||||
rumble_length_ms = 100;
|
||||
#endif
|
||||
GamepadSetRumble((GAMEPAD_DEVICE)(controllerHandle - 1), ((double)usLeftSpeed) / 65535.0, ((double)usRightSpeed) / 65535.0, rumble_length_ms);
|
||||
}
|
||||
|
||||
|
||||
|
@ -735,7 +744,10 @@ void SetLEDColor( ControllerHandle_t controllerHandle, uint8 nColorR, uint8 nCol
|
|||
int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::GetGamepadIndexForController\n");
|
||||
return 0;
|
||||
auto controller = controllers.find(ulControllerHandle);
|
||||
if (controller == controllers.end()) return -1;
|
||||
|
||||
return ulControllerHandle - 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -743,7 +755,10 @@ int GetGamepadIndexForController( ControllerHandle_t ulControllerHandle )
|
|||
ControllerHandle_t GetControllerForGamepadIndex( int nIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::GetControllerForGamepadIndex\n");
|
||||
return 0;
|
||||
ControllerHandle_t out = nIndex + 1;
|
||||
auto controller = controllers.find(out);
|
||||
if (controller == controllers.end()) return 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
@ -788,14 +803,86 @@ const char *GetStringForActionOrigin( EInputActionOrigin eOrigin )
|
|||
// Get a local path to art for on-screen glyph for a particular origin
|
||||
const char *GetGlyphForActionOrigin( EControllerActionOrigin eOrigin )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin\n");
|
||||
return "";
|
||||
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin %i\n", eOrigin);
|
||||
|
||||
if (steamcontroller_glyphs.empty()) {
|
||||
std::string dir = settings->glyphs_directory;
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_A] = dir + "button_a.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_B] = dir + "button_b.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_X] = dir + "button_x.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Y] = dir + "button_y.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
|
||||
steamcontroller_glyphs[k_EControllerActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
|
||||
}
|
||||
|
||||
auto glyph = steamcontroller_glyphs.find(eOrigin);
|
||||
if (glyph == steamcontroller_glyphs.end()) return "";
|
||||
return glyph->second.c_str();
|
||||
}
|
||||
|
||||
const char *GetGlyphForActionOrigin( EInputActionOrigin eOrigin )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin steaminput\n");
|
||||
return "";
|
||||
PRINT_DEBUG("Steam_Controller::GetGlyphForActionOrigin steaminput %i\n", eOrigin);
|
||||
if (steaminput_glyphs.empty()) {
|
||||
std::string dir = settings->glyphs_directory;
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_A] = dir + "button_a.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_B] = dir + "button_b.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_X] = dir + "button_x.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_Y] = dir + "button_y.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftBumper] = dir + "shoulder_l.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightBumper] = dir + "shoulder_r.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_Start] = dir + "xbox_button_start.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_Back] = dir + "xbox_button_select.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Pull] = dir + "trigger_l_pull.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftTrigger_Click] = dir + "trigger_l_click.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Pull] = dir + "trigger_r_pull.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightTrigger_Click] = dir + "trigger_r_click.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Move] = dir + "stick_l_move.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_Click] = dir + "stick_l_click.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadNorth] = dir + "stick_dpad_n.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadSouth] = dir + "stick_dpad_s.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadWest] = dir + "stick_dpad_w.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_LeftStick_DPadEast] = dir + "stick_dpad_e.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Move] = dir + "stick_r_move.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_Click] = dir + "stick_r_click.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadNorth] = dir + "stick_dpad_n.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadSouth] = dir + "stick_dpad_s.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadWest] = dir + "stick_dpad_w.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_RightStick_DPadEast] = dir + "stick_dpad_e.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_North] = dir + "xbox_button_dpad_n.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_South] = dir + "xbox_button_dpad_s.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_West] = dir + "xbox_button_dpad_w.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_East] = dir + "xbox_button_dpad_e.png";
|
||||
steaminput_glyphs[k_EInputActionOrigin_XBox360_DPad_Move] = dir + "xbox_button_dpad_move.png";
|
||||
//steaminput_glyphs[] = dir + "";
|
||||
}
|
||||
|
||||
auto glyph = steaminput_glyphs.find(eOrigin);
|
||||
if (glyph == steaminput_glyphs.end()) return "";
|
||||
return glyph->second.c_str();
|
||||
}
|
||||
|
||||
// Returns the input type for a particular handle
|
||||
|
|
|
@ -15,7 +15,11 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef __INCLUDED_STEAM_FRIENDS_H__
|
||||
#define __INCLUDED_STEAM_FRIENDS_H__
|
||||
|
||||
#include "base.h"
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
#define SEND_FRIEND_RATE 4.0
|
||||
|
||||
|
@ -46,6 +50,7 @@ public ISteamFriends
|
|||
class SteamCallBacks *callbacks;
|
||||
class SteamCallResults *callback_results;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
class Steam_Overlay* overlay;
|
||||
|
||||
Friend us;
|
||||
bool modified;
|
||||
|
@ -98,10 +103,9 @@ struct Avatar_Numbers add_friend_avatars(CSteamID id)
|
|||
|
||||
//TODO: get real image data from self/other peers
|
||||
struct Avatar_Numbers avatar_numbers;
|
||||
char zero_array[184 * 184 * 4] = {};
|
||||
std::string small_avatar(zero_array, 32 * 32 * 4);
|
||||
std::string medium_avatar(zero_array, 64 * 64 * 4);
|
||||
std::string large_avatar(zero_array, 184 * 184 * 4);
|
||||
std::string small_avatar(32 * 32 * 4, 0);
|
||||
std::string medium_avatar(64 * 64 * 4, 0);
|
||||
std::string large_avatar(184 * 184 * 4, 0);
|
||||
|
||||
avatar_numbers.smallest = settings->add_image(small_avatar, 32, 32);
|
||||
avatar_numbers.medium = settings->add_image(medium_avatar, 64, 64);
|
||||
|
@ -128,13 +132,14 @@ static void steam_friends_run_every_runcb(void *object)
|
|||
steam_friends->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Friends(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
Steam_Friends(Settings* settings, Networking* network, SteamCallResults* callback_results, SteamCallBacks* callbacks, RunEveryRunCB* run_every_runcb, Steam_Overlay* overlay):
|
||||
settings(settings),
|
||||
network(network),
|
||||
callbacks(callbacks),
|
||||
callback_results(callback_results),
|
||||
run_every_runcb(run_every_runcb),
|
||||
overlay(overlay)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->callbacks = callbacks;
|
||||
this->callback_results = callback_results;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
this->network->setCallback(CALLBACK_ID_FRIEND, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
|
||||
this->network->setCallback(CALLBACK_ID_FRIEND_MESSAGES, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Friends::steam_friends_callback, this);
|
||||
|
@ -150,12 +155,9 @@ Steam_Friends(class Settings *settings, class Networking *network, class SteamCa
|
|||
|
||||
static bool ok_friend_flags(int iFriendFlags)
|
||||
{
|
||||
if (iFriendFlags & k_EFriendFlagBlocked) return false;
|
||||
if (iFriendFlags & k_EFriendFlagIgnored) return false;
|
||||
if (iFriendFlags & k_EFriendFlagIgnoredFriend) return false;
|
||||
if (iFriendFlags & k_EFriendFlagFriendshipRequested) return false;
|
||||
if (iFriendFlags & k_EFriendFlagRequestingFriendship) return false;
|
||||
return true;
|
||||
if (iFriendFlags & k_EFriendFlagImmediate) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the local players name - guaranteed to not be NULL.
|
||||
|
@ -212,11 +214,11 @@ EPersonaState GetPersonaState()
|
|||
// then GetFriendByIndex() can then be used to return the id's of each of those users
|
||||
int GetFriendCount( int iFriendFlags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::GetFriendCount\n");
|
||||
PRINT_DEBUG("Steam_Friends::GetFriendCount %i\n", iFriendFlags);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
int count = 0;
|
||||
if (ok_friend_flags(iFriendFlags)) count = friends.size();
|
||||
|
||||
PRINT_DEBUG("count %i\n", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -475,7 +477,7 @@ SteamAPICall_t DownloadClanActivityCounts( STEAM_ARRAY_COUNT(cClansToRequest) CS
|
|||
// steamIDSource can be the steamID of a group, game server, lobby or chat room
|
||||
int GetFriendCountFromSource( CSteamID steamIDSource )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::GetFriendCountFromSource\n");
|
||||
PRINT_DEBUG("Steam_Friends::GetFriendCountFromSource %llu\n", steamIDSource.ConvertToUint64());
|
||||
//TODO
|
||||
return 0;
|
||||
}
|
||||
|
@ -496,6 +498,10 @@ bool IsUserInSource( CSteamID steamIDUser, CSteamID steamIDSource )
|
|||
if (settings->get_lobby() == steamIDSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (settings->subscribed_groups.find(steamIDSource.ConvertToUint64()) != settings->subscribed_groups.end()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Friend *f = find_friend(steamIDUser);
|
||||
if (!f) return false;
|
||||
|
@ -518,6 +524,7 @@ void SetInGameVoiceSpeaking( CSteamID steamIDUser, bool bSpeaking )
|
|||
void ActivateGameOverlay( const char *pchDialog )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::ActivateGameOverlay %s\n", pchDialog);
|
||||
overlay->OpenOverlay(pchDialog);
|
||||
}
|
||||
|
||||
|
||||
|
@ -574,7 +581,7 @@ void SetPlayedWith( CSteamID steamIDUserPlayedWith )
|
|||
void ActivateGameOverlayInviteDialog( CSteamID steamIDLobby )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayInviteDialog\n");
|
||||
// TODO: Here open the overlay
|
||||
overlay->OpenOverlayInvite(steamIDLobby);
|
||||
}
|
||||
|
||||
// gets the small (32x32) avatar of the current user, which is a handle to be used in IClientUtils::GetImageRGBA(), or 0 if none set
|
||||
|
@ -859,8 +866,13 @@ AppId_t GetFriendCoplayGame( CSteamID steamIDFriend )
|
|||
STEAM_CALL_RESULT( JoinClanChatRoomCompletionResult_t )
|
||||
SteamAPICall_t JoinClanChatRoom( CSteamID steamIDClan )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::JoinClanChatRoom\n");
|
||||
return 0;
|
||||
PRINT_DEBUG("Steam_Friends::JoinClanChatRoom %llu\n", steamIDClan.ConvertToUint64());
|
||||
//TODO actually join a room
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
JoinClanChatRoomCompletionResult_t data;
|
||||
data.m_steamIDClanChat = steamIDClan;
|
||||
data.m_eChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
|
||||
bool LeaveClanChatRoom( CSteamID steamIDClan )
|
||||
|
@ -982,6 +994,20 @@ int GetNumChatsWithUnreadPriorityMessages()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ActivateGameOverlayRemotePlayTogetherInviteDialog( CSteamID steamIDLobby )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::ActivateGameOverlayRemotePlayTogetherInviteDialog\n");
|
||||
}
|
||||
|
||||
// Call this before calling ActivateGameOverlayToWebPage() to have the Steam Overlay Browser block navigations
|
||||
// to your specified protocol (scheme) uris and instead dispatch a OverlayBrowserProtocolNavigation_t callback to your game.
|
||||
// ActivateGameOverlayToWebPage() must have been called with k_EActivateGameOverlayToWebPageMode_Modal
|
||||
bool RegisterProtocolInOverlayBrowser( const char *pchProtocol )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::RegisterProtocolInOverlayBrowser\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Friends::RunCallbacks\n");
|
||||
|
@ -1014,6 +1040,7 @@ void Callback(Common_Message *msg)
|
|||
auto f = std::find_if(friends.begin(), friends.end(), [&id](Friend const& item) { return item.id() == id; });
|
||||
if (friends.end() != f) {
|
||||
persona_change((uint64)f->id(), k_EPersonaChangeStatus);
|
||||
overlay->FriendDisconnect(*f);
|
||||
friends.erase(f);
|
||||
}
|
||||
}
|
||||
|
@ -1039,6 +1066,7 @@ void Callback(Common_Message *msg)
|
|||
if (!f) {
|
||||
if (msg->friend_().id() != settings->get_local_steam_id().ConvertToUint64()) {
|
||||
friends.push_back(msg->friend_());
|
||||
overlay->FriendConnect(msg->friend_());
|
||||
persona_change((uint64)msg->friend_().id(), k_EPersonaChangeName);
|
||||
}
|
||||
} else {
|
||||
|
@ -1059,16 +1087,40 @@ void Callback(Common_Message *msg)
|
|||
if (msg->has_friend_messages()) {
|
||||
if (msg->friend_messages().type() == Friend_Messages::LOBBY_INVITE) {
|
||||
PRINT_DEBUG("Steam_Friends Got Lobby Invite\n");
|
||||
Friend *f = find_friend((uint64)msg->source_id());
|
||||
if (f) {
|
||||
LobbyInvite_t data;
|
||||
data.m_ulSteamIDUser = msg->source_id();
|
||||
data.m_ulSteamIDLobby = msg->friend_messages().lobby_id();
|
||||
data.m_ulGameID = f->appid();
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
if (overlay->Ready())
|
||||
{
|
||||
//TODO: the user should accept the invite first but we auto accept it because there's no gui yet
|
||||
// Then we will handle it !
|
||||
overlay->SetLobbyInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().lobby_id());
|
||||
}
|
||||
else
|
||||
{
|
||||
GameLobbyJoinRequested_t data;
|
||||
data.m_steamIDLobby = CSteamID((uint64)msg->friend_messages().lobby_id());
|
||||
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->friend_messages().type() == Friend_Messages::GAME_INVITE) {
|
||||
PRINT_DEBUG("Steam_Friends Got Game Invite\n");
|
||||
//TODO: I'm pretty sure that the user should accept the invite before this is posted but we do like above
|
||||
if (overlay->Ready())
|
||||
{
|
||||
// Then we will handle it !
|
||||
overlay->SetRichInvite(*find_friend(static_cast<uint64>(msg->source_id())), msg->friend_messages().connect_str().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string const& connect_str = msg->friend_messages().connect_str();
|
||||
GameRichPresenceJoinRequested_t data = {};
|
||||
data.m_steamIDFriend = CSteamID((uint64)msg->source_id());
|
||||
|
@ -1076,6 +1128,9 @@ void Callback(Common_Message *msg)
|
|||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif//__INCLUDED_STEAM_FRIENDS_H__
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#include <queue>
|
||||
|
||||
class Steam_Game_Coordinator :
|
||||
public ISteamGameCoordinator
|
||||
|
|
|
@ -71,6 +71,7 @@ Steam_Game_Search(class Settings *settings, class Networking *network, class Ste
|
|||
EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char *pchValuesToFind )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::AddGameSearchParams\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -80,6 +81,7 @@ EGameSearchErrorCode_t AddGameSearchParams( const char *pchKeyToFind, const char
|
|||
EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlayerMin, int nPlayerMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SearchForGameWithLobby\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -88,6 +90,7 @@ EGameSearchErrorCode_t SearchForGameWithLobby( CSteamID steamIDLobby, int nPlaye
|
|||
EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SearchForGameSolo\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,11 +99,13 @@ EGameSearchErrorCode_t SearchForGameSolo( int nPlayerMin, int nPlayerMax )
|
|||
EGameSearchErrorCode_t AcceptGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::AcceptGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
EGameSearchErrorCode_t DeclineGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::DeclineGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,6 +113,7 @@ EGameSearchErrorCode_t DeclineGame()
|
|||
EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pchConnectionDetails, int cubConnectionDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::RetrieveConnectionDetails\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -115,6 +121,7 @@ EGameSearchErrorCode_t RetrieveConnectionDetails( CSteamID steamIDHost, char *pc
|
|||
EGameSearchErrorCode_t EndGameSearch()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::EndGameSearch\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -125,6 +132,7 @@ EGameSearchErrorCode_t EndGameSearch()
|
|||
EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SetGameHostParams\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -132,6 +140,7 @@ EGameSearchErrorCode_t SetGameHostParams( const char *pchKey, const char *pchVal
|
|||
EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, int cubConnectionDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SetConnectionDetails\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -143,6 +152,7 @@ EGameSearchErrorCode_t SetConnectionDetails( const char *pchConnectionDetails, i
|
|||
EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, int nMaxTeamSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::RequestPlayersForGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -152,6 +162,7 @@ EGameSearchErrorCode_t RequestPlayersForGame( int nPlayerMin, int nPlayerMax, in
|
|||
EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::HostConfirmGameStart\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -160,6 +171,7 @@ EGameSearchErrorCode_t HostConfirmGameStart( uint64 ullUniqueGameID )
|
|||
EGameSearchErrorCode_t CancelRequestPlayersForGame()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::CancelRequestPlayersForGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -167,6 +179,7 @@ EGameSearchErrorCode_t CancelRequestPlayersForGame()
|
|||
EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID steamIDPlayer, EPlayerResult_t EPlayerResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::SubmitPlayerResult\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -175,6 +188,7 @@ EGameSearchErrorCode_t SubmitPlayerResult( uint64 ullUniqueGameID, CSteamID stea
|
|||
EGameSearchErrorCode_t EndGame( uint64 ullUniqueGameID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Game_Search::EndGame\n");
|
||||
return k_EGameSearchErrorCode_Failed_Offline;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
|
|
|
@ -187,7 +187,7 @@ bool Steam_GameServer::BSecure()
|
|||
PRINT_DEBUG("BSecure\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!policy_response_called) return false;
|
||||
return flags == eServerModeAuthenticationAndSecure;
|
||||
return !!(flags & k_unServerFlagSecure);
|
||||
}
|
||||
|
||||
CSteamID Steam_GameServer::GetSteamID()
|
||||
|
@ -472,6 +472,7 @@ bool Steam_GameServer::BSetServerType( uint32 unServerFlags, uint32 unGameIP, ui
|
|||
version.erase(std::remove(version.begin(), version.end(), ' '), version.end());
|
||||
version.erase(std::remove(version.begin(), version.end(), '.'), version.end());
|
||||
server_data.set_version(stoi(version));
|
||||
flags = unServerFlags;
|
||||
|
||||
//TODO?
|
||||
return true;
|
||||
|
@ -599,15 +600,22 @@ SteamAPICall_t Steam_GameServer::GetServerReputation()
|
|||
// Returns the public IP of the server according to Steam, useful when the server is
|
||||
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
|
||||
// connect to
|
||||
uint32 Steam_GameServer::GetPublicIP()
|
||||
uint32 Steam_GameServer::GetPublicIP_old()
|
||||
{
|
||||
PRINT_DEBUG("GetPublicIP\n");
|
||||
PRINT_DEBUG("GetPublicIP_old\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
uint32 ip = network->getOwnIP();
|
||||
PRINT_DEBUG("%X\n", ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
SteamIPAddress_t Steam_GameServer::GetPublicIP()
|
||||
{
|
||||
PRINT_DEBUG("GetPublicIP\n");
|
||||
SteamIPAddress_t ip = SteamIPAddress_t::IPv4Any();
|
||||
ip.m_unIPv4 = GetPublicIP_old();
|
||||
return ip;
|
||||
}
|
||||
|
||||
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
|
||||
// socket to talk to the master server on, it lets the game use its socket to forward messages
|
||||
|
@ -726,7 +734,7 @@ void Steam_GameServer::RunCallbacks()
|
|||
if (logged_in && !policy_response_called) {
|
||||
PRINT_DEBUG("Steam_GameServer::GSPolicyResponse_t\n");
|
||||
GSPolicyResponse_t data;
|
||||
data.m_bSecure = flags == eServerModeAuthenticationAndSecure;
|
||||
data.m_bSecure = !!(flags & k_unServerFlagSecure);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.11);
|
||||
policy_response_called = true;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public ISteamGameServer008,
|
|||
public ISteamGameServer009,
|
||||
public ISteamGameServer010,
|
||||
public ISteamGameServer011,
|
||||
public ISteamGameServer012,
|
||||
public ISteamGameServer
|
||||
{
|
||||
class Settings *settings;
|
||||
|
@ -288,7 +289,8 @@ public:
|
|||
// Returns the public IP of the server according to Steam, useful when the server is
|
||||
// behind NAT and you want to advertise its IP in a lobby for other clients to directly
|
||||
// connect to
|
||||
uint32 GetPublicIP();
|
||||
uint32 GetPublicIP_old();
|
||||
SteamIPAddress_t GetPublicIP();
|
||||
|
||||
// These are in GameSocketShare mode, where instead of ISteamGameServer creating its own
|
||||
// socket to talk to the master server on, it lets the game use its socket to forward messages
|
||||
|
|
|
@ -116,7 +116,7 @@ bool Steam_HTTP::SetHTTPRequestGetOrPostParameter( HTTPRequestHandle hRequest, c
|
|||
// header and only do a local cache lookup rather than sending any actual remote request.
|
||||
bool Steam_HTTP::SendHTTPRequest( HTTPRequestHandle hRequest, SteamAPICall_t *pCallHandle )
|
||||
{
|
||||
PRINT_DEBUG("SendHTTPRequest\n");
|
||||
PRINT_DEBUG("SendHTTPRequest %u %p\n", hRequest, pCallHandle);
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
|
@ -276,7 +276,12 @@ bool Steam_HTTP::GetHTTPDownloadProgressPct( HTTPRequestHandle hRequest, float *
|
|||
bool Steam_HTTP::SetHTTPRequestRawPostBody( HTTPRequestHandle hRequest, const char *pchContentType, uint8 *pubBody, uint32 unBodyLen )
|
||||
{
|
||||
PRINT_DEBUG("SetHTTPRequestRawPostBody\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -360,5 +365,11 @@ bool Steam_HTTP::SetHTTPRequestAbsoluteTimeoutMS( HTTPRequestHandle hRequest, ui
|
|||
bool Steam_HTTP::GetHTTPRequestWasTimedOut( HTTPRequestHandle hRequest, bool *pbWasTimedOut )
|
||||
{
|
||||
PRINT_DEBUG("GetHTTPRequestWasTimedOut\n");
|
||||
Steam_Http_Request *request = get_request(hRequest);
|
||||
if (!request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pbWasTimedOut) *pbWasTimedOut = false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h" // For SteamItemDef_t
|
||||
#include "../json/json.hpp"
|
||||
|
||||
struct Steam_Inventory_Requests {
|
||||
double timeout = 0.1;
|
||||
|
@ -441,6 +440,7 @@ bool DeserializeResult( SteamInventoryResult_t *pOutResultHandle, STEAM_BUFFER_C
|
|||
bool GenerateItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("GenerateItems\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -452,6 +452,11 @@ STEAM_METHOD_DESC(GrantPromoItems() checks the list of promotional items for whi
|
|||
bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
|
||||
{
|
||||
PRINT_DEBUG("GrantPromoItems\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -463,13 +468,25 @@ bool GrantPromoItems( SteamInventoryResult_t *pResultHandle )
|
|||
bool AddPromoItem( SteamInventoryResult_t *pResultHandle, SteamItemDef_t itemDef )
|
||||
{
|
||||
PRINT_DEBUG("AddPromoItem\n");
|
||||
return false;
|
||||
//TODO
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddPromoItems( SteamInventoryResult_t *pResultHandle, STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("AddPromoItems\n");
|
||||
return false;
|
||||
//TODO
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -480,6 +497,7 @@ STEAM_METHOD_DESC(ConsumeItem() removes items from the inventory permanently.)
|
|||
bool ConsumeItem( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemConsume, uint32 unQuantity )
|
||||
{
|
||||
PRINT_DEBUG("ConsumeItem\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -496,6 +514,7 @@ bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
|
|||
STEAM_ARRAY_COUNT(unArrayDestroyLength) const SteamItemInstanceID_t *pArrayDestroy, STEAM_ARRAY_COUNT(unArrayDestroyLength) const uint32 *punArrayDestroyQuantity, uint32 unArrayDestroyLength )
|
||||
{
|
||||
PRINT_DEBUG("ExchangeItems\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -507,6 +526,7 @@ bool ExchangeItems( SteamInventoryResult_t *pResultHandle,
|
|||
bool TransferItemQuantity( SteamInventoryResult_t *pResultHandle, SteamItemInstanceID_t itemIdSource, uint32 unQuantity, SteamItemInstanceID_t itemIdDest )
|
||||
{
|
||||
PRINT_DEBUG("TransferItemQuantity\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -534,8 +554,13 @@ void SendItemDropHeartbeat()
|
|||
STEAM_METHOD_DESC(Playtime credit must be consumed and turned into item drops by your game.)
|
||||
bool TriggerItemDrop( SteamInventoryResult_t *pResultHandle, SteamItemDef_t dropListDefinition )
|
||||
{
|
||||
PRINT_DEBUG("TriggerItemDrop\n");
|
||||
PRINT_DEBUG("TriggerItemDrop %p %i\n", pResultHandle, dropListDefinition);
|
||||
//TODO: if gameserver return false
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Steam_Inventory_Requests* request = new_inventory_result(false);
|
||||
|
||||
if (pResultHandle != nullptr)
|
||||
*pResultHandle = request->inventory_result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -591,7 +616,7 @@ bool GetItemDefinitionIDs(
|
|||
STEAM_OUT_ARRAY_COUNT(punItemDefIDsArraySize,List of item definition IDs) SteamItemDef_t *pItemDefIDs,
|
||||
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
|
||||
{
|
||||
PRINT_DEBUG("GetItemDefinitionIDs\n");
|
||||
PRINT_DEBUG("GetItemDefinitionIDs %p\n", pItemDefIDs);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (!punItemDefIDsArraySize)
|
||||
return false;
|
||||
|
@ -725,6 +750,7 @@ STEAM_CALL_RESULT( SteamInventoryEligiblePromoItemDefIDs_t )
|
|||
SteamAPICall_t RequestEligiblePromoItemDefinitionsIDs( CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("RequestEligiblePromoItemDefinitionsIDs\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -737,6 +763,7 @@ bool GetEligiblePromoItemDefinitionIDs(
|
|||
STEAM_DESC(Size of array is passed in and actual size used is returned in this param) uint32 *punItemDefIDsArraySize )
|
||||
{
|
||||
PRINT_DEBUG("GetEligiblePromoItemDefinitionIDs\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -749,6 +776,7 @@ STEAM_CALL_RESULT( SteamInventoryStartPurchaseResult_t )
|
|||
SteamAPICall_t StartPurchase( STEAM_ARRAY_COUNT(unArrayLength) const SteamItemDef_t *pArrayItemDefs, STEAM_ARRAY_COUNT(unArrayLength) const uint32 *punArrayQuantity, uint32 unArrayLength )
|
||||
{
|
||||
PRINT_DEBUG("StartPurchase\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -793,6 +821,7 @@ bool GetItemsWithPrices( STEAM_ARRAY_COUNT(unArrayLength) STEAM_OUT_ARRAY_COUNT(
|
|||
bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pCurrentPrice, uint64 *pBasePrice )
|
||||
{
|
||||
PRINT_DEBUG("GetItemPrice\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieves the price for the item definition id
|
||||
|
@ -808,39 +837,52 @@ bool GetItemPrice( SteamItemDef_t iDefinition, uint64 *pPrice )
|
|||
SteamInventoryUpdateHandle_t StartUpdateProperties()
|
||||
{
|
||||
PRINT_DEBUG("StartUpdateProperties\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove the property on the item
|
||||
bool RemoveProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName )
|
||||
{
|
||||
PRINT_DEBUG("RemoveProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accessor methods to set properties on items
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, const char *pchPropertyValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, int64 nValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetProperty( SteamInventoryUpdateHandle_t handle, SteamItemInstanceID_t nItemID, const char *pchPropertyName, float flValue )
|
||||
{
|
||||
PRINT_DEBUG("SetProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Submit the update request by handle
|
||||
bool SubmitUpdateProperties( SteamInventoryUpdateHandle_t handle, SteamInventoryResult_t * pResultHandle )
|
||||
{
|
||||
PRINT_DEBUG("SubmitUpdateProperties\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InspectItem( SteamInventoryResult_t *pResultHandle, const char *pchItemToken )
|
||||
{
|
||||
PRINT_DEBUG("InspectItem\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
|
|
|
@ -91,7 +91,23 @@ public ISteamMatchmaking
|
|||
std::vector<struct Chat_Entry> chat_entries;
|
||||
std::vector<struct Data_Requested> data_requested;
|
||||
|
||||
std::map<uint64, std::map<std::string, std::string>> self_lobby_member_data;
|
||||
std::map<uint64, ::google::protobuf::Map<std::string, std::string>> self_lobby_member_data;
|
||||
google::protobuf::Map<std::string,std::string>::const_iterator caseinsensitive_find(const ::google::protobuf::Map< ::std::string, ::std::string >& map, std::string key)
|
||||
{
|
||||
auto x = map.begin();
|
||||
while (x != map.end()) {
|
||||
if (key.size() == x->first.size() && std::equal(x->first.begin(), x->first.end(), key.begin(),
|
||||
[](char a, char b) {
|
||||
return tolower(a) == tolower(b);
|
||||
})) {
|
||||
break;
|
||||
}
|
||||
++x;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
Lobby *get_lobby(CSteamID id)
|
||||
{
|
||||
auto lobby = std::find_if(lobbies.begin(), lobbies.end(), [&id](Lobby const& item) { return item.room_id() == id.ConvertToUint64(); });
|
||||
|
@ -116,7 +132,7 @@ void send_lobby_data()
|
|||
}
|
||||
}
|
||||
|
||||
void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, double cb_timeout=0.0)
|
||||
void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, double cb_timeout=0.005, bool send_changed_lobby=true)
|
||||
{
|
||||
PRINT_DEBUG("Lobby dataupdate %llu %llu\n", lobby.ConvertToUint64(), member.ConvertToUint64());
|
||||
LobbyDataUpdate_t data;
|
||||
|
@ -135,11 +151,13 @@ void trigger_lobby_dataupdate(CSteamID lobby, CSteamID member, bool success, dou
|
|||
|
||||
Lobby *l = get_lobby(lobby);
|
||||
if (l && l->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
||||
if (send_changed_lobby) {
|
||||
Common_Message msg = Common_Message();
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_allocated_lobby(new Lobby(*l));
|
||||
network->sendToAllIndividuals(&msg, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_lobby_member_join_leave(CSteamID lobby, CSteamID member, bool leaving, bool success, double cb_timeout=0.0)
|
||||
|
@ -382,8 +400,7 @@ bool RemoveFavoriteGame( AppId_t nAppID, uint32 nIP, uint16 nConnPort, uint16 nQ
|
|||
}
|
||||
*/
|
||||
//
|
||||
#define LOBBY_SEARCH_TIMEOUT 3.0 //defined by steam as being 20 seconds max and 3 seconds typically (Should be no less than 3 because or else SGZH doesn't work).
|
||||
//Sanctum 2 needs this to be 2 seconds for the game to appear in the list though.
|
||||
#define LOBBY_SEARCH_TIMEOUT 0.2 //Tested on real steam
|
||||
STEAM_CALL_RESULT( LobbyMatchList_t )
|
||||
SteamAPICall_t RequestLobbyList()
|
||||
{
|
||||
|
@ -458,7 +475,7 @@ void AddRequestLobbyListFilterSlotsAvailable( int nSlotsAvailable )
|
|||
// sets the distance for which we should search for lobbies (based on users IP address to location map on the Steam backed)
|
||||
void AddRequestLobbyListDistanceFilter( ELobbyDistanceFilter eLobbyDistanceFilter )
|
||||
{
|
||||
PRINT_DEBUG("AddRequestLobbyListDistanceFilter\n");
|
||||
PRINT_DEBUG("AddRequestLobbyListDistanceFilter %i\n", eLobbyDistanceFilter);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
|
||||
|
@ -506,12 +523,13 @@ CSteamID GetLobbyByIndex( int iLobby )
|
|||
return id;
|
||||
}
|
||||
|
||||
static void enter_lobby(Lobby *lobby, CSteamID id)
|
||||
static bool enter_lobby(Lobby *lobby, CSteamID id)
|
||||
{
|
||||
if (get_lobby_member(lobby, id)) return;
|
||||
if (get_lobby_member(lobby, id)) return false;
|
||||
|
||||
Lobby_Member *member = lobby->add_members();
|
||||
member->set_id(id.ConvertToUint64());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool leave_lobby(Lobby *lobby, CSteamID id)
|
||||
|
@ -603,35 +621,12 @@ SteamAPICall_t JoinLobby( CSteamID steamIDLobby )
|
|||
PRINT_DEBUG("JoinLobby %llu\n", steamIDLobby.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
auto pj = std::find_if(pending_joins.begin(), pending_joins.end(), [&steamIDLobby](Pending_Joins const& item) {return item.lobby_id == steamIDLobby;});
|
||||
if (pj != pending_joins.end()) return pj->api_id;
|
||||
|
||||
Pending_Joins pending_join;
|
||||
pending_join.api_id = callback_results->reserveCallResult();
|
||||
pending_join.lobby_id = steamIDLobby;
|
||||
Lobby *lobby = get_lobby(steamIDLobby);
|
||||
bool success = true;
|
||||
if (lobby && lobby->deleted()) {
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = lobby->room_id();
|
||||
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
||||
data.m_bLocked = false;
|
||||
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseError;
|
||||
auto api = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
if (get_lobby_member(lobby, settings->get_local_steam_id())) {
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = lobby->room_id();
|
||||
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
||||
data.m_bLocked = false;
|
||||
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
|
||||
auto api = callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
pending_join.joined = std::chrono::high_resolution_clock::now();
|
||||
pending_joins.push_back(pending_join);
|
||||
|
||||
|
@ -653,6 +648,7 @@ void LeaveLobby( CSteamID steamIDLobby )
|
|||
if (lobbies.end() != lobby) {
|
||||
if (!lobby->deleted()) {
|
||||
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), true);
|
||||
self_lobby_member_data.erase(lobby->room_id());
|
||||
if (lobby->owner() != settings->get_local_steam_id().ConvertToUint64()) {
|
||||
PRINT_DEBUG("LeaveLobby not owner\n");
|
||||
leave_lobby(&(*lobby), settings->get_local_steam_id());
|
||||
|
@ -746,12 +742,13 @@ CSteamID GetLobbyMemberByIndex( CSteamID steamIDLobby, int iMember )
|
|||
const char *GetLobbyData( CSteamID steamIDLobby, const char *pchKey )
|
||||
{
|
||||
PRINT_DEBUG("GetLobbyData %llu %s\n", steamIDLobby.ConvertToUint64(), pchKey);
|
||||
if (!pchKey) return "";
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Lobby *lobby = get_lobby(steamIDLobby);
|
||||
const char *ret = "";
|
||||
if (lobby) {
|
||||
auto result = lobby->values().find(pchKey);
|
||||
if (result != lobby->values().end()) ret = lobby->values().find(pchKey)->second.c_str();
|
||||
auto result = caseinsensitive_find(lobby->values(), pchKey);
|
||||
if (result != lobby->values().end()) ret = result->second.c_str();
|
||||
}
|
||||
|
||||
PRINT_DEBUG("returned %s\n", ret);
|
||||
|
@ -776,12 +773,16 @@ bool SetLobbyData( CSteamID steamIDLobby, const char *pchKey, const char *pchVal
|
|||
return false;
|
||||
}
|
||||
|
||||
auto result = lobby->values().find(pchKey);
|
||||
if (result == lobby->values().end() || result->second != std::string(pchValue)) {
|
||||
auto result = caseinsensitive_find(lobby->values(), pchKey);
|
||||
bool changed = true;
|
||||
if (result == lobby->values().end()) {
|
||||
(*lobby->mutable_values())[pchKey] = pchValue;
|
||||
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true);
|
||||
} else {
|
||||
if (result->second == std::string(pchValue)) changed = false;
|
||||
(*lobby->mutable_values())[result->first] = pchValue;
|
||||
}
|
||||
|
||||
trigger_lobby_dataupdate(steamIDLobby, steamIDLobby, true, 0.0, changed);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -847,6 +848,7 @@ bool DeleteLobbyData( CSteamID steamIDLobby, const char *pchKey )
|
|||
const char *GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, const char *pchKey )
|
||||
{
|
||||
PRINT_DEBUG("GetLobbyMemberData %s %llu %llu\n", pchKey, steamIDLobby.ConvertToUint64(), steamIDUser.ConvertToUint64());
|
||||
if (!pchKey) return "";
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct Lobby_Member *member = get_lobby_member(get_lobby(steamIDLobby), steamIDUser);
|
||||
const char *ret = "";
|
||||
|
@ -854,13 +856,13 @@ const char *GetLobbyMemberData( CSteamID steamIDLobby, CSteamID steamIDUser, con
|
|||
if (steamIDUser == settings->get_local_steam_id()) {
|
||||
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
|
||||
if (result != self_lobby_member_data.end()) {
|
||||
auto value = result->second.find(std::string(pchKey));
|
||||
auto value = caseinsensitive_find(result->second, std::string(pchKey));
|
||||
if (value != result->second.end()) {
|
||||
ret = value->second.c_str();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto result = member->values().find(std::string(pchKey));
|
||||
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
|
||||
if (result == member->values().end()) return "";
|
||||
ret = result->second.c_str();
|
||||
}
|
||||
|
@ -885,7 +887,12 @@ void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *
|
|||
Lobby_Member *member = get_lobby_member(lobby, settings->get_local_steam_id());
|
||||
if (member) {
|
||||
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
||||
auto result = caseinsensitive_find(member->values(), std::string(pchKey));
|
||||
if (result == member->values().end()) {
|
||||
(*member->mutable_values())[pchKey] = pchValue;
|
||||
} else {
|
||||
(*member->mutable_values())[result->first] = pchValue;
|
||||
}
|
||||
trigger_lobby_dataupdate(steamIDLobby, (uint64)member->id(), true);
|
||||
} else {
|
||||
Lobby_Messages *message = new Lobby_Messages();
|
||||
|
@ -894,10 +901,20 @@ void SetLobbyMemberData( CSteamID steamIDLobby, const char *pchKey, const char *
|
|||
send_owner_packet(steamIDLobby, message);
|
||||
}
|
||||
|
||||
{
|
||||
auto result = self_lobby_member_data.find(steamIDLobby.ConvertToUint64());
|
||||
if (result != self_lobby_member_data.end()) {
|
||||
auto value = caseinsensitive_find(result->second, std::string(pchKey));
|
||||
if (value != result->second.end()) {
|
||||
self_lobby_member_data[steamIDLobby.ConvertToUint64()][value->first] = pchValue;
|
||||
} else {
|
||||
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
self_lobby_member_data[steamIDLobby.ConvertToUint64()][pchKey] = pchValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1086,7 +1103,7 @@ bool SetLobbyJoinable( CSteamID steamIDLobby, bool bLobbyJoinable )
|
|||
|
||||
|
||||
// returns the current lobby owner
|
||||
// you must be a member of the lobby to access this
|
||||
// you must be a member of the lobby to access this (Mr_Goldberg note: This is a lie)
|
||||
// there always one lobby owner - if the current owner leaves, another user will become the owner
|
||||
// it is possible (bur rare) to join a lobby just as the owner is leaving, thus entering a lobby with self as the owner
|
||||
CSteamID GetLobbyOwner( CSteamID steamIDLobby )
|
||||
|
@ -1094,10 +1111,10 @@ CSteamID GetLobbyOwner( CSteamID steamIDLobby )
|
|||
PRINT_DEBUG("GetLobbyOwner %llu\n", steamIDLobby.ConvertToUint64());
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
Lobby *lobby = get_lobby(steamIDLobby);
|
||||
CSteamID id = k_steamIDNil;
|
||||
if (lobby) id = (uint64)lobby->owner();
|
||||
if (!lobby || lobby->deleted()) return k_steamIDNil;
|
||||
|
||||
return id;
|
||||
//TODO: might be better to require the lobby info to be at least requested first.
|
||||
return (uint64)lobby->owner();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1153,7 +1170,7 @@ void RunCallbacks()
|
|||
PRINT_DEBUG("use lobby: %u, filters: %zu, joinable: %u, type: %u, deleted: %u\n", use, filter_values_copy.size(), l.joinable(), l.type(), l.deleted());
|
||||
for (auto & f : filter_values_copy) {
|
||||
PRINT_DEBUG("%s:%s/%i %u %i\n", f.key.c_str(), f.value_string.c_str(), f.value_int, f.is_int, f.eComparisonType);
|
||||
auto value = l.values().find(f.key);
|
||||
auto value = caseinsensitive_find(l.values(), f.key);
|
||||
if (value != l.values().end()) {
|
||||
//TODO: eComparisonType
|
||||
if (!f.is_int) {
|
||||
|
@ -1169,7 +1186,12 @@ void RunCallbacks()
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
int compare_to = std::stoi(value->second, 0, 0);
|
||||
PRINT_DEBUG("%s\n", value->second.c_str());
|
||||
int compare_to = 0;
|
||||
//TODO: check if this is how real steam behaves
|
||||
if (value->second.size()) {
|
||||
compare_to = std::stoll(value->second, 0, 0);
|
||||
}
|
||||
PRINT_DEBUG("Compare Values %i %i\n", compare_to, f.value_int);
|
||||
if (f.eComparisonType == k_ELobbyComparisonEqual) {
|
||||
if (compare_to == f.value_int) {
|
||||
|
@ -1227,13 +1249,36 @@ void RunCallbacks()
|
|||
g->message_sent = send_owner_packet(g->lobby_id, message);
|
||||
}
|
||||
|
||||
Lobby *lobby = get_lobby(g->lobby_id);
|
||||
if (lobby && lobby->deleted()) {
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = lobby->room_id();
|
||||
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
||||
data.m_bLocked = false;
|
||||
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
|
||||
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
g = pending_joins.erase(g);
|
||||
} else
|
||||
|
||||
if (get_lobby_member(lobby, settings->get_local_steam_id())) {
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = lobby->room_id();
|
||||
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
||||
data.m_bLocked = false;
|
||||
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseSuccess;
|
||||
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
g = pending_joins.erase(g);
|
||||
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
||||
} else
|
||||
|
||||
if (check_timedout(g->joined, PENDING_JOIN_TIMEOUT)) {
|
||||
bool success = false;
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = g->lobby_id.ConvertToUint64();
|
||||
data.m_rgfChatPermissions = 0; //Unused - Always 0
|
||||
data.m_bLocked = false;
|
||||
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
|
||||
data.m_EChatRoomEnterResponse = k_EChatRoomEnterResponseDoesntExist;
|
||||
callback_results->addCallResult(g->api_id, data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
g = pending_joins.erase(g);
|
||||
|
@ -1271,7 +1316,7 @@ void Callback(Common_Message *msg)
|
|||
if (msg->lobby().owner() != settings->get_local_steam_id().ConvertToUint64() && msg->lobby().appid() == settings->get_local_game_id().AppID()) {
|
||||
Lobby *lobby = get_lobby((uint64)msg->lobby().room_id());
|
||||
if (!lobby) {
|
||||
unsigned int old_size = lobbies.size();
|
||||
size_t old_size = lobbies.size();
|
||||
lobbies.resize(old_size + 1);
|
||||
lobbies[old_size].set_room_id(msg->lobby().room_id());
|
||||
lobby = &(lobbies[old_size]);
|
||||
|
@ -1301,8 +1346,9 @@ void Callback(Common_Message *msg)
|
|||
if (!member) {
|
||||
if (m.id() == settings->get_local_steam_id().ConvertToUint64()) {
|
||||
CSteamID id((uint64)lobby->room_id());
|
||||
auto pd = std::find_if(pending_joins.begin(), pending_joins.end(), [&id](Pending_Joins const& item) { return item.lobby_id == id; });
|
||||
if (pd != pending_joins.end()) {
|
||||
auto pd = pending_joins.begin();
|
||||
while (pd != pending_joins.end()) {
|
||||
if (pd->lobby_id == id) {
|
||||
bool success = true;
|
||||
LobbyEnter_t data;
|
||||
data.m_ulSteamIDLobby = lobby->room_id();
|
||||
|
@ -1311,10 +1357,15 @@ void Callback(Common_Message *msg)
|
|||
data.m_EChatRoomEnterResponse = success ? k_EChatRoomEnterResponseSuccess : k_EChatRoomEnterResponseError;
|
||||
callback_results->addCallResult(pd->api_id, data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
pending_joins.erase(pd);
|
||||
pd = pending_joins.erase(pd);
|
||||
joined = true;
|
||||
} else {
|
||||
++pd;
|
||||
}
|
||||
}
|
||||
if (joined) {
|
||||
on_self_enter_leave_lobby((uint64)lobby->room_id(), lobby->type(), false);
|
||||
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)lobby->room_id(), true);
|
||||
joined = true;
|
||||
}
|
||||
} else {
|
||||
if (we_are_in_lobby) trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)m.id(), false, true);
|
||||
|
@ -1355,8 +1406,9 @@ void Callback(Common_Message *msg)
|
|||
if (lobby->owner() == settings->get_local_steam_id().ConvertToUint64()) {
|
||||
if (msg->lobby_messages().type() == Lobby_Messages::JOIN) {
|
||||
PRINT_DEBUG("LOBBY MESSAGE: JOIN\n");
|
||||
enter_lobby(lobby, (uint64)msg->source_id());
|
||||
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), false, true);
|
||||
if (enter_lobby(lobby, (uint64)msg->source_id())) {
|
||||
trigger_lobby_member_join_leave((uint64)lobby->room_id(), (uint64)msg->source_id(), false, true, 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->lobby_messages().type() == Lobby_Messages::MEMBER_DATA) {
|
||||
|
@ -1365,7 +1417,12 @@ void Callback(Common_Message *msg)
|
|||
if (member) {
|
||||
for (auto const &p : msg->lobby_messages().map()) {
|
||||
PRINT_DEBUG("member data %s:%s\n", p.first.c_str(), p.second.c_str());
|
||||
auto result = caseinsensitive_find(member->values(), p.first);
|
||||
if (result == member->values().end()) {
|
||||
(*member->mutable_values())[p.first] = p.second;
|
||||
} else {
|
||||
(*member->mutable_values())[result->first] = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
trigger_lobby_dataupdate((uint64)lobby->room_id(), (uint64)member->id(), true);
|
||||
|
|
|
@ -338,6 +338,7 @@ HServerQuery Steam_Matchmaking_Servers::PingServer( uint32 unIP, uint16 usPort,
|
|||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.ping_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
@ -352,6 +353,7 @@ HServerQuery Steam_Matchmaking_Servers::PlayerDetails( uint32 unIP, uint16 usPor
|
|||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.players_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
@ -367,6 +369,7 @@ HServerQuery Steam_Matchmaking_Servers::ServerRules( uint32 unIP, uint16 usPort,
|
|||
r.ip = unIP;
|
||||
r.port = usPort;
|
||||
r.rules_response = pRequestServersResponse;
|
||||
r.created = std::chrono::high_resolution_clock::now();
|
||||
direct_ip_requests.push_back(r);
|
||||
return r.id;
|
||||
}
|
||||
|
@ -439,8 +442,16 @@ void Steam_Matchmaking_Servers::RunCallbacks()
|
|||
}
|
||||
}
|
||||
|
||||
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp = direct_ip_requests;
|
||||
direct_ip_requests.clear();
|
||||
std::vector <struct Steam_Matchmaking_Servers_Direct_IP_Request> direct_ip_requests_temp;
|
||||
auto dip = std::begin(direct_ip_requests);
|
||||
while (dip != std::end(direct_ip_requests)) {
|
||||
if (check_timedout(dip->created, DIRECT_IP_DELAY)) {
|
||||
direct_ip_requests_temp.push_back(*dip);
|
||||
dip = direct_ip_requests.erase(dip);
|
||||
} else {
|
||||
++dip;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &r : direct_ip_requests_temp) {
|
||||
PRINT_DEBUG("dip request: %lu:%hu\n", r.ip, r.port);
|
||||
|
@ -486,6 +497,7 @@ void Steam_Matchmaking_Servers::RunCallbacks()
|
|||
void Steam_Matchmaking_Servers::Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_gameserver()) {
|
||||
PRINT_DEBUG("got SERVER %llu, offline:%u\n", msg->gameserver().id(), msg->gameserver().offline());
|
||||
if (msg->gameserver().offline()) {
|
||||
for (auto &g : gameservers) {
|
||||
if (g.server.id() == msg->gameserver().id()) {
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
#include "base.h"
|
||||
|
||||
#define SERVER_TIMEOUT 10.0
|
||||
#define DIRECT_IP_DELAY 0.05
|
||||
|
||||
struct Steam_Matchmaking_Servers_Direct_IP_Request {
|
||||
HServerQuery id;
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point created;
|
||||
ISteamMatchmakingRulesResponse *rules_response = NULL;
|
||||
ISteamMatchmakingPlayersResponse *players_response = NULL;
|
||||
ISteamMatchmakingPingResponse *ping_response = NULL;
|
||||
|
|
|
@ -20,171 +20,203 @@
|
|||
// Service Definition
|
||||
bool Steam_MusicRemote::RegisterSteamMusicRemote( const char *pchName )
|
||||
{
|
||||
PRINT_DEBUG("RegisterSteamMusicRemote\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::DeregisterSteamMusicRemote()
|
||||
{
|
||||
PRINT_DEBUG("DeregisterSteamMusicRemote\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::BIsCurrentMusicRemote()
|
||||
{
|
||||
PRINT_DEBUG("BIsCurrentMusicRemote\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::BActivationSuccess( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("BActivationSuccess\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Steam_MusicRemote::SetDisplayName( const char *pchDisplayName )
|
||||
{
|
||||
PRINT_DEBUG("SetDisplayName\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetPNGIcon_64x64( void *pvBuffer, uint32 cbBufferLength )
|
||||
{
|
||||
PRINT_DEBUG("SetPNGIcon_64x64\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Abilities for the user interface
|
||||
bool Steam_MusicRemote::EnablePlayPrevious(bool bValue)
|
||||
{
|
||||
PRINT_DEBUG("EnablePlayPrevious\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnablePlayNext( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("EnablePlayNext\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableShuffled( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("EnableShuffled\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableLooped( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("EnableLooped\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnableQueue( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("EnableQueue\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::EnablePlaylists( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("EnablePlaylists\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Status
|
||||
bool Steam_MusicRemote::UpdatePlaybackStatus( AudioPlayback_Status nStatus )
|
||||
{
|
||||
PRINT_DEBUG("UpdatePlaybackStatus\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateShuffled( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("UpdateShuffled\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateLooped( bool bValue )
|
||||
{
|
||||
PRINT_DEBUG("UpdateLooped\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateVolume( float flValue )
|
||||
{
|
||||
PRINT_DEBUG("UpdateVolume\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
// volume is between 0.0 and 1.0
|
||||
|
||||
// Current Entry
|
||||
bool Steam_MusicRemote::CurrentEntryWillChange()
|
||||
{
|
||||
PRINT_DEBUG("CurrentEntryWillChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::CurrentEntryIsAvailable( bool bAvailable )
|
||||
{
|
||||
PRINT_DEBUG("CurrentEntryIsAvailable\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryText( const char *pchText )
|
||||
{
|
||||
PRINT_DEBUG("UpdateCurrentEntryText\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryElapsedSeconds( int nValue )
|
||||
{
|
||||
PRINT_DEBUG("UpdateCurrentEntryElapsedSeconds\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::UpdateCurrentEntryCoverArt( void *pvBuffer, uint32 cbBufferLength )
|
||||
{
|
||||
PRINT_DEBUG("UpdateCurrentEntryCoverArt\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::CurrentEntryDidChange()
|
||||
{
|
||||
PRINT_DEBUG("CurrentEntryDidChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Queue
|
||||
bool Steam_MusicRemote::QueueWillChange()
|
||||
{
|
||||
PRINT_DEBUG("QueueWillChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::ResetQueueEntries()
|
||||
{
|
||||
PRINT_DEBUG("ResetQueueEntries\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetQueueEntry( int nID, int nPosition, const char *pchEntryText )
|
||||
{
|
||||
PRINT_DEBUG("SetQueueEntry\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetCurrentQueueEntry( int nID )
|
||||
{
|
||||
PRINT_DEBUG("SetCurrentQueueEntry\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::QueueDidChange()
|
||||
{
|
||||
PRINT_DEBUG("QueueDidChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Playlist
|
||||
bool Steam_MusicRemote::PlaylistWillChange()
|
||||
{
|
||||
PRINT_DEBUG("PlaylistWillChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::ResetPlaylistEntries()
|
||||
{
|
||||
PRINT_DEBUG("ResetPlaylistEntries\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetPlaylistEntry( int nID, int nPosition, const char *pchEntryText )
|
||||
{
|
||||
PRINT_DEBUG("SetPlaylistEntry\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::SetCurrentPlaylistEntry( int nID )
|
||||
{
|
||||
PRINT_DEBUG("SetCurrentPlaylistEntry\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Steam_MusicRemote::PlaylistDidChange()
|
||||
{
|
||||
PRINT_DEBUG("PlaylistDidChange\n");
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#define ORPHANED_PACKET_TIMEOUT (20)
|
||||
#define NEW_CONNECTION_TIMEOUT (20.0)
|
||||
|
||||
//kingdom 2 crowns doesn't work with a 0.3 delay or lower
|
||||
#define NEW_CONNECTION_DELAY (0.4)
|
||||
|
||||
#define OLD_CHANNEL_NUMBER 1
|
||||
|
||||
struct Steam_Networking_Connection {
|
||||
|
@ -59,6 +62,7 @@ public ISteamNetworking001,
|
|||
public ISteamNetworking002,
|
||||
public ISteamNetworking003,
|
||||
public ISteamNetworking004,
|
||||
public ISteamNetworking005,
|
||||
public ISteamNetworking
|
||||
{
|
||||
class Settings *settings;
|
||||
|
@ -67,21 +71,28 @@ public ISteamNetworking
|
|||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
std::vector<Common_Message> messages;
|
||||
std::recursive_mutex messages_mutex;
|
||||
std::list<Common_Message> messages;
|
||||
std::list<Common_Message> unprocessed_messages;
|
||||
|
||||
std::recursive_mutex connections_edit_mutex;
|
||||
std::vector<struct Steam_Networking_Connection> connections;
|
||||
|
||||
std::vector<struct steam_listen_socket> listen_sockets;
|
||||
std::vector<struct steam_connection_socket> connection_sockets;
|
||||
|
||||
std::map<CSteamID, std::chrono::high_resolution_clock::time_point> new_connection_times;
|
||||
std::queue<CSteamID> new_connections_to_call_cb;
|
||||
|
||||
bool connection_exists(CSteamID id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
|
||||
return std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;}) != connections.end();
|
||||
}
|
||||
|
||||
struct Steam_Networking_Connection *get_or_create_connection(CSteamID id)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
|
||||
auto conn = std::find_if(connections.begin(), connections.end(), [&id](struct Steam_Networking_Connection const& conn) { return conn.remote == id;});
|
||||
|
||||
if (connections.end() == conn) {
|
||||
|
@ -96,6 +107,8 @@ struct Steam_Networking_Connection *get_or_create_connection(CSteamID id)
|
|||
|
||||
void remove_connection(CSteamID id)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(connections_edit_mutex);
|
||||
auto conn = std::begin(connections);
|
||||
while (conn != std::end(connections)) {
|
||||
if (conn->remote == id) {
|
||||
|
@ -105,8 +118,11 @@ void remove_connection(CSteamID id)
|
|||
++conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//pretty sure steam also clears the entire queue of messages for that connection
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
|
||||
auto msg = std::begin(messages);
|
||||
while (msg != std::end(messages)) {
|
||||
if (msg->source_id() == id.ConvertToUint64()) {
|
||||
|
@ -115,13 +131,26 @@ void remove_connection(CSteamID id)
|
|||
++msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto msg = std::begin(unprocessed_messages);
|
||||
while (msg != std::end(unprocessed_messages)) {
|
||||
if (msg->source_id() == id.ConvertToUint64()) {
|
||||
msg = unprocessed_messages.erase(msg);
|
||||
} else {
|
||||
++msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SNetSocket_t create_connection_socket(CSteamID target, int nVirtualPort, uint32 nIP, uint16 nPort, SNetListenSocket_t id=0, enum steam_socket_connection_status status=SOCKET_CONNECTING, SNetSocket_t other_id=0)
|
||||
{
|
||||
static SNetSocket_t socket_number = 0;
|
||||
bool found = 0;
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
++socket_number;
|
||||
for (auto & c: connection_sockets) {
|
||||
if (c.id == socket_number || socket_number == 0) {
|
||||
|
@ -248,16 +277,16 @@ bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData,
|
|||
Common_Message msg;
|
||||
msg.set_source_id(settings->get_local_steam_id().ConvertToUint64());
|
||||
msg.set_dest_id(steamIDRemote.ConvertToUint64());
|
||||
msg.set_allocated_network(new Network);
|
||||
msg.set_allocated_network(new Network_pb);
|
||||
|
||||
if (!connection_exists(steamIDRemote)) {
|
||||
msg.mutable_network()->set_type(Network::NEW_CONNECTION);
|
||||
msg.mutable_network()->set_type(Network_pb::NEW_CONNECTION);
|
||||
network->sendTo(&msg, true);
|
||||
}
|
||||
|
||||
msg.mutable_network()->set_channel(nChannel);
|
||||
msg.mutable_network()->set_data(pubData, cubData);
|
||||
msg.mutable_network()->set_type(Network::DATA);
|
||||
msg.mutable_network()->set_type(Network_pb::DATA);
|
||||
|
||||
struct Steam_Networking_Connection *conn = get_or_create_connection(steamIDRemote);
|
||||
new_connection_times.erase(steamIDRemote);
|
||||
|
@ -277,7 +306,7 @@ bool SendP2PPacket( CSteamID steamIDRemote, const void *pubData, uint32 cubData,
|
|||
bool IsP2PPacketAvailable( uint32 *pcubMsgSize, int nChannel)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::IsP2PPacketAvailable channel: %i\n", nChannel);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
|
||||
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
|
||||
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
|
||||
//this->network->Run();
|
||||
|
@ -311,7 +340,7 @@ bool IsP2PPacketAvailable( uint32 *pcubMsgSize)
|
|||
bool ReadP2PPacket( void *pubDest, uint32 cubDest, uint32 *pcubMsgSize, CSteamID *psteamIDRemote, int nChannel)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::ReadP2PPacket %u %i\n", cubDest, nChannel);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
|
||||
//Not sure if this should be here because it slightly screws up games that don't like such low "pings"
|
||||
//Commenting it out for now because it looks like it causes a bug where 20xx gets stuck in an infinite receive packet loop
|
||||
//this->network->Run();
|
||||
|
@ -437,9 +466,8 @@ bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionS
|
|||
pConnectionState->m_bUsingRelay = false;
|
||||
pConnectionState->m_nBytesQueuedForSend = 0;
|
||||
pConnectionState->m_nPacketsQueuedForSend = 0;
|
||||
//TODO ip?
|
||||
pConnectionState->m_nRemoteIP = 0;
|
||||
pConnectionState->m_nRemotePort = 0;
|
||||
pConnectionState->m_nRemoteIP = network->getIP(steamIDRemote);
|
||||
pConnectionState->m_nRemotePort = 12345;
|
||||
}
|
||||
|
||||
PRINT_DEBUG("Connection\n");
|
||||
|
@ -454,7 +482,7 @@ bool GetP2PSessionState( CSteamID steamIDRemote, P2PSessionState_t *pConnectionS
|
|||
// P2P packet relay is allowed by default
|
||||
bool AllowP2PPacketRelay( bool bAllow )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::AllowP2PPacketRelay\n");
|
||||
PRINT_DEBUG("Steam_Networking::AllowP2PPacketRelay %u\n", bAllow);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -480,7 +508,7 @@ SNetListenSocket_t socket_number = 0;
|
|||
// pass in 0 if you don't want users to be able to connect via IP/Port, but expect to be always peer-to-peer connections only
|
||||
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::CreateListenSocket %i %u %hu %u\n", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
|
||||
PRINT_DEBUG("Steam_Networking::CreateListenSocket old %i %u %hu %u\n", nVirtualP2PPort, nIP, nPort, bAllowUseOfPacketRelay);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
for (auto & c : listen_sockets) {
|
||||
if (c.nVirtualP2PPort == nVirtualP2PPort || c.nPort == nPort)
|
||||
|
@ -499,6 +527,13 @@ SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 n
|
|||
return socket.id;
|
||||
}
|
||||
|
||||
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, SteamIPAddress_t nIP, uint16 nPort, bool bAllowUseOfPacketRelay )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::CreateListenSocket %i %i %u %hu %u\n", nVirtualP2PPort, nIP.m_eType, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
|
||||
//TODO: ipv6
|
||||
return CreateListenSocket(nVirtualP2PPort, nIP.m_unIPv4, nPort, bAllowUseOfPacketRelay);
|
||||
}
|
||||
|
||||
SNetListenSocket_t CreateListenSocket( int nVirtualP2PPort, uint32 nIP, uint16 nPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::CreateListenSocket old\n");
|
||||
|
@ -525,12 +560,18 @@ SNetSocket_t CreateP2PConnectionSocket( CSteamID steamIDTarget, int nVirtualPort
|
|||
|
||||
SNetSocket_t CreateConnectionSocket( uint32 nIP, uint16 nPort, int nTimeoutSec )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket %u %hu %i\n", nIP, nPort, nTimeoutSec);
|
||||
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket_old %u %hu %i\n", nIP, nPort, nTimeoutSec);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
//TODO: nTimeoutSec
|
||||
return create_connection_socket((uint64)0, 0, nIP, nPort);
|
||||
}
|
||||
|
||||
SNetSocket_t CreateConnectionSocket( SteamIPAddress_t nIP, uint16 nPort, int nTimeoutSec )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::CreateConnectionSocket %i %u %hu %i\n", nIP.m_eType, nIP.m_unIPv4, nPort, nTimeoutSec);
|
||||
//TODO: ipv6
|
||||
return CreateConnectionSocket(nIP.m_unIPv4, nPort, nTimeoutSec);
|
||||
}
|
||||
|
||||
// disconnects the connection to the socket, if any, and invalidates the handle
|
||||
// any unread data on the socket will be thrown away
|
||||
|
@ -692,7 +733,7 @@ bool RetrieveData( SNetListenSocket_t hListenSocket, void *pubDest, uint32 cubDe
|
|||
// returns information about the specified socket, filling out the contents of the pointers
|
||||
bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, uint32 *punIPRemote, uint16 *punPortRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::GetSocketInfo\n");
|
||||
PRINT_DEBUG("Steam_Networking::GetSocketInfo_old\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
struct steam_connection_socket *socket = get_connection_socket(hSocket);
|
||||
if (!socket) return false;
|
||||
|
@ -717,12 +758,28 @@ bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocke
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GetSocketInfo( SNetSocket_t hSocket, CSteamID *pSteamIDRemote, int *peSocketStatus, SteamIPAddress_t *punIPRemote, uint16 *punPortRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::GetSocketInfo\n");
|
||||
//TODO: ipv6
|
||||
uint32 *ip_remote = NULL;
|
||||
if (punIPRemote) {
|
||||
ip_remote = &(punIPRemote->m_unIPv4);
|
||||
}
|
||||
|
||||
bool ret = GetSocketInfo(hSocket, pSteamIDRemote, peSocketStatus, ip_remote, punPortRemote );
|
||||
if (punIPRemote && ret) {
|
||||
punIPRemote->m_eType = k_ESteamIPTypeIPv4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns which local port the listen socket is bound to
|
||||
// *pnIP and *pnPort will be 0 if the socket is set to listen for P2P connections only
|
||||
bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16 *pnPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo\n");
|
||||
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo_old\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto conn = std::find_if(listen_sockets.begin(), listen_sockets.end(), [&hListenSocket](struct steam_listen_socket const& conn) { return conn.id == hListenSocket;});
|
||||
if (conn == listen_sockets.end()) return false;
|
||||
|
@ -731,6 +788,22 @@ bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, uint32 *pnIP, uint16
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GetListenSocketInfo( SNetListenSocket_t hListenSocket, SteamIPAddress_t *pnIP, uint16 *pnPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking::GetListenSocketInfo\n");
|
||||
//TODO: ipv6
|
||||
uint32 *ip = NULL;
|
||||
if (pnIP) {
|
||||
ip = &(pnIP->m_unIPv4);
|
||||
}
|
||||
|
||||
bool ret = GetListenSocketInfo(hListenSocket, ip, pnPort );
|
||||
if (pnIP && ret) {
|
||||
pnIP->m_eType = k_ESteamIPTypeIPv4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// returns true to describe how the socket ended up connecting
|
||||
ESNetSocketConnectionType GetSocketConnectionType( SNetSocket_t hSocket )
|
||||
|
@ -754,24 +827,27 @@ void RunCallbacks()
|
|||
{
|
||||
uint64 current_time = std::chrono::duration_cast<std::chrono::duration<uint64>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
for (auto &msg : messages) {
|
||||
CSteamID source_id((uint64)msg.source_id());
|
||||
if (!msg.network().processed()) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
|
||||
|
||||
{
|
||||
auto msg = std::begin(unprocessed_messages);
|
||||
while (msg != std::end(unprocessed_messages)) {
|
||||
CSteamID source_id((uint64)msg->source_id());
|
||||
if (!connection_exists(source_id)) {
|
||||
if (new_connection_times.find(source_id) == new_connection_times.end()) {
|
||||
P2PSessionRequest_t data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.m_steamIDRemote = CSteamID(source_id);
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.1);
|
||||
new_connections_to_call_cb.push(source_id);
|
||||
new_connection_times[source_id] = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
} else {
|
||||
struct Steam_Networking_Connection *conn = get_or_create_connection(source_id);
|
||||
conn->open_channels.insert(msg.network().channel());
|
||||
conn->open_channels.insert(msg->network().channel());
|
||||
}
|
||||
|
||||
msg.mutable_network()->set_processed(true);
|
||||
msg.mutable_network()->set_time_processed(current_time);
|
||||
msg->mutable_network()->set_processed(true);
|
||||
msg->mutable_network()->set_time_processed(current_time);
|
||||
messages.push_back(*msg);
|
||||
msg = unprocessed_messages.erase(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,6 +869,27 @@ void RunCallbacks()
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (!new_connections_to_call_cb.empty()) {
|
||||
CSteamID source_id = new_connections_to_call_cb.front();
|
||||
auto t = new_connection_times.find(source_id);
|
||||
if (t == new_connection_times.end()) {
|
||||
new_connections_to_call_cb.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!check_timedout(t->second, NEW_CONNECTION_DELAY)) {
|
||||
break;
|
||||
}
|
||||
|
||||
P2PSessionRequest_t data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.m_steamIDRemote = source_id;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
new_connections_to_call_cb.pop();
|
||||
}
|
||||
|
||||
//TODO: not sure if sockets should be wiped right away
|
||||
remove_killed_connection_sockets();
|
||||
|
||||
|
@ -816,11 +913,12 @@ void Callback(Common_Message *msg)
|
|||
}PRINT_DEBUG("\n");
|
||||
#endif
|
||||
|
||||
if (msg->network().type() == Network::DATA) {
|
||||
messages.push_back(Common_Message(*msg));
|
||||
if (msg->network().type() == Network_pb::DATA) {
|
||||
unprocessed_messages.push_back(Common_Message(*msg));
|
||||
}
|
||||
|
||||
if (msg->network().type() == Network::NEW_CONNECTION) {
|
||||
if (msg->network().type() == Network_pb::NEW_CONNECTION) {
|
||||
std::lock_guard<std::recursive_mutex> lock(messages_mutex);
|
||||
auto msg_temp = std::begin(messages);
|
||||
while (msg_temp != std::end(messages)) {
|
||||
//only delete processed to handle unreliable message arriving at the same time.
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_Networking_Messages :
|
||||
public ISteamNetworkingMessages
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_networking_messages_callback\n");
|
||||
|
||||
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
|
||||
steam_networking_messages->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_networking_messages_run_every_runcb\n");
|
||||
|
||||
Steam_Networking_Messages *steam_networking_messages = (Steam_Networking_Messages *)object;
|
||||
steam_networking_messages->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_Networking_Messages(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_Networking_Messages::steam_callback, this);
|
||||
this->run_every_runcb->add(&Steam_Networking_Messages::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_Networking_Messages()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
this->run_every_runcb->remove(&Steam_Networking_Messages::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
/// Sends a message to the specified host. If we don't already have a session with that user,
|
||||
/// a session is implicitly created. There might be some handshaking that needs to happen
|
||||
/// before we can actually begin sending message data. If this handshaking fails and we can't
|
||||
/// get through, an error will be posted via the callback SteamNetworkingMessagesSessionFailed_t.
|
||||
/// There is no notification when the operation succeeds. (You should have the peer send a reply
|
||||
/// for this purpose.)
|
||||
///
|
||||
/// Sending a message to a host will also implicitly accept any incoming connection from that host.
|
||||
///
|
||||
/// nSendFlags is a bitmask of k_nSteamNetworkingSend_xxx options
|
||||
///
|
||||
/// nRemoteChannel is a routing number you can use to help route message to different systems.
|
||||
/// You'll have to call ReceiveMessagesOnChannel() with the same channel number in order to retrieve
|
||||
/// the data on the other end.
|
||||
///
|
||||
/// Using different channels to talk to the same user will still use the same underlying
|
||||
/// connection, saving on resources. If you don't need this feature, use 0.
|
||||
/// Otherwise, small integers are the most efficient.
|
||||
///
|
||||
/// It is guaranteed that reliable messages to the same host on the same channel
|
||||
/// will be be received by the remote host (if they are received at all) exactly once,
|
||||
/// and in the same order that they were send.
|
||||
///
|
||||
/// NO other order guarantees exist! In particular, unreliable messages may be dropped,
|
||||
/// received out of order with respect to each other and with respect to reliable data,
|
||||
/// or may be received multiple times. Messages on different channels are *not* guaranteed
|
||||
/// to be received in the order they were sent.
|
||||
///
|
||||
/// A note for those familiar with TCP/IP ports, or converting an existing codebase that
|
||||
/// opened multiple sockets: You might notice that there is only one channel, and with
|
||||
/// TCP/IP each endpoint has a port number. You can think of the channel number as the
|
||||
/// *destination* port. If you need each message to also include a "source port" (so the
|
||||
/// recipient can route the reply), then just put that in your message. That is essentially
|
||||
/// how UDP works!
|
||||
///
|
||||
/// Returns:
|
||||
/// - k_EREsultOK on success.
|
||||
/// - k_EResultNoConnection will be returned if the session has failed or was closed by the peer,
|
||||
/// and k_nSteamNetworkingSend_AutoRestartBrokwnSession is not used. (You can use
|
||||
/// GetSessionConnectionInfo to get the details.) In order to acknowledge the broken session
|
||||
/// and start a new one, you must call CloseSessionWithUser
|
||||
/// - See SendMessageToConnection::SendMessageToConnection for more
|
||||
EResult SendMessageToUser( const SteamNetworkingIdentity &identityRemote, const void *pubData, uint32 cubData, int nSendFlags, int nRemoteChannel )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::SendMessageToUser\n");
|
||||
return k_EResultNoConnection;
|
||||
}
|
||||
|
||||
/// Reads the next message that has been sent from another user via SendMessageToUser() on the given channel.
|
||||
/// Returns number of messages returned into your list. (0 if no message are available on that channel.)
|
||||
///
|
||||
/// When you're done with the message object(s), make sure and call Release()!
|
||||
int ReceiveMessagesOnChannel( int nLocalChannel, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::ReceiveMessagesOnChannel\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// AcceptSessionWithUser() should only be called in response to a SteamP2PSessionRequest_t callback
|
||||
/// SteamP2PSessionRequest_t will be posted if another user tries to send you a message, and you haven't
|
||||
/// tried to talk to them. If you don't want to talk to them, just ignore the request.
|
||||
/// If the user continues to send you messages, SteamP2PSessionRequest_t callbacks will continue to
|
||||
/// be posted periodically. This may be called multiple times for a single user.
|
||||
///
|
||||
/// Calling SendMessage() on the other user, this implicitly accepts any pending session request.
|
||||
bool AcceptSessionWithUser( const SteamNetworkingIdentity &identityRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::AcceptSessionWithUser\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Call this when you're done talking to a user to immediately free up resources under-the-hood.
|
||||
/// If the remote user tries to send data to you again, another P2PSessionRequest_t callback will
|
||||
/// be posted.
|
||||
///
|
||||
/// Note that sessions that go unused for a few minutes are automatically timed out.
|
||||
bool CloseSessionWithUser( const SteamNetworkingIdentity &identityRemote )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::CloseSessionWithUser\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Call this when you're done talking to a user on a specific channel. Once all
|
||||
/// open channels to a user have been closed, the open session to the user will be
|
||||
/// closed, and any new data from this user will trigger a SteamP2PSessionRequest_t
|
||||
/// callback
|
||||
bool CloseChannelWithUser( const SteamNetworkingIdentity &identityRemote, int nLocalChannel )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::CloseChannelWithUser\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns information about the latest state of a connection, if any, with the given peer.
|
||||
/// Primarily intended for debugging purposes, but can also be used to get more detailed
|
||||
/// failure information. (See SendMessageToUser and k_nSteamNetworkingSend_AutoRestartBrokwnSession.)
|
||||
///
|
||||
/// Returns the value of SteamNetConnectionInfo_t::m_eState, or k_ESteamNetworkingConnectionState_None
|
||||
/// if no connection exists with specified peer. You may pass nullptr for either parameter if
|
||||
/// you do not need the corresponding details. Note that sessions time out after a while,
|
||||
/// so if a connection fails, or SendMessageToUser returns SendMessageToUser, you cannot wait
|
||||
/// indefinitely to obtain the reason for failure.
|
||||
ESteamNetworkingConnectionState GetSessionConnectionInfo( const SteamNetworkingIdentity &identityRemote, SteamNetConnectionInfo_t *pConnectionInfo, SteamNetworkingQuickConnectionStatus *pQuickStatus )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Messages::GetSessionConnectionInfo\n");
|
||||
return k_ESteamNetworkingConnectionState_None;
|
||||
}
|
||||
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
|
@ -44,11 +44,17 @@ struct Connect_Socket {
|
|||
int64 user_data;
|
||||
|
||||
std::queue<std::string> data;
|
||||
HSteamNetPollGroup poll_group;
|
||||
|
||||
unsigned long long packet_receive_counter;
|
||||
};
|
||||
|
||||
class Steam_Networking_Sockets :
|
||||
public ISteamNetworkingSockets001,
|
||||
public ISteamNetworkingSockets002,
|
||||
public ISteamNetworkingSockets003,
|
||||
public ISteamNetworkingSockets006,
|
||||
public ISteamNetworkingSockets008,
|
||||
public ISteamNetworkingSockets
|
||||
{
|
||||
class Settings *settings;
|
||||
|
@ -59,6 +65,9 @@ public ISteamNetworkingSockets
|
|||
|
||||
std::vector<struct Listen_Socket> listen_sockets;
|
||||
std::map<HSteamNetConnection, struct Connect_Socket> connect_sockets;
|
||||
std::map<HSteamNetPollGroup, std::list<HSteamNetConnection>> poll_groups;
|
||||
std::chrono::steady_clock::time_point created;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
|
@ -87,6 +96,8 @@ Steam_Networking_Sockets(class Settings *settings, class Networking *network, cl
|
|||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
|
||||
this->created = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
~Steam_Networking_Sockets()
|
||||
|
@ -151,6 +162,7 @@ HSteamNetConnection new_connect_socket(SteamNetworkingIdentity remote_identity,
|
|||
socket.remote_id = remote_id;
|
||||
socket.status = status;
|
||||
socket.user_data = -1;
|
||||
socket.poll_group = k_HSteamNetPollGroup_Invalid;
|
||||
|
||||
static HSteamNetConnection socket_id;
|
||||
++socket_id;
|
||||
|
@ -238,8 +250,15 @@ HSteamListenSocket CreateListenSocket( int nSteamConnectVirtualPort, uint32 nIP,
|
|||
/// When a client attempts to connect, a SteamNetConnectionStatusChangedCallback_t
|
||||
/// will be posted. The connection will be in the connecting state.
|
||||
HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketIP old\n");
|
||||
return k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
|
||||
HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddress, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketIP\n");
|
||||
return k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
|
||||
/// Creates a connection and begins talking to a "server" over UDP at the
|
||||
|
@ -261,8 +280,15 @@ HSteamListenSocket CreateListenSocketIP( const SteamNetworkingIPAddr &localAddre
|
|||
/// way of knowing who is actually on the other end, and thus are vulnerable to
|
||||
/// man-in-the-middle attacks.
|
||||
HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectByIPAddress old\n");
|
||||
return k_HSteamNetConnection_Invalid;
|
||||
}
|
||||
|
||||
HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectByIPAddress\n");
|
||||
return k_HSteamNetConnection_Invalid;
|
||||
}
|
||||
|
||||
/// Like CreateListenSocketIP, but clients will connect using ConnectP2P
|
||||
|
@ -276,8 +302,16 @@ HSteamNetConnection ConnectByIPAddress( const SteamNetworkingIPAddr &address )
|
|||
/// If you use this, you probably want to call ISteamNetworkingUtils::InitializeRelayNetworkAccess()
|
||||
/// when your app initializes
|
||||
HSteamListenSocket CreateListenSocketP2P( int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketP2P old %i\n", nVirtualPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return new_listen_socket(nVirtualPort);
|
||||
}
|
||||
|
||||
HSteamListenSocket CreateListenSocketP2P( int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateListenSocketP2P %i\n", nVirtualPort);
|
||||
//TODO config options
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return new_listen_socket(nVirtualPort);
|
||||
}
|
||||
|
@ -295,7 +329,7 @@ HSteamListenSocket CreateListenSocketP2P( int nVirtualPort )
|
|||
/// when your app initializes
|
||||
HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2P %u\n", nVirtualPort);
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2P old %i\n", nVirtualPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
const SteamNetworkingIPAddr *ip = identityRemote.GetIPAddr();
|
||||
|
@ -315,6 +349,13 @@ HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, i
|
|||
return socket;
|
||||
}
|
||||
|
||||
HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2P %i\n", nVirtualPort);
|
||||
//TODO config options
|
||||
return ConnectP2P(identityRemote, nVirtualPort);
|
||||
}
|
||||
|
||||
/// Creates a connection and begins talking to a remote destination. The remote host
|
||||
/// must be listening with a matching call to CreateListenSocket.
|
||||
///
|
||||
|
@ -327,12 +368,14 @@ HSteamNetConnection ConnectP2P( const SteamNetworkingIdentity &identityRemote, i
|
|||
HSteamNetConnection ConnectBySteamID( CSteamID steamIDTarget, int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectBySteamID\n");
|
||||
return k_HSteamNetConnection_Invalid;
|
||||
}
|
||||
|
||||
//#endif
|
||||
HSteamNetConnection ConnectByIPv4Address( uint32 nIP, uint16 nPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectByIPv4Address\n");
|
||||
return k_HSteamNetConnection_Invalid;
|
||||
}
|
||||
|
||||
|
||||
|
@ -379,6 +422,7 @@ EResult AcceptConnection( HSteamNetConnection hConn )
|
|||
if (connect_socket->second.status != CONNECT_SOCKET_NOT_ACCEPTED) return k_EResultInvalidState;
|
||||
connect_socket->second.status = CONNECT_SOCKET_CONNECTED;
|
||||
send_packet_new_connection(connect_socket->first);
|
||||
launch_callback(connect_socket->first, CONNECT_SOCKET_NOT_ACCEPTED);
|
||||
|
||||
return k_EResultOK;
|
||||
}
|
||||
|
@ -443,6 +487,7 @@ bool CloseConnection( HSteamNetConnection hPeer, int nReason, const char *pszDeb
|
|||
bool CloseListenSocket( HSteamListenSocket hSocket, const char *pszNotifyRemoteReason )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CloseListenSocket old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Destroy a listen socket. All the connections that were accepting on the listen
|
||||
|
@ -510,6 +555,7 @@ void SetConnectionName( HSteamNetConnection hPeer, const char *pszName )
|
|||
bool GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionName\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -540,6 +586,7 @@ bool GetConnectionName( HSteamNetConnection hPeer, char *pszName, int nMaxLen )
|
|||
EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, ESteamNetworkingSendType eSendType )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SendMessageToConnection old\n");
|
||||
return k_EResultFail;
|
||||
}
|
||||
|
||||
/// Send a message to the remote host on the specified connection.
|
||||
|
@ -567,6 +614,9 @@ EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, u
|
|||
/// sockets that does not write excessively small chunks will
|
||||
/// work without any changes.
|
||||
///
|
||||
/// The pOutMessageNumber is an optional pointer to receive the
|
||||
/// message number assigned to the message, if sending was successful.
|
||||
///
|
||||
/// Returns:
|
||||
/// - k_EResultInvalidParam: invalid connection handle, or the individual message is too big.
|
||||
/// (See k_cbMaxSteamNetworkingSocketsMessageSizeSend)
|
||||
|
@ -576,7 +626,7 @@ EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, u
|
|||
/// we were not ready to send it.
|
||||
/// - k_EResultLimitExceeded: there was already too much data queued to be sent.
|
||||
/// (See k_ESteamNetworkingConfig_SendBufferSize)
|
||||
virtual EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags )
|
||||
EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags, int64 *pOutMessageNumber )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SendMessageToConnection %u, len %u, flags %i\n", hConn, cbData, nSendFlags);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
@ -603,6 +653,50 @@ virtual EResult SendMessageToConnection( HSteamNetConnection hConn, const void *
|
|||
return k_EResultFail;
|
||||
}
|
||||
|
||||
EResult SendMessageToConnection( HSteamNetConnection hConn, const void *pData, uint32 cbData, int nSendFlags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SendMessageToConnection old %u, len %u, flags %i\n", hConn, cbData, nSendFlags);
|
||||
return SendMessageToConnection(hConn, pData, cbData, nSendFlags, NULL);
|
||||
}
|
||||
|
||||
/// Send one or more messages without copying the message payload.
|
||||
/// This is the most efficient way to send messages. To use this
|
||||
/// function, you must first allocate a message object using
|
||||
/// ISteamNetworkingUtils::AllocateMessage. (Do not declare one
|
||||
/// on the stack or allocate your own.)
|
||||
///
|
||||
/// You should fill in the message payload. You can either let
|
||||
/// it allocate the buffer for you and then fill in the payload,
|
||||
/// or if you already have a buffer allocated, you can just point
|
||||
/// m_pData at your buffer and set the callback to the appropriate function
|
||||
/// to free it. Note that if you use your own buffer, it MUST remain valid
|
||||
/// until the callback is executed. And also note that your callback can be
|
||||
/// invoked at ant time from any thread (perhaps even before SendMessages
|
||||
/// returns!), so it MUST be fast and threadsafe.
|
||||
///
|
||||
/// You MUST also fill in:
|
||||
/// - m_conn - the handle of the connection to send the message to
|
||||
/// - m_nFlags - bitmask of k_nSteamNetworkingSend_xxx flags.
|
||||
///
|
||||
/// All other fields are currently reserved and should not be modified.
|
||||
///
|
||||
/// The library will take ownership of the message structures. They may
|
||||
/// be modified or become invalid at any time, so you must not read them
|
||||
/// after passing them to this function.
|
||||
///
|
||||
/// pOutMessageNumberOrResult is an optional array that will receive,
|
||||
/// for each message, the message number that was assigned to the message
|
||||
/// if sending was successful. If sending failed, then a negative EResult
|
||||
/// valid is placed into the array. For example, the array will hold
|
||||
/// -k_EResultInvalidState if the connection was in an invalid state.
|
||||
/// See ISteamNetworkingSockets::SendMessageToConnection for possible
|
||||
/// failure codes.
|
||||
void SendMessages( int nMessages, SteamNetworkingMessage_t *const *pMessages, int64 *pOutMessageNumberOrResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SendMessages\n");
|
||||
}
|
||||
|
||||
|
||||
/// If Nagle is enabled (its on by default) then when calling
|
||||
/// SendMessageToConnection the message will be queued up the Nagle time
|
||||
/// before being sent to merge small messages into the same packet.
|
||||
|
@ -612,6 +706,7 @@ virtual EResult SendMessageToConnection( HSteamNetConnection hConn, const void *
|
|||
EResult FlushMessagesOnConnection( HSteamNetConnection hConn )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::FlushMessagesOnConnection\n");
|
||||
return k_EResultOK;
|
||||
}
|
||||
|
||||
static void free_steam_message_data(SteamNetworkingMessage_t *pMsg)
|
||||
|
@ -637,15 +732,17 @@ SteamNetworkingMessage_t *get_steam_message_connection(HSteamNetConnection hConn
|
|||
pMsg->m_cbSize = size;
|
||||
memcpy(pMsg->m_pData, connect_socket->second.data.front().data(), size);
|
||||
pMsg->m_conn = hConn;
|
||||
pMsg->m_sender = connect_socket->second.remote_identity;
|
||||
pMsg->m_identityPeer = connect_socket->second.remote_identity;
|
||||
pMsg->m_nConnUserData = connect_socket->second.user_data;
|
||||
//TODO
|
||||
//pMsg->m_usecTimeReceived =
|
||||
//pMsg->m_nMessageNumber =
|
||||
pMsg->m_usecTimeReceived = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - created).count();
|
||||
pMsg->m_nMessageNumber = connect_socket->second.packet_receive_counter;
|
||||
++connect_socket->second.packet_receive_counter;
|
||||
|
||||
pMsg->m_pfnFreeData = &free_steam_message_data;
|
||||
pMsg->m_pfnRelease = &delete_steam_message;
|
||||
pMsg->m_nChannel = 0;
|
||||
connect_socket->second.data.pop();
|
||||
PRINT_DEBUG("get_steam_message_connection %u %u\n", hConn, size);
|
||||
return pMsg;
|
||||
}
|
||||
|
||||
|
@ -716,6 +813,7 @@ int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMe
|
|||
bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pInfo )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionInfo\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -738,6 +836,7 @@ bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo_t *pIn
|
|||
int ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessage001_t **ppOutMessages, int nMaxMessages )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnConnection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -752,6 +851,7 @@ int ReceiveMessagesOnConnection( HSteamNetConnection hConn, SteamNetworkingMessa
|
|||
int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMessage001_t **ppOutMessages, int nMaxMessages )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnListenSocket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -759,6 +859,7 @@ int ReceiveMessagesOnListenSocket( HSteamListenSocket hSocket, SteamNetworkingMe
|
|||
bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo001_t *pInfo )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionInfo\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -767,6 +868,7 @@ bool GetConnectionInfo( HSteamNetConnection hConn, SteamNetConnectionInfo001_t *
|
|||
bool GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickConnectionStatus *pStats )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetQuickConnectionStatus\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -780,6 +882,7 @@ bool GetQuickConnectionStatus( HSteamNetConnection hConn, SteamNetworkingQuickCo
|
|||
int GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cbBuf )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetDetailedConnectionStatus\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Returns local IP and port that a listen socket created using CreateListenSocketIP is bound to.
|
||||
|
@ -789,6 +892,7 @@ int GetDetailedConnectionStatus( HSteamNetConnection hConn, char *pszBuf, int cb
|
|||
bool GetListenSocketAddress( HSteamListenSocket hSocket, SteamNetworkingIPAddr *address )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetListenSocketAddress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns information about the listen socket.
|
||||
|
@ -830,6 +934,7 @@ bool GetListenSocketInfo( HSteamListenSocket hSocket, uint32 *pnIP, uint16 *pnPo
|
|||
bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateSocketPair old\n");
|
||||
return CreateSocketPair(pOutConnection1, pOutConnection2, bUseNetworkLoopback, NULL, NULL);
|
||||
}
|
||||
|
||||
/// Create a pair of connections that are talking to each other, e.g. a loopback connection.
|
||||
|
@ -854,7 +959,18 @@ bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection
|
|||
/// actual bound loopback port. Otherwise, the port will be zero.
|
||||
bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection *pOutConnection2, bool bUseNetworkLoopback, const SteamNetworkingIdentity *pIdentity1, const SteamNetworkingIdentity *pIdentity2 )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateSocketPair\n");
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateSocketPair %u %p %p\n", bUseNetworkLoopback, pIdentity1, pIdentity2);
|
||||
if (!pOutConnection1 || !pOutConnection1) return false;
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
|
||||
SteamNetworkingIdentity remote_identity;
|
||||
remote_identity.SetSteamID(settings->get_local_steam_id());
|
||||
HSteamNetConnection con1 = new_connect_socket(remote_identity, 0, CONNECT_SOCKET_CONNECTED, k_HSteamListenSocket_Invalid, k_HSteamNetConnection_Invalid);
|
||||
HSteamNetConnection con2 = new_connect_socket(remote_identity, 0, CONNECT_SOCKET_CONNECTED, k_HSteamListenSocket_Invalid, con1);
|
||||
connect_sockets[con1].remote_id = con2;
|
||||
*pOutConnection1 = con1;
|
||||
*pOutConnection2 = con2;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Get the identity assigned to this interface.
|
||||
|
@ -865,6 +981,9 @@ bool CreateSocketPair( HSteamNetConnection *pOutConnection1, HSteamNetConnection
|
|||
bool GetIdentity( SteamNetworkingIdentity *pIdentity )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetIdentity\n");
|
||||
if (!pIdentity) return false;
|
||||
pIdentity->SetSteamID(settings->get_local_steam_id());
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Indicate our desire to be ready participate in authenticated communications.
|
||||
|
@ -893,6 +1012,7 @@ bool GetIdentity( SteamNetworkingIdentity *pIdentity )
|
|||
ESteamNetworkingAvailability InitAuthentication()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::InitAuthentication\n");
|
||||
return k_ESteamNetworkingAvailability_Current;
|
||||
}
|
||||
|
||||
/// Query our readiness to participate in authenticated communications. A
|
||||
|
@ -905,8 +1025,132 @@ ESteamNetworkingAvailability InitAuthentication()
|
|||
ESteamNetworkingAvailability GetAuthenticationStatus( SteamNetAuthenticationStatus_t *pDetails )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetAuthenticationStatus\n");
|
||||
return k_ESteamNetworkingAvailability_Current;
|
||||
}
|
||||
|
||||
/// Create a new poll group.
|
||||
///
|
||||
/// You should destroy the poll group when you are done using DestroyPollGroup
|
||||
HSteamNetPollGroup CreatePollGroup()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreatePollGroup\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
static HSteamNetPollGroup poll_group_counter;
|
||||
++poll_group_counter;
|
||||
|
||||
HSteamNetPollGroup poll_group_number = poll_group_counter;
|
||||
poll_groups[poll_group_number] = std::list<HSteamNetConnection>();
|
||||
return poll_group_number;
|
||||
}
|
||||
|
||||
/// Destroy a poll group created with CreatePollGroup().
|
||||
///
|
||||
/// If there are any connections in the poll group, they are removed from the group,
|
||||
/// and left in a state where they are not part of any poll group.
|
||||
/// Returns false if passed an invalid poll group handle.
|
||||
bool DestroyPollGroup( HSteamNetPollGroup hPollGroup )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::DestroyPollGroup\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto group = poll_groups.find(hPollGroup);
|
||||
if (group == poll_groups.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto c : group->second) {
|
||||
auto connect_socket = connect_sockets.find(c);
|
||||
if (connect_socket != connect_sockets.end()) {
|
||||
connect_socket->second.poll_group = k_HSteamNetPollGroup_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
poll_groups.erase(group);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Assign a connection to a poll group. Note that a connection may only belong to a
|
||||
/// single poll group. Adding a connection to a poll group implicitly removes it from
|
||||
/// any other poll group it is in.
|
||||
///
|
||||
/// You can pass k_HSteamNetPollGroup_Invalid to remove a connection from its current
|
||||
/// poll group without adding it to a new poll group.
|
||||
///
|
||||
/// If there are received messages currently pending on the connection, an attempt
|
||||
/// is made to add them to the queue of messages for the poll group in approximately
|
||||
/// the order that would have applied if the connection was already part of the poll
|
||||
/// group at the time that the messages were received.
|
||||
///
|
||||
/// Returns false if the connection handle is invalid, or if the poll group handle
|
||||
/// is invalid (and not k_HSteamNetPollGroup_Invalid).
|
||||
bool SetConnectionPollGroup( HSteamNetConnection hConn, HSteamNetPollGroup hPollGroup )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SetConnectionPollGroup %u %u\n", hConn, hPollGroup);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto connect_socket = connect_sockets.find(hConn);
|
||||
if (connect_socket == connect_sockets.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto group = poll_groups.find(hPollGroup);
|
||||
if (group == poll_groups.end() && hPollGroup != k_HSteamNetPollGroup_Invalid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HSteamNetPollGroup old_poll_group = connect_socket->second.poll_group;
|
||||
if (old_poll_group != k_HSteamNetPollGroup_Invalid) {
|
||||
auto group = poll_groups.find(hPollGroup);
|
||||
if (group != poll_groups.end()) {
|
||||
group->second.remove(hConn);
|
||||
}
|
||||
}
|
||||
|
||||
connect_socket->second.poll_group = hPollGroup;
|
||||
if (hPollGroup == k_HSteamNetPollGroup_Invalid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
group->second.push_back(hConn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Same as ReceiveMessagesOnConnection, but will return the next messages available
|
||||
/// on any connection in the poll group. Examine SteamNetworkingMessage_t::m_conn
|
||||
/// to know which connection. (SteamNetworkingMessage_t::m_nConnUserData might also
|
||||
/// be useful.)
|
||||
///
|
||||
/// Delivery order of messages among different connections will usually match the
|
||||
/// order that the last packet was received which completed the message. But this
|
||||
/// is not a strong guarantee, especially for packets received right as a connection
|
||||
/// is being assigned to poll group.
|
||||
///
|
||||
/// Delivery order of messages on the same connection is well defined and the
|
||||
/// same guarantees are present as mentioned in ReceiveMessagesOnConnection.
|
||||
/// (But the messages are not grouped by connection, so they will not necessarily
|
||||
/// appear consecutively in the list; they may be interleaved with messages for
|
||||
/// other connections.)
|
||||
int ReceiveMessagesOnPollGroup( HSteamNetPollGroup hPollGroup, SteamNetworkingMessage_t **ppOutMessages, int nMaxMessages )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ReceiveMessagesOnPollGroup %u %i\n", hPollGroup, nMaxMessages);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
auto group = poll_groups.find(hPollGroup);
|
||||
if (group == poll_groups.end()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamNetworkingMessage_t *msg = NULL;
|
||||
int messages = 0;
|
||||
|
||||
for (auto c : group->second) {
|
||||
while ((msg = get_steam_message_connection(c)) && messages < nMaxMessages) {
|
||||
ppOutMessages[messages] = msg;
|
||||
++messages;
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
//#ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
|
||||
|
||||
//
|
||||
|
@ -921,6 +1165,7 @@ ESteamNetworkingAvailability GetAuthenticationStatus( SteamNetAuthenticationStat
|
|||
bool ReceivedRelayAuthTicket( const void *pvTicket, int cbTicket, SteamDatagramRelayAuthTicket *pOutParsedTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ReceivedRelayAuthTicket\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -933,6 +1178,7 @@ bool ReceivedRelayAuthTicket( const void *pvTicket, int cbTicket, SteamDatagramR
|
|||
int FindRelayAuthTicketForServer( CSteamID steamID, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::FindRelayAuthTicketForServer old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Search cache for a ticket to talk to the server on the specified virtual port.
|
||||
|
@ -944,6 +1190,7 @@ int FindRelayAuthTicketForServer( CSteamID steamID, int nVirtualPort, SteamDatag
|
|||
int FindRelayAuthTicketForServer( const SteamNetworkingIdentity &identityGameServer, int nVirtualPort, SteamDatagramRelayAuthTicket *pOutParsedTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::FindRelayAuthTicketForServer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Client call to connect to a server hosted in a Valve data center, on the specified virtual
|
||||
|
@ -957,7 +1204,8 @@ int FindRelayAuthTicketForServer( const SteamNetworkingIdentity &identityGameSer
|
|||
/// when your app initializes
|
||||
HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentity &identityTarget, int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer\n");
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer old\n");
|
||||
return k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
|
||||
/// Client call to connect to a server hosted in a Valve data center, on the specified virtual
|
||||
|
@ -968,9 +1216,15 @@ HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentit
|
|||
/// connection to Steam or the central backend, or the app is restarted or crashes, etc.
|
||||
HSteamNetConnection ConnectToHostedDedicatedServer( CSteamID steamIDTarget, int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer old\n");
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer older\n");
|
||||
return k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
|
||||
HSteamNetConnection ConnectToHostedDedicatedServer( const SteamNetworkingIdentity &identityTarget, int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectToHostedDedicatedServer\n");
|
||||
return k_HSteamListenSocket_Invalid;
|
||||
}
|
||||
|
||||
//
|
||||
// Servers hosted in Valve data centers
|
||||
|
@ -990,6 +1244,7 @@ uint16 GetHostedDedicatedServerPort()
|
|||
SteamNetworkingPOPID GetHostedDedicatedServerPOPID()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetHostedDedicatedServerPOPID\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1058,7 +1313,24 @@ virtual EResult GetHostedDedicatedServerAddress( SteamDatagramHostedAddress *pRo
|
|||
/// Note that this call MUST be made through the SteamNetworkingSocketsGameServer() interface
|
||||
HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateHostedDedicatedServerListenSocket %i\n", nVirtualPort);
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateHostedDedicatedServerListenSocket old %i\n", nVirtualPort);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return new_listen_socket(nVirtualPort);
|
||||
}
|
||||
|
||||
/// Create a listen socket on the specified virtual port. The physical UDP port to use
|
||||
/// will be determined by the SDR_LISTEN_PORT environment variable. If a UDP port is not
|
||||
/// configured, this call will fail.
|
||||
///
|
||||
/// Note that this call MUST be made through the SteamGameServerNetworkingSockets() interface
|
||||
///
|
||||
/// If you need to set any initial config options, pass them here. See
|
||||
/// SteamNetworkingConfigValue_t for more about why this is preferable to
|
||||
/// setting the options "immediately" after creation.
|
||||
HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::CreateHostedDedicatedServerListenSocket old %i\n", nVirtualPort);
|
||||
//TODO config options
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
return new_listen_socket(nVirtualPort);
|
||||
}
|
||||
|
@ -1072,6 +1344,7 @@ HSteamListenSocket CreateHostedDedicatedServerListenSocket( int nVirtualPort )
|
|||
bool GetConnectionDebugText( HSteamNetConnection hConn, char *pOut, int nOutCCH )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionDebugText\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1082,6 +1355,7 @@ bool GetConnectionDebugText( HSteamNetConnection hConn, char *pOut, int nOutCCH
|
|||
int32 GetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationValue\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns true if successfully set
|
||||
|
@ -1096,6 +1370,7 @@ bool SetConfigurationValue( ESteamNetworkingConfigurationValue eConfigValue, int
|
|||
const char *GetConfigurationValueName( ESteamNetworkingConfigurationValue eConfigValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationValueName\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1107,11 +1382,13 @@ const char *GetConfigurationValueName( ESteamNetworkingConfigurationValue eConfi
|
|||
int32 GetConfigurationString( ESteamNetworkingConfigurationString eConfigString, char *pDest, int32 destSize )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationString\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool SetConfigurationString( ESteamNetworkingConfigurationString eConfigString, const char *pString )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SetConfigurationString\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1119,6 +1396,7 @@ bool SetConfigurationString( ESteamNetworkingConfigurationString eConfigString,
|
|||
const char *GetConfigurationStringName( ESteamNetworkingConfigurationString eConfigString )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConfigurationStringName\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1129,12 +1407,14 @@ const char *GetConfigurationStringName( ESteamNetworkingConfigurationString eCon
|
|||
int32 GetConnectionConfigurationValue( HSteamNetConnection hConn, ESteamNetworkingConnectionConfigurationValue eConfigValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetConnectionConfigurationValue\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns true if successfully set
|
||||
bool SetConnectionConfigurationValue( HSteamNetConnection hConn, ESteamNetworkingConnectionConfigurationValue eConfigValue, int32 nValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SetConnectionConfigurationValue\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Generate an authentication blob that can be used to securely login with
|
||||
|
@ -1170,6 +1450,120 @@ bool SetConnectionConfigurationValue( HSteamNetConnection hConn, ESteamNetworkin
|
|||
EResult GetGameCoordinatorServerLogin( SteamDatagramGameCoordinatorServerLogin *pLoginInfo, int *pcbSignedBlob, void *pBlob )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetGameCoordinatorServerLogin\n");
|
||||
return k_EResultFail;
|
||||
}
|
||||
|
||||
//
|
||||
// Relayed connections using custom signaling protocol
|
||||
//
|
||||
// This is used if you have your own method of sending out-of-band
|
||||
// signaling / rendezvous messages through a mutually trusted channel.
|
||||
//
|
||||
|
||||
/// Create a P2P "client" connection that does signaling over a custom
|
||||
/// rendezvous/signaling channel.
|
||||
///
|
||||
/// pSignaling points to a new object that you create just for this connection.
|
||||
/// It must stay valid until Release() is called. Once you pass the
|
||||
/// object to this function, it assumes ownership. Release() will be called
|
||||
/// from within the function call if the call fails. Furthermore, until Release()
|
||||
/// is called, you should be prepared for methods to be invoked on your
|
||||
/// object from any thread! You need to make sure your object is threadsafe!
|
||||
/// Furthermore, you should make sure that dispatching the methods is done
|
||||
/// as quickly as possible.
|
||||
///
|
||||
/// This function will immediately construct a connection in the "connecting"
|
||||
/// state. Soon after (perhaps before this function returns, perhaps in another thread),
|
||||
/// the connection will begin sending signaling messages by calling
|
||||
/// ISteamNetworkingConnectionCustomSignaling::SendSignal.
|
||||
///
|
||||
/// When the remote peer accepts the connection (See
|
||||
/// ISteamNetworkingCustomSignalingRecvContext::OnConnectRequest),
|
||||
/// it will begin sending signaling messages. When these messages are received,
|
||||
/// you can pass them to the connection using ReceivedP2PCustomSignal.
|
||||
///
|
||||
/// If you know the identity of the peer that you expect to be on the other end,
|
||||
/// you can pass their identity to improve debug output or just detect bugs.
|
||||
/// If you don't know their identity yet, you can pass NULL, and their
|
||||
/// identity will be established in the connection handshake.
|
||||
///
|
||||
/// If you use this, you probably want to call ISteamNetworkingUtils::InitRelayNetworkAccess()
|
||||
/// when your app initializes
|
||||
///
|
||||
/// If you need to set any initial config options, pass them here. See
|
||||
/// SteamNetworkingConfigValue_t for more about why this is preferable to
|
||||
/// setting the options "immediately" after creation.
|
||||
HSteamNetConnection ConnectP2PCustomSignaling( ISteamNetworkingConnectionCustomSignaling *pSignaling, const SteamNetworkingIdentity *pPeerIdentity, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2PCustomSignaling old\n");
|
||||
return ConnectP2PCustomSignaling(pSignaling, pPeerIdentity, 0, nOptions, pOptions);
|
||||
}
|
||||
|
||||
HSteamNetConnection ConnectP2PCustomSignaling( ISteamNetworkingConnectionCustomSignaling *pSignaling, const SteamNetworkingIdentity *pPeerIdentity, int nRemoteVirtualPort, int nOptions, const SteamNetworkingConfigValue_t *pOptions )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ConnectP2PCustomSignaling\n");
|
||||
return k_HSteamNetConnection_Invalid;
|
||||
}
|
||||
|
||||
|
||||
/// Called when custom signaling has received a message. When your
|
||||
/// signaling channel receives a message, it should save off whatever
|
||||
/// routing information was in the envelope into the context object,
|
||||
/// and then pass the payload to this function.
|
||||
///
|
||||
/// A few different things can happen next, depending on the message:
|
||||
///
|
||||
/// - If the signal is associated with existing connection, it is dealt
|
||||
/// with immediately. If any replies need to be sent, they will be
|
||||
/// dispatched using the ISteamNetworkingConnectionCustomSignaling
|
||||
/// associated with the connection.
|
||||
/// - If the message represents a connection request (and the request
|
||||
/// is not redundant for an existing connection), a new connection
|
||||
/// will be created, and ReceivedConnectRequest will be called on your
|
||||
/// context object to determine how to proceed.
|
||||
/// - Otherwise, the message is for a connection that does not
|
||||
/// exist (anymore). In this case, we *may* call SendRejectionReply
|
||||
/// on your context object.
|
||||
///
|
||||
/// In any case, we will not save off pContext or access it after this
|
||||
/// function returns.
|
||||
///
|
||||
/// Returns true if the message was parsed and dispatched without anything
|
||||
/// unusual or suspicious happening. Returns false if there was some problem
|
||||
/// with the message that prevented ordinary handling. (Debug output will
|
||||
/// usually have more information.)
|
||||
///
|
||||
/// If you expect to be using relayed connections, then you probably want
|
||||
/// to call ISteamNetworkingUtils::InitRelayNetworkAccess() when your app initializes
|
||||
bool ReceivedP2PCustomSignal( const void *pMsg, int cbMsg, ISteamNetworkingCustomSignalingRecvContext *pContext )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::ReceivedP2PCustomSignal\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Certificate provision by the application. On Steam, we normally handle all this automatically
|
||||
// and you will not need to use these advanced functions.
|
||||
//
|
||||
|
||||
/// Get blob that describes a certificate request. You can send this to your game coordinator.
|
||||
/// Upon entry, *pcbBlob should contain the size of the buffer. On successful exit, it will
|
||||
/// return the number of bytes that were populated. You can pass pBlob=NULL to query for the required
|
||||
/// size. (256 bytes is a very conservative estimate.)
|
||||
///
|
||||
/// Pass this blob to your game coordinator and call SteamDatagram_CreateCert.
|
||||
bool GetCertificateRequest( int *pcbBlob, void *pBlob, SteamNetworkingErrMsg &errMsg )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::GetCertificateRequest\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Set the certificate. The certificate blob should be the output of
|
||||
/// SteamDatagram_CreateCert.
|
||||
bool SetCertificate( const void *pCertificate, int cbCertificate, SteamNetworkingErrMsg &errMsg )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets::SetCertificate\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TEMP KLUDGE Call to invoke all queued callbacks.
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
class Steam_Networking_Sockets_Serialized :
|
||||
public ISteamNetworkingSocketsSerialized002,
|
||||
public ISteamNetworkingSocketsSerialized003
|
||||
public ISteamNetworkingSocketsSerialized003,
|
||||
public ISteamNetworkingSocketsSerialized004
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
|
@ -116,6 +117,17 @@ void PostConnectionStateMsg( const void *pMsg, uint32 cbMsg )
|
|||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::PostConnectionStateMsg\n");
|
||||
}
|
||||
|
||||
bool GetSTUNServer(int dont_know, char *buf, unsigned int len)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::GetSTUNServer %i %p %u\n", dont_know, buf, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BAllowDirectConnectToPeer(SteamNetworkingIdentity const &identity)
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Sockets_Serialized::BAllowDirectConnectToPeer\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
class Steam_Networking_Utils :
|
||||
public ISteamNetworkingUtils001,
|
||||
public ISteamNetworkingUtils002,
|
||||
public ISteamNetworkingUtils
|
||||
{
|
||||
class Settings *settings;
|
||||
|
@ -65,6 +66,32 @@ Steam_Networking_Utils(class Settings *settings, class Networking *network, clas
|
|||
this->run_every_runcb->remove(&Steam_Networking_Utils::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
/// Allocate and initialize a message object. Usually the reason
|
||||
/// you call this is to pass it to ISteamNetworkingSockets::SendMessages.
|
||||
/// The returned object will have all of the relevant fields cleared to zero.
|
||||
///
|
||||
/// Optionally you can also request that this system allocate space to
|
||||
/// hold the payload itself. If cbAllocateBuffer is nonzero, the system
|
||||
/// will allocate memory to hold a payload of at least cbAllocateBuffer bytes.
|
||||
/// m_pData will point to the allocated buffer, m_cbSize will be set to the
|
||||
/// size, and m_pfnFreeData will be set to the proper function to free up
|
||||
/// the buffer.
|
||||
///
|
||||
/// If cbAllocateBuffer=0, then no buffer is allocated. m_pData will be NULL,
|
||||
/// m_cbSize will be zero, and m_pfnFreeData will be NULL. You will need to
|
||||
/// set each of these.
|
||||
///
|
||||
/// You can use SteamNetworkingMessage_t::Release to free up the message
|
||||
/// bookkeeping object and any associated buffer. See
|
||||
/// ISteamNetworkingSockets::SendMessages for details on reference
|
||||
/// counting and ownership.
|
||||
SteamNetworkingMessage_t *AllocateMessage( int cbAllocateBuffer )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::AllocateMessage\n");
|
||||
//TODO
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool InitializeRelayAccess()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::InitializeRelayAccess\n");
|
||||
|
@ -161,24 +188,28 @@ bool IsPingMeasurementInProgress()
|
|||
int GetPingToDataCenter( SteamNetworkingPOPID popID, SteamNetworkingPOPID *pViaRelayPoP )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPingToDataCenter\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetDirectPingToPOP( SteamNetworkingPOPID popID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetDirectPingToPOP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetPOPCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPOPCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int GetPOPList( SteamNetworkingPOPID *list, int nListSz )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetPOPList\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,6 +295,7 @@ bool SetConfigValue( ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigS
|
|||
ESteamNetworkingConfigDataType eDataType, const void *pArg )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SetConfigValue\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -278,6 +310,7 @@ ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue
|
|||
ESteamNetworkingConfigDataType *pOutDataType, void *pResult, size_t *cbResult )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValue\n");
|
||||
return k_ESteamNetworkingGetConfigValue_BadValue;
|
||||
}
|
||||
|
||||
|
||||
|
@ -288,6 +321,7 @@ ESteamNetworkingGetConfigValueResult GetConfigValue( ESteamNetworkingConfigValue
|
|||
bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutName, ESteamNetworkingConfigDataType *pOutDataType, ESteamNetworkingConfigScope *pOutScope, ESteamNetworkingConfigValue *pOutNextValue )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetConfigValueInfo\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -295,6 +329,7 @@ bool GetConfigValueInfo( ESteamNetworkingConfigValue eValue, const char **pOutNa
|
|||
ESteamNetworkingConfigValue GetFirstConfigValue()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::GetFirstConfigValue\n");
|
||||
return k_ESteamNetworkingConfig_Invalid;
|
||||
}
|
||||
|
||||
|
||||
|
@ -303,21 +338,291 @@ ESteamNetworkingConfigValue GetFirstConfigValue()
|
|||
void SteamNetworkingIPAddr_ToString( const SteamNetworkingIPAddr &addr, char *buf, size_t cbBuf, bool bWithPort )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ToString\n");
|
||||
if (buf == nullptr || cbBuf == 0)
|
||||
return;
|
||||
|
||||
char buffer[64]; // Its enought for ipv4 & ipv6 + port
|
||||
std::string str_addr;
|
||||
if (addr.IsIPv4())
|
||||
{
|
||||
in_addr ipv4_addr;
|
||||
ipv4_addr.s_addr = htonl(addr.GetIPv4());
|
||||
|
||||
if (inet_ntop(AF_INET, &ipv4_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
|
||||
{
|
||||
if (bWithPort)
|
||||
{
|
||||
str_addr = buffer;
|
||||
str_addr += ':';
|
||||
str_addr += std::to_string(addr.m_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_addr = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
in6_addr ipv6_addr;
|
||||
memcpy(ipv6_addr.s6_addr, addr.m_ipv6, sizeof(addr.m_ipv6));
|
||||
|
||||
if (inet_ntop(AF_INET6, &ipv6_addr, buffer, sizeof(buffer) / sizeof(*buffer)) != nullptr)
|
||||
{
|
||||
if (bWithPort)
|
||||
{
|
||||
str_addr = '[';
|
||||
str_addr += buffer;
|
||||
str_addr += "]:";
|
||||
str_addr += std::to_string(addr.m_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_addr = buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cbBuf = std::min<size_t>(cbBuf, str_addr.length() + 1);
|
||||
strncpy(buf, str_addr.c_str(), cbBuf);
|
||||
buf[cbBuf - 1] = '\0';
|
||||
}
|
||||
|
||||
bool SteamNetworkingIPAddr_ParseString( SteamNetworkingIPAddr *pAddr, const char *pszStr )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIPAddr_ParseString\n");
|
||||
|
||||
bool valid = false;
|
||||
|
||||
if (pAddr == nullptr || pszStr == nullptr)
|
||||
return valid;
|
||||
|
||||
std::string str(pszStr);
|
||||
size_t pos = str.find(':');
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{// Try ipv4 with port
|
||||
in_addr ipv4_addr;
|
||||
std::string tmp(str);
|
||||
tmp[pos] = 0;
|
||||
const char* ip = tmp.c_str();
|
||||
const char* port = &tmp[pos + 1];
|
||||
|
||||
if (inet_pton(AF_INET, ip, &ipv4_addr) == 1)
|
||||
{
|
||||
valid = true;
|
||||
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), strtoul(port, nullptr, 10));
|
||||
}
|
||||
}
|
||||
else
|
||||
{// Try ipv4 without port
|
||||
in_addr ipv4_addr;
|
||||
if (inet_pton(AF_INET, str.c_str(), &ipv4_addr) == 1)
|
||||
{
|
||||
valid = true;
|
||||
pAddr->SetIPv4(ntohl(ipv4_addr.s_addr), 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{// Try ipv6
|
||||
addrinfo* info = nullptr;
|
||||
addrinfo hints = {};
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
|
||||
|
||||
size_t sep_pos = 0;
|
||||
std::string ip;
|
||||
int sep_count = 0;
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
{
|
||||
if (str[i] == ':')
|
||||
{
|
||||
sep_pos = i;
|
||||
++sep_count;
|
||||
}
|
||||
}
|
||||
if (sep_count == 8)
|
||||
{
|
||||
ip = std::move(std::string(str.begin(), str.begin() + sep_pos));
|
||||
}
|
||||
else
|
||||
{
|
||||
ip = str;
|
||||
}
|
||||
|
||||
if (getaddrinfo(ip.c_str(), nullptr, &hints, &info) == 0)
|
||||
{
|
||||
sockaddr_in6* maddr = (sockaddr_in6*)info->ai_addr;
|
||||
|
||||
size_t pos = str.find(']');
|
||||
std::string str_port("0");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
str_port = std::move(std::string(str.begin() + pos + 2, str.end()));
|
||||
}
|
||||
else if (sep_count == 8)
|
||||
{
|
||||
str_port = std::move(std::string(str.begin() + sep_pos + 1, str.end()));
|
||||
}
|
||||
try
|
||||
{
|
||||
int port = std::stoi(str_port);
|
||||
if (port >= 0 && port <= 65535)
|
||||
{
|
||||
pAddr->SetIPv6(maddr->sin6_addr.s6_addr, port);
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{ }
|
||||
}
|
||||
|
||||
if (info)
|
||||
{
|
||||
freeaddrinfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
pAddr->Clear();
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
void SteamNetworkingIdentity_ToString( const SteamNetworkingIdentity &identity, char *buf, size_t cbBuf )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ToString\n");
|
||||
if (buf == nullptr)
|
||||
return;
|
||||
|
||||
std::string str;
|
||||
str.reserve(SteamNetworkingIdentity::k_cchMaxString);
|
||||
switch (identity.m_eType)
|
||||
{
|
||||
case k_ESteamNetworkingIdentityType_SteamID:
|
||||
{
|
||||
str = "steamid:";
|
||||
str += std::move(std::to_string(identity.GetSteamID64()));
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_IPAddress:
|
||||
{
|
||||
str = "ip:";
|
||||
char buff[SteamNetworkingIPAddr::k_cchMaxString];
|
||||
auto& addr = *identity.GetIPAddr();
|
||||
SteamNetworkingIPAddr_ToString(addr, buff, sizeof(buff), true);
|
||||
str += buff;
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_GenericBytes:
|
||||
{
|
||||
int generic_len;
|
||||
const uint8* pBuf = identity.GetGenericBytes(generic_len);
|
||||
|
||||
str = "gen:";
|
||||
str.resize(4 + (generic_len * 2));
|
||||
|
||||
char* pDest = &str[4];
|
||||
while(generic_len--)
|
||||
{
|
||||
// I don't care for the last char, I've reserved the max string size
|
||||
snprintf(pDest, 3, "%02x", *pBuf);
|
||||
++pBuf;
|
||||
pDest += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_GenericString:
|
||||
{
|
||||
str = "str:";
|
||||
str += identity.GetGenericString();
|
||||
}
|
||||
break;
|
||||
|
||||
case k_ESteamNetworkingIdentityType_UnknownType:
|
||||
{
|
||||
str = identity.m_szUnknownRawString;
|
||||
}
|
||||
break;
|
||||
}
|
||||
cbBuf = std::min<size_t>(cbBuf, str.length() + 1);
|
||||
strncpy(buf, str.c_str(), cbBuf);
|
||||
buf[cbBuf - 1] = '\0';
|
||||
}
|
||||
|
||||
bool SteamNetworkingIdentity_ParseString( SteamNetworkingIdentity *pIdentity, const char *pszStr )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Networking_Utils::SteamNetworkingIdentity_ParseString\n");
|
||||
bool valid = false;
|
||||
if (pIdentity == nullptr)
|
||||
{
|
||||
return valid;
|
||||
}
|
||||
|
||||
if (pszStr != nullptr)
|
||||
{
|
||||
const char* end = strchr(pszStr, ':');
|
||||
if (end != nullptr)
|
||||
{
|
||||
++end;
|
||||
if (strncmp(pszStr, "gen:", end - pszStr) == 0)
|
||||
{
|
||||
size_t length = strlen(end);
|
||||
if (!(length % 2) && length <= (sizeof(pIdentity->m_genericBytes) * 2))
|
||||
{// Must be even
|
||||
valid = true;
|
||||
length /= 2;
|
||||
pIdentity->m_eType = k_ESteamNetworkingIdentityType_GenericBytes;
|
||||
pIdentity->m_cbSize = length;
|
||||
uint8* pBytes = pIdentity->m_genericBytes;
|
||||
|
||||
char hex[3] = { 0,0,0 };
|
||||
while (length)
|
||||
{
|
||||
hex[0] = end[0];
|
||||
hex[1] = end[1];
|
||||
// Steam doesn't check if wasn't a hex char
|
||||
*pBytes = strtol(hex, nullptr, 16);
|
||||
|
||||
++pBytes;
|
||||
end += 2;
|
||||
--length;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncmp(pszStr, "steamid:", end - pszStr) == 0)
|
||||
{
|
||||
CSteamID steam_id(uint64(strtoull(end, nullptr, 10)));
|
||||
if (steam_id.IsValid())
|
||||
{
|
||||
valid = true;
|
||||
pIdentity->SetSteamID(steam_id);
|
||||
}
|
||||
}
|
||||
else if (strncmp(pszStr, "str:", end - pszStr) == 0)
|
||||
{
|
||||
valid = pIdentity->SetGenericString(end);
|
||||
}
|
||||
else if (strncmp(pszStr, "ip:", end - pszStr) == 0)
|
||||
{
|
||||
SteamNetworkingIPAddr steam_addr;
|
||||
if (SteamNetworkingIPAddr_ParseString(&steam_addr, end))
|
||||
{
|
||||
valid = true;
|
||||
pIdentity->SetIPAddr(steam_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -71,16 +71,19 @@ Steam_Parties(class Settings *settings, class Networking *network, class SteamCa
|
|||
uint32 GetNumActiveBeacons()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetNumActiveBeacons\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PartyBeaconID_t GetBeaconByIndex( uint32 unIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconByIndex\n");
|
||||
return k_ulPartyBeaconIdInvalid;
|
||||
}
|
||||
|
||||
bool GetBeaconDetails( PartyBeaconID_t ulBeaconID, CSteamID *pSteamIDBeaconOwner, STEAM_OUT_STRUCT() SteamPartyBeaconLocation_t *pLocation, STEAM_OUT_STRING_COUNT(cchMetadata) char *pchMetadata, int cchMetadata )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconDetails\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -90,6 +93,7 @@ STEAM_CALL_RESULT( JoinPartyCallback_t )
|
|||
SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::JoinParty\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -100,11 +104,13 @@ SteamAPICall_t JoinParty( PartyBeaconID_t ulBeaconID )
|
|||
bool GetNumAvailableBeaconLocations( uint32 *puNumLocations )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetNumAvailableBeaconLocations\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetAvailableBeaconLocations( SteamPartyBeaconLocation_t *pLocationList, uint32 uMaxNumLocations )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetAvailableBeaconLocations\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,6 +122,7 @@ STEAM_CALL_RESULT( CreateBeaconCallback_t )
|
|||
SteamAPICall_t CreateBeacon( uint32 unOpenSlots, SteamPartyBeaconLocation_t *pBeaconLocation, const char *pchConnectString, const char *pchMetadata )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::CreateBeacon\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -143,6 +150,7 @@ STEAM_CALL_RESULT( ChangeNumOpenSlotsCallback_t )
|
|||
SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::ChangeNumOpenSlots\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,6 +158,7 @@ SteamAPICall_t ChangeNumOpenSlots( PartyBeaconID_t ulBeacon, uint32 unOpenSlots
|
|||
bool DestroyBeacon( PartyBeaconID_t ulBeacon )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::DestroyBeacon\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -157,6 +166,7 @@ bool DestroyBeacon( PartyBeaconID_t ulBeacon )
|
|||
bool GetBeaconLocationData( SteamPartyBeaconLocation_t BeaconLocation, ESteamPartyBeaconLocationData eData, STEAM_OUT_STRING_COUNT(cchDataStringOut) char *pchDataStringOut, int cchDataStringOut )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Parties::GetBeaconLocationData\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ SteamAPICall_t FileReadAsync( const char *pchFile, uint32 nOffset, uint32 cubToR
|
|||
a_read.size = size;
|
||||
|
||||
async_reads.push_back(a_read);
|
||||
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data));
|
||||
callback_results->addCallResult(data.m_hFileReadAsync, data.k_iCallback, &data, sizeof(data), 0.0);
|
||||
return data.m_hFileReadAsync;
|
||||
}
|
||||
|
||||
|
@ -405,11 +405,13 @@ SteamAPICall_t UGCDownload( UGCHandle_t hContent )
|
|||
bool GetUGCDownloadProgress( UGCHandle_t hContent, int32 *pnBytesDownloaded, int32 *pnBytesExpected )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, uint32 *pnBytesExpected )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDownloadProgress old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -417,6 +419,7 @@ bool GetUGCDownloadProgress( UGCHandle_t hContent, uint32 *pnBytesDownloaded, ui
|
|||
bool GetUGCDetails( UGCHandle_t hContent, AppId_t *pnAppID, STEAM_OUT_STRING() char **ppchName, int32 *pnFileSizeInBytes, STEAM_OUT_STRUCT() CSteamID *pSteamIDOwner )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUGCDetails\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -460,11 +463,13 @@ int32 UGCRead( UGCHandle_t hContent, void *pvData, int32 cubDataToRead, uint32 c
|
|||
int32 GetCachedUGCCount()
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCCount\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
UGCHandle_t GetCachedUGCHandle( int32 iCachedContent )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetCachedUGCHandle\n");
|
||||
return k_UGCHandleInvalid;
|
||||
}
|
||||
|
||||
|
||||
|
@ -517,62 +522,74 @@ STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
|||
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags, EWorkshopFileType eWorkshopFileType )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PublishedFileUpdateHandle_t CreatePublishedFileUpdateRequest( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::CreatePublishedFileUpdateRequest\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileFile( PublishedFileUpdateHandle_t updateHandle, const char *pchFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileFile\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
SteamAPICall_t PublishFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t PublishWorkshopFile( const char *pchFile, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishWorkshopFile old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SteamAPICall_t UpdatePublishedFile( RemoteStorageUpdatePublishedFileRequest_t updatePublishedFileRequest )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFilePreviewFile( PublishedFileUpdateHandle_t updateHandle, const char *pchPreviewFile )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFilePreviewFile\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileTitle( PublishedFileUpdateHandle_t updateHandle, const char *pchTitle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTitle\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileDescription\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileVisibility( PublishedFileUpdateHandle_t updateHandle, ERemoteStoragePublishedFileVisibility eVisibility )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileVisibility\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileTags( PublishedFileUpdateHandle_t updateHandle, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileTags\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUpdatePublishedFileResult_t )
|
||||
SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHandle )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::CommitPublishedFileUpdate\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gets published file details for the given publishedfileid. If unMaxSecondsOld is greater than 0,
|
||||
|
@ -581,19 +598,30 @@ SteamAPICall_t CommitPublishedFileUpdate( PublishedFileUpdateHandle_t updateHand
|
|||
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId, uint32 unMaxSecondsOld )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails\n");
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails %llu\n", unPublishedFileId);
|
||||
//TODO: check what this function really returns
|
||||
return 0;
|
||||
/*
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
RemoteStorageGetPublishedFileDetailsResult_t data = {};
|
||||
data.m_eResult = k_EResultFail;
|
||||
data.m_nPublishedFileId = unPublishedFileId;
|
||||
return callback_results->addCallResult(data.k_iCallback, &data, sizeof(data));
|
||||
*/
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedFileDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedFileDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedFileDetails old\n");
|
||||
return GetPublishedFileDetails(unPublishedFileId, 0);
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageDeletePublishedFileResult_t )
|
||||
SteamAPICall_t DeletePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::DeletePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// enumerate the files that the current user published with this app
|
||||
|
@ -614,6 +642,7 @@ STEAM_CALL_RESULT( RemoteStorageSubscribePublishedFileResult_t )
|
|||
SteamAPICall_t SubscribePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SubscribePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserSubscribedFilesResult_t )
|
||||
|
@ -634,29 +663,34 @@ STEAM_CALL_RESULT( RemoteStorageUnsubscribePublishedFileResult_t )
|
|||
SteamAPICall_t UnsubscribePublishedFile( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UnsubscribePublishedFile\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UpdatePublishedFileSetChangeDescription( PublishedFileUpdateHandle_t updateHandle, const char *pchChangeDescription )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdatePublishedFileSetChangeDescription\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
|
||||
SteamAPICall_t GetPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetPublishedItemVoteDetails\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageUpdateUserPublishedItemVoteResult_t )
|
||||
SteamAPICall_t UpdateUserPublishedItemVote( PublishedFileId_t unPublishedFileId, bool bVoteUp )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UpdateUserPublishedItemVote\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageGetPublishedItemVoteDetailsResult_t )
|
||||
SteamAPICall_t GetUserPublishedItemVoteDetails( PublishedFileId_t unPublishedFileId )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::GetUserPublishedItemVoteDetails\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumerateUserPublishedFilesResult_t )
|
||||
|
@ -683,24 +717,28 @@ STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
|||
SteamAPICall_t PublishVideo( EWorkshopVideoProvider eVideoProvider, const char *pchVideoAccount, const char *pchVideoIdentifier, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStoragePublishFileProgress_t )
|
||||
SteamAPICall_t PublishVideo(const char *pchFileName, const char *pchPreviewFile, AppId_t nConsumerAppId, const char *pchTitle, const char *pchDescription, ERemoteStoragePublishedFileVisibility eVisibility, SteamParamStringArray_t *pTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::PublishVideo old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageSetUserPublishedFileActionResult_t )
|
||||
SteamAPICall_t SetUserPublishedFileAction( PublishedFileId_t unPublishedFileId, EWorkshopFileAction eAction )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::SetUserPublishedFileAction\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STEAM_CALL_RESULT( RemoteStorageEnumeratePublishedFilesByUserActionResult_t )
|
||||
SteamAPICall_t EnumeratePublishedFilesByUserAction( EWorkshopFileAction eAction, uint32 unStartIndex )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedFilesByUserAction\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// this method enumerates the public view of workshop files
|
||||
|
@ -708,6 +746,7 @@ STEAM_CALL_RESULT( RemoteStorageEnumerateWorkshopFilesResult_t )
|
|||
SteamAPICall_t EnumeratePublishedWorkshopFiles( EWorkshopEnumerationType eEnumerationType, uint32 unStartIndex, uint32 unCount, uint32 unDays, SteamParamStringArray_t *pTags, SteamParamStringArray_t *pUserTags )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::EnumeratePublishedWorkshopFiles\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -715,6 +754,7 @@ STEAM_CALL_RESULT( RemoteStorageDownloadUGCResult_t )
|
|||
SteamAPICall_t UGCDownloadToLocation( UGCHandle_t hContent, const char *pchLocation, uint32 unPriority )
|
||||
{
|
||||
PRINT_DEBUG("Steam_Remote_Storage::UGCDownloadToLocation\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -105,6 +105,14 @@ bool BGetSessionClientResolution( uint32 unSessionID, int *pnResolutionX, int *p
|
|||
return false;
|
||||
}
|
||||
|
||||
// Invite a friend to Remote Play Together
|
||||
// This returns false if the invite can't be sent
|
||||
bool BSendRemotePlayTogetherInvite( CSteamID steamIDFriend )
|
||||
{
|
||||
PRINT_DEBUG("Steam_RemotePlay::BSendRemotePlayTogetherInvite\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -17,12 +17,40 @@
|
|||
|
||||
#include "steam_screenshots.h"
|
||||
|
||||
Steam_Screenshots::Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks) :
|
||||
local_storage(local_storage),
|
||||
callbacks(callbacks)
|
||||
{
|
||||
}
|
||||
|
||||
ScreenshotHandle Steam_Screenshots::create_screenshot_handle()
|
||||
{
|
||||
static ScreenshotHandle handle = 100;
|
||||
return handle++;
|
||||
}
|
||||
|
||||
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight )
|
||||
{
|
||||
PRINT_DEBUG("WriteScreenshot\n");
|
||||
|
||||
char buff[128];
|
||||
auto now = std::chrono::system_clock::now();
|
||||
time_t now_time;
|
||||
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
|
||||
std::string screenshot_name = buff;
|
||||
screenshot_name += ".png";
|
||||
|
||||
if (!local_storage->save_screenshot( screenshot_name, (uint8_t*)pubRGB, nWidth, nHeight, 3))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
auto handle = create_screenshot_handle();
|
||||
auto& infos = _screenshots[handle];
|
||||
infos.screenshot_name = buff;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,7 +61,30 @@ ScreenshotHandle Steam_Screenshots::WriteScreenshot( void *pubRGB, uint32 cubRGB
|
|||
ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight )
|
||||
{
|
||||
PRINT_DEBUG("AddScreenshotToLibrary\n");
|
||||
|
||||
if (pchFilename == nullptr)
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
std::vector<image_pixel_t> pixels(std::move(local_storage->load_image(pchFilename)));
|
||||
if (pixels.size() != size_t(nWidth) * size_t(nHeight))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
char buff[128];
|
||||
auto now = std::chrono::system_clock::now();
|
||||
time_t now_time;
|
||||
now_time = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
|
||||
strftime(buff, 128, "%a_%b_%d_%H_%M_%S_%Y", localtime(&now_time));
|
||||
std::string screenshot_name = buff;
|
||||
screenshot_name += ".png";
|
||||
|
||||
if (!local_storage->save_screenshot(screenshot_name, (uint8_t*)pixels.data(), nWidth, nHeight, 4))
|
||||
return INVALID_SCREENSHOT_HANDLE;
|
||||
|
||||
auto handle = create_screenshot_handle();
|
||||
auto& infos = _screenshots[handle];
|
||||
infos.screenshot_name = buff;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,6 +92,16 @@ ScreenshotHandle Steam_Screenshots::AddScreenshotToLibrary( const char *pchFilen
|
|||
void Steam_Screenshots::TriggerScreenshot()
|
||||
{
|
||||
PRINT_DEBUG("TriggerScreenshot\n");
|
||||
|
||||
if (hooked)
|
||||
{
|
||||
ScreenshotRequested_t data;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT_DEBUG("TODO: Make the overlay take a screenshot");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,7 +119,15 @@ void Steam_Screenshots::HookScreenshots( bool bHook )
|
|||
bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *pchLocation )
|
||||
{
|
||||
PRINT_DEBUG("SetLocation\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["locations"].push_back(pchLocation);
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,7 +135,15 @@ bool Steam_Screenshots::SetLocation( ScreenshotHandle hScreenshot, const char *p
|
|||
bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID )
|
||||
{
|
||||
PRINT_DEBUG("TagUser\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["users"].push_back(uint64_t(steamID.ConvertToUint64()));
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,7 +151,15 @@ bool Steam_Screenshots::TagUser( ScreenshotHandle hScreenshot, CSteamID steamID
|
|||
bool Steam_Screenshots::TagPublishedFile( ScreenshotHandle hScreenshot, PublishedFileId_t unPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("TagPublishedFile\n");
|
||||
|
||||
auto it = _screenshots.find(hScreenshot);
|
||||
if (it == _screenshots.end())
|
||||
return false;
|
||||
|
||||
it->second.metadatas["published_files"].push_back(uint64_t(unPublishedFileID));
|
||||
local_storage->write_json_file(Local_Storage::screenshots_folder, it->second.screenshot_name + ".json", it->second.metadatas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,10 +17,25 @@
|
|||
|
||||
#include "base.h"
|
||||
|
||||
struct screenshot_infos_t
|
||||
{
|
||||
std::string screenshot_name;
|
||||
nlohmann::json metadatas;
|
||||
};
|
||||
|
||||
class Steam_Screenshots : public ISteamScreenshots
|
||||
{
|
||||
bool hooked = false;
|
||||
std::map<ScreenshotHandle, screenshot_infos_t> _screenshots;
|
||||
|
||||
class Local_Storage* local_storage;
|
||||
class SteamCallBacks* callbacks;
|
||||
|
||||
ScreenshotHandle create_screenshot_handle();
|
||||
|
||||
public:
|
||||
Steam_Screenshots(class Local_Storage* local_storage, class SteamCallBacks* callbacks);
|
||||
|
||||
// Writes a screenshot to the user's screenshot library given the raw image data, which must be in RGB format.
|
||||
// The return value is a handle that is valid for the duration of the game process and can be used to apply tags.
|
||||
ScreenshotHandle WriteScreenshot( void *pubRGB, uint32 cubRGB, int nWidth, int nHeight );
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
|
||||
class Steam_TV :
|
||||
public ISteamTV
|
||||
{
|
||||
class Settings *settings;
|
||||
class Networking *network;
|
||||
class SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class RunEveryRunCB *run_every_runcb;
|
||||
std::chrono::time_point<std::chrono::steady_clock> initialized_time = std::chrono::steady_clock::now();
|
||||
FSteamNetworkingSocketsDebugOutput debug_function;
|
||||
|
||||
public:
|
||||
static void steam_callback(void *object, Common_Message *msg)
|
||||
{
|
||||
PRINT_DEBUG("steam_tv_callback\n");
|
||||
|
||||
Steam_TV *steam_parties = (Steam_TV *)object;
|
||||
steam_parties->Callback(msg);
|
||||
}
|
||||
|
||||
static void steam_run_every_runcb(void *object)
|
||||
{
|
||||
PRINT_DEBUG("steam_tv_run_every_runcb\n");
|
||||
|
||||
Steam_TV *steam_parties = (Steam_TV *)object;
|
||||
steam_parties->RunCallbacks();
|
||||
}
|
||||
|
||||
Steam_TV(class Settings *settings, class Networking *network, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, class RunEveryRunCB *run_every_runcb)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->network = network;
|
||||
this->run_every_runcb = run_every_runcb;
|
||||
//this->network->setCallback(CALLBACK_ID_USER_STATUS, settings->get_local_steam_id(), &Steam_TV::steam_callback, this);
|
||||
// this->run_every_runcb->add(&Steam_TV::steam_run_every_runcb, this);
|
||||
|
||||
this->callback_results = callback_results;
|
||||
this->callbacks = callbacks;
|
||||
}
|
||||
|
||||
~Steam_TV()
|
||||
{
|
||||
//TODO rm network callbacks
|
||||
//this->run_every_runcb->remove(&Steam_TV::steam_run_every_runcb, this);
|
||||
}
|
||||
|
||||
bool IsBroadcasting(int *pnNumViewers)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddBroadcastGameData(const char * pchKey, const char * pchValue)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RemoveBroadcastGameData(const char * pchKey)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void AddTimelineMarker(const char * pchTemplateName, bool bPersistent, uint8 nColorR, uint8 nColorG, uint8 nColorB)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RemoveTimelineMarker()
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
uint32 AddRegion(const char * pchElementName, const char * pchTimelineDataSection, const SteamTVRegion_t * pSteamTVRegion, ESteamTVRegionBehavior eSteamTVRegionBehavior)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RemoveRegion(uint32 unRegionHandle)
|
||||
{
|
||||
PRINT_DEBUG("%s\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
void RunCallbacks()
|
||||
{
|
||||
}
|
||||
|
||||
void Callback(Common_Message *msg)
|
||||
{
|
||||
if (msg->has_low_level()) {
|
||||
if (msg->low_level().type() == Low_Level::CONNECT) {
|
||||
|
||||
}
|
||||
|
||||
if (msg->low_level().type() == Low_Level::DISCONNECT) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->has_networking_sockets()) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
};
|
|
@ -37,6 +37,7 @@ public ISteamUGC008,
|
|||
public ISteamUGC009,
|
||||
public ISteamUGC010,
|
||||
public ISteamUGC012,
|
||||
public ISteamUGC013,
|
||||
public ISteamUGC
|
||||
{
|
||||
class Settings *settings;
|
||||
|
@ -274,6 +275,11 @@ bool AddRequiredTag( UGCQueryHandle_t handle, const char *pTagName )
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AddRequiredTagGroup( UGCQueryHandle_t handle, const SteamParamStringArray_t *pTagGroups )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::AddRequiredTagGroup\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddExcludedTag( UGCQueryHandle_t handle, const char *pTagName )
|
||||
{
|
||||
|
@ -665,7 +671,7 @@ uint32 GetSubscribedItems( PublishedFileId_t* pvecPublishedFileID, uint32 cMaxEn
|
|||
// get EItemState flags about item on this client
|
||||
uint32 GetItemState( PublishedFileId_t nPublishedFileID )
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemState\n");
|
||||
PRINT_DEBUG("Steam_UGC::GetItemState %llu\n", nPublishedFileID);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (subscribed.count(nPublishedFileID)) {
|
||||
if (settings->isModInstalled(nPublishedFileID)) {
|
||||
|
@ -706,10 +712,10 @@ bool GetItemDownloadInfo( PublishedFileId_t nPublishedFileID, uint64 *punBytesDo
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
|
||||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, STEAM_OUT_STRING_COUNT( cchFolderSize ) char *pchFolder, uint32 cchFolderSize, bool *pbLegacyItem ) // returns true if item is installed
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo old\n");
|
||||
return false;
|
||||
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
|
||||
}
|
||||
|
||||
bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate, bool *pbIsDownloading, uint64 *punBytesDownloaded, uint64 *punBytesTotal )
|
||||
|
@ -721,7 +727,7 @@ bool GetItemUpdateInfo( PublishedFileId_t nPublishedFileID, bool *pbNeedsUpdate,
|
|||
bool GetItemInstallInfo( PublishedFileId_t nPublishedFileID, uint64 *punSizeOnDisk, char *pchFolder, uint32 cchFolderSize ) // returns true if item is installed
|
||||
{
|
||||
PRINT_DEBUG("Steam_UGC::GetItemInstallInfo older\n");
|
||||
return false;
|
||||
return GetItemInstallInfo(nPublishedFileID, punSizeOnDisk, pchFolder, cchFolderSize, (uint32*) nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ public ISteamUser016,
|
|||
public ISteamUser017,
|
||||
public ISteamUser018,
|
||||
public ISteamUser019,
|
||||
public ISteamUser020,
|
||||
public ISteamUser
|
||||
{
|
||||
Settings *settings;
|
||||
|
@ -82,7 +83,6 @@ bool BLoggedOn()
|
|||
CSteamID GetSteamID()
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::GetSteamID\n");
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
CSteamID id = settings->get_local_steam_id();
|
||||
|
||||
return id;
|
||||
|
@ -110,6 +110,7 @@ CSteamID GetSteamID()
|
|||
int InitiateGameConnection( void *pAuthBlob, int cbMaxAuthBlob, CSteamID steamIDGameServer, uint32 unIPServer, uint16 usPortServer, bool bSecure )
|
||||
{
|
||||
PRINT_DEBUG("InitiateGameConnection %i %llu %u %u %u\n", cbMaxAuthBlob, steamIDGameServer.ConvertToUint64(), unIPServer, usPortServer, bSecure);
|
||||
std::lock_guard<std::recursive_mutex> lock(global_mutex);
|
||||
if (cbMaxAuthBlob < INITIATE_GAME_CONNECTION_TICKET_SIZE) return 0;
|
||||
uint32 out_size = INITIATE_GAME_CONNECTION_TICKET_SIZE;
|
||||
ticket_manager->getTicketData(pAuthBlob, INITIATE_GAME_CONNECTION_TICKET_SIZE, &out_size);
|
||||
|
@ -374,8 +375,15 @@ SteamAPICall_t RequestEncryptedAppTicket( void *pDataToInclude, int cbDataToIncl
|
|||
bool GetEncryptedAppTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket )
|
||||
{
|
||||
PRINT_DEBUG("Steam_User::GetEncryptedAppTicket %i\n", cbMaxTicket);
|
||||
if (!pcbTicket || !pTicket) return false;
|
||||
if (!pcbTicket) return false;
|
||||
unsigned int ticket_size = encrypted_app_ticket.size() + 126;
|
||||
if (!cbMaxTicket) {
|
||||
*pcbTicket = ticket_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pTicket) return false;
|
||||
|
||||
//TODO figure out exact sizes?
|
||||
if (ticket_size < cbMaxTicket) cbMaxTicket = ticket_size;
|
||||
char ticket_base[] = {0x08, 0x01};
|
||||
|
@ -462,4 +470,13 @@ SteamAPICall_t GetDurationControl()
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Advise steam china duration control system about the online state of the game.
|
||||
// This will prevent offline gameplay time from counting against a user's
|
||||
// playtime limits.
|
||||
bool BSetDurationControlOnlineState( EDurationControlOnlineState eNewState )
|
||||
{
|
||||
PRINT_DEBUG("BSetDurationControlOnlineState\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "base.h"
|
||||
#ifndef __INCLUDED_STEAM_USER_STATS_H__
|
||||
#define __INCLUDED_STEAM_USER_STATS_H__
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include "../json/json.hpp"
|
||||
#include "base.h"
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
struct Steam_Leaderboard {
|
||||
std::string name;
|
||||
|
@ -36,6 +36,7 @@ public ISteamUserStats007,
|
|||
public ISteamUserStats008,
|
||||
public ISteamUserStats009,
|
||||
public ISteamUserStats010,
|
||||
public ISteamUserStats011,
|
||||
public ISteamUserStats
|
||||
{
|
||||
public:
|
||||
|
@ -47,6 +48,8 @@ private:
|
|||
Settings *settings;
|
||||
SteamCallResults *callback_results;
|
||||
class SteamCallBacks *callbacks;
|
||||
class Steam_Overlay* overlay;
|
||||
|
||||
std::vector<struct Steam_Leaderboard> leaderboards;
|
||||
|
||||
nlohmann::json defined_achievements;
|
||||
|
@ -80,13 +83,14 @@ void save_achievements()
|
|||
}
|
||||
|
||||
public:
|
||||
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks):
|
||||
Steam_User_Stats(Settings *settings, Local_Storage *local_storage, class SteamCallResults *callback_results, class SteamCallBacks *callbacks, Steam_Overlay* overlay):
|
||||
settings(settings),
|
||||
local_storage(local_storage),
|
||||
callback_results(callback_results),
|
||||
callbacks(callbacks),
|
||||
defined_achievements(nlohmann::json::object()),
|
||||
user_achievements(nlohmann::json::object())
|
||||
user_achievements(nlohmann::json::object()),
|
||||
overlay(overlay)
|
||||
{
|
||||
load_achievements_db(); // achievements db
|
||||
load_achievements(); // achievements per user
|
||||
|
@ -238,6 +242,9 @@ bool SetAchievement( const char *pchName )
|
|||
if (user_achievements.find(pchName) == user_achievements.end() || user_achievements[pchName].value("earned", false) == false) {
|
||||
user_achievements[pchName]["earned"] = true;
|
||||
user_achievements[pchName]["earned_time"] = std::chrono::duration_cast<std::chrono::duration<uint32>>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
#ifdef EMU_OVERLAY
|
||||
overlay->AddAchievementNotification(it.value());
|
||||
#endif
|
||||
save_achievements();
|
||||
}
|
||||
|
||||
|
@ -293,7 +300,7 @@ bool GetAchievementAndUnlockTime( const char *pchName, bool *pbAchieved, uint32
|
|||
|
||||
if(pbAchieved != nullptr) *pbAchieved = false;
|
||||
if(punUnlockTime != nullptr) *punUnlockTime = 0;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -312,7 +319,7 @@ bool StoreStats()
|
|||
UserStatsStored_t data;
|
||||
data.m_nGameID = settings->get_local_game_id().ToUint64();
|
||||
data.m_eResult = k_EResultOK;
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data));
|
||||
callbacks->addCBResult(data.k_iCallback, &data, sizeof(data), 0.01);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -768,6 +775,7 @@ STEAM_CALL_RESULT( GlobalAchievementPercentagesReady_t )
|
|||
SteamAPICall_t RequestGlobalAchievementPercentages()
|
||||
{
|
||||
PRINT_DEBUG("RequestGlobalAchievementPercentages\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -777,6 +785,7 @@ SteamAPICall_t RequestGlobalAchievementPercentages()
|
|||
int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
|
||||
{
|
||||
PRINT_DEBUG("GetMostAchievedAchievementInfo\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -786,6 +795,7 @@ int GetMostAchievedAchievementInfo( char *pchName, uint32 unNameBufLen, float *p
|
|||
int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, uint32 unNameBufLen, float *pflPercent, bool *pbAchieved )
|
||||
{
|
||||
PRINT_DEBUG("GetNextMostAchievedAchievementInfo\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -793,6 +803,7 @@ int GetNextMostAchievedAchievementInfo( int iIteratorPrevious, char *pchName, ui
|
|||
bool GetAchievementAchievedPercent( const char *pchName, float *pflPercent )
|
||||
{
|
||||
PRINT_DEBUG("GetAchievementAchievedPercent\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -841,4 +852,23 @@ int32 GetGlobalStatHistory( const char *pchStatName, STEAM_ARRAY_COUNT(cubData)
|
|||
PRINT_DEBUG("GetGlobalStatHistory double %s\n", pchStatName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For achievements that have related Progress stats, use this to query what the bounds of that progress are.
|
||||
// You may want this info to selectively call IndicateAchievementProgress when appropriate milestones of progress
|
||||
// have been made, to show a progress notification to the user.
|
||||
bool GetAchievementProgressLimits( const char *pchName, int32 *pnMinProgress, int32 *pnMaxProgress )
|
||||
{
|
||||
PRINT_DEBUG("GetAchievementProgressLimits int\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetAchievementProgressLimits( const char *pchName, float *pfMinProgress, float *pfMaxProgress )
|
||||
{
|
||||
PRINT_DEBUG("GetAchievementProgressLimits float\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif//__INCLUDED_STEAM_USER_STATS_H__
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "base.h"
|
||||
#include "local_storage.h"
|
||||
#include "../overlay_experimental/steam_overlay.h"
|
||||
|
||||
static std::chrono::time_point<std::chrono::steady_clock> app_initialized_time = std::chrono::steady_clock::now();
|
||||
|
||||
|
@ -32,18 +33,20 @@ public ISteamUtils005,
|
|||
public ISteamUtils006,
|
||||
public ISteamUtils007,
|
||||
public ISteamUtils008,
|
||||
public ISteamUtils009,
|
||||
public ISteamUtils
|
||||
{
|
||||
private:
|
||||
Settings *settings;
|
||||
class SteamCallResults *callback_results;
|
||||
Steam_Overlay* overlay;
|
||||
|
||||
public:
|
||||
Steam_Utils(Settings *settings, class SteamCallResults *callback_results)
|
||||
{
|
||||
this->settings = settings;
|
||||
this->callback_results = callback_results;
|
||||
}
|
||||
Steam_Utils(Settings *settings, class SteamCallResults *callback_results, Steam_Overlay *overlay):
|
||||
settings(settings),
|
||||
callback_results(callback_results),
|
||||
overlay(overlay)
|
||||
{}
|
||||
|
||||
// return the number of seconds since the user
|
||||
uint32 GetSecondsSinceAppActive()
|
||||
|
@ -147,6 +150,7 @@ uint32 GetAppID()
|
|||
void SetOverlayNotificationPosition( ENotificationPosition eNotificationPosition )
|
||||
{
|
||||
PRINT_DEBUG("SetOverlayNotificationPosition\n");
|
||||
overlay->SetNotificationPosition(eNotificationPosition);
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,7 +193,7 @@ bool GetAPICallResult( SteamAPICall_t hSteamAPICall, void *pCallback, int cubCal
|
|||
// Deprecated. Applications should use SteamAPI_RunCallbacks() instead. Game servers do not need to call this function.
|
||||
STEAM_PRIVATE_API( void RunFrame()
|
||||
{
|
||||
PRINT_DEBUG("RunFrame\n");
|
||||
PRINT_DEBUG("Steam_Utils::RunFrame\n");
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -221,8 +225,7 @@ void SetWarningMessageHook( SteamAPIWarningMessageHook_t pFunction )
|
|||
bool IsOverlayEnabled()
|
||||
{
|
||||
PRINT_DEBUG("IsOverlayEnabled\n");
|
||||
//TODO
|
||||
return false;
|
||||
return overlay->Ready();
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,7 +241,7 @@ bool IsOverlayEnabled()
|
|||
bool BOverlayNeedsPresent()
|
||||
{
|
||||
PRINT_DEBUG("BOverlayNeedsPresent\n");
|
||||
return false;
|
||||
return overlay->NeedPresent();
|
||||
}
|
||||
|
||||
|
||||
|
@ -308,6 +311,7 @@ bool IsSteamRunningInVR()
|
|||
void SetOverlayNotificationInset( int nHorizontalInset, int nVerticalInset )
|
||||
{
|
||||
PRINT_DEBUG("SetOverlayNotificationInset\n");
|
||||
overlay->SetNotificationInset(nHorizontalInset, nVerticalInset);
|
||||
}
|
||||
|
||||
|
||||
|
@ -356,6 +360,15 @@ bool IsSteamChinaLauncher()
|
|||
// Initializes text filtering.
|
||||
// Returns false if filtering is unavailable for the language the user is currently running in.
|
||||
bool InitFilterText()
|
||||
{
|
||||
PRINT_DEBUG("InitFilterText old\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initializes text filtering.
|
||||
// unFilterOptions are reserved for future use and should be set to 0
|
||||
// Returns false if filtering is unavailable for the language the user is currently running in.
|
||||
bool InitFilterText( uint32 unFilterOptions )
|
||||
{
|
||||
PRINT_DEBUG("InitFilterText\n");
|
||||
return false;
|
||||
|
@ -368,11 +381,33 @@ bool InitFilterText()
|
|||
// bLegalOnly should be false if you want profanity and legally required filtering (where required) and true if you want legally required filtering only
|
||||
// Returns the number of characters (not bytes) filtered.
|
||||
int FilterText( char* pchOutFilteredText, uint32 nByteSizeOutFilteredText, const char * pchInputMessage, bool bLegalOnly )
|
||||
{
|
||||
PRINT_DEBUG("FilterText old\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Filters the provided input message and places the filtered result into pchOutFilteredText, using legally required filtering and additional filtering based on the context and user settings
|
||||
// eContext is the type of content in the input string
|
||||
// sourceSteamID is the Steam ID that is the source of the input string (e.g. the player with the name, or who said the chat text)
|
||||
// pchInputText is the input string that should be filtered, which can be ASCII or UTF-8
|
||||
// pchOutFilteredText is where the output will be placed, even if no filtering is performed
|
||||
// nByteSizeOutFilteredText is the size (in bytes) of pchOutFilteredText, should be at least strlen(pchInputText)+1
|
||||
// Returns the number of characters (not bytes) filtered
|
||||
int FilterText( ETextFilteringContext eContext, CSteamID sourceSteamID, const char *pchInputMessage, char *pchOutFilteredText, uint32 nByteSizeOutFilteredText )
|
||||
{
|
||||
PRINT_DEBUG("FilterText\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Return what we believe your current ipv6 connectivity to "the internet" is on the specified protocol.
|
||||
// This does NOT tell you if the Steam client is currently connected to Steam via ipv6.
|
||||
ESteamIPv6ConnectivityState GetIPv6ConnectivityState( ESteamIPv6ConnectivityProtocol eProtocol )
|
||||
{
|
||||
PRINT_DEBUG("GetIPv6ConnectivityState\n");
|
||||
return k_ESteamIPv6ConnectivityState_Unknown;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,566 @@
|
|||
/* Copyright (C) 2019 Mr Goldberg
|
||||
This file is part of the Goldberg Emulator
|
||||
|
||||
The Goldberg Emulator is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
The Goldberg Emulator is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the Goldberg Emulator; if not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#if defined(WIN64) || defined(_WIN64) || defined(__MINGW64__)
|
||||
#define __WINDOWS_64__
|
||||
#elif defined(WIN32) || defined(_WIN32) || defined(__MINGW32__)
|
||||
#define __WINDOWS_32__
|
||||
#endif
|
||||
|
||||
#if defined(__WINDOWS_32__) || defined(__WINDOWS_64__)
|
||||
// Nothing to be done here
|
||||
#else
|
||||
#define STEAM_API_FUNCTIONS_IMPL
|
||||
#include "base.h"
|
||||
#include "dll.h"
|
||||
|
||||
#define PATH_SEPARATOR_CHAR '/'
|
||||
#define STEAM_PATH_CACHE_SIZE 4096
|
||||
|
||||
const char *STEAM_PATH;
|
||||
size_t STEAM_PATH_SIZE;
|
||||
|
||||
// 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()
|
||||
{
|
||||
char *home_path = getenv("HOME");
|
||||
char steam_path[STEAM_PATH_CACHE_SIZE];
|
||||
char *steam_realpath = nullptr;
|
||||
|
||||
// Build steam_path from home
|
||||
int required_size = snprintf(steam_path, STEAM_PATH_CACHE_SIZE, "%s/.steam/steam", home_path);
|
||||
|
||||
// Allocate more space for steam_path if needed (required_size does not count terminator)
|
||||
if (required_size > 0 && required_size >= STEAM_PATH_CACHE_SIZE) {
|
||||
char *large_steam_path = (char *)malloc(sizeof(char) * (required_size + 1));
|
||||
int check_size = snprintf(large_steam_path, required_size + 1, "%s/.steam/steam", home_path);
|
||||
// Check that path fits this time
|
||||
if (check_size == required_size) {
|
||||
steam_realpath = realpath(large_steam_path, nullptr);
|
||||
}
|
||||
free(large_steam_path);
|
||||
} else {
|
||||
steam_realpath = realpath(steam_path, nullptr);
|
||||
}
|
||||
|
||||
// Terminate path with a file separator
|
||||
if (steam_realpath && *steam_realpath) {
|
||||
size_t path_size = strlen(steam_realpath);
|
||||
if (steam_realpath[path_size - 1] != PATH_SEPARATOR_CHAR) {
|
||||
steam_realpath = (char *)realloc(steam_realpath, path_size + 2);
|
||||
steam_realpath[path_size] = PATH_SEPARATOR_CHAR;
|
||||
steam_realpath[path_size + 1] = 0;
|
||||
}
|
||||
} else {
|
||||
// Failsafe to root
|
||||
steam_realpath = strdup("/");
|
||||
}
|
||||
|
||||
return steam_realpath;
|
||||
}
|
||||
|
||||
// Fixes given path by navigating filesystem and lowering case to match
|
||||
// existing entries on disk
|
||||
bool match_path(char *path, int start, bool accept_same_case)
|
||||
{
|
||||
if (!path[start + 1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Snap to the next separator in path
|
||||
int separator = start + 1;
|
||||
while (path[separator] != PATH_SEPARATOR_CHAR && path[separator]) {
|
||||
separator++;
|
||||
}
|
||||
|
||||
bool is_last_component = path[separator] != PATH_SEPARATOR_CHAR;
|
||||
|
||||
char stored_char = path[separator];
|
||||
path[separator] = 0;
|
||||
bool path_accessible = access(path, F_OK) == 0;
|
||||
path[separator] = stored_char;
|
||||
|
||||
if (!path_accessible || (!is_last_component && !match_path(path, separator, accept_same_case))) {
|
||||
DIR *current_directory = nullptr;
|
||||
int component = start + 1;
|
||||
|
||||
if (start) {
|
||||
stored_char = path[start];
|
||||
path[start] = 0;
|
||||
current_directory = opendir(path);
|
||||
path[start] = stored_char;
|
||||
component = start + 1;
|
||||
} else {
|
||||
if (*path == PATH_SEPARATOR_CHAR) {
|
||||
component = start + 1;
|
||||
current_directory = opendir("/");
|
||||
} else {
|
||||
component = start;
|
||||
current_directory = opendir(".");
|
||||
}
|
||||
}
|
||||
|
||||
// 0123456789012345678901234567890123456789
|
||||
// path = /this/is/a/sample/path/to/file.txt
|
||||
// ^^ ^
|
||||
// ab c
|
||||
// a. start = 10
|
||||
// b. component = 11
|
||||
// c. separator = 17
|
||||
// current_directory = /this/is/a/
|
||||
|
||||
if (current_directory) {
|
||||
dirent64 *entry = (dirent64 *)readdir64(current_directory);
|
||||
while (entry) {
|
||||
const char *entry_name = entry->d_name;
|
||||
stored_char = path[separator];
|
||||
path[separator] = 0;
|
||||
|
||||
// Fix current component if entry with similar name exists
|
||||
if (!strcasecmp(&path[component], entry_name)) {
|
||||
bool case_differs = strcmp(&path[component], entry_name) != 0;
|
||||
path[separator] = stored_char;
|
||||
if (case_differs) {
|
||||
char *iterator = &path[component];
|
||||
// Replace with entry name
|
||||
while (*entry_name != PATH_SEPARATOR_CHAR && *entry_name) {
|
||||
*(iterator++) = *(entry_name++);
|
||||
}
|
||||
// Fix next component
|
||||
if (is_last_component || match_path(path, separator, accept_same_case)) {
|
||||
closedir(current_directory);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
path[separator] = stored_char;
|
||||
}
|
||||
entry = (dirent64 *)readdir64(current_directory);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_directory) {
|
||||
closedir(current_directory);
|
||||
}
|
||||
|
||||
return accept_same_case && is_last_component;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tries to convert the given path to the preferred lower-cased version
|
||||
const char *lowercase_path(const char *path, bool accept_same_case, bool stop_at_separator)
|
||||
{
|
||||
std::locale loc;
|
||||
char *path_lowercased = nullptr;
|
||||
|
||||
if (path && *path) {
|
||||
// If file does not exist
|
||||
if (access(path, F_OK)) {
|
||||
// Make a copy of the path on which to work on
|
||||
path_lowercased = strdup(path);
|
||||
if (!path_lowercased) {
|
||||
return nullptr;
|
||||
}
|
||||
// Load steam path if not done already
|
||||
if (!STEAM_PATH) {
|
||||
STEAM_PATH = get_steam_path();
|
||||
STEAM_PATH_SIZE = strlen(STEAM_PATH);
|
||||
}
|
||||
|
||||
char *lowercase_iterator = path_lowercased;
|
||||
// Lowercase whole steam path if possible
|
||||
bool has_steam_root = false;
|
||||
if (!strncasecmp(path_lowercased, STEAM_PATH, STEAM_PATH_SIZE)) {
|
||||
memcpy(path_lowercased, STEAM_PATH, STEAM_PATH_SIZE);
|
||||
lowercase_iterator = &path_lowercased[STEAM_PATH_SIZE - 1];
|
||||
has_steam_root = true;
|
||||
}
|
||||
// Lowercase rest of the path
|
||||
char *iterator = lowercase_iterator;
|
||||
while ((!stop_at_separator || *iterator != PATH_SEPARATOR_CHAR) && *iterator) {
|
||||
*iterator = std::tolower(*iterator, loc);
|
||||
iterator++;
|
||||
}
|
||||
|
||||
// Check if we can access the lowered-case path
|
||||
int error = access(path_lowercased, F_OK);
|
||||
if (!error) {
|
||||
// The new path is valid
|
||||
return path_lowercased;
|
||||
} else {
|
||||
if (accept_same_case) {
|
||||
const char *name_iterator = &path[lowercase_iterator - path_lowercased];
|
||||
while (*lowercase_iterator) {
|
||||
*(lowercase_iterator++) = *(name_iterator++);
|
||||
}
|
||||
}
|
||||
// Retry accesing the file again and tweak the path if needed
|
||||
if (match_path(path_lowercased, has_steam_root? STEAM_PATH_SIZE - 1 : 0, accept_same_case)) {
|
||||
return path_lowercased;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_freopen(const char *path, const char *modes, FILE *stream)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = freopen(path_lowercased, modes, stream);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_fopen(const char *path, const char *modes)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = fopen(path_lowercased, modes);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API FILE *__wrap_fopen64(const char *path, const char *modes)
|
||||
{
|
||||
bool is_writable = strpbrk(modes, "wa+") != 0;
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
FILE *result = fopen64(path_lowercased, modes);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_open(const char *path, int flags, mode_t mode)
|
||||
{
|
||||
bool is_writable = flags & (X_OK | W_OK);
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
int result = open(path_lowercased, flags, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_open64(const char *path, int flags, mode_t mode)
|
||||
{
|
||||
bool is_writable = flags & (X_OK | W_OK);
|
||||
const char *path_lowercased = lowercase_path(path, is_writable, true);
|
||||
int result = open64(path_lowercased, flags, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_access(const char *path, int mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = access(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_stat(const char * path, struct stat * stat_buf)
|
||||
{
|
||||
return __wrap___xstat(3, path, 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);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_lstat(const char * path, struct stat * stat_buf)
|
||||
{
|
||||
return __wrap___lxstat(3, path, stat_buf);
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_scandir(const char *path, struct dirent ***namelist, int (*sel)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = scandir(path_lowercased, namelist, sel, compar);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_scandir64(const char *path, struct dirent64 ***namelist, int (*sel)(const struct dirent64 *), int (*compar)(const struct dirent64 **, const struct dirent64 **))
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = scandir64(path_lowercased, namelist, sel, compar);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API DIR *__wrap_opendir(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
DIR *result = opendir(path_lowercased);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_statvfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = statvfs(path_lowercased, buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_statvfs64(const char *path, struct statvfs64 *buf)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = statvfs64(path_lowercased, buf);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_chmod(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = chmod(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_chown(const char *path, uid_t owner, gid_t group)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = chown(path_lowercased, owner, group);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_lchown(const char *path, uid_t owner, gid_t group)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = lchown(path_lowercased, owner, group);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_symlink(const char *path1, const char *path2)
|
||||
{
|
||||
const char *path_lowercased1 = lowercase_path(path1, true, true);
|
||||
const char *path_lowercased2 = lowercase_path(path2, false, false);
|
||||
int result = symlink(path_lowercased1, path_lowercased2);
|
||||
if (path_lowercased1 != path1) {
|
||||
free((void *)path_lowercased1);
|
||||
}
|
||||
if (path_lowercased2 != path2) {
|
||||
free((void *)path_lowercased2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_link(const char *path1, const char *path2)
|
||||
{
|
||||
const char *path_lowercased1 = lowercase_path(path1, true, true);
|
||||
const char *path_lowercased2 = lowercase_path(path2, false, false);
|
||||
int result = link(path_lowercased1, path_lowercased2);
|
||||
if (path_lowercased1 != path1) {
|
||||
free((void *)path_lowercased1);
|
||||
}
|
||||
if (path_lowercased2 != path2) {
|
||||
free((void *)path_lowercased2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data)
|
||||
{
|
||||
const char *source_lowercased = lowercase_path(source, false, false);
|
||||
const char *target_lowercased = lowercase_path(target, false, false);
|
||||
int result = mount(source_lowercased, target_lowercased, filesystemtype, mountflags, data);
|
||||
if (source_lowercased != source) {
|
||||
free((void *)source_lowercased);
|
||||
}
|
||||
if (target_lowercased != target) {
|
||||
free((void *)target_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_unlink(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = unlink(path);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mkfifo(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, true, true);
|
||||
int result = mkfifo(path, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_rename(const char *old_name, const char *new_name)
|
||||
{
|
||||
const char *old_name_lowercased = lowercase_path(old_name, true, true);
|
||||
const char *new_name_lowercased = lowercase_path(new_name, false, false);
|
||||
int result = rename(old_name_lowercased, new_name_lowercased);
|
||||
if (old_name_lowercased != old_name) {
|
||||
free((void *)old_name_lowercased);
|
||||
}
|
||||
if (new_name_lowercased != new_name) {
|
||||
free((void *)new_name_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_utime(const char *path, const struct utimbuf *times)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = utime(path_lowercased, times);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_utimes(const char *path, const struct timeval times[2])
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = utimes(path_lowercased, times);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, true, true);
|
||||
int result = mkdir(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API int __wrap_rmdir(const char *path)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
int result = rmdir(path_lowercased);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API void *__wrap_dlopen(const char *path, int mode)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
void * result = dlopen(path_lowercased, mode);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
STEAMAPI_API void *__wrap_dlmopen(Lmid_t lmid, const char *path, int flags)
|
||||
{
|
||||
const char *path_lowercased = lowercase_path(path, false, false);
|
||||
void * result = dlmopen(lmid, path_lowercased, flags);
|
||||
if (path_lowercased != path) {
|
||||
free((void *)path_lowercased);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 5.9 KiB |