Commit | Line | Data |
---|---|---|
3719602c PC |
1 | #include <formats/cdfs.h> |
2 | ||
3 | #include <retro_miscellaneous.h> | |
4 | #include <compat/strl.h> | |
5 | #include <file/file_path.h> | |
6 | #include <string/stdstring.h> | |
7 | ||
8 | #ifdef HAVE_CHD | |
9 | #include <streams/chd_stream.h> | |
10 | #endif | |
11 | ||
12 | static void cdfs_determine_sector_size(cdfs_track_t* track) | |
13 | { | |
14 | uint8_t buffer[32]; | |
15 | const int toc_sector = 16; | |
16 | ||
17 | /* MODE information is normally found in the CUE sheet, but we can try to determine it from the raw data. | |
18 | * | |
19 | * MODE1/2048 - CDROM Mode1 Data (cooked) [no header, no footer] | |
20 | * MODE1/2352 - CDROM Mode1 Data (raw) [16 byte header, 288 byte footer] | |
21 | * MODE2/2336 - CDROM-XA Mode2 Data [8 byte header, 280 byte footer] | |
22 | * MODE2/2352 - CDROM-XA Mode2 Data [24 byte header, 280 byte footer] | |
23 | * | |
24 | * Note that MODE is actually a property on each sector and can change between 1 and 2 depending on how much error | |
25 | * correction the author desired. To support that, the data format must be "/2352" to include the full header and | |
26 | * data without error correction information, at which point the CUE sheet information becomes just a hint. | |
27 | */ | |
28 | ||
29 | /* The boot record or primary volume descriptor is always at sector 16 and will contain a "CD001" marker */ | |
30 | intfstream_seek(track->stream, toc_sector * 2352 + track->first_sector_offset, SEEK_SET); | |
31 | if (intfstream_read(track->stream, &buffer, sizeof(buffer)) != (int64_t)sizeof(buffer)) | |
32 | return; | |
33 | ||
34 | /* if this is a CDROM-XA data source, the "CD001" tag will be 25 bytes into the sector */ | |
35 | if ( buffer[25] == 0x43 | |
36 | && buffer[26] == 0x44 | |
37 | && buffer[27] == 0x30 | |
38 | && buffer[28] == 0x30 | |
39 | && buffer[29] == 0x31) | |
40 | { | |
41 | track->stream_sector_size = 2352; | |
42 | track->stream_sector_header_size = 24; | |
43 | } | |
44 | /* otherwise it should be 17 bytes into the sector */ | |
45 | else if (buffer[17] == 0x43 | |
46 | && buffer[18] == 0x44 | |
47 | && buffer[19] == 0x30 | |
48 | && buffer[20] == 0x30 | |
49 | && buffer[21] == 0x31) | |
50 | { | |
51 | track->stream_sector_size = 2352; | |
52 | track->stream_sector_header_size = 16; | |
53 | } | |
54 | else | |
55 | { | |
56 | /* ISO-9660 says the first twelve bytes of a sector should be the sync pattern 00 FF FF FF FF FF FF FF FF FF FF 00 */ | |
57 | if ( | |
58 | buffer[ 0] == 0 | |
59 | && buffer[ 1] == 0xFF | |
60 | && buffer[ 2] == 0xFF | |
61 | && buffer[ 3] == 0xFF | |
62 | && buffer[ 4] == 0xFF | |
63 | && buffer[ 5] == 0xFF | |
64 | && buffer[ 6] == 0xFF | |
65 | && buffer[ 7] == 0xFF | |
66 | && buffer[ 8] == 0xFF | |
67 | && buffer[ 9] == 0xFF | |
68 | && buffer[10] == 0xFF | |
69 | && buffer[11] == 0) | |
70 | { | |
71 | /* if we didn't find a CD001 tag, this format may predate ISO-9660 */ | |
72 | ||
73 | /* after the 12 byte sync pattern is three bytes identifying the sector and then one byte for the mode (total 16 bytes) */ | |
74 | track->stream_sector_size = 2352; | |
75 | track->stream_sector_header_size = 16; | |
76 | } | |
77 | } | |
78 | } | |
79 | ||
80 | static void cdfs_determine_sector_size_from_file_size(cdfs_track_t* track) | |
81 | { | |
82 | /* attempt to determine stream_sector_size from file size */ | |
83 | size_t size = intfstream_get_size(track->stream); | |
84 | ||
85 | if ((size % 2352) == 0) | |
86 | { | |
87 | /* raw tracks use all 2352 bytes and have a 24 byte header */ | |
88 | track->stream_sector_size = 2352; | |
89 | track->stream_sector_header_size = 24; | |
90 | } | |
91 | else if ((size % 2048) == 0) | |
92 | { | |
93 | /* cooked tracks eliminate all header/footer data */ | |
94 | track->stream_sector_size = 2048; | |
95 | track->stream_sector_header_size = 0; | |
96 | } | |
97 | else if ((size % 2336) == 0) | |
98 | { | |
99 | /* MODE 2 format without 16-byte sync data */ | |
100 | track->stream_sector_size = 2336; | |
101 | track->stream_sector_header_size = 8; | |
102 | } | |
103 | } | |
104 | ||
105 | static void cdfs_seek_track_sector(cdfs_track_t* track, unsigned int sector) | |
106 | { | |
107 | intfstream_seek(track->stream, | |
108 | sector * track->stream_sector_size | |
109 | + track->stream_sector_header_size | |
110 | + track->first_sector_offset, SEEK_SET); | |
111 | } | |
112 | ||
113 | void cdfs_seek_sector(cdfs_file_t* file, unsigned int sector) | |
114 | { | |
115 | /* only allowed if open_file was called with a NULL path */ | |
116 | if (file->first_sector == 0) | |
117 | { | |
118 | if (file->current_sector != (int)sector) | |
119 | { | |
120 | file->current_sector = (int)sector; | |
121 | file->sector_buffer_valid = 0; | |
122 | } | |
123 | ||
124 | file->pos = sector * 2048; | |
125 | file->current_sector_offset = 0; | |
126 | } | |
127 | } | |
128 | ||
129 | uint32_t cdfs_get_num_sectors(cdfs_file_t* file) | |
130 | { | |
131 | uint32_t frame_size = intfstream_get_frame_size(file->track->stream); | |
132 | if (frame_size == 0) | |
133 | { | |
134 | frame_size = file->track->stream_sector_size; | |
135 | if (frame_size == 0) | |
136 | frame_size = 1; /* prevent divide by 0 error if sector size is unknown */ | |
137 | } | |
138 | return (uint32_t)(intfstream_get_size(file->track->stream) / frame_size); | |
139 | } | |
140 | ||
141 | uint32_t cdfs_get_first_sector(cdfs_file_t* file) | |
142 | { | |
143 | return file->track->first_sector_index; | |
144 | } | |
145 | ||
146 | static int cdfs_find_file(cdfs_file_t* file, const char* path) | |
147 | { | |
148 | size_t path_length; | |
149 | int sector; | |
150 | uint8_t buffer[2048], *tmp; | |
151 | const char* slash = strrchr(path, '\\'); | |
152 | ||
153 | if (slash) | |
154 | { | |
155 | /* navigate the path to the directory record for the file */ | |
156 | const int dir_length = (int)(slash - path); | |
157 | memcpy(buffer, path, dir_length); | |
158 | buffer[dir_length] = '\0'; | |
159 | ||
160 | sector = cdfs_find_file(file, (const char*)buffer); | |
161 | if (sector < 0) | |
162 | return sector; | |
163 | ||
164 | path += dir_length + 1; | |
165 | } | |
166 | else | |
167 | { | |
168 | int offset; | |
169 | ||
170 | /* find the CD information (always 16 frames in) */ | |
171 | cdfs_seek_track_sector(file->track, 16); | |
172 | intfstream_read(file->track->stream, buffer, sizeof(buffer)); | |
173 | ||
174 | /* the directory_record starts at 156 bytes into the sector. | |
175 | * the sector containing the root directory contents is a | |
176 | * 3 byte value that is 2 bytes into the directory_record. */ | |
177 | offset = 156 + 2; | |
178 | sector = buffer[offset] | (buffer[offset + 1] << 8) | (buffer[offset + 2] << 16); | |
179 | } | |
180 | ||
181 | /* process the contents of the directory */ | |
182 | cdfs_seek_track_sector(file->track, sector); | |
183 | intfstream_read(file->track->stream, buffer, sizeof(buffer)); | |
184 | ||
185 | path_length = strlen(path); | |
186 | tmp = buffer; | |
187 | ||
188 | while (tmp < buffer + sizeof(buffer)) | |
189 | { | |
190 | /* The first byte of the record is the length of | |
191 | * the record - if 0, we reached the end of the data */ | |
192 | if (!*tmp) | |
193 | break; | |
194 | ||
195 | /* filename is 33 bytes into the record and | |
196 | * the format is "FILENAME;version" or "DIRECTORY" */ | |
197 | if ( (tmp[33 + path_length] == ';' | |
198 | || (tmp[33 + path_length] == '\0')) | |
199 | && strncasecmp((const char*)(tmp + 33), path, path_length) == 0) | |
200 | { | |
201 | /* the file size is in bytes 10-13 of the record */ | |
202 | file->size = | |
203 | (tmp[10]) | |
204 | | (tmp[11] << 8) | |
205 | | (tmp[12] << 16) | |
206 | | (tmp[13] << 24); | |
207 | ||
208 | /* the file contents are in the sector identified | |
209 | * in bytes 2-4 of the record */ | |
210 | sector = tmp[2] | (tmp[3] << 8) | (tmp[4] << 16); | |
211 | return sector; | |
212 | } | |
213 | ||
214 | /* the first byte of the record is the length of the record */ | |
215 | tmp += tmp[0]; | |
216 | } | |
217 | ||
218 | return -1; | |
219 | } | |
220 | ||
221 | int cdfs_open_file(cdfs_file_t* file, cdfs_track_t* track, const char* path) | |
222 | { | |
223 | if (!file || !track) | |
224 | return 0; | |
225 | ||
226 | memset(file, 0, sizeof(*file)); | |
227 | ||
228 | file->track = track; | |
229 | file->current_sector = -1; | |
230 | file->first_sector = -1; | |
231 | ||
232 | if (path) | |
233 | file->first_sector = cdfs_find_file(file, path); | |
234 | else if (file->track->stream_sector_size) | |
235 | { | |
236 | file->first_sector = 0; | |
237 | file->size = (unsigned int)((intfstream_get_size( | |
238 | file->track->stream) / file->track->stream_sector_size) | |
239 | * 2048); | |
240 | return 1; | |
241 | } | |
242 | ||
243 | return (file->first_sector >= 0); | |
244 | } | |
245 | ||
246 | int64_t cdfs_read_file(cdfs_file_t* file, void* buffer, uint64_t len) | |
247 | { | |
248 | int bytes_read = 0; | |
249 | ||
250 | if (!file || file->first_sector < 0 || !buffer) | |
251 | return 0; | |
252 | ||
253 | if (len > file->size - file->pos) | |
254 | len = file->size - file->pos; | |
255 | ||
256 | if (len == 0) | |
257 | return 0; | |
258 | ||
259 | if (file->sector_buffer_valid) | |
260 | { | |
261 | size_t remaining = 2048 - file->current_sector_offset; | |
262 | if (remaining > 0) | |
263 | { | |
264 | if (remaining >= len) | |
265 | { | |
266 | memcpy(buffer, | |
267 | &file->sector_buffer[file->current_sector_offset], | |
268 | (size_t)len); | |
269 | file->current_sector_offset += len; | |
270 | return len; | |
271 | } | |
272 | ||
273 | memcpy(buffer, | |
274 | &file->sector_buffer[file->current_sector_offset], remaining); | |
275 | buffer = (char*)buffer + remaining; | |
276 | bytes_read += remaining; | |
277 | len -= remaining; | |
278 | ||
279 | file->current_sector_offset += remaining; | |
280 | } | |
281 | ||
282 | ++file->current_sector; | |
283 | file->current_sector_offset = 0; | |
284 | file->sector_buffer_valid = 0; | |
285 | } | |
286 | else if (file->current_sector < file->first_sector) | |
287 | { | |
288 | file->current_sector = file->first_sector; | |
289 | file->current_sector_offset = 0; | |
290 | } | |
291 | ||
292 | while (len >= 2048) | |
293 | { | |
294 | cdfs_seek_track_sector(file->track, file->current_sector); | |
295 | intfstream_read(file->track->stream, buffer, 2048); | |
296 | ||
297 | buffer = (char*)buffer + 2048; | |
298 | bytes_read += 2048; | |
299 | ||
300 | ++file->current_sector; | |
301 | ||
302 | len -= 2048; | |
303 | } | |
304 | ||
305 | if (len > 0) | |
306 | { | |
307 | cdfs_seek_track_sector(file->track, file->current_sector); | |
308 | intfstream_read(file->track->stream, file->sector_buffer, 2048); | |
309 | memcpy(buffer, file->sector_buffer, (size_t)len); | |
310 | file->current_sector_offset = (unsigned int)len; | |
311 | file->sector_buffer_valid = 1; | |
312 | ||
313 | bytes_read += len; | |
314 | } | |
315 | ||
316 | file->pos += bytes_read; | |
317 | return bytes_read; | |
318 | } | |
319 | ||
320 | void cdfs_close_file(cdfs_file_t* file) | |
321 | { | |
322 | /* Not really anything to do here, just | |
323 | * clear out the first_sector so | |
324 | * read() won't do anything */ | |
325 | if (file) | |
326 | file->first_sector = -1; | |
327 | } | |
328 | ||
329 | int64_t cdfs_get_size(cdfs_file_t* file) | |
330 | { | |
331 | if (!file || file->first_sector < 0) | |
332 | return 0; | |
333 | return file->size; | |
334 | } | |
335 | ||
336 | int64_t cdfs_tell(cdfs_file_t* file) | |
337 | { | |
338 | if (!file || file->first_sector < 0) | |
339 | return -1; | |
340 | return file->pos; | |
341 | } | |
342 | ||
343 | int64_t cdfs_seek(cdfs_file_t* file, int64_t offset, int whence) | |
344 | { | |
345 | int64_t new_pos; | |
346 | int new_sector; | |
347 | ||
348 | if (!file || file->first_sector < 0) | |
349 | return -1; | |
350 | ||
351 | switch (whence) | |
352 | { | |
353 | case SEEK_SET: | |
354 | new_pos = offset; | |
355 | break; | |
356 | ||
357 | case SEEK_CUR: | |
358 | new_pos = file->pos + offset; | |
359 | break; | |
360 | ||
361 | case SEEK_END: | |
362 | new_pos = file->size - offset; | |
363 | break; | |
364 | ||
365 | default: | |
366 | return -1; | |
367 | } | |
368 | ||
369 | if (new_pos < 0) | |
370 | return -1; | |
371 | else if (new_pos > file->size) | |
372 | return -1; | |
373 | ||
374 | file->pos = (unsigned int)new_pos; | |
375 | file->current_sector_offset = file->pos % 2048; | |
376 | ||
377 | new_sector = file->pos / 2048; | |
378 | if (new_sector != file->current_sector) | |
379 | { | |
380 | file->current_sector = new_sector; | |
381 | file->sector_buffer_valid = false; | |
382 | } | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static void cdfs_skip_spaces(const char** ptr) | |
388 | { | |
389 | while (**ptr && (**ptr == ' ' || **ptr == '\t')) | |
390 | ++(*ptr); | |
391 | } | |
392 | ||
393 | static cdfs_track_t* cdfs_wrap_stream( | |
394 | intfstream_t* stream, | |
395 | unsigned first_sector_offset, | |
396 | unsigned first_sector_index) | |
397 | { | |
398 | cdfs_track_t* track = NULL; | |
399 | ||
400 | if (!stream) | |
401 | return NULL; | |
402 | ||
403 | track = (cdfs_track_t*) | |
404 | calloc(1, sizeof(*track)); | |
405 | track->stream = stream; | |
406 | track->first_sector_offset = first_sector_offset; | |
407 | track->first_sector_index = first_sector_index; | |
408 | ||
409 | cdfs_determine_sector_size(track); | |
410 | ||
411 | return track; | |
412 | } | |
413 | ||
414 | static cdfs_track_t* cdfs_open_cue_track( | |
415 | const char* path, unsigned int track_index) | |
416 | { | |
417 | char* cue = NULL; | |
418 | const char* line = NULL; | |
419 | int found_track = 0; | |
420 | char current_track_path[PATH_MAX_LENGTH] = {0}; | |
421 | char track_path[PATH_MAX_LENGTH] = {0}; | |
422 | unsigned int sector_size = 0; | |
423 | unsigned int previous_sector_size = 0; | |
424 | unsigned int previous_index_sector_offset= 0; | |
425 | unsigned int track_offset = 0; | |
426 | intfstream_t *cue_stream = intfstream_open_file(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
427 | int64_t stream_size = intfstream_get_size(cue_stream); | |
428 | char *cue_contents = (char*)malloc((size_t)(stream_size + 1)); | |
429 | cdfs_track_t* track = NULL; | |
430 | ||
431 | if (!cue_contents) | |
432 | { | |
433 | intfstream_close(cue_stream); | |
434 | return NULL; | |
435 | } | |
436 | ||
437 | intfstream_read(cue_stream, cue_contents, stream_size); | |
438 | intfstream_close(cue_stream); | |
439 | ||
440 | cue_contents[stream_size] = '\0'; | |
441 | ||
442 | cue = cue_contents; | |
443 | while (*cue) | |
444 | { | |
445 | cdfs_skip_spaces((const char**)&cue); | |
446 | line = cue; | |
447 | ||
448 | while (*cue && *cue != '\n') | |
449 | ++cue; | |
450 | if (*cue) | |
451 | *cue++ = '\0'; | |
452 | ||
453 | if (!strncasecmp(line, "FILE", 4)) | |
454 | { | |
455 | const char *file = line + 4; | |
456 | cdfs_skip_spaces(&file); | |
457 | ||
458 | if (file[0]) | |
459 | { | |
460 | const char *file_end = cue - 1; | |
461 | while (file_end > file && *file_end != ' ' && *file_end != '\t') | |
462 | --file_end; | |
463 | ||
464 | if ( file[0] == '"' | |
465 | && file_end[-1] == '"') | |
466 | { | |
467 | ++file; | |
468 | --file_end; | |
469 | } | |
470 | ||
471 | memcpy(current_track_path, file, file_end - file); | |
472 | current_track_path[file_end - file] = '\0'; | |
473 | } | |
474 | ||
475 | previous_sector_size = 0; | |
476 | previous_index_sector_offset = 0; | |
477 | track_offset = 0; | |
478 | } | |
479 | else if (!strncasecmp(line, "TRACK", 5)) | |
480 | { | |
481 | char *ptr = NULL; | |
482 | unsigned track_number = 0; | |
483 | const char *track = line + 5; | |
484 | cdfs_skip_spaces(&track); | |
485 | ||
486 | track_number = (unsigned)strtol(track, &ptr, 10); | |
487 | while (*track && *track != ' ' && *track != '\n') | |
488 | ++track; | |
489 | ||
490 | previous_sector_size = sector_size; | |
491 | ||
492 | cdfs_skip_spaces(&track); | |
493 | ||
494 | if (!strncasecmp(track, "MODE", 4)) | |
495 | { | |
496 | /* track_index = 0 means find the first data track */ | |
497 | if (!track_index || track_index == track_number) | |
498 | found_track = track_number; | |
499 | ||
500 | sector_size = atoi(track + 6); | |
501 | } | |
502 | else /* assume AUDIO */ | |
503 | sector_size = 2352; | |
504 | } | |
505 | else if (!strncasecmp(line, "INDEX", 5)) | |
506 | { | |
507 | unsigned sector_offset; | |
508 | unsigned min = 0, sec = 0, frame = 0; | |
509 | unsigned index_number = 0; | |
510 | const char *index = line + 5; | |
511 | ||
512 | cdfs_skip_spaces(&index); | |
513 | sscanf(index, "%u", &index_number); | |
514 | while (*index && *index != ' ' && *index != '\n') | |
515 | ++index; | |
516 | cdfs_skip_spaces(&index); | |
517 | ||
518 | sscanf(index, "%u:%u:%u", &min, &sec, &frame); | |
519 | sector_offset = ((min * 60) + sec) * 75 + frame; | |
520 | sector_offset -= previous_index_sector_offset; | |
521 | track_offset += sector_offset * previous_sector_size; | |
522 | previous_sector_size = sector_size; | |
523 | previous_index_sector_offset += sector_offset; | |
524 | ||
525 | if (found_track && index_number == 1) | |
526 | { | |
527 | if ( strstr(current_track_path, "/") | |
528 | || strstr(current_track_path, "\\")) | |
529 | strncpy(track_path, current_track_path, sizeof(track_path)); | |
530 | else | |
531 | { | |
532 | fill_pathname_basedir(track_path, path, sizeof(track_path)); | |
533 | strlcat(track_path, current_track_path, sizeof(track_path)); | |
534 | } | |
535 | ||
536 | break; | |
537 | } | |
538 | } | |
539 | } | |
540 | ||
541 | free(cue_contents); | |
542 | ||
543 | if (string_is_empty(track_path)) | |
544 | return NULL; | |
545 | ||
546 | /* NOTE: previous_index_sector_offset will only be valid if all tracks are in the same BIN file. | |
547 | * Otherwise, we need to determine how many tracks are in each previous BIN file, which is not | |
548 | * stored in the CUE file. This will affect cdfs_get_first_sector, which luckily isn't used much. */ | |
549 | track = cdfs_wrap_stream(intfstream_open_file( | |
550 | track_path, RETRO_VFS_FILE_ACCESS_READ, | |
551 | RETRO_VFS_FILE_ACCESS_HINT_NONE), track_offset, previous_index_sector_offset); | |
552 | ||
553 | if (track && track->stream_sector_size == 0) | |
554 | { | |
555 | track->stream_sector_size = sector_size; | |
556 | ||
557 | if (sector_size == 2352) | |
558 | track->stream_sector_header_size = 16; | |
559 | else if (sector_size == 2336) | |
560 | track->stream_sector_header_size = 8; | |
561 | } | |
562 | ||
563 | return track; | |
564 | } | |
565 | ||
566 | #ifdef HAVE_CHD | |
567 | static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index) | |
568 | { | |
569 | cdfs_track_t *track; | |
570 | intfstream_t *intf_stream = intfstream_open_chd_track(path, | |
571 | RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE, | |
572 | track_index); | |
573 | if (!intf_stream) | |
574 | return NULL; | |
575 | ||
576 | track = cdfs_wrap_stream(intf_stream, | |
577 | intfstream_get_offset_to_start(intf_stream), | |
578 | intfstream_get_first_sector(intf_stream)); | |
579 | ||
580 | if (track && track->stream_sector_header_size == 0) | |
581 | { | |
582 | track->stream_sector_size = intfstream_get_frame_size(intf_stream); | |
583 | ||
584 | if (track->stream_sector_size == 2352) | |
585 | track->stream_sector_header_size = 16; | |
586 | else if (track->stream_sector_size == 2336) | |
587 | track->stream_sector_header_size = 8; | |
588 | } | |
589 | ||
590 | return track; | |
591 | } | |
592 | #endif | |
593 | ||
594 | struct cdfs_track_t* cdfs_open_track(const char* path, | |
595 | unsigned int track_index) | |
596 | { | |
597 | const char* ext = path_get_extension(path); | |
598 | ||
599 | if (string_is_equal_noncase(ext, "cue")) | |
600 | return cdfs_open_cue_track(path, track_index); | |
601 | ||
602 | #ifdef HAVE_CHD | |
603 | if (string_is_equal_noncase(ext, "chd")) | |
604 | return cdfs_open_chd_track(path, track_index); | |
605 | #endif | |
606 | ||
607 | /* if opening track 1, try opening as a raw track */ | |
608 | if (track_index == 1) | |
609 | return cdfs_open_raw_track(path); | |
610 | ||
611 | /* unsupported file type */ | |
612 | return NULL; | |
613 | } | |
614 | ||
615 | struct cdfs_track_t* cdfs_open_data_track(const char* path) | |
616 | { | |
617 | const char* ext = path_get_extension(path); | |
618 | ||
619 | if (string_is_equal_noncase(ext, "cue")) | |
620 | return cdfs_open_cue_track(path, 0); | |
621 | ||
622 | #ifdef HAVE_CHD | |
623 | if (string_is_equal_noncase(ext, "chd")) | |
624 | return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY); | |
625 | #endif | |
626 | ||
627 | /* unsupported file type - try opening as a raw track */ | |
628 | return cdfs_open_raw_track(path); | |
629 | } | |
630 | ||
631 | cdfs_track_t* cdfs_open_raw_track(const char* path) | |
632 | { | |
633 | const char *ext = path_get_extension(path); | |
634 | cdfs_track_t *track = NULL; | |
635 | ||
636 | if ( string_is_equal_noncase(ext, "bin") | |
637 | || string_is_equal_noncase(ext, "iso")) | |
638 | { | |
639 | intfstream_t* file = intfstream_open_file(path, | |
640 | RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); | |
641 | ||
642 | track = cdfs_wrap_stream(file, 0, 0); | |
643 | if (track && track->stream_sector_size == 0) | |
644 | { | |
645 | cdfs_determine_sector_size_from_file_size(track); | |
646 | if (track->stream_sector_size == 0) | |
647 | { | |
648 | cdfs_close_track(track); | |
649 | track = NULL; | |
650 | } | |
651 | } | |
652 | } | |
653 | ||
654 | return track; | |
655 | } | |
656 | ||
657 | void cdfs_close_track(cdfs_track_t* track) | |
658 | { | |
659 | if (track) | |
660 | { | |
661 | if (track->stream) | |
662 | { | |
663 | intfstream_close(track->stream); | |
664 | free(track->stream); | |
665 | } | |
666 | ||
667 | free(track); | |
668 | } | |
669 | } |