OSDN Git Service

[VM][SCSI_CDROM][PCENGINE][WIP] .
[csp-qt/common_source_project-fm7.git] / source / src / vm / scsi_cdrom.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2016.03.06-
6
7         [ SCSI CD-ROM drive ]
8 */
9
10 #include "scsi_cdrom.h"
11 #include "../fifo.h"
12
13 #define CDDA_OFF        0
14 #define CDDA_PLAYING    1
15 #define CDDA_PAUSED     2
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()
23 {
24         SCSI_DEV::initialize();
25         fio_img = new FILEIO();
26         
27         if(44100 % emu->get_sound_rate() == 0) {
28                 mix_loop_num = 44100 / emu->get_sound_rate();
29         } else {
30                 mix_loop_num = 0;
31         }
32         event_cdda = -1;
33         event_cdda_delay_play = -1;
34         cdda_status = CDDA_OFF;
35         is_cue = false;
36         current_track = 0;
37         for(int i = 0; i < 99; i++) {
38                 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
39         }
40 }
41
42 void SCSI_CDROM::release()
43 {
44         if(fio_img->IsOpened()) {
45                 fio_img->Fclose();
46         }
47         delete fio_img;
48         SCSI_DEV::release();
49 }
50
51 void SCSI_CDROM::reset()
52 {
53         touch_sound();
54         SCSI_DEV::reset();
55         set_cdda_status(CDDA_OFF);
56         // Q: Does not seek to track 0? 20181118 K.O
57         //current_track = 0;
58 }
59
60 uint32_t SCSI_CDROM::read_signal(int id)
61 {
62         switch(id) {
63         case SIG_SCSI_CDROM_PLAYING:
64                 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
65                 
66         case SIG_SCSI_CDROM_SAMPLE_L:
67                 return (uint32_t)abs(cdda_sample_l);
68                 
69         case SIG_SCSI_CDROM_SAMPLE_R:
70                 return (uint32_t)abs(cdda_sample_r);
71         }
72         return SCSI_DEV::read_signal(id);
73 }
74
75 void SCSI_CDROM::event_callback(int event_id, int err)
76 {
77         switch (event_id) {
78         case EVENT_CDDA_DELAY_PLAY:
79                 if(cdda_status != CDDA_PLAYING) {
80                         set_cdda_status(CDDA_PLAYING);
81                 }
82                 event_cdda_delay_play = -1;
83                 break;
84         case EVENT_CDROM_SEEK_SCSI:
85                 seek_time = 10.0;
86                 event_cdda_delay_play = -1;
87                 SCSI_DEV::start_command();
88                 break;
89         case EVENT_CDDA:
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) {
95                         // one frame finished
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"));
102                                 #endif
103                                 if(cdda_repeat) {
104                                         // reload buffer
105                                         if(is_cue) {
106                                                 fio_img->Fclose();
107                                                 current_track = 0;
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);
111                                         } else {
112                                                 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
113                                         }
114                                         fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
115                                         cdda_buffer_ptr = 0;
116                                         cdda_playing_frame = cdda_start_frame;
117                                         access = true;
118                                 } else {
119                                         // stop
120                                         if(cdda_interrupt) {
121                                                 write_signals(&outputs_done, 0xffffffff);
122                                         }
123                                         set_cdda_status(CDDA_OFF);
124                                 }
125                         } else if(cdda_buffer_ptr == array_length(cdda_buffer)) {
126                                 // refresh buffer
127                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
128                                 cdda_buffer_ptr = 0;
129                                 access = true;
130                                 
131                         }
132                 }
133                 break;
134         default:
135                 SCSI_DEV::event_callback(event_id, err);
136                 break;
137         }
138 }
139
140 void SCSI_CDROM::set_cdda_status(uint8_t status)
141 {
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);
146                         }
147                 }
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) {
155                                 // Unpause
156                                 // Maybe need seek position.
157                                 get_track_by_track_num(current_track); // Re-Play
158                         }
159                         touch_sound();
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"));
163                         #endif
164                 }
165         } else {
166                 if(event_cdda != -1) {
167                         cancel_event(this, event_cdda);
168                         event_cdda = -1;
169                 }
170                 if(cdda_status == CDDA_PLAYING) {
171                         // Notify to release bus.
172                         write_signals(&outputs_done, 0x00000000);
173                         if(status == CDDA_OFF) {
174                                 //if(is_cue) {
175                                 //      if(fio_img->IsOpened()) fio_img->Fclose();
176                                 //}
177                                 //current_track = 0;
178                         }
179                         touch_sound();
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"));
183                         #endif
184                 }
185         }
186         cdda_status = status;
187 }
188
189 void SCSI_CDROM::reset_device()
190 {
191         set_cdda_status(CDDA_OFF);
192         SCSI_DEV::reset_device();
193 }
194
195 bool SCSI_CDROM::is_device_ready()
196 {
197         return mounted();
198 }
199
200 int SCSI_CDROM::get_command_length(int value)
201 {
202         switch(value) {
203         case 0xd8:
204         case 0xd9:
205         case 0xda:
206         case 0xdd:
207         case 0xde:
208                 return 10;
209         }
210         return SCSI_DEV::get_command_length(value);
211 }
212
213 void SCSI_CDROM::get_track_by_track_num(int track)
214 {
215         if((track <= 0) || (track >= track_num)) {
216                 if(is_cue) {
217                         if(fio_img->IsOpened()) fio_img->Fclose();
218                 }
219                 //if(track <= 0) current_track = 0;
220                 //if(track >= track_num)current_track = track_num;
221                 current_track = 0;
222                 return;
223         }
224         if(is_cue) {
225                 // ToDo: Apply audio with some codecs.
226                 if((current_track != track) || !(fio_img->IsOpened())){
227                         if(fio_img->IsOpened()) {
228                                 fio_img->Fclose();
229                         }
230                 #ifdef _CDROM_DEBUG_LOG
231                         this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
232                 #endif
233                         
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))) {
237                                         track = 0;
238                                 }
239                         } else {
240                                 track = 0;
241                         }
242                 }
243         }
244         current_track = track;
245 }
246
247 // Detect only track num.
248 int SCSI_CDROM::get_track_noop(uint32_t lba)
249 {
250         int track = 0;
251         for(int i = 0; i < track_num; i++) {
252                 if(lba >= toc_table[i].index0) {
253                         track = i;
254                 } else {
255                         break;
256                 }
257         }
258         return track;
259 }       
260
261 int SCSI_CDROM::get_track(uint32_t lba)
262 {
263         int track = 0;
264         track = get_track_noop(lba);
265         if(is_cue) {
266                 get_track_by_track_num(track);
267         } else {
268                 current_track = track;
269         }
270         return track;
271 }
272
273 double SCSI_CDROM::get_seek_time(uint32_t lba)
274 {
275         if(fio_img->IsOpened()) {
276                 uint32_t cur_position = (uint32_t)fio_img->Ftell();
277                 int distance;
278                 if(is_cue) {
279                         int track = 0;
280                         for(int i = 0; i < track_num; i++) {
281                                 if(lba >= toc_table[i].index0) {
282                                         track = i;
283                                 } else {
284                                         break;
285                                 }
286                         }
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);
290                         }
291                 } else {
292                         distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
293                 }
294                 double ratio = (double)distance / 333000 / physical_block_size(); // 333000: sectors in media
295                 return max(10, (int)(400000 * 2 * ratio));
296         } else {
297                 return 400000; // 400msec
298         }
299 }
300
301 uint32_t SCSI_CDROM::lba_to_msf(uint32_t lba)
302 {
303         uint8_t m = lba / (60 * 75);
304         lba -= m * (60 * 75);
305         uint8_t s = lba / 75;
306         uint8_t f = lba % 75;
307
308         return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
309 }
310
311 uint32_t SCSI_CDROM::lba_to_msf_alt(uint32_t lba)
312 {
313         uint32_t ret = 0;
314         ret |= ((lba / (60 * 75)) & 0xff) << 16;
315         ret |= (((lba / 75) % 60) & 0xff) <<  8;
316         ret |= ((lba % 75)        & 0xff) <<  0;
317         return ret;
318 }
319
320 void SCSI_CDROM::start_command()
321 {
322         touch_sound();
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]);
325         #endif
326         switch(command[0]) {
327         case SCSI_CMD_READ6:
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;
335                         }
336                         register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
337                         return;
338                 } else {
339                         seek_time = 10.0;
340                 }
341                 break;
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;
350                         }
351                         register_event(this, EVENT_CDROM_SEEK_SCSI, seek_time - 10.0, false, &event_cdda_delay_play);
352                         return;
353                 } else {
354                         seek_time = 10.0;
355                 }
356                 break;
357                 
358         case 0xd8:
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]);
361                 #endif
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
367                                 if(is_cue) {
368                                         get_track_by_track_num(track_num);
369                                 }
370                                 set_cdda_status(CDDA_OFF);
371                         } else {
372                                 uint32_t seek_offset = 0;
373                                 switch(command[9] & 0xc0) {
374                                 case 0x00:
375                                         cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
376                                         break;
377                                 case 0x40:
378                                         {
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);
383                                                 
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;
388                                                 
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;
393                                                 }
394                                         }
395                                         break;
396                                 case 0x80:
397                                         if(is_cue) {
398                                                 int trk = FROM_BCD(command[2]) - 1;
399 //                                              int trk = FROM_BCD(command[2]);
400                                                 get_track_by_track_num(trk);
401                                         }
402                                         cdda_start_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
403 //                                      cdda_start_frame = toc_table[FROM_BCD(command[2])].index1;
404                                         break;
405                                 default:
406                                         cdda_start_frame = 0;
407                                         break;
408                                 }
409 //                              if(cdda_status == CDDA_PAUSED) {
410 //                                      cdda_end_frame = toc_table[track_num].index0; // end of disc
411 //                                      set_cdda_status(CDDA_OFF);
412 //                              } else
413                                 if((command[1] & 3) != 0) {
414                                         if((is_cue) && (current_track != track_num)){
415                                                 get_track_by_track_num(track_num);
416                                         }
417                                         cdda_end_frame = toc_table[track_num].index0; // end of disc
418                                         set_cdda_status(CDDA_PLAYING);
419                                 } else {
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);
424                                 }
425                                 cdda_repeat = false;
426                                 cdda_interrupt = ((command[1] & 3) == 2);
427                                 
428                                 // read buffer
429                                 double delay_time = get_seek_time(cdda_start_frame);
430                                 if(is_cue) {
431                                         fio_img->Fseek((cdda_start_frame - toc_table[current_track].lba_offset) * 2352, FILEIO_SEEK_SET);
432                                 } else {
433                                         fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
434                                 }
435                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
436                                 cdda_buffer_ptr = 0;
437                                 cdda_playing_frame = cdda_start_frame;
438                                 access = true;
439                                 
440                                 // change to status phase
441                                 set_dat(SCSI_STATUS_GOOD);
442                                 // From Mame 0.203
443                                 write_signals(&outputs_done, 0xffffffff);
444                                 set_phase_delay(SCSI_PHASE_STATUS, delay_time);
445                                 return;
446                         }
447                 }
448                 // change to status phase
449                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
450                 // From Mame 0.203
451                 //if(is_device_ready()) write_signals(&outputs_done, 0xffffffff);
452                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
453
454                 return;
455                 
456         case 0xd9:
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]);
459                 #endif
460                 if(is_device_ready()) {
461                         switch(command[9] & 0xc0) {
462                         case 0x00:
463                                 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
464                                 break;
465                         case 0x40:
466                                 {
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);
472                                         
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?)
475                                         
476                                         int track = current_track;
477                                         //track = get_track_noop(cdda_end_frame);
478                                         
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;
481                                         }
482                                 }
483                                 break;
484                         case 0x80:
485                                 {
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]);
490                                         if(_track >= 0) {
491                                                 cdda_end_frame = toc_table[_track].index1;
492                                                 if(is_cue) {
493                                                         get_track_by_track_num(_track);
494                                                 }
495                                         }
496                                 }
497                                 break;
498                         default:
499                                 break; // ToDo
500                         }
501                         if((command[1] & 3) != 0) {
502                                 cdda_repeat = ((command[1] & 3) == 1);
503                                 cdda_interrupt = ((command[1] & 3) == 2);
504                                 
505                                 set_cdda_status(CDDA_PLAYING);
506                         } else {
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
511                         }
512                 }
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);
517                 return;
518                 
519         case 0xda:
520                 #ifdef _SCSI_DEBUG_LOG
521                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
522                 #endif
523                 if(is_device_ready()) {
524                         if(cdda_status == CDDA_PLAYING) {
525                                 set_cdda_status(CDDA_PAUSED);
526                         }
527                 }
528                 if(is_cue) { // NEC PC-ENGINE ONLY.This expect to be temporally workaround.20181119 K.O
529                         get_track_by_track_num(2);
530                 }
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);
535                 return;
536                 
537         case 0xdd:
538 //              #ifdef _SCSI_DEBUG_LOG
539 //                      this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
540 //              #endif
541                 if(is_device_ready()) {
542                         // create track info
543                         uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
544                         uint32_t msf_abs = lba_to_msf_alt(frame);
545                         int track;
546                         double delay_time = 10.0;
547                         if(frame == 0) {
548                                 track = current_track;
549                         } else {
550                                 track = get_track(frame);
551                         }
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;
558                                 }
559                                 register_event(this, EVENT_CDDA_DELAY_PLAY, delay_time, false, &event_cdda_delay_play);
560                         }
561                         uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
562                         buffer->clear();
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)
573                         // transfer length
574                         remain = buffer->count();
575                         // set first data
576                         set_dat(buffer->read());
577                         // change to data in phase
578                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
579                 } else {
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);
583                 }
584                 return;
585                 
586         case 0xde:
587                 #ifdef _SCSI_DEBUG_LOG
588                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
589                 #endif
590                 if(is_device_ready()) {
591                         buffer->clear();
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]);
594                 #endif
595                         switch(command[1]) {
596                         case 0x00:      /* Get first and last track numbers */
597                                 buffer->write(TO_BCD(1));
598                                 buffer->write(TO_BCD(track_num));
599                                 break;
600                         case 0x01:      /* Get total disk size in MSF format */
601                                 {
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);
606                                 }
607                                 break;
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 ?
615                                 } else {
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;
621                                         }
622                                         get_track_by_track_num(track);
623                                         
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);
629                                 }
630                                 break;
631                         }
632                         // transfer length
633                         remain = buffer->count();
634                         // set first data
635                         set_dat(buffer->read());
636                         // change to data in phase
637                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
638                 } else {
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);
642                 }
643                 return;
644         case 0xff:
645                 // End of List
646                 set_dat(SCSI_STATUS_CHKCOND);
647                 return;
648         }
649         
650         // start standard command
651         SCSI_DEV::start_command();
652 }
653
654 bool SCSI_CDROM::read_buffer(int length)
655 {
656         if(!fio_img->IsOpened()) {
657                 set_sense_code(SCSI_SENSE_NOTREADY);
658                 return false;
659         }
660         uint32_t offset = (uint32_t)(position % 2352);
661
662         if(is_cue) {
663                 // ToDo: Need seek wait.
664                 #ifdef _CDROM_DEBUG_LOG
665                         this->out_debug_log(_T("Seek to LBA %d\n"), position / 2352);
666                 #endif
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__);
671                         #endif
672                         return false;
673                 }
674         } else {
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__);
679                         #endif
680                         return false;
681                 }
682         }
683         while(length > 0) {
684                 uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
685                 int tmp_length = min(length, (int)sizeof(tmp_buffer));
686                 
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__);
690                         #endif
691                         
692                         set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
693                         return false;
694                 }
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);
699                                 length--;
700                         }
701                         position++;
702                         offset = (offset + 1) % 2352;
703                 }
704                 access = true;
705         }
706         // Is This right? 20181120 K.O
707         write_signals(&outputs_done, 0xffffffff);
708         set_sense_code(SCSI_SENSE_NOSENSE);
709         return true;
710 }
711
712 int get_frames_from_msf(const char *string)
713 {
714         const char *ptr = string;
715         int frames[3] = {0};
716         int index = 0;
717         
718         while(1) {
719                 if(*ptr >= '0' && *ptr <= '9') {
720                         frames[index] = frames[index] * 10 + (*ptr - '0');
721                 } else if(*ptr == ':') {
722                         if(++index == 3) {
723                                 // abnormal data
724                                 break;
725                         }
726                 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
727                         // end of line
728                         break;
729                 }
730                 ptr++;
731         }
732         return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
733 }
734
735 int hexatoi(const char *string)
736 {
737         const char *ptr = string;
738         int value = 0;
739         
740         while(1) {
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') {
748                         break;
749                 }
750                 ptr++;
751         }
752         return value;
753 }
754
755 #include <string>
756
757 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
758 {
759         std::string line_buf;
760         std::string line_buf_shadow;
761         std::string image_tmp_data_path;
762
763         _TCHAR full_path_cue[_MAX_PATH];
764         size_t ptr;
765         int line_count = 0;
766         int slen;
767         int nr_current_track = 0;
768         FILEIO* fio = new FILEIO();
769         if(fio == NULL) return false;
770
771         memset(full_path_cue, 0x00, sizeof(full_path_cue));
772         image_tmp_data_path.clear();
773
774         get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
775         
776         _TCHAR *parent_dir = get_parent_dir(full_path_cue);
777
778         size_t _arg1_ptr;
779         size_t _arg2_ptr;
780         size_t _arg2_ptr_s;
781         size_t _arg3_ptr;
782         size_t _arg3_ptr_s;
783
784         std::string _arg1;
785         std::string _arg2;
786         std::string _arg3;
787         
788         if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
789                 line_buf.clear();
790                 for(int i = 0; i < 100; i++) {
791                         memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
792                 }
793                 int _c;
794                 bool is_eof = false;
795                 int sptr = 0;
796                 while(1) {
797                         line_buf.clear();
798                         int _np = 0;
799                         _c = EOF;
800                         do {
801                                 _c = fio->Fgetc();
802                                 if((_c == '\0') || (_c == '\n') || (_c == EOF)) break;;
803                                 if(_c != '\r') line_buf.push_back((char)_c);
804                         } while(1);
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
809                         ptr = 0;
810                         sptr = 0;
811                         // Tokenize
812                         _arg1.clear();
813                         _arg2.clear();
814                         _arg3.clear();
815                         
816                         ptr = line_buf.find_first_not_of((const char*)" \t");
817                         if(ptr == std::string::npos) {
818                                 goto _n_continue;
819                         }
820                         // Token1
821                         line_buf_shadow = line_buf.substr(ptr);
822
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); });
828
829                         _arg2_ptr = _arg2.find_first_not_of((const char *)" \t");
830
831                         if(_arg2_ptr != std::string::npos) {
832                                 _arg2 = _arg2.substr(_arg2_ptr);
833                         }
834
835                         if(_arg1 == "REM") {
836                                 // REM
837                                 goto _n_continue;
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;
841                                 
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);
846                                 
847                                 image_tmp_data_path.clear();
848                                 image_tmp_data_path = std::string(parent_dir);
849                                 image_tmp_data_path.append(_arg2);
850                                 
851                                 #ifdef _CDROM_DEBUG_LOG
852                                         this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path.c_str());
853                                 #endif
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");
857                                 
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());
862                                 
863                                 // Set image file
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);
867                                         
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);
870                                         
871                                         _arg3_ptr_s = _arg3.find_first_of((const char *)" \t\n");
872                                         _arg3.substr(0, _arg3_ptr_s);
873                                         
874                                         std::transform(_arg3.begin(), _arg3.end(), _arg3.begin(),
875                                                                    [](unsigned char c) -> unsigned char{ return std::toupper(c); });
876                                         
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;
881                                                 
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;
886                                         } else {
887                                                 // ToDo:  another type
888                                         }
889                                         if(track_num < (_nr_num + 1)) track_num = _nr_num + 1;
890                                 } else {
891                                         // ToDo: 20181118 K.Ohta
892                                         nr_current_track = 0;
893                                 }
894                                 goto _n_continue;
895                         } else if(_arg1 == "INDEX") {
896                                 
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;
900                                         
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;
905
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());
910
911                                         switch(index_type) {
912                                         case 0:
913                                                 toc_table[nr_current_track].index0 = get_frames_from_msf(_arg3.c_str());
914                                                 break;
915                                         case 1:
916                                                 toc_table[nr_current_track].index1 = get_frames_from_msf(_arg3.c_str());
917                                                 break;
918                                         default:
919                                                 break;
920                                         }
921                                         goto _n_continue;
922                                 } else {
923                                         goto _n_continue;
924                                 }
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);
929                                         
930                                         toc_table[nr_current_track].pregap = get_frames_from_msf(_arg2.c_str());
931                                         goto _n_continue;
932                                 } else {
933                                         goto _n_continue;
934                                 }
935                         }
936                 _n_continue:
937                         if(is_eof) break;
938                         line_buf.clear();
939                         continue;
940                 }
941                 // Finish
942                 max_logical_block = 0;
943                 if(track_num > 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;
947                         // P1: Calc
948                         int _n = 0;
949                         for(int i = 1; i < track_num; i++) {
950
951                                 if(fio_img->IsOpened()) {
952                                         fio_img->Fclose();
953                                 }
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;
960                                                 }
961                                         }
962                                 } else {
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;
966                                         } else {
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;
970                                                 }
971                                         }
972                                 }                                       
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;
977                                                 }
978                                         }
979                                 }
980                                 toc_table[i].lba_size = _n;
981                                 toc_table[i].lba_offset = max_logical_block - _n;
982
983                                 
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]);
989                                 #endif
990                         }
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;
995                         
996                 }
997                 fio->Fclose();
998         }
999         delete fio;
1000
1001         is_cue = false;
1002         if(track_num > 0) is_cue = true;
1003         // Not Cue FILE.
1004         return is_cue;
1005 }
1006
1007 void SCSI_CDROM::open(const _TCHAR* file_path)
1008 {
1009         _TCHAR img_file_path[_MAX_PATH];
1010         
1011         close();
1012         
1013         // ToDo: Process multi track cue file.Most of CDROMs contain both audio and data and more than 2 tracks.
1014         // 20181014 K.O
1015         if(check_file_extension(file_path, _T(".cue"))) {
1016 #if 1
1017                 is_cue = false;
1018                 current_track = 0;
1019                 open_cue_file(file_path);
1020 #else
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));
1031                                         }
1032                                 }
1033                         }
1034                 }
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) {
1038                                 // read cue file
1039                                 FILEIO* fio = new FILEIO();
1040                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1041                                         char line[1024], *ptr;
1042                                         int track = -1;
1043                                         while(fio->Fgets(line, 1024) != NULL) {
1044                                                 if(strstr(line, "FILE") != NULL) {
1045                                                         // do nothing
1046                                                 } else if((ptr = strstr(line, "TRACK")) != NULL) {
1047                                                         // "TRACK 01 AUDIO"
1048                                                         // "TRACK 02 MODE1/2352"
1049                                                         ptr += 6;
1050                                                         while(*ptr == ' ' || *ptr == 0x09) {
1051                                                                 ptr++;
1052                                                         }
1053                                                         if((track = atoi(ptr)) > 0) {
1054                                                                 if(track > track_num) {
1055                                                                         track_num = track;
1056                                                                 }
1057                                                                 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
1058                                                         }
1059                                                 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
1060                                                         // "PREGAP 00:02:00"
1061                                                         if(track > 0) {
1062                                                                 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
1063                                                         }
1064                                                 } else if((ptr = strstr(line, "INDEX")) != NULL) {
1065                                                         // "INDEX 01 00:00:00"
1066                                                         if(track > 0) {
1067                                                                 ptr += 6;
1068                                                                 while(*ptr == ' ' || *ptr == 0x09) {
1069                                                                         ptr++;
1070                                                                 }
1071                                                                 int num = atoi(ptr);
1072                                                                 while(*ptr >= '0' && *ptr <= '9') {
1073                                                                         ptr++;
1074                                                                 }
1075                                                                 if(num == 0) {
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);
1079                                                                 }
1080                                                         }
1081                                                 }
1082                                         }
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;
1089                                                         }
1090                                                 }
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;
1093                                         } else {
1094                                                 fio_img->Fclose();
1095                                         }
1096                                         fio->Fclose();
1097                                 }
1098                                 delete fio;
1099                         }
1100                 }
1101 #endif
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));
1109                         }
1110                 }
1111                 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1112                         is_cue = false;
1113                         current_track = 0;
1114                         // get image file size
1115                         if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1116                                 // read cue file
1117                                 FILEIO* fio = new FILEIO();
1118                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1119                                         char line[1024], *ptr;
1120                                         int track = -1;
1121                                         while(fio->Fgets(line, 1024) != NULL) {
1122                                                 if(strstr(line, "[Session ") != NULL) {
1123                                                         track = -1;
1124                                                 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1125                                                         if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1126                                                                 if(track > track_num) {
1127                                                                         track_num = track;
1128                                                                 }
1129                                                         }
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);
1133                                                         }
1134                                                 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1135                                                         if(track > 0 && track < 0xa0) {
1136                                                                 toc_table[track - 1].pregap = atoi(ptr + 6);
1137                                                         }
1138                                                 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1139                                                         if(track > 0 && track < 0xa0) {
1140                                                                 toc_table[track - 1].index1 = atoi(ptr + 5);
1141                                                         }
1142                                                 }
1143                                         }
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;
1151                                                 }
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;
1154                                                 if(track_num > 0) {
1155                                                         toc_table[track_num].lba_size = max_logical_block - toc_table[track_num - 1].lba_offset;
1156                                                 } else {
1157                                                         toc_table[track_num].lba_size = 0;
1158                                                 }
1159                                         } else {
1160                                                 fio_img->Fclose();
1161                                         }
1162                                         fio->Fclose();
1163                                 }
1164                                 delete fio;
1165                         }
1166                 }
1167         }
1168 #ifdef _SCSI_DEBUG_LOG
1169         if(mounted()) {
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);
1178                 }
1179         }
1180 #endif
1181 }
1182
1183 void SCSI_CDROM::close()
1184 {
1185         if(fio_img->IsOpened()) {
1186                 fio_img->Fclose();
1187         }
1188         memset(toc_table, 0, sizeof(toc_table));
1189         track_num = 0;
1190         is_cue = false;
1191         current_track = 0;
1192         set_cdda_status(CDDA_OFF);
1193 }
1194
1195 bool SCSI_CDROM::mounted()
1196 {
1197         if(is_cue) return true;
1198         return fio_img->IsOpened();
1199 }
1200
1201 bool SCSI_CDROM::accessed()
1202 {
1203         bool value = access;
1204         access = false;
1205         return value;
1206 }
1207
1208 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1209 {
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;
1217                         }
1218                         cdda_sample_l = tmp_l / mix_loop_num;
1219                         cdda_sample_r = tmp_r / mix_loop_num;
1220                 }
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);
1223                 
1224                 for(int i = 0; i < cnt; i++) {
1225                         *buffer++ += val_l; // L
1226                         *buffer++ += val_r; // R
1227                 }
1228         }
1229 }
1230
1231 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1232 {
1233         volume_l = decibel_to_volume(decibel_l);
1234         volume_r = decibel_to_volume(decibel_r);
1235 }
1236
1237 void SCSI_CDROM::set_volume(int volume)
1238 {
1239         volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1240 }
1241
1242 #define STATE_VERSION   3
1243
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)
1247 {
1248         uint32_t offset = 0;
1249         
1250         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1251                 return false;
1252         }
1253         if(!state_fio->StateCheckInt32(this_device_id)) {
1254                 return false;
1255         }
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);
1270         if(loading) {
1271                 offset = state_fio->FgetUint32_LE();
1272         } else {
1273                 if(fio_img->IsOpened()) {
1274                         offset = fio_img->Ftell();
1275                 }
1276                 state_fio->FputUint32_LE(offset);
1277         }
1278         
1279         state_fio->StateValue(is_cue);
1280         state_fio->StateValue(current_track);
1281         // ToDo: Re-Open Image.20181118 K.O
1282         // post process
1283         if(loading && fio_img->IsOpened()) {
1284                 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1285         }
1286         return SCSI_DEV::process_state(state_fio, loading);
1287 }