Skip to content

Commit 51c4918

Browse files
committed
enhancement: improve config file handling and parameter validation in retroarch
1 parent e535218 commit 51c4918

File tree

1 file changed

+130
-22
lines changed

1 file changed

+130
-22
lines changed

src/osdep/retroarch.cpp

Lines changed: 130 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <cstdio>
22
#include <cstring>
33
#include <cstdlib>
4+
#include <cctype>
45

56
#include "sysdeps.h"
67
#include "options.h"
@@ -45,6 +46,12 @@ const std::string controller_axis_list[] = {
4546
int find_retroarch(const std::string& find_setting, const std::string& retroarch_file)
4647
{
4748
std::ifstream read_file(retroarch_file);
49+
if (!read_file.is_open())
50+
{
51+
write_log("Controller Detection: Could not open config file: %s\n", retroarch_file.c_str());
52+
return -1; // File not found / not readable
53+
}
54+
4855
std::string line;
4956
std::string delimiter = " = ";
5057
int button = -1;
@@ -57,23 +64,67 @@ int find_retroarch(const std::string& find_setting, const std::string& retroarch
5764
auto delimiter_pos = line.find(delimiter);
5865
auto option = line.substr(0, delimiter_pos);
5966

67+
// If delimiter not found or not the setting we want, skip early
6068
if (option == line || option != find_setting)
6169
continue;
6270

63-
auto param = line.substr(delimiter_pos + delimiter.length());
71+
// Safe extraction of parameter portion
72+
std::string param;
73+
if (delimiter_pos != std::string::npos)
74+
param = line.substr(delimiter_pos + delimiter.length());
75+
else
76+
continue; // Should not happen due to earlier continue, but guard anyway
77+
78+
// Remove quotes
6479
param.erase(std::remove(param.begin(), param.end(), '"'), param.end());
80+
// Trim whitespace
81+
param.erase(0, param.find_first_not_of(" \t\r\n"));
82+
if (!param.empty())
83+
{
84+
const auto last_non_ws = param.find_last_not_of(" \t\r\n");
85+
if (last_non_ws != std::string::npos && last_non_ws + 1 < param.size())
86+
param.erase(last_non_ws + 1);
87+
}
6588

89+
if (param.empty())
90+
{
91+
write_log("Controller Detection: %s has empty value, skipping\n", find_setting.c_str());
92+
continue; // Keep searching; maybe another occurrence exists (unlikely but safe)
93+
}
94+
95+
// Special handling for hats: value starting with 'h'
6696
if (find_setting == "count_hats" && param[0] == 'h')
6797
{
6898
button = 1;
6999
break;
70100
}
71101

72-
if (param[0] != 'h') // check it isn't some kind of hat starting 'h' (so if D-pad uses buttons)
102+
// Ignore hat-style entries for non-hat settings
103+
if (param[0] == 'h')
104+
{
105+
write_log("Controller Detection: %s value '%s' looks like hat descriptor, ignoring numeric parse\n", find_setting.c_str(), param.c_str());
106+
continue;
107+
}
108+
109+
// Validate first character for integer parsing
110+
if (!(std::isdigit(static_cast<unsigned char>(param[0])) || param[0] == '-' || param[0] == '+'))
111+
{
112+
write_log("Controller Detection: %s value '%s' not numeric, skipping\n", find_setting.c_str(), param.c_str());
113+
continue;
114+
}
115+
116+
try
73117
{
74-
button = abs(std::stoi(param));
118+
int value = std::stoi(param); // May throw
119+
button = std::abs(value);
120+
}
121+
catch (const std::exception& e)
122+
{
123+
write_log("Controller Detection: %s invalid integer '%s' (%s)\n", find_setting.c_str(), param.c_str(), e.what());
124+
// Leave button as -1 and continue searching (though typically only one entry exists)
75125
}
76126

127+
// If we successfully matched the setting, we can break
77128
if (option == find_setting)
78129
break;
79130
}
@@ -85,6 +136,8 @@ int find_retroarch(const std::string& find_setting, const std::string& retroarch
85136
bool find_retroarch_polarity(const std::string& find_setting, const std::string& retroarch_file)
86137
{
87138
std::ifstream read_file(retroarch_file);
139+
if (!read_file.is_open())
140+
return false;
88141
std::string line;
89142
std::string delimiter = " = ";
90143
bool button = false;
@@ -100,8 +153,16 @@ bool find_retroarch_polarity(const std::string& find_setting, const std::string&
100153
if (option == line || option != find_setting)
101154
continue;
102155

103-
auto param = line.substr(delimiter_pos + delimiter.length());
156+
std::string param;
157+
if (delimiter_pos != std::string::npos)
158+
param = line.substr(delimiter_pos + delimiter.length());
159+
else
160+
continue;
161+
104162
param.erase(std::remove(param.begin(), param.end(), '"'), param.end());
163+
param.erase(0, param.find_first_not_of(" \t\r\n"));
164+
if (param.empty())
165+
continue;
105166

106167
if (param[0] == '-')
107168
{
@@ -116,32 +177,51 @@ bool find_retroarch_polarity(const std::string& find_setting, const std::string&
116177
std::string find_retroarch_key(const std::string& find_setting_prefix, int player, const std::string& suffix, const std::string& retroarch_file)
117178
{
118179
std::ifstream read_file(retroarch_file);
180+
if (!read_file.is_open())
181+
return "nul"; // Fail fast if file can't be opened
182+
119183
std::string line;
120-
std::string delimiter = " = ";
184+
const std::string delimiter = " = ";
121185
std::string output = "nul";
122186

123187
std::string find_setting = find_setting_prefix;
124188
if (!suffix.empty())
125-
{
126189
find_setting += std::to_string(player) + "_" + suffix;
127-
}
128190

129191
while (std::getline(read_file, line))
130192
{
131193
if (line.length() <= 1)
132194
continue;
133195

134-
auto delimiter_pos = line.find(delimiter);
135-
auto option = line.substr(0, delimiter_pos);
196+
const auto delimiter_pos = line.find(delimiter);
197+
if (delimiter_pos == std::string::npos)
198+
continue; // No delimiter, skip
136199

137-
if (option == line || option != find_setting)
200+
const auto option = line.substr(0, delimiter_pos);
201+
if (option != find_setting)
138202
continue;
139203

140-
auto param = line.substr(delimiter_pos + delimiter.length());
204+
// Extract parameter safely
205+
std::string param = line.substr(delimiter_pos + delimiter.length());
206+
// Remove quotes
141207
param.erase(std::remove(param.begin(), param.end(), '"'), param.end());
208+
// Trim leading/trailing whitespace
209+
param.erase(0, param.find_first_not_of(" \t\r\n"));
210+
if (!param.empty())
211+
{
212+
const auto last_non_ws = param.find_last_not_of(" \t\r\n");
213+
if (last_non_ws != std::string::npos && last_non_ws + 1 < param.size())
214+
param.erase(last_non_ws + 1);
215+
}
216+
217+
if (param.empty())
218+
{
219+
write_log("Controller Detection: %s present but empty in config\n", find_setting.c_str());
220+
break; // We found the setting but it's empty; stop searching
221+
}
142222

143223
output = param;
144-
break;
224+
break; // Found it
145225
}
146226

147227
return output;
@@ -167,6 +247,11 @@ std::string sanitize_retroarch_name(std::string s)
167247

168248
bool init_kb_from_retroarch(const int index, const std::string& retroarch_file)
169249
{
250+
if (index < 0 || index > 3)
251+
{
252+
write_log("Controller init_kb_from_retroarch: invalid index %d\n", index);
253+
return false;
254+
}
170255
const auto player = index + 1;
171256
std::string key;
172257
int x;
@@ -180,24 +265,32 @@ bool init_kb_from_retroarch(const int index, const std::string& retroarch_file)
180265
{
181266
key = find_retroarch_key("input_player", player, i, retroarch_file);
182267
x = find_string_in_array(remap_key_map_list_strings, key);
183-
if (x == -1 || x == 0) break;
268+
if (x <= 0 || x >= static_cast<int>(remap_key_map_list_strings.size()))
269+
break;
184270

185271
valid = true;
186272
if (idx < 9)
187273
{
188-
kbs[index][idx] = remap_key_map_list[x];
189-
kbs_3[index][idx] = remap_key_map_list[x];
190-
kbs_cd32[index][idx] = remap_key_map_list[x];
274+
if (x < static_cast<int>(remap_key_map_list_strings.size()))
275+
{
276+
kbs[index][idx] = remap_key_map_list[x];
277+
kbs_3[index][idx] = remap_key_map_list[x];
278+
kbs_cd32[index][idx] = remap_key_map_list[x];
279+
}
191280
}
192281
else if (idx == 9 || idx == 11)
193282
{
194-
kbs[index][idx] = remap_key_map_list[x];
195-
kbs_3[index][idx + 1] = remap_key_map_list[x];
196-
kbs_cd32[index][idx + 1] = remap_key_map_list[x];
283+
if (x < static_cast<int>(remap_key_map_list_strings.size()))
284+
{
285+
kbs[index][idx] = remap_key_map_list[x];
286+
kbs_3[index][idx + 1] = remap_key_map_list[x];
287+
kbs_cd32[index][idx + 1] = remap_key_map_list[x];
288+
}
197289
}
198290
else if (idx >= 13 && idx <= 23)
199291
{
200-
kbs_cd32[index][idx + 1] = remap_key_map_list[x];
292+
if (x < static_cast<int>(remap_key_map_list_strings.size()))
293+
kbs_cd32[index][idx + 1] = remap_key_map_list[x];
201294
}
202295
if (idx < 23) idx++;
203296
}
@@ -207,14 +300,27 @@ bool init_kb_from_retroarch(const int index, const std::string& retroarch_file)
207300
{
208301
key = find_retroarch_key("input_enable_hotkey", player, "", retroarch_file);
209302
x = find_string_in_array(remap_key_map_list_strings, key);
303+
if (x > 0 && x < static_cast<int>(remap_key_map_list_strings.size()))
304+
{
305+
// Store hotkey enabling key if needed later (currently not assigned to a global)
306+
write_log("Controller Detection: hotkey key '%s' index %d\n", key.c_str(), x);
307+
}
308+
else if (!key.empty() && key != "nul")
309+
write_log("Controller Detection: invalid hotkey key '%s'\n", key.c_str());
210310

211311
key = find_retroarch_key("input_exit_emulator", player, "", retroarch_file);
212312
x = find_string_in_array(remap_key_map_list_strings, key);
213-
quit_key.scancode = remap_key_map_list[x];
313+
if (x > 0 && x < static_cast<int>(remap_key_map_list_strings.size()))
314+
quit_key.scancode = remap_key_map_list[x];
315+
else
316+
write_log("Controller Detection: exit emulator key '%s' not mapped (index %d)\n", key.c_str(), x);
214317

215318
key = find_retroarch_key("input_menu_toggle", player, "", retroarch_file);
216319
x = find_string_in_array(remap_key_map_list_strings, key);
217-
enter_gui_key.scancode = remap_key_map_list[x];
320+
if (x > 0 && x < static_cast<int>(remap_key_map_list_strings.size()))
321+
enter_gui_key.scancode = remap_key_map_list[x];
322+
else
323+
write_log("Controller Detection: menu toggle key '%s' not mapped (index %d)\n", key.c_str(), x);
218324
}
219325

220326
write_log("Controller init_kb_from_retroarch(%i): %s \n", index, valid ? "Found" : "Not found");
@@ -247,12 +353,14 @@ void map_from_retroarch(controller_mapping& mapping, const std::string& control_
247353
// RetroArch supports 15 buttons
248354
for (int b = 0; b < 15; ++b)
249355
{
356+
if (retroarch_button_list[b].empty()) { mapping.button[b] = -1; continue; }
250357
mapping.button[b] = find_retroarch(ra_player_input(retroarch_button_list[b], player), control_config);
251358
}
252359

253360
// RetroArch supports 6 axes
254361
for (int a = 0; a < 6; ++a)
255362
{
363+
if (retroarch_axis_list[a].empty()) { mapping.axis[a] = -1; continue; }
256364
mapping.axis[a] = find_retroarch(ra_player_input(retroarch_axis_list[a], player), control_config);
257365
}
258366

0 commit comments

Comments
 (0)