2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
10 #include "scsi_cdrom.h"
14 #define CDDA_PLAYING 1
16 //#define _SCSI_DEBUG_LOG
17 #define _CDROM_DEBUG_LOG
18 // 0-99 is reserved for SCSI_DEV class
19 #define EVENT_CDDA 100
21 void SCSI_CDROM::initialize()
23 SCSI_DEV::initialize();
24 fio_img = new FILEIO();
26 if(44100 % emu->get_sound_rate() == 0) {
27 mix_loop_num = 44100 / emu->get_sound_rate();
32 cdda_status = CDDA_OFF;
35 for(int i = 0; i < 99; i++) {
36 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
40 void SCSI_CDROM::release()
42 if(fio_img->IsOpened()) {
49 void SCSI_CDROM::reset()
53 set_cdda_status(CDDA_OFF);
54 // Q: Does not seek to track 0? 20181118 K.O
58 uint32_t SCSI_CDROM::read_signal(int id)
61 case SIG_SCSI_CDROM_PLAYING:
62 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
64 case SIG_SCSI_CDROM_SAMPLE_L:
65 return (uint32_t)abs(cdda_sample_l);
67 case SIG_SCSI_CDROM_SAMPLE_R:
68 return (uint32_t)abs(cdda_sample_r);
70 return SCSI_DEV::read_signal(id);
73 void SCSI_CDROM::event_callback(int event_id, int err)
77 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
78 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
79 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
81 if((cdda_buffer_ptr += 4) % 2352 == 0) {
83 if(++cdda_playing_frame == cdda_end_frame) {
84 // reached to end frame
87 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
88 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
90 cdda_playing_frame = cdda_start_frame;
95 write_signals(&outputs_done, 0xffffffff);
97 set_cdda_status(CDDA_OFF);
99 } else if(cdda_buffer_ptr == array_length(cdda_buffer)) {
101 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
108 SCSI_DEV::event_callback(event_id, err);
113 void SCSI_CDROM::set_cdda_status(uint8_t status)
115 if(status == CDDA_PLAYING) {
116 if(mix_loop_num == 0) {
117 if(event_cdda == -1) {
118 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
121 if(cdda_status != CDDA_PLAYING) {
123 set_realtime_render(this, true);
126 //if(status == CDDA_OFF) {
127 // if((status != cdda_status) && (is_cue)) {
128 // if(fio_img->IsOpened()) fio_img->Fclose();
131 if(event_cdda != -1) {
132 cancel_event(this, event_cdda);
135 if(cdda_status == CDDA_PLAYING) {
137 set_realtime_render(this, false);
140 cdda_status = status;
143 void SCSI_CDROM::reset_device()
145 set_cdda_status(CDDA_OFF);
146 SCSI_DEV::reset_device();
149 bool SCSI_CDROM::is_device_ready()
154 int SCSI_CDROM::get_command_length(int value)
164 return SCSI_DEV::get_command_length(value);
167 void SCSI_CDROM::get_track_by_track_num(int track)
169 if((track <= 0) || (track > track_num)) {
171 if(fio_img->IsOpened()) fio_img->Fclose();
173 if(track <= 0) current_track = 0;
174 if(track >= track_num)current_track = track_num;
178 // ToDo: Apply audio with some codecs.
179 if(current_track != track) {
180 if(fio_img != NULL) {
181 if(fio_img->IsOpened()) {
184 current_track = track;
186 #ifdef _CDROM_DEBUG_LOG
187 this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
190 if((track > 0) && (track < 100) && (track <= track_num)) {
191 if((strlen(track_data_path[track - 1]) <= 0) ||
192 !(fio_img->Fopen(track_data_path[track - 1], FILEIO_READ_BINARY))) {
202 int SCSI_CDROM::get_track(uint32_t lba)
207 for(int i = 1; i <= track_num; i++) {
208 if(lba >= toc_table[i - 1].lba_offset) {
215 for(int i = 0; i < track_num; i++) {
216 if(lba >= toc_table[i].index0) {
224 get_track_by_track_num(track);
226 current_track = track;
231 double SCSI_CDROM::get_seek_time(uint32_t lba)
233 if(fio_img->IsOpened()) {
234 uint32_t cur_position = (int)fio_img->Ftell();
235 int distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
236 double ratio = (double)distance / 333000 / physical_block_size(); // 333000: sectors in media
237 return max(10, (int)(400000 * 2 * ratio));
239 return 400000; // 400msec
243 uint32_t SCSI_CDROM::lba_to_msf(int trk, uint32_t lba)
247 lba = lba + toc_table[trk - 1].lba_offset;
250 uint8_t m = lba / (60 * 75);
251 lba -= m * (60 * 75);
252 uint8_t s = lba / 75;
253 uint8_t f = lba % 75;
255 return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
258 uint32_t SCSI_CDROM::lba_to_msf_alt(int trk, uint32_t lba)
262 lba = lba + toc_table[trk - 1].lba_offset;
266 ret |= ((lba / (60 * 75)) & 0xff) << 16;
267 ret |= (((lba / 75) % 60) & 0xff) << 8;
268 ret |= ((lba % 75) & 0xff) << 0;
272 void SCSI_CDROM::start_command()
275 #ifdef _SCSI_DEBUG_LOG
276 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: #%02x %02x %02x %02x %02x %02x\n"), scsi_id, command[0], command[1], command[2], command[3], command[4], command[5]);
280 seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
281 set_cdda_status(CDDA_OFF);
284 case SCSI_CMD_READ10:
285 case SCSI_CMD_READ12:
286 seek_time = 10;//get_seek_time(command[2] * 0x1000000 + command[3] * 0x10000 + command[4] * 0x100 + command[5]);
287 set_cdda_status(CDDA_OFF);
291 #ifdef _SCSI_DEBUG_LOG
292 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback Start Position\n"), scsi_id);
294 if(is_device_ready()) {
295 if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
296 // stop cd-da if all params are zero
297 cdda_start_frame = 0;
298 cdda_end_frame = toc_table[track_num].index0; // end of disc
300 get_track_by_track_num(track_num);
302 set_cdda_status(CDDA_OFF);
304 uint32_t seek_offset = 0;
305 switch(command[9] & 0xc0) {
307 cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
311 uint8_t m = FROM_BCD(command[2]);
312 uint8_t s = FROM_BCD(command[3]);
313 uint8_t f = FROM_BCD(command[4]);
314 cdda_start_frame = f + 75 * (s + m * 60);
316 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
317 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
318 int track =get_track(cdda_start_frame);
319 if((is_cue) && (track > 1)) {
320 seek_offset = toc_table[current_track - 1].lba_offset;
321 if(cdda_start_frame >= toc_table[track - 1].lba_offset) {
322 cdda_start_frame = cdda_start_frame - toc_table[track - 1].lba_offset;
324 cdda_start_frame = 0;
327 cdda_start_frame -= toc_table[track].pregap;
329 if(cdda_start_frame < toc_table[track].index1) {
330 cdda_start_frame = toc_table[track].index1; // don't play pregap
331 } else if(cdda_start_frame > max_logical_block) {
332 cdda_start_frame = 0;
338 int trk = FROM_BCD(command[2]) - 1;
339 seek_offset = toc_table[current_track - 1].lba_offset;
340 if(trk != current_track) {
341 get_track_by_track_num(trk);
344 cdda_start_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
347 cdda_start_frame = 0;
350 // if(cdda_status == CDDA_PAUSED) {
351 // cdda_end_frame = toc_table[track_num].index0; // end of disc
352 // set_cdda_status(CDDA_OFF);
354 if((command[1] & 3) != 0) {
355 if((is_cue) && (current_track != track_num)){
356 seek_offset = toc_table[current_track - 1].lba_offset;
357 get_track(toc_table[track_num].lba_offset + 1);
359 cdda_end_frame = toc_table[track_num].index0; // end of disc
360 set_cdda_status(CDDA_PLAYING);
362 uint32_t _sframe = cdda_start_frame;
364 if((is_cue) && (current_track > 0)) {
365 seek_offset = toc_table[current_track - 1].lba_offset;
366 _sframe = _sframe + toc_table[current_track - 1].lba_offset;
368 cdda_end_frame = toc_table[get_track(_sframe) + 1].index1; // end of this track
369 set_cdda_status(CDDA_PAUSED);
372 cdda_interrupt = ((command[1] & 3) == 2);
375 double seek_time = get_seek_time(cdda_start_frame);
376 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
377 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
379 cdda_playing_frame = cdda_start_frame;
382 // change to status phase
383 set_dat(SCSI_STATUS_GOOD);
384 set_phase_delay(SCSI_PHASE_STATUS, seek_time);
388 // change to status phase
389 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
390 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
394 #ifdef _SCSI_DEBUG_LOG
395 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback End Position\n"), scsi_id);
397 if(is_device_ready()) {
398 switch(command[9] & 0xc0) {
400 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
404 uint8_t m = FROM_BCD(command[2]);
405 uint8_t s = FROM_BCD(command[3]);
406 uint8_t f = FROM_BCD(command[4]);
407 cdda_end_frame = f + 75 * (s + m * 60);
409 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
410 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
411 int track =get_track(cdda_end_frame);
412 if((is_cue) && (track > 1)) {
413 if(cdda_end_frame >= toc_table[track - 1].lba_offset) {
414 cdda_end_frame = cdda_end_frame - toc_table[track - 1].lba_offset;
420 if(cdda_end_frame > toc_table[track].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track].index1) {
421 cdda_end_frame = toc_table[track].index1;
426 cdda_end_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
429 if((command[1] & 3) != 0) {
430 set_cdda_status(CDDA_PLAYING);
431 cdda_repeat = ((command[1] & 3) == 1);
432 cdda_interrupt = ((command[1] & 3) == 2);
434 set_cdda_status(CDDA_OFF);
437 // change to status phase
438 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
439 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
443 #ifdef _SCSI_DEBUG_LOG
444 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
446 if(is_device_ready()) {
447 if(cdda_status == CDDA_PLAYING) {
448 set_cdda_status(CDDA_OFF);
451 if(is_cue) { // NEC PC-ENGINE ONLY.This expect to be temporally workaround.20181119 K.O
452 get_track_by_track_num(2);
454 // change to status phase
455 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
456 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
460 #ifdef _SCSI_DEBUG_LOG
461 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
463 if(is_device_ready()) {
465 uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
466 uint32_t msf_abs = lba_to_msf_alt(current_track, frame);
469 //track = get_track(frame + ((current_track > 0) ? toc_table[current_track - 1].lba_offset : 0));
470 track = current_track;
472 track = get_track(frame);
474 uint32_t msf_rel = lba_to_msf_alt(track, frame - toc_table[track].index0);
476 buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
477 buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
478 buffer->write(TO_BCD(track + 1)); // Track
479 buffer->write(0x01); // Index
480 buffer->write(TO_BCD((msf_rel >> 16) & 0xff)); // M (relative)
481 buffer->write(TO_BCD((msf_rel >> 8) & 0xff)); // S (relative)
482 buffer->write(TO_BCD((msf_rel >> 0) & 0xff)); // F (relative)
483 buffer->write(TO_BCD((msf_abs >> 16) & 0xff)); // M (absolute)
484 buffer->write(TO_BCD((msf_abs >> 8) & 0xff)); // S (absolute)
485 buffer->write(TO_BCD((msf_abs >> 0) & 0xff)); // F (absolute)
487 remain = buffer->count();
489 set_dat(buffer->read());
490 // change to data in phase
491 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
493 // change to status phase
494 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
495 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
500 #ifdef _SCSI_DEBUG_LOG
501 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
503 if(is_device_ready()) {
505 #ifdef _SCSI_DEBUG_LOG
506 this->out_debug_log(_T("[SCSI_DEV:ID=%d] CMD=%02x ARG=%02x \n"), scsi_id, command[1], command[2]);
509 case 0x00: /* Get first and last track numbers */
510 buffer->write(TO_BCD(1));
511 buffer->write(TO_BCD(track_num));
513 case 0x01: /* Get total disk size in MSF format */
515 uint32_t msf = lba_to_msf(track_num, toc_table[track_num].index0 + 150);
516 buffer->write((msf >> 16) & 0xff);
517 buffer->write((msf >> 8) & 0xff);
518 buffer->write((msf >> 0) & 0xff);
521 case 0x02: /* Get track information */
522 if(command[2] == 0xaa) {
523 uint32_t msf = lba_to_msf(track_num, toc_table[track_num].index0 + 150);
524 buffer->write((msf >> 16) & 0xff);
525 buffer->write((msf >> 8) & 0xff);
526 buffer->write((msf >> 0) & 0xff);
527 buffer->write(0x04); // correct ?
529 int track = max(FROM_BCD(command[2]), 1);
530 uint32_t frame = toc_table[track - 1].index0;
531 // PCE wants the start sector for data tracks to *not* include the pregap
532 if(!toc_table[track - 1].is_audio) {
533 frame += toc_table[track - 1].pregap;
535 get_track_by_track_num(track);
537 uint32_t msf = lba_to_msf(track - 1, toc_table[track - 1].index1 + 150);
538 buffer->write((msf >> 16) & 0xff); // M
539 buffer->write((msf >> 8) & 0xff); // S
540 buffer->write((msf >> 0) & 0xff); // F
541 buffer->write(toc_table[track - 1].is_audio ? 0x00 : 0x04);
546 remain = buffer->count();
548 set_dat(buffer->read());
549 // change to data in phase
550 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
552 // change to status phase
553 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
554 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
559 // start standard command
560 SCSI_DEV::start_command();
563 bool SCSI_CDROM::read_buffer(int length)
565 if(!fio_img->IsOpened()) {
566 set_sense_code(SCSI_SENSE_NOTREADY);
569 uint32_t offset = (uint32_t)(position % 2352);
571 if(fio_img->Fseek((long)position, FILEIO_SEEK_SET) != 0) {
572 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
576 uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
577 int tmp_length = min(length, (int)sizeof(tmp_buffer));
579 if(fio_img->Fread(tmp_buffer, tmp_length, 1) != 1) {
580 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
583 for(int i = 0; i < tmp_length && length > 0; i++) {
584 if(offset >= 16 && offset < 16 + 2048) {
585 int value = tmp_buffer[i];
586 buffer->write(value);
590 offset = (offset + 1) % 2352;
594 set_sense_code(SCSI_SENSE_NOSENSE);
598 int get_frames_from_msf(const char *string)
600 const char *ptr = string;
605 if(*ptr >= '0' && *ptr <= '9') {
606 frames[index] = frames[index] * 10 + (*ptr - '0');
607 } else if(*ptr == ':') {
612 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
618 return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
621 int hexatoi(const char *string)
623 const char *ptr = string;
627 if(*ptr >= '0' && *ptr <= '9') {
628 value = value * 16 + (*ptr - '0');
629 } else if(*ptr >= 'a' && *ptr <= 'f') {
630 value = value * 16 + (*ptr - 'a' + 10);
631 } else if(*ptr >= 'A' && *ptr <= 'F') {
632 value = value * 16 + (*ptr - 'A' + 10);
633 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
641 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
644 _TCHAR line_buf[2048];
645 _TCHAR line_buf_shadow[2048];
646 _TCHAR full_path_cue[_MAX_PATH];
647 _TCHAR image_tmp_data_path[_MAX_PATH];
651 int nr_current_track = 0;
652 FILEIO* fio = new FILEIO();
653 if(fio == NULL) return false;
655 memset(full_path_cue, 0x00, sizeof(full_path_cue));
656 memset(image_tmp_data_path, 0x00, sizeof(image_tmp_data_path));
658 get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
660 _TCHAR *parent_dir = get_parent_dir(full_path_cue);
661 if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
662 memset(line_buf, 0x00, sizeof(line_buf));
664 for(int i = 0; i < 100; i++) {
665 memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
671 memset(line_buf, 0x00, sizeof(line_buf));
676 if((_c == '\n') || (_c == EOF) || (_np >= 2048)) break;
677 if(_c != '\r') line_buf[_np++] = (_TCHAR)_c;
679 if(_c == EOF) is_eof = true;
680 slen = strlen(line_buf);
681 if(slen <= 0) goto _n_continue;
682 // Trim head of Space or TAB
685 while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
687 if(ptr >= slen) break;
692 memset(line_buf_shadow, 0x00, sizeof(line_buf_shadow));
693 while((line_buf[ptr] != ' ') && (line_buf[ptr] != '\0') && (line_buf[ptr] != '\t')) {
694 line_buf_shadow[sptr++] = (_TCHAR)(toupper(line_buf[ptr++]));
695 if(ptr >= slen) break;
697 if((ptr > slen) || (sptr == 0)) {
701 if(strcmp(line_buf_shadow, "REM") == 0) {
704 } else if(strcmp(line_buf_shadow, "FILE") == 0) {
705 while((line_buf[ptr] != '"') && (line_buf[ptr] != '\0')) {
707 if(ptr > slen) break;
714 if(ptr >= slen) goto _n_continue;
716 _TCHAR _tmps[_MAX_PATH];
717 memset(_tmps, 0x00, sizeof(_tmps));
718 while((line_buf[ptr] != '"') && (line_buf[ptr] != '\0')) {
719 _tmps[sptr++] = line_buf[ptr++];
720 if(ptr >= slen) break;
725 if((sptr != 0) && (line_buf[ptr] == '"')) {
726 strncpy(image_tmp_data_path, parent_dir, _MAX_PATH);
727 strncat(image_tmp_data_path, _tmps, _MAX_PATH - 1);
728 #ifdef _SCSI_DEBUG_LOG
729 this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path);
733 goto _n_continue; // ToDo: Check ARG2 (BINARY etc..) 20181118 K.O
734 } else if(strcmp(line_buf_shadow, "TRACK") == 0) {
735 while((line_buf[ptr] == ' ') || (line_buf[ptr] == '\t')) {
737 if(ptr >= slen) break;
739 if(ptr > slen) goto _n_continue;
743 memset(_tmps, 0x00, sizeof(_tmps));
744 while((line_buf[ptr] != ' ') && (line_buf[ptr] != '\t') && (line_buf[ptr] != '\0')) {
745 if(ptr >= slen) break;
746 _tmps[_n++] = line_buf[ptr++];
748 _nr_num = atoi(_tmps);
751 if((_nr_num > 0) && (_nr_num < 100)) {
752 nr_current_track = _nr_num;
753 if(strlen(image_tmp_data_path) > 0) {
754 strncpy(track_data_path[_nr_num - 1], image_tmp_data_path, _MAX_PATH);
756 while((line_buf[ptr] == ' ') || (line_buf[ptr] == '\t')) {
758 if(ptr >= slen) break;
760 memset(_tmps, 0x00, sizeof(_tmps));
761 for(int i = 0; i < 127; i++) {
762 _tmps[i] = (_TCHAR)(toupper(line_buf[ptr++]));
763 if(ptr > slen) break;
766 toc_table[nr_current_track - 1].is_audio = false;
767 toc_table[nr_current_track - 1].index0 = 0;
768 toc_table[nr_current_track - 1].index1 = 0;
769 toc_table[nr_current_track - 1].pregap = 0;
771 if(strcmp(_tmps, "AUDIO") == 0) {
772 toc_table[nr_current_track - 1].is_audio = true;
773 } else if(strcmp(_tmps, "MODE1/2352") == 0) {
774 toc_table[nr_current_track - 1].is_audio = false;
776 // ToDo: Another data mode 20181118 K.Ohta
777 memset(track_data_path[_nr_num - 1], 0x00, _MAX_PATH * sizeof(_TCHAR));
779 if(track_num < _nr_num) track_num = _nr_num;
782 // ToDo: 20181118 K.Ohta
783 nr_current_track = 0;
786 } else if(strcmp(line_buf_shadow, "INDEX") == 0) {
788 if((nr_current_track > 0) && (nr_current_track < 100)) {
789 while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
791 if(ptr >= slen) break;
796 memset(_tmps, 0x00, sizeof(_tmps));
797 for(int i = 0; i < 31; i++) {
798 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) break;
799 _tmps[i] = line_buf[ptr++];
800 if(ptr > slen) break;
802 index_type = atoi(_tmps);
803 if(ptr >= slen) goto _n_continue;
806 while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
808 if(ptr > slen) break;
810 memset(_tmps, 0x00, sizeof(_tmps));
811 for(int i = 0; i < 31; i++) {
812 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) {
813 if(line_buf[ptr] != ':') break;
815 _tmps[i] = line_buf[ptr++];
816 if(ptr > slen) break;
821 toc_table[nr_current_track - 1].index0 = get_frames_from_msf(_tmps);
824 toc_table[nr_current_track - 1].index1 = get_frames_from_msf(_tmps);
833 } else if(strcmp(line_buf_shadow, "PREGAP") == 0) {
834 if((nr_current_track > 0) && (nr_current_track < 100)) {
835 while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
837 if(ptr >= slen) break;
841 memset(_tmps, 0x00, sizeof(_tmps));
842 for(int i = 0; i < 31; i++) {
843 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) {
844 if(line_buf[ptr] != ':') break;
846 _tmps[i] = line_buf[ptr++];
847 if(ptr >= slen) goto _n_continue;
849 toc_table[nr_current_track - 1].pregap = get_frames_from_msf(_tmps);
857 memset(line_buf, 0x00, sizeof(line_buf));
862 toc_table[0].lba_offset = 0;
863 for(int i = 1; i < track_num; i++) {
865 if(toc_table[i].index0 == 0) {
866 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
867 } else if(toc_table[i].pregap == 0) {
868 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
870 if(fio_img->IsOpened()) {
873 // ToDo: Support unreguler (!CDDA, i.e. MP3/WAV...) codec.20181118 K.O
875 if(strlen(track_data_path[i - 1]) > 0) {
876 if(fio_img->Fopen(track_data_path[i - 1], FILEIO_READ_BINARY)) {
877 if((_n = fio_img->FileLength() / 2352) > 0) {
878 max_logical_block += _n;
882 toc_table[i].lba_offset = toc_table[i - 1].lba_offset + _n;
883 #ifdef _CDROM_DEBUG_LOG
884 this->out_debug_log(_T("TRACK#%02d TYPE=%s PREGAP=%d INDEX0=%d INDEX1=%d LBA_SIZE=%d LBA_OFFSET=%d PATH=%s\n"),
885 i, (toc_table[i].is_audio) ? _T("AUDIO") : _T("MODE1/2352"),
886 toc_table[i].pregap, toc_table[i].index0, toc_table[i].index1,
887 _n, toc_table[i].lba_offset, track_data_path[i - 1]);
890 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
891 //toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
892 toc_table[track_num].lba_offset = max_logical_block;
899 if(track_num > 0) is_cue = true;
904 void SCSI_CDROM::open(const _TCHAR* file_path)
906 _TCHAR img_file_path[_MAX_PATH];
910 // ToDo: Process multi track cue file.Most of CDROMs contain both audio and data and more than 2 tracks.
912 if(check_file_extension(file_path, _T(".cue"))) {
916 open_cue_file(file_path);
918 // get image file name
919 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
920 if(!FILEIO::IsFileExisting(img_file_path)) {
921 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
922 if(!FILEIO::IsFileExisting(img_file_path)) {
923 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
924 if(!FILEIO::IsFileExisting(img_file_path)) {
925 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
926 if(!FILEIO::IsFileExisting(img_file_path)) {
927 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin.gz"), get_file_path_without_extensiton(file_path));
932 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
933 // get image file size
934 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
936 FILEIO* fio = new FILEIO();
937 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
938 char line[1024], *ptr;
940 while(fio->Fgets(line, 1024) != NULL) {
941 if(strstr(line, "FILE") != NULL) {
943 } else if((ptr = strstr(line, "TRACK")) != NULL) {
945 // "TRACK 02 MODE1/2352"
947 while(*ptr == ' ' || *ptr == 0x09) {
950 if((track = atoi(ptr)) > 0) {
951 if(track > track_num) {
954 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
956 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
959 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
961 } else if((ptr = strstr(line, "INDEX")) != NULL) {
962 // "INDEX 01 00:00:00"
965 while(*ptr == ' ' || *ptr == 0x09) {
969 while(*ptr >= '0' && *ptr <= '9') {
973 toc_table[track - 1].index0 = get_frames_from_msf(ptr);
974 } else if(num == 1) {
975 toc_table[track - 1].index1 = get_frames_from_msf(ptr);
981 for(int i = 1; i < track_num; i++) {
982 if(toc_table[i].index0 == 0) {
983 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
984 } else if(toc_table[i].pregap == 0) {
985 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
988 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
989 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
999 } else if(check_file_extension(file_path, _T(".ccd"))) {
1000 // get image file name
1001 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1002 if(!FILEIO::IsFileExisting(img_file_path)) {
1003 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1004 if(!FILEIO::IsFileExisting(img_file_path)) {
1005 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1008 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1011 // get image file size
1012 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1014 FILEIO* fio = new FILEIO();
1015 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1016 char line[1024], *ptr;
1018 while(fio->Fgets(line, 1024) != NULL) {
1019 if(strstr(line, "[Session ") != NULL) {
1021 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1022 if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1023 if(track > track_num) {
1027 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
1028 if(track > 0 && track < 0xa0) {
1029 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
1031 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1032 if(track > 0 && track < 0xa0) {
1033 toc_table[track - 1].pregap = atoi(ptr + 6);
1035 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1036 if(track > 0 && track < 0xa0) {
1037 toc_table[track - 1].index1 = atoi(ptr + 5);
1041 if(track_num != 0) {
1042 toc_table[0].lba_offset = 0;
1043 for(int i = 1; i < track_num; i++) {
1044 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1045 toc_table[i].lba_offset = toc_table[i].index0;
1047 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1048 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1058 #ifdef _SCSI_DEBUG_LOG
1060 for(int i = 0; i < track_num + 1; i++) {
1061 uint32_t idx0_msf = lba_to_msf(i, toc_table[i].index0);
1062 uint32_t idx1_msf = lba_to_msf(i, toc_table[i].index1);
1063 uint32_t pgap_msf = lba_to_msf(i, toc_table[i].pregap);
1064 this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGpap=%02x:%02x:%02x\n"), i + 1,
1065 (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
1066 (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
1067 (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
1073 void SCSI_CDROM::close()
1075 if(fio_img->IsOpened()) {
1078 memset(toc_table, 0, sizeof(toc_table));
1082 set_cdda_status(CDDA_OFF);
1085 bool SCSI_CDROM::mounted()
1087 if(is_cue) return true;
1088 return fio_img->IsOpened();
1091 bool SCSI_CDROM::accessed()
1093 bool value = access;
1098 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1100 if(cdda_status == CDDA_PLAYING) {
1101 if(mix_loop_num != 0) {
1102 int tmp_l = 0, tmp_r = 0;
1103 for(int i = 0; i < mix_loop_num; i++) {
1104 event_callback(EVENT_CDDA, 0);
1105 tmp_l += cdda_sample_l;
1106 tmp_r += cdda_sample_r;
1108 cdda_sample_l = tmp_l / mix_loop_num;
1109 cdda_sample_r = tmp_r / mix_loop_num;
1111 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
1112 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
1114 for(int i = 0; i < cnt; i++) {
1115 *buffer++ += val_l; // L
1116 *buffer++ += val_r; // R
1121 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1123 volume_l = decibel_to_volume(decibel_l);
1124 volume_r = decibel_to_volume(decibel_r);
1127 void SCSI_CDROM::set_volume(int volume)
1129 volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1132 #define STATE_VERSION 3
1134 // Q: If loading state when using another (saved) image? 20181013 K.O
1135 // May need close() and open() (or ...?).
1136 bool SCSI_CDROM::process_state(FILEIO* state_fio, bool loading)
1138 uint32_t offset = 0;
1140 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1143 if(!state_fio->StateCheckInt32(this_device_id)) {
1146 state_fio->StateValue(cdda_start_frame);
1147 state_fio->StateValue(cdda_end_frame);
1148 state_fio->StateValue(cdda_playing_frame);
1149 state_fio->StateValue(cdda_status);
1150 state_fio->StateValue(cdda_repeat);
1151 state_fio->StateValue(cdda_interrupt);
1152 state_fio->StateArray(cdda_buffer, sizeof(cdda_buffer), 1);
1153 state_fio->StateValue(cdda_buffer_ptr);
1154 state_fio->StateValue(cdda_sample_l);
1155 state_fio->StateValue(cdda_sample_r);
1156 state_fio->StateValue(event_cdda);
1157 // state_fio->StateValue(mix_loop_num);
1158 state_fio->StateValue(volume_m);
1160 offset = state_fio->FgetUint32_LE();
1162 if(fio_img->IsOpened()) {
1163 offset = fio_img->Ftell();
1165 state_fio->FputUint32_LE(offset);
1168 state_fio->StateValue(is_cue);
1169 state_fio->StateValue(current_track);
1170 // ToDo: Re-Open Image.20181118 K.O
1172 if(loading && fio_img->IsOpened()) {
1173 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1175 return SCSI_DEV::process_state(state_fio, loading);