1 /*
2  * Nestopia UE
3  *
4  * Copyright (C) 2007-2008 R. Belmont
5  * Copyright (C) 2012-2021 R. Danbrook
6  * Copyright (C) 2018-2018 Phil Smith
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301, USA.
22  *
23  */
24 
25 #include <iostream>
26 #include <fstream>
27 #include <sstream>
28 
29 #include <cstdio>
30 #include <cstring>
31 #include <libgen.h>
32 
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 
37 #include <archive.h>
38 #include <archive_entry.h>
39 
40 #include <SDL.h>
41 
42 // Nst Common
43 #include "nstcommon.h"
44 #include "config.h"
45 #include "cheats.h"
46 #include "homebrew.h"
47 #include "input.h"
48 #include "audio.h"
49 #include "video.h"
50 #include "samples.h"
51 
52 Emulator emulator;
53 Video::Output *cNstVideo;
54 Sound::Output *cNstSound;
55 Input::Controllers *cNstPads;
56 
57 nstpaths_t nstpaths;
58 
59 static bool ffspeed = false;
60 static bool playing = false;
61 
62 static std::ifstream *nstdb;
63 
64 static std::ifstream *fdsbios;
65 
66 static std::ifstream *moviefile;
67 static std::fstream *movierecfile;
68 
69 void *custompalette = NULL;
70 static size_t custpalsize;
71 
72 int loaded = 0;
73 
74 bool (*nst_archive_select)(const char*, char*, size_t);
75 
nst_cb_videolock(void * userData,Video::Output & video)76 static bool NST_CALLBACK nst_cb_videolock(void* userData, Video::Output& video) {
77 	video.pitch = video_lock_screen(video.pixels);
78 	return true;
79 }
80 
nst_cb_videounlock(void * userData,Video::Output & video)81 static void NST_CALLBACK nst_cb_videounlock(void* userData, Video::Output& video) {
82 	video_unlock_screen(video.pixels);
83 }
84 
nst_cb_soundlock(void * userData,Sound::Output & sound)85 static bool NST_CALLBACK nst_cb_soundlock(void* userData, Sound::Output& sound) {
86 	audio_queue();
87 	return true;
88 }
89 
nst_cb_soundunlock(void * userData,Sound::Output & sound)90 static void NST_CALLBACK nst_cb_soundunlock(void* userData, Sound::Output& sound) {
91 	// Do Nothing
92 }
93 
nst_cb_event(void * userData,User::Event event,const void * data)94 static void NST_CALLBACK nst_cb_event(void *userData, User::Event event, const void* data) {
95 	// Handle special events
96 	switch (event) {
97 		case User::EVENT_CPU_JAM:
98 			fprintf(stderr, "Cpu: Jammed\n");
99 			break;
100 		case User::EVENT_CPU_UNOFFICIAL_OPCODE:
101 			fprintf(stderr, "Cpu: Unofficial Opcode %s\n", (const char*)data);
102 			break;
103 		case User::EVENT_DISPLAY_TIMER:
104 			nst_video_print_time((const char*)data + strlen((char*)data) - 5, true);
105 			break;
106 		default: break;
107 	}
108 }
109 
nst_cb_log(void * userData,const char * string,unsigned long int length)110 static void NST_CALLBACK nst_cb_log(void *userData, const char *string, unsigned long int length) {
111 	// Print logging information to stderr
112 	fprintf(stderr, "%s", string);
113 }
114 
nst_cb_file(void * userData,User::File & file)115 static void NST_CALLBACK nst_cb_file(void *userData, User::File& file) {
116 	unsigned char *compbuffer;
117 	int compsize, compoffset;
118 	char *filename;
119 
120 	switch (file.GetAction()) {
121 		case User::File::LOAD_ROM:
122 			// Nothing here for now
123 			break;
124 
125 		case User::File::LOAD_SAMPLE: break;
126 		case User::File::LOAD_SAMPLE_MOERO_PRO_YAKYUU: nst_sample_load_samples(file, "moepro"); break;
127 		case User::File::LOAD_SAMPLE_MOERO_PRO_YAKYUU_88: nst_sample_load_samples(file, "moepro88"); break;
128 		case User::File::LOAD_SAMPLE_MOERO_PRO_TENNIS: nst_sample_load_samples(file, "mptennis"); break;
129 		case User::File::LOAD_SAMPLE_TERAO_NO_DOSUKOI_OOZUMOU: nst_sample_load_samples(file, "terao"); break;
130 		case User::File::LOAD_SAMPLE_AEROBICS_STUDIO: nst_sample_load_samples(file, "ftaerobi"); break;
131 
132 		case User::File::LOAD_BATTERY: // load in battery data from a file
133 		case User::File::LOAD_EEPROM: // used by some Bandai games, can be treated the same as battery files
134 		case User::File::LOAD_TAPE: // for loading Famicom cassette tapes
135 		case User::File::LOAD_TURBOFILE: // for loading turbofile data
136 		{
137 			std::ifstream batteryFile(nstpaths.savename, std::ifstream::in|std::ifstream::binary);
138 
139 			if (batteryFile.is_open()) { file.SetContent(batteryFile); }
140 			break;
141 		}
142 
143 		case User::File::SAVE_BATTERY: // save battery data to a file
144 		case User::File::SAVE_EEPROM: // can be treated the same as battery files
145 		case User::File::SAVE_TAPE: // for saving Famicom cassette tapes
146 		case User::File::SAVE_TURBOFILE: // for saving turbofile data
147 		{
148 			std::ofstream batteryFile(nstpaths.savename, std::ifstream::out|std::ifstream::binary);
149 			const void* savedata;
150 			unsigned long savedatasize;
151 
152 			file.GetContent(savedata, savedatasize);
153 
154 			if (batteryFile.is_open()) { batteryFile.write((const char*) savedata, savedatasize); }
155 
156 			break;
157 		}
158 
159 		case User::File::LOAD_FDS: // for loading modified Famicom Disk System files
160 		{
161 			char fdsname[512];
162 
163 			snprintf(fdsname, sizeof(fdsname), "%s.ups", nstpaths.fdssave);
164 
165 			std::ifstream batteryFile( fdsname, std::ifstream::in|std::ifstream::binary );
166 
167 			// no ups, look for ips
168 			if (!batteryFile.is_open())
169 			{
170 				snprintf(fdsname, sizeof(fdsname), "%s.ips", nstpaths.fdssave);
171 
172 				std::ifstream batteryFile( fdsname, std::ifstream::in|std::ifstream::binary );
173 
174 				if (!batteryFile.is_open())
175 				{
176 					return;
177 				}
178 
179 				file.SetPatchContent(batteryFile);
180 				return;
181 			}
182 
183 			file.SetPatchContent(batteryFile);
184 			break;
185 		}
186 
187 		case User::File::SAVE_FDS: // for saving modified Famicom Disk System files
188 		{
189 			char fdsname[512];
190 
191 			snprintf(fdsname, sizeof(fdsname), "%s.ups", nstpaths.fdssave);
192 
193 			std::ofstream fdsFile( fdsname, std::ifstream::out|std::ifstream::binary );
194 
195 			if (fdsFile.is_open())
196 				file.GetPatchContent( User::File::PATCH_UPS, fdsFile );
197 
198 			break;
199 		}
200 	}
201 }
202 
nst_default_system()203 static Machine::FavoredSystem nst_default_system() {
204 	switch (conf.misc_default_system) {
205 		case 2: return Machine::FAVORED_NES_PAL; break;
206 		case 3: return Machine::FAVORED_FAMICOM; break;
207 		case 4: return Machine::FAVORED_DENDY; break;
208 		default: return Machine::FAVORED_NES_NTSC; break;
209 	}
210 }
211 
nst_ptr_video()212 void* nst_ptr_video() { return &cNstVideo; }
nst_ptr_sound()213 void* nst_ptr_sound() { return &cNstSound; }
nst_ptr_input()214 void* nst_ptr_input() { return &cNstPads; }
215 
nst_archive_checkext(const char * filename)216 bool nst_archive_checkext(const char *filename) {
217 	// Check if the file extension is valid
218 	int len = strlen(filename);
219 
220 	if ((!strcasecmp(&filename[len-4], ".nes")) ||
221 	    (!strcasecmp(&filename[len-4], ".fds")) ||
222 	    (!strcasecmp(&filename[len-4], ".nsf")) ||
223 	    (!strcasecmp(&filename[len-4], ".unf")) ||
224 	    (!strcasecmp(&filename[len-5], ".unif"))||
225 	    (!strcasecmp(&filename[len-4], ".xml"))) {
226 		return true;
227 	}
228 	return false;
229 }
230 
nst_archive_select_file(const char * filename,char * reqfile,size_t reqsize)231 bool nst_archive_select_file(const char *filename, char *reqfile, size_t reqsize) {
232 	// Select a filename to pull out of the archive
233 	struct archive *a;
234 	struct archive_entry *entry;
235 	int r, numarchives = 0;
236 
237 	a = archive_read_new();
238 	archive_read_support_filter_all(a);
239 	archive_read_support_format_all(a);
240 	r = archive_read_open_filename(a, filename, 10240);
241 
242 	// Test if it's actually an archive
243 	if (r != ARCHIVE_OK) {
244 		r = archive_read_free(a);
245 		return false;
246 	}
247 	// If it is an archive, handle it
248 	else {
249 		// Find files with valid extensions within the archive
250 		while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
251 			const char *currentfile = archive_entry_pathname(entry);
252 			if (nst_archive_checkext(currentfile)) {
253 				numarchives++;
254 				snprintf(reqfile, reqsize, "%s", currentfile);
255 			}
256 			archive_read_data_skip(a);
257 			break; // Load the first one found
258 		}
259 		// Free the archive
260 		r = archive_read_free(a);
261 
262 		// If there are no valid files in the archive, return
263 		if (numarchives == 0) {	return false; }
264 		else { return true; }
265 	}
266 	return false;
267 }
268 
nst_archive_open(const char * filename,char ** rom,int * romsize,const char * reqfile)269 bool nst_archive_open(const char *filename, char **rom, int *romsize, const char *reqfile) {
270 	// Opens archives
271 	struct archive *a;
272 	struct archive_entry *entry;
273 	int r;
274 	int64_t entrysize;
275 
276 	a = archive_read_new();
277 	archive_read_support_filter_all(a);
278 	archive_read_support_format_all(a);
279 	r = archive_read_open_filename(a, filename, 10240);
280 
281 	// Test if it's actually an archive
282 	if (r != ARCHIVE_OK) {
283 		r = archive_read_free(a);
284 		return false;
285 	}
286 
287 	// Scan through the archive for files
288 	while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
289 		char *rombuf;
290 		const char *currentfile = archive_entry_pathname(entry);
291 		if (nst_archive_checkext(currentfile)) {
292 			nst_set_paths(currentfile);
293 			// If there's a specific file we want, load it
294 			if (reqfile != NULL) {
295 				if (!strcmp(currentfile, reqfile)) {
296 					entrysize = archive_entry_size(entry);
297 					rombuf = (char*)malloc(entrysize);
298 					archive_read_data(a, rombuf, entrysize);
299 					archive_read_data_skip(a);
300 					r = archive_read_free(a);
301 					*romsize = entrysize;
302 					*rom = rombuf;
303 					return true;
304 				}
305 			}
306 			// Otherwise just take the first file in the archive
307 			else {
308 				entrysize = archive_entry_size(entry);
309 				rombuf = (char*)malloc(entrysize);
310 				archive_read_data(a, rombuf, entrysize);
311 				archive_read_data_skip(a);
312 				r = archive_read_free(a);
313 				*romsize = entrysize;
314 				*rom = rombuf;
315 				return true;
316 			}
317 		}
318 	}
319 	return false;
320 }
321 
nst_db_load()322 void nst_db_load() {
323 	Nes::Api::Cartridge::Database database(emulator);
324 	char dbpath[512];
325 
326 	if (nstdb) { return; }
327 
328 	// Try to open the database file
329 	snprintf(dbpath, sizeof(dbpath), "%sNstDatabase.xml", nstpaths.nstdir);
330 	nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
331 
332 	if (nstdb->is_open()) {
333 		database.Load(*nstdb);
334 		database.Enable(true);
335 		return;
336 	}
337 
338 	// If it fails, try looking in the data directory
339 	snprintf(dbpath, sizeof(dbpath), "%s/NstDatabase.xml", DATADIR);
340 	nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
341 
342 	if (nstdb->is_open()) {
343 		database.Load(*nstdb);
344 		database.Enable(true);
345 		return;
346 	}
347 
348 	// If that fails, try looking in the working directory
349 	char *pwd = getenv("PWD");
350 	snprintf(dbpath, sizeof(dbpath), "%s/NstDatabase.xml", pwd);
351 	nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
352 
353 	if (nstdb->is_open()) {
354 		database.Load(*nstdb);
355 		database.Enable(true);
356 		return;
357 	}
358 	else {
359 		fprintf(stderr, "NstDatabase.xml not found!\n");
360 		delete nstdb;
361 		nstdb = NULL;
362 	}
363 }
364 
nst_db_unload()365 void nst_db_unload() {
366 	if (nstdb) { delete nstdb; nstdb = NULL; }
367 }
368 
nst_dipswitch()369 void nst_dipswitch() {
370 	// Print DIP switch information and call handler
371 	DipSwitches dipswitches(emulator);
372 
373 	int numdips = dipswitches.NumDips();
374 
375 	if (numdips > 0) {
376 		for (int i = 0; i < numdips; i++) {
377 			fprintf(stderr, "%d: %s\n", i, dipswitches.GetDipName(i));
378 			int numvalues = dipswitches.NumValues(i);
379 
380 			for (int j = 0; j < numvalues; j++) {
381 				fprintf(stderr, " %d: %s\n", j, dipswitches.GetValueName(i, j));
382 			}
383 		}
384 
385 		char dippath[512];
386 		snprintf(dippath, sizeof(dippath), "%s%s.dip", nstpaths.savedir, nstpaths.gamename);
387 		nst_dip_handle(dippath);
388 	}
389 }
390 
nst_fds_bios_load()391 void nst_fds_bios_load() {
392 	// Load the Famicom Disk System BIOS
393 	Nes::Api::Fds fds(emulator);
394 	char biospath[512];
395 
396 	if (fdsbios) { return; }
397 
398 	snprintf(biospath, sizeof(biospath), "%sdisksys.rom", nstpaths.nstdir);
399 
400 	fdsbios = new std::ifstream(biospath, std::ifstream::in|std::ifstream::binary);
401 
402 	if (fdsbios->is_open())	{
403 		fds.SetBIOS(fdsbios);
404 	}
405 	else {
406 		fprintf(stderr, "Fds: BIOS not found: %s\n", biospath);
407 		delete fdsbios;
408 		fdsbios = NULL;
409 	}
410 }
411 
nst_fds_bios_unload()412 void nst_fds_bios_unload() {
413 	if (fdsbios) { delete fdsbios; fdsbios = NULL; }
414 }
415 
nst_fds_info()416 void nst_fds_info() {
417 	Fds fds(emulator);
418 
419 	const char* disk;
420 	const char* side;
421 	char textbuf[24];
422 
423 	fds.GetCurrentDisk() == 0 ? disk = "1" : disk = "2";
424 	fds.GetCurrentDiskSide() == 0 ? side = "A" : side = "B";
425 
426 	fprintf(stderr, "Fds: Disk %s Side %s\n", disk, side);
427 	snprintf(textbuf, sizeof(textbuf), "Disk %s Side %s", disk, side);
428 	nst_video_print((const char*)textbuf, 8, 16, 2, true);
429 }
430 
nst_fds_flip()431 void nst_fds_flip() {
432 	// Flips the FDS disk
433 	Fds fds(emulator);
434 
435 	if (fds.CanChangeDiskSide()) {
436 		fds.ChangeSide();
437 		nst_fds_info();
438 	}
439 }
440 
nst_fds_switch()441 void nst_fds_switch() {
442 	// Switches the FDS disk in multi-disk games
443 	Fds fds(emulator);
444 
445 	int currentdisk = fds.GetCurrentDisk();
446 
447 	// If it's a multi-disk game, eject and insert the other disk
448 	if (fds.GetNumDisks() > 1) {
449 		fds.EjectDisk();
450 		fds.InsertDisk(!currentdisk, 0);
451 		nst_fds_info();
452 	}
453 }
454 
nst_movie_save(const char * filename)455 void nst_movie_save(const char *filename) {
456 	// Save/Record a movie
457 	Movie movie(emulator);
458 
459 	movierecfile = new std::fstream(filename, std::ifstream::out|std::ifstream::binary);
460 
461 	if (movierecfile->is_open()) {
462 		movie.Record((std::iostream&)*movierecfile, Nes::Api::Movie::CLEAN);
463 	}
464 	else {
465 		delete movierecfile;
466 		movierecfile = NULL;
467 	}
468 }
469 
nst_movie_load(const char * filename)470 void nst_movie_load(const char *filename) {
471 	// Load and play a movie
472 	Movie movie(emulator);
473 
474 	moviefile = new std::ifstream(filename, std::ifstream::in|std::ifstream::binary);
475 
476 	if (moviefile->is_open()) {
477 		movie.Play(*moviefile);
478 	}
479 	else {
480 		delete moviefile;
481 		moviefile = NULL;
482 	}
483 }
484 
nst_movie_stop()485 void nst_movie_stop() {
486 	// Stop any movie that is playing or recording
487 	Movie movie(emulator);
488 
489 	if (movie.IsPlaying() || movie.IsRecording()) {
490 		movie.Stop();
491 		movierecfile = NULL;
492 		delete movierecfile;
493 		moviefile = NULL;
494 		delete moviefile;
495 	}
496 }
497 
nst_nsf()498 bool nst_nsf() {
499 	Machine machine(emulator);
500 	return machine.Is(Machine::SOUND);
501 }
502 
nst_nsf_play()503 void nst_nsf_play() {
504 	Nsf nsf(emulator);
505 	nsf.PlaySong();
506 	video_clear_buffer();
507 	video_disp_nsf();
508 }
509 
nst_nsf_stop()510 void nst_nsf_stop() {
511 	Nsf nsf(emulator);
512 	nsf.StopSong();
513 }
514 
nst_nsf_prev()515 void nst_nsf_prev() {
516 	Nsf nsf(emulator);
517 	nsf.SelectPrevSong();
518 	video_clear_buffer();
519 	video_disp_nsf();
520 }
521 
nst_nsf_next()522 void nst_nsf_next() {
523 	Nsf nsf(emulator);
524 	nsf.SelectNextSong();
525 	video_clear_buffer();
526 	video_disp_nsf();
527 }
528 
nst_pal()529 bool nst_pal() {
530 	Machine machine(emulator);
531 	return machine.GetMode() == Machine::PAL;
532 }
533 
nst_playing()534 bool nst_playing() { return playing; }
535 
nst_palette_load(const char * filename)536 void nst_palette_load(const char *filename) {
537 	// Load a custom palette
538 
539 	FILE *file;
540 	long filesize; // File size in bytes
541 	size_t result;
542 
543 	char custgamepalpath[512];
544 	snprintf(custgamepalpath, sizeof(custgamepalpath), "%s%s%s", nstpaths.nstdir, nstpaths.gamename, ".pal");
545 
546 	// Try the game-specific palette first
547 	file = fopen(custgamepalpath, "rb");
548 	if (!file) { file = fopen(filename, "rb"); }
549 
550 	// Then try the global custom palette
551 	if (!file) {
552 		if (conf.video_palette_mode == 2) {
553 			fprintf(stderr, "Custom palette: not found: %s\n", filename);
554 			conf.video_palette_mode = 0;
555 		}
556 		return;
557 	}
558 
559 	fseek(file, 0, SEEK_END);
560 	filesize = ftell(file);
561 	fseek(file, 0, SEEK_SET);
562 
563 	if (custompalette) { free(custompalette); }
564 	custompalette = malloc(filesize * sizeof(uint8_t));
565 	custpalsize = filesize * sizeof(uint8_t);
566 
567 	result = fread(custompalette, sizeof(uint8_t), filesize, file);
568 
569 	fclose(file);
570 }
571 
nst_palette_save()572 void nst_palette_save() {
573 	// Save a custom palette
574 	FILE *file;
575 	void *custpalout;
576 
577 	file = fopen(nstpaths.palettepath, "wb");
578 	if (!file) { return; }
579 
580 	custpalout = malloc(custpalsize);
581 
582 	memcpy(custpalout, custompalette, custpalsize);
583 
584 	fwrite(custpalout, custpalsize, sizeof(uint8_t), file);
585 	fclose(file);
586 	free(custpalout);
587 }
588 
nst_palette_unload()589 void nst_palette_unload() {
590 	if (custompalette) { free(custompalette); }
591 }
592 
nst_find_patch(char * patchname,unsigned int patchname_length,const char * filename)593 bool nst_find_patch(char *patchname, unsigned int patchname_length, const char *filename) {
594 	// Check for a patch in the same directory as the game
595 	FILE *file;
596 	char filedir[512];
597 
598 	// Copy filename (will be used by dirname)
599 	// dirname needs a copy because it can modify its argument
600 	strncpy(filedir, filename, sizeof(filedir));
601 	filedir[sizeof(filedir) - 1] = '\0';
602 	// Use memmove because dirname can return the same pointer as its argument,
603 	// since copying into same string as the argument we don't want any overlap
604 	memmove(filedir, dirname(filedir), sizeof(filedir));
605 	filedir[sizeof(filedir) - 1] = '\0';
606 
607 	if (!conf.misc_soft_patching) { return 0; }
608 
609 	snprintf(patchname, patchname_length, "%s/%s.ips", filedir, nstpaths.gamename);
610 
611 	if ((file = fopen(patchname, "rb")) != NULL) { fclose(file); return 1; }
612 	else {
613 		snprintf(patchname, patchname_length, "%s/%s.ups", filedir, nstpaths.gamename);
614 		if ((file = fopen(patchname, "rb")) != NULL) { fclose(file); return 1; }
615 	}
616 
617 	return 0;
618 }
619 
nst_set_callbacks()620 void nst_set_callbacks() {
621 	// Set up the callbacks
622 	void *userData = (void*)0xDEADC0DE;
623 
624 	Video::Output::lockCallback.Set(nst_cb_videolock, userData);
625 	Video::Output::unlockCallback.Set(nst_cb_videounlock, userData);
626 
627 	Sound::Output::lockCallback.Set(nst_cb_soundlock, userData);
628 	Sound::Output::unlockCallback.Set(nst_cb_soundunlock, userData);
629 
630 	User::fileIoCallback.Set(nst_cb_file, userData);
631 	User::logCallback.Set(nst_cb_log, userData);
632 	User::eventCallback.Set(nst_cb_event, userData);
633 }
634 
nst_set_dirs()635 void nst_set_dirs() {
636 	// Set up system directories
637 	// create config directory if it doesn't exist
638 	if (getenv("XDG_CONFIG_HOME")) {
639 		snprintf(nstpaths.nstconfdir, sizeof(nstpaths.nstconfdir), "%s/nestopia/", getenv("XDG_CONFIG_HOME"));
640 	}
641 	else {
642 		snprintf(nstpaths.nstconfdir, sizeof(nstpaths.nstconfdir), "%s/.config/nestopia/", getenv("HOME"));
643 	}
644 
645 	if (mkdir(nstpaths.nstconfdir, 0755) && errno != EEXIST) {
646 		fprintf(stderr, "Failed to create %s: %d\n", nstpaths.nstconfdir, errno);
647 	}
648 
649 	// create data directory if it doesn't exist
650 	if (getenv("XDG_DATA_HOME")) {
651 		snprintf(nstpaths.nstdir, sizeof(nstpaths.nstdir), "%s/nestopia/", getenv("XDG_DATA_HOME"));
652 	}
653 	else {
654 		snprintf(nstpaths.nstdir, sizeof(nstpaths.nstdir), "%s/.local/share/nestopia/", getenv("HOME"));
655 	}
656 
657 	if (mkdir(nstpaths.nstdir, 0755) && errno != EEXIST) {
658 		fprintf(stderr, "Failed to create %s: %d\n", nstpaths.nstdir, errno);
659 	}
660 
661 	// create save and state directories if they don't exist
662 	char dirstr[256];
663 	snprintf(dirstr, sizeof(dirstr), "%ssave", nstpaths.nstdir);
664 
665 	if (mkdir(dirstr, 0755) && errno != EEXIST) {
666 		fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
667 	}
668 
669 	snprintf(dirstr, sizeof(dirstr), "%sstate", nstpaths.nstdir);
670 	if (mkdir(dirstr, 0755) && errno != EEXIST) {
671 		fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
672 	}
673 
674 	// create cheats directory if it doesn't exist
675 	snprintf(dirstr, sizeof(dirstr), "%scheats", nstpaths.nstdir);
676 	if (mkdir(dirstr, 0755) && errno != EEXIST) {
677 		fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
678 	}
679 
680 	// create screenshots directory if it doesn't exist
681 	snprintf(dirstr, sizeof(dirstr), "%sscreenshots", nstpaths.nstdir);
682 	if (mkdir(dirstr, 0755) && errno != EEXIST) {
683 		fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
684 	}
685 
686 	// Construct the custom palette path
687 	snprintf(nstpaths.palettepath, sizeof(nstpaths.palettepath), "%s%s", nstpaths.nstdir, "custom.pal");
688 
689 	// Construct samples directory if it doesn't exist
690 	snprintf(dirstr, sizeof(dirstr), "%ssamples", nstpaths.nstdir);
691 	if (mkdir(dirstr, 0755) && errno != EEXIST) {
692 		fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
693 	}
694 }
695 
nst_set_paths(const char * filename)696 void nst_set_paths(const char *filename) {
697 
698 	// Set up the save directory
699 	snprintf(nstpaths.savedir, sizeof(nstpaths.savedir), "%ssave/", nstpaths.nstdir);
700 
701 	// Copy the full file path to the savename variable
702 	snprintf(nstpaths.savename, sizeof(nstpaths.savename), "%s", filename);
703 
704 	// strip the . and extention off the filename for saving
705 	for (int i = strlen(nstpaths.savename)-1; i > 0; i--) {
706 		if (nstpaths.savename[i] == '.') {
707 			nstpaths.savename[i] = '\0';
708 			break;
709 		}
710 	}
711 
712 	// Set up the sample directory
713 	snprintf(nstpaths.sampdir, sizeof(nstpaths.sampdir), "%ssamples/", nstpaths.nstdir);
714 
715 	// Get the name of the game minus file path and extension
716 	snprintf(nstpaths.gamename, sizeof(nstpaths.gamename), "%s", basename(nstpaths.savename));
717 
718 	// Construct save path
719 	snprintf(nstpaths.savename, sizeof(nstpaths.savename), "%s%s%s", nstpaths.savedir, nstpaths.gamename, ".sav");
720 
721 	// Construct path for FDS save patches
722 	snprintf(nstpaths.fdssave, sizeof(nstpaths.fdssave), "%s%s", nstpaths.savedir, nstpaths.gamename);
723 
724 	// Construct the save state path
725 	snprintf(nstpaths.statepath, sizeof(nstpaths.statepath), "%sstate/%s", nstpaths.nstdir, nstpaths.gamename);
726 
727 	// Construct the cheat path
728 	snprintf(nstpaths.cheatpath, sizeof(nstpaths.cheatpath), "%scheats/%s.xml", nstpaths.nstdir, nstpaths.gamename);
729 }
730 
nst_set_region()731 void nst_set_region() {
732 	// Set the region
733 	Machine machine(emulator);
734 	Cartridge::Database database(emulator);
735 
736 	/*if (database.IsLoaded()) {
737 		std::ifstream dbfile(filename, std::ios::in|std::ios::binary);
738 		Cartridge::Profile profile;
739 		Cartridge::ReadInes(dbfile, nst_default_system(), profile);
740 		dbentry = database.FindEntry(profile.hash, nst_default_system());
741 		printf("Mapper: %d\n", dbentry.GetMapper());
742 	}*/
743 
744 	switch (conf.misc_default_system) {
745 		case 0: machine.SetMode(machine.GetDesiredMode()); break; // Auto
746 		case 1: machine.SetMode(Machine::NTSC); break; // NTSC
747 		case 2: machine.SetMode(Machine::PAL); break; // PAL
748 		case 3: machine.SetMode(Machine::NTSC); break; // Famicom
749 		case 4: machine.SetMode(Machine::PAL); break; // Dendy
750 	}
751 }
752 
nst_set_rewind(int direction)753 void nst_set_rewind(int direction) {
754 	// Set the rewinder backward or forward
755 	switch (direction) {
756 		case 0: Rewinder(emulator).SetDirection(Rewinder::BACKWARD); break;
757 		case 1: Rewinder(emulator).SetDirection(Rewinder::FORWARD); break;
758 		default: break;
759 	}
760 }
761 
nst_state_save(const char * filename)762 void nst_state_save(const char *filename) {
763 	// Save a state by filename
764 	Machine machine(emulator);
765 
766 	std::ofstream statefile(filename, std::ifstream::out|std::ifstream::binary);
767 
768 	if (statefile.is_open()) { machine.SaveState(statefile, Nes::Api::Machine::NO_COMPRESSION); }
769 	fprintf(stderr, "State Saved: %s\n", filename);
770 	nst_video_print("State Saved", 8, 212, 2, true);
771 }
772 
nst_state_load(const char * filename)773 void nst_state_load(const char *filename) {
774 	// Load a state by filename
775 	Machine machine(emulator);
776 
777 	std::ifstream statefile(filename, std::ifstream::in|std::ifstream::binary);
778 
779 	if (statefile.is_open()) { machine.LoadState(statefile); }
780 	fprintf(stderr, "State Loaded: %s\n", filename);
781 	nst_video_print("State Loaded", 8, 212, 2, true);
782 }
783 
nst_state_quicksave(int slot)784 void nst_state_quicksave(int slot) {
785 	// Quick Save State
786 	if (!loaded) { return; }
787 	char slotpath[520];
788 	snprintf(slotpath, sizeof(slotpath), "%s_%d.nst", nstpaths.statepath, slot);
789 	nst_state_save(slotpath);
790 }
791 
792 
nst_state_quickload(int slot)793 void nst_state_quickload(int slot) {
794 	// Quick Load State
795 	if (!loaded) { return; }
796 	char slotpath[520];
797 	snprintf(slotpath, sizeof(slotpath), "%s_%d.nst", nstpaths.statepath, slot);
798 
799 	struct stat qloadstat;
800 	if (stat(slotpath, &qloadstat) == -1) {
801 		fprintf(stderr, "No State to Load\n");
802 		nst_video_print("No State to Load", 8, 212, 2, true);
803 		return;
804 	}
805 
806 	nst_state_load(slotpath);
807 }
808 
nst_timing_runframes()809 int nst_timing_runframes() {
810 	// Calculate how many emulation frames to run
811 	if (ffspeed) { return conf.timing_ffspeed; }
812 	return 1;
813 }
814 
nst_timing_set_ffspeed()815 void nst_timing_set_ffspeed() {
816 	// Set the framerate to the fast-forward speed
817 	ffspeed = true;
818 	audio_set_speed(conf.timing_ffspeed);
819 }
820 
nst_timing_set_default()821 void nst_timing_set_default() {
822 	// Set the framerate to the default
823 	ffspeed = false;
824 	audio_set_speed(1);
825 }
826 
nst_reset(bool hardreset)827 void nst_reset(bool hardreset) {
828 	// Reset the machine (soft or hard)
829 	Machine machine(emulator);
830 	Fds fds(emulator);
831 	machine.SetRamPowerState(conf.misc_power_state);
832 	machine.Reset(hardreset);
833 
834 	// Set the FDS disk to defaults
835 	fds.EjectDisk();
836 	fds.InsertDisk(0, 0);
837 }
838 
nst_emuloop()839 void nst_emuloop() {
840 	// Main Emulation Loop
841 	if (NES_SUCCEEDED(Rewinder(emulator).Enable(true))) {
842 		Rewinder(emulator).EnableSound(true);
843 	}
844 
845 	if (playing) {
846 		// Pulse the turbo buttons
847 		nst_input_turbo_pulse(cNstPads);
848 
849 		// Execute frames
850 		for (int i = 0; i < nst_timing_runframes(); i++) {
851 			emulator.Execute(cNstVideo, cNstSound, cNstPads);
852 		}
853 	}
854 }
855 
nst_unload()856 void nst_unload() {
857 	// Remove the cartridge and shut down the NES
858 	Machine machine(emulator);
859 
860 	// Power down the NES
861 	machine.Power(false);
862 
863 	// Remove the cartridge
864 	machine.Unload();
865 }
866 
nst_pause()867 void nst_pause() {
868 	// Pauses the game
869 	if (playing) {
870 		audio_pause();
871 		audio_deinit();
872 	}
873 
874 	playing = false;
875 }
876 
nst_play()877 void nst_play() {
878 	// Play the game
879 	if (playing) { return; }
880 
881 	video_init();
882 	audio_init();
883 	nst_input_init();
884 	nst_cheats_init(nstpaths.cheatpath);
885 	nst_homebrew_init();
886 
887 	cNstVideo = new Video::Output;
888 	cNstSound = new Sound::Output;
889 	cNstPads  = new Input::Controllers;
890 
891 	audio_set_params(cNstSound);
892 	audio_unpause();
893 
894 	if (nst_nsf()) {
895 		Nsf nsf(emulator);
896 		nsf.PlaySong();
897 		video_disp_nsf();
898 	}
899 
900 	playing = true;
901 }
902 
nst_load(const char * filename)903 int nst_load(const char *filename) {
904 	// Load a Game ROM
905 	Machine machine(emulator);
906 	Nsf nsf(emulator);
907 	Sound sound(emulator);
908 	Nes::Result result;
909 	char *rom;
910 	int romsize;
911 	char patchname[512];
912 
913 	// Pause play before pulling out a cartridge
914 	if (playing) { nst_pause(); }
915 
916 	// Pull out any inserted cartridges
917 	if (loaded) { nst_unload(); }
918 	nst_video_print_time("", false);
919 
920 	// Check if the file is an archive and select the file within
921 	char reqfile[256]; // Requested file inside the archive
922 	if (nst_archive_select(filename, reqfile, sizeof(reqfile))) {
923 		// Extract the contents
924 		nst_archive_open(filename, &rom, &romsize, reqfile);
925 
926 		// Convert the malloc'd char* to an istream
927 		std::string rombuf(rom, romsize);
928 		std::istringstream file(rombuf);
929 		free(rom);
930 
931 		result = machine.Load(file, nst_default_system());
932 	}
933 	else { // Otherwise just load the file
934 		std::ifstream file(filename, std::ios::in|std::ios::binary);
935 
936 		// Set the file paths
937 		nst_set_paths(filename);
938 
939 		if (nst_find_patch(patchname, sizeof(patchname), filename)) { // Load with a patch if there is one
940 			std::ifstream pfile(patchname, std::ios::in|std::ios::binary);
941 			Machine::Patch patch(pfile, false);
942 			result = machine.Load(file, nst_default_system(), patch);
943 		}
944 		else { result = machine.Load(file, nst_default_system()); }
945 	}
946 
947 	if (NES_FAILED(result)) {
948 		char errorstring[32];
949 		switch (result) {
950 			case Nes::RESULT_ERR_INVALID_FILE:
951 				snprintf(errorstring, sizeof(errorstring), "Error: Invalid file");
952 				break;
953 
954 			case Nes::RESULT_ERR_OUT_OF_MEMORY:
955 				snprintf(errorstring, sizeof(errorstring), "Error: Out of Memory");
956 				break;
957 
958 			case Nes::RESULT_ERR_CORRUPT_FILE:
959 				snprintf(errorstring, sizeof(errorstring), "Error: Corrupt or Missing File");
960 				break;
961 
962 			case Nes::RESULT_ERR_UNSUPPORTED_MAPPER:
963 				snprintf(errorstring, sizeof(errorstring), "Error: Unsupported Mapper");
964 				break;
965 
966 			case Nes::RESULT_ERR_MISSING_BIOS:
967 				snprintf(errorstring, sizeof(errorstring), "Error: Missing Fds BIOS");
968 				break;
969 
970 			default:
971 				snprintf(errorstring, sizeof(errorstring), "Error: %d", result);
972 				break;
973 		}
974 
975 		fprintf(stderr, "%s\n", errorstring);
976 
977 		return 0;
978 	}
979 
980 	// Deal with any DIP Switches
981 	nst_dipswitch();
982 
983 	// Set the region
984 	nst_set_region();
985 
986 	if (machine.Is(Machine::DISK)) {
987 		Fds fds(emulator);
988 		fds.InsertDisk(0, 0);
989 		nst_fds_info();
990 	}
991 
992 	// Check if this is an NSF
993 	if (nst_nsf()) { nsf.StopSong(); }
994 
995 	// Check if sound distortion should be enabled
996 	sound.SetGenie(conf.misc_genie_distortion);
997 
998 	// Load the custom palette
999 	nst_palette_load(nstpaths.palettepath);
1000 
1001 	// Set the RAM's power state
1002 	machine.SetRamPowerState(conf.misc_power_state);
1003 
1004 	// Power on
1005 	machine.Power(true);
1006 
1007 	loaded = 1;
1008 	return loaded;
1009 }
1010