1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (vfs_implementation_cdrom.c).
5 * ---------------------------------------------------------------------------------------
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 #include <vfs/vfs_implementation.h>
24 #include <file/file_path.h>
25 #include <compat/fopen_utf8.h>
26 #include <string/stdstring.h>
27 #include <cdrom/cdrom.h>
29 #if defined(_WIN32) && !defined(_XBOX)
33 /* TODO/FIXME - static global variable */
34 static cdrom_toc_t vfs_cdrom_toc = {0};
36 const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void)
38 return &vfs_cdrom_toc;
41 int64_t retro_vfs_file_seek_cdrom(
42 libretro_vfs_implementation_file *stream,
43 int64_t offset, int whence)
45 const char *ext = path_get_extension(stream->orig_path);
47 if (string_is_equal_noncase(ext, "cue"))
52 stream->cdrom.byte_pos = offset;
55 stream->cdrom.byte_pos += offset;
58 stream->cdrom.byte_pos = (stream->cdrom.cue_len - 1) + offset;
63 printf("[CDROM] Seek: Path %s Offset %" PRIu64 " is now at %" PRIu64 "\n",
66 stream->cdrom.byte_pos);
70 else if (string_is_equal_noncase(ext, "bin"))
72 int lba = (offset / 2352);
73 unsigned char min = 0;
74 unsigned char sec = 0;
75 unsigned char frame = 0;
77 const char *seek_type = "SEEK_SET";
86 seek_type = "SEEK_CUR";
88 stream->cdrom.byte_pos += offset;
89 new_lba = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352);
91 cdrom_lba_to_msf(new_lba, &min, &sec, &frame);
96 ssize_t pregap_lba_len = (vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].audio
98 : (vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba - vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba_start));
99 ssize_t lba_len = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_size - pregap_lba_len;
101 seek_type = "SEEK_END";
104 cdrom_lba_to_msf(lba_len + lba, &min, &sec, &frame);
105 stream->cdrom.byte_pos = lba_len * 2352;
112 seek_type = "SEEK_SET";
114 stream->cdrom.byte_pos = offset;
115 cdrom_lba_to_msf(vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352), &min, &sec, &frame);
120 stream->cdrom.cur_min = min;
121 stream->cdrom.cur_sec = sec;
122 stream->cdrom.cur_frame = frame;
123 stream->cdrom.cur_lba = cdrom_msf_to_lba(min, sec, frame);
127 "[CDROM] Seek %s: Path %s Offset %" PRIu64 " is now at %" PRIu64 " (MSF %02u:%02u:%02u) (LBA %u)...\n",
131 stream->cdrom.byte_pos,
132 (unsigned)stream->cdrom.cur_min,
133 (unsigned)stream->cdrom.cur_sec,
134 (unsigned)stream->cdrom.cur_frame,
135 stream->cdrom.cur_lba);
145 void retro_vfs_file_open_cdrom(
146 libretro_vfs_implementation_file *stream,
147 const char *path, unsigned mode, unsigned hints)
149 #if defined(__linux__) && !defined(ANDROID)
150 char cdrom_path[] = "/dev/sg1";
151 size_t path_len = strlen(path);
152 const char *ext = path_get_extension(path);
154 stream->cdrom.cur_track = 1;
156 if ( !string_is_equal_noncase(ext, "cue")
157 && !string_is_equal_noncase(ext, "bin"))
160 if (path_len >= STRLEN_CONST("drive1-track01.bin"))
162 if (!memcmp(path, "drive", STRLEN_CONST("drive")))
164 if (!memcmp(path + 6, "-track", STRLEN_CONST("-track")))
166 if (sscanf(path + 12, "%02u", (unsigned*)&stream->cdrom.cur_track))
169 printf("[CDROM] Opening track %d\n", stream->cdrom.cur_track);
177 if (path_len >= STRLEN_CONST("drive1.cue"))
179 if (!memcmp(path, "drive", STRLEN_CONST("drive")))
181 if (path[5] >= '0' && path[5] <= '9')
183 cdrom_path[7] = path[5];
184 stream->cdrom.drive = path[5];
185 vfs_cdrom_toc.drive = stream->cdrom.drive;
191 printf("[CDROM] Open: Path %s URI %s\n", cdrom_path, path);
194 stream->fp = (FILE*)fopen_utf8(cdrom_path, "r+b");
199 if (string_is_equal_noncase(ext, "cue"))
201 if (stream->cdrom.cue_buf)
203 free(stream->cdrom.cue_buf);
204 stream->cdrom.cue_buf = NULL;
207 cdrom_write_cue(stream,
208 &stream->cdrom.cue_buf,
209 &stream->cdrom.cue_len,
211 &vfs_cdrom_toc.num_tracks,
213 cdrom_get_timeouts(stream, &vfs_cdrom_toc.timeouts);
216 if (string_is_empty(stream->cdrom.cue_buf))
218 printf("[CDROM] Error writing cue sheet.\n");
223 printf("[CDROM] CUE Sheet:\n%s\n", stream->cdrom.cue_buf);
229 #if defined(_WIN32) && !defined(_XBOX)
230 char cdrom_path[] = "\\\\.\\D:";
231 size_t path_len = strlen(path);
232 const char *ext = path_get_extension(path);
234 if ( !string_is_equal_noncase(ext, "cue")
235 && !string_is_equal_noncase(ext, "bin"))
238 if (path_len >= STRLEN_CONST("d:/drive-track01.bin"))
240 if (!memcmp(path + 1, ":/drive-track", STRLEN_CONST(":/drive-track")))
242 if (sscanf(path + 14, "%02u", (unsigned*)&stream->cdrom.cur_track))
245 printf("[CDROM] Opening track %d\n", stream->cdrom.cur_track);
252 if (path_len >= STRLEN_CONST("d:/drive.cue"))
254 if (!memcmp(path + 1, ":/drive", STRLEN_CONST(":/drive")))
256 if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
258 cdrom_path[4] = path[0];
259 stream->cdrom.drive = path[0];
260 vfs_cdrom_toc.drive = stream->cdrom.drive;
266 printf("[CDROM] Open: Path %s URI %s\n", cdrom_path, path);
269 stream->fh = CreateFile(cdrom_path,
270 GENERIC_READ | GENERIC_WRITE,
271 FILE_SHARE_READ | FILE_SHARE_WRITE,
274 FILE_ATTRIBUTE_NORMAL,
277 if (stream->fh == INVALID_HANDLE_VALUE)
280 if (string_is_equal_noncase(ext, "cue"))
282 if (stream->cdrom.cue_buf)
284 free(stream->cdrom.cue_buf);
285 stream->cdrom.cue_buf = NULL;
288 cdrom_write_cue(stream,
289 &stream->cdrom.cue_buf,
290 &stream->cdrom.cue_len,
292 &vfs_cdrom_toc.num_tracks,
294 cdrom_get_timeouts(stream,
295 &vfs_cdrom_toc.timeouts);
298 if (string_is_empty(stream->cdrom.cue_buf))
300 printf("[CDROM] Error writing cue sheet.\n");
305 printf("[CDROM] CUE Sheet:\n%s\n", stream->cdrom.cue_buf);
311 if (vfs_cdrom_toc.num_tracks > 1 && stream->cdrom.cur_track)
313 stream->cdrom.cur_min = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].min;
314 stream->cdrom.cur_sec = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].sec;
315 stream->cdrom.cur_frame = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].frame;
316 stream->cdrom.cur_lba = cdrom_msf_to_lba(stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame);
320 stream->cdrom.cur_min = vfs_cdrom_toc.track[0].min;
321 stream->cdrom.cur_sec = vfs_cdrom_toc.track[0].sec;
322 stream->cdrom.cur_frame = vfs_cdrom_toc.track[0].frame;
323 stream->cdrom.cur_lba = cdrom_msf_to_lba(stream->cdrom.cur_min, stream->cdrom.cur_sec, stream->cdrom.cur_frame);
327 int retro_vfs_file_close_cdrom(libretro_vfs_implementation_file *stream)
330 printf("[CDROM] Close: Path %s\n", stream->orig_path);
334 #if defined(_WIN32) && !defined(_XBOX)
335 if (!stream->fh || !CloseHandle(stream->fh))
338 if (!stream->fp || fclose(stream->fp))
345 int64_t retro_vfs_file_tell_cdrom(libretro_vfs_implementation_file *stream)
347 const char *ext = NULL;
351 ext = path_get_extension(stream->orig_path);
353 if (string_is_equal_noncase(ext, "cue"))
356 printf("[CDROM] (cue) Tell: Path %s Position %" PRIu64 "\n", stream->orig_path, stream->cdrom.byte_pos);
359 return stream->cdrom.byte_pos;
361 else if (string_is_equal_noncase(ext, "bin"))
364 printf("[CDROM] (bin) Tell: Path %s Position %" PRId64 "\n", stream->orig_path, stream->cdrom.byte_pos);
367 return stream->cdrom.byte_pos;
373 int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream,
374 void *s, uint64_t len)
377 const char *ext = path_get_extension(stream->orig_path);
379 if (string_is_equal_noncase(ext, "cue"))
381 if ((int64_t)len >= (int64_t)stream->cdrom.cue_len
382 - stream->cdrom.byte_pos)
383 len = stream->cdrom.cue_len - stream->cdrom.byte_pos - 1;
386 "[CDROM] Read: Reading %" PRIu64 " bytes from cuesheet starting at %" PRIu64 "...\n",
388 stream->cdrom.byte_pos);
391 memcpy(s, stream->cdrom.cue_buf + stream->cdrom.byte_pos, len);
392 stream->cdrom.byte_pos += len;
396 else if (string_is_equal_noncase(ext, "bin"))
398 unsigned char min = 0;
399 unsigned char sec = 0;
400 unsigned char frame = 0;
401 unsigned char rmin = 0;
402 unsigned char rsec = 0;
403 unsigned char rframe = 0;
404 size_t skip = stream->cdrom.byte_pos % 2352;
406 if (stream->cdrom.byte_pos >=
407 vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes)
410 if (stream->cdrom.byte_pos + len >
411 vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes)
412 len -= (stream->cdrom.byte_pos + len)
413 - vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes;
415 cdrom_lba_to_msf(stream->cdrom.cur_lba, &min, &sec, &frame);
416 cdrom_lba_to_msf(stream->cdrom.cur_lba
417 - vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba,
418 &rmin, &rsec, &rframe);
422 "[CDROM] Read: Reading %" PRIu64 " bytes from %s starting at byte offset %" PRIu64 " (rMSF %02u:%02u:%02u aMSF %02u:%02u:%02u) (LBA %u) skip %" PRIu64 "...\n",
425 stream->cdrom.byte_pos,
432 stream->cdrom.cur_lba,
438 rv = cdrom_read(stream, &vfs_cdrom_toc.timeouts, min, sec,
439 frame, s, (size_t)len, skip);
441 rv = cdrom_read_lba(stream, stream->cdrom.cur_lba, s,
448 printf("[CDROM] Failed to read %" PRIu64 " bytes from CD.\n", len);
454 stream->cdrom.byte_pos += len;
455 stream->cdrom.cur_lba =
456 vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba
457 + (stream->cdrom.byte_pos / 2352);
459 cdrom_lba_to_msf(stream->cdrom.cur_lba,
460 &stream->cdrom.cur_min,
461 &stream->cdrom.cur_sec,
462 &stream->cdrom.cur_frame);
466 "[CDROM] read %" PRIu64 " bytes, position is now: %" PRIu64 " (MSF %02u:%02u:%02u) (LBA %u)\n",
468 stream->cdrom.byte_pos,
469 (unsigned)stream->cdrom.cur_min,
470 (unsigned)stream->cdrom.cur_sec,
471 (unsigned)stream->cdrom.cur_frame,
473 stream->cdrom.cur_min,
474 stream->cdrom.cur_sec,
475 stream->cdrom.cur_frame)
486 int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream)
491 const vfs_cdrom_t* retro_vfs_file_get_cdrom_position(
492 const libretro_vfs_implementation_file *stream)
494 return &stream->cdrom;