1 /***************************************************************************
2 
3 	fileio.c - file access functions
4 
5 ***************************************************************************/
6 
7 #include <compat/zlib.h>
8 
9 #include <stdio.h>
10 #include <assert.h>
11 
12 #include <streams/file_stream.h>
13 
14 #include "driver.h"
15 #include "unzip.h"
16 #include "fileio.h"
17 #include "log.h"
18 
19 
20 /***************************************************************************
21 	CONSTANTS
22 ***************************************************************************/
23 
24 #define PLAIN_FILE				0
25 #define RAM_FILE				1
26 #define ZIPPED_FILE				2
27 #define UNLOADED_ZIPPED_FILE	3
28 
29 #define FILEFLAG_OPENREAD		0x01
30 #define FILEFLAG_OPENWRITE		0x02
31 #define FILEFLAG_HASH			0x04
32 #define FILEFLAG_REVERSE_SEARCH	0x08
33 #define FILEFLAG_VERIFY_ONLY	0x10
34 #define FILEFLAG_NOZIP			0x20
35 
36 #ifdef MAME_DEBUG
37 #define DEBUG_COOKIE			0xbaadf00d
38 #endif
39 
40 /***************************************************************************
41 	TYPE DEFINITIONS
42 ***************************************************************************/
43 
44 struct _mame_file
45 {
46 #ifdef DEBUG_COOKIE
47 	UINT32 debug_cookie;
48 #endif
49 	FILE *file;
50 	UINT8 *data;
51 	UINT64 offset;
52 	UINT64 length;
53 	UINT8 eof;
54 	UINT8 type;
55 	char hash[HASH_BUF_SIZE];
56 };
57 
58 
59 
60 /***************************************************************************
61 	PROTOTYPES
62 ***************************************************************************/
63 
64 static mame_file *generic_fopen(int pathtype, const char *gamename, const char *filename, const char* hash, UINT32 flags);
65 static int checksum_file(int pathtype, int pathindex, const char *file, UINT8 **p, UINT64 *size, char* hash);
66 
67 
68 /***************************************************************************
69 	mame_fopen
70 ***************************************************************************/
71 
mame_fopen(const char * gamename,const char * filename,int filetype,int openforwrite)72 mame_file *mame_fopen(const char *gamename, const char *filename, int filetype, int openforwrite)
73 {
74 	/* first verify that we aren't trying to open read-only types as writeables */
75 	switch (filetype)
76 	{
77 		/* read-only cases */
78 		case FILETYPE_ROM:
79 		case FILETYPE_IMAGE:
80 		case FILETYPE_SAMPLE:
81 		case FILETYPE_SAMPLE_FLAC:
82 		case FILETYPE_ARTWORK:
83 		case FILETYPE_HISTORY:
84 		case FILETYPE_LANGUAGE:
85 			if (openforwrite)
86 			{
87 				log_cb(RETRO_LOG_ERROR, LOGPRE "mame_fopen: type %02x write not supported\n", filetype);
88 				return NULL;
89 			}
90 			break;
91 
92 	}
93 
94 	/* now open the file appropriately */
95 	switch (filetype)
96 	{
97 		/* ROM files */
98 		case FILETYPE_ROM:
99 			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD | FILEFLAG_HASH);
100 
101 		/* read-only disk images */
102 		case FILETYPE_IMAGE:
103 			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD | FILEFLAG_NOZIP);
104 
105 		/* differencing disk images */
106 		case FILETYPE_IMAGE_DIFF:
107 			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD | FILEFLAG_OPENWRITE);
108 
109 		/* samples */
110 		case FILETYPE_SAMPLE:
111 			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD);
112 
113 		case FILETYPE_SAMPLE_FLAC:
114  			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD);
115 
116 		/* artwork files */
117 		case FILETYPE_ARTWORK:
118 			return generic_fopen(filetype, gamename, filename, 0, FILEFLAG_OPENREAD);
119 
120 		/* NVRAM files */
121 		case FILETYPE_NVRAM:
122 			return generic_fopen(filetype, NULL, gamename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
123 
124 		/* high score files */
125 		case FILETYPE_HIGHSCORE:
126 			return generic_fopen(filetype, NULL, gamename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
127 
128 		/* highscore database */
129 		case FILETYPE_HIGHSCORE_DB:
130 			return generic_fopen(filetype, NULL, filename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
131 
132 		/* config files */
133 		case FILETYPE_CONFIG:
134 			return generic_fopen(filetype, NULL, gamename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
135 
136 		/* memory card files */
137 		case FILETYPE_MEMCARD:
138 			return generic_fopen(filetype, NULL, filename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
139 
140 		/* history files */
141 		case FILETYPE_HISTORY:
142 			return generic_fopen(filetype, NULL, filename, 0, FILEFLAG_OPENREAD);
143 
144 		/* cheat file */
145 		case FILETYPE_CHEAT:
146 			return generic_fopen(filetype, NULL, filename, 0, FILEFLAG_OPENREAD | (openforwrite ? FILEFLAG_OPENWRITE : 0));
147 
148 		/* language file */
149 		case FILETYPE_LANGUAGE:
150 			return generic_fopen(filetype, NULL, filename, 0, FILEFLAG_OPENREAD);
151 
152 		/* ctrlr files */
153 		case FILETYPE_CTRLR:
154 			return generic_fopen(filetype, gamename, filename, 0, openforwrite ? FILEFLAG_OPENWRITE : FILEFLAG_OPENREAD);
155 
156 		/* anything else */
157 		default:
158 			log_cb(RETRO_LOG_ERROR, LOGPRE "mame_fopen(): unknown filetype %02x\n", filetype);
159 			return NULL;
160 	}
161 	return NULL;
162 }
163 
164 
165 /******************************************************************************
166 
167 File I/O
168 
169 ******************************************************************************/
170 
osd_get_path_count(int pathtype)171 int osd_get_path_count(int pathtype)
172 {
173 	return 1;
174 }
175 
176 /******************************************************************************
177 
178  osd_get_path
179  Sets char* path to point at a valid path of the type incidated by int pathtype,
180  although the path itself does not necessarily exist at this point in the process.
181 
182  *****************************************************************************/
osd_get_path(int pathtype,char * path)183 void osd_get_path(int pathtype, char* path)
184 {
185   char save_path_buffer[PATH_MAX_LENGTH]= {0};
186   char sys_path_buffer[PATH_MAX_LENGTH]= {0};
187 
188   save_path_buffer[0] = '\0';
189   if(options.save_subfolder)
190     snprintf(save_path_buffer, PATH_MAX_LENGTH, "%s%c%s", options.libretro_save_path, PATH_DEFAULT_SLASH_C(), APPNAME);
191   else
192     snprintf(save_path_buffer, PATH_MAX_LENGTH, "%s", options.libretro_save_path);
193 
194   sys_path_buffer[0] = '\0';
195   if(options.system_subfolder)
196     snprintf(sys_path_buffer, PATH_MAX_LENGTH, "%s%c%s", options.libretro_system_path, PATH_DEFAULT_SLASH_C(), APPNAME);
197   else
198     snprintf(sys_path_buffer, PATH_MAX_LENGTH, "%s", options.libretro_system_path);
199 
200   /* force system and save paths to be created if not already there */
201   if ( !(path_is_directory(sys_path_buffer)) || !(path_is_directory(save_path_buffer)) )
202   {
203     log_cb(RETRO_LOG_INFO, LOGPRE "Searching for missing directories.........\n");
204 
205     if (path_mkdir(sys_path_buffer))
206       log_cb(RETRO_LOG_INFO, LOGPRE "Verified system directory exists:  %s\n", sys_path_buffer);
207     else
208       log_cb(RETRO_LOG_INFO, LOGPRE "Failed to create missing system directory:  %s\n", sys_path_buffer);
209 
210     if (path_mkdir(save_path_buffer))
211       log_cb(RETRO_LOG_INFO, LOGPRE "Verified save directory exists:  %s\n", save_path_buffer);
212     else
213       log_cb(RETRO_LOG_INFO, LOGPRE "Failed to create missing save directory:  %s\n", save_path_buffer);
214   }
215 
216    switch (pathtype)
217    {
218        case FILETYPE_ROM:
219        case FILETYPE_IMAGE:
220           strcpy(path, options.libretro_content_path);
221           break;
222 
223       /* user-initiated content goes in mame2003 save directory subfolders */
224       case FILETYPE_IMAGE_DIFF:
225          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "diff");
226          break;
227       case FILETYPE_NVRAM:
228          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "nvram");
229          break;
230       case FILETYPE_HIGHSCORE:
231           snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "hi");
232          break;
233       case FILETYPE_CONFIG:
234          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "cfg");
235          break;
236       case FILETYPE_MEMCARD:
237          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "memcard");
238          break;
239       case FILETYPE_CTRLR:
240          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", save_path_buffer, PATH_DEFAULT_SLASH_C(), "ctrlr");
241          break;
242       case FILETYPE_XML_DAT:
243          snprintf(path, PATH_MAX_LENGTH, "%s", save_path_buffer);
244          break;
245 
246          /* static, pregenerated content goes in mam2003 system directory subfolders */
247       case FILETYPE_ARTWORK:
248          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", sys_path_buffer, PATH_DEFAULT_SLASH_C(), "artwork");
249          break;
250       case FILETYPE_SAMPLE:
251          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", sys_path_buffer, PATH_DEFAULT_SLASH_C(), "samples");
252          break;
253       case FILETYPE_SAMPLE_FLAC:
254          snprintf(path, PATH_MAX_LENGTH, "%s%c%s", sys_path_buffer, PATH_DEFAULT_SLASH_C(), "samples");
255          break;
256 
257       default:
258          /* .dat files and additional core content goes in mame2003 system directory */
259          snprintf(path, PATH_MAX_LENGTH, "%s", sys_path_buffer);
260          break;
261    }
262    /* Create path if it doesn't exist and log create failures */
263    if (!path_is_directory(path))
264      if (!path_mkdir(path)) log_cb(RETRO_LOG_DEBUG, LOGPRE "osd_get_path() failed to create path:  %s\n", path);
265 
266    log_cb(RETRO_LOG_DEBUG, LOGPRE "osd_get_path() return path=  %s\n", path);
267 }
268 
osd_get_path_info(int pathtype,int pathindex,const char * filename)269 int osd_get_path_info(int pathtype, int pathindex, const char *filename)
270 {
271    char buffer[PATH_MAX_LENGTH];
272    char currDir[PATH_MAX_LENGTH];
273 
274    osd_get_path(pathtype, currDir);
275    snprintf(buffer, PATH_MAX_LENGTH, "%s%c%s", currDir, PATH_DEFAULT_SLASH_C(), filename);
276 
277    log_cb(RETRO_LOG_DEBUG, "(osd_get_path_info) buffer=  %s\n", buffer);
278 
279    if (path_is_directory(buffer))
280    {
281        log_cb(RETRO_LOG_DEBUG, "(osd_get_path_info) path is directory _-_ %s\n",buffer);
282       return PATH_IS_DIRECTORY;
283    }
284    else if (filestream_exists(buffer))
285    {
286       log_cb(RETRO_LOG_DEBUG, "(osd_get_path_info) path is file _-_ %s\n",buffer);
287       return PATH_IS_FILE;
288    }
289    log_cb(RETRO_LOG_DEBUG, "(osd_get_path_info) path not found _-_ %s\n",buffer);
290    return PATH_NOT_FOUND;
291 }
292 
osd_fopen(int pathtype,int pathindex,const char * filename,const char * mode)293 FILE* osd_fopen(int pathtype, int pathindex, const char *filename, const char *mode)
294 {
295    char buffer[PATH_MAX_LENGTH]= {0};
296    char currDir[PATH_MAX_LENGTH]= {0};
297    FILE* out;
298 
299    osd_get_path(pathtype, currDir);
300    snprintf(buffer, PATH_MAX_LENGTH, "%s%c%s", currDir, PATH_DEFAULT_SLASH_C(), filename);
301 
302    out = fopen(buffer, mode);
303    if (out)  log_cb(RETRO_LOG_DEBUG, "(osd_fopen) opened the file:  %s\n", buffer);
304    else  log_cb(RETRO_LOG_DEBUG, "(osd_fopen) failed to open file:  %s\n", buffer);
305 
306    return out;
307 }
308 
309 
310 
311 /***************************************************************************
312 	mame_fopen_rom
313 ***************************************************************************/
314 
315 /* Similar to mame_fopen(,,FILETYPE_ROM), but lets you specify an expected checksum
316    (better encapsulation of the load by CRC used for ZIP files) */
mame_fopen_rom(const char * gamename,const char * filename,const char * exphash)317 mame_file *mame_fopen_rom(const char *gamename, const char *filename, const char* exphash)
318 {
319 	return generic_fopen(FILETYPE_ROM, gamename, filename, exphash, FILEFLAG_OPENREAD | FILEFLAG_HASH);
320 }
321 
322 
323 /***************************************************************************
324 	mame_fclose
325 ***************************************************************************/
326 
mame_fclose(mame_file * file)327 void mame_fclose(mame_file *file)
328 {
329 #ifdef DEBUG_COOKIE
330 	assert(file->debug_cookie == DEBUG_COOKIE);
331 	file->debug_cookie = 0;
332 #endif
333 
334 	/* switch off the file type */
335 	switch (file->type)
336 	{
337 		case PLAIN_FILE:
338 			fclose(file->file);
339 			break;
340 
341 		case ZIPPED_FILE:
342 		case RAM_FILE:
343 			if (file->data)
344 				free(file->data);
345 			break;
346 	}
347 
348 	/* free the file data */
349 	free(file);
350 }
351 
352 
353 
354 /***************************************************************************
355 	mame_faccess
356 ***************************************************************************/
357 
mame_faccess(const char * filename,int filetype)358 int mame_faccess(const char *filename, int filetype)
359 {
360 	const char *extension = get_extension_for_filetype(filetype);
361 	int pathcount = osd_get_path_count(filetype);
362 	char modified_filename[256];
363 	int pathindex;
364 
365 	/* copy the filename and add an extension */
366 	strcpy(modified_filename, filename);
367 	if (extension)
368 	{
369 		char *p = strchr(modified_filename, '.');
370 		if (p)
371 			strcpy(p, extension);
372 		else
373 		{
374 			strcat(modified_filename, ".");
375 			strcat(modified_filename, extension);
376 		}
377 	}
378 
379 	/* loop over all paths */
380 	for (pathindex = 0; pathindex < pathcount; pathindex++)
381 	{
382 		char name[PATH_MAX_LENGTH];
383 
384 		/* first check the raw filename, in case we're looking for a directory */
385 		sprintf(name, "%s", filename);
386 		log_cb(RETRO_LOG_DEBUG, LOGPRE "mame_faccess: trying %s\n", name);
387 		if (osd_get_path_info(filetype, pathindex, name) != PATH_NOT_FOUND)
388 			return 1;
389 
390 		/* try again with a .zip extension */
391 		sprintf(name, "%s.zip", filename);
392 		log_cb(RETRO_LOG_DEBUG, LOGPRE "mame_faccess: trying %s\n", name);
393 		if (osd_get_path_info(filetype, pathindex, name) != PATH_NOT_FOUND)
394 			return 1;
395 
396 		/* does such a directory (or file) exist? */
397 		sprintf(name, "%s", modified_filename);
398 		log_cb(RETRO_LOG_DEBUG, LOGPRE "mame_faccess: trying %s\n", name);
399 		if (osd_get_path_info(filetype, pathindex, name) != PATH_NOT_FOUND)
400 			return 1;
401 	}
402 
403 	/* no match */
404 	return 0;
405 }
406 
407 
408 
409 /***************************************************************************
410 	mame_fread
411 ***************************************************************************/
412 
mame_fread(mame_file * file,void * buffer,UINT32 length)413 UINT32 mame_fread(mame_file *file, void *buffer, UINT32 length)
414 {
415 	/* switch off the file type */
416 	switch (file->type)
417 	{
418 		case PLAIN_FILE:
419 			return fread(buffer, 1, length, file->file);
420 
421 		case ZIPPED_FILE:
422 		case RAM_FILE:
423 			if (file->data)
424 			{
425 				if (file->offset + length > file->length)
426 				{
427 					length = file->length - file->offset;
428 					file->eof = 1;
429 				}
430 				memcpy(buffer, file->data + file->offset, length);
431 				file->offset += length;
432 				return length;
433 			}
434 			break;
435 	}
436 
437 	return 0;
438 }
439 
440 
441 
442 /***************************************************************************
443 	mame_fwrite
444 ***************************************************************************/
445 
mame_fwrite(mame_file * file,const void * buffer,UINT32 length)446 UINT32 mame_fwrite(mame_file *file, const void *buffer, UINT32 length)
447 {
448 	/* check against null pointer */
449 	if (!file)
450 		return 0;
451 
452 	/* switch off the file type */
453 	switch (file->type)
454 	{
455 		case PLAIN_FILE:
456 			return fwrite(buffer, 1, length, file->file);
457 	}
458 
459 	return 0;
460 }
461 
462 
463 
464 /***************************************************************************
465 	mame_fseek
466 ***************************************************************************/
467 
mame_fseek(mame_file * file,INT64 offset,int whence)468 int mame_fseek(mame_file *file, INT64 offset, int whence)
469 {
470 	int err = 0;
471 
472 	/* switch off the file type */
473 	switch (file->type)
474 	{
475 		case PLAIN_FILE:
476 			return fseek(file->file, offset, whence);
477 
478 		case ZIPPED_FILE:
479 		case RAM_FILE:
480 			switch (whence)
481 			{
482 				case SEEK_SET:
483 					file->offset = offset;
484 					break;
485 				case SEEK_CUR:
486 					file->offset += offset;
487 					break;
488 				case SEEK_END:
489 					file->offset = file->length + offset;
490 					break;
491 			}
492 			file->eof = 0;
493 			break;
494 	}
495 
496 	return err;
497 }
498 
499 
500 
501 /***************************************************************************
502 	mame_fchecksum
503 ***************************************************************************/
504 
mame_fchecksum(const char * gamename,const char * filename,unsigned int * length,char * hash)505 int mame_fchecksum(const char *gamename, const char *filename, unsigned int *length, char* hash)
506 {
507 	mame_file *file;
508 
509 	/* first open the file; we pass the source hash because it contains
510 	   the expected checksum for the file (used to load by checksum) */
511 	file = generic_fopen(FILETYPE_ROM, gamename, filename, hash, FILEFLAG_OPENREAD | FILEFLAG_HASH | FILEFLAG_VERIFY_ONLY);
512 
513 	/* if we didn't succeed return -1 */
514 	if (!file)
515 		return -1;
516 
517 	/* close the file and save the length & checksum */
518 	hash_data_copy(hash, file->hash);
519 	*length = file->length;
520 	mame_fclose(file);
521 	return 0;
522 }
523 
524 
525 
526 /***************************************************************************
527 	mame_fsize
528 ***************************************************************************/
529 
mame_fsize(mame_file * file)530 UINT64 mame_fsize(mame_file *file)
531 {
532 	/* switch off the file type */
533 	switch (file->type)
534 	{
535 		case PLAIN_FILE:
536 		{
537 			int size, offs;
538 			offs = ftell(file->file);
539 			fseek(file->file, 0, SEEK_END);
540 			size = ftell(file->file);
541 			fseek(file->file, offs, SEEK_SET);
542 			return size;
543 		}
544 
545 		case RAM_FILE:
546 		case ZIPPED_FILE:
547 			return file->length;
548 	}
549 
550 	return 0;
551 }
552 
553 
554 
555 /***************************************************************************
556 	mame_fhash
557 ***************************************************************************/
558 
mame_fhash(mame_file * file)559 const char* mame_fhash(mame_file *file)
560 {
561 	return file->hash;
562 }
563 
564 
565 
566 /***************************************************************************
567 	mame_fgetc
568 ***************************************************************************/
569 
mame_fgetc(mame_file * file)570 int mame_fgetc(mame_file *file)
571 {
572 	unsigned char buffer;
573 
574 	/* switch off the file type */
575 	switch (file->type)
576 	{
577 		case PLAIN_FILE:
578 			if (fread(&buffer, 1, 1, file->file) == 1)
579 				return buffer;
580 			return EOF;
581 
582 		case RAM_FILE:
583 		case ZIPPED_FILE:
584 			if (file->offset < file->length)
585 				return file->data[file->offset++];
586 			else
587 				file->eof = 1;
588 			return EOF;
589 	}
590 	return EOF;
591 }
592 
593 
594 
595 /***************************************************************************
596 	mame_ungetc
597 ***************************************************************************/
598 
mame_ungetc(int c,mame_file * file)599 int mame_ungetc(int c, mame_file *file)
600 {
601 	/* switch off the file type */
602 	switch (file->type)
603 	{
604 		case PLAIN_FILE:
605 			if (feof(file->file))
606 			{
607 				if (fseek(file->file, 0, SEEK_CUR))
608 					return c;
609 			}
610 			else
611 			{
612 				if (fseek(file->file, -1, SEEK_CUR))
613 					return c;
614 			}
615 			return EOF;
616 
617 		case RAM_FILE:
618 		case ZIPPED_FILE:
619 			if (file->eof)
620 				file->eof = 0;
621 			else if (file->offset > 0)
622 			{
623 				file->offset--;
624 				return c;
625 			}
626 			return EOF;
627 	}
628 	return EOF;
629 }
630 
631 
632 
633 /***************************************************************************
634 	mame_fgets
635 ***************************************************************************/
636 
mame_fgets(char * buffer,int length,mame_file * file)637 char *mame_fgets(char *buffer, int length, mame_file *file)
638 {
639 	char *needle = buffer;
640 
641 	/* loop while we have characters */
642 	while (length > 0)
643 	{
644 		int character = mame_fgetc(file);
645 		if (character == EOF)
646 			break;
647 
648 		/* if it's CR, look for an LF afterwards */
649 		if (character == 0x0d)
650 		{
651 			int next_char = mame_fgetc(file);
652 			if (next_char != 0x0a)
653 				mame_ungetc(next_char, file);
654 			*needle++ = 0x0d;
655 			length--;
656 			break;
657 		}
658 
659 		/* if it's LF, reinterp as a CR for consistency */
660 		else if (character == 0x0a)
661 		{
662 			*needle++ = 0x0d;
663 			length--;
664 			break;
665 		}
666 
667 		/* otherwise, pop the character in and continue */
668 		*needle++ = character;
669 		length--;
670 	}
671 
672 	/* if we put nothing in, return NULL */
673 	if (needle == buffer)
674 		return NULL;
675 
676 	/* otherwise, terminate */
677 	if (length > 0)
678 		*needle++ = 0;
679 	return buffer;
680 }
681 
682 
683 /***************************************************************************
684 	mame_feof
685 ***************************************************************************/
686 
mame_feof(mame_file * file)687 int mame_feof(mame_file *file)
688 {
689 	/* switch off the file type */
690 	switch (file->type)
691 	{
692 		case PLAIN_FILE:
693 			return feof(file->file);
694 
695 		case RAM_FILE:
696 		case ZIPPED_FILE:
697 			return (file->eof);
698 	}
699 
700 	return 1;
701 }
702 
703 
704 
705 /***************************************************************************
706 	mame_ftell
707 ***************************************************************************/
708 
mame_ftell(mame_file * file)709 UINT64 mame_ftell(mame_file *file)
710 {
711 	/* switch off the file type */
712 	switch (file->type)
713 	{
714 		case PLAIN_FILE:
715 			return ftell(file->file);
716 
717 		case RAM_FILE:
718 		case ZIPPED_FILE:
719 			return file->offset;
720 	}
721 
722 	return -1L;
723 }
724 
725 
726 
727 /***************************************************************************
728 	mame_fread_swap
729 ***************************************************************************/
730 
mame_fread_swap(mame_file * file,void * buffer,UINT32 length)731 UINT32 mame_fread_swap(mame_file *file, void *buffer, UINT32 length)
732 {
733 	UINT8 *buf;
734 	UINT8 temp;
735 	int res, i;
736 
737 	/* standard read first */
738 	res = mame_fread(file, buffer, length);
739 
740 	/* swap the result */
741 	buf = buffer;
742 	for (i = 0; i < res; i += 2)
743 	{
744 		temp = buf[i];
745 		buf[i] = buf[i + 1];
746 		buf[i + 1] = temp;
747 	}
748 
749 	return res;
750 }
751 
752 
753 
754 /***************************************************************************
755 	mame_fwrite_swap
756 ***************************************************************************/
757 
mame_fwrite_swap(mame_file * file,const void * buffer,UINT32 length)758 UINT32 mame_fwrite_swap(mame_file *file, const void *buffer, UINT32 length)
759 {
760 	UINT8 *buf;
761 	UINT8 temp;
762 	int res, i;
763 
764 	/* swap the data first */
765 	buf = (UINT8 *)buffer;
766 	for (i = 0; i < length; i += 2)
767 	{
768 		temp = buf[i];
769 		buf[i] = buf[i + 1];
770 		buf[i + 1] = temp;
771 	}
772 
773 	/* do the write */
774 	res = mame_fwrite(file, buffer, length);
775 
776 	/* swap the data back */
777 	for (i = 0; i < length; i += 2)
778 	{
779 		temp = buf[i];
780 		buf[i] = buf[i + 1];
781 		buf[i + 1] = temp;
782 	}
783 
784 	return res;
785 }
786 
787 
788 
789 /***************************************************************************
790 	compose_path
791 ***************************************************************************/
792 
compose_path(char * output,const char * gamename,const char * filename,const char * extension)793 static INLINE void compose_path(char *output, const char *gamename, const char *filename, const char *extension)
794 {
795 	char *filename_base = output;
796 	*output = 0;
797 
798 	/* if there's a gamename, add that; only add a '/' if there is a filename as well */
799 	if (gamename)
800 	{
801 		strcat(output, gamename);
802 		if (filename)
803 		{
804 			strcat(output, "/");
805 			filename_base = &output[strlen(output)];
806 		}
807 	}
808 
809 	/* if there's a filename, add that */
810 	if (filename)
811 		strcat(output, filename);
812 
813 	/* if there's no extension in the filename, add the extension */
814 	if (extension && !strchr(filename_base, '.'))
815 	{
816 		strcat(output, ".");
817 		strcat(output, extension);
818 	}
819 }
820 
821 
822 
823 /***************************************************************************
824 	get_extension_for_filetype
825 ***************************************************************************/
826 
get_extension_for_filetype(int filetype)827 const char *get_extension_for_filetype(int filetype)
828 {
829 	const char *extension;
830 
831 	/* now open the file appropriately */
832 	switch (filetype)
833 	{
834 		case FILETYPE_RAW:              /* raw data files */
835 		case FILETYPE_ROM:              /* ROM files */
836 		case FILETYPE_HIGHSCORE_DB:     /* highscore database/history files */
837 		case FILETYPE_HISTORY:          /* game history files */
838 		case FILETYPE_CHEAT:            /* cheat file */
839 		default:                        /* anything else */
840 			extension = NULL;
841 			break;
842 
843 		case FILETYPE_IMAGE:		/* disk image files */
844 			extension = "chd";
845 			break;
846 
847 		case FILETYPE_IMAGE_DIFF:	/* differencing drive images */
848 			extension = "dif";
849 			break;
850 
851 		case FILETYPE_SAMPLE:		/* samples */
852 			extension = "wav";
853 			break;
854 
855 		case FILETYPE_SAMPLE_FLAC:	/* samples */
856 			extension = "flac";
857 			break;
858 
859 		case FILETYPE_ARTWORK:		/* artwork files */
860 			extension = "png";
861 			break;
862 
863 		case FILETYPE_NVRAM:		/* NVRAM files */
864 			extension = "nv";
865 			break;
866 
867 		case FILETYPE_HIGHSCORE:	/* high score files */
868 			extension = "hi";
869 			break;
870 
871 		case FILETYPE_LANGUAGE:		/* language files */
872 			extension = "lng";
873 			break;
874 
875 		case FILETYPE_CONFIG:		/* config files */
876 			extension = "cfg";
877 			break;
878 
879 		case FILETYPE_MEMCARD:		/* memory card files */
880 			extension = "mem";
881 			break;
882 
883 		case FILETYPE_CTRLR:		/* config files */
884 			extension = "ini";
885 			break;
886 
887 	}
888 	return extension;
889 }
890 
891 
892 
893 /***************************************************************************
894 	generic_fopen
895 ***************************************************************************/
896 
generic_fopen(int pathtype,const char * gamename,const char * filename,const char * hash,UINT32 flags)897 static mame_file *generic_fopen(int pathtype, const char *gamename, const char *filename, const char* hash, UINT32 flags)
898 {
899 	static const char *access_modes[] = { "rb", "rb", "wb", "r+b" };
900 	const char *extension = get_extension_for_filetype(pathtype);
901 	int pathcount = osd_get_path_count(pathtype);
902 	int pathindex, pathstart, pathstop, pathinc;
903 	mame_file file, *newfile;
904 	char tempname[256];
905 
906 	log_cb(RETRO_LOG_DEBUG, "(generic_fopen) (pathtype:%d, gamename:%s, filename:%s, extension:%s, flags:%X)\n", pathtype, gamename, filename, extension, flags);
907 
908 	/* reset the file handle */
909 	memset(&file, 0, sizeof(file));
910 
911 	/* check for incompatible flags */
912 	if ((flags & FILEFLAG_OPENWRITE) && (flags & FILEFLAG_HASH))
913 		fprintf(stderr, "Can't use HASH option with WRITE option in generic_fopen!\n");
914 
915 	/* determine start/stop based on reverse search flag */
916 	if (!(flags & FILEFLAG_REVERSE_SEARCH))
917 	{
918 		pathstart = 0;
919 		pathstop = pathcount;
920 		pathinc = 1;
921 	}
922 	else
923 	{
924 		pathstart = pathcount - 1;
925 		pathstop = -1;
926 		pathinc = -1;
927 	}
928 
929 	/* loop over paths */
930 	for (pathindex = pathstart; pathindex != pathstop; pathindex += pathinc)
931 	{
932 		char name[PATH_MAX_LENGTH];
933 
934 		/* ----------------- STEP 1: OPEN THE FILE RAW -------------------- */
935 
936 		/* first look for path/gamename as a directory */
937 		compose_path(name, gamename, NULL, NULL);
938 		log_cb(RETRO_LOG_DEBUG, LOGPRE "Trying %s\n", name);
939 
940 
941 		/* if the directory exists, proceed */
942 		if (*name == 0 || osd_get_path_info(pathtype, pathindex, name) == PATH_IS_DIRECTORY)
943 		{
944 			log_cb(RETRO_LOG_DEBUG, LOGPRE "(generic_fopen) directory exists: %s\n", name);
945 			/* now look for path/gamename/filename.ext */
946 			compose_path(name, gamename, filename, extension);
947 
948 			/* if we need checksums, load it into RAM and compute it along the way */
949 			if (flags & FILEFLAG_HASH)
950 			{
951 				if (checksum_file(pathtype, pathindex, name, &file.data, &file.length, file.hash) == 0)
952 				{
953 					file.type = RAM_FILE;
954 					break;
955 				}
956 			}
957 
958 			/* otherwise, just open it straight */
959 			else
960 			{
961 				log_cb(RETRO_LOG_DEBUG, LOGPRE " (generic_fopen) using osd_fopen %s\n", name);
962 				file.type = PLAIN_FILE;
963 				file.file = osd_fopen(pathtype, pathindex, name, access_modes[flags & 3]);
964 				if (file.file == NULL && (flags & 3) == 3)
965 					file.file = osd_fopen(pathtype, pathindex, name, "w+b");
966 				if (file.file != NULL)
967 					break;
968 			}
969 
970 
971 		}
972 
973 		/* ----------------- STEP 2: OPEN THE FILE IN A ZIP -------------------- */
974 
975 		/* now look for it within a ZIP file */
976 		if (!(flags & (FILEFLAG_OPENWRITE | FILEFLAG_NOZIP)))
977 		{
978 			/* first look for path/gamename.zip */
979 			compose_path(name, gamename, NULL, "zip");
980 			log_cb(RETRO_LOG_DEBUG, LOGPRE "Trying %s file\n", name);
981 
982 			/* if the ZIP file exists, proceed */
983 			if (osd_get_path_info(pathtype, pathindex, name) == PATH_IS_FILE)
984 			{
985 				UINT32 ziplength;
986 
987 				/* if the file was able to be extracted from the ZIP, continue */
988 				compose_path(tempname, NULL, filename, extension);
989 
990 				/* verify-only case */
991 				if (flags & FILEFLAG_VERIFY_ONLY)
992 				{
993 					UINT8 crcs[4];
994 					UINT32 crc = 0;
995 
996 					/* Since this is a .ZIP file, we extract the CRC from the expected hash
997 					   (if any), so that we can load by CRC if needed. We must check that
998 					   the hash really contains a CRC, because it could be a NO_DUMP rom
999 					   for which we do not know the CRC yet. */
1000 					if (hash && hash_data_extract_binary_checksum(hash, HASH_CRC, crcs) != 0)
1001 					{
1002 						/* Store the CRC in a single DWORD */
1003 						crc = ((unsigned long)crcs[0] << 24) |
1004 							  ((unsigned long)crcs[1] << 16) |
1005 							  ((unsigned long)crcs[2] <<  8) |
1006 							  ((unsigned long)crcs[3] <<  0);
1007 					}
1008 
1009 					hash_data_clear(file.hash);
1010 
1011 					if (checksum_zipped_file(pathtype, pathindex, name, tempname, &ziplength, &crc) == 0)
1012 					{
1013 						file.length = ziplength;
1014 						file.type = UNLOADED_ZIPPED_FILE;
1015 
1016 						crcs[0] = (UINT8)(crc >> 24);
1017 						crcs[1] = (UINT8)(crc >> 16);
1018 						crcs[2] = (UINT8)(crc >> 8);
1019 						crcs[3] = (UINT8)(crc >> 0);
1020 						hash_data_insert_binary_checksum(file.hash, HASH_CRC, crcs);
1021 						break;
1022 					}
1023 				}
1024 
1025 				/* full load case */
1026 				else
1027 				{
1028 					int err;
1029 
1030 					/* Try loading the file */
1031 					err = load_zipped_file(pathtype, pathindex, name, tempname, &file.data, &ziplength);
1032 
1033 					/* If it failed, since this is a ZIP file, we can try to load by CRC
1034 					   if an expected hash has been provided. unzip.c uses this ugly hack
1035 					   of specifying the CRC as filename. */
1036 					if (err && hash)
1037 					{
1038 						char crcn[9];
1039 
1040 						hash_data_extract_printable_checksum(hash, HASH_CRC, crcn);
1041 
1042 						err = load_zipped_file(pathtype, pathindex, name, crcn, &file.data, &ziplength);
1043 					}
1044 
1045 					if (err == 0)
1046 					{
1047 						unsigned functions;
1048 
1049 						log_cb(RETRO_LOG_DEBUG, LOGPRE "Using (mame_fopen) zip file for %s\n", filename);
1050 						file.length = ziplength;
1051 						file.type = ZIPPED_FILE;
1052 
1053 						/* Since we already loaded the file, we can easily calculate the
1054 						   checksum of all the functions. In practice, we use only the
1055 						   functions for which we have an expected checksum to compare with. */
1056 						functions = hash_data_used_functions(hash);
1057 
1058 						/* If user asked for CRC only, and there is an expected checksum
1059 						   for CRC in the driver, compute only CRC. */
1060 						if (options.crc_only && (functions & HASH_CRC))
1061 							functions = HASH_CRC;
1062 
1063 						hash_compute(file.hash, file.data, file.length, functions);
1064 						break;
1065 					}
1066 				}
1067 			}
1068 		}
1069 	}
1070 
1071 	/* if we didn't succeed, just return NULL */
1072 	if (pathindex == pathstop)
1073 		return NULL;
1074 
1075 	/* otherwise, duplicate the file */
1076 	newfile = malloc(sizeof(file));
1077 	if (newfile)
1078 	{
1079 		*newfile = file;
1080 #ifdef DEBUG_COOKIE
1081 		newfile->debug_cookie = DEBUG_COOKIE;
1082 #endif
1083 	}
1084 
1085 	return newfile;
1086 }
1087 
1088 
1089 
1090 /***************************************************************************
1091 	checksum_file
1092 ***************************************************************************/
1093 
checksum_file(int pathtype,int pathindex,const char * file,UINT8 ** p,UINT64 * size,char * hash)1094 static int checksum_file(int pathtype, int pathindex, const char *file, UINT8 **p, UINT64 *size, char* hash)
1095 {
1096 	UINT64 length;
1097 	UINT8 *data;
1098 	FILE *f;
1099 	unsigned int functions;
1100 
1101 	/* open the file */
1102 	f = osd_fopen(pathtype, pathindex, file, "rb");
1103 	if (!f)
1104 		return -1;
1105 
1106 	/* determine length of file */
1107 	if (fseek(f, 0L, SEEK_END) != 0)
1108 	{
1109 		fclose(f);
1110 		return -1;
1111 	}
1112 
1113 	length = ftell(f);
1114 	if (length == -1L)
1115 	{
1116 		fclose(f);
1117 		return -1;
1118 	}
1119 
1120 	/* allocate space for entire file */
1121 	data = malloc(length);
1122 	if (!data)
1123 	{
1124 		fclose(f);
1125 		return -1;
1126 	}
1127 
1128 	/* read entire file into memory */
1129 	if (fseek(f, 0L, SEEK_SET) != 0)
1130 	{
1131 		free(data);
1132 		fclose(f);
1133 		return -1;
1134 	}
1135 
1136 	if (fread(data, 1, length, f) != length)
1137 	{
1138 		free(data);
1139 		fclose(f);
1140 		return -1;
1141 	}
1142 
1143 	*size = length;
1144 
1145 
1146 	/* compute the checksums (only the functions for which we have an expected
1147 	   checksum). Take also care of crconly: if the user asked, we will calculate
1148 	   only the CRC, but only if there is an expected CRC for this file. */
1149 	functions = hash_data_used_functions(hash);
1150 	if (options.crc_only && (functions & HASH_CRC))
1151 		functions = HASH_CRC;
1152 	hash_compute(hash, data, length, functions);
1153 
1154 	/* if the caller wants the data, give it away, otherwise free it */
1155 	if (p)
1156 		*p = data;
1157 	else
1158 		free(data);
1159 
1160 	/* close the file */
1161 	fclose(f);
1162 	return 0;
1163 }
1164 
1165 
1166 /***************************************************************************
1167 	mame_vfprintf
1168 ***************************************************************************/
1169 
mame_vfprintf(mame_file * f,const char * fmt,va_list va)1170 int mame_vfprintf(mame_file *f, const char *fmt, va_list va)
1171 {
1172 	char buf[512];
1173 	vsnprintf(buf, sizeof(buf), fmt, va);
1174 	return mame_fwrite(f, buf, strlen(buf));
1175 }
1176 
1177 /***************************************************************************
1178 	spawn_bootstrap_nvram
1179   creates a new nvram file for the current romset (as specified in
1180   options.romset_filename_noext) using bootstrap_nvram as the source.
1181 ***************************************************************************/
1182 
spawn_bootstrap_nvram(unsigned char const * bootstrap_nvram,unsigned nvram_length)1183 mame_file *spawn_bootstrap_nvram(unsigned char const *bootstrap_nvram, unsigned nvram_length)
1184 {
1185   mame_file *nvram_file = NULL;
1186 
1187   log_cb(RETRO_LOG_INFO, LOGPRE "Generating bootstrap nvram for %s\n", options.romset_filename_noext);
1188 
1189   nvram_file = mame_fopen(options.romset_filename_noext, 0, FILETYPE_NVRAM, 1);
1190   mame_fwrite(nvram_file, bootstrap_nvram, nvram_length);
1191   mame_fclose(nvram_file);
1192 
1193   nvram_file = mame_fopen(options.romset_filename_noext, 0, FILETYPE_NVRAM, 0);
1194 
1195   if(!nvram_file)
1196     log_cb(RETRO_LOG_ERROR, LOGPRE "Error generating nvram bootstrap file!\n");
1197 
1198   return nvram_file;
1199 }
1200 
1201 
1202 /***************************************************************************
1203 	mame_fprintf
1204 ***************************************************************************/
1205 
mame_fprintf(mame_file * f,const char * fmt,...)1206 int CLIB_DECL mame_fprintf(mame_file *f, const char *fmt, ...)
1207 {
1208 	int rc;
1209 	va_list va;
1210 	va_start(va, fmt);
1211 	rc = mame_vfprintf(f, fmt, va);
1212 	va_end(va);
1213 	return rc;
1214 }
1215