1 #include <vector>
2 #include <string>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 
6 #include "libretro.h"
7 #include "burner.h"
8 #include "burnint.h"
9 #include "aud_dsp.h"
10 
11 #include "retro_common.h"
12 #include "retro_cdemu.h"
13 #include "retro_input.h"
14 #include "retro_memory.h"
15 #include "ugui_tools.h"
16 
17 #include <file/file_path.h>
18 
19 #include <streams/file_stream.h>
20 #include <string/stdstring.h>
21 
22 #define snprintf_nowarn(...) (snprintf(__VA_ARGS__) < 0 ? abort() : (void)0)
23 #define PRINTF_BUFFER_SIZE 512
24 
25 #define STAT_NOFIND  0
26 #define STAT_OK      1
27 #define STAT_CRC     2
28 #define STAT_SMALL   3
29 #define STAT_LARGE   4
30 
31 int counter;           // General purpose variable used when debugging
32 struct MovieExtInfo
33 {
34 	// date & time
35 	UINT32 year, month, day;
36 	UINT32 hour, minute, second;
37 };
38 struct MovieExtInfo MovieInfo = { 0, 0, 0, 0, 0, 0 };
39 
log_dummy(enum retro_log_level level,const char * fmt,...)40 static void log_dummy(enum retro_log_level level, const char *fmt, ...) { }
41 
42 retro_log_printf_t log_cb = log_dummy;
43 retro_environment_t environ_cb;
44 static retro_video_refresh_t video_cb;
45 static retro_audio_sample_batch_t audio_batch_cb;
46 
47 static unsigned libretro_msg_interface_version = 0;
48 
49 int kNetGame = 0;
50 INT32 nReplayStatus = 0;
51 INT32 nIpsMaxFileLen = 0;
52 unsigned nGameType = 0;
53 static INT32 nGameWidth = 640;
54 static INT32 nGameHeight = 480;
55 static INT32 nGameMaximumGeometry;
56 static INT32 nNextGeometryCall = RETRO_ENVIRONMENT_SET_GEOMETRY;
57 
58 extern INT32 EnableHiscores;
59 
60 struct RomFind { int nState; int nZip; int nPos; };
61 static struct RomFind* pRomFind = NULL;
62 static unsigned nRomCount;
63 
64 struct located_archive
65 {
66 	std::string path;
67 	bool ignoreCrc;
68 };
69 static std::vector<located_archive> g_find_list_path;
70 
71 std::vector<cheat_core_option> cheat_core_options;
72 
73 INT32 nAudSegLen = 0;
74 
75 static UINT8* pVidImage = NULL;
76 static bool bVidImageNeedRealloc = false;
77 static bool bRotationDone = false;
78 static int16_t *pAudBuffer = NULL;
79 
80 // Frameskipping v2 Support
81 #define FRAMESKIP_MAX 30
82 
83 UINT32 nFrameskipType                      = 0;
84 UINT32 nFrameskipThreshold                 = 0;
85 static UINT32 nFrameskipCounter            = 0;
86 static UINT32 nAudioLatency                = 0;
87 static bool bUpdateAudioLatency            = false;
88 
89 static bool retro_audio_buff_active        = false;
90 static unsigned retro_audio_buff_occupancy = 0;
91 static bool retro_audio_buff_underrun      = false;
92 
retro_audio_buff_status_cb(bool active,unsigned occupancy,bool underrun_likely)93 static void retro_audio_buff_status_cb(bool active, unsigned occupancy, bool underrun_likely)
94 {
95 	retro_audio_buff_active    = active;
96 	retro_audio_buff_occupancy = occupancy;
97 	retro_audio_buff_underrun  = underrun_likely;
98 }
99 
100 // Mapping of PC inputs to game inputs
101 struct GameInp* GameInp = NULL;
102 UINT32 nGameInpCount = 0;
103 INT32 nAnalogSpeed = 0x0100;
104 INT32 nFireButtons = 0;
105 
106 char g_driver_name[128];
107 char g_rom_dir[MAX_PATH];
108 char g_rom_parent_dir[MAX_PATH];
109 char g_save_dir[MAX_PATH];
110 char g_system_dir[MAX_PATH];
111 char g_autofs_path[MAX_PATH];
112 
113 // MemCard support
114 static TCHAR szMemoryCardFile[MAX_PATH];
115 static int nMinVersion;
116 static bool bMemCardFC1Format;
117 
118 // UGUI
119 static bool gui_show = false;
120 
121 // FBNEO stubs
122 unsigned ArcadeJoystick;
123 
124 int bDrvOkay;
125 int bRunPause;
126 bool bAlwaysProcessKeyboardInput;
127 
128 bool bDoIpsPatch;
IpsApplyPatches(UINT8 *,char *)129 void IpsApplyPatches(UINT8 *, char *) {}
130 
131 TCHAR szAppEEPROMPath[MAX_PATH];
132 TCHAR szAppHiscorePath[MAX_PATH];
133 TCHAR szAppSamplesPath[MAX_PATH];
134 TCHAR szAppBlendPath[MAX_PATH];
135 TCHAR szAppHDDPath[MAX_PATH];
136 TCHAR szAppCheatsPath[MAX_PATH];
137 TCHAR szAppBurnVer[16];
138 
139 static int nDIPOffset;
140 
141 const int nConfigMinVersion = 0x020921;
142 
HandleMessage(enum retro_log_level level,TCHAR * szFormat,...)143 int HandleMessage(enum retro_log_level level, TCHAR* szFormat, ...)
144 {
145 	char buf[PRINTF_BUFFER_SIZE];
146 	va_list vp;
147 	va_start(vp, szFormat);
148 	int rc = vsnprintf(buf, PRINTF_BUFFER_SIZE, szFormat, vp);
149 	va_end(vp);
150 	if (rc >= 0)
151 	{
152 		// Errors are blocking, so let's display them for 10s in frontend
153 		if (level == RETRO_LOG_ERROR)
154 		{
155 			if (libretro_msg_interface_version >= 1)
156 			{
157 				struct retro_message_ext msg = {
158 					buf,
159 					10000,
160 					3,
161 					level,
162 					RETRO_MESSAGE_TARGET_OSD,
163 					RETRO_MESSAGE_TYPE_NOTIFICATION,
164 					-1
165 				};
166 				environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
167 			}
168 			else
169 			{
170 				struct retro_message msg =
171 				{
172 					buf,
173 					600
174 				};
175 				environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
176 			}
177 		}
178 		log_cb(level, buf);
179 	}
180 
181 	return rc;
182 }
183 
libretro_bprintf(INT32 nStatus,TCHAR * szFormat,...)184 static INT32 __cdecl libretro_bprintf(INT32 nStatus, TCHAR* szFormat, ...)
185 {
186 	char buf[PRINTF_BUFFER_SIZE];
187 	va_list vp;
188 
189 	// some format specifiers don't translate well into the retro logs, replace them
190 	szFormat = string_replace_substring(szFormat, "%S", "%s");
191 
192 	// retro logs prefer ending with \n
193 	// 2021-10-26: disabled it's causing overflow in a few cases, find a better way to do this...
194 	//if (szFormat[strlen(szFormat)-1] != '\n') strncat(szFormat, "\n", 1);
195 
196 	va_start(vp, szFormat);
197 	int rc = vsnprintf(buf, PRINTF_BUFFER_SIZE, szFormat, vp);
198 	va_end(vp);
199 
200 	if (rc >= 0)
201 	{
202 #ifdef FBNEO_DEBUG
203 		retro_log_level retro_log = RETRO_LOG_INFO;
204 #else
205 		retro_log_level retro_log = RETRO_LOG_DEBUG;
206 #endif
207 		if (nStatus == PRINT_UI)
208 			retro_log = RETRO_LOG_INFO;
209 		else if (nStatus == PRINT_IMPORTANT)
210 			retro_log = RETRO_LOG_WARN;
211 		else if (nStatus == PRINT_ERROR)
212 			retro_log = RETRO_LOG_ERROR;
213 
214 		HandleMessage(retro_log, buf);
215 
216 #ifdef FBNEO_DEBUG
217 		// Let's send errors to a file
218 		if (nStatus == PRINT_ERROR)
219 		{
220 			FILE * error_file;
221 			char error_path[MAX_PATH];
222 			char error_path_to_create[MAX_PATH];
223 			snprintf_nowarn (error_path_to_create, sizeof(error_path_to_create), "%s%cfbneo%cerrors", g_save_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
224 			path_mkdir(error_path_to_create);
225 			snprintf_nowarn (error_path, sizeof(error_path), "%s%cfbneo%cerrors%c%s.txt", g_save_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), BurnDrvGetTextA(DRV_NAME));
226 			error_file = fopen(error_path, filestream_exists(error_path) ? "a" : "w");
227 			fwrite(buf , strlen(buf), 1, error_file);
228 			fclose(error_file);
229 		}
230 #endif
231 
232 	}
233 
234 	return rc;
235 }
236 
237 INT32 (__cdecl *bprintf) (INT32 nStatus, TCHAR* szFormat, ...) = libretro_bprintf;
238 
239 // libretro globals
retro_set_video_refresh(retro_video_refresh_t cb)240 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
retro_set_audio_sample(retro_audio_sample_t)241 void retro_set_audio_sample(retro_audio_sample_t) {}
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)242 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
243 
retro_set_environment(retro_environment_t cb)244 void retro_set_environment(retro_environment_t cb)
245 {
246 	environ_cb = cb;
247 
248 	// Subsystem (needs to be called now, or it won't work on command line)
249 	static const struct retro_subsystem_rom_info subsystem_rom[] = {
250 		{ "Rom", "zip|7z", true, true, true, NULL, 0 },
251 	};
252 	static const struct retro_subsystem_rom_info subsystem_iso[] = {
253 		{ "Iso", "ccd|cue", true, true, true, NULL, 0 },
254 	};
255 	static const struct retro_subsystem_info subsystems[] = {
256 		{ "CBS ColecoVision", "cv", subsystem_rom, 1, RETRO_GAME_TYPE_CV },
257 		{ "Fairchild ChannelF", "chf", subsystem_rom, 1, RETRO_GAME_TYPE_CHF },
258 		{ "MSX 1", "msx", subsystem_rom, 1, RETRO_GAME_TYPE_MSX },
259 		{ "Nec PC-Engine", "pce", subsystem_rom, 1, RETRO_GAME_TYPE_PCE },
260 		{ "Nec SuperGrafX", "sgx", subsystem_rom, 1, RETRO_GAME_TYPE_SGX },
261 		{ "Nec TurboGrafx-16", "tg16", subsystem_rom, 1, RETRO_GAME_TYPE_TG },
262 		{ "Nintendo Entertainment System", "nes", subsystem_rom, 1, RETRO_GAME_TYPE_NES },
263 		{ "Nintendo Family Disk System", "fds", subsystem_rom, 1, RETRO_GAME_TYPE_FDS },
264 		{ "Sega GameGear", "gg", subsystem_rom, 1, RETRO_GAME_TYPE_GG },
265 		{ "Sega Master System", "sms", subsystem_rom, 1, RETRO_GAME_TYPE_SMS },
266 		{ "Sega Megadrive", "md", subsystem_rom, 1, RETRO_GAME_TYPE_MD },
267 		{ "Sega SG-1000", "sg1k", subsystem_rom, 1, RETRO_GAME_TYPE_SG1K },
268 		{ "SNK Neo Geo Pocket", "ngp", subsystem_rom, 1, RETRO_GAME_TYPE_NGP },
269 		{ "ZX Spectrum", "spec", subsystem_rom, 1, RETRO_GAME_TYPE_SPEC },
270 		{ "Neogeo CD", "neocd", subsystem_iso, 1, RETRO_GAME_TYPE_NEOCD },
271 		{ NULL },
272 	};
273 
274 	environ_cb(RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO, (void*)subsystems);
275 }
276 
277 extern unsigned int (__cdecl *BurnHighCol) (signed int r, signed int g, signed int b, signed int i);
278 
retro_get_system_info(struct retro_system_info * info)279 void retro_get_system_info(struct retro_system_info *info)
280 {
281 	char *library_version = (char*)calloc(22, sizeof(char));
282 
283 #ifndef GIT_VERSION
284 #define GIT_VERSION ""
285 #endif
286 
287 	sprintf(library_version, "v%x.%x.%x.%02x %s", nBurnVer >> 20, (nBurnVer >> 16) & 0x0F, (nBurnVer >> 8) & 0xFF, nBurnVer & 0xFF, GIT_VERSION);
288 
289 	info->library_name = APP_TITLE;
290 	info->library_version = strdup(library_version);
291 	info->need_fullpath = true;
292 	info->block_extract = true;
293 	info->valid_extensions = "zip|7z|cue|ccd";
294 
295 	free(library_version);
296 }
297 
InpDIPSWGetOffset(void)298 static void InpDIPSWGetOffset (void)
299 {
300 	BurnDIPInfo bdi;
301 	nDIPOffset = 0;
302 
303 	for(int i = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++)
304 	{
305 		if (bdi.nFlags == 0xF0)
306 		{
307 			nDIPOffset = bdi.nInput;
308 			HandleMessage(RETRO_LOG_INFO, "DIP switches offset: %d.\n", bdi.nInput);
309 			break;
310 		}
311 	}
312 }
313 
InpDIPSWResetDIPs(void)314 void InpDIPSWResetDIPs (void)
315 {
316 	int i = 0;
317 	BurnDIPInfo bdi;
318 	struct GameInp * pgi = NULL;
319 
320 	InpDIPSWGetOffset();
321 
322 	while (BurnDrvGetDIPInfo(&bdi, i) == 0)
323 	{
324 		if (bdi.nFlags == 0xFF)
325 		{
326 			pgi = GameInp + bdi.nInput + nDIPOffset;
327 			if (pgi)
328 				pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
329 		}
330 		i++;
331 	}
332 }
333 
create_variables_from_dipswitches()334 static int create_variables_from_dipswitches()
335 {
336 	HandleMessage(RETRO_LOG_INFO, "Initialize DIP switches.\n");
337 
338 	dipswitch_core_options.clear();
339 
340 	BurnDIPInfo bdi;
341 	struct GameInp *pgi;
342 
343 	const char * drvname = BurnDrvGetTextA(DRV_NAME);
344 
345 	if (!drvname)
346 		return 0;
347 
348 	for (int i = 0, j = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++)
349 	{
350 		/* 0xFE is the beginning label for a DIP switch entry */
351 		/* 0xFD are region DIP switches */
352 		if ((bdi.nFlags == 0xFE || bdi.nFlags == 0xFD) && bdi.nSetting > 1)
353 		{
354 			dipswitch_core_options.push_back(dipswitch_core_option());
355 			dipswitch_core_option *dip_option = &dipswitch_core_options.back();
356 
357 			// Clean the dipswitch name to creation the core option name (removing space and equal characters)
358 			std::string option_name;
359 
360 			// Some dipswitch has no name...
361 			if (bdi.szText)
362 			{
363 				option_name = bdi.szText;
364 			}
365 			else // ... so, to not hang, we will generate a name based on the position of the dip (DIPSWITCH 1, DIPSWITCH 2...)
366 			{
367 				option_name = SSTR( "DIPSWITCH " << dipswitch_core_options.size() );
368 				HandleMessage(RETRO_LOG_WARN, "Error in %sDIPList : The DIPSWITCH '%d' has no name. '%s' name has been generated\n", drvname, dipswitch_core_options.size(), option_name.c_str());
369 			}
370 
371 			dip_option->friendly_name = SSTR( "[Dipswitch] " << option_name.c_str() );
372 			dip_option->friendly_name_categorized = option_name.c_str();
373 
374 			std::replace( option_name.begin(), option_name.end(), ' ', '_');
375 			std::replace( option_name.begin(), option_name.end(), '=', '_');
376 			std::replace( option_name.begin(), option_name.end(), ':', '_');
377 
378 			dip_option->option_name = SSTR( "fbneo-dipswitch-" << drvname << "-" << option_name.c_str() );
379 
380 			// Search for duplicate name, and add number to make them unique in the core-options file
381 			for (int dup_idx = 0, dup_nbr = 1; dup_idx < dipswitch_core_options.size() - 1; dup_idx++) // - 1 to exclude the current one
382 			{
383 				if (dip_option->option_name.compare(dipswitch_core_options[dup_idx].option_name) == 0)
384 				{
385 					dup_nbr++;
386 					dip_option->option_name = SSTR( "fbneo-dipswitch-" << drvname << "-" << option_name.c_str() << "_" << dup_nbr );
387 				}
388 			}
389 
390 			dip_option->values.reserve(bdi.nSetting);
391 			dip_option->values.assign(bdi.nSetting, dipswitch_core_option_value());
392 
393 			int values_count = 0;
394 			bool skip_unusable_option = false;
395 			for (int k = 0; values_count < bdi.nSetting; k++)
396 			{
397 				BurnDIPInfo bdi_value;
398 				if (BurnDrvGetDIPInfo(&bdi_value, k + i + 1) != 0)
399 				{
400 					HandleMessage(RETRO_LOG_WARN, "Error in %sDIPList for DIPSWITCH '%s': End of the struct was reached too early\n", drvname, dip_option->friendly_name.c_str());
401 					break;
402 				}
403 
404 				if (bdi_value.nFlags == 0xFE || bdi_value.nFlags == 0xFD)
405 				{
406 					HandleMessage(RETRO_LOG_WARN, "Error in %sDIPList for DIPSWITCH '%s': Start of next DIPSWITCH is too early\n", drvname, dip_option->friendly_name.c_str());
407 					break;
408 				}
409 
410 				struct GameInp *pgi_value = GameInp + bdi_value.nInput + nDIPOffset;
411 
412 				// When the pVal of one value is NULL => the DIP switch is unusable. So it will be skipped by removing it from the list
413 				if (pgi_value->Input.pVal == 0)
414 				{
415 					skip_unusable_option = true;
416 					break;
417 				}
418 
419 				// Filter away NULL entries
420 				if (bdi_value.nFlags == 0)
421 				{
422 					HandleMessage(RETRO_LOG_WARN, "Error in %sDIPList for DIPSWITCH '%s': the line '%d' is useless\n", drvname, dip_option->friendly_name.c_str(), k + 1);
423 					continue;
424 				}
425 
426 				dipswitch_core_option_value *dip_value = &dip_option->values[values_count];
427 
428 				BurnDrvGetDIPInfo(&(dip_value->bdi), k + i + 1);
429 				dip_value->pgi = pgi_value;
430 				dip_value->friendly_name = dip_value->bdi.szText;
431 
432 				bool is_default_value = (dip_value->pgi->Input.Constant.nConst & dip_value->bdi.nMask) == (dip_value->bdi.nSetting);
433 
434 				if (is_default_value)
435 				{
436 					dip_option->default_bdi = dip_value->bdi;
437 				}
438 
439 				values_count++;
440 			}
441 
442 			if (bdi.nSetting > values_count)
443 			{
444 				// Truncate the list at the values_count found to not have empty values
445 				dip_option->values.resize(values_count); // +1 for default value
446 				HandleMessage(RETRO_LOG_WARN, "Error in %sDIPList for DIPSWITCH '%s': '%d' values were intended and only '%d' were found\n", drvname, dip_option->friendly_name.c_str(), bdi.nSetting, values_count);
447 			}
448 
449 			// Skip the unusable option by removing it from the list
450 			if (skip_unusable_option)
451 			{
452 				dipswitch_core_options.pop_back();
453 				continue;
454 			}
455 
456 			pgi = GameInp + bdi.nInput + nDIPOffset;
457 
458 			j++;
459 		}
460 	}
461 
462 	evaluate_neogeo_bios_mode(drvname);
463 
464 	return 0;
465 }
466 
467 // Update DIP switches value  depending of the choice the user made in core options
apply_dipswitches_from_variables()468 static bool apply_dipswitches_from_variables()
469 {
470 	bool dip_changed = false;
471 #if 0
472 	HandleMessage(RETRO_LOG_INFO, "Apply DIP switches value from core options.\n");
473 #endif
474 	struct retro_variable var = {0};
475 
476 	for (int dip_idx = 0; dip_idx < dipswitch_core_options.size(); dip_idx++)
477 	{
478 		dipswitch_core_option *dip_option = &dipswitch_core_options[dip_idx];
479 
480 		var.key = dip_option->option_name.c_str();
481 		if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) == false || !var.value)
482 			continue;
483 
484 		for (int dip_value_idx = 0; dip_value_idx < dip_option->values.size(); dip_value_idx++)
485 		{
486 			dipswitch_core_option_value *dip_value = &(dip_option->values[dip_value_idx]);
487 
488 			if (dip_value->friendly_name.compare(var.value) != 0)
489 				continue;
490 
491 			int old_nConst = dip_value->pgi->Input.Constant.nConst;
492 
493 			dip_value->pgi->Input.Constant.nConst = (dip_value->pgi->Input.Constant.nConst & ~dip_value->bdi.nMask) | (dip_value->bdi.nSetting & dip_value->bdi.nMask);
494 			dip_value->pgi->Input.nVal = dip_value->pgi->Input.Constant.nConst;
495 			if (dip_value->pgi->Input.pVal)
496 				*(dip_value->pgi->Input.pVal) = dip_value->pgi->Input.nVal;
497 
498 			if (dip_value->pgi->Input.Constant.nConst == old_nConst)
499 			{
500 #if 0
501 				HandleMessage(RETRO_LOG_INFO, "DIP switch at PTR: [%-10d] [0x%02x] -> [0x%02x] - No change - '%s' '%s' [0x%02x]\n",
502 				dip_value->pgi->Input.pVal, old_nConst, dip_value->pgi->Input.Constant.nConst, dip_option->friendly_name.c_str(), dip_value->friendly_name, dip_value->bdi.nSetting);
503 #endif
504 			}
505 			else
506 			{
507 				dip_changed = true;
508 #if 0
509 				HandleMessage(RETRO_LOG_INFO, "DIP switch at PTR: [%-10d] [0x%02x] -> [0x%02x] - Changed   - '%s' '%s' [0x%02x]\n",
510 				dip_value->pgi->Input.pVal, old_nConst, dip_value->pgi->Input.Constant.nConst, dip_option->friendly_name.c_str(), dip_value->friendly_name, dip_value->bdi.nSetting);
511 #endif
512 			}
513 		}
514 	}
515 
516 	return dip_changed;
517 }
518 
nl_remover(TCHAR * str)519 static TCHAR* nl_remover(TCHAR* str)
520 {
521 	TCHAR* tmp = strdup(str);
522 	tmp[strcspn(tmp, "\r\n")] = 0;
523 	return tmp;
524 }
525 
create_variables_from_cheats()526 static int create_variables_from_cheats()
527 {
528 	// Load cheats so that we can turn them into core options, it needs to
529 	// be done early because libretro won't accept a variable number of core
530 	// options, and some core options need to be known before BurnDrvInit is called...
531 	ConfigCheatLoad();
532 
533 	cheat_core_options.clear();
534 	const char * drvname = BurnDrvGetTextA(DRV_NAME);
535 
536 	CheatInfo* pCurrentCheat = pCheatInfo;
537 
538 	int num = 0;
539 
540 	while (pCurrentCheat) {
541 		// Ignore "empty" cheats, they seem common in cheat bundles (as separators and/or hints ?)
542 		int count = 0;
543 		for (int i = 0; i < CHEAT_MAX_OPTIONS; i++) {
544 			if(pCurrentCheat->pOption[i] == NULL || pCurrentCheat->pOption[i]->szOptionName[0] == '\0') break;
545 			count++;
546 		}
547 		if (count > 0 && count < RETRO_NUM_CORE_OPTION_VALUES_MAX)
548 		{
549 			cheat_core_options.push_back(cheat_core_option());
550 			cheat_core_option *cheat_option = &cheat_core_options.back();
551 			std::string option_name = nl_remover(pCurrentCheat->szCheatName);
552 			cheat_option->friendly_name = SSTR( "[Cheat] " << option_name.c_str() );
553 			cheat_option->friendly_name_categorized = option_name.c_str();
554 			std::replace( option_name.begin(), option_name.end(), ' ', '_');
555 			std::replace( option_name.begin(), option_name.end(), '=', '_');
556 			std::replace( option_name.begin(), option_name.end(), ':', '_');
557 			cheat_option->option_name = SSTR( "fbneo-cheat-" << num << "-" << drvname << "-" << option_name.c_str() );
558 			cheat_option->num = num;
559 			cheat_option->values.reserve(count);
560 			cheat_option->values.assign(count, cheat_core_option_value());
561 			for (int i = 0; i < count; i++) {
562 				cheat_core_option_value *cheat_value = &cheat_option->values[i];
563 				cheat_value->nValue = i;
564 				// prepending name with value, some cheats from official pack have 2 values matching default's name,
565 				// and picking the wrong one prevents some games to boot
566 				std::string option_value_name = nl_remover(pCurrentCheat->pOption[i]->szOptionName);
567 				cheat_value->friendly_name = SSTR( i << " - " << option_value_name.c_str());
568 				if (pCurrentCheat->nDefault == i) cheat_option->default_value = SSTR( i << " - " << option_value_name.c_str());
569 			}
570 		}
571 		num++;
572 		pCurrentCheat = pCurrentCheat->pNext;
573 	}
574 	return 0;
575 }
576 
apply_cheats_from_variables()577 static int apply_cheats_from_variables()
578 {
579 	// It is also required to load cheats this after BurnDrvInit is called,
580 	// so there might be no way to avoid having 2 calls to this function...
581 	ConfigCheatLoad();
582 	bCheatsAllowed = true;
583 
584 	struct retro_variable var = {0};
585 
586 	for (int cheat_idx = 0; cheat_idx < cheat_core_options.size(); cheat_idx++)
587 	{
588 		cheat_core_option *cheat_option = &cheat_core_options[cheat_idx];
589 
590 		var.key = cheat_option->option_name.c_str();
591 		if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) == false || !var.value)
592 			continue;
593 
594 		for (int cheat_value_idx = 0; cheat_value_idx < cheat_option->values.size(); cheat_value_idx++)
595 		{
596 			cheat_core_option_value *cheat_value = &(cheat_option->values[cheat_value_idx]);
597 			if (cheat_value->friendly_name.compare(var.value) != 0)
598 				continue;
599 			CheatEnable(cheat_option->num, cheat_value->nValue);
600 		}
601 	}
602 	return 0;
603 }
604 
InputSetCooperativeLevel(const bool bExclusive,const bool bForeGround)605 int InputSetCooperativeLevel(const bool bExclusive, const bool bForeGround) { return 0; }
606 
Reinitialise(void)607 void Reinitialise(void)
608 {
609 	// Update the geometry, some games (sfiii2) and systems (megadrive) need it.
610 	BurnDrvGetFullSize(&nGameWidth, &nGameHeight);
611 	nBurnPitch = nGameWidth * nBurnBpp;
612 	struct retro_system_av_info av_info;
613 	retro_get_system_av_info(&av_info);
614 	environ_cb(nNextGeometryCall, &av_info);
615 	nNextGeometryCall = RETRO_ENVIRONMENT_SET_GEOMETRY;
616 }
617 
ForceFrameStep(bool bSkip)618 static void ForceFrameStep(bool bSkip)
619 {
620 #ifdef FBNEO_DEBUG
621 	nFramesEmulated++;
622 #endif
623 	nCurrentFrame++;
624 
625 	if (bSkip)
626 		pBurnDraw = NULL;
627 #ifdef FBNEO_DEBUG
628 	else
629 		nFramesRendered++;
630 #endif
631 	BurnDrvFrame();
632 }
633 
634 // Non-idiomatic (OutString should be to the left to match strcpy())
635 // Seems broken to not check nOutSize.
TCHARToANSI(const TCHAR * pszInString,char * pszOutString,int)636 char* TCHARToANSI(const TCHAR* pszInString, char* pszOutString, int /*nOutSize*/)
637 {
638 	if (pszOutString)
639 	{
640 		strcpy(pszOutString, pszInString);
641 		return pszOutString;
642 	}
643 
644 	return (char*)pszInString;
645 }
646 
647 // addition to support loading of roms without crc check
find_rom_by_name(char * name,const ZipEntry * list,unsigned elems,uint32_t * nCrc)648 static int find_rom_by_name(char* name, const ZipEntry *list, unsigned elems, uint32_t* nCrc)
649 {
650 	unsigned i = 0;
651 	for (i = 0; i < elems; i++)
652 	{
653 		if (list[i].szName) {
654 			if( strcmp(list[i].szName, name) == 0 )
655 			{
656 				*nCrc = list[i].nCrc;
657 				return i;
658 			}
659 		}
660 	}
661 
662 #if 0
663 	HandleMessage(RETRO_LOG_ERROR, "Not found: %s (name = %s)\n", list[i].szName, name);
664 #endif
665 
666 	return -1;
667 }
668 
find_rom_by_crc(uint32_t crc,const ZipEntry * list,unsigned elems,char ** szName)669 static int find_rom_by_crc(uint32_t crc, const ZipEntry *list, unsigned elems, char** szName)
670 {
671 	unsigned i = 0;
672 	for (i = 0; i < elems; i++)
673 	{
674 		if (list[i].szName) {
675 			if (list[i].nCrc == crc)
676 			{
677 				*szName = list[i].szName;
678 				return i;
679 			}
680 		}
681 	}
682 
683 #if 0
684 	HandleMessage(RETRO_LOG_ERROR, "Not found: 0x%X (crc: 0x%X)\n", list[i].nCrc, crc);
685 #endif
686 
687 	return -1;
688 }
689 
free_archive_list(ZipEntry * list,unsigned count)690 static void free_archive_list(ZipEntry *list, unsigned count)
691 {
692 	if (list)
693 	{
694 		for (unsigned i = 0; i < count; i++)
695 			free(list[i].szName);
696 		free(list);
697 	}
698 }
699 
archive_load_rom(uint8_t * dest,int * wrote,int i)700 static int archive_load_rom(uint8_t *dest, int *wrote, int i)
701 {
702 	if (i < 0 || i >= nRomCount)
703 		return 1;
704 
705 	int archive = pRomFind[i].nZip;
706 
707 	if (ZipOpen((char*)g_find_list_path[archive].path.c_str()) != 0)
708 		return 1;
709 
710 	BurnRomInfo ri = {0};
711 	BurnDrvGetRomInfo(&ri, i);
712 
713 	if (!(ri.nType & BRF_NODUMP))
714 	{
715 		if (ZipLoadFile(dest, ri.nLen, wrote, pRomFind[i].nPos) != 0)
716 		{
717 			ZipClose();
718 			return 1;
719 		}
720 	}
721 
722 	ZipClose();
723 	return 0;
724 }
725 
locate_archive(std::vector<located_archive> & pathList,const char * const romName)726 static void locate_archive(std::vector<located_archive>& pathList, const char* const romName)
727 {
728 	static char path[MAX_PATH];
729 
730 	if (bPatchedRomsetsEnabled)
731 	{
732 		// Search system fbneo "patched" subdirectory
733 		snprintf_nowarn(path, sizeof(path), "%s%cfbneo%cpatched%c%s", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), romName);
734 		if (ZipOpen(path) == 0)
735 		{
736 			g_find_list_path.push_back(located_archive());
737 			located_archive *located = &g_find_list_path.back();
738 			located->path = path;
739 			located->ignoreCrc = true;
740 			ZipClose();
741 			HandleMessage(RETRO_LOG_INFO, "[FBNeo] Patched romset found at %s\n", path);
742 		}
743 		else
744 			HandleMessage(RETRO_LOG_INFO, "[FBNeo] No patched romset found at %s\n", path);
745 	}
746 	// Search rom dir
747 	snprintf_nowarn(path, sizeof(path), "%s%c%s", g_rom_dir, PATH_DEFAULT_SLASH_C(), romName);
748 	if (ZipOpen(path) == 0)
749 	{
750 		g_find_list_path.push_back(located_archive());
751 		located_archive *located = &g_find_list_path.back();
752 		located->path = path;
753 		located->ignoreCrc = false;
754 		ZipClose();
755 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Romset found at %s\n", path);
756 	}
757 	else
758 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] No romset found at %s\n", path);
759 	// Search system fbneo subdirectory (where samples/hiscore are stored)
760 	snprintf_nowarn(path, sizeof(path), "%s%cfbneo%c%s", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), romName);
761 	if (ZipOpen(path) == 0)
762 	{
763 		g_find_list_path.push_back(located_archive());
764 		located_archive *located = &g_find_list_path.back();
765 		located->path = path;
766 		located->ignoreCrc = false;
767 		ZipClose();
768 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Romset found at %s\n", path);
769 	}
770 	else
771 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] No romset found at %s\n", path);
772 	// Search system directory
773 	snprintf_nowarn(path, sizeof(path), "%s%c%s", g_system_dir, PATH_DEFAULT_SLASH_C(), romName);
774 	if (ZipOpen(path) == 0)
775 	{
776 		g_find_list_path.push_back(located_archive());
777 		located_archive *located = &g_find_list_path.back();
778 		located->path = path;
779 		located->ignoreCrc = false;
780 		ZipClose();
781 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Romset found at %s\n", path);
782 	}
783 	else
784 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] No romset found at %s\n", path);
785 }
786 
787 // This code is very confusing. The original code is even more confusing :(
open_archive()788 static bool open_archive()
789 {
790 	int nMemLen;														// Zip name number
791 
792 	// Count the number of roms needed
793 	for (nRomCount = 0; ; nRomCount++) {
794 		if (BurnDrvGetRomInfo(NULL, nRomCount)) {
795 			break;
796 		}
797 	}
798 	if (nRomCount <= 0) {
799 		return false;
800 	}
801 
802 	// Create an array for holding lookups for each rom -> zip entries
803 	nMemLen = nRomCount * sizeof(struct RomFind);
804 	pRomFind = (struct RomFind*)malloc(nMemLen);
805 	if (pRomFind == NULL) {
806 		return false;
807 	}
808 	memset(pRomFind, 0, nMemLen);
809 
810 	g_find_list_path.clear();
811 
812 	// List all locations for archives, max number of differently named archives involved should be 3 (romset, parent, bios)
813 	// with each of those having 4 potential locations (see locate_archive)
814 	char *rom_name;
815 	for (unsigned index = 0; index < 3; index++)
816 	{
817 		if (BurnDrvGetZipName(&rom_name, index))
818 			continue;
819 
820 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Searching all possible locations for romset %s\n", rom_name);
821 
822 		locate_archive(g_find_list_path, rom_name);
823 	}
824 
825 	for (unsigned z = 0; z < g_find_list_path.size(); z++)
826 	{
827 		if (ZipOpen((char*)g_find_list_path[z].path.c_str()) != 0)
828 		{
829 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Failed to open archive %s\n", g_find_list_path[z].path.c_str());
830 			return false;
831 		}
832 
833 		ZipEntry *list = NULL;
834 		int count;
835 		ZipGetList(&list, &count);
836 
837 		// Try to map the ROMs FBNeo wants to ROMs we find inside our pretty archives ...
838 		for (unsigned i = 0; i < nRomCount; i++)
839 		{
840 			if (pRomFind[i].nState == STAT_OK)
841 				continue;
842 
843 			struct BurnRomInfo ri;
844 			memset(&ri, 0, sizeof(ri));
845 			BurnDrvGetRomInfo(&ri, i);
846 
847 			if (ri.nType == 0 || ri.nLen == 0 || ri.nCrc == 0)
848 			{
849 				pRomFind[i].nState = STAT_OK;
850 				continue;
851 			}
852 
853 			char *real_rom_name;
854 			uint32_t real_rom_crc;
855 			int index = find_rom_by_crc(ri.nCrc, list, count, &real_rom_name);
856 
857 			BurnDrvGetRomName(&rom_name, i, 0);
858 
859 			bool unknown_crc = false;
860 
861 			if (index < 0 && g_find_list_path[z].ignoreCrc && bPatchedRomsetsEnabled)
862 			{
863 				index = find_rom_by_name(rom_name, list, count, &real_rom_crc);
864 				if (index >= 0)
865 					unknown_crc = true;
866 			}
867 
868 			if (index >= 0)
869 			{
870 				if (unknown_crc)
871 					HandleMessage(RETRO_LOG_WARN, "[FBNeo] Using ROM with unknown crc 0x%08x and name %s from archive %s\n", real_rom_crc, rom_name, g_find_list_path[z].path.c_str());
872 				else
873 					HandleMessage(RETRO_LOG_INFO, "[FBNeo] Using ROM with known crc 0x%08x and name %s from archive %s\n", ri.nCrc, real_rom_name, g_find_list_path[z].path.c_str());
874 			}
875 			else
876 			{
877 				continue;
878 			}
879 
880 			if (is_neogeo_game)
881 				set_neogeo_bios_availability(list[index].szName, list[index].nCrc, (g_find_list_path[z].ignoreCrc && bPatchedRomsetsEnabled));
882 
883 			// Yay, we found it!
884 			pRomFind[i].nZip = z;
885 			pRomFind[i].nPos = index;
886 			pRomFind[i].nState = STAT_OK;
887 
888 			if (list[index].nLen < ri.nLen)
889 				pRomFind[i].nState = STAT_SMALL;
890 			else if (list[index].nLen > ri.nLen)
891 				pRomFind[i].nState = STAT_LARGE;
892 		}
893 
894 		free_archive_list(list, count);
895 		ZipClose();
896 	}
897 
898 	if (is_neogeo_game)
899 		set_neo_system_bios();
900 
901 	// Going over every rom to see if they are properly loaded before we continue ...
902 	for (unsigned i = 0; i < nRomCount; i++)
903 	{
904 		if (pRomFind[i].nState != STAT_OK)
905 		{
906 			struct BurnRomInfo ri;
907 			memset(&ri, 0, sizeof(ri));
908 			BurnDrvGetRomInfo(&ri, i);
909 			if(!(ri.nType & BRF_OPT))
910 			{
911 				BurnDrvGetRomName(&rom_name, i, 0);
912 				HandleMessage(RETRO_LOG_ERROR, "[FBNeo] ROM at index %d with name %s and CRC 0x%08x is required ...\n", i, rom_name, ri.nCrc);
913 				return false;
914 			}
915 		}
916 	}
917 
918 	BurnExtLoadRom = archive_load_rom;
919 	return true;
920 }
921 
SetRotation()922 static void SetRotation()
923 {
924 	unsigned rotation;
925 	switch (BurnDrvGetFlags() & (BDF_ORIENTATION_FLIPPED | BDF_ORIENTATION_VERTICAL))
926 	{
927 		case BDF_ORIENTATION_VERTICAL:
928 			rotation = (nVerticalMode == 1 ? 0 : (nVerticalMode == 2 ? 2 : 1));
929 			break;
930 		case BDF_ORIENTATION_FLIPPED:
931 			rotation = (nVerticalMode == 1 ? 1 : (nVerticalMode == 2 ? 3 : 2));
932 			break;
933 		case BDF_ORIENTATION_VERTICAL | BDF_ORIENTATION_FLIPPED:
934 			rotation = (nVerticalMode == 1 ? 2 : (nVerticalMode == 2 ? 0 : 3));
935 			break;
936 		default:
937 			rotation = (nVerticalMode == 1 ? 3 : (nVerticalMode == 2 ? 1 : 0));;
938 			break;
939 	}
940 	bRotationDone = environ_cb(RETRO_ENVIRONMENT_SET_ROTATION, &rotation);
941 }
942 
943 #ifdef AUTOGEN_DATS
CreateAllDatfiles()944 int CreateAllDatfiles()
945 {
946 	INT32 nRet = 0;
947 	TCHAR szFilename[MAX_PATH];
948 
949 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Arcade only");
950 	create_datfile(szFilename, DAT_ARCADE_ONLY);
951 
952 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Megadrive only");
953 	create_datfile(szFilename, DAT_MEGADRIVE_ONLY);
954 
955 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, PC-Engine only");
956 	create_datfile(szFilename, DAT_PCENGINE_ONLY);
957 
958 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, TurboGrafx16 only");
959 	create_datfile(szFilename, DAT_TG16_ONLY);
960 
961 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, SuprGrafx only");
962 	create_datfile(szFilename, DAT_SGX_ONLY);
963 
964 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Sega SG-1000 only");
965 	create_datfile(szFilename, DAT_SG1000_ONLY);
966 
967 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, ColecoVision only");
968 	create_datfile(szFilename, DAT_COLECO_ONLY);
969 
970 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Master System only");
971 	create_datfile(szFilename, DAT_MASTERSYSTEM_ONLY);
972 
973 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Game Gear only");
974 	create_datfile(szFilename, DAT_GAMEGEAR_ONLY);
975 
976 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, MSX 1 Games only");
977 	create_datfile(szFilename, DAT_MSX_ONLY);
978 
979 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, ZX Spectrum Games only");
980 	create_datfile(szFilename, DAT_SPECTRUM_ONLY);
981 
982 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, NES Games only");
983 	create_datfile(szFilename, DAT_NES_ONLY);
984 
985 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, FDS Games only");
986 	create_datfile(szFilename, DAT_FDS_ONLY);
987 
988 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Neogeo only");
989 	create_datfile(szFilename, DAT_NEOGEO_ONLY);
990 
991 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, NeoGeo Pocket Games only");
992 	create_datfile(szFilename, DAT_NGP_ONLY);
993 
994 	snprintf_nowarn(szFilename, sizeof(szFilename), "%s%c%s (%s).dat", "dats", PATH_DEFAULT_SLASH_C(), APP_TITLE, "ClrMame Pro XML, Fairchild Channel F Games only");
995 	create_datfile(szFilename, DAT_CHANNELF_ONLY);
996 
997 	return nRet;
998 }
999 #endif
1000 
retro_init()1001 void retro_init()
1002 {
1003 	struct retro_log_callback log;
1004 
1005 	if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
1006 		log_cb = log.log;
1007 	else
1008 		log_cb = log_dummy;
1009 
1010 	libretro_msg_interface_version = 0;
1011 	environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &libretro_msg_interface_version);
1012 
1013 	snprintf_nowarn(szAppBurnVer, sizeof(szAppBurnVer), "%x.%x.%x.%02x", nBurnVer >> 20, (nBurnVer >> 16) & 0x0F, (nBurnVer >> 8) & 0xFF, nBurnVer & 0xFF);
1014 	BurnLibInit();
1015 #ifdef AUTOGEN_DATS
1016 	CreateAllDatfiles();
1017 #endif
1018 
1019 	nFrameskipType             = 0;
1020 	nFrameskipThreshold        = 0;
1021 	nFrameskipCounter          = 0;
1022 	nAudioLatency              = 0;
1023 	bUpdateAudioLatency        = false;
1024 	retro_audio_buff_active    = false;
1025 	retro_audio_buff_occupancy = 0;
1026 	retro_audio_buff_underrun  = false;
1027 
1028 	DspInit();
1029 
1030 	// Check RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK support
1031 	bLibretroSupportsAudioBuffStatus = environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
1032 }
1033 
retro_deinit()1034 void retro_deinit()
1035 {
1036 	DspExit();
1037 	BurnLibExit();
1038 }
1039 
retro_reset()1040 void retro_reset()
1041 {
1042 	// Saving minimal savestate (handle some machine settings)
1043 	// note : This is only useful to avoid losing nvram when switching from mvs to aes/unibios and resetting,
1044 	//        it can actually be "harmful" in other games (trackfld)
1045 	if (is_neogeo_game && BurnStateSave(g_autofs_path, 0) == 0 && path_is_valid(g_autofs_path))
1046 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] EEPROM succesfully saved to %s\n", g_autofs_path);
1047 
1048 	if (pgi_reset)
1049 	{
1050 		pgi_reset->Input.nVal = 1;
1051 		*(pgi_reset->Input.pVal) = pgi_reset->Input.nVal;
1052 	}
1053 
1054 	check_variables();
1055 	apply_dipswitches_from_variables();
1056 	apply_cheats_from_variables();
1057 
1058 	// restore the NeoSystem because it was changed during the gameplay
1059 	if (is_neogeo_game)
1060 		set_neo_system_bios();
1061 
1062 	ForceFrameStep(1);
1063 
1064 	// Loading minimal savestate (handle some machine settings)
1065 	if (is_neogeo_game && BurnStateLoad(g_autofs_path, 0, NULL) == 0)
1066 	{
1067 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] EEPROM succesfully loaded from %s\n", g_autofs_path);
1068 		// eeproms are loading nCurrentFrame, but we probably don't want this
1069 		nCurrentFrame = 0;
1070 	}
1071 }
1072 
VideoBufferInit()1073 static void VideoBufferInit()
1074 {
1075 	size_t nSize = nGameWidth * nGameHeight * nBurnBpp;
1076 	if (pVidImage)
1077 		pVidImage = (UINT8*)realloc(pVidImage, nSize);
1078 	else
1079 		pVidImage = (UINT8*)malloc(nSize);
1080 	if (pVidImage)
1081 		memset(pVidImage, 0, nSize);
1082 }
1083 
retro_run()1084 void retro_run()
1085 {
1086 #if 0
1087 	// Disabled for now since retroarch might not be totally reliable about it ?
1088 	int nAudioVideoEnable = -1;
1089 	environ_cb(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &nAudioVideoEnable);
1090 
1091 	// Only draw when required by frontend or core
1092 	pBurnDraw = ((BurnDrvGetFlags() & BDF_RUNAHEAD_DRAWSYNC) || (nAudioVideoEnable & 1)) ? pVidImage : NULL;
1093 	// Disabling audio seems broken in retroarch at the moment with runahead,
1094 	// i think it's playing back sound from all frames it ran or something, even when it says it won't,
1095 	// 2-instances with its hard-disabled-audio seems ok
1096 	// pBurnSoundOut = nAudioVideoEnable & 2 && !(nAudioVideoEnable & 8) ? pAudBuffer : NULL;
1097 	pBurnSoundOut = !(nAudioVideoEnable & 8) ? pAudBuffer : NULL;
1098 #else
1099 	pBurnDraw = pVidImage; // set to NULL to skip frame rendering
1100 	pBurnSoundOut = pAudBuffer; // set to NULL to skip sound rendering
1101 #endif
1102 
1103 	bool bSkipFrame = false;
1104 
1105 	if (gui_show && nGameWidth > 0 && nGameHeight > 0)
1106 	{
1107 		gui_draw();
1108 		video_cb(gui_get_framebuffer(), nGameWidth, nGameHeight, nGameWidth * sizeof(unsigned));
1109 		audio_batch_cb(pAudBuffer, nBurnSoundLen);
1110 		return;
1111 	}
1112 
1113 	InputMake();
1114 
1115 	// Check whether current frame should be skipped
1116 	if ((nFrameskipType > 1) && retro_audio_buff_active)
1117 	{
1118 		switch (nFrameskipType)
1119 		{
1120 			case 2:
1121 				bSkipFrame = retro_audio_buff_underrun;
1122 			break;
1123 			case 3:
1124 				bSkipFrame = (retro_audio_buff_occupancy < nFrameskipThreshold);
1125 			break;
1126 		}
1127 
1128 		if (!bSkipFrame || nFrameskipCounter >= FRAMESKIP_MAX)
1129 		{
1130 			bSkipFrame        = false;
1131 			nFrameskipCounter = 0;
1132 		}
1133 		else
1134 			nFrameskipCounter++;
1135 	}
1136 	else if (!bLibretroSupportsAudioBuffStatus || nFrameskipType == 1)
1137 		bSkipFrame = !(nCurrentFrame % nFrameskip == 0);
1138 
1139 	// if frameskip settings have changed, update frontend audio latency
1140 	if (bUpdateAudioLatency)
1141 	{
1142 		if (nFrameskipType > 1)
1143 		{
1144 			float frame_time_msec = 100000.0f / nBurnFPS;
1145 			nAudioLatency = (UINT32)((6.0f * frame_time_msec) + 0.5f);
1146 			nAudioLatency = (nAudioLatency + 0x1F) & ~0x1F;
1147 
1148 			struct retro_audio_buffer_status_callback buf_status_cb;
1149 			buf_status_cb.callback = retro_audio_buff_status_cb;
1150 			environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, &buf_status_cb);
1151 		}
1152 		else
1153 		{
1154 			nAudioLatency = 0;
1155 			environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
1156 		}
1157 		environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY, &nAudioLatency);
1158 		bUpdateAudioLatency = false;
1159 	}
1160 
1161 	ForceFrameStep(bSkipFrame);
1162 
1163 	if (bLowPassFilterEnabled)
1164 		DspDo(pAudBuffer, nBurnSoundLen);
1165 	audio_batch_cb(pAudBuffer, nBurnSoundLen);
1166 	bool updated = false;
1167 
1168 	if (bVidImageNeedRealloc)
1169 	{
1170 		bVidImageNeedRealloc = false;
1171 		VideoBufferInit();
1172 		// current frame will be corrupted, let's dupe instead
1173 		video_cb(NULL, nGameWidth, nGameHeight, nBurnPitch);
1174 	}
1175 	else
1176 		video_cb(pVidImage, nGameWidth, nGameHeight, nBurnPitch);
1177 
1178 	if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
1179 	{
1180 		UINT32 old_nVerticalMode = nVerticalMode;
1181 		UINT32 old_nFrameskipType = nFrameskipType;
1182 
1183 		check_variables();
1184 
1185 		apply_dipswitches_from_variables();
1186 		apply_cheats_from_variables();
1187 
1188 		// change orientation/geometry if vertical mode was toggled on/off
1189 		if (old_nVerticalMode != nVerticalMode)
1190 		{
1191 			SetRotation();
1192 			struct retro_system_av_info av_info;
1193 			retro_get_system_av_info(&av_info);
1194 			environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &av_info);
1195 		}
1196 
1197 		if (old_nFrameskipType != nFrameskipType)
1198 			bUpdateAudioLatency = true;
1199 	}
1200 }
1201 
retro_cheat_reset()1202 void retro_cheat_reset() {}
retro_cheat_set(unsigned,bool,const char *)1203 void retro_cheat_set(unsigned, bool, const char *) {}
1204 
retro_get_system_av_info(struct retro_system_av_info * info)1205 void retro_get_system_av_info(struct retro_system_av_info *info)
1206 {
1207 	int game_aspect_x, game_aspect_y;
1208 	bVidImageNeedRealloc = true;
1209 	if (nBurnDrvActive != ~0U)
1210 	{
1211 		BurnDrvGetAspect(&game_aspect_x, &game_aspect_y);
1212 
1213 		// if game is vertical and rotation couldn't occur, "fix" the rotated aspect ratio
1214 		if ((BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) && !bRotationDone)
1215 		{
1216 			int temp = game_aspect_x;
1217 			game_aspect_x = game_aspect_y;
1218 			game_aspect_y = temp;
1219 		}
1220 	}
1221 	else
1222 	{
1223 		game_aspect_x = 4;
1224 		game_aspect_y = 3;
1225 	}
1226 
1227 	INT32 oldMax = nGameMaximumGeometry;
1228 	nGameMaximumGeometry = nGameWidth > nGameHeight ? nGameWidth : nGameHeight;
1229 	nGameMaximumGeometry = oldMax > nGameMaximumGeometry ? oldMax : nGameMaximumGeometry;
1230 	struct retro_game_geometry geom = { (unsigned)nGameWidth, (unsigned)nGameHeight, (unsigned)nGameMaximumGeometry, (unsigned)nGameMaximumGeometry };
1231 	if (oldMax != 0 && oldMax < nGameMaximumGeometry)
1232 		nNextGeometryCall = RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO;
1233 
1234 	geom.aspect_ratio = (nVerticalMode != 0 ? ((float)game_aspect_y / (float)game_aspect_x) : ((float)game_aspect_x / (float)game_aspect_y));
1235 
1236 	struct retro_system_timing timing = { (nBurnFPS / 100.0), (nBurnFPS / 100.0) * nAudSegLen };
1237 
1238 	info->geometry = geom;
1239 	info->timing   = timing;
1240 }
1241 
HighCol15(INT32 r,INT32 g,INT32 b,INT32)1242 static UINT32 __cdecl HighCol15(INT32 r, INT32 g, INT32 b, INT32  /* i */)
1243 {
1244 	UINT32 t;
1245 	t =(r<<7)&0x7c00;
1246 	t|=(g<<2)&0x03e0;
1247 	t|=(b>>3)&0x001f;
1248 	return t;
1249 }
1250 
1251 // 16-bit
HighCol16(INT32 r,INT32 g,INT32 b,INT32)1252 static UINT32 __cdecl HighCol16(INT32 r, INT32 g, INT32 b, INT32 /* i */)
1253 {
1254 	UINT32 t;
1255 	t =(r<<8)&0xf800;
1256 	t|=(g<<3)&0x07e0;
1257 	t|=(b>>3)&0x001f;
1258 	return t;
1259 }
1260 
1261 // 32-bit
HighCol32(INT32 r,INT32 g,INT32 b,INT32)1262 static UINT32 __cdecl HighCol32(INT32 r, INT32 g, INT32 b, INT32  /* i */)
1263 {
1264 	UINT32 t;
1265 	t =(r<<16)&0xff0000;
1266 	t|=(g<<8 )&0x00ff00;
1267 	t|=(b    )&0x0000ff;
1268 
1269 	return t;
1270 }
1271 
SetBurnHighCol(INT32 nDepth)1272 INT32 SetBurnHighCol(INT32 nDepth)
1273 {
1274 	BurnRecalcPal();
1275 
1276 	enum retro_pixel_format fmt;
1277 
1278 	if (nDepth == 32) {
1279 		fmt = RETRO_PIXEL_FORMAT_XRGB8888;
1280 		if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1281 		{
1282 			BurnHighCol = HighCol32;
1283 			nBurnBpp = 4;
1284 			return 0;
1285 		}
1286 	}
1287 
1288 	fmt = RETRO_PIXEL_FORMAT_RGB565;
1289 	if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1290 	{
1291 		BurnHighCol = HighCol16;
1292 		nBurnBpp = 2;
1293 		return 0;
1294 	}
1295 
1296 	fmt = RETRO_PIXEL_FORMAT_0RGB1555;
1297 	if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1298 	{
1299 		BurnHighCol = HighCol15;
1300 		nBurnBpp = 2;
1301 		return 0;
1302 	}
1303 
1304 	return 0;
1305 }
1306 
SetColorDepth()1307 static void SetColorDepth()
1308 {
1309 	if ((BurnDrvGetFlags() & BDF_16BIT_ONLY) || !bAllowDepth32) {
1310 		SetBurnHighCol(16);
1311 	} else {
1312 		SetBurnHighCol(32);
1313 	}
1314 	nBurnPitch = nGameWidth * nBurnBpp;
1315 }
1316 
AudioBufferInit(INT32 sample_rate,INT32 fps)1317 static void AudioBufferInit(INT32 sample_rate, INT32 fps)
1318 {
1319 	nAudSegLen = (sample_rate * 100 + (fps >> 1)) / fps;
1320 	size_t nSize = nAudSegLen<<2 * sizeof(int16_t);
1321 	if (pAudBuffer)
1322 		pAudBuffer = (int16_t*)realloc(pAudBuffer, nSize);
1323 	else
1324 		pAudBuffer = (int16_t*)malloc(nSize);
1325 	if (pAudBuffer)
1326 		memset(pAudBuffer, 0, nSize);
1327 	nBurnSoundLen = nAudSegLen;
1328 }
1329 
extract_basename(char * buf,const char * path,size_t size,char * prefix)1330 static void extract_basename(char *buf, const char *path, size_t size, char *prefix)
1331 {
1332 	strcpy(buf, prefix);
1333 	strncat(buf, path_basename(path), size - 1);
1334 	buf[size - 1] = '\0';
1335 
1336 	char *ext = strrchr(buf, '.');
1337 	if (ext)
1338 		*ext = '\0';
1339 }
1340 
extract_directory(char * buf,const char * path,size_t size)1341 static void extract_directory(char *buf, const char *path, size_t size)
1342 {
1343 	strncpy(buf, path, size - 1);
1344 	buf[size - 1] = '\0';
1345 
1346 	char *base = strrchr(buf, PATH_DEFAULT_SLASH_C());
1347 
1348 	if (base)
1349 		*base = '\0';
1350 	else
1351 	{
1352 		buf[0] = '.';
1353 		buf[1] = '\0';
1354 	}
1355 }
1356 
1357 // MemCard support
MemCardRead(TCHAR * szFilename,unsigned char * pData,int nSize)1358 static int MemCardRead(TCHAR* szFilename, unsigned char* pData, int nSize)
1359 {
1360 	const char* szHeader  = "FB1 FC1 ";				// File + chunk identifier
1361 	char szReadHeader[8] = "";
1362 
1363 	bMemCardFC1Format = false;
1364 
1365 	FILE* fp = fopen(szFilename, _T("rb"));
1366 	if (fp == NULL) {
1367 		return 1;
1368 	}
1369 
1370 	fread(szReadHeader, 1, 8, fp);					// Read identifiers
1371 	if (memcmp(szReadHeader, szHeader, 8) == 0) {
1372 
1373 		// FB Alpha memory card file
1374 
1375 		int nChunkSize = 0;
1376 		int nVersion = 0;
1377 
1378 		bMemCardFC1Format = true;
1379 
1380 		fread(&nChunkSize, 1, 4, fp);				// Read chunk size
1381 		if (nSize < nChunkSize - 32) {
1382 			fclose(fp);
1383 			return 1;
1384 		}
1385 
1386 		fread(&nVersion, 1, 4, fp);					// Read version
1387 		if (nVersion < nMinVersion) {
1388 			fclose(fp);
1389 			return 1;
1390 		}
1391 		fread(&nVersion, 1, 4, fp);
1392 #if 0
1393 		if (nVersion < nBurnVer) {
1394 			fclose(fp);
1395 			return 1;
1396 		}
1397 #endif
1398 
1399 		fseek(fp, 0x0C, SEEK_CUR);					// Move file pointer to the start of the data block
1400 
1401 		fread(pData, 1, nChunkSize - 32, fp);		// Read the data
1402 	} else {
1403 
1404 		// MAME or old FB Alpha memory card file
1405 
1406 		unsigned char* pTemp = (unsigned char*)malloc(nSize >> 1);
1407 
1408 		memset(pData, 0, nSize);
1409 		fseek(fp, 0x00, SEEK_SET);
1410 
1411 		if (pTemp) {
1412 			fread(pTemp, 1, nSize >> 1, fp);
1413 
1414 			for (int i = 1; i < nSize; i += 2) {
1415 				pData[i] = pTemp[i >> 1];
1416 			}
1417 
1418 			free(pTemp);
1419 			pTemp = NULL;
1420 		}
1421 	}
1422 
1423 	fclose(fp);
1424 
1425 	return 0;
1426 }
1427 
MemCardWrite(TCHAR * szFilename,unsigned char * pData,int nSize)1428 static int MemCardWrite(TCHAR* szFilename, unsigned char* pData, int nSize)
1429 {
1430 	FILE* fp = _tfopen(szFilename, _T("wb"));
1431 	if (fp == NULL) {
1432 		return 1;
1433 	}
1434 
1435 	if (bMemCardFC1Format) {
1436 
1437 		// FB Alpha memory card file
1438 
1439 		const char* szFileHeader  = "FB1 ";				// File identifier
1440 		const char* szChunkHeader = "FC1 ";				// Chunk identifier
1441 		const int nZero = 0;
1442 
1443 		int nChunkSize = nSize + 32;
1444 
1445 		fwrite(szFileHeader, 1, 4, fp);
1446 		fwrite(szChunkHeader, 1, 4, fp);
1447 
1448 		fwrite(&nChunkSize, 1, 4, fp);					// Chunk size
1449 
1450 		fwrite(&nBurnVer, 1, 4, fp);					// Version of FBA this was saved from
1451 		fwrite(&nMinVersion, 1, 4, fp);					// Min version of FBA data will work with
1452 
1453 		fwrite(&nZero, 1, 4, fp);						// Reserved
1454 		fwrite(&nZero, 1, 4, fp);						//
1455 		fwrite(&nZero, 1, 4, fp);						//
1456 
1457 		fwrite(pData, 1, nSize, fp);
1458 	} else {
1459 
1460 		// MAME or old FB Alpha memory card file
1461 
1462 		unsigned char* pTemp = (unsigned char*)malloc(nSize >> 1);
1463 		if (pTemp) {
1464 			for (int i = 1; i < nSize; i += 2) {
1465 				pTemp[i >> 1] = pData[i];
1466 			}
1467 
1468 			fwrite(pTemp, 1, nSize >> 1, fp);
1469 
1470 			free(pTemp);
1471 			pTemp = NULL;
1472 		}
1473 	}
1474 
1475 	fclose(fp);
1476 
1477 	return 0;
1478 }
1479 
MemCardDoInsert(struct BurnArea * pba)1480 static int MemCardDoInsert(struct BurnArea* pba)
1481 {
1482 	if (MemCardRead(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen)) {
1483 		return 1;
1484 	}
1485 
1486 	return 0;
1487 }
1488 
MemCardDoEject(struct BurnArea * pba)1489 static int MemCardDoEject(struct BurnArea* pba)
1490 {
1491 	if (MemCardWrite(szMemoryCardFile, (unsigned char*)pba->Data, pba->nLen) == 0) {
1492 		return 0;
1493 	}
1494 
1495 	return 1;
1496 }
1497 
MemCardInsert()1498 static int MemCardInsert()
1499 {
1500 	BurnAcb = MemCardDoInsert;
1501 	BurnAreaScan(ACB_WRITE | ACB_MEMCARD, &nMinVersion);
1502 
1503 	return 0;
1504 }
1505 
MemCardEject()1506 static int MemCardEject()
1507 {
1508 	BurnAcb = MemCardDoEject;
1509 	nMinVersion = 0;
1510 	BurnAreaScan(ACB_READ | ACB_MEMCARD, &nMinVersion);
1511 
1512 	return 0;
1513 }
1514 
BurnDrvGetIndexByName(const char * name)1515 static unsigned int BurnDrvGetIndexByName(const char* name)
1516 {
1517 	unsigned int ret = ~0U;
1518 	for (unsigned int i = 0; i < nBurnDrvCount; i++) {
1519 		nBurnDrvActive = i;
1520 		if (strcmp(BurnDrvGetText(DRV_NAME), name) == 0) {
1521 			ret = i;
1522 			break;
1523 		}
1524 	}
1525 	return ret;
1526 }
1527 
SetUguiError(const char * error)1528 static void SetUguiError(const char* error)
1529 {
1530 	gui_set_message(error);
1531 	gui_show = true;
1532 	enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
1533 	environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt);
1534 	gui_init(nGameWidth, nGameHeight, sizeof(unsigned));
1535 	gui_set_window_title("FBNeo Error");
1536 }
1537 
retro_load_game_common()1538 static bool retro_load_game_common()
1539 {
1540 	const char *dir = NULL;
1541 	// If save directory is defined use it, ...
1542 	if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir) {
1543 		memcpy(g_save_dir, dir, sizeof(g_save_dir));
1544 		HandleMessage(RETRO_LOG_INFO, "Setting save dir to %s\n", g_save_dir);
1545 	} else {
1546 		// ... otherwise use rom directory
1547 		strncpy(g_save_dir, g_rom_dir, sizeof(g_save_dir));
1548 		HandleMessage(RETRO_LOG_WARN, "Save dir not defined => use roms dir %s\n", g_save_dir);
1549 	}
1550 
1551 	// If system directory is defined use it, ...
1552 	if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
1553 		memcpy(g_system_dir, dir, sizeof(g_system_dir));
1554 		HandleMessage(RETRO_LOG_INFO, "Setting system dir to %s\n", g_system_dir);
1555 	} else {
1556 		// ... otherwise use rom directory
1557 		strncpy(g_system_dir, g_rom_dir, sizeof(g_system_dir));
1558 		HandleMessage(RETRO_LOG_WARN, "System dir not defined => use roms dir %s\n", g_system_dir);
1559 	}
1560 
1561 	// Initialize EEPROM path
1562 	snprintf_nowarn (szAppEEPROMPath, sizeof(szAppEEPROMPath), "%s%cfbneo%c", g_save_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
1563 
1564 	// Create EEPROM path if it does not exist
1565 	// because of some bug on gekko based devices (see https://github.com/libretro/libretro-common/issues/161), we can't use the szAppEEPROMPath variable which requires the trailing slash
1566 	char EEPROMPathToCreate[MAX_PATH];
1567 	snprintf_nowarn (EEPROMPathToCreate, sizeof(EEPROMPathToCreate), "%s%cfbneo", g_save_dir, PATH_DEFAULT_SLASH_C());
1568 	path_mkdir(EEPROMPathToCreate);
1569 
1570 	// Initialize Hiscore path
1571 	snprintf_nowarn (szAppHiscorePath, sizeof(szAppHiscorePath), "%s%cfbneo%c", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
1572 
1573 	// Initialize Samples path
1574 	snprintf_nowarn (szAppSamplesPath, sizeof(szAppSamplesPath), "%s%cfbneo%csamples%c", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
1575 
1576 	// Initialize Cheats path
1577 	snprintf_nowarn (szAppCheatsPath, sizeof(szAppCheatsPath), "%s%cfbneo%ccheats%c", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
1578 
1579 	// Initialize Blend path
1580 	snprintf_nowarn (szAppBlendPath, sizeof(szAppBlendPath), "%s%cfbneo%cblend%c", g_system_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C());
1581 
1582 	// Initialize HDD path
1583 	snprintf_nowarn (szAppHDDPath, sizeof(szAppHDDPath), "%s%c", g_rom_dir, PATH_DEFAULT_SLASH_C());
1584 
1585 	gui_show = false;
1586 
1587 #ifdef FBNEO_DEBUG
1588 	for (nBurnDrvActive=0; nBurnDrvActive<nBurnDrvCount; nBurnDrvActive++)
1589 	{
1590 		if (BurnDrvGetFlags() & BDF_CLONE) {
1591 			if (BurnDrvGetTextA(DRV_PARENT) == NULL)
1592 			{
1593 				HandleMessage(RETRO_LOG_INFO, "Clone %s is missing parent\n", BurnDrvGetTextA(DRV_NAME));
1594 			}
1595 		}
1596 		else
1597 		{
1598 			if (BurnDrvGetTextA(DRV_PARENT) != NULL)
1599 			{
1600 				HandleMessage(RETRO_LOG_INFO, "%s has a parent but isn't marked as clone\n", BurnDrvGetTextA(DRV_NAME));
1601 			}
1602 		}
1603 	}
1604 #endif
1605 
1606 	nBurnDrvActive = BurnDrvGetIndexByName(g_driver_name);
1607 	if (nBurnDrvActive < nBurnDrvCount) {
1608 
1609 		// If the game is marked as not working, let's stop here
1610 		if (!(BurnDrvIsWorking())) {
1611 			SetUguiError("This romset is known but marked as not working\nOne of its clones might work so maybe give it a try");
1612 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] This romset is known but marked as not working\n");
1613 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] One of its clones might work so maybe give it a try\n");
1614 			goto end;
1615 		}
1616 
1617 		// If the game is a bios, let's stop here
1618 		if ((BurnDrvGetFlags() & BDF_BOARDROM)) {
1619 			SetUguiError("Bioses aren't meant to be launched this way");
1620 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Bioses aren't meant to be launched this way\n");
1621 			goto end;
1622 		}
1623 
1624 		if ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOCD && CDEmuImage[0] == '\0') {
1625 			SetUguiError("You need a disc image to launch neogeo CD\n");
1626 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] You need a disc image to launch neogeo CD\n");
1627 			goto end;
1628 		}
1629 
1630 		is_neogeo_game = ((BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO);
1631 
1632 		// Define nMaxPlayers early;
1633 		nMaxPlayers = BurnDrvGetMaxPlayers();
1634 
1635 		// Set sane default device types
1636 		SetDefaultDeviceTypes();
1637 
1638 		// Initialize inputs
1639 		InputInit();
1640 
1641 		// Calling RETRO_ENVIRONMENT_SET_CONTROLLER_INFO after RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS shouldn't hurt ?
1642 		SetControllerInfo();
1643 
1644 		// Initialize dipswitches
1645 		create_variables_from_dipswitches();
1646 
1647 		// Initialize debug variables
1648 		nBurnLayer = 0xff;
1649 		nSpriteEnable = 0xff;
1650 
1651 		// Create cheats core options
1652 		create_variables_from_cheats();
1653 
1654 		set_environment();
1655 		check_variables();
1656 
1657 		if (nFrameskipType > 1)
1658 			bUpdateAudioLatency = true;
1659 
1660 #ifdef USE_CYCLONE
1661 		SetSekCpuCore();
1662 #endif
1663 
1664 		if (!open_archive()) {
1665 #ifdef INCLUDE_7Z_SUPPORT
1666 			SetUguiError("This romset is known but yours doesn't match this emulator and its version\nRead https://docs.libretro.com/library/fbneo/#building-romsets-for-fbneo");
1667 #else
1668 			SetUguiError("This romset is known but yours doesn't match this emulator and its version\nNote that 7z support is disabled for your platform\nRead https://docs.libretro.com/library/fbneo/#building-romsets-for-fbneo");
1669 #endif
1670 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] This romset is known but yours doesn't match this emulator and its version\n");
1671 #ifndef INCLUDE_7Z_SUPPORT
1672 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Note that 7z support is disabled for your platform\n");
1673 #endif
1674 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Read https://docs.libretro.com/library/fbneo/#building-romsets-for-fbneo\n");
1675 			goto end;
1676 		}
1677 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] No missing files, proceeding\n");
1678 
1679 		// Announcing to fbneo which samplerate we want
1680 		// Some game drivers won't initialize with an undefined nBurnSoundLen
1681 		nBurnSoundRate = g_audio_samplerate;
1682 		AudioBufferInit(nBurnSoundRate, 6000);
1683 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Samplerate set to %d\n", nBurnSoundRate);
1684 
1685 		// Start CD reader emulation if needed
1686 		if (nGameType == RETRO_GAME_TYPE_NEOCD) {
1687 			if (CDEmuInit()) {
1688 				HandleMessage(RETRO_LOG_INFO, "[FBNeo] Starting neogeo CD\n");
1689 			}
1690 		}
1691 
1692 		// Apply dipswitches
1693 		apply_dipswitches_from_variables();
1694 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Applied dipswitches from core options\n");
1695 
1696 		// Override the NeoGeo bios DIP Switch by the main one (for the moment)
1697 		if (is_neogeo_game)
1698 			set_neo_system_bios();
1699 
1700 		// Initialize game driver
1701 		if(BurnDrvInit() == 0)
1702 			HandleMessage(RETRO_LOG_INFO, "[FBNeo] Initialized driver for %s\n", g_driver_name);
1703 		else
1704 		{
1705 			SetUguiError("Failed initializing driver\nThis is unexpected, you should probably report it.");
1706 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Failed initializing driver.\n");
1707 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] This is unexpected, you should probably report it.\n");
1708 			goto end;
1709 		}
1710 
1711 		// MemCard has to be inserted after emulation is started
1712 		if (is_neogeo_game && nMemcardMode != 0)
1713 		{
1714 			// Initialize MemCard path
1715 			snprintf_nowarn (szMemoryCardFile, sizeof(szMemoryCardFile), "%s%cfbneo%c%s.memcard", g_save_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), (nMemcardMode == 2 ? g_driver_name : "shared"));
1716 			MemCardInsert();
1717 		}
1718 
1719 		// Now we know real game fps, let's initialize sound buffer again
1720 		AudioBufferInit(nBurnSoundRate, nBurnFPS);
1721 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Adjusted audio buffer to match driver's refresh rate (%f Hz)\n", (nBurnFPS/100.0f));
1722 
1723 		// Expose Ram for cheevos/cheats support
1724 		CheevosInit();
1725 
1726 		// Loading minimal savestate (handle some machine settings)
1727 		snprintf_nowarn (g_autofs_path, sizeof(g_autofs_path), "%s%cfbneo%c%s.fs", g_save_dir, PATH_DEFAULT_SLASH_C(), PATH_DEFAULT_SLASH_C(), BurnDrvGetTextA(DRV_NAME));
1728 		if (BurnStateLoad(g_autofs_path, 0, NULL) == 0) {
1729 			HandleMessage(RETRO_LOG_INFO, "[FBNeo] EEPROM succesfully loaded from %s\n", g_autofs_path);
1730 			// eeproms are loading nCurrentFrame, but we probably don't want this
1731 			nCurrentFrame = 0;
1732 		}
1733 
1734 		if (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0) {
1735 			HandleMessage(RETRO_LOG_WARN, "[FBNeo] %s\n", BurnDrvGetTextA(DRV_COMMENT));
1736 		}
1737 
1738 		// Initializing display, autorotate if needed
1739 		BurnDrvGetFullSize(&nGameWidth, &nGameHeight);
1740 		SetRotation();
1741 		SetColorDepth();
1742 
1743 		VideoBufferInit();
1744 
1745 		if (pVidImage == NULL) {
1746 			HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Failed allocating framebuffer memory\n");
1747 			goto end;
1748 		}
1749 
1750 		apply_cheats_from_variables();
1751 
1752 		// Initialization done
1753 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] Driver %s was successfully started : game's full name is %s\n", g_driver_name, BurnDrvGetTextA(DRV_FULLNAME));
1754 	}
1755 	else
1756 	{
1757 		SetUguiError("Romset is unknown\nRead https://docs.libretro.com/library/fbneo/#building-romsets-for-fbneo");
1758 		HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Romset is unknown\n");
1759 		HandleMessage(RETRO_LOG_ERROR, "[FBNeo] Read https://docs.libretro.com/library/fbneo/#building-romsets-for-fbneo\n");
1760 		goto end;
1761 	}
1762 	return true;
1763 
1764 end:
1765 	nBurnSoundRate = 48000;
1766 	nBurnFPS = 6000;
1767 	nBurnDrvActive = ~0U;
1768 	AudioBufferInit(nBurnSoundRate, nBurnFPS);
1769 	return true;
1770 }
1771 
retro_load_game(const struct retro_game_info * info)1772 bool retro_load_game(const struct retro_game_info *info)
1773 {
1774 	if (!info)
1775 		return false;
1776 
1777 	extract_basename(g_driver_name, info->path, sizeof(g_driver_name), "");
1778 	extract_directory(g_rom_dir, info->path, sizeof(g_rom_dir));
1779 	extract_basename(g_rom_parent_dir, g_rom_dir, sizeof(g_rom_parent_dir),"");
1780 	char * prefix="";
1781 	if(strcmp(g_rom_parent_dir, "coleco")==0 || strcmp(g_rom_parent_dir, "colecovision")==0) {
1782 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem cv identified from parent folder\n");
1783 		if (strncmp(g_driver_name, "cv_", 3) != 0) prefix = "cv_";
1784 	}
1785 	if(strcmp(g_rom_parent_dir, "gamegear")==0) {
1786 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem gg identified from parent folder\n");
1787 		if (strncmp(g_driver_name, "gg_", 3) != 0) prefix = "gg_";
1788 	}
1789 	if(strcmp(g_rom_parent_dir, "megadriv")==0 || strcmp(g_rom_parent_dir, "megadrive")==0 || strcmp(g_rom_parent_dir, "genesis")==0) {
1790 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem md identified from parent folder\n");
1791 		if (strncmp(g_driver_name, "md_", 3) != 0) prefix = "md_";
1792 	}
1793 	if(strcmp(g_rom_parent_dir, "msx")==0 || strcmp(g_rom_parent_dir, "msx1")==0) {
1794 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem msx identified from parent folder\n");
1795 		if (strncmp(g_driver_name, "msx_", 4) != 0) prefix = "msx_";
1796 	}
1797 	if(strcmp(g_rom_parent_dir, "pce")==0 || strcmp(g_rom_parent_dir, "pcengine")==0) {
1798 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem pce identified from parent folder\n");
1799 		if (strncmp(g_driver_name, "pce_", 4) != 0) prefix = "pce_";
1800 	}
1801 	if(strcmp(g_rom_parent_dir, "sg1000")==0) {
1802 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem sg1k identified from parent folder\n");
1803 		if (strncmp(g_driver_name, "sg1k_", 5) != 0) prefix = "sg1k_";
1804 	}
1805 	if(strcmp(g_rom_parent_dir, "sgx")==0 || strcmp(g_rom_parent_dir, "supergrafx")==0) {
1806 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem sgx identified from parent folder\n");
1807 		if (strncmp(g_driver_name, "sgx_", 4) != 0) prefix = "sgx_";
1808 	}
1809 	if(strcmp(g_rom_parent_dir, "sms")==0 || strcmp(g_rom_parent_dir, "mastersystem")==0) {
1810 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem sms identified from parent folder\n");
1811 		if (strncmp(g_driver_name, "sms_", 4) != 0) prefix = "sms_";
1812 	}
1813 	if(strcmp(g_rom_parent_dir, "spectrum")==0 || strcmp(g_rom_parent_dir, "zxspectrum")==0) {
1814 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem spec identified from parent folder\n");
1815 		if (strncmp(g_driver_name, "spec_", 5) != 0) prefix = "spec_";
1816 	}
1817 	if(strcmp(g_rom_parent_dir, "tg16")==0) {
1818 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem tg identified from parent folder\n");
1819 		if (strncmp(g_driver_name, "tg_", 3) != 0) prefix = "tg_";
1820 	}
1821 	if(strcmp(g_rom_parent_dir, "nes")==0) {
1822 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem nes identified from parent folder\n");
1823 		if (strncmp(g_driver_name, "nes_", 4) != 0) prefix = "nes_";
1824 	}
1825 	if(strcmp(g_rom_parent_dir, "fds")==0) {
1826 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem fds identified from parent folder\n");
1827 		if (strncmp(g_driver_name, "fds_", 4) != 0) prefix = "fds_";
1828 	}
1829 	if(strcmp(g_rom_parent_dir, "ngp")==0) {
1830 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem ngp identified from parent folder\n");
1831 		if (strncmp(g_driver_name, "ngp_", 4) != 0) prefix = "ngp_";
1832 	}
1833 	if(strcmp(g_rom_parent_dir, "chf")==0 || strcmp(g_rom_parent_dir, "channelf")==0) {
1834 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem chf identified from parent folder\n");
1835 		if (strncmp(g_driver_name, "chf_", 4) != 0) prefix = "chf_";
1836 	}
1837 	if(strcmp(g_rom_parent_dir, "neocd")==0) {
1838 		HandleMessage(RETRO_LOG_INFO, "[FBNeo] subsystem neocd identified from parent folder\n");
1839 		prefix = "";
1840 		nGameType = RETRO_GAME_TYPE_NEOCD;
1841 		strcpy(CDEmuImage, info->path);
1842 		extract_basename(g_driver_name, "neocdz", sizeof(g_driver_name), prefix);
1843 	} else {
1844 		extract_basename(g_driver_name, info->path, sizeof(g_driver_name), prefix);
1845 	}
1846 
1847 	return retro_load_game_common();
1848 }
1849 
retro_load_game_special(unsigned game_type,const struct retro_game_info * info,size_t)1850 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t)
1851 {
1852 	if (!info)
1853 		return false;
1854 
1855 	nGameType = game_type;
1856 
1857 	char * prefix;
1858 	switch (nGameType) {
1859 		case RETRO_GAME_TYPE_CV:
1860 			prefix = "cv_";
1861 			break;
1862 		case RETRO_GAME_TYPE_GG:
1863 			prefix = "gg_";
1864 			break;
1865 		case RETRO_GAME_TYPE_MD:
1866 			prefix = "md_";
1867 			break;
1868 		case RETRO_GAME_TYPE_MSX:
1869 			prefix = "msx_";
1870 			break;
1871 		case RETRO_GAME_TYPE_PCE:
1872 			prefix = "pce_";
1873 			break;
1874 		case RETRO_GAME_TYPE_SG1K:
1875 			prefix = "sg1k_";
1876 			break;
1877 		case RETRO_GAME_TYPE_SGX:
1878 			prefix = "sgx_";
1879 			break;
1880 		case RETRO_GAME_TYPE_SMS:
1881 			prefix = "sms_";
1882 			break;
1883 		case RETRO_GAME_TYPE_SPEC:
1884 			prefix = "spec_";
1885 			break;
1886 		case RETRO_GAME_TYPE_TG:
1887 			prefix = "tg_";
1888 			break;
1889 		case RETRO_GAME_TYPE_NES:
1890 			prefix = "nes_";
1891 			break;
1892 		case RETRO_GAME_TYPE_FDS:
1893 			prefix = "fds_";
1894 			break;
1895 		case RETRO_GAME_TYPE_NGP:
1896 			prefix = "ngp_";
1897 			break;
1898 		case RETRO_GAME_TYPE_CHF:
1899 			prefix = "chf_";
1900 			break;
1901 		case RETRO_GAME_TYPE_NEOCD:
1902 			prefix = "";
1903 			strcpy(CDEmuImage, info->path);
1904 			break;
1905 		default:
1906 			return false;
1907 			break;
1908 	}
1909 
1910 	extract_basename(g_driver_name, info->path, sizeof(g_driver_name), prefix);
1911 	extract_directory(g_rom_dir, info->path, sizeof(g_rom_dir));
1912 
1913 	if(nGameType == RETRO_GAME_TYPE_NEOCD)
1914 		extract_basename(g_driver_name, "neocdz", sizeof(g_driver_name), "");
1915 
1916 	return retro_load_game_common();
1917 }
1918 
retro_unload_game(void)1919 void retro_unload_game(void)
1920 {
1921 	if (nBurnDrvActive != ~0U)
1922 	{
1923 		if (is_neogeo_game && nMemcardMode != 0) {
1924 			// Force newer format if the file doesn't exist yet
1925 			if(!filestream_exists(szMemoryCardFile))
1926 				bMemCardFC1Format = true;
1927 			MemCardEject();
1928 		}
1929 		// Saving minimal savestate (handle some machine settings)
1930 		if (BurnStateSave(g_autofs_path, 0) == 0 && path_is_valid(g_autofs_path))
1931 			HandleMessage(RETRO_LOG_INFO, "[FBNeo] EEPROM succesfully saved to %s\n", g_autofs_path);
1932 		BurnDrvExit();
1933 		if (nGameType == RETRO_GAME_TYPE_NEOCD)
1934 			CDEmuExit();
1935 		nBurnDrvActive = ~0U;
1936 	}
1937 	if (pVidImage) {
1938 		free(pVidImage);
1939 		pVidImage = NULL;
1940 	}
1941 	if (pAudBuffer) {
1942 		free(pAudBuffer);
1943 		pAudBuffer = NULL;
1944 	}
1945 	InputExit();
1946 	CheevosExit();
1947 }
1948 
retro_get_region()1949 unsigned retro_get_region() { return RETRO_REGION_NTSC; }
1950 
retro_api_version()1951 unsigned retro_api_version() { return RETRO_API_VERSION; }
1952 
1953 #ifdef ANDROID
1954 #include <wchar.h>
1955 
mbstowcs(wchar_t * pwcs,const char * s,size_t n)1956 size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n)
1957 {
1958 	if (pwcs == NULL)
1959 		return strlen(s);
1960 	return mbsrtowcs(pwcs, &s, n, NULL);
1961 }
1962 
wcstombs(char * s,const wchar_t * pwcs,size_t n)1963 size_t wcstombs(char *s, const wchar_t *pwcs, size_t n)
1964 {
1965 	return wcsrtombs(s, &pwcs, n, NULL);
1966 }
1967 
1968 #endif
1969 
1970 // Driver Save State module
1971 // If bAll=0 save/load all non-volatile ram to .fs
1972 // If bAll=1 save/load all ram to .fs
1973 
1974 // ------------ State len --------------------
1975 static INT32 nTotalLen = 0;
1976 
StateLenAcb(struct BurnArea * pba)1977 static INT32 __cdecl StateLenAcb(struct BurnArea* pba)
1978 {
1979 	nTotalLen += pba->nLen;
1980 
1981 	return 0;
1982 }
1983 
StateInfo(int * pnLen,int * pnMinVer,INT32 bAll,INT32 bRead=1)1984 static INT32 StateInfo(int* pnLen, int* pnMinVer, INT32 bAll, INT32 bRead = 1)
1985 {
1986 	INT32 nMin = 0;
1987 	nTotalLen = 0;
1988 	BurnAcb = StateLenAcb;
1989 
1990 	// we need to know the read/write context here, otherwise drivers like ngp won't return anything -barbudreadmon
1991 	BurnAreaScan(ACB_NVRAM | (bRead ? ACB_READ : ACB_WRITE), &nMin);						// Scan nvram
1992 	if (bAll) {
1993 		INT32 m;
1994 		BurnAreaScan(ACB_MEMCARD | (bRead ? ACB_READ : ACB_WRITE), &m);					// Scan memory card
1995 		if (m > nMin) {									// Up the minimum, if needed
1996 			nMin = m;
1997 		}
1998 		BurnAreaScan(ACB_VOLATILE | (bRead ? ACB_READ : ACB_WRITE), &m);					// Scan volatile ram
1999 		if (m > nMin) {									// Up the minimum, if needed
2000 			nMin = m;
2001 		}
2002 	}
2003 	*pnLen = nTotalLen;
2004 	*pnMinVer = nMin;
2005 
2006 	return 0;
2007 }
2008 
2009 // State load
BurnStateLoadEmbed(FILE * fp,INT32 nOffset,INT32 bAll,INT32 (* pLoadGame)())2010 INT32 BurnStateLoadEmbed(FILE* fp, INT32 nOffset, INT32 bAll, INT32 (*pLoadGame)())
2011 {
2012 	const char* szHeader = "FS1 ";                  // Chunk identifier
2013 
2014 	INT32 nLen = 0;
2015 	INT32 nMin = 0, nFileVer = 0, nFileMin = 0;
2016 	INT32 t1 = 0, t2 = 0;
2017 	char ReadHeader[4];
2018 	char szForName[33];
2019 	INT32 nChunkSize = 0;
2020 	UINT8 *Def = NULL;
2021 	INT32 nDefLen = 0;                           // Deflated version
2022 	INT32 nRet = 0;
2023 
2024 	if (nOffset >= 0) {
2025 		fseek(fp, nOffset, SEEK_SET);
2026 	} else {
2027 		if (nOffset == -2) {
2028 			fseek(fp, 0, SEEK_END);
2029 		} else {
2030 			fseek(fp, 0, SEEK_CUR);
2031 		}
2032 	}
2033 
2034 	memset(ReadHeader, 0, 4);
2035 	fread(ReadHeader, 1, 4, fp);                  // Read identifier
2036 	if (memcmp(ReadHeader, szHeader, 4)) {            // Not the right file type
2037 		return -2;
2038 	}
2039 
2040 	fread(&nChunkSize, 1, 4, fp);
2041 	if (nChunkSize <= 0x40) {                     // Not big enough
2042 		return -1;
2043 	}
2044 
2045 	INT32 nChunkData = ftell(fp);
2046 
2047 	fread(&nFileVer, 1, 4, fp);                     // Version of FB that this file was saved from
2048 
2049 	fread(&t1, 1, 4, fp);                        // Min version of FB that NV  data will work with
2050 	fread(&t2, 1, 4, fp);                        // Min version of FB that All data will work with
2051 
2052 	if (bAll) {                                 // Get the min version number which applies to us
2053 		nFileMin = t2;
2054 	} else {
2055 		nFileMin = t1;
2056 	}
2057 
2058 	fread(&nDefLen, 1, 4, fp);                     // Get the size of the compressed data block
2059 
2060 	memset(szForName, 0, sizeof(szForName));
2061 	fread(szForName, 1, 32, fp);
2062 
2063 	if (nBurnVer < nFileMin) {                     // Error - emulator is too old to load this state
2064 		return -5;
2065 	}
2066 
2067 	// Check the game the savestate is for, and load it if needed.
2068 	{
2069 		bool bLoadGame = false;
2070 
2071 		if (nBurnDrvActive < nBurnDrvCount) {
2072 			if (strcmp(szForName, BurnDrvGetTextA(DRV_NAME))) {   // The save state is for the wrong game
2073 				bLoadGame = true;
2074 			}
2075 		} else {                              // No game loaded
2076 			bLoadGame = true;
2077 		}
2078 
2079 		if (bLoadGame) {
2080 			UINT32 nCurrentGame = nBurnDrvActive;
2081 			UINT32 i;
2082 			for (i = 0; i < nBurnDrvCount; i++) {
2083 				nBurnDrvActive = i;
2084 				if (strcmp(szForName, BurnDrvGetTextA(DRV_NAME)) == 0) {
2085 					break;
2086 				}
2087 			}
2088 			if (i == nBurnDrvCount) {
2089 				nBurnDrvActive = nCurrentGame;
2090 				return -3;
2091 			} else {
2092 				if (pLoadGame == NULL) {
2093 					return -1;
2094 				}
2095 				if (pLoadGame()) {
2096 					return -1;
2097 				}
2098 			}
2099 		}
2100 	}
2101 
2102 	StateInfo(&nLen, &nMin, bAll, 0);
2103 	if (nLen <= 0) {                           // No memory to load
2104 		return -1;
2105 	}
2106 
2107 	// Check if the save state is okay
2108 	if (nFileVer < nMin) {                        // Error - this state is too old and cannot be loaded.
2109 		return -4;
2110 	}
2111 
2112 	fseek(fp, nChunkData + 0x30, SEEK_SET);            // Read current frame
2113 	fread(&nCurrentFrame, 1, 4, fp);               //
2114 
2115 	fseek(fp, 0x0C, SEEK_CUR);                     // Move file pointer to the start of the compressed block
2116 	Def = (UINT8*)malloc(nDefLen);
2117 	if (Def == NULL) {
2118 		return -1;
2119 	}
2120 	memset(Def, 0, nDefLen);
2121 	fread(Def, 1, nDefLen, fp);                     // Read in deflated block
2122 
2123 	nRet = BurnStateDecompress(Def, nDefLen, bAll);      // Decompress block into driver
2124 	if (Def) {
2125 		free(Def);                                 // free deflated block
2126 		Def = NULL;
2127 	}
2128 
2129 	fseek(fp, nChunkData + nChunkSize, SEEK_SET);
2130 
2131 	if (nRet) {
2132 		return -1;
2133 	} else {
2134 		return 0;
2135 	}
2136 }
2137 
2138 // State load
BurnStateLoad(TCHAR * szName,INT32 bAll,INT32 (* pLoadGame)())2139 INT32 BurnStateLoad(TCHAR* szName, INT32 bAll, INT32 (*pLoadGame)())
2140 {
2141 	const char szHeader[] = "FB1 ";                  // File identifier
2142 	char szReadHeader[4] = "";
2143 	INT32 nRet = 0;
2144 
2145 	FILE* fp = _tfopen(szName, _T("rb"));
2146 	if (fp == NULL) {
2147 		return 1;
2148 	}
2149 
2150 	fread(szReadHeader, 1, 4, fp);                  // Read identifier
2151 	if (memcmp(szReadHeader, szHeader, 4) == 0) {      // Check filetype
2152 		nRet = BurnStateLoadEmbed(fp, -1, bAll, pLoadGame);
2153 	}
2154 	fclose(fp);
2155 
2156 	if (nRet < 0) {
2157 		return -nRet;
2158 	} else {
2159 		return 0;
2160 	}
2161 }
2162 
2163 // Write a savestate as a chunk of an "FB1 " file
2164 // nOffset is the absolute offset from the beginning of the file
2165 // -1: Append at current position
2166 // -2: Append at EOF
BurnStateSaveEmbed(FILE * fp,INT32 nOffset,INT32 bAll)2167 INT32 BurnStateSaveEmbed(FILE* fp, INT32 nOffset, INT32 bAll)
2168 {
2169 	const char* szHeader = "FS1 ";                  // Chunk identifier
2170 
2171 	INT32 nLen = 0;
2172 	INT32 nNvMin = 0, nAMin = 0;
2173 	INT32 nZero = 0;
2174 	char szGame[33];
2175 	UINT8 *Def = NULL;
2176 	INT32 nDefLen = 0;                           // Deflated version
2177 	INT32 nRet = 0;
2178 
2179 	if (fp == NULL) {
2180 		return -1;
2181 	}
2182 
2183 	StateInfo(&nLen, &nNvMin, 0);                  // Get minimum version for NV part
2184 	nAMin = nNvMin;
2185 	if (bAll) {                                 // Get minimum version for All data
2186 		StateInfo(&nLen, &nAMin, 1);
2187 	}
2188 
2189 	if (nLen <= 0) {                           // No memory to save
2190 		return -1;
2191 	}
2192 
2193 	if (nOffset >= 0) {
2194 		fseek(fp, nOffset, SEEK_SET);
2195 	} else {
2196 		if (nOffset == -2) {
2197 			fseek(fp, 0, SEEK_END);
2198 		} else {
2199 			fseek(fp, 0, SEEK_CUR);
2200 		}
2201 	}
2202 
2203 	fwrite(szHeader, 1, 4, fp);                     // Chunk identifier
2204 	INT32 nSizeOffset = ftell(fp);                  // Reserve space to write the size of this chunk
2205 	fwrite(&nZero, 1, 4, fp);                     //
2206 
2207 	fwrite(&nBurnVer, 1, 4, fp);                  // Version of FB this was saved from
2208 	fwrite(&nNvMin, 1, 4, fp);                     // Min version of FB NV  data will work with
2209 	fwrite(&nAMin, 1, 4, fp);                     // Min version of FB All data will work with
2210 
2211 	fwrite(&nZero, 1, 4, fp);                     // Reserve space to write the compressed data size
2212 
2213 	memset(szGame, 0, sizeof(szGame));               // Game name
2214 	sprintf(szGame, "%.32s", BurnDrvGetTextA(DRV_NAME));         //
2215 	fwrite(szGame, 1, 32, fp);                     //
2216 
2217 	fwrite(&nCurrentFrame, 1, 4, fp);               // Current frame
2218 
2219 	fwrite(&nZero, 1, 4, fp);                     // Reserved
2220 	fwrite(&nZero, 1, 4, fp);                     //
2221 	fwrite(&nZero, 1, 4, fp);                     //
2222 
2223 	nRet = BurnStateCompress(&Def, &nDefLen, bAll);      // Compress block from driver and return deflated buffer
2224 	if (Def == NULL) {
2225 		return -1;
2226 	}
2227 
2228 	nRet = fwrite(Def, 1, nDefLen, fp);               // Write block to disk
2229 	if (Def) {
2230 		free(Def);                                 // free deflated block and close file
2231 		Def = NULL;
2232 	}
2233 
2234 	if (nRet != nDefLen) {                        // error writing block to disk
2235 		return -1;
2236 	}
2237 
2238 	if (nDefLen & 3) {                           // Chunk size must be a multiple of 4
2239 		fwrite(&nZero, 1, 4 - (nDefLen & 3), fp);      // Pad chunk if needed
2240 	}
2241 
2242 	fseek(fp, nSizeOffset + 0x10, SEEK_SET);         // Write size of the compressed data
2243 	fwrite(&nDefLen, 1, 4, fp);                     //
2244 
2245 	nDefLen = (nDefLen + 0x43) & ~3;               // Add for header size and align
2246 
2247 	fseek(fp, nSizeOffset, SEEK_SET);               // Write size of the chunk
2248 	fwrite(&nDefLen, 1, 4, fp);                     //
2249 
2250 	fseek (fp, 0, SEEK_END);                     // Set file pointer to the end of the chunk
2251 
2252 	return nDefLen;
2253 }
2254 
2255 // State save
BurnStateSave(TCHAR * szName,INT32 bAll)2256 INT32 BurnStateSave(TCHAR* szName, INT32 bAll)
2257 {
2258 	const char szHeader[] = "FB1 ";                  // File identifier
2259 	INT32 nLen = 0, nVer = 0;
2260 	INT32 nRet = 0;
2261 
2262 	if (bAll) {                                 // Get amount of data
2263 		StateInfo(&nLen, &nVer, 1);
2264 	} else {
2265 		StateInfo(&nLen, &nVer, 0);
2266 	}
2267 	if (nLen <= 0) {                           // No data, so exit without creating a savestate
2268 		return 1;                              // Return an error code so we know no savestate was created
2269 	}
2270 
2271 	FILE* fp = fopen(szName, _T("wb"));
2272 	if (fp == NULL) {
2273 		return 1;
2274 	}
2275 
2276 	fwrite(&szHeader, 1, 4, fp);
2277 	nRet = BurnStateSaveEmbed(fp, -1, bAll);
2278 	fclose(fp);
2279 
2280 	if (nRet < 0) {
2281 		return 1;
2282 	} else {
2283 		return 0;
2284 	}
2285 }
2286 
DecorateGameName(UINT32 nBurnDrv)2287 char* DecorateGameName(UINT32 nBurnDrv)
2288 {
2289 	static char szDecoratedName[256];
2290 	UINT32 nOldBurnDrv = nBurnDrvActive;
2291 
2292 	nBurnDrvActive = nBurnDrv;
2293 
2294 	const char* s1 = "";
2295 	const char* s2 = "";
2296 	const char* s3 = "";
2297 	const char* s4 = "";
2298 	const char* s5 = "";
2299 	const char* s6 = "";
2300 	const char* s7 = "";
2301 	const char* s8 = "";
2302 	const char* s9 = "";
2303 	const char* s10 = "";
2304 	const char* s11 = "";
2305 	const char* s12 = "";
2306 	const char* s13 = "";
2307 	const char* s14 = "";
2308 
2309 	s1 = BurnDrvGetTextA(DRV_FULLNAME);
2310 	if ((BurnDrvGetFlags() & BDF_DEMO) || (BurnDrvGetFlags() & BDF_HACK) || (BurnDrvGetFlags() & BDF_HOMEBREW) || (BurnDrvGetFlags() & BDF_PROTOTYPE) || (BurnDrvGetFlags() & BDF_BOOTLEG) || (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0)) {
2311 		s2 = " [";
2312 		if (BurnDrvGetFlags() & BDF_DEMO) {
2313 			s3 = "Demo";
2314 			if ((BurnDrvGetFlags() & BDF_HACK) || (BurnDrvGetFlags() & BDF_HOMEBREW) || (BurnDrvGetFlags() & BDF_PROTOTYPE) || (BurnDrvGetFlags() & BDF_BOOTLEG) || (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0)) {
2315 				s4 = ", ";
2316 			}
2317 		}
2318 		if (BurnDrvGetFlags() & BDF_HACK) {
2319 			s5 = "Hack";
2320 			if ((BurnDrvGetFlags() & BDF_HOMEBREW) || (BurnDrvGetFlags() & BDF_PROTOTYPE) || (BurnDrvGetFlags() & BDF_BOOTLEG) || (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0)) {
2321 				s6 = ", ";
2322 			}
2323 		}
2324 		if (BurnDrvGetFlags() & BDF_HOMEBREW) {
2325 			s7 = "Homebrew";
2326 			if ((BurnDrvGetFlags() & BDF_PROTOTYPE) || (BurnDrvGetFlags() & BDF_BOOTLEG) || (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0)) {
2327 				s8 = ", ";
2328 			}
2329 		}
2330 		if (BurnDrvGetFlags() & BDF_PROTOTYPE) {
2331 			s9 = "Prototype";
2332 			if ((BurnDrvGetFlags() & BDF_BOOTLEG) || (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0)) {
2333 				s10 = ", ";
2334 			}
2335 		}
2336 		if (BurnDrvGetFlags() & BDF_BOOTLEG) {
2337 			s11 = "Bootleg";
2338 			if (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0) {
2339 				s12 = ", ";
2340 			}
2341 		}
2342 		if (BurnDrvGetTextA(DRV_COMMENT) && strlen(BurnDrvGetTextA(DRV_COMMENT)) > 0) {
2343 			s13 = BurnDrvGetTextA(DRV_COMMENT);
2344 		}
2345 		s14 = "]";
2346 	}
2347 
2348 	sprintf(szDecoratedName, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14);
2349 
2350 	nBurnDrvActive = nOldBurnDrv;
2351 	return szDecoratedName;
2352 }
2353 
2354 #ifdef LIGHT
2355 // stub those functions
nes_add_cheat(char * code)2356 void nes_add_cheat(char *code) {}
nes_remove_cheat(char * code)2357 void nes_remove_cheat(char *code) {}
2358 #endif
2359