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[] = {
4546int 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
85136bool 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&
116177std::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
168248bool 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