diff --git a/meson.build b/meson.build index 6e5cc4d..d2526c0 100644 --- a/meson.build +++ b/meson.build @@ -144,6 +144,7 @@ executable( executable( 'gsr-game-tracker', [ + 'tools/gsr-game-tracker/native_games.c', 'tools/gsr-game-tracker/main.c' ], install : true diff --git a/tools/gsr-game-tracker/create-native-games-list.py b/tools/gsr-game-tracker/create-native-games-list.py new file mode 100755 index 0000000..1595d80 --- /dev/null +++ b/tools/gsr-game-tracker/create-native-games-list.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import sys +import os +import json + +# Skips steam games and games that end with .x86_64, .x86 or .x64 +def extract_non_tracked_games(filepath): + game_names = [] + with open(filepath, "r") as file: + is_steam_game = False + for line in file.readlines(): + if line.startswith("#"): + is_steam_game = "steam" in line + elif line.startswith("{") and not is_steam_game: + game_name = json.loads(line)["name"] + if not game_name.endswith(".x86_64") and not game_name.endswith(".x86") and not game_name.endswith(".x64"): + game_names.append(json.loads(line)["name"]) + return game_names + +def write_process_name_matcher_code_file(filepath, all_games): + games_by_len = {} + for game in all_games: + l = games_by_len.get(len(game), []) + l.append(game) + games_by_len[len(game)] = l + + with open(filepath, "w") as file: + file.write( + '#include "native_games.h"\n' + '\n' + '#include \n' + '\n' + '/* This file was auto-generated with create-native-games-list.py, do not edit manually! */\n' + '\n') + + for game_len in games_by_len: + games_quoted = ", ".join('"%s"' % game for game in games_by_len[game_len]) + file.write("static const char *process_names_len_%d[] = { %s, NULL };\n" % (game_len, games_quoted)) + + file.write("\n") + file.write( + "bool is_process_name_native_game(const char *process_name, size_t size) {\n" + " switch(size) {\n") + + for game_len in games_by_len: + file.write( + " case %d: {\n" + " for(size_t i = 0; process_names_len_%d[i] != NULL; ++i) {\n" + " if(memcmp(process_name, process_names_len_%d[i], size) == 0)\n" + " return true;\n" + " }\n" + " return false;\n" + " }\n" % (game_len, game_len, game_len)) + + file.write( + " default: {\n" + " return false;\n" + " }\n" + " }\n" + "}\n" + ) + +def add_games_from_rules_filepath(filepath): + games = extract_non_tracked_games(filepath) + print("Scanning file: %s, num games found: %d" % (filepath, len(games))) + return games + +def add_games_from_rules_recursive(dir): + games_added = [] + for folder, subfolders, files in os.walk(dir): + for file in files: + filepath = os.path.abspath(os.path.join(folder, file)) + games_added.extend(add_games_from_rules_filepath(filepath)) + return games_added + +def main(): + if len(sys.argv) != 2: + print("usage: create-native-games-list.py ") + exit(1) + + ananicy_root_path = sys.argv[1] + ananicy_conf_path = os.path.join(ananicy_root_path, "ananicy.conf") + if not os.path.exists(ananicy_conf_path): + print("error: the path this script was launched with is not the ananicy-rules root directory (file %s doesn't exist)" % ananicy_conf_path) + exit(1) + + ananicy_native_games_path = os.path.join(ananicy_root_path, "00-default", "Games", "linux-native") + ananicy_emulators_path = os.path.join(ananicy_root_path, "00-default", "Games", "emulators.rules") + + all_games = [] + all_games.extend(add_games_from_rules_recursive(ananicy_native_games_path)) + all_games.extend(add_games_from_rules_filepath(ananicy_emulators_path)) + + script_dir = os.path.dirname(os.path.realpath(__file__)) + write_process_name_matcher_code_file(os.path.join(script_dir, "native_games.c"), all_games) + +main() \ No newline at end of file diff --git a/tools/gsr-game-tracker/main.c b/tools/gsr-game-tracker/main.c index fa76164..584c5c0 100644 --- a/tools/gsr-game-tracker/main.c +++ b/tools/gsr-game-tracker/main.c @@ -1,3 +1,5 @@ +#include "native_games.h" + #include #include #include @@ -107,28 +109,30 @@ static bool memeql(const char *haystack, size_t haystack_size, const char *needl return haystack_size == needle_size && memcmp(haystack, needle, needle_size) == 0; } -static bool is_wine_binary(const char *argv0, size_t size) { - const char *base = memchr_reverse(argv0, '/', size); +static const char* process_get_basename(const char *argv0, size_t argv0_len, size_t *basename_len) { + *basename_len = 0; + const char *base = memchr_reverse(argv0, '/', argv0_len); base = base ? base + 1 : argv0; - const size_t base_len = argv0 + size - base; - return memeql(base, base_len, "wine") || - memeql(base, base_len, "wine64") || - memeql(base, base_len, "wine-preloader") || - memeql(base, base_len, "wine64-preloader"); + *basename_len = argv0 + argv0_len - base; + return base; } -static bool is_wine_server(const char *argv0, size_t size) { - const char *base = memchr_reverse(argv0, '/', size); - base = base ? base + 1 : argv0; - const size_t base_len = argv0 + size - base; - return memeql(base, base_len, "wineserver"); +static bool is_wine_binary(const char *process_basename, size_t process_basename_len) { + return memeql(process_basename, process_basename_len, "wine") || + memeql(process_basename, process_basename_len, "wine64") || + memeql(process_basename, process_basename_len, "wine-preloader") || + memeql(process_basename, process_basename_len, "wine64-preloader"); } -static bool has_game_arch_suffix(const char *argv0, size_t size) { +static bool is_wine_server(const char *process_basename, size_t process_basename_len) { + return memeql(process_basename, process_basename_len, "wineserver"); +} + +static bool has_game_arch_suffix(const char *process_basename, size_t process_basename_len) { static const char *suffixes[] = { ".x86_64", ".x64", ".x86" }; for (int i = 0; i < 3; i++) { const size_t slen = strlen(suffixes[i]); - if (size >= slen && memcmp(argv0 + size - slen, suffixes[i], slen) == 0) + if (process_basename_len >= slen && memcmp(process_basename + process_basename_len - slen, suffixes[i], slen) == 0) return true; } return false; @@ -173,8 +177,15 @@ static void check_process(pid_t pid) { const char *argv1 = cmdline_buf + argv0_len + 1; const size_t argv1_len = (size_t)cmd_n > argv0_len + 1 ? get_argv_len(argv1, cmd_n - (argv0_len + 1)) : 0; - if (cmd_n > 0 && (is_wine_binary(argv0, argv0_len) || has_game_arch_suffix(argv0, argv0_len) || has_wine_env) - && !is_wine_server(argv0, argv0_len) + size_t process_basename_len = 0; + const char *process_basename = process_get_basename(argv0, argv0_len, &process_basename_len); + + if (cmd_n > 0 + && (is_wine_binary(process_basename, process_basename_len) + || has_game_arch_suffix(process_basename, process_basename_len) + || has_wine_env + || is_process_name_native_game(process_basename, process_basename_len)) + && !is_wine_server(process_basename, process_basename_len) && !memeql(argv1, argv1_len, "--version") && (!is_steam_app || steam_overlay_game_id != NULL)) { diff --git a/tools/gsr-game-tracker/native_games.c b/tools/gsr-game-tracker/native_games.c new file mode 100644 index 0000000..87063d6 --- /dev/null +++ b/tools/gsr-game-tracker/native_games.c @@ -0,0 +1,174 @@ +#include "native_games.h" + +#include + +/* This file was auto-generated with create-native-games-list.py, do not edit manually! */ + +static const char *process_names_len_12[] = { "soh.appimage", "supertuxkart", "HytaleClient", "DDNet-Server", "Vintagestory", "ut-bin-amd64", "fs-uae-devel", "xenia_canary", "shadPS4:Main", NULL }; +static const char *process_names_len_5[] = { "sober", "DDNet", "zdoom", "cen64", "mesen", "higan", "rpcs3", NULL }; +static const char *process_names_len_14[] = { "sober_services", "chocolate-doom", "CodenameEngine", "quakespasm-svn", "2ship.appimage", "rott-shareware", "unreal-bin-x86", "PPSSPPHeadless", NULL }; +static const char *process_names_len_15[] = { "UnleashedRecomp", "CoherentUI_Host", "linux_64_client", "rott-registered", "UnrealLinux.bin", "Quake3-UrT.i386", "DuckStation-x64", NULL }; +static const char *process_names_len_10[] = { "sm64coopdx", "luanti.bin", "quakespasm", "srcds_i486", "rbdoom3bfg", "tyr-glqwcl", "ut-bin-x86", NULL }; +static const char *process_names_len_6[] = { "luanti", "Mobbos", "glhwcl", "hexen2", "dhewm3", "Funkin", "quake2", "gzdoom", "quake3", "ecwolf", "ut-bin", "fs-uae", "dosbox", NULL }; +static const char *process_names_len_11[] = { "PsychEngine", "devilutionx", "d1x-rebirth", "d2x-rebirth", "Kade Engine", "tyr-glquake", "xonotic-glx", "xonotic-sdl", "dolphin-emu", NULL }; +static const char *process_names_len_8[] = { "glhexen2", "alephone", "openrct2", "freesynd", "riot-web", "rottexpr", "tyr-qwcl", "ioquake3", "ironwail", "vkquake2", "cen64-qt", "mednafen", "PPSSPPQt", "pcsx2-qt", NULL }; +static const char *process_names_len_4[] = { "hwcl", "osu!", "rott", "xemu", "cemu", "suyu", "yuzu", "eden", "mame", NULL }; +static const char *process_names_len_9[] = { "openmohaa", "omohaaded", "berkelium", "cefsimple", "srcds_run", "tyr-quake", "iortcw-mp", "iortcw-sp", "xenia.exe", "retroarch", "mesen-git", "PPSSPPSDL", NULL }; +static const char *process_names_len_21[] = { "launch_openmohaa_base", NULL }; +static const char *process_names_len_29[] = { "launch_openmohaa_breakthrough", NULL }; +static const char *process_names_len_26[] = { "launch_openmohaa_spearhead", NULL }; +static const char *process_names_len_13[] = { "yamagi-quake2", NULL }; +static const char *process_names_len_17[] = { "yamagi-quake2-git", "Zelda64Recompiled", "xonotic-local-sdl", NULL }; +static const char *process_names_len_3[] = { "0ad", "etl", "RMG", NULL }; +static const char *process_names_len_2[] = { "fm", "et", NULL }; +static const char *process_names_len_7[] = { "Etterna", "etterna", "vkquake", "melonDS", "ryujinx", "sdlmame", "scummvm", "blastem", "redream", "shadps4", NULL }; +static const char *process_names_len_18[] = { "VintagestoryServer", NULL }; +static const char *process_names_len_16[] = { "unreal-bin-amd64", "xenia_canary.exe", NULL }; + +bool is_process_name_native_game(const char *process_name, size_t size) { + switch(size) { + case 12: { + for(size_t i = 0; process_names_len_12[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_12[i], size) == 0) + return true; + } + return false; + } + case 5: { + for(size_t i = 0; process_names_len_5[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_5[i], size) == 0) + return true; + } + return false; + } + case 14: { + for(size_t i = 0; process_names_len_14[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_14[i], size) == 0) + return true; + } + return false; + } + case 15: { + for(size_t i = 0; process_names_len_15[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_15[i], size) == 0) + return true; + } + return false; + } + case 10: { + for(size_t i = 0; process_names_len_10[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_10[i], size) == 0) + return true; + } + return false; + } + case 6: { + for(size_t i = 0; process_names_len_6[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_6[i], size) == 0) + return true; + } + return false; + } + case 11: { + for(size_t i = 0; process_names_len_11[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_11[i], size) == 0) + return true; + } + return false; + } + case 8: { + for(size_t i = 0; process_names_len_8[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_8[i], size) == 0) + return true; + } + return false; + } + case 4: { + for(size_t i = 0; process_names_len_4[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_4[i], size) == 0) + return true; + } + return false; + } + case 9: { + for(size_t i = 0; process_names_len_9[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_9[i], size) == 0) + return true; + } + return false; + } + case 21: { + for(size_t i = 0; process_names_len_21[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_21[i], size) == 0) + return true; + } + return false; + } + case 29: { + for(size_t i = 0; process_names_len_29[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_29[i], size) == 0) + return true; + } + return false; + } + case 26: { + for(size_t i = 0; process_names_len_26[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_26[i], size) == 0) + return true; + } + return false; + } + case 13: { + for(size_t i = 0; process_names_len_13[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_13[i], size) == 0) + return true; + } + return false; + } + case 17: { + for(size_t i = 0; process_names_len_17[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_17[i], size) == 0) + return true; + } + return false; + } + case 3: { + for(size_t i = 0; process_names_len_3[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_3[i], size) == 0) + return true; + } + return false; + } + case 2: { + for(size_t i = 0; process_names_len_2[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_2[i], size) == 0) + return true; + } + return false; + } + case 7: { + for(size_t i = 0; process_names_len_7[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_7[i], size) == 0) + return true; + } + return false; + } + case 18: { + for(size_t i = 0; process_names_len_18[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_18[i], size) == 0) + return true; + } + return false; + } + case 16: { + for(size_t i = 0; process_names_len_16[i] != NULL; ++i) { + if(memcmp(process_name, process_names_len_16[i], size) == 0) + return true; + } + return false; + } + default: { + return false; + } + } +} diff --git a/tools/gsr-game-tracker/native_games.h b/tools/gsr-game-tracker/native_games.h new file mode 100644 index 0000000..10ad9fb --- /dev/null +++ b/tools/gsr-game-tracker/native_games.h @@ -0,0 +1,10 @@ +#ifndef NATIVE_GAMES_H +#define NATIVE_GAMES_H + +#include +#include + +/* Checks for native games (or emulators) that are not steam games and neither ends with .x86_64, .x86 or .x64 */ +bool is_process_name_native_game(const char *process_name, size_t size); + +#endif /* NATIVE_GAMES_H */