Explorar el Código

Merge pull request #30 from TotalTaxAmount/linux-support

Linux support
stan hace 3 semanas
padre
commit
6f54354a98

+ 2 - 0
.clangd

@@ -0,0 +1,2 @@
+CompileFlags:
+  CompilationDatabase: "build"

+ 1 - 1
.editorconfig

@@ -2,7 +2,7 @@ root = true
 
 [*]
 charset = utf-8
-end_of_line = crlf
+end_of_line = lf
 insert_final_newline = true
 trim_trailing_whitespace = true
 

+ 13 - 2
CMakeLists.txt

@@ -15,8 +15,6 @@ file(GLOB_RECURSE VENDOR_SOURCES CONFIGURE_DEPENDS
     "Sunrise/vendor/detours/*.h"
     "Sunrise/vendor/imgui/*.cpp"
     "Sunrise/vendor/imgui/*.h"
-    "Sunrise/vendor/*/*LICENSE*"
-    "Sunrise/vendor/*/*LICENSE.txt"
 )
 file(GLOB_RECURSE RESOURCE_FILES CONFIGURE_DEPENDS
     "Sunrise/resources/*.rc"
@@ -44,10 +42,12 @@ endif()
 target_include_directories(steam_api64
     PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/Sunrise/src"
 )
+
 # sunrise.rc names its payloads relative to itself, so its directory has to be on the include path.
 target_include_directories(steam_api64
     PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/Sunrise/resources"
 )
+
 target_include_directories(steam_api64 SYSTEM PRIVATE
     "${CMAKE_CURRENT_SOURCE_DIR}/Sunrise/vendor/detours"
     "${CMAKE_CURRENT_SOURCE_DIR}/Sunrise/vendor/imgui"
@@ -96,6 +96,17 @@ if(MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
     )
 endif()
 
+if(CMAKE_HOST_LINUX AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    target_compile_options(steam_api64 PRIVATE
+        $<$<CONFIG:Debug>:/clang:-gdwarf>
+        $<$<CONFIG:Debug>:/clang:-g>
+    )
+
+    target_link_options(steam_api64 PRIVATE
+        $<$<CONFIG:Debug>:/DEBUG:DWARF>
+    )
+endif()
+
 target_link_libraries(steam_api64 PRIVATE
     kernel32 bcrypt user32 gdi32 dwmapi
     d3dcompiler shell32 ws2_32 synchronization

+ 4 - 0
Sunrise/Sunrise.vcxproj

@@ -120,6 +120,7 @@
     <ClCompile Include="src\core\settings\state\key_bindings_parser.cpp" />
     <ClCompile Include="src\core\settings\state\social_parser.cpp" />
     <ClCompile Include="src\core\runtime\core_runtime.cpp" />
+    <ClCompile Include="src\core\runtime\host_environment.cpp" />
     <ClCompile Include="src\core\ui\modules\ui_module_descriptor.cpp" />
     <ClCompile Include="src\core\ui\modules\registry\ui_module_registry.cpp" />
     <ClCompile Include="src\core\ui\modules\logs\logs_module_runtime.cpp" />
@@ -247,6 +248,7 @@
     <ClCompile Include="src\client\hooks\network\http\http_worker_completion.cpp" />
     <ClCompile Include="src\client\hooks\network\signon\signon_readiness_replacements.cpp" />
     <ClCompile Include="src\client\patterns\game\signon\signon_readiness_signature_bytes.cpp" />
+    <ClCompile Include="src\client\graphics\wine_compat.cpp" />
     <ClCompile Include="src\core\settings\client\external\client_external_settings_parser.cpp" />
     <ClCompile Include="src\client\targets\game\game_retail_log_targets.cpp" />
     <ClCompile Include="src\client\patterns\game\retail_log\retail_log_signature_bytes.cpp" />
@@ -706,6 +708,7 @@
     <ClInclude Include="src\core\settings\server\definition.h" />
     <ClInclude Include="src\core\settings\steam\definition.h" />
     <ClInclude Include="src\core\runtime\core_runtime.h" />
+    <ClInclude Include="src\core\runtime\host_environment.h" />
     <ClInclude Include="src\core\ui\imgui_user_config.h" />
     <ClInclude Include="src\core\ui\modules\ui_module_descriptor.h" />
     <ClInclude Include="src\core\ui\modules\registry\ui_module_registry.h" />
@@ -1091,6 +1094,7 @@
     <ClInclude Include="src\client\patterns\game\config_getter\config_getter_signature_bytes.h" />
     <ClInclude Include="src\client\patterns\game\packages\package_key_signature_bytes.h" />
     <ClInclude Include="src\client\patterns\game\signon\signon_readiness_signature_bytes.h" />
+    <ClInclude Include="src\client\graphics\wine_compat.h" />
     <ClInclude Include="src\middleware\content\packages\tables\spawn_reader.h" />
     <ClInclude Include="src\middleware\content\packages\tables\component_container_reader.h" />
     <ClInclude Include="src\client\patterns\image_scan.h" />

+ 2 - 2
Sunrise/resources/sunrise.rc

@@ -3,8 +3,8 @@
 #include "resource.h"
 
 IDR_DEFAULT_SETTINGS RCDATA "default_settings.json"
-IDR_IMGUI_LICENSE RCDATA "..\\vendor\\imgui\\LICENSE.txt"
-IDR_DETOURS_LICENSE RCDATA "..\\vendor\\detours\\LICENSE.md"
+IDR_IMGUI_LICENSE RCDATA "../vendor/imgui/LICENSE.txt"
+IDR_DETOURS_LICENSE RCDATA "../vendor/detours/LICENSE.md"
 
 VS_VERSION_INFO VERSIONINFO
 FILEVERSION SUNRISE_VER_MAJOR, SUNRISE_VER_MINOR, SUNRISE_VER_PATCH, SUNRISE_VER_BUILD

+ 14 - 0
Sunrise/src/client/graphics/wine_compat.cpp

@@ -0,0 +1,14 @@
+#include "wine_compat.h"
+
+#include "windows.h"
+
+namespace sunrise::client::graphics {
+
+// Helper function to let wine know that we will be using the display
+void initialize_wine_display() {
+    HDC hdc = GetDC(NULL);
+    if (hdc) {
+        ReleaseDC(NULL, hdc);
+    }
+}
+} // namespace sunrise::client::graphics

+ 5 - 0
Sunrise/src/client/graphics/wine_compat.h

@@ -0,0 +1,5 @@
+#pragma once
+
+namespace sunrise::client::graphics {
+void initialize_wine_display();
+}

+ 36 - 4
Sunrise/src/client/hooks/egress/lifecycle/egress_guard_exports.cpp

@@ -1,7 +1,10 @@
+#include <string>
+
 #include "../dns/egress_dns_replacements.h"
 #include "../extensions/egress_extension_replacements.h"
 #include "../resolver/replacements.h"
 #include "../winsock/replacements.h"
+#include "core/runtime/host_environment.h"
 #include "internal.h"
 
 namespace sunrise::client::hooks::egress::lifecycle {
@@ -21,12 +24,28 @@ export_definition(ModuleSlot module, const char* name, Function replacement) noe
 
 } // namespace
 
+// Dummy functions, these are not exported by wine so we provide at least 5 bytes for detours to
+// copy
+__declspec(noinline) int WSAAPI Dummy_WSAConnectByList() {
+    volatile int dummy = 0;
+    dummy += 1;
+    return -1; // SOCKET_ERROR
+}
+
+__declspec(noinline) int WSAAPI Dummy_GetAddrInfoExA() {
+    volatile int dummy = 0;
+    dummy += 1;
+    return 11001; // WSAHOST_NOT_FOUND
+}
+
 /** Finds the whole Windows SDK egress surface for one atomic Detours batch. */
 bool resolve_specs(std::span<hooking::detour::Spec, kHookCount> specs,
                    std::span<const char*, kHookCount> names,
                    std::span<bool, kHookCount> resolved,
                    std::size_t& count) noexcept {
     count = 0;
+    const bool is_wine = sunrise::core::runtime::is_wine();
+
     const std::array<ExportDefinition, kHookCount> exports{
         export_definition(ModuleSlot::winsock, "connect", &winsock::connection::connect_socket),
         export_definition(
@@ -71,14 +90,27 @@ bool resolve_specs(std::span<hooking::detour::Spec, kHookCount> specs,
     for (std::size_t index = 0; index < exports.size(); ++index) {
         const ExportDefinition& definition = exports[index];
         const HMODULE module = module_handle(definition.module);
-        void* const target = reinterpret_cast<void*>(GetProcAddress(module, definition.name));
+        void* target = reinterpret_cast<void*>(GetProcAddress(module, definition.name));
         names[index] = definition.name;
+
+        if (target == nullptr && is_wine) {
+            const std::string_view funcName(definition.name);
+            if (funcName == "WSAConnectByList") {
+                target = reinterpret_cast<void*>(&Dummy_WSAConnectByList);
+            } else if (funcName == "GetAddrInfoExA") {
+                target = reinterpret_cast<void*>(&Dummy_GetAddrInfoExA);
+            }
+        }
+
         resolved[index] = target != nullptr;
+
         if (target == nullptr) {
-            // Older Windows builds cannot expose an egress path through an absent DnsQueryRaw.
-            return index == kRequiredHookCount;
+            if (index < kRequiredHookCount) {
+                return false;
+            }
+            continue;
         }
-        specs[index] = hooking::detour::Spec{target, definition.replacement};
+        specs[count] = hooking::detour::Spec{target, definition.replacement};
         count = index + 1;
     }
     return count >= kRequiredHookCount;

+ 18 - 0
Sunrise/src/core/runtime/host_environment.cpp

@@ -0,0 +1,18 @@
+#include "host_environment.h"
+
+#include "windows.h"
+
+namespace sunrise::core::runtime {
+bool is_wine() {
+    static const bool is_linux = []() -> bool {
+        HMODULE hntdll = GetModuleHandleW(L"ntdll.dll");
+        if (!hntdll) {
+            return false;
+        }
+        return GetProcAddress(hntdll, "wine_get_version")
+               != nullptr; // Exported by wine to identify itself but not by real windows
+    }();
+
+    return is_linux;
+}
+} // namespace sunrise::core::runtime

+ 5 - 0
Sunrise/src/core/runtime/host_environment.h

@@ -0,0 +1,5 @@
+#pragma once
+
+namespace sunrise::core::runtime {
+bool is_wine();
+}

+ 5 - 0
Sunrise/src/steam/runtime/steam_lifecycle.cpp

@@ -2,11 +2,13 @@
 
 #include <atomic>
 
+#include "../../client/graphics/wine_compat.h"
 #include "../../client/hooks/egress/runtime.h"
 #include "../../client/hooks/package_trust/package_trust_bypass.h"
 #include "../../client/runtime/runtime.h"
 #include "../../core/logging/log.h"
 #include "../../core/runtime/core_runtime.h"
+#include "../../core/runtime/host_environment.h"
 #include "callbacks/callback_registry.h"
 #include "context/steam_context_state.h"
 #include "internal.h"
@@ -47,6 +49,9 @@ bool g_platformActivationAttempted{};
 
 /** Starts the Steam shim and its exported state. */
 bool initialize(void* module) noexcept {
+    if (core::runtime::is_wine()) {
+        client::graphics::initialize_wine_display();
+    }
     if (!client::hooks::egress::install()) {
         return false;
     }