// Runtime config loader: applies an optional on-device cfg over the baked-in // defaults. The cfg format is "Key = Value", one per line, '#' comments and // [section] headers ignored -- the same keys DorkNet.ClientMod uses on PC, so a // PC config reads here too. #include "config.h" #include #include #include extern "C" int __android_log_print(int prio, const char *tag, const char *fmt, ...); #define CLOGI(...) __android_log_print(4, "DORKNET", __VA_ARGS__) static void trim(char *s) { char *p = s; while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++; if (p != s) memmove(s, p, strlen(p) + 1); size_t n = strlen(s); while (n && (s[n - 1] == ' ' || s[n - 1] == '\t' || s[n - 1] == '\r' || s[n - 1] == '\n')) s[--n] = '\0'; } static bool as_bool(const char *v, bool dflt) { if (!v || !*v) return dflt; if (!strcasecmp(v, "true") || !strcasecmp(v, "1") || !strcasecmp(v, "yes") || !strcasecmp(v, "on")) return true; if (!strcasecmp(v, "false") || !strcasecmp(v, "0") || !strcasecmp(v, "no") || !strcasecmp(v, "off")) return false; return dflt; } // Accept a bare host or a full URL; store just the host. static void set_host(char *out, size_t cap, const char *val) { const char *scheme = strstr(val, "://"); const char *host = scheme ? scheme + 3 : val; size_t i = 0; for (; host[i] && host[i] != '/' && i + 1 < cap; i++) out[i] = host[i]; out[i] = '\0'; } void dn_load_runtime_config(char *server_host, size_t server_host_cap, char *oculus_appid, size_t oculus_appid_cap, char *photon_realtime, char *photon_voice, char *photon_chat, char *photon_server, size_t photon_server_cap, int *photon_port, char *photon_region, size_t photon_region_cap, bool *tls_bypass, bool *accept_images, bool *log_http, bool *debug_http, int *body_max) { char path[256]; snprintf(path, sizeof(path), "/sdcard/Android/data/%s/files/%s", DN_PACKAGE, DN_RUNTIME_CFG_NAME); FILE *f = fopen(path, "r"); if (!f) { CLOGI("no runtime config at %s; using baked-in defaults", path); return; } char line[256]; while (fgets(line, sizeof(line), f)) { char *hash = strchr(line, '#'); if (hash) *hash = '\0'; char *eq = strchr(line, '='); if (!eq) continue; *eq = '\0'; char *key = line, *val = eq + 1; trim(key); trim(val); if (!strcasecmp(key, "ServerHost") && *val) set_host(server_host, server_host_cap, val); else if (!strcasecmp(key, "OculusAppId")) { oculus_appid[0] = '\0'; strncpy(oculus_appid, val, oculus_appid_cap - 1); } else if (!strcasecmp(key, "PhotonAppId") && *val) strncpy(photon_realtime, val, 63); else if (!strcasecmp(key, "PhotonVoiceAppId") && *val) strncpy(photon_voice, val, 63); else if (!strcasecmp(key, "PhotonChatAppId") && *val) strncpy(photon_chat, val, 63); else if (!strcasecmp(key, "PhotonNameServer")) set_host(photon_server, photon_server_cap, val); else if (!strcasecmp(key, "PhotonNameServerPort")) *photon_port = atoi(val); else if (!strcasecmp(key, "PhotonRegion") && *val) { photon_region[0] = ''; strncpy(photon_region, val, photon_region_cap - 1); } else if (!strcasecmp(key, "EnableTlsTrustBypass")) *tls_bypass = as_bool(val, *tls_bypass); else if (!strcasecmp(key, "AcceptUnsignedImages")) *accept_images = as_bool(val, *accept_images); else if (!strcasecmp(key, "LogHttp")) *log_http = as_bool(val, *log_http); else if (!strcasecmp(key, "Debug")) *debug_http = as_bool(val, *debug_http); else if (!strcasecmp(key, "LogHttpBodyMax") && *val) *body_max = atoi(val); } fclose(f); CLOGI("runtime config applied from %s", path); }