Compare commits

...

6 Commits

30 changed files with 488 additions and 143 deletions

View File

@ -44,6 +44,10 @@ 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 The contents of this file are: appid=DLC name
See the steam_settings.EXAMPLE folder for an example. See the steam_settings.EXAMPLE folder for an example.
Languages:
You can include a steam_settings\supported_languages.txt file with a list of languages that the game supports. If the global emu language setting is not in this list of languages the emu will default to the first language in the list.
See the steam_settings.EXAMPLE folder for an example.
Depots: 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. 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. See the steam_settings.EXAMPLE folder for an example.

View File

@ -563,15 +563,9 @@ static bool is_whitelist_ip(unsigned char *ip)
return false; return false;
} }
static bool is_lan_ip(const sockaddr *addr, int namelen) static bool is_lan_ipv4(unsigned char *ip)
{ {
if (!namelen) return false; PRINT_DEBUG("CHECK LAN IP %hhu.%hhu.%hhu.%hhu\n", ip[0], ip[1], ip[2], ip[3]);
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
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_whitelist_ip(ip)) return true; if (is_whitelist_ip(ip)) return true;
if (ip[0] == 127) return true; if (ip[0] == 127) return true;
if (ip[0] == 10) return true; if (ip[0] == 10) return true;
@ -582,13 +576,25 @@ static bool is_lan_ip(const sockaddr *addr, int namelen)
if (ip[0] == 239) return true; //multicast if (ip[0] == 239) return true; //multicast
if (ip[0] == 0) return true; //Current network if (ip[0] == 0) return true; //Current network
if (ip[0] == 192 && (ip[1] == 18 || ip[1] == 19)) return true; //Used for benchmark testing of inter-network communications between two separate subnets. if (ip[0] == 192 && (ip[1] == 18 || ip[1] == 19)) return true; //Used for benchmark testing of inter-network communications between two separate subnets.
if (ip[0] >= 224) return true; //ip multicast (224 - 239) future use (240.0.0.0255.255.255.254) broadcast (255.255.255.255) if (ip[0] >= 224) return true; //ip multicast (224 - 239) future use (240.0.0.0 - 255.255.255.254) broadcast (255.255.255.255)
return false;
}
static bool is_lan_ip(const sockaddr *addr, int namelen)
{
if (!namelen) return false;
if (addr->sa_family == AF_INET) {
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
unsigned char ip[4];
memcpy(ip, &addr_in->sin_addr, sizeof(ip));
if (is_lan_ipv4(ip)) return true;
} else if (addr->sa_family == AF_INET6) { } else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
unsigned char ip[16]; unsigned char ip[16];
unsigned char zeroes[16] = {}; unsigned char zeroes[16] = {};
memcpy(ip, &addr_in6->sin6_addr, sizeof(ip)); memcpy(ip, &addr_in6->sin6_addr, sizeof(ip));
PRINT_DEBUG("CHECK LAN IP6 %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu...%hhu\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[15]); PRINT_DEBUG("CHECK LAN IP6 %hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu.%hhu\n", ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15]);
if (((ip[0] == 0xFF) && (ip[1] < 3) && (ip[15] == 1)) || if (((ip[0] == 0xFF) && (ip[1] < 3) && (ip[15] == 1)) ||
((ip[0] == 0xFE) && ((ip[1] & 0xC0) == 0x80))) return true; ((ip[0] == 0xFE) && ((ip[1] & 0xC0) == 0x80))) return true;
if (memcmp(zeroes, ip, sizeof(ip)) == 0) return true; if (memcmp(zeroes, ip, sizeof(ip)) == 0) return true;
@ -596,7 +602,13 @@ static bool is_lan_ip(const sockaddr *addr, int namelen)
if (ip[0] == 0xff) return true; //multicast if (ip[0] == 0xff) return true; //multicast
if (ip[0] == 0xfc) return true; //unique local if (ip[0] == 0xfc) return true; //unique local
if (ip[0] == 0xfd) return true; //unique local if (ip[0] == 0xfd) return true; //unique local
//TODO: ipv4 mapped?
unsigned char ipv4_mapped[12] = {};
ipv4_mapped[10] = 0xFF;
ipv4_mapped[11] = 0xFF;
if (memcmp(ipv4_mapped, ip, sizeof(ipv4_mapped)) == 0) {
if (is_lan_ipv4(ip + 12)) return true;
}
} }
PRINT_DEBUG("NOT LAN IP\n"); PRINT_DEBUG("NOT LAN IP\n");

View File

@ -129,23 +129,23 @@ STEAMAPI_API ISteamClient *g_pSteamClientGameServer;
ISteamClient *g_pSteamClientGameServer; ISteamClient *g_pSteamClientGameServer;
#endif #endif
static Steam_Client *client; static Steam_Client *steamclient_instance;
Steam_Client *get_steam_client() Steam_Client *get_steam_client()
{ {
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!client) { if (!steamclient_instance) {
client = new Steam_Client(); steamclient_instance = new Steam_Client();
} }
return client; return steamclient_instance;
} }
void destroy_client() void destroy_client()
{ {
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (client) { if (steamclient_instance) {
delete client; delete steamclient_instance;
client = NULL; steamclient_instance = NULL;
} }
} }
@ -416,6 +416,7 @@ STEAMAPI_API void S_CALLTYPE SteamAPI_UnregisterCallback( class CCallbackBase *p
{ {
PRINT_DEBUG("SteamAPI_UnregisterCallback %p\n", pCallback); PRINT_DEBUG("SteamAPI_UnregisterCallback %p\n", pCallback);
std::lock_guard<std::recursive_mutex> lock(global_mutex); std::lock_guard<std::recursive_mutex> lock(global_mutex);
if (!steamclient_instance) return;
get_steam_client()->UnregisterCallback(pCallback); get_steam_client()->UnregisterCallback(pCallback);
} }
@ -435,6 +436,7 @@ STEAMAPI_API void S_CALLTYPE SteamAPI_UnregisterCallResult( class CCallbackBase
if (!hAPICall) if (!hAPICall)
return; return;
if (!steamclient_instance) return;
get_steam_client()->UnregisterCallResult(pCallback, hAPICall); get_steam_client()->UnregisterCallResult(pCallback, hAPICall);
} }

View File

@ -156,6 +156,9 @@ public:
//app build id //app build id
int build_id = 10; int build_id = 10;
//supported languages
std::set<std::string> supported_languages;
//make lobby creation fail in the matchmaking interface //make lobby creation fail in the matchmaking interface
bool disable_lobby_creation = false; bool disable_lobby_creation = false;

View File

@ -260,6 +260,41 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text)); local_storage->store_data_settings("user_steam_id.txt", temp_text, strlen(temp_text));
} }
std::set<std::string> supported_languages;
{
std::string lang_config_path = Local_Storage::get_game_settings_path() + "supported_languages.txt";
std::ifstream input( utf8_decode(lang_config_path) );
std::string first_language;
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();
}
try {
std::string lang = line;
if (!first_language.size()) first_language = lang;
supported_languages.insert(lang);
PRINT_DEBUG("Added supported_language %s\n", lang.c_str());
} catch (...) {}
}
}
if (!supported_languages.count(language)) {
if (first_language.size()) {
memset(language, 0, sizeof(language));
first_language.copy(language, sizeof(language) - 1);
}
}
}
bool steam_offline_mode = false; bool steam_offline_mode = false;
bool disable_networking = false; bool disable_networking = false;
bool disable_overlay = false; bool disable_overlay = false;
@ -336,6 +371,8 @@ uint32 create_localstorage_settings(Settings **settings_client_out, Settings **s
settings_server->warn_forced = warn_forced; settings_server->warn_forced = warn_forced;
settings_client->warn_local_save = local_save; settings_client->warn_local_save = local_save;
settings_server->warn_local_save = local_save; settings_server->warn_local_save = local_save;
settings_client->supported_languages = supported_languages;
settings_server->supported_languages = supported_languages;
{ {
std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt"; std::string dlc_config_path = Local_Storage::get_game_settings_path() + "DLC.txt";

View File

@ -0,0 +1,8 @@
english
german
french
spanish
schinese
russian
japanese
latam

View File

@ -134,7 +134,7 @@ private:
bool vulkan_hooked; bool vulkan_hooked;
Base_Hook detection_hooks; Base_Hook detection_hooks;
Renderer_Hook* renderer_hook; ingame_overlay::Renderer_Hook* renderer_hook;
DX12_Hook* dx12_hook; DX12_Hook* dx12_hook;
DX11_Hook* dx11_hook; DX11_Hook* dx11_hook;
DX10_Hook* dx10_hook; DX10_Hook* dx10_hook;
@ -238,7 +238,7 @@ private:
void HookDetected(T*& detected_renderer) void HookDetected(T*& detected_renderer)
{ {
detection_hooks.UnhookAll(); detection_hooks.UnhookAll();
renderer_hook = static_cast<Renderer_Hook*>(detected_renderer); renderer_hook = static_cast<ingame_overlay::Renderer_Hook*>(detected_renderer);
detected_renderer = nullptr; detected_renderer = nullptr;
detection_done = true; detection_done = true;
DestroyHWND(); DestroyHWND();
@ -1032,7 +1032,7 @@ private:
} }
public: public:
Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout) ingame_overlay::Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout)
{ {
std::unique_lock<std::timed_mutex> detection_lock(detector_mutex, std::defer_lock); std::unique_lock<std::timed_mutex> detection_lock(detector_mutex, std::defer_lock);
@ -1173,7 +1173,7 @@ private:
bool openglx_hooked; bool openglx_hooked;
//bool vulkan_hooked; //bool vulkan_hooked;
Renderer_Hook* renderer_hook; ingame_overlay::Renderer_Hook* renderer_hook;
OpenGLX_Hook* openglx_hook; OpenGLX_Hook* openglx_hook;
bool detection_done; bool detection_done;
@ -1191,7 +1191,7 @@ private:
if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1)) if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(3, 1))
{ {
inst->detection_hooks.UnhookAll(); inst->detection_hooks.UnhookAll();
inst->renderer_hook = static_cast<Renderer_Hook*>(Inst()->openglx_hook); inst->renderer_hook = static_cast<ingame_overlay::Renderer_Hook*>(Inst()->openglx_hook);
inst->openglx_hook = nullptr; inst->openglx_hook = nullptr;
inst->detection_done = true; inst->detection_done = true;
} }
@ -1238,7 +1238,7 @@ private:
} }
public: public:
Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout) ingame_overlay::Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout)
{ {
std::pair<const char*, void(Renderer_Detector::*)(std::string const&)> libraries[]{ std::pair<const char*, void(Renderer_Detector::*)(std::string const&)> libraries[]{
std::pair<const char*, void(Renderer_Detector::*)(std::string const&)>{OpenGLX_Hook::DLL_NAME, &Renderer_Detector::hook_openglx}, std::pair<const char*, void(Renderer_Detector::*)(std::string const&)>{OpenGLX_Hook::DLL_NAME, &Renderer_Detector::hook_openglx},
@ -1352,7 +1352,7 @@ private:
bool opengl_hooked; bool opengl_hooked;
Renderer_Hook* renderer_hook; ingame_overlay::Renderer_Hook* renderer_hook;
OpenGL_Hook* opengl_hook; OpenGL_Hook* opengl_hook;
bool detection_done; bool detection_done;
@ -1368,7 +1368,7 @@ private:
if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(2, 0)) if (gladLoaderLoadGL() >= GLAD_MAKE_VERSION(2, 0))
{ {
inst->detection_hooks.UnhookAll(); inst->detection_hooks.UnhookAll();
inst->renderer_hook = static_cast<Renderer_Hook*>(Inst()->opengl_hook); inst->renderer_hook = static_cast<ingame_overlay::Renderer_Hook*>(Inst()->opengl_hook);
inst->opengl_hook = nullptr; inst->opengl_hook = nullptr;
inst->detection_done = true; inst->detection_done = true;
} }
@ -1417,7 +1417,7 @@ private:
} }
public: public:
Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout) ingame_overlay::Renderer_Hook* detect_renderer(std::chrono::milliseconds timeout)
{ {
std::pair<const char*, void(Renderer_Detector::*)(std::string const&)> libraries[]{ std::pair<const char*, void(Renderer_Detector::*)(std::string const&)> libraries[]{
std::pair<const char*, void(Renderer_Detector::*)(std::string const&)>{OpenGL_Hook::DLL_NAME, &Renderer_Detector::hook_opengl} std::pair<const char*, void(Renderer_Detector::*)(std::string const&)>{OpenGL_Hook::DLL_NAME, &Renderer_Detector::hook_opengl}
@ -1495,7 +1495,7 @@ Renderer_Detector* Renderer_Detector::instance = nullptr;
namespace ingame_overlay { namespace ingame_overlay {
std::future<Renderer_Hook*> DetectRenderer(std::chrono::milliseconds timeout) std::future<ingame_overlay::Renderer_Hook*> DetectRenderer(std::chrono::milliseconds timeout)
{ {
return std::async(std::launch::async, &Renderer_Detector::detect_renderer, Renderer_Detector::Inst(), timeout); return std::async(std::launch::async, &Renderer_Detector::detect_renderer, Renderer_Detector::Inst(), timeout);
} }

View File

@ -23,6 +23,16 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
#include <set>
namespace ingame_overlay {
enum class ToggleKey
{
SHIFT, CTRL, ALT,
TAB,
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
};
class Renderer_Hook class Renderer_Hook
{ {
@ -37,7 +47,7 @@ public:
std::function<void()> OverlayProc; std::function<void()> OverlayProc;
std::function<void(bool)> OverlayHookReady; std::function<void(bool)> OverlayHookReady;
virtual bool StartHook(std::function<bool(bool)> key_combination_callback) = 0; virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ToggleKey> toggle_keys) = 0;
virtual bool IsStarted() = 0; virtual bool IsStarted() = 0;
// Returns a Handle to the renderer image ressource or nullptr if it failed to create the resource, the handle can be used in ImGui's Image calls, image_buffer must be RGBA ordered // Returns a Handle to the renderer image ressource or nullptr if it failed to create the resource, the handle can be used in ImGui's Image calls, image_buffer must be RGBA ordered
virtual std::weak_ptr<uint64_t> CreateImageResource(const void* image_data, uint32_t width, uint32_t height) = 0; virtual std::weak_ptr<uint64_t> CreateImageResource(const void* image_data, uint32_t width, uint32_t height) = 0;
@ -45,3 +55,5 @@ public:
virtual std::string GetLibraryName() const = 0; virtual std::string GetLibraryName() const = 0;
}; };
}

View File

@ -29,7 +29,7 @@ OpenGLX_Hook* OpenGLX_Hook::_inst = nullptr;
constexpr decltype(OpenGLX_Hook::DLL_NAME) OpenGLX_Hook::DLL_NAME; constexpr decltype(OpenGLX_Hook::DLL_NAME) OpenGLX_Hook::DLL_NAME;
bool OpenGLX_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool OpenGLX_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -39,7 +39,7 @@ bool OpenGLX_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!X11_Hook::Inst()->StartHook(key_combination_callback)) if (!X11_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_X11Hooked = true; _X11Hooked = true;

View File

@ -26,7 +26,7 @@
#include <GL/glx.h> #include <GL/glx.h>
class OpenGLX_Hook : class OpenGLX_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -59,7 +59,7 @@ public:
virtual ~OpenGLX_Hook(); virtual ~OpenGLX_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static OpenGLX_Hook* Inst(); static OpenGLX_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -29,10 +29,62 @@ constexpr decltype(X11_Hook::DLL_NAME) X11_Hook::DLL_NAME;
X11_Hook* X11_Hook::_inst = nullptr; X11_Hook* X11_Hook::_inst = nullptr;
bool X11_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback) uint32_t ToggleKeyToNativeKey(ingame_overlay::ToggleKey k)
{
struct {
ingame_overlay::ToggleKey lib_key;
uint32_t native_key;
} mapping[] = {
{ ingame_overlay::ToggleKey::ALT , XK_Alt_L },
{ ingame_overlay::ToggleKey::CTRL , XK_Control_L },
{ ingame_overlay::ToggleKey::SHIFT, XK_Shift_L },
{ ingame_overlay::ToggleKey::TAB , XK_Tab },
{ ingame_overlay::ToggleKey::F1 , XK_F1 },
{ ingame_overlay::ToggleKey::F2 , XK_F2 },
{ ingame_overlay::ToggleKey::F3 , XK_F3 },
{ ingame_overlay::ToggleKey::F4 , XK_F4 },
{ ingame_overlay::ToggleKey::F5 , XK_F5 },
{ ingame_overlay::ToggleKey::F6 , XK_F6 },
{ ingame_overlay::ToggleKey::F7 , XK_F7 },
{ ingame_overlay::ToggleKey::F8 , XK_F8 },
{ ingame_overlay::ToggleKey::F9 , XK_F9 },
{ ingame_overlay::ToggleKey::F10 , XK_F10 },
{ ingame_overlay::ToggleKey::F11 , XK_F11 },
{ ingame_overlay::ToggleKey::F12 , XK_F12 },
};
for (auto const& item : mapping)
{
if (item.lib_key == k)
return item.native_key;
}
return 0;
}
bool GetKeyState(Display* d, KeySym keySym, char szKey[32])
{
int iKeyCodeToFind = XKeysymToKeycode(d, keySym);
return szKey[iKeyCodeToFind / 8] & (1 << (iKeyCodeToFind % 8));
}
bool X11_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback, std::set<ingame_overlay::ToggleKey> const& toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
if (!_key_combination_callback)
{
SPDLOG_ERROR("Failed to hook X11: No key combination callback.");
return false;
}
if (toggle_keys.empty())
{
SPDLOG_ERROR("Failed to hook X11: No key combination.");
return false;
}
void* hX11 = System::Library::GetLibraryHandle(DLL_NAME); void* hX11 = System::Library::GetLibraryHandle(DLL_NAME);
if (hX11 == nullptr) if (hX11 == nullptr)
{ {
@ -60,6 +112,16 @@ bool X11_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback)
SPDLOG_INFO("Hooked X11"); SPDLOG_INFO("Hooked X11");
_KeyCombinationCallback = std::move(_key_combination_callback); _KeyCombinationCallback = std::move(_key_combination_callback);
for (auto& key : toggle_keys)
{
uint32_t k = ToggleKeyToNativeKey(key);
if (k != 0)
{
_NativeKeyCombination.insert(k);
}
}
_Hooked = true; _Hooked = true;
UnhookAll(); UnhookAll();
@ -140,33 +202,47 @@ int X11_Hook::_CheckForOverlay(Display *d, int num_events)
static Time prev_time = {}; static Time prev_time = {};
X11_Hook* inst = Inst(); X11_Hook* inst = Inst();
if( inst->_Initialized ) char szKey[32];
if( _Initialized )
{ {
XEvent event; XEvent event;
while(num_events) while(num_events)
{ {
bool skip_input = inst->_KeyCombinationCallback(false); bool skip_input = _KeyCombinationCallback(false);
XPeekEvent(d, &event); XPeekEvent(d, &event);
ImGui_ImplX11_EventHandler(event); ImGui_ImplX11_EventHandler(event);
// Is the event is a key press // Is the event is a key press
if (event.type == KeyPress) if (event.type == KeyPress || event.type == KeyRelease)
{ {
// Tab is pressed and was not pressed before XQueryKeymap(d, szKey);
if (event.xkey.keycode == XKeysymToKeycode(d, XK_Tab) && event.xkey.state & ShiftMask) int key_count = 0;
for (auto const& key : inst->_NativeKeyCombination)
{ {
// if key TAB is held, don't make the overlay flicker :p if (GetKeyState(d, key, szKey))
if (event.xkey.time != prev_time) ++key_count;
}
if (key_count == inst->_NativeKeyCombination.size())
{// All shortcut keys are pressed
if (!inst->_KeyCombinationPushed)
{
if (inst->_KeyCombinationCallback(true))
{ {
skip_input = true; skip_input = true;
inst->_KeyCombinationCallback(true); // Save the last known cursor pos when opening the overlay
// so we can spoof the GetCursorPos return value.
//inst->GetCursorPos(&inst->_SavedCursorPos);
}
inst->_KeyCombinationPushed = true;
} }
} }
} else
else if(event.type == KeyRelease && event.xkey.keycode == XKeysymToKeycode(d, XK_Tab))
{ {
prev_time = event.xkey.time; inst->_KeyCombinationPushed = false;
}
} }
if (!skip_input || !IgnoreEvent(event)) if (!skip_input || !IgnoreEvent(event))
@ -215,6 +291,7 @@ X11_Hook::X11_Hook() :
_Initialized(false), _Initialized(false),
_Hooked(false), _Hooked(false),
_GameWnd(0), _GameWnd(0),
_KeyCombinationPushed(false),
XEventsQueued(nullptr), XEventsQueued(nullptr),
XPending(nullptr) XPending(nullptr)
{ {
@ -241,3 +318,4 @@ std::string X11_Hook::GetLibraryName() const
{ {
return LibraryName; return LibraryName;
} }

View File

@ -41,6 +41,12 @@ private:
bool _Initialized; bool _Initialized;
Window _GameWnd; Window _GameWnd;
// In (bool): Is toggle wanted
// Out(bool): Is the overlay visible, if true, inputs will be disabled
std::function<bool(bool)> _KeyCombinationCallback;
std::set<uint32_t> _NativeKeyCombination;
bool _KeyCombinationPushed;
// Functions // Functions
X11_Hook(); X11_Hook();
int _CheckForOverlay(Display *d, int num_events); int _CheckForOverlay(Display *d, int num_events);
@ -52,8 +58,6 @@ private:
static int MyXEventsQueued(Display * display, int mode); static int MyXEventsQueued(Display * display, int mode);
static int MyXPending(Display* display); static int MyXPending(Display* display);
std::function<bool(bool)> _KeyCombinationCallback;
public: public:
std::string LibraryName; std::string LibraryName;
@ -65,7 +69,7 @@ public:
Window GetGameWnd() const{ return _GameWnd; } Window GetGameWnd() const{ return _GameWnd; }
bool StartHook(std::function<bool(bool)>& key_combination_callback); bool StartHook(std::function<bool(bool)>& key_combination_callback, std::set<ingame_overlay::ToggleKey> const& toggle_keys);
static X11_Hook* Inst(); static X11_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;
}; };

View File

@ -213,6 +213,7 @@ void Steam_Overlay::UnSetupOverlay()
void Steam_Overlay::HookReady(bool ready) void Steam_Overlay::HookReady(bool ready)
{ {
PRINT_DEBUG("%s %u\n", __FUNCTION__, ready);
{ {
// TODO: Uncomment this and draw our own cursor (cosmetics) // TODO: Uncomment this and draw our own cursor (cosmetics)
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
@ -223,9 +224,6 @@ void Steam_Overlay::HookReady(bool ready)
io.IniFilename = NULL; io.IniFilename = NULL;
is_ready = ready; is_ready = ready;
if (is_ready) {
CreateFonts();
}
} }
} }
@ -710,18 +708,64 @@ void Steam_Overlay::BuildNotifications(int width, int height)
void Steam_Overlay::CreateFonts() void Steam_Overlay::CreateFonts()
{ {
//TODO: remove from dx_whatever hook
if(ImGui::GetCurrentContext() == nullptr)
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImFontConfig fontcfg; ImFontConfig fontcfg;
float font_size = 16.0;
fontcfg.OversampleH = fontcfg.OversampleV = 1; fontcfg.OversampleH = fontcfg.OversampleV = 1;
fontcfg.PixelSnapH = true; fontcfg.PixelSnapH = true;
fontcfg.GlyphRanges = io.Fonts->GetGlyphRangesDefault(); fontcfg.SizePixels = font_size;
font_default = io.Fonts->AddFontDefault(&fontcfg); ImFontGlyphRangesBuilder font_builder;
font_notif = io.Fonts->AddFontDefault(&fontcfg); for (auto & x : achievements) {
font_builder.AddText(x.title.c_str());
font_builder.AddText(x.description.c_str());
}
font_builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
ImVector<ImWchar> ranges;
font_builder.BuildRanges(&ranges);
bool need_extra_fonts = false;
for (auto &x : ranges) {
if (x > 0xFF) {
need_extra_fonts = true;
break;
}
}
fontcfg.GlyphRanges = ranges.Data;
ImFont *font = NULL;
#if defined(__WINDOWS__)
font = io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\micross.ttf", font_size, &fontcfg);
#endif
if (!font) {
font = io.Fonts->AddFontDefault(&fontcfg);
}
font_notif = font_default = font;
if (need_extra_fonts) {
PRINT_DEBUG("loading extra fonts\n");
fontcfg.MergeMode = true;
#if defined(__WINDOWS__)
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\simsun.ttc", font_size, &fontcfg);
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\malgun.ttf", font_size, &fontcfg);
#endif
}
io.Fonts->Build();
ImGuiStyle& style = ImGui::GetStyle(); ImGuiStyle& style = ImGui::GetStyle();
style.WindowRounding = 0.0; // Disable round window style.WindowRounding = 0.0; // Disable round window
reset_LastError();
} }
// Try to make this function as short as possible or it might affect game's fps. // Try to make this function as short as possible or it might affect game's fps.
@ -893,7 +937,7 @@ void Steam_Overlay::OverlayProc()
bool show_warning = local_save || warning_forced || appid == 0; bool show_warning = local_save || warning_forced || appid == 0;
if (show_warning) { if (show_warning) {
ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 64, ImGui::GetFontSize() * 64), ImVec2(8192, 8192)); ImGui::SetNextWindowSizeConstraints(ImVec2(ImGui::GetFontSize() * 32, ImGui::GetFontSize() * 32), ImVec2(8192, 8192));
ImGui::SetNextWindowFocus(); ImGui::SetNextWindowFocus();
if (ImGui::Begin("WARNING", &show_warning)) { if (ImGui::Begin("WARNING", &show_warning)) {
if (appid == 0) { if (appid == 0) {
@ -955,10 +999,45 @@ void Steam_Overlay::Callback(Common_Message *msg)
void Steam_Overlay::RunCallbacks() void Steam_Overlay::RunCallbacks()
{ {
if (!achievements.size()) {
Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats;
uint32 achievements_num = steamUserStats->GetNumAchievements();
if (achievements_num) {
PRINT_DEBUG("POPULATE OVERLAY ACHIEVEMENTS\n");
for (unsigned i = 0; i < achievements_num; ++i) {
Overlay_Achievement ach;
ach.name = steamUserStats->GetAchievementName(i);
ach.title = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "name");
ach.description = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "desc");
const char *hidden = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "hidden");
if (strlen(hidden) && hidden[0] == '1') {
ach.hidden = true;
} else {
ach.hidden = false;
}
bool achieved = false;
uint32 unlock_time = 0;
if (steamUserStats->GetAchievementAndUnlockTime(ach.name.c_str(), &achieved, &unlock_time)) {
ach.achieved = achieved;
ach.unlock_time = unlock_time;
} else {
ach.achieved = false;
ach.unlock_time = 0;
}
achievements.push_back(ach);
}
PRINT_DEBUG("POPULATE OVERLAY ACHIEVEMENTS DONE\n");
}
}
if (!Ready() && future_renderer.valid()) { if (!Ready() && future_renderer.valid()) {
if (future_renderer.wait_for(std::chrono::milliseconds{0}) == std::future_status::ready) { if (future_renderer.wait_for(std::chrono::milliseconds{0}) == std::future_status::ready) {
_renderer = future_renderer.get(); _renderer = future_renderer.get();
PRINT_DEBUG("got renderer %p\n", _renderer); PRINT_DEBUG("got renderer %p\n", _renderer);
CreateFonts();
} }
} }
@ -967,7 +1046,8 @@ void Steam_Overlay::RunCallbacks()
_renderer->OverlayProc = std::bind(&Steam_Overlay::OverlayProc, this); _renderer->OverlayProc = std::bind(&Steam_Overlay::OverlayProc, this);
auto callback = std::bind(&Steam_Overlay::OpenOverlayHook, this, std::placeholders::_1); auto callback = std::bind(&Steam_Overlay::OpenOverlayHook, this, std::placeholders::_1);
PRINT_DEBUG("start renderer\n", _renderer); PRINT_DEBUG("start renderer\n", _renderer);
bool started = _renderer->StartHook(callback); std::set<ingame_overlay::ToggleKey> keys = {ingame_overlay::ToggleKey::SHIFT, ingame_overlay::ToggleKey::TAB};
bool started = _renderer->StartHook(callback, keys);
PRINT_DEBUG("tried to start renderer %u\n", started); PRINT_DEBUG("tried to start renderer %u\n", started);
} }
@ -1094,40 +1174,6 @@ void Steam_Overlay::RunCallbacks()
} }
has_friend_action.pop(); has_friend_action.pop();
} }
if (!achievements.size()) {
Steam_User_Stats* steamUserStats = get_steam_client()->steam_user_stats;
uint32 achievements_num = steamUserStats->GetNumAchievements();
if (achievements_num) {
PRINT_DEBUG("POPULATE OVERLAY ACHIEVEMENTS\n");
for (unsigned i = 0; i < achievements_num; ++i) {
Overlay_Achievement ach;
ach.name = steamUserStats->GetAchievementName(i);
ach.title = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "name");
ach.description = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "desc");
const char *hidden = steamUserStats->GetAchievementDisplayAttribute(ach.name.c_str(), "hidden");
if (strlen(hidden) && hidden[0] == '1') {
ach.hidden = true;
} else {
ach.hidden = false;
}
bool achieved = false;
uint32 unlock_time = 0;
if (steamUserStats->GetAchievementAndUnlockTime(ach.name.c_str(), &achieved, &unlock_time)) {
ach.achieved = achieved;
ach.unlock_time = unlock_time;
} else {
ach.achieved = false;
ach.unlock_time = 0;
}
achievements.push_back(ach);
}
PRINT_DEBUG("POPULATE OVERLAY ACHIEVEMENTS DONE\n");
}
}
} }
#endif #endif

View File

@ -123,8 +123,8 @@ class Steam_Overlay
std::recursive_mutex overlay_mutex; std::recursive_mutex overlay_mutex;
std::atomic<bool> i_have_lobby; std::atomic<bool> i_have_lobby;
std::future<Renderer_Hook*> future_renderer; std::future<ingame_overlay::Renderer_Hook*> future_renderer;
Renderer_Hook* _renderer; ingame_overlay::Renderer_Hook* _renderer;
Steam_Overlay(Steam_Overlay const&) = delete; Steam_Overlay(Steam_Overlay const&) = delete;
Steam_Overlay(Steam_Overlay&&) = delete; Steam_Overlay(Steam_Overlay&&) = delete;

View File

@ -35,7 +35,7 @@ inline void SafeRelease(T*& pUnk)
} }
} }
bool DX10_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool DX10_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -45,7 +45,7 @@ bool DX10_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -25,7 +25,7 @@
#include <dxgi1_2.h> #include <dxgi1_2.h>
class DX10_Hook : class DX10_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -64,7 +64,7 @@ public:
virtual ~DX10_Hook(); virtual ~DX10_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static DX10_Hook* Inst(); static DX10_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -45,7 +45,7 @@ static HRESULT GetDeviceAndCtxFromSwapchain(IDXGISwapChain* pSwapChain, ID3D11De
return ret; return ret;
} }
bool DX11_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool DX11_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -55,7 +55,7 @@ bool DX11_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -25,7 +25,7 @@
#include <dxgi1_2.h> #include <dxgi1_2.h>
class DX11_Hook : class DX11_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -65,7 +65,7 @@ public:
virtual ~DX11_Hook(); virtual ~DX11_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static DX11_Hook* Inst(); static DX11_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -35,7 +35,7 @@ inline void SafeRelease(T*& pUnk)
} }
} }
bool DX12_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool DX12_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -45,7 +45,7 @@ bool DX12_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -25,7 +25,7 @@
#include <dxgi1_4.h> #include <dxgi1_4.h>
class DX12_Hook : class DX12_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -127,7 +127,7 @@ public:
virtual ~DX12_Hook(); virtual ~DX12_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static DX12_Hook* Inst(); static DX12_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -36,7 +36,7 @@ inline void SafeRelease(T*& pUnk)
} }
} }
bool DX9_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool DX9_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -46,7 +46,7 @@ bool DX9_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -24,7 +24,7 @@
#include <d3d9.h> #include <d3d9.h>
class DX9_Hook : class DX9_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -63,7 +63,7 @@ public:
virtual ~DX9_Hook(); virtual ~DX9_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static DX9_Hook* Inst(); static DX9_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -27,7 +27,7 @@
OpenGL_Hook* OpenGL_Hook::_inst = nullptr; OpenGL_Hook* OpenGL_Hook::_inst = nullptr;
bool OpenGL_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool OpenGL_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
@ -37,7 +37,7 @@ bool OpenGL_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -22,7 +22,7 @@
#include "../internal_includes.h" #include "../internal_includes.h"
class OpenGL_Hook : class OpenGL_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -56,7 +56,7 @@ public:
virtual ~OpenGL_Hook(); virtual ~OpenGL_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static OpenGL_Hook* Inst(); static OpenGL_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -25,7 +25,7 @@
Vulkan_Hook* Vulkan_Hook::_inst = nullptr; Vulkan_Hook* Vulkan_Hook::_inst = nullptr;
bool Vulkan_Hook::StartHook(std::function<bool(bool)> key_combination_callback) bool Vulkan_Hook::StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys)
{ {
SPDLOG_WARN("Vulkan overlay is not yet supported."); SPDLOG_WARN("Vulkan overlay is not yet supported.");
return false; return false;
@ -37,7 +37,7 @@ bool Vulkan_Hook::StartHook(std::function<bool(bool)> key_combination_callback)
return false; return false;
} }
if (!Windows_Hook::Inst()->StartHook(key_combination_callback)) if (!Windows_Hook::Inst()->StartHook(key_combination_callback, toggle_keys))
return false; return false;
_WindowsHooked = true; _WindowsHooked = true;

View File

@ -24,7 +24,7 @@
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
class Vulkan_Hook : class Vulkan_Hook :
public Renderer_Hook, public ingame_overlay::Renderer_Hook,
public Base_Hook public Base_Hook
{ {
public: public:
@ -54,7 +54,7 @@ public:
virtual ~Vulkan_Hook(); virtual ~Vulkan_Hook();
virtual bool StartHook(std::function<bool(bool)> key_combination_callback); virtual bool StartHook(std::function<bool(bool)> key_combination_callback, std::set<ingame_overlay::ToggleKey> toggle_keys);
virtual bool IsStarted(); virtual bool IsStarted();
static Vulkan_Hook* Inst(); static Vulkan_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;

View File

@ -29,10 +29,55 @@ constexpr decltype(Windows_Hook::DLL_NAME) Windows_Hook::DLL_NAME;
Windows_Hook* Windows_Hook::_inst = nullptr; Windows_Hook* Windows_Hook::_inst = nullptr;
bool Windows_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback) int ToggleKeyToNativeKey(ingame_overlay::ToggleKey k)
{
struct {
ingame_overlay::ToggleKey lib_key;
int native_key;
} mapping[] = {
{ ingame_overlay::ToggleKey::ALT , VK_MENU },
{ ingame_overlay::ToggleKey::CTRL , VK_CONTROL },
{ ingame_overlay::ToggleKey::SHIFT, VK_SHIFT },
{ ingame_overlay::ToggleKey::TAB , VK_TAB },
{ ingame_overlay::ToggleKey::F1 , VK_F1 },
{ ingame_overlay::ToggleKey::F2 , VK_F2 },
{ ingame_overlay::ToggleKey::F3 , VK_F3 },
{ ingame_overlay::ToggleKey::F4 , VK_F4 },
{ ingame_overlay::ToggleKey::F5 , VK_F5 },
{ ingame_overlay::ToggleKey::F6 , VK_F6 },
{ ingame_overlay::ToggleKey::F7 , VK_F7 },
{ ingame_overlay::ToggleKey::F8 , VK_F8 },
{ ingame_overlay::ToggleKey::F9 , VK_F9 },
{ ingame_overlay::ToggleKey::F10 , VK_F10 },
{ ingame_overlay::ToggleKey::F11 , VK_F11 },
{ ingame_overlay::ToggleKey::F12 , VK_F12 },
};
for (auto const& item : mapping)
{
if (item.lib_key == k)
return item.native_key;
}
return 0;
}
bool Windows_Hook::StartHook(std::function<bool(bool)>& _key_combination_callback, std::set<ingame_overlay::ToggleKey> const& toggle_keys)
{ {
if (!_Hooked) if (!_Hooked)
{ {
if (!_key_combination_callback)
{
SPDLOG_ERROR("Failed to hook Windows: No key combination callback.");
return false;
}
if (toggle_keys.empty())
{
SPDLOG_ERROR("Failed to hook Windows: No key combination.");
return false;
}
void* hUser32 = System::Library::GetLibraryHandle(DLL_NAME); void* hUser32 = System::Library::GetLibraryHandle(DLL_NAME);
if (hUser32 == nullptr) if (hUser32 == nullptr)
{ {
@ -71,6 +116,15 @@ bool Windows_Hook::StartHook(std::function<bool(bool)>& _key_combination_callbac
SPDLOG_INFO("Hooked Windows"); SPDLOG_INFO("Hooked Windows");
_KeyCombinationCallback = std::move(_key_combination_callback); _KeyCombinationCallback = std::move(_key_combination_callback);
for (auto& key : toggle_keys)
{
uint32_t k = ToggleKeyToNativeKey(key);
if (k != 0)
{
_NativeKeyCombination.insert(k);
}
}
BeginHook(); BeginHook();
HookFuncs( HookFuncs(
std::make_pair<void**, void*>(&(PVOID&)GetRawInputBuffer, &Windows_Hook::MyGetRawInputBuffer), std::make_pair<void**, void*>(&(PVOID&)GetRawInputBuffer, &Windows_Hook::MyGetRawInputBuffer),
@ -134,12 +188,9 @@ bool Windows_Hook::PrepareForOverlay(HWND hWnd)
POINT pos; POINT pos;
if (this->GetCursorPos(&pos) && ScreenToClient(hWnd, &pos)) if (this->GetCursorPos(&pos) && ScreenToClient(hWnd, &pos))
{ {
io.MousePos = ImVec2((float)pos.x, (float)pos.y); io.AddMousePosEvent((float)pos.x, (float)pos.y);
} }
io.KeyCtrl = (this->GetKeyState(VK_CONTROL) & 0x8000) != 0;
io.KeyShift = (this->GetKeyState(VK_SHIFT) & 0x8000) != 0;
io.KeyAlt = (this->GetKeyState(VK_MENU) & 0x8000) != 0;
return true; return true;
} }
@ -219,13 +270,18 @@ LRESULT CALLBACK Windows_Hook::HookWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
if (inst->_Initialized) if (inst->_Initialized)
{ {
// Is the event is a key press // Is the event is a key press
if (uMsg == WM_KEYDOWN) if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)
{ {
// Tab is pressed and was not pressed before int key_count = 0;
if (wParam == VK_TAB && !(lParam & (1 << 30))) for (auto const& key : inst->_NativeKeyCombination)
{ {
// If Left Shift is pressed if (inst->GetAsyncKeyState(key) & (1 << 15))
if (inst->GetAsyncKeyState(VK_LSHIFT) & (1 << 15)) ++key_count;
}
if (key_count == inst->_NativeKeyCombination.size())
{// All shortcut keys are pressed
if (!inst->_KeyCombinationPushed)
{ {
if (inst->_KeyCombinationCallback(true)) if (inst->_KeyCombinationCallback(true))
{ {
@ -234,11 +290,12 @@ LRESULT CALLBACK Windows_Hook::HookWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
// so we can spoof the GetCursorPos return value. // so we can spoof the GetCursorPos return value.
inst->GetCursorPos(&inst->_SavedCursorPos); inst->GetCursorPos(&inst->_SavedCursorPos);
} }
inst->_KeyCombinationPushed = true;
}
}
else else
{ {
clean_keys = true; inst->_KeyCombinationPushed = false;
}
}
} }
} }
@ -248,7 +305,8 @@ LRESULT CALLBACK Windows_Hook::HookWndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
if (clean_keys) if (clean_keys)
{ {
auto& io = ImGui::GetIO(); auto& io = ImGui::GetIO();
memset(io.KeysDown, 0, sizeof(io.KeysDown)); io.ClearInputKeys();
io.ClearInputCharacters();
} }
return 0; return 0;
} }
@ -369,6 +427,7 @@ Windows_Hook::Windows_Hook() :
_RecurseCallCount(0), _RecurseCallCount(0),
_GameHwnd(nullptr), _GameHwnd(nullptr),
_GameWndProc(nullptr), _GameWndProc(nullptr),
_KeyCombinationPushed(false),
GetRawInputBuffer(nullptr), GetRawInputBuffer(nullptr),
GetRawInputData(nullptr), GetRawInputData(nullptr),
GetKeyState(nullptr), GetKeyState(nullptr),

View File

@ -38,6 +38,12 @@ private:
WNDPROC _GameWndProc; WNDPROC _GameWndProc;
POINT _SavedCursorPos; POINT _SavedCursorPos;
// In (bool): Is toggle wanted
// Out(bool): Is the overlay visible, if true, inputs will be disabled
std::function<bool(bool)> _KeyCombinationCallback;
std::set<int> _NativeKeyCombination;
bool _KeyCombinationPushed;
// Functions // Functions
Windows_Hook(); Windows_Hook();
@ -59,10 +65,6 @@ private:
static BOOL WINAPI MyGetCursorPos(LPPOINT lpPoint); static BOOL WINAPI MyGetCursorPos(LPPOINT lpPoint);
static BOOL WINAPI MySetCursorPos(int X, int Y); static BOOL WINAPI MySetCursorPos(int X, int Y);
// In (bool): Is toggle wanted
// Out(bool): Is the overlay visible, if true, inputs will be disabled
std::function<bool(bool)> _KeyCombinationCallback;
public: public:
std::string LibraryName; std::string LibraryName;
@ -75,7 +77,7 @@ public:
HWND GetGameHwnd() const; HWND GetGameHwnd() const;
WNDPROC GetGameWndProc() const; WNDPROC GetGameWndProc() const;
bool StartHook(std::function<bool(bool)>& key_combination_callback); bool StartHook(std::function<bool(bool)>& key_combination_callback, std::set<ingame_overlay::ToggleKey> const& toggle_keys);
static Windows_Hook* Inst(); static Windows_Hook* Inst();
virtual std::string GetLibraryName() const; virtual std::string GetLibraryName() const;
}; };

View File

@ -17,6 +17,9 @@ import os
import sys import sys
import json import json
import urllib.request import urllib.request
import urllib.error
import threading
import queue
prompt_for_unavailable = True prompt_for_unavailable = True
@ -82,7 +85,49 @@ def get_stats_schema(client, game_id, owner_id):
client.send(message) client.send(message)
return client.wait_msg(EMsg.ClientGetUserStatsResponse, timeout=5) return client.wait_msg(EMsg.ClientGetUserStatsResponse, timeout=5)
def download_achievement_images(game_id, image_names, output_folder):
q = queue.Queue()
def downloader_thread():
while True:
name = q.get()
succeeded = False
if name is None:
q.task_done()
return
for u in ["https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/", "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"]:
url = "{}{}/{}".format(u, game_id, name)
try:
with urllib.request.urlopen(url) as response:
image_data = response.read()
with open(os.path.join(output_folder, name), "wb") as f:
f.write(image_data)
succeeded = True
break
except urllib.error.HTTPError as e:
print("HTTPError downloading", url, e.code)
except urllib.error.URLError as e:
print("URLError downloading", url, e.code)
if not succeeded:
print("error could not download", name)
q.task_done()
num_threads = 5
for i in range(num_threads):
threading.Thread(target=downloader_thread, daemon=True).start()
for name in image_names:
q.put(name)
q.join()
for i in range(num_threads):
q.put(None)
q.join()
def generate_achievement_stats(client, game_id, output_directory, backup_directory): def generate_achievement_stats(client, game_id, output_directory, backup_directory):
achievement_images_dir = os.path.join(output_directory, "achievement_images")
images_to_download = []
steam_id_list = TOP_OWNER_IDS + [client.steam_id] steam_id_list = TOP_OWNER_IDS + [client.steam_id]
for x in steam_id_list: for x in steam_id_list:
out = get_stats_schema(client, game_id, x) out = get_stats_schema(client, game_id, x)
@ -91,10 +136,20 @@ def generate_achievement_stats(client, game_id, output_directory, backup_directo
with open(os.path.join(backup_directory, 'UserGameStatsSchema_{}.bin'.format(appid)), 'wb') as f: with open(os.path.join(backup_directory, 'UserGameStatsSchema_{}.bin'.format(appid)), 'wb') as f:
f.write(out.body.schema) f.write(out.body.schema)
achievements, stats = achievements_gen.generate_stats_achievements(out.body.schema, output_directory) achievements, stats = achievements_gen.generate_stats_achievements(out.body.schema, output_directory)
return for ach in achievements:
if "icon" in ach:
images_to_download.append(ach["icon"])
if "icon_gray" in ach:
images_to_download.append(ach["icon_gray"])
break
else: else:
print("no schema", out) print("no schema", out)
if (len(images_to_download) > 0):
if not os.path.exists(achievement_images_dir):
os.makedirs(achievement_images_dir)
download_achievement_images(game_id, images_to_download, achievement_images_dir)
def get_ugc_info(client, published_file_id): def get_ugc_info(client, published_file_id):
return client.send_um_and_wait('PublishedFile.GetDetails#1', { return client.send_um_and_wait('PublishedFile.GetDetails#1', {
'publishedfileids': [published_file_id], 'publishedfileids': [published_file_id],
@ -173,6 +228,13 @@ for appid in appids:
game_info_common = game_info["common"] game_info_common = game_info["common"]
if "community_visible_stats" in game_info_common: if "community_visible_stats" in game_info_common:
generate_achievement_stats(client, appid, out_dir, backup_dir) generate_achievement_stats(client, appid, out_dir, backup_dir)
if "languages" in game_info_common:
with open(os.path.join(out_dir, "supported_languages.txt"), 'w') as f:
languages = game_info_common["languages"]
for l in languages:
if languages[l] != "0":
f.write("{}\n".format(l))
with open(os.path.join(out_dir, "steam_appid.txt"), 'w') as f: with open(os.path.join(out_dir, "steam_appid.txt"), 'w') as f:
f.write(str(appid)) f.write(str(appid))
@ -187,6 +249,7 @@ for appid in appids:
dlc_config_list = [] dlc_config_list = []
dlc_list, depot_app_list = get_dlc(game_info) dlc_list, depot_app_list = get_dlc(game_info)
dlc_infos_backup = ""
if (len(dlc_list) > 0): if (len(dlc_list) > 0):
dlc_raw = client.get_product_info(apps=dlc_list)["apps"] dlc_raw = client.get_product_info(apps=dlc_list)["apps"]
for dlc in dlc_raw: for dlc in dlc_raw:
@ -194,6 +257,7 @@ for appid in appids:
dlc_config_list.append((dlc, dlc_raw[dlc]["common"]["name"])) dlc_config_list.append((dlc, dlc_raw[dlc]["common"]["name"]))
except: except:
dlc_config_list.append((dlc, None)) dlc_config_list.append((dlc, None))
dlc_infos_backup = json.dumps(dlc_raw, indent=4)
with open(os.path.join(out_dir, "DLC.txt"), 'w', encoding="utf-8") as f: with open(os.path.join(out_dir, "DLC.txt"), 'w', encoding="utf-8") as f:
for x in dlc_config_list: for x in dlc_config_list:
@ -218,7 +282,21 @@ for appid in appids:
if (controller_type in ["controller_xbox360", "controller_xboxone"] and (("default" in enabled_branches) or ("public" in enabled_branches))): if (controller_type in ["controller_xbox360", "controller_xboxone"] and (("default" in enabled_branches) or ("public" in enabled_branches))):
parse_controller_vdf.generate_controller_config(out_vdf.decode('utf-8'), os.path.join(out_dir, "controller")) parse_controller_vdf.generate_controller_config(out_vdf.decode('utf-8'), os.path.join(out_dir, "controller"))
config_generated = True config_generated = True
if "steamcontrollertouchconfigdetails" in game_info["config"]:
controller_details = game_info["config"]["steamcontrollertouchconfigdetails"]
for id in controller_details:
details = controller_details[id]
controller_type = ""
enabled_branches = ""
if "controller_type" in details:
controller_type = details["controller_type"]
if "enabled_branches" in details:
enabled_branches = details["enabled_branches"]
print(id, controller_type)
out_vdf = download_published_file(client, int(id), os.path.join(backup_dir, controller_type + str(id)))
game_info_backup = json.dumps(game_info, indent=4) game_info_backup = json.dumps(game_info, indent=4)
with open(os.path.join(backup_dir, "product_info.json"), "w") as f: with open(os.path.join(backup_dir, "product_info.json"), "w") as f:
f.write(game_info_backup) f.write(game_info_backup)
with open(os.path.join(backup_dir, "dlc_product_info.json"), "w") as f:
f.write(dlc_infos_backup)

View File

@ -75,7 +75,7 @@ def generate_stats_achievements(schema, config_directory):
with open(os.path.join(config_directory, "stats.txt"), 'w') as f: with open(os.path.join(config_directory, "stats.txt"), 'w') as f:
f.write(output_stats) f.write(output_stats)
return (output_ach, output_stats) return (achievements_out, stats_out)
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) < 2: if len(sys.argv) < 2: