-
-
Notifications
You must be signed in to change notification settings - Fork 215
feat(windows): add WINE and Proton runtime context #1995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
7f8e655
293a4a9
d2696f4
be92308
28b9db1
c736f8c
1a30d04
7d00bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| #include "sentry_os.h" | ||
| #include "sentry_path.h" | ||
| #include "sentry_slice.h" | ||
| #include "sentry_string.h" | ||
| #if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_WINDOWS) | ||
|
|
@@ -177,6 +178,201 @@ sentry__get_windows_version(windows_version_t *win_ver) | |
| return 1; | ||
| } | ||
|
|
||
| static bool | ||
| string_ends_with(const char *value, size_t value_len, const char *suffix) | ||
| { | ||
| const size_t suffix_len = strlen(suffix); | ||
| return value_len >= suffix_len | ||
| && memcmp(value + value_len - suffix_len, suffix, suffix_len) == 0; | ||
| } | ||
|
|
||
| static sentry_path_t * | ||
| make_wine_path(const char *path, const char *suffix) | ||
| { | ||
| sentry_stringbuilder_t sb; | ||
| sentry__stringbuilder_init(&sb); | ||
| if (sentry__stringbuilder_append(&sb, "Z:") | ||
| || sentry__stringbuilder_append(&sb, path) | ||
| || sentry__stringbuilder_append(&sb, suffix)) { | ||
| sentry__stringbuilder_cleanup(&sb); | ||
| return NULL; | ||
| } | ||
| return sentry__path_from_str_owned(sentry__stringbuilder_into_string(&sb)); | ||
| } | ||
|
|
||
| static char * | ||
| read_wine_file(const sentry_path_t *path) | ||
| { | ||
| return path ? sentry__path_read_to_buffer(path, NULL) : NULL; | ||
| } | ||
|
|
||
| static char * | ||
| get_proton_version(bool *is_proton) | ||
| { | ||
| *is_proton = false; | ||
| char *compat_path | ||
| = sentry__string_from_wstr(_wgetenv(L"STEAM_COMPAT_DATA_PATH")); | ||
| if (sentry__string_empty(compat_path)) { | ||
| sentry_free(compat_path); | ||
| return NULL; | ||
| } | ||
|
|
||
| sentry_path_t *config_path = make_wine_path(compat_path, "/config_info"); | ||
| sentry_free(compat_path); | ||
| char *config = config_path ? read_wine_file(config_path) : NULL; | ||
| sentry__path_free(config_path); | ||
| if (!config) { | ||
| return NULL; | ||
| } | ||
|
|
||
| char *fonts_path = strchr(config, '\n'); | ||
| if (!fonts_path) { | ||
| sentry_free(config); | ||
| return NULL; | ||
| } | ||
| fonts_path++; | ||
| while (*fonts_path == ' ' || *fonts_path == '\t') { | ||
| fonts_path++; | ||
| } | ||
| size_t fonts_path_len = strcspn(fonts_path, "\r\n"); | ||
| while (fonts_path_len > 0 | ||
| && (fonts_path[fonts_path_len - 1] == ' ' | ||
| || fonts_path[fonts_path_len - 1] == '\t')) { | ||
| fonts_path_len--; | ||
| } | ||
|
|
||
| const char *fonts_suffix = NULL; | ||
| if (string_ends_with(fonts_path, fonts_path_len, "/files/share/fonts/")) { | ||
| fonts_suffix = "/files/share/fonts/"; | ||
| } else if (string_ends_with( | ||
| fonts_path, fonts_path_len, "/dist/share/fonts/")) { | ||
| fonts_suffix = "/dist/share/fonts/"; | ||
| } | ||
| if (!fonts_suffix) { | ||
| sentry_free(config); | ||
| return NULL; | ||
| } | ||
|
|
||
| const size_t root_len = fonts_path_len - strlen(fonts_suffix); | ||
| char *proton_root = sentry__string_clone_n(fonts_path, root_len); | ||
| sentry_free(config); | ||
| if (!proton_root) { | ||
| return NULL; | ||
| } | ||
|
|
||
| sentry_path_t *proton_path = make_wine_path(proton_root, "/proton"); | ||
| *is_proton = proton_path && sentry__path_is_file(proton_path); | ||
| sentry__path_free(proton_path); | ||
|
|
||
| sentry_path_t *version_path = make_wine_path(proton_root, "/version"); | ||
| sentry_free(proton_root); | ||
| char *version_file = version_path ? read_wine_file(version_path) : NULL; | ||
| sentry__path_free(version_path); | ||
| if (!version_file) { | ||
| return NULL; | ||
| } | ||
|
|
||
| // Format: "<timestamp> <git-tag>", e.g. "1769167055 proton-10.0-4". | ||
| char *version = strpbrk(version_file, " \t"); | ||
| if (!version) { | ||
| sentry_free(version_file); | ||
| return NULL; | ||
| } | ||
| while (*version == ' ' || *version == '\t') { | ||
| version++; | ||
| } | ||
| size_t version_len = strcspn(version, "\r\n"); | ||
| while (version_len > 0 | ||
| && (version[version_len - 1] == ' ' | ||
| || version[version_len - 1] == '\t')) { | ||
| version_len--; | ||
| } | ||
|
|
||
| char *result | ||
| = version_len ? sentry__string_clone_n(version, version_len) : NULL; | ||
| sentry_free(version_file); | ||
| return result; | ||
| } | ||
|
|
||
| static bool | ||
| string_starts_with(const char *value, const char *prefix) | ||
| { | ||
| const size_t value_len = strlen(value); | ||
| const size_t prefix_len = strlen(prefix); | ||
| return value_len >= prefix_len && memcmp(value, prefix, prefix_len) == 0; | ||
| } | ||
|
|
||
| typedef const char *(CDECL *sentry__wine_get_version_t)(void); | ||
|
|
||
| static sentry_value_t | ||
| make_wine_context(sentry__wine_get_version_t wine_get_version, | ||
| const char *proton_version, bool is_proton) | ||
| { | ||
| if (!wine_get_version) { | ||
| return sentry_value_new_null(); | ||
| } | ||
|
|
||
| const char *runtime_name = "Wine"; | ||
| const char *runtime_version = wine_get_version(); | ||
| if (!sentry__string_empty(proton_version)) { | ||
| runtime_version = proton_version; | ||
| if (is_proton) { | ||
| if (string_starts_with(proton_version, "proton-")) { | ||
| runtime_name = "Proton"; | ||
| runtime_version += strlen("proton-"); | ||
| } else if (string_starts_with(proton_version, "experimental-")) { | ||
| runtime_name = "Proton Experimental"; | ||
| runtime_version += strlen("experimental-"); | ||
| } else if (string_starts_with(proton_version, "GE-Proton")) { | ||
| runtime_name = "GE-Proton"; | ||
| runtime_version += strlen("GE-Proton"); | ||
|
sentry[bot] marked this conversation as resolved.
|
||
| } else if (string_starts_with(proton_version, "hotfix-")) { | ||
| runtime_name = "Proton Hotfix"; | ||
| runtime_version += strlen("hotfix-"); | ||
| } else { | ||
| runtime_name = "Proton Custom"; | ||
| } | ||
| } | ||
| } | ||
| if (sentry__string_empty(runtime_version)) { | ||
| return sentry_value_new_null(); | ||
| } | ||
|
|
||
| sentry_value_t context = sentry_value_new_object(); | ||
| if (sentry_value_is_null(context)) { | ||
| return context; | ||
| } | ||
| sentry_value_set_by_key( | ||
| context, "type", sentry_value_new_string("runtime")); | ||
| sentry_value_set_by_key( | ||
| context, "name", sentry_value_new_string(runtime_name)); | ||
| sentry_value_set_by_key( | ||
| context, "version", sentry_value_new_string(runtime_version)); | ||
| sentry_value_freeze(context); | ||
| return context; | ||
| } | ||
|
|
||
| sentry_value_t | ||
| sentry__get_wine_context(void) | ||
| { | ||
| const HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); | ||
| if (!ntdll) { | ||
| return sentry_value_new_null(); | ||
| } | ||
|
|
||
| const sentry__wine_get_version_t wine_get_version | ||
| = (sentry__wine_get_version_t)GetProcAddress(ntdll, "wine_get_version"); | ||
| if (!wine_get_version) { | ||
| return sentry_value_new_null(); | ||
| } | ||
| bool is_proton = false; | ||
| char *proton_version = get_proton_version(&is_proton); | ||
|
Comment on lines
+368
to
+369
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unlike the Godot implementation, which calls Proton detection only after
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. I suggest returning early if the function address didn't resolve. const sentry__wine_get_version_t wine_get_version
= (sentry__wine_get_version_t)GetProcAddress(ntdll, "wine_get_version");
if (!wine_get_version) {
return sentry_value_new_null();
} |
||
| sentry_value_t context | ||
| = make_wine_context(wine_get_version, proton_version, is_proton); | ||
| sentry_free(proton_version); | ||
| return context; | ||
| } | ||
|
|
||
| # endif // !defined(SENTRY_PLATFORM_XBOX) | ||
|
|
||
| sentry_value_t | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, the code is hard follow and needs some context: what are we parsing and extracting. Not every reader will have an easy access to a Linux box with a proton installation. Let's add a comment.