123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- #include <jansson.h>
- #include <obs-module.h>
- #include <util/dstr.h>
- #include <util/platform.h>
- #include <util/windows/window-helpers.h>
- #include "compat-helpers.h"
- #include "compat-format-ver.h"
- enum match_flags {
- MATCH_EXE = 1 << 0,
- MATCH_TITLE = 1 << 1,
- MATCH_CLASS = 1 << 2,
- };
- static inline const char *get_string_val(json_t *entry, const char *key)
- {
- json_t *str_val = json_object_get(entry, key);
- if (!str_val || !json_is_string(str_val))
- return NULL;
- return json_string_value(str_val);
- }
- static inline int get_int_val(json_t *entry, const char *key)
- {
- json_t *integer_val = json_object_get(entry, key);
- if (!integer_val || !json_is_integer(integer_val))
- return 0;
- return (int)json_integer_value(integer_val);
- }
- static inline bool get_bool_val(json_t *entry, const char *key)
- {
- json_t *bool_val = json_object_get(entry, key);
- if (!bool_val || !json_is_boolean(bool_val))
- return false;
- return json_is_true(bool_val);
- }
- static json_t *open_json_file(const char *file)
- {
- char *file_data = os_quick_read_utf8_file(file);
- json_error_t error;
- json_t *root;
- json_t *list;
- int format_ver;
- if (!file_data)
- return NULL;
- root = json_loads(file_data, JSON_REJECT_DUPLICATES, &error);
- bfree(file_data);
- if (!root) {
- blog(LOG_WARNING,
- "compat-helpers.c: [open_json_file] "
- "Error reading JSON file (%d): %s",
- error.line, error.text);
- return NULL;
- }
- format_ver = get_int_val(root, "format_version");
- if (format_ver != COMPAT_FORMAT_VERSION) {
- blog(LOG_DEBUG,
- "compat-helpers.c: [open_json_file] "
- "Wrong format version (%d), expected %d",
- format_ver, COMPAT_FORMAT_VERSION);
- json_decref(root);
- return NULL;
- }
- list = json_object_get(root, "entries");
- if (list)
- json_incref(list);
- json_decref(root);
- if (!list) {
- blog(LOG_WARNING, "compat-helpers.c: [open_json_file] "
- "No compatibility list");
- return NULL;
- }
- return list;
- }
- static json_t *open_compat_file(void)
- {
- char *file;
- json_t *root = NULL;
- file = obs_module_config_path("compatibility.json");
- if (file) {
- root = open_json_file(file);
- bfree(file);
- }
- if (!root) {
- file = obs_module_file("compatibility.json");
- if (file) {
- root = open_json_file(file);
- bfree(file);
- }
- }
- return root;
- }
- static json_t *compat_entries;
- struct compat_result *check_compatibility(const char *win_title, const char *win_class, const char *exe,
- enum source_type type)
- {
- if (!compat_entries) {
- json_t *root = open_compat_file();
- if (!root)
- return NULL;
- compat_entries = root;
- }
- struct dstr message;
- struct compat_result *res = NULL;
- json_t *entry;
- size_t index;
- json_array_foreach (compat_entries, index, entry) {
- if (type == GAME_CAPTURE && !get_bool_val(entry, "game_capture"))
- continue;
- if (type == WINDOW_CAPTURE_WGC && !get_bool_val(entry, "window_capture_wgc"))
- continue;
- if (type == WINDOW_CAPTURE_BITBLT && !get_bool_val(entry, "window_capture"))
- continue;
- int match_flags = get_int_val(entry, "match_flags");
- const char *j_exe = get_string_val(entry, "executable");
- const char *j_title = get_string_val(entry, "window_title");
- const char *j_class = get_string_val(entry, "window_class");
- if ((match_flags & MATCH_CLASS) && (!win_class || strcmp(win_class, j_class) != 0))
- continue;
- if ((match_flags & MATCH_EXE) && (!exe || astrcmpi(exe, j_exe) != 0))
- continue;
- /* Title supports partial matches as some games append additional
- * information after the title, e.g., "Minecraft 1.18". */
- if ((match_flags & MATCH_TITLE) && (!win_title || astrcmpi_n(win_title, j_title, strlen(j_title)) != 0))
- continue;
- /* Attempt to translate and compile message */
- const char *key = get_string_val(entry, "translation_key");
- const char *msg = get_string_val(entry, "message");
- obs_module_get_string(key, &msg);
- dstr_init_copy(&message, msg);
- const char *name = get_string_val(entry, "name");
- /* Replace placeholders in generic messages */
- if (name && dstr_find(&message, "%") != NULL) {
- dstr_replace(&message, "%name%", name);
- }
- const char *url = get_string_val(entry, "url");
- /* Append clickable URL in Qt rich text */
- if (url && strncmp(url, "https://", 8) == 0) {
- dstr_catf(&message, "<br>\n<a href=\"%s\">%s</a>", url, url + 8);
- }
- res = bzalloc(sizeof(struct compat_result));
- res->severity = get_int_val(entry, "severity");
- res->message = message.array;
- break;
- }
- return res;
- }
- void compat_result_free(struct compat_result *res)
- {
- bfree(res->message);
- bfree(res);
- }
- void compat_json_free()
- {
- json_decref(compat_entries);
- }
|