update libchdr
[pcsx_rearmed.git] / deps / libretro-common / vfs / vfs_implementation_cdrom.c
CommitLineData
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 */
34static cdrom_toc_t vfs_cdrom_toc = {0};
35
36const cdrom_toc_t* retro_vfs_file_get_cdrom_toc(void)
37{
38 return &vfs_cdrom_toc;
39}
40
41int64_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
145void 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
327int 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
345int64_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
373int64_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
486int retro_vfs_file_error_cdrom(libretro_vfs_implementation_file *stream)
487{
488 return 0;
489}
490
491const vfs_cdrom_t* retro_vfs_file_get_cdrom_position(
492 const libretro_vfs_implementation_file *stream)
493{
494 return &stream->cdrom;
495}