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