ohmu
·
2026-10-15
1#include "fastfetch.h"
2#include "common/jsonconfig.h"
3#include "common/processing.h"
4#include "common/strutil.h"
5#include "common/lua.h"
6#include "options/general.h"
7
8#include <unistd.h>
9
10const char* ffOptionsParseGeneralJsonConfig(FFOptionsGeneral* options, yyjson_val* root, yyjson_val** pkey) {
11 yyjson_val* object = yyjson_obj_get(root, "general");
12 if (!object) {
13 return nullptr;
14 }
15 if (!yyjson_is_obj(object)) {
16 return "Property 'general' must be an object";
17 }
18
19 yyjson_val *key, *val;
20 size_t idx, max;
21 yyjson_obj_foreach (object, idx, max, key, val) {
22 *pkey = key;
23 if (unsafe_yyjson_equals_str(key, "thread")) {
24 options->multithreading = yyjson_get_bool(val);
25 } else if (unsafe_yyjson_equals_str(key, "processingTimeout")) {
26 options->processingTimeout = (int32_t) yyjson_get_int(val);
27 } else if (unsafe_yyjson_equals_str(key, "preRun")) {
28 return "general.preRun is removed due to security concerns.";
29 } else if (unsafe_yyjson_equals_str(key, "detectVersion")) {
30 options->detectVersion = yyjson_get_bool(val);
31 } else if (unsafe_yyjson_equals_str(key, "playerName")) {
32 ffStrbufSetJsonVal(&options->playerName, val);
33 } else if (unsafe_yyjson_equals_str(key, "preload")) {
34 if (!yyjson_is_obj(val)) {
35 return "Property 'general.preload' must be an object";
36 }
37
38 yyjson_val* script = yyjson_obj_get(val, "lua");
39 if (script) {
40 if (!yyjson_is_str(script)) {
41 return "Property 'general.preload.lua' must be a string";
42 }
43
44#if FF_HAVE_LUA
45 const char* error = ffLuaLoadState();
46 if (error) {
47 return error;
48 }
49
50 lua_State* L = luaData.L;
51 lua_settop(L, 0);
52 if (luaL_loadbuffer(L, unsafe_yyjson_get_str(script), unsafe_yyjson_get_len(script), "@general.preload.lua") != LUA_OK ||
53 lua_pcall(L, 0, 0, 0) != LUA_OK) {
54 // The position Lua puts in front of a message is kept here: this is printed once, so naming the
55 // line of the preload script that raised it is useful. `lua_tolstring` only converts strings and
56 // numbers, so an error value of any other type leaves nothing to report.
57 return lua_tolstring(L, -1, nullptr) ?: "the script raised an error that is not a string";
58 }
59 lua_settop(L, 0);
60
61#else
62 return "Property 'general.preload.lua' requires a build with Lua support";
63#endif
64 }
65 }
66#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
67 else if (unsafe_yyjson_equals_str(key, "dsForceDrm")) {
68 if (yyjson_is_str(val)) {
69 int value;
70 const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
71 { "sysfs-only", FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY },
72 { "false", FF_DS_FORCE_DRM_TYPE_FALSE },
73 { "true", FF_DS_FORCE_DRM_TYPE_TRUE },
74 {},
75 });
76 if (error) {
77 return "Invalid enum value of `dsForceDrm`";
78 } else {
79 options->dsForceDrm = (FFDsForceDrmType) value;
80 }
81 } else {
82 options->dsForceDrm = yyjson_get_bool(val) ? FF_DS_FORCE_DRM_TYPE_TRUE : FF_DS_FORCE_DRM_TYPE_FALSE;
83 }
84 }
85#endif
86 else {
87 return "Unknown general property";
88 }
89 }
90
91 return nullptr;
92}
93
94bool ffOptionsParseGeneralCommandLine(FFOptionsGeneral* options, const char* key, const char* value) {
95 if (ffStrEqualsIgnCase(key, "--thread") || ffStrEqualsIgnCase(key, "--multithreading")) {
96 options->multithreading = ffOptionParseBoolean(value);
97 } else if (ffStrEqualsIgnCase(key, "--processing-timeout")) {
98 options->processingTimeout = ffOptionParseInt32(key, value);
99 } else if (ffStrEqualsIgnCase(key, "--detect-version")) {
100 options->detectVersion = ffOptionParseBoolean(value);
101 } else if (ffStrEqualsIgnCase(key, "--player-name")) {
102 ffOptionParseString(key, value, &options->playerName);
103 }
104#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
105 else if (ffStrEqualsIgnCase(key, "--ds-force-drm")) {
106 if (ffOptionParseBoolean(value)) {
107 options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_TRUE;
108 } else if (ffStrEqualsIgnCase(value, "sysfs-only")) {
109 options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY;
110 } else {
111 options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
112 }
113 }
114#endif
115 else {
116 return false;
117 }
118
119 return true;
120}
121
122void ffOptionsInitGeneral(FFOptionsGeneral* options) {
123 options->processingTimeout = 5000;
124 options->multithreading = true;
125 options->detectVersion = true;
126 ffStrbufInit(&options->playerName);
127
128#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
129 options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
130#endif
131}
132
133void ffOptionsDestroyGeneral(FFOptionsGeneral* options) {
134 ffStrbufDestroy(&options->playerName);
135}
136
137[[gnu::cold]]
138void ffOptionsGenerateGeneralJsonConfig(FFdata* data, FFOptionsGeneral* options) {
139 yyjson_mut_doc* doc = data->resultDoc;
140 yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, doc->root, "general");
141
142 yyjson_mut_obj_add_bool(doc, obj, "thread", options->multithreading);
143
144 yyjson_mut_obj_add_int(doc, obj, "processingTimeout", options->processingTimeout);
145
146 yyjson_mut_obj_add_bool(doc, obj, "detectVersion", options->detectVersion);
147
148 yyjson_mut_obj_add_strbuf(doc, obj, "playerName", &options->playerName);
149
150#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
151
152 switch (options->dsForceDrm) {
153 case FF_DS_FORCE_DRM_TYPE_FALSE:
154 yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", false);
155 break;
156 case FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY:
157 yyjson_mut_obj_add_str(doc, obj, "dsForceDrm", "sysfs-only");
158 break;
159 case FF_DS_FORCE_DRM_TYPE_TRUE:
160 yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", true);
161 break;
162 }
163#endif
164}