gsr-game-tracker: track native games and emulators (from ananicy-rules)

This commit is contained in:
dec05eba
2026-04-28 09:47:03 +02:00
parent e3ccfed987
commit 1adbb13ca5
5 changed files with 310 additions and 16 deletions

View File

@@ -144,6 +144,7 @@ executable(
executable( executable(
'gsr-game-tracker', 'gsr-game-tracker',
[ [
'tools/gsr-game-tracker/native_games.c',
'tools/gsr-game-tracker/main.c' 'tools/gsr-game-tracker/main.c'
], ],
install : true install : true

View File

@@ -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 <string.h>\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 <path-to-ananicy-rules>")
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()

View File

@@ -1,3 +1,5 @@
#include "native_games.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
@@ -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; return haystack_size == needle_size && memcmp(haystack, needle, needle_size) == 0;
} }
static bool is_wine_binary(const char *argv0, size_t size) { static const char* process_get_basename(const char *argv0, size_t argv0_len, size_t *basename_len) {
const char *base = memchr_reverse(argv0, '/', size); *basename_len = 0;
const char *base = memchr_reverse(argv0, '/', argv0_len);
base = base ? base + 1 : argv0; base = base ? base + 1 : argv0;
const size_t base_len = argv0 + size - base; *basename_len = argv0 + argv0_len - base;
return memeql(base, base_len, "wine") || return base;
memeql(base, base_len, "wine64") ||
memeql(base, base_len, "wine-preloader") ||
memeql(base, base_len, "wine64-preloader");
} }
static bool is_wine_server(const char *argv0, size_t size) { static bool is_wine_binary(const char *process_basename, size_t process_basename_len) {
const char *base = memchr_reverse(argv0, '/', size); return memeql(process_basename, process_basename_len, "wine") ||
base = base ? base + 1 : argv0; memeql(process_basename, process_basename_len, "wine64") ||
const size_t base_len = argv0 + size - base; memeql(process_basename, process_basename_len, "wine-preloader") ||
return memeql(base, base_len, "wineserver"); 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" }; static const char *suffixes[] = { ".x86_64", ".x64", ".x86" };
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
const size_t slen = strlen(suffixes[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 true;
} }
return false; return false;
@@ -173,8 +177,15 @@ static void check_process(pid_t pid) {
const char *argv1 = cmdline_buf + argv0_len + 1; 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; 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) size_t process_basename_len = 0;
&& !is_wine_server(argv0, argv0_len) 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") && !memeql(argv1, argv1_len, "--version")
&& (!is_steam_app || steam_overlay_game_id != NULL)) && (!is_steam_app || steam_overlay_game_id != NULL))
{ {

View File

@@ -0,0 +1,174 @@
#include "native_games.h"
#include <string.h>
/* 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;
}
}
}

View File

@@ -0,0 +1,10 @@
#ifndef NATIVE_GAMES_H
#define NATIVE_GAMES_H
#include <stdbool.h>
#include <stddef.h>
/* 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 */