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
20 #define EVENT_CDDA_DELAY_PLAY 101
21 #define EVENT_CDROM_SEEK_SCSI 102
22 void SCSI_CDROM::initialize()
24 SCSI_DEV::initialize();
25 fio_img = new FILEIO();
27 if(44100 % emu->get_sound_rate() == 0) {
28 mix_loop_num = 44100 / emu->get_sound_rate();
33 event_cdda_delay_play = -1;
34 cdda_status = CDDA_OFF;
37 for(int i = 0; i < 99; i++) {
38 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
42 void SCSI_CDROM::release()
44 if(fio_img->IsOpened()) {
51 void SCSI_CDROM::reset()
55 set_cdda_status(CDDA_OFF);
56 // Q: Does not seek to track 0? 20181118 K.O
60 uint32_t SCSI_CDROM::read_signal(int id)
63 case SIG_SCSI_CDROM_PLAYING:
64 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
66 case SIG_SCSI_CDROM_SAMPLE_L:
67 return (uint32_t)abs(cdda_sample_l);
69 case SIG_SCSI_CDROM_SAMPLE_R:
70 return (uint32_t)abs(cdda_sample_r);
72 return SCSI_DEV::read_signal(id);
75 void SCSI_CDROM::event_callback(int event_id, int err)
78 case EVENT_CDDA_DELAY_PLAY:
79 if(cdda_status != CDDA_PLAYING) {
80 set_cdda_status(CDDA_PLAYING);
82 event_cdda_delay_play = -1;
84 case EVENT_CDROM_SEEK_SCSI:
86 event_cdda_delay_play = -1;
87 SCSI_DEV::start_command();
90 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
91 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
92 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
93 // ToDo: CLEAR IRQ Line (for PCE)
94 if((cdda_buffer_ptr += 4) % 2352 == 0) {
96 if(++cdda_playing_frame == cdda_end_frame) {
97 // reached to end frame
98 #ifdef _CDROM_DEBUG_LOG
99 this->out_debug_log(_T("Reaches to the end of track.(FRAME %d). START_FRAME=%d END_FRAME=%d REPEAT=%s INTERRUPT=%s\n"),
100 cdda_playing_frame, cdda_start_frame, cdda_end_frame,
101 (cdda_repeat) ? _T("YES") : _T("NO"), (cdda_interrupt) ? _T("YES") : _T("NO"));
108 int trk = get_track(cdda_start_frame);
109 //int trk = current_track;
110 fio_img->Fseek((cdda_start_frame - toc_table[trk].lba_offset) * 2352, FILEIO_SEEK_SET);
112 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
114 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
116 cdda_playing_frame = cdda_start_frame;
121 write_signals(&outputs_done, 0xffffffff);
123 set_cdda_status(CDDA_OFF);
125 } else if(cdda_buffer_ptr == array_length(cdda_buffer)) {
127 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
135 SCSI_DEV::event_callback(event_id, err);
140 void SCSI_CDROM::set_cdda_status(uint8_t status)
142 if(status == CDDA_PLAYING) {
143 if(mix_loop_num == 0) {
144 if(event_cdda == -1) {
145 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
148 if(cdda_status != CDDA_PLAYING) {
149 //// Notify to release bus.
150 //write_signals(&outputs_done, 0x00000000);
151 if(cdda_status == CDDA_OFF) {
152 //get_track_by_track_num(current_track); // Re-Play
153 cdda_playing_frame = cdda_start_frame;
154 } else if(cdda_status == CDDA_PAUSED) {
156 // Maybe need seek position.
157 get_track_by_track_num(current_track); // Re-Play
160 set_realtime_render(this, true);
161 #ifdef _CDROM_DEBUG_LOG
162 this->out_debug_log(_T("Play CDDA from %s.\n"), (cdda_status == CDDA_PAUSED) ? _T("PAUSED") : _T("STOPPED"));
166 if(event_cdda != -1) {
167 cancel_event(this, event_cdda);
170 if(cdda_status == CDDA_PLAYING) {
171 // Notify to release bus.
172 write_signals(&outputs_done, 0x00000000);
173 if(status == CDDA_OFF) {
175 // if(fio_img->IsOpened()) fio_img->Fclose();
180 set_realtime_render(this, false);
181 #ifdef _CDROM_DEBUG_LOG
182 this->out_debug_log(_T("%s playing CDDA.\n"), (status == CDDA_PAUSED) ? _T("PAUSE") : _T("STOP"));
186 cdda_status = status;
189 void SCSI_CDROM::reset_device()
191 set_cdda_status(CDDA_OFF);
192 SCSI_DEV::reset_device();
195 bool SCSI_CDROM::is_device_ready()
200 int SCSI_CDROM::get_command_length(int value)
210 return SCSI_DEV::get_command_length(value);
213 void SCSI_CDROM::get_track_by_track_num(int track)
215 if((track <= 0) || (track >= track_num)) {
217 if(fio_img->IsOpened()) fio_img->Fclose();
219 //if(track <= 0) current_track = 0;
220 //if(track >= track_num)current_track = track_num;
225 // ToDo: Apply audio with some codecs.
226 if((current_track != track) || !(fio_img->IsOpened())){
227 if(fio_img->IsOpened()) {
230 #ifdef _CDROM_DEBUG_LOG
231 this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
234 if((track > 0) && (track < 100) && (track < track_num)) {
235 if((strlen(track_data_path[track - 1]) <= 0) ||
236 !(fio_img->Fopen(track_data_path[track - 1], FILEIO_READ_BINARY))) {
244 current_track = track;
247 // Detect only track num.
248 int SCSI_CDROM::get_track_noop(uint32_t lba)
251 for(int i = 0; i < track_num; i++) {
252 if(lba >= toc_table[i].index0) {
261 int SCSI_CDROM::get_track(uint32_t lba)
264 track = get_track_noop(lba);
266 get_track_by_track_num(track);
268 current_track = track;
273 double SCSI_CDROM::get_seek_time(uint32_t lba)
275 if(fio_img->IsOpened()) {
276 uint32_t cur_position = (uint32_t)fio_img->Ftell();
280 for(int i = 0; i < track_num; i++) {
281 if(lba >= toc_table[i].index0) {
287 distance = abs((int)(lba * physical_block_size()) - (int)(cur_position + toc_table[current_track].lba_offset * physical_block_size()));
288 if(track != current_track) {
289 current_track = get_track(lba);
292 distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
294 double ratio = (double)distance / 333000 / physical_block_size(); // 333000: sectors in media
295 return max(10, (int)(400000 * 2 * ratio));
297 return 400000; // 400msec
301 uint32_t SCSI_CDROM::lba_to_msf(uint32_t lba)
303 uint8_t m = lba / (60 * 75);
304 lba -= m * (60 * 75);
305 uint8_t s = lba / 75;
306 uint8_t f = lba % 75;
308 return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
311 uint32_t SCSI_CDROM::lba_to_msf_alt(uint32_t lba)
314 ret |= ((lba / (60 * 75)) & 0xff) << 16;
315 ret |= (((lba / 75) % 60) & 0xff) << 8;
316 ret |= ((lba % 75) & 0xff) << 0;
320 void SCSI_CDROM::start_command()
323 #ifdef _SCSI_DEBUG_LOG
324 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]);
328 //seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
329 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
330 set_cdda_status(CDDA_OFF);
331 if(seek_time > 10.0) {
332 if(event_cdda_delay_play >= 0) {
333 cancel_event(this, event_cdda_delay_play);
334 event_cdda_delay_play = -1;
336 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
342 case SCSI_CMD_READ10:
343 case SCSI_CMD_READ12:
344 seek_time = get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
345 set_cdda_status(CDDA_OFF);
346 if(seek_time > 10.0) {
347 if(event_cdda_delay_play >= 0) {
348 cancel_event(this, event_cdda_delay_play);
349 event_cdda_delay_play = -1;
351 register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
359 #ifdef _SCSI_DEBUG_LOG
360 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback Start Position CMD=%02x\n"), scsi_id, ((command[2] == 0) && (command[3] == 0) && (command[4] == 0)) ? 0x00 : command[9]);
362 if(is_device_ready()) {
363 if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
364 // stop cd-da if all params are zero
365 cdda_start_frame = 0;
366 cdda_end_frame = toc_table[track_num].index0; // end of disc
368 get_track_by_track_num(track_num);
370 set_cdda_status(CDDA_OFF);
372 uint32_t seek_offset = 0;
373 switch(command[9] & 0xc0) {
375 cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
379 uint8_t m = FROM_BCD(command[2]);
380 uint8_t s = FROM_BCD(command[3]);
381 uint8_t f = FROM_BCD(command[4]);
382 cdda_start_frame = f + 75 * (s + m * 60);
384 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
385 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
386 int track = get_track(cdda_start_frame);
387 cdda_start_frame -= toc_table[track].pregap;
389 if(cdda_start_frame < toc_table[track].index1) {
390 cdda_start_frame = toc_table[track].index1; // don't play pregap
391 } else if(cdda_start_frame > max_logical_block) {
392 cdda_start_frame = 0;
398 int trk = FROM_BCD(command[2]) - 1;
399 // int trk = FROM_BCD(command[2]);
400 get_track_by_track_num(trk);
402 cdda_start_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
403 // cdda_start_frame = toc_table[FROM_BCD(command[2])].index1;
406 cdda_start_frame = 0;
409 // if(cdda_status == CDDA_PAUSED) {
410 // cdda_end_frame = toc_table[track_num].index0; // end of disc
411 // set_cdda_status(CDDA_OFF);
413 if((command[1] & 3) != 0) {
414 if((is_cue) && (current_track != track_num)){
415 get_track_by_track_num(track_num);
417 cdda_end_frame = toc_table[track_num].index0; // end of disc
418 set_cdda_status(CDDA_PLAYING);
420 uint32_t _sframe = cdda_start_frame;
421 //cdda_end_frame = toc_table[get_track(_sframe) + 1].index1; // end of this track
422 cdda_end_frame = toc_table[get_track(_sframe)].index1; // end of this track
423 set_cdda_status(CDDA_PAUSED);
426 cdda_interrupt = ((command[1] & 3) == 2);
429 double delay_time = get_seek_time(cdda_start_frame);
431 fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
433 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
435 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
437 cdda_playing_frame = cdda_start_frame;
440 // change to status phase
441 set_dat(SCSI_STATUS_GOOD);
443 write_signals(&outputs_done, 0xffffffff);
444 set_phase_delay(SCSI_PHASE_STATUS, delay_time);
448 // change to status phase
449 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
451 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
452 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
457 #ifdef _SCSI_DEBUG_LOG
458 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback End Position CMD=%02x ARG=%02x %02x %02x %02x\n"), scsi_id, command[9], command[2], command[3], command[4], command[5]);
460 if(is_device_ready()) {
461 switch(command[9] & 0xc0) {
463 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
467 uint8_t m = FROM_BCD(command[2]);
468 uint8_t s = FROM_BCD(command[3]);
469 uint8_t f = FROM_BCD(command[4]);
470 cdda_end_frame = f + 75 * (s + m * 60);
471 //printf("END FRAME=%d\n", cdda_end_frame);
473 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
474 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
476 int track = current_track;
477 //track = get_track_noop(cdda_end_frame);
479 if(cdda_end_frame > toc_table[track].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track].index1) {
480 cdda_end_frame = toc_table[track].index1;
486 // ToDo: When _track != current_track.
487 // ToDo: Over reading boundary of both tracks.
488 int _track = FROM_BCD(command[2]) - 1;
489 // int _track = FROM_BCD(command[2]);
491 cdda_end_frame = toc_table[_track].index1;
493 get_track_by_track_num(_track);
501 if((command[1] & 3) != 0) {
502 cdda_repeat = ((command[1] & 3) == 1);
503 cdda_interrupt = ((command[1] & 3) == 2);
505 set_cdda_status(CDDA_PLAYING);
507 set_cdda_status(CDDA_OFF);
508 cdda_start_frame = 0;
509 cdda_end_frame = toc_table[track_num].index0; // end of disc
510 get_track_by_track_num(track_num); // END OF DISC
513 // change to status phase
514 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
515 if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
516 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
520 #ifdef _SCSI_DEBUG_LOG
521 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
523 if(is_device_ready()) {
524 if(cdda_status == CDDA_PLAYING) {
525 set_cdda_status(CDDA_PAUSED);
528 if(is_cue) { // NEC PC-ENGINE ONLY.This expect to be temporally workaround.20181119 K.O
529 get_track_by_track_num(2);
531 // change to status phase
532 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
533 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
534 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
538 // #ifdef _SCSI_DEBUG_LOG
539 // this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
541 if(is_device_ready()) {
543 uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
544 uint32_t msf_abs = lba_to_msf_alt(frame);
546 double delay_time = 10.0;
548 track = current_track;
550 track = get_track(frame);
552 if((cdda_status == CDDA_OFF) && (toc_table[track].is_audio)) { // OK? (or force ERROR) 20181120 K.O
553 //set_cdda_status(CDDA_PLAYING);
554 delay_time = get_seek_time(frame);
555 if(event_cdda_delay_play >= 0) {
556 cancel_event(this, event_cdda_delay_play);
557 event_cdda_delay_play = -1;
559 register_event(this, EVENT_CDDA_DELAY_PLAY, delay_time, false, &event_cdda_delay_play);
561 uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
563 buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
564 buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
565 buffer->write(TO_BCD(track + 1)); // Track
566 buffer->write(0x01); // Index
567 buffer->write(TO_BCD((msf_rel >> 16) & 0xff)); // M (relative)
568 buffer->write(TO_BCD((msf_rel >> 8) & 0xff)); // S (relative)
569 buffer->write(TO_BCD((msf_rel >> 0) & 0xff)); // F (relative)
570 buffer->write(TO_BCD((msf_abs >> 16) & 0xff)); // M (absolute)
571 buffer->write(TO_BCD((msf_abs >> 8) & 0xff)); // S (absolute)
572 buffer->write(TO_BCD((msf_abs >> 0) & 0xff)); // F (absolute)
574 remain = buffer->count();
576 set_dat(buffer->read());
577 // change to data in phase
578 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
580 // change to status phase
581 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
582 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
587 #ifdef _SCSI_DEBUG_LOG
588 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
590 if(is_device_ready()) {
592 #ifdef _SCSI_DEBUG_LOG
593 this->out_debug_log(_T("[SCSI_DEV:ID=%d] CMD=%02x ARG=%02x \n"), scsi_id, command[1], command[2]);
596 case 0x00: /* Get first and last track numbers */
597 buffer->write(TO_BCD(1));
598 buffer->write(TO_BCD(track_num));
600 case 0x01: /* Get total disk size in MSF format */
602 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
603 buffer->write((msf >> 16) & 0xff);
604 buffer->write((msf >> 8) & 0xff);
605 buffer->write((msf >> 0) & 0xff);
608 case 0x02: /* Get track information */
609 if(command[2] == 0xaa) {
610 uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
611 buffer->write((msf >> 16) & 0xff);
612 buffer->write((msf >> 8) & 0xff);
613 buffer->write((msf >> 0) & 0xff);
614 buffer->write(0x04); // correct ?
616 int track = max(FROM_BCD(command[2]), 1);
617 uint32_t frame = toc_table[track].index0;
618 // PCE wants the start sector for data tracks to *not* include the pregap
619 if(!toc_table[track].is_audio) {
620 frame += toc_table[track].pregap;
622 get_track_by_track_num(track);
624 uint32_t msf = lba_to_msf(toc_table[track].index1 + 150);
625 buffer->write((msf >> 16) & 0xff); // M
626 buffer->write((msf >> 8) & 0xff); // S
627 buffer->write((msf >> 0) & 0xff); // F
628 buffer->write(toc_table[track].is_audio ? 0x00 : 0x04);
633 remain = buffer->count();
635 set_dat(buffer->read());
636 // change to data in phase
637 set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
639 // change to status phase
640 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
641 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
646 set_dat(SCSI_STATUS_CHKCOND);
650 // start standard command
651 SCSI_DEV::start_command();
654 bool SCSI_CDROM::read_buffer(int length)
656 if(!fio_img->IsOpened()) {
657 set_sense_code(SCSI_SENSE_NOTREADY);
660 uint32_t offset = (uint32_t)(position % 2352);
663 // ToDo: Need seek wait.
664 #ifdef _CDROM_DEBUG_LOG
665 this->out_debug_log(_T("Seek to LBA %d\n"), position / 2352);
667 if(fio_img->Fseek(((long)position - (long)(toc_table[current_track].lba_offset * 2352)), FILEIO_SEEK_SET) != 0) {
668 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
669 #ifdef _SCSI_DEBUG_LOG
670 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
675 if(fio_img->Fseek((long)position, FILEIO_SEEK_SET) != 0) {
676 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
677 #ifdef _SCSI_DEBUG_LOG
678 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
684 uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
685 int tmp_length = min(length, (int)sizeof(tmp_buffer));
687 if(fio_img->Fread(tmp_buffer, tmp_length, 1) != 1) {
688 #ifdef _SCSI_DEBUG_LOG
689 this->out_debug_log(_T("[SCSI_DEV:ID=%d] Error on reading (ILLGLBLKADDR) at line %d\n"), scsi_id, __LINE__);
692 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
695 for(int i = 0; i < tmp_length && length > 0; i++) {
696 if(offset >= 16 && offset < 16 + 2048) {
697 int value = tmp_buffer[i];
698 buffer->write(value);
702 offset = (offset + 1) % 2352;
706 // Is This right? 20181120 K.O
707 write_signals(&outputs_done, 0xffffffff);
708 set_sense_code(SCSI_SENSE_NOSENSE);
712 int get_frames_from_msf(const char *string)
714 const char *ptr = string;
719 if(*ptr >= '0' && *ptr <= '9') {
720 frames[index] = frames[index] * 10 + (*ptr - '0');
721 } else if(*ptr == ':') {
726 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
732 return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
735 int hexatoi(const char *string)
737 const char *ptr = string;
741 if(*ptr >= '0' && *ptr <= '9') {
742 value = value * 16 + (*ptr - '0');
743 } else if(*ptr >= 'a' && *ptr <= 'f') {
744 value = value * 16 + (*ptr - 'a' + 10);
745 } else if(*ptr >= 'A' && *ptr <= 'F') {
746 value = value * 16 + (*ptr - 'A' + 10);
747 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
757 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
759 std::string line_buf;
760 std::string line_buf_shadow;
761 std::string image_tmp_data_path;
763 _TCHAR full_path_cue[_MAX_PATH];
767 int nr_current_track = 0;
768 FILEIO* fio = new FILEIO();
769 if(fio == NULL) return false;
771 memset(full_path_cue, 0x00, sizeof(full_path_cue));
772 image_tmp_data_path.clear();
774 get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
776 _TCHAR *parent_dir = get_parent_dir(full_path_cue);
788 if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
790 for(int i = 0; i < 100; i++) {
791 memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
802 if((_c == '\0') || (_c == '\n') || (_c == EOF)) break;;
803 if(_c != '\r') line_buf.push_back((char)_c);
805 if(_c == EOF) is_eof = true;
806 slen = (int)line_buf.length();
807 if(slen <= 0) goto _n_continue;
808 // Trim head of Space or TAB
816 ptr = line_buf.find_first_not_of((const char*)" \t");
817 if(ptr == std::string::npos) {
821 line_buf_shadow = line_buf.substr(ptr);
823 _arg1_ptr = line_buf_shadow.find_first_of((const char *)" \t");
824 _arg1 = line_buf_shadow.substr(0, _arg1_ptr);
825 _arg2 = line_buf_shadow.substr(_arg1_ptr);
826 std::transform(_arg1.begin(), _arg1.end(), _arg1.begin(),
827 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
829 _arg2_ptr = _arg2.find_first_not_of((const char *)" \t");
831 if(_arg2_ptr != std::string::npos) {
832 _arg2 = _arg2.substr(_arg2_ptr);
838 } else if(_arg1 == "FILE") {
839 _arg2_ptr = _arg2.find_first_of((const char *)"\"") + 1;
840 if(_arg2_ptr == std::string::npos) goto _n_continue;
842 _arg2 = _arg2.substr(_arg2_ptr);
843 _arg3_ptr = _arg2.find_first_of((const char *)"\"");
844 if(_arg3_ptr == std::string::npos) goto _n_continue;
845 _arg2 = _arg2.substr(0, _arg3_ptr);
847 image_tmp_data_path.clear();
848 image_tmp_data_path = std::string(parent_dir);
849 image_tmp_data_path.append(_arg2);
851 #ifdef _CDROM_DEBUG_LOG
852 this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path.c_str());
854 goto _n_continue; // ToDo: Check ARG2 (BINARY etc..) 20181118 K.O
855 } else if(_arg1 == "TRACK") {
856 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
858 _arg3 = _arg2.substr(_arg2_ptr_s);
859 _arg2 = _arg2.substr(0, _arg2_ptr_s);
860 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
861 int _nr_num = atoi(_arg2.c_str());
864 if((_nr_num > 0) && (_nr_num < 100) && (_arg3_ptr != std::string::npos)) {
865 nr_current_track = _nr_num;
866 _arg3 = _arg3.substr(_arg3_ptr);
868 memset(track_data_path[_nr_num - 1], 0x00, sizeof(_TCHAR) * _MAX_PATH);
869 strncpy((char *)(track_data_path[_nr_num - 1]), image_tmp_data_path.c_str(), _MAX_PATH);
871 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t\n");
872 _arg3.substr(0, _arg3_ptr_s);
874 std::transform(_arg3.begin(), _arg3.end(), _arg3.begin(),
875 [](unsigned char c) -> unsigned char{ return std::toupper(c); });
877 toc_table[nr_current_track].is_audio = false;
878 toc_table[nr_current_track].index0 = 0;
879 toc_table[nr_current_track].index1 = 0;
880 toc_table[nr_current_track].pregap = 0;
882 if(_arg3.compare("AUDIO") == 0) {
883 toc_table[nr_current_track].is_audio = true;
884 } else if(_arg3.compare("MODE1/2352") == 0) {
885 toc_table[nr_current_track].is_audio = false;
887 // ToDo: another type
889 if(track_num < (_nr_num + 1)) track_num = _nr_num + 1;
891 // ToDo: 20181118 K.Ohta
892 nr_current_track = 0;
895 } else if(_arg1 == "INDEX") {
897 if((nr_current_track > 0) && (nr_current_track < 100)) {
898 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
899 if(_arg2_ptr_s == std::string::npos) goto _n_continue;
901 _arg3 = _arg2.substr(_arg2_ptr_s);
902 _arg2 = _arg2.substr(0, _arg2_ptr_s);
903 _arg3_ptr = _arg3.find_first_not_of((const char *)" \t");
904 if(_arg3_ptr == std::string::npos) goto _n_continue;
906 _arg3 = _arg3.substr(_arg3_ptr);
907 _arg3_ptr_s = _arg3.find_first_of((const char *)" \t");
908 _arg3.substr(0, _arg3_ptr_s);
909 int index_type = atoi(_arg2.c_str());
913 toc_table[nr_current_track].index0 = get_frames_from_msf(_arg3.c_str());
916 toc_table[nr_current_track].index1 = get_frames_from_msf(_arg3.c_str());
925 } else if(_arg1 == "PREGAP") {
926 if((nr_current_track > 0) && (nr_current_track < 100)) {
927 _arg2_ptr_s = _arg2.find_first_of((const char *)" \t");
928 _arg2 = _arg2.substr(0, _arg2_ptr_s - 1);
930 toc_table[nr_current_track].pregap = get_frames_from_msf(_arg2.c_str());
942 max_logical_block = 0;
944 toc_table[0].lba_offset = 0;
945 toc_table[0].lba_size = 0;
946 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
949 for(int i = 1; i < track_num; i++) {
951 if(fio_img->IsOpened()) {
954 if(toc_table[i].index1 != 0) {
955 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
956 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
957 if(toc_table[i].index0 != max_logical_block) {
958 if(toc_table[i].pregap == 0) {
959 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
963 toc_table[i].index1 = toc_table[i].index1 + max_logical_block;
964 if(toc_table[i].index0 == 0) {
965 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
967 toc_table[i].index0 = toc_table[i].index0 + max_logical_block;
968 if(toc_table[i].pregap == 0) {
969 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
973 if(strlen(track_data_path[i - 1]) > 0) {
974 if(fio_img->Fopen(track_data_path[i - 1], FILEIO_READ_BINARY)) {
975 if((_n = fio_img->FileLength() / 2352) > 0) {
976 max_logical_block += _n;
980 toc_table[i].lba_size = _n;
981 toc_table[i].lba_offset = max_logical_block - _n;
984 #ifdef _CDROM_DEBUG_LOG
985 this->out_debug_log(_T("TRACK#%02d TYPE=%s PREGAP=%d INDEX0=%d INDEX1=%d LBA_SIZE=%d LBA_OFFSET=%d PATH=%s\n"),
986 i, (toc_table[i].is_audio) ? _T("AUDIO") : _T("MODE1/2352"),
987 toc_table[i].pregap, toc_table[i].index0, toc_table[i].index1,
988 toc_table[i].lba_size, toc_table[i].lba_offset, track_data_path[i - 1]);
991 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
992 // toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
993 // toc_table[track_num].lba_offset = max_logical_block;
994 // toc_table[track_num].lba_size = 0;
1002 if(track_num > 0) is_cue = true;
1007 void SCSI_CDROM::open(const _TCHAR* file_path)
1009 _TCHAR img_file_path[_MAX_PATH];
1013 // ToDo: Process multi track cue file.Most of CDROMs contain both audio and data and more than 2 tracks.
1015 if(check_file_extension(file_path, _T(".cue"))) {
1019 open_cue_file(file_path);
1021 // get image file name
1022 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1023 if(!FILEIO::IsFileExisting(img_file_path)) {
1024 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
1025 if(!FILEIO::IsFileExisting(img_file_path)) {
1026 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1027 if(!FILEIO::IsFileExisting(img_file_path)) {
1028 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1029 if(!FILEIO::IsFileExisting(img_file_path)) {
1030 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin.gz"), get_file_path_without_extensiton(file_path));
1035 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1036 // get image file size
1037 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1039 FILEIO* fio = new FILEIO();
1040 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1041 char line[1024], *ptr;
1043 while(fio->Fgets(line, 1024) != NULL) {
1044 if(strstr(line, "FILE") != NULL) {
1046 } else if((ptr = strstr(line, "TRACK")) != NULL) {
1048 // "TRACK 02 MODE1/2352"
1050 while(*ptr == ' ' || *ptr == 0x09) {
1053 if((track = atoi(ptr)) > 0) {
1054 if(track > track_num) {
1057 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
1059 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
1060 // "PREGAP 00:02:00"
1062 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
1064 } else if((ptr = strstr(line, "INDEX")) != NULL) {
1065 // "INDEX 01 00:00:00"
1068 while(*ptr == ' ' || *ptr == 0x09) {
1071 int num = atoi(ptr);
1072 while(*ptr >= '0' && *ptr <= '9') {
1076 toc_table[track - 1].index0 = get_frames_from_msf(ptr);
1077 } else if(num == 1) {
1078 toc_table[track - 1].index1 = get_frames_from_msf(ptr);
1083 if(track_num != 0) {
1084 for(int i = 1; i < track_num; i++) {
1085 if(toc_table[i].index0 == 0) {
1086 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1087 } else if(toc_table[i].pregap == 0) {
1088 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
1091 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1092 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1102 } else if(check_file_extension(file_path, _T(".ccd"))) {
1103 // get image file name
1104 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1105 if(!FILEIO::IsFileExisting(img_file_path)) {
1106 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1107 if(!FILEIO::IsFileExisting(img_file_path)) {
1108 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1111 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1114 // get image file size
1115 if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1117 FILEIO* fio = new FILEIO();
1118 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1119 char line[1024], *ptr;
1121 while(fio->Fgets(line, 1024) != NULL) {
1122 if(strstr(line, "[Session ") != NULL) {
1124 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1125 if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1126 if(track > track_num) {
1130 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
1131 if(track > 0 && track < 0xa0) {
1132 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
1134 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1135 if(track > 0 && track < 0xa0) {
1136 toc_table[track - 1].pregap = atoi(ptr + 6);
1138 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1139 if(track > 0 && track < 0xa0) {
1140 toc_table[track - 1].index1 = atoi(ptr + 5);
1144 if(track_num != 0) {
1145 toc_table[0].lba_offset = 0;
1146 toc_table[0].pregap = 0;
1147 for(int i = 1; i < track_num; i++) {
1148 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1149 toc_table[i].lba_offset = toc_table[i].pregap;
1150 toc_table[i - 1].lba_size = toc_table[i].pregap - toc_table[i - 1].pregap;
1152 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1153 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1155 toc_table[track_num].lba_size = max_logical_block - toc_table[track_num - 1].lba_offset;
1157 toc_table[track_num].lba_size = 0;
1168 #ifdef _SCSI_DEBUG_LOG
1170 for(int i = 0; i < track_num + 1; i++) {
1171 uint32_t idx0_msf = lba_to_msf(toc_table[i].index0);
1172 uint32_t idx1_msf = lba_to_msf(toc_table[i].index1);
1173 uint32_t pgap_msf = lba_to_msf(toc_table[i].pregap);
1174 this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGpap=%02x:%02x:%02x\n"), i + 1,
1175 (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
1176 (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
1177 (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
1183 void SCSI_CDROM::close()
1185 if(fio_img->IsOpened()) {
1188 memset(toc_table, 0, sizeof(toc_table));
1192 set_cdda_status(CDDA_OFF);
1195 bool SCSI_CDROM::mounted()
1197 if(is_cue) return true;
1198 return fio_img->IsOpened();
1201 bool SCSI_CDROM::accessed()
1203 bool value = access;
1208 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1210 if(cdda_status == CDDA_PLAYING) {
1211 if(mix_loop_num != 0) {
1212 int tmp_l = 0, tmp_r = 0;
1213 for(int i = 0; i < mix_loop_num; i++) {
1214 event_callback(EVENT_CDDA, 0);
1215 tmp_l += cdda_sample_l;
1216 tmp_r += cdda_sample_r;
1218 cdda_sample_l = tmp_l / mix_loop_num;
1219 cdda_sample_r = tmp_r / mix_loop_num;
1221 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
1222 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
1224 for(int i = 0; i < cnt; i++) {
1225 *buffer++ += val_l; // L
1226 *buffer++ += val_r; // R
1231 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1233 volume_l = decibel_to_volume(decibel_l);
1234 volume_r = decibel_to_volume(decibel_r);
1237 void SCSI_CDROM::set_volume(int volume)
1239 volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1242 #define STATE_VERSION 3
1244 // Q: If loading state when using another (saved) image? 20181013 K.O
1245 // May need close() and open() (or ...?).
1246 bool SCSI_CDROM::process_state(FILEIO* state_fio, bool loading)
1248 uint32_t offset = 0;
1250 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1253 if(!state_fio->StateCheckInt32(this_device_id)) {
1256 state_fio->StateValue(cdda_start_frame);
1257 state_fio->StateValue(cdda_end_frame);
1258 state_fio->StateValue(cdda_playing_frame);
1259 state_fio->StateValue(cdda_status);
1260 state_fio->StateValue(cdda_repeat);
1261 state_fio->StateValue(cdda_interrupt);
1262 state_fio->StateArray(cdda_buffer, sizeof(cdda_buffer), 1);
1263 state_fio->StateValue(cdda_buffer_ptr);
1264 state_fio->StateValue(cdda_sample_l);
1265 state_fio->StateValue(cdda_sample_r);
1266 state_fio->StateValue(event_cdda);
1267 state_fio->StateValue(event_cdda_delay_play);
1268 // state_fio->StateValue(mix_loop_num);
1269 state_fio->StateValue(volume_m);
1271 offset = state_fio->FgetUint32_LE();
1273 if(fio_img->IsOpened()) {
1274 offset = fio_img->Ftell();
1276 state_fio->FputUint32_LE(offset);
1279 state_fio->StateValue(is_cue);
1280 state_fio->StateValue(current_track);
1281 // ToDo: Re-Open Image.20181118 K.O
1283 if(loading && fio_img->IsOpened()) {
1284 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1286 return SCSI_DEV::process_state(state_fio, loading);