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