diff --git a/include/DLL_VERSION.H b/include/DLL_VERSION.H index e206b1b..d45392d 100644 --- a/include/DLL_VERSION.H +++ b/include/DLL_VERSION.H @@ -3,8 +3,8 @@ #define TBF_MAJOR 0 #define TBF_MINOR 9 -#define TBF_BUILD 3 -#define TBF_REV 2 +#define TBF_BUILD 4 +#define TBF_REV 0 diff --git a/include/config.h b/include/config.h index dbed65e..07647bb 100644 --- a/include/config.h +++ b/include/config.h @@ -28,6 +28,22 @@ extern std::wstring TBF_VER_STR; extern std::wstring DEFAULT_BK2; +struct keybind_s +{ + std::wstring human_readable; + + struct { + bool ctrl, + shift, + alt; + }; + + BYTE vKey; + + void parse (void); + void update (void); +}; + struct tbf_config_t { struct { @@ -99,10 +115,20 @@ struct tbf_config_t } input; struct { - std::wstring swap_keys = L""; - bool swap_wasd = false; + std::wstring swap_keys = L""; + bool swap_wasd = false; + + keybind_s hudless { L"Ctrl+Shift+F10", { true, true, false }, VK_F10 }, + screenshot { L"F10", { false, false, false }, VK_F10 }, + config_menu { L"Ctrl+Shift+Backspace", { true, true, false }, VK_BACK }; + } keyboard; + struct { + bool keep = true; + bool import_to_steam = true; + } screenshots; + struct { std::wstring version = TBF_VER_STR; diff --git a/src/ImGui/control_panel.cpp b/src/ImGui/control_panel.cpp index 99fc3fb..9f69815 100644 --- a/src/ImGui/control_panel.cpp +++ b/src/ImGui/control_panel.cpp @@ -228,6 +228,76 @@ TBFix_GamepadConfigDlg (void) } } +DWORD TBFix_Modal = 0UL; + +void +TBFix_KeybindDialog (keybind_s* keybind) +{ + const float font_size = ImGui::GetFont ()->FontSize * ImGui::GetIO ().FontGlobalScale; + static bool was_open = false; + + static BYTE bind_keys [256] = { 0 }; + + ImGui::SetNextWindowSizeConstraints ( ImVec2 (font_size * 9, font_size * 3), ImVec2 (font_size * 30, font_size * 6)); + + if (ImGui::BeginPopupModal ("Keyboard Binding", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ShowBorders)) + { + ImGui::GetIO ().WantCaptureKeyboard = false; + + int keys = 256; + + if (! was_open) + { + //keybind->vKey = 0; + + for (int i = 0 ; i < keys ; i++ ) bind_keys [i] = GetKeyState (i); + + was_open = true; + } + + BYTE active_keys [256]; + + for (int i = 0 ; i < keys ; i++ ) active_keys [i] = GetKeyState (i); + + if (memcmp (active_keys, bind_keys, keys)) + { + int i = 0; + + for (i = (VK_MENU + 1); i < keys; i++) + { + if ( i == VK_LCONTROL || i == VK_RCONTROL || + i == VK_LSHIFT || i == VK_RSHIFT || + i == VK_LMENU || i == VK_RMENU ) + continue; + + if ( active_keys [i] != bind_keys [i] ) + break; + } + + if (i != keys) + { + keybind->vKey = i; + was_open = false; + ImGui::CloseCurrentPopup (); + } + + //memcpy (bind_keys, active_keys, keys); + } + + keybind->ctrl = GetAsyncKeyState (VK_CONTROL); + keybind->shift = GetAsyncKeyState (VK_SHIFT); + keybind->alt = GetAsyncKeyState (VK_MENU); + + keybind->update (); + + ImGui::Text ("Binding: %ws", keybind->human_readable.c_str ()); + + TBFix_Modal = timeGetTime (); + + ImGui::EndPopup (); + } +} + #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) IMGUI_API @@ -727,6 +797,44 @@ TBFix_DrawConfigUI (void) ImGui::TreePop ( ); } + if (ImGui::CollapsingHeader ("Screenshots")) + { + if (ImGui::IsItemHovered ()) + { + char szWorkingDir [MAX_PATH]; + GetCurrentDirectoryA (MAX_PATH, szWorkingDir); + + ImGui::SetTooltip ("Screenshots are stored in '%s\\Screenshots'", szWorkingDir); + } + + ImGui::TreePush (""); + + ImGui::Text ("No HUD Keybinding: %ws", config.keyboard.hudless.human_readable.c_str ()); + + if (ImGui::IsItemClicked ()) { + ImGui::OpenPopup ("Keyboard Binding"); + } + + TBFix_KeybindDialog (&config.keyboard.hudless); + + ImGui::Checkbox ("Import Screenshot to Steam", &config.screenshots.import_to_steam); + + if (config.screenshots.import_to_steam) + { + ImGui::TreePush (""); + ImGui::Checkbox ("Keep High Quality PNG Screenshot After Import", &config.screenshots.keep); + + if (ImGui::IsItemHovered ()) + ImGui::SetTooltip ("Screenshots will register in Steam much quicker with this disabled."); + + ImGui::TreePop (); + } + else + config.screenshots.keep = true; + + ImGui::TreePop (); + } + if (ImGui::CollapsingHeader ("Audio")) { ImGui::TreePush (""); diff --git a/src/config.cpp b/src/config.cpp index 8b9674a..1640a20 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -82,6 +82,13 @@ struct { tbf::ParameterFloat* ui_scale; } render; +struct { + tbf::ParameterBool* keep; + tbf::ParameterBool* add_to_steam; + tbf::ParameterStringW* hudless_keybind; + tbf::ParameterStringW* regular_keybind; +} screenshots; + struct { tbf::ParameterBool* remaster; tbf::ParameterBool* uncompressed; @@ -399,6 +406,41 @@ TBF_LoadConfig (std::wstring name) L"ImGui.Settings", L"ShowOSDDisclaimer" ); + + screenshots.hudless_keybind = + static_cast + (g_ParameterFactory.create_parameter ( + L"HUDless Screenshot Keybinding") + ); + + screenshots.hudless_keybind->register_to_ini ( + render_ini, + L"Screenshot.Capture", + L"KeybindNoHUD" ); + + screenshots.keep = + static_cast + (g_ParameterFactory.create_parameter ( + L"Keep Original (High Quality) Screenshots") + ); + + screenshots.keep->register_to_ini ( + render_ini, + L"Screenshot.Capture", + L"KeepOriginal" ); + + screenshots.add_to_steam = + static_cast + (g_ParameterFactory.create_parameter ( + L"Add Screenshot to Steam Library") + ); + + screenshots.add_to_steam->register_to_ini ( + render_ini, + L"Screenshot.Capture", + L"AddToSteamLibrary" ); + + framerate.replace_limiter = static_cast (g_ParameterFactory.create_parameter ( @@ -545,6 +587,12 @@ TBF_LoadConfig (std::wstring name) framerate.tolerance->load (config.framerate.tolerance); //framerate.limit_addr->load (config.framerate.limit_addr); + screenshots.add_to_steam->load (config.screenshots.import_to_steam); + screenshots.keep->load (config.screenshots.keep); + screenshots.hudless_keybind->load (config.keyboard.hudless.human_readable); + + config.keyboard.hudless.parse (); + textures.remaster->load (config.textures.remaster); textures.uncompressed->load (config.textures.uncompressed); textures.lod_bias->load (config.textures.lod_bias); @@ -600,6 +648,12 @@ TBF_SaveConfig (std::wstring name, bool close_config) input.gamepad.texture_set->store (config.input.gamepad.texture_set); input.gamepad.virtual_controllers->store (config.input.gamepad.virtual_controllers); + config.keyboard.hudless.update (); + + screenshots.add_to_steam->store (config.screenshots.import_to_steam); + screenshots.keep->store (config.screenshots.keep); + screenshots.hudless_keybind->store (config.keyboard.hudless.human_readable); + keyboard.swap_wasd->store (config.keyboard.swap_wasd); keyboard.swap_keys->store (config.keyboard.swap_keys); @@ -663,4 +717,118 @@ TBF_SaveConfig (std::wstring name, bool close_config) keyboard_ini = nullptr; } } +} + + + +#include + +std::unordered_map humanKeyNameToVirtKeyCode; +std::unordered_map virtKeyCodeToHumanKeyName; + +#include + +void +keybind_s::update (void) +{ + human_readable = L""; + + std::wstring key_name = virtKeyCodeToHumanKeyName [vKey]; + + if (! key_name.length ()) + return; + + std::queue words; + + if (ctrl) + words.push (L"Ctrl"); + + if (alt) + words.push (L"Alt"); + + if (shift) + words.push (L"Shift"); + + words.push (key_name); + + while (! words.empty ()) + { + human_readable += words.front (); + words.pop (); + + if (! words.empty ()) + human_readable += L"+"; + } +} + +void +keybind_s::parse (void) +{ + vKey = 0x00; + + static bool init = false; + + if (! init) + { + for (int i = 0; i < 0xFF; i++) + { + wchar_t name [32] = { L'\0' }; + + GetKeyNameText ( (MapVirtualKey (i, MAPVK_VK_TO_VSC) & 0xFF) << 16, + name, + 32 ); + + if (i != VK_CONTROL && i != VK_MENU && i != VK_SHIFT && i != VK_OEM_PLUS && i != VK_OEM_MINUS) + { + humanKeyNameToVirtKeyCode [ name] = (BYTE)i; + virtKeyCodeToHumanKeyName [(BYTE)i] = name; + } + } + + humanKeyNameToVirtKeyCode [L"Plus"] = VK_OEM_PLUS; + humanKeyNameToVirtKeyCode [L"Minus"] = VK_OEM_MINUS; + humanKeyNameToVirtKeyCode [L"Ctrl"] = VK_CONTROL; + humanKeyNameToVirtKeyCode [L"Alt"] = VK_MENU; + humanKeyNameToVirtKeyCode [L"Shift"] = VK_SHIFT; + + virtKeyCodeToHumanKeyName [VK_CONTROL] = L"Ctrl"; + virtKeyCodeToHumanKeyName [VK_MENU] = L"Alt"; + virtKeyCodeToHumanKeyName [VK_SHIFT] = L"Shift"; + virtKeyCodeToHumanKeyName [VK_OEM_PLUS] = L"Plus"; + virtKeyCodeToHumanKeyName [VK_OEM_MINUS] = L"Minus"; + + init = true; + } + + wchar_t wszKeyBind [128] = { L'\0' }; + + wcsncpy (wszKeyBind, human_readable.c_str (), 128); + + wchar_t* wszBuf; + wchar_t* wszTok = std::wcstok (wszKeyBind, L"+", &wszBuf); + + ctrl = false; + alt = false; + shift = false; + + if (wszTok == nullptr) + { + vKey = humanKeyNameToVirtKeyCode [wszKeyBind]; + } + + while (wszTok) + { + BYTE vKey_ = humanKeyNameToVirtKeyCode [wszTok]; + + if (vKey_ == VK_CONTROL) + ctrl = true; + else if (vKey_ == VK_SHIFT) + shift = true; + else if (vKey_ == VK_MENU) + alt = true; + else + vKey = vKey_; + + wszTok = std::wcstok (nullptr, L"+", &wszBuf); + } } \ No newline at end of file diff --git a/src/input.cpp b/src/input.cpp index a594cd7..e1aaff9 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -93,15 +93,33 @@ SK_TBF_PluginKeyPress ( BOOL Control, BOOL Alt, BYTE vkCode ) { + extern DWORD TBFix_Modal; + + if (timeGetTime () < (TBFix_Modal + 500UL)) { + SK_PluginKeyPress_Original (Control, Shift, Alt, vkCode); + return; + } + SK_ICommandProcessor& command = *SK_GetCommandProcessor (); - if (Control && Shift) - { - if (vkCode == VK_F10) - tbf::RenderFix::tex_mgr.queueScreenshot (L"Screenshot.dds"); + if ( vkCode == config.keyboard.hudless.vKey && + (Control != 0) == config.keyboard.hudless.ctrl && + (Shift != 0) == config.keyboard.hudless.shift && + (Alt != 0) == config.keyboard.hudless.alt ) + tbf::RenderFix::tex_mgr.queueScreenshot (L"NOT_IMPLEMENTED", true); - else if (vkCode == VK_DELETE) { +#if 0 + else if ( vkCode == config.keyboard.screenshot.vKey && + Control == config.keyboard.screenshot.ctrl && + Shift == config.keyboard.screenshot.shift && + Alt == config.keyboard.screenshot.alt ) + tbf::RenderFix::tex_mgr.queueScreenshot (L"NOT_IMPLEMENTED", false); +#endif + + else if (Control && Shift) + { + if (vkCode == VK_DELETE) { config.render.osd_disclaimer = (! config.render.osd_disclaimer); } diff --git a/src/textures.cpp b/src/textures.cpp index 1c84349..5b0947d 100644 --- a/src/textures.cpp +++ b/src/textures.cpp @@ -47,6 +47,7 @@ #include #include +#include #define TBFIX_TEXTURE_DIR L"TBFix_Res" #define TBFIX_TEXTURE_EXT L".dds" @@ -68,6 +69,18 @@ typedef HRESULT (STDMETHODCALLTYPE *SetRenderState_pfn) DWORD Value ); +typedef HRESULT (WINAPI *D3DXLoadSurfaceFromSurface_pfn) +( + _In_ LPDIRECT3DSURFACE9 pDestSurface, + _In_ const PALETTEENTRY *pDestPalette, + _In_ const RECT *pDestRect, + _In_ LPDIRECT3DSURFACE9 pSrcSurface, + _In_ const PALETTEENTRY *pSrcPalette, + _In_ const RECT *pSrcRect, + _In_ DWORD Filter, + _In_ D3DCOLOR ColorKey +); + static D3DXSaveTextureToFile_pfn D3DXSaveTextureToFile = nullptr; static D3DXCreateTextureFromFileInMemoryEx_pfn D3DXCreateTextureFromFileInMemoryEx_Original = nullptr; @@ -101,7 +114,7 @@ std::set incomplete_textures; // Cleanup -std::queue screenshots; +std::queue screenshots_to_delete; tbf::RenderFix::pad_buttons_t tbf::RenderFix::pad_buttons; @@ -3200,10 +3213,10 @@ tbf::RenderFix::TextureManager::Shutdown (void) time_saved / frame_time ); tex_log->close (); - while (! screenshots.empty ()) + while (! screenshots_to_delete.empty ()) { - std::wstring file_to_delete = screenshots.front (); - screenshots.pop (); + std::wstring file_to_delete = screenshots_to_delete.front (); + screenshots_to_delete.pop (); DeleteFileW (file_to_delete.c_str ()); } @@ -4236,41 +4249,167 @@ tbf::RenderFix::TextureManager::takeScreenshot (IDirect3DSurface9* pSurf) { static int count = 0; - wchar_t wszOut [MAX_PATH] = { }; - char szOut [MAX_PATH] = { }; + wchar_t wszOut [MAX_PATH] = { }; + wchar_t wszThumb [MAX_PATH] = { }; + char szOut [MAX_PATH] = { }; + char szThumb [MAX_PATH] = { }; + + CreateDirectoryW (L"Screenshots", nullptr); + + time_t now; + struct tm* now_tm; + + time (&now); + now_tm = localtime (&now); + + const wchar_t* wszTimestamp = config.screenshots.keep ? + L"Screenshots\\TBFix_NoHUD_%Y_%m_%d-%H'%M'%S.png" : + L"Screenshots\\TBFix_NoHUD_%Y_%m_%d-%H'%M'%S.tga"; - _swprintf (wszOut, L"TBFix_Screenshot%lu.tga", count ); + wcsftime (wszOut, MAX_PATH, wszTimestamp, now_tm); + _swprintf (wszThumb, L"Screenshots\\TBFix_NoHUD_Thumbnail%lu.tga", count++ ); GetCurrentDirectoryA (MAX_PATH, szOut); - sprintf ( szOut, "%s\\TBFix_Screenshot%lu.tga", szOut, count++); + GetCurrentDirectoryA (MAX_PATH, szThumb); + + sprintf ( szOut, "%s\\%ws", szOut, wszOut); + sprintf ( szThumb, "%s\\%ws", szThumb, wszThumb); + + if (! config.screenshots.keep) + screenshots_to_delete.push (wszOut); + + if (config.screenshots.import_to_steam) + screenshots_to_delete.push (wszThumb); want_screenshot = false; D3DSURFACE_DESC desc; pSurf->GetDesc (&desc); - HRESULT hr = - D3DXSaveSurfaceToFileW ( wszOut, - D3DXIFF_TGA, pSurf, - nullptr, nullptr ); - - if (SUCCEEDED (hr)) + static D3DXLoadSurfaceFromSurface_pfn + D3DXLoadSurfaceFromSurface = + (D3DXLoadSurfaceFromSurface_pfn) + GetProcAddress ( d3dx9_43_dll, "D3DXLoadSurfaceFromSurface" ); + + extern HMODULE hInjectorDLL; + + typedef void (WINAPI *SK_SteamAPI_AddScreenshotToLibrary_pfn) + (const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight); + + static SK_SteamAPI_AddScreenshotToLibrary_pfn + SK_SteamAPI_AddScreenshotToLibrary = + (SK_SteamAPI_AddScreenshotToLibrary_pfn) + GetProcAddress ( hInjectorDLL, + "SK_SteamAPI_AddScreenshotToLibrary" ); + + IDirect3DSurface9* pSurfScreenshot = nullptr; + + if (SUCCEEDED ( D3D9CreateRenderTarget ( tbf::RenderFix::pDevice, + desc.Width, desc.Height, + desc.Format, desc.MultiSampleType, desc.MultiSampleQuality, + TRUE, + &pSurfScreenshot, nullptr + ) + ) + ) { - extern HMODULE hInjectorDLL; + if ( SUCCEEDED ( D3D9StretchRect ( tbf::RenderFix::pDevice, + pSurf, nullptr, + pSurfScreenshot, nullptr, + D3DTEXF_NONE + ) + ) + ) + { + struct screenshot_params_s { + D3DSURFACE_DESC desc; + IDirect3DSurface9* pSurf; + std::wstring name_w, thumbnail_w; + std::string name, thumbnail; + } *params = + + new screenshot_params_s { + desc, + pSurfScreenshot, + wszOut, wszThumb, + szOut, szThumb + }; + + CreateThread ( nullptr, 0, + [](LPVOID user) -> + DWORD + { + D3DXIMAGE_FILEFORMAT format = config.screenshots.keep ? + D3DXIFF_PNG : D3DXIFF_TGA; - typedef void (WINAPI *SK_SteamAPI_AddScreenshotToLibrary_pfn) - (const char *pchFilename, const char *pchThumbnailFilename, int nWidth, int nHeight); + screenshot_params_s* params = + (screenshot_params_s *)user; - static SK_SteamAPI_AddScreenshotToLibrary_pfn - SK_SteamAPI_AddScreenshotToLibrary = - (SK_SteamAPI_AddScreenshotToLibrary_pfn) - GetProcAddress ( hInjectorDLL, - "SK_SteamAPI_AddScreenshotToLibrary" ); + HRESULT hr = + D3DXSaveSurfaceToFileW ( params->name_w.c_str (), + format, params->pSurf, + nullptr, nullptr ); - SK_SteamAPI_AddScreenshotToLibrary (szOut, nullptr, desc.Width, desc.Height); + bool with_thumbnail = false; - screenshots.push (wszOut); + if (config.screenshots.import_to_steam && SUCCEEDED (hr)) + { + CComPtr pSurfThumb = nullptr; + + if (SUCCEEDED ( D3D9CreateRenderTarget ( tbf::RenderFix::pDevice, + 200, (UINT)(((float)params->desc.Height / (float)params->desc.Width) * 200), + params->desc.Format, + D3DMULTISAMPLE_NONE, 0, + TRUE, &pSurfThumb, + nullptr + ) + ) + ) + { + // Slightly higher quality filtering than if we just used StretchRect with a linear filter,+ + // (200 x ...) pixels still sucks, but we can make it suck just a little bit less. + if (SUCCEEDED ( D3DXLoadSurfaceFromSurface ( pSurfThumb, + nullptr, nullptr, + params->pSurf, + nullptr, nullptr, + D3DX_DEFAULT, 0xFF000000 + ) + ) + ) + { + if ( SUCCEEDED ( + D3DXSaveSurfaceToFileW ( params->thumbnail_w.c_str (), + D3DXIFF_TGA, pSurfThumb, + nullptr, nullptr + ) + ) + ) + { + SK_SteamAPI_AddScreenshotToLibrary (params->name.c_str (), params->thumbnail.c_str (), params->desc.Width, params->desc.Height); + with_thumbnail = true; + } + } + } + + if (! with_thumbnail) + SK_SteamAPI_AddScreenshotToLibrary (params->name.c_str (), nullptr, params->desc.Width, params->desc.Height); + } + + params->pSurf->Release (); + + delete params; + + CloseHandle (GetCurrentThread ()); + + return 0; + }, + + params, + 0, + nullptr + ); + } } - return hr; + return S_OK; } \ No newline at end of file diff --git a/version.ini b/version.ini index ff281c0..10dc22b 100644 Binary files a/version.ini and b/version.ini differ