Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /* Copyright (C) 2010-2020 The RetroArch team |
2 | * | |
3 | * --------------------------------------------------------------------------------------- | |
4 | * The following license statement only applies to this file (vfs_implementation_cdrom.c). | |
5 | * --------------------------------------------------------------------------------------- | |
6 | * | |
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: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
14 | * | |
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. | |
21 | */ | |
22 | ||
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> | |
28 | ||
29 | #if defined(_WIN32) && !defined(_XBOX) | |
30 | #include <windows.h> | |
31 | #endif | |
32 | ||
33 | /* TODO/FIXME - static global variable */ | |
34 | static cdrom_toc_t vfs_cdrom_toc = {0}; | |
35 | ||
36 | const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void) | |
37 | { | |
38 | return &vfs_cdrom_toc; | |
39 | } | |
40 | ||
41 | int64_t retro_vfs_file_seek_cdrom( | |
42 | libretro_vfs_implementation_file *stream, | |
43 | int64_t offset, int whence) | |
44 | { | |
45 | const char *ext = path_get_extension(stream->orig_path); | |
46 | ||
47 | if (string_is_equal_noncase(ext, "cue")) | |
48 | { | |
49 | switch (whence) | |
50 | { | |
51 | case SEEK_SET: | |
52 | stream->cdrom.byte_pos = offset; | |
53 | break; | |
54 | case SEEK_CUR: | |
55 | stream->cdrom.byte_pos += offset; | |
56 | break; | |
57 | case SEEK_END: | |
58 | stream->cdrom.byte_pos = (stream->cdrom.cue_len - 1) + offset; | |
59 | break; | |
60 | } | |
61 | ||
62 | #ifdef CDROM_DEBUG | |
63 | printf("[CDROM] Seek: Path %s Offset %" PRIu64 " is now at %" PRIu64 "\n", | |
64 | stream->orig_path, | |
65 | offset, | |
66 | stream->cdrom.byte_pos); | |
67 | fflush(stdout); | |
68 | #endif | |
69 | } | |
70 | else if (string_is_equal_noncase(ext, "bin")) | |
71 | { | |
72 | int lba = (offset / 2352); | |
73 | unsigned char min = 0; | |
74 | unsigned char sec = 0; | |
75 | unsigned char frame = 0; | |
76 | #ifdef CDROM_DEBUG | |
77 | const char *seek_type = "SEEK_SET"; | |
78 | #endif | |
79 | ||
80 | switch (whence) | |
81 | { | |
82 | case SEEK_CUR: | |
83 | { | |
84 | unsigned new_lba; | |
85 | #ifdef CDROM_DEBUG | |
86 | seek_type = "SEEK_CUR"; | |
87 | #endif | |
88 | stream->cdrom.byte_pos += offset; | |
89 | new_lba = vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].lba + (stream->cdrom.byte_pos / 2352); | |
90 | ||
91 | cdrom_lba_to_msf(new_lba, &min, &sec, &frame); | |
92 | } | |
93 | break; | |
94 | case SEEK_END: | |
95 | { | |
96 | ssize_t pregap_lba_len = (vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].audio | |
97 | ? 0 | |
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; | |
100 | #ifdef CDROM_DEBUG | |
101 | seek_type = "SEEK_END"; | |
102 | #endif | |
103 | ||
104 | cdrom_lba_to_msf(lba_len + lba, &min, &sec, &frame); | |
105 | stream->cdrom.byte_pos = lba_len * 2352; | |
106 | } | |
107 | break; | |
108 | case SEEK_SET: | |
109 | default: | |
110 | { | |
111 | #ifdef CDROM_DEBUG | |
112 | seek_type = "SEEK_SET"; | |
113 | #endif | |
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); | |
116 | } | |
117 | break; | |
118 | } | |
119 | ||
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); | |
124 | ||
125 | #ifdef CDROM_DEBUG | |
126 | printf( | |
127 | "[CDROM] Seek %s: Path %s Offset %" PRIu64 " is now at %" PRIu64 " (MSF %02u:%02u:%02u) (LBA %u)...\n", | |
128 | seek_type, | |
129 | stream->orig_path, | |
130 | offset, | |
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); | |
136 | fflush(stdout); | |
137 | #endif | |
138 | } | |
139 | else | |
140 | return -1; | |
141 | ||
142 | return 0; | |
143 | } | |
144 | ||
145 | void retro_vfs_file_open_cdrom( | |
146 | libretro_vfs_implementation_file *stream, | |
147 | const char *path, unsigned mode, unsigned hints) | |
148 | { | |
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); | |
153 | ||
154 | stream->cdrom.cur_track = 1; | |
155 | ||
156 | if ( !string_is_equal_noncase(ext, "cue") | |
157 | && !string_is_equal_noncase(ext, "bin")) | |
158 | return; | |
159 | ||
160 | if (path_len >= STRLEN_CONST("drive1-track01.bin")) | |
161 | { | |
162 | if (!memcmp(path, "drive", STRLEN_CONST("drive"))) | |
163 | { | |
164 | if (!memcmp(path + 6, "-track", STRLEN_CONST("-track"))) | |
165 | { | |
166 | if (sscanf(path + 12, "%02u", (unsigned*)&stream->cdrom.cur_track)) | |
167 | { | |
168 | #ifdef CDROM_DEBUG | |
169 | printf("[CDROM] Opening track %d\n", stream->cdrom.cur_track); | |
170 | fflush(stdout); | |
171 | #endif | |
172 | } | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | if (path_len >= STRLEN_CONST("drive1.cue")) | |
178 | { | |
179 | if (!memcmp(path, "drive", STRLEN_CONST("drive"))) | |
180 | { | |
181 | if (path[5] >= '0' && path[5] <= '9') | |
182 | { | |
183 | cdrom_path[7] = path[5]; | |
184 | stream->cdrom.drive = path[5]; | |
185 | vfs_cdrom_toc.drive = stream->cdrom.drive; | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | #ifdef CDROM_DEBUG | |
191 | printf("[CDROM] Open: Path %s URI %s\n", cdrom_path, path); | |
192 | fflush(stdout); | |
193 | #endif | |
194 | stream->fp = (FILE*)fopen_utf8(cdrom_path, "r+b"); | |
195 | ||
196 | if (!stream->fp) | |
197 | return; | |
198 | ||
199 | if (string_is_equal_noncase(ext, "cue")) | |
200 | { | |
201 | if (stream->cdrom.cue_buf) | |
202 | { | |
203 | free(stream->cdrom.cue_buf); | |
204 | stream->cdrom.cue_buf = NULL; | |
205 | } | |
206 | ||
207 | cdrom_write_cue(stream, | |
208 | &stream->cdrom.cue_buf, | |
209 | &stream->cdrom.cue_len, | |
210 | stream->cdrom.drive, | |
211 | &vfs_cdrom_toc.num_tracks, | |
212 | &vfs_cdrom_toc); | |
213 | cdrom_get_timeouts(stream, &vfs_cdrom_toc.timeouts); | |
214 | ||
215 | #ifdef CDROM_DEBUG | |
216 | if (string_is_empty(stream->cdrom.cue_buf)) | |
217 | { | |
218 | printf("[CDROM] Error writing cue sheet.\n"); | |
219 | fflush(stdout); | |
220 | } | |
221 | else | |
222 | { | |
223 | printf("[CDROM] CUE Sheet:\n%s\n", stream->cdrom.cue_buf); | |
224 | fflush(stdout); | |
225 | } | |
226 | #endif | |
227 | } | |
228 | #endif | |
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); | |
233 | ||
234 | if ( !string_is_equal_noncase(ext, "cue") | |
235 | && !string_is_equal_noncase(ext, "bin")) | |
236 | return; | |
237 | ||
238 | if (path_len >= STRLEN_CONST("d:/drive-track01.bin")) | |
239 | { | |
240 | if (!memcmp(path + 1, ":/drive-track", STRLEN_CONST(":/drive-track"))) | |
241 | { | |
242 | if (sscanf(path + 14, "%02u", (unsigned*)&stream->cdrom.cur_track)) | |
243 | { | |
244 | #ifdef CDROM_DEBUG | |
245 | printf("[CDROM] Opening track %d\n", stream->cdrom.cur_track); | |
246 | fflush(stdout); | |
247 | #endif | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
252 | if (path_len >= STRLEN_CONST("d:/drive.cue")) | |
253 | { | |
254 | if (!memcmp(path + 1, ":/drive", STRLEN_CONST(":/drive"))) | |
255 | { | |
256 | if ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')) | |
257 | { | |
258 | cdrom_path[4] = path[0]; | |
259 | stream->cdrom.drive = path[0]; | |
260 | vfs_cdrom_toc.drive = stream->cdrom.drive; | |
261 | } | |
262 | } | |
263 | } | |
264 | ||
265 | #ifdef CDROM_DEBUG | |
266 | printf("[CDROM] Open: Path %s URI %s\n", cdrom_path, path); | |
267 | fflush(stdout); | |
268 | #endif | |
269 | stream->fh = CreateFile(cdrom_path, | |
270 | GENERIC_READ | GENERIC_WRITE, | |
271 | FILE_SHARE_READ | FILE_SHARE_WRITE, | |
272 | NULL, | |
273 | OPEN_EXISTING, | |
274 | FILE_ATTRIBUTE_NORMAL, | |
275 | NULL); | |
276 | ||
277 | if (stream->fh == INVALID_HANDLE_VALUE) | |
278 | return; | |
279 | ||
280 | if (string_is_equal_noncase(ext, "cue")) | |
281 | { | |
282 | if (stream->cdrom.cue_buf) | |
283 | { | |
284 | free(stream->cdrom.cue_buf); | |
285 | stream->cdrom.cue_buf = NULL; | |
286 | } | |
287 | ||
288 | cdrom_write_cue(stream, | |
289 | &stream->cdrom.cue_buf, | |
290 | &stream->cdrom.cue_len, | |
291 | stream->cdrom.drive, | |
292 | &vfs_cdrom_toc.num_tracks, | |
293 | &vfs_cdrom_toc); | |
294 | cdrom_get_timeouts(stream, | |
295 | &vfs_cdrom_toc.timeouts); | |
296 | ||
297 | #ifdef CDROM_DEBUG | |
298 | if (string_is_empty(stream->cdrom.cue_buf)) | |
299 | { | |
300 | printf("[CDROM] Error writing cue sheet.\n"); | |
301 | fflush(stdout); | |
302 | } | |
303 | else | |
304 | { | |
305 | printf("[CDROM] CUE Sheet:\n%s\n", stream->cdrom.cue_buf); | |
306 | fflush(stdout); | |
307 | } | |
308 | #endif | |
309 | } | |
310 | #endif | |
311 | if (vfs_cdrom_toc.num_tracks > 1 && stream->cdrom.cur_track) | |
312 | { | |
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); | |
317 | } | |
318 | else | |
319 | { | |
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); | |
324 | } | |
325 | } | |
326 | ||
327 | int retro_vfs_file_close_cdrom(libretro_vfs_implementation_file *stream) | |
328 | { | |
329 | #ifdef CDROM_DEBUG | |
330 | printf("[CDROM] Close: Path %s\n", stream->orig_path); | |
331 | fflush(stdout); | |
332 | #endif | |
333 | ||
334 | #if defined(_WIN32) && !defined(_XBOX) | |
335 | if (!stream->fh || !CloseHandle(stream->fh)) | |
336 | return -1; | |
337 | #else | |
338 | if (!stream->fp || fclose(stream->fp)) | |
339 | return -1; | |
340 | #endif | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
345 | int64_t retro_vfs_file_tell_cdrom(libretro_vfs_implementation_file *stream) | |
346 | { | |
347 | const char *ext = NULL; | |
348 | if (!stream) | |
349 | return -1; | |
350 | ||
351 | ext = path_get_extension(stream->orig_path); | |
352 | ||
353 | if (string_is_equal_noncase(ext, "cue")) | |
354 | { | |
355 | #ifdef CDROM_DEBUG | |
356 | printf("[CDROM] (cue) Tell: Path %s Position %" PRIu64 "\n", stream->orig_path, stream->cdrom.byte_pos); | |
357 | fflush(stdout); | |
358 | #endif | |
359 | return stream->cdrom.byte_pos; | |
360 | } | |
361 | else if (string_is_equal_noncase(ext, "bin")) | |
362 | { | |
363 | #ifdef CDROM_DEBUG | |
364 | printf("[CDROM] (bin) Tell: Path %s Position %" PRId64 "\n", stream->orig_path, stream->cdrom.byte_pos); | |
365 | fflush(stdout); | |
366 | #endif | |
367 | return stream->cdrom.byte_pos; | |
368 | } | |
369 | ||
370 | return -1; | |
371 | } | |
372 | ||
373 | int64_t retro_vfs_file_read_cdrom(libretro_vfs_implementation_file *stream, | |
374 | void *s, uint64_t len) | |
375 | { | |
376 | int rv; | |
377 | const char *ext = path_get_extension(stream->orig_path); | |
378 | ||
379 | if (string_is_equal_noncase(ext, "cue")) | |
380 | { | |
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; | |
384 | #ifdef CDROM_DEBUG | |
385 | printf( | |
386 | "[CDROM] Read: Reading %" PRIu64 " bytes from cuesheet starting at %" PRIu64 "...\n", | |
387 | len, | |
388 | stream->cdrom.byte_pos); | |
389 | fflush(stdout); | |
390 | #endif | |
391 | memcpy(s, stream->cdrom.cue_buf + stream->cdrom.byte_pos, len); | |
392 | stream->cdrom.byte_pos += len; | |
393 | ||
394 | return len; | |
395 | } | |
396 | else if (string_is_equal_noncase(ext, "bin")) | |
397 | { | |
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; | |
405 | ||
406 | if (stream->cdrom.byte_pos >= | |
407 | vfs_cdrom_toc.track[stream->cdrom.cur_track - 1].track_bytes) | |
408 | return 0; | |
409 | ||
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; | |
414 | ||
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); | |
419 | ||
420 | #ifdef CDROM_DEBUG | |
421 | printf( | |
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", | |
423 | len, | |
424 | stream->orig_path, | |
425 | stream->cdrom.byte_pos, | |
426 | (unsigned)rmin, | |
427 | (unsigned)rsec, | |
428 | (unsigned)rframe, | |
429 | (unsigned)min, | |
430 | (unsigned)sec, | |
431 | (unsigned)frame, | |
432 | stream->cdrom.cur_lba, | |
433 | skip); | |
434 | fflush(stdout); | |
435 | #endif | |
436 | ||
437 | #if 1 | |
438 | rv = cdrom_read(stream, &vfs_cdrom_toc.timeouts, min, sec, | |
439 | frame, s, (size_t)len, skip); | |
440 | #else | |
441 | rv = cdrom_read_lba(stream, stream->cdrom.cur_lba, s, | |
442 | (size_t)len, skip); | |
443 | #endif | |
444 | ||
445 | if (rv) | |
446 | { | |
447 | #ifdef CDROM_DEBUG | |
448 | printf("[CDROM] Failed to read %" PRIu64 " bytes from CD.\n", len); | |
449 | fflush(stdout); | |
450 | #endif | |
451 | return 0; | |
452 | } | |
453 | ||
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); | |
458 | ||
459 | cdrom_lba_to_msf(stream->cdrom.cur_lba, | |
460 | &stream->cdrom.cur_min, | |
461 | &stream->cdrom.cur_sec, | |
462 | &stream->cdrom.cur_frame); | |
463 | ||
464 | #ifdef CDROM_DEBUG | |
465 | printf( | |
466 | "[CDROM] read %" PRIu64 " bytes, position is now: %" PRIu64 " (MSF %02u:%02u:%02u) (LBA %u)\n", | |
467 | len, | |
468 | stream->cdrom.byte_pos, | |
469 | (unsigned)stream->cdrom.cur_min, | |
470 | (unsigned)stream->cdrom.cur_sec, | |
471 | (unsigned)stream->cdrom.cur_frame, | |
472 | cdrom_msf_to_lba( | |
473 | stream->cdrom.cur_min, | |
474 | stream->cdrom.cur_sec, | |
475 | stream->cdrom.cur_frame) | |
476 | ); | |
477 | fflush(stdout); | |
478 | #endif | |
479 | ||
480 | return len; | |
481 | } | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
486 | int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream) | |
487 | { | |
488 | return 0; | |
489 | } | |
490 | ||
491 | const vfs_cdrom_t* retro_vfs_file_get_cdrom_position( | |
492 | const libretro_vfs_implementation_file *stream) | |
493 | { | |
494 | return &stream->cdrom; | |
495 | } |