OSDN Git Service

[GENERAL] Merge upstream 2017-03-04. Still checking FTBFSs.
[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
17 // 0-99 is reserved for SCSI_DEV class
18 #define EVENT_CDDA      100
19
20 void SCSI_CDROM::initialize()
21 {
22         SCSI_DEV::initialize();
23         fio_img = new FILEIO();
24         
25         if(44100 % emu->get_sound_rate() == 0) {
26                 mix_loop_num = 44100 / emu->get_sound_rate();
27         } else {
28                 mix_loop_num = 0;
29         }
30         event_cdda = -1;
31         cdda_status = CDDA_OFF;
32 }
33
34 void SCSI_CDROM::release()
35 {
36         if(fio_img->IsOpened()) {
37                 fio_img->Fclose();
38         }
39         delete fio_img;
40         SCSI_DEV::release();
41 }
42
43 void SCSI_CDROM::reset()
44 {
45         touch_sound();
46         SCSI_DEV::reset();
47         set_cdda_status(CDDA_OFF);
48 }
49
50 uint32_t SCSI_CDROM::read_signal(int id)
51 {
52         switch(id) {
53         case SIG_SCSI_CDROM_PLAYING:
54                 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
55                 
56         case SIG_SCSI_CDROM_SAMPLE_L:
57                 return (uint32_t)abs(cdda_sample_l);
58                 
59         case SIG_SCSI_CDROM_SAMPLE_R:
60                 return (uint32_t)abs(cdda_sample_r);
61         }
62         return SCSI_DEV::read_signal(id);
63 }
64
65 void SCSI_CDROM::event_callback(int event_id, int err)
66 {
67         switch (event_id) {
68         case EVENT_CDDA:
69                 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
70                 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
71                 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
72                 
73                 if((cdda_buffer_ptr += 4) % 2352 == 0) {
74                         // one frame finished
75                         if(++cdda_playing_frame == cdda_end_frame) {
76                                 // reached to end frame
77                                 if(cdda_repeat) {
78                                         // reload buffer
79                                         fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
80                                         fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
81                                         cdda_buffer_ptr = 0;
82                                         cdda_playing_frame = cdda_start_frame;
83                                 } else {
84                                         // stop
85                                         if(cdda_interrupt) {
86                                                 write_signals(&outputs_done, 0xffffffff);
87                                         }
88                                         set_cdda_status(CDDA_OFF);
89                                 }
90                         } else if(cdda_buffer_ptr == array_length(cdda_buffer)) {
91                                 // refresh buffer
92                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
93                                 cdda_buffer_ptr = 0;
94                         }
95                 }
96                 break;
97         default:
98                 SCSI_DEV::event_callback(event_id, err);
99                 break;
100         }
101 }
102
103 void SCSI_CDROM::set_cdda_status(uint8_t status)
104 {
105         if(status == CDDA_PLAYING) {
106                 if(mix_loop_num == 0) {
107                         if(event_cdda == -1) {
108                                 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
109                         }
110                 }
111                 if(cdda_status != CDDA_PLAYING) {
112                         touch_sound();
113                         set_realtime_render(this, true);
114                 }
115         } else {
116                 if(event_cdda != -1) {
117                         cancel_event(this, event_cdda);
118                         event_cdda = -1;
119                 }
120                 if(cdda_status == CDDA_PLAYING) {
121                         touch_sound();
122                         set_realtime_render(this, false);
123                 }
124         }
125         cdda_status = status;
126 }
127
128 void SCSI_CDROM::reset_device()
129 {
130         set_cdda_status(CDDA_OFF);
131         SCSI_DEV::reset_device();
132 }
133
134 bool SCSI_CDROM::is_device_ready()
135 {
136         return is_disc_inserted();
137 }
138
139 int SCSI_CDROM::get_command_length(int value)
140 {
141         switch(value) {
142         case 0xd8:
143         case 0xd9:
144         case 0xda:
145         case 0xdd:
146         case 0xde:
147                 return 10;
148         }
149         return SCSI_DEV::get_command_length(value);
150 }
151
152 int SCSI_CDROM::get_track(uint32_t lba)
153 {
154         int track = 0;
155         
156         for(int i = 0; i < track_num; i++) {
157                 if(lba >= toc_table[i].index0) {
158                         track = i;
159                 } else {
160                         break;
161                 }
162         }
163         return track;
164 }
165
166 double SCSI_CDROM::get_seek_time(uint32_t lba)
167 {
168         if(fio_img->IsOpened()) {
169                 uint32_t cur_position = (int)fio_img->Ftell();
170                 int distance = abs((int)(lba * physical_block_size) - (int)cur_position);
171                 double ratio = (double)distance / 333000 / physical_block_size; // 333000: sectors in media
172                 return max(10, (int)(400000 * 2 * ratio));
173         } else {
174                 return 400000; // 400msec
175         }
176 }
177
178 uint32_t lba_to_msf(uint32_t lba)
179 {
180         uint8_t m = lba / (60 * 75);
181         lba -= m * (60 * 75);
182         uint8_t s = lba / 75;
183         uint8_t f = lba % 75;
184
185         return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
186 }
187
188 uint32_t lba_to_msf_alt(uint32_t lba)
189 {
190         uint32_t ret = 0;
191         ret |= ((lba / (60 * 75)) & 0xff) << 16;
192         ret |= (((lba / 75) % 60) & 0xff) <<  8;
193         ret |= ((lba % 75)        & 0xff) <<  0;
194         return ret;
195 }
196
197 void SCSI_CDROM::start_command()
198 {
199         touch_sound();
200         switch(command[0]) {
201         case SCSI_CMD_READ6:
202                 seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
203                 set_cdda_status(CDDA_OFF);
204                 break;
205                 
206         case SCSI_CMD_READ10:
207         case SCSI_CMD_READ12:
208                 seek_time = 10;//get_seek_time(command[2] * 0x1000000 + command[3] * 0x10000 + command[4] * 0x100 + command[5]);
209                 set_cdda_status(CDDA_OFF);
210                 break;
211                 
212         case 0xd8:
213                 #ifdef _SCSI_DEBUG_LOG
214                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback Start Position\n"), scsi_id);
215                 #endif
216                 if(is_device_ready()) {
217                         if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
218                                 // stop cd-da if all params are zero
219                                 cdda_start_frame = 0;
220                                 cdda_end_frame = toc_table[track_num].index0; // end of disc
221                                 set_cdda_status(CDDA_OFF);
222                         } else {
223                                 switch(command[9] & 0xc0) {
224                                 case 0x00:
225                                         cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
226                                         break;
227                                 case 0x40:
228                                         {
229                                                 uint8_t m = FROM_BCD(command[2]);
230                                                 uint8_t s = FROM_BCD(command[3]);
231                                                 uint8_t f = FROM_BCD(command[4]);
232                                                 cdda_start_frame = f + 75 * (s + m * 60);
233                                                 
234                                                 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
235                                                 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
236                                                 int track =get_track(cdda_start_frame);
237                                                 cdda_start_frame -= toc_table[track].pregap;
238                                                 
239                                                 if(cdda_start_frame < toc_table[track].index1) {
240                                                         cdda_start_frame = toc_table[track].index1; // don't play pregap
241                                                 } else if(cdda_start_frame > (max_logical_block_addr + 1)) {
242                                                         cdda_start_frame = 0;
243                                                 }
244                                         }
245                                         break;
246                                 case 0x80:
247                                         cdda_start_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
248                                         break;
249                                 default:
250                                         cdda_start_frame = 0;
251                                         break;
252                                 }
253 //                              if(cdda_status == CDDA_PAUSED) {
254 //                                      cdda_end_frame = toc_table[track_num].index0; // end of disc
255 //                                      set_cdda_status(CDDA_OFF);
256 //                              } else
257                                 if((command[1] & 3) != 0) {
258                                         cdda_end_frame = toc_table[track_num].index0; // end of disc
259                                         set_cdda_status(CDDA_PLAYING);
260                                 } else {
261                                         cdda_end_frame = toc_table[get_track(cdda_start_frame) + 1].index1; // end of this track
262                                         set_cdda_status(CDDA_PAUSED);
263                                 }
264                                 cdda_repeat = false;
265                                 cdda_interrupt = ((command[1] & 3) == 2);
266                                 
267                                 // read buffer
268                                 double seek_time = get_seek_time(cdda_start_frame);
269                                 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
270                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
271                                 cdda_buffer_ptr = 0;
272                                 cdda_playing_frame = cdda_start_frame;
273                                 
274                                 // change to status phase
275                                 set_dat(SCSI_STATUS_GOOD);
276                                 set_phase_delay(SCSI_PHASE_STATUS, seek_time);
277                                 return;
278                         }
279                 }
280                 // change to status phase
281                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
282                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
283                 return;
284                 
285         case 0xd9:
286                 #ifdef _SCSI_DEBUG_LOG
287                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback End Position\n"), scsi_id);
288                 #endif
289                 if(is_device_ready()) {
290                         switch(command[9] & 0xc0) {
291                         case 0x00:
292                                 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
293                                 break;
294                         case 0x40:
295                                 {
296                                         uint8_t m = FROM_BCD(command[2]);
297                                         uint8_t s = FROM_BCD(command[3]);
298                                         uint8_t f = FROM_BCD(command[4]);
299                                         cdda_end_frame = f + 75 * (s + m * 60);
300                                         
301                                         // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
302                                         // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
303                                         int track =get_track(cdda_end_frame);
304                                         
305                                         if(cdda_end_frame > toc_table[track].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track].index1) {
306                                                 cdda_end_frame = toc_table[track].index1;
307                                         }
308                                 }
309                                 break;
310                         case 0x80:
311                                 cdda_end_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
312                                 break;
313                         }
314                         if((command[1] & 3) != 0) {
315                                 set_cdda_status(CDDA_PLAYING);
316                                 cdda_repeat = ((command[1] & 3) == 1);
317                                 cdda_interrupt = ((command[1] & 3) == 2);
318                         } else {
319                                 set_cdda_status(CDDA_OFF);
320                         }
321                 }
322                 // change to status phase
323                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
324                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
325                 return;
326                 
327         case 0xda:
328                 #ifdef _SCSI_DEBUG_LOG
329                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
330                 #endif
331                 if(is_device_ready()) {
332                         if(cdda_status == CDDA_PLAYING) {
333                                 set_cdda_status(CDDA_OFF);
334                         }
335                 }
336                 // change to status phase
337                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
338                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
339                 return;
340                 
341         case 0xdd:
342                 #ifdef _SCSI_DEBUG_LOG
343                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
344                 #endif
345                 if(is_device_ready()) {
346                         // create track info
347                         uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
348                         uint32_t msf_abs = lba_to_msf_alt(frame);
349                         int track = get_track(frame);
350                         uint32_t msf_rel = lba_to_msf_alt(frame - toc_table[track].index0);
351                         buffer->clear();
352                         buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
353                         buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
354                         buffer->write(TO_BCD(track + 1));               // Track
355                         buffer->write(0x01);                            // Index
356                         buffer->write(TO_BCD((msf_rel >> 16) & 0xff));  // M (relative)
357                         buffer->write(TO_BCD((msf_rel >>  8) & 0xff));  // S (relative)
358                         buffer->write(TO_BCD((msf_rel >>  0) & 0xff));  // F (relative)
359                         buffer->write(TO_BCD((msf_abs >> 16) & 0xff));  // M (absolute)
360                         buffer->write(TO_BCD((msf_abs >>  8) & 0xff));  // S (absolute)
361                         buffer->write(TO_BCD((msf_abs >>  0) & 0xff));  // F (absolute)
362                         // transfer length
363                         remain = buffer->count();
364                         // set first data
365                         set_dat(buffer->read());
366                         // change to data in phase
367                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
368                 } else {
369                         // change to status phase
370                         set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
371                         set_phase_delay(SCSI_PHASE_STATUS, 10.0);
372                 }
373                 return;
374                 
375         case 0xde:
376                 #ifdef _SCSI_DEBUG_LOG
377                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
378                 #endif
379                 if(is_device_ready()) {
380                         buffer->clear();
381                         switch(command[1]) {
382                         case 0x00:      /* Get first and last track numbers */
383                                 buffer->write(TO_BCD(1));
384                                 buffer->write(TO_BCD(track_num));
385                                 break;
386                         case 0x01:      /* Get total disk size in MSF format */
387                                 {
388                                         uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
389                                         buffer->write((msf >> 16) & 0xff);
390                                         buffer->write((msf >>  8) & 0xff);
391                                         buffer->write((msf >>  0) & 0xff);
392                                 }
393                                 break;
394                         case 0x02:      /* Get track information */
395                                 if(command[2] == 0xaa) {
396                                         uint32_t msf = lba_to_msf(toc_table[track_num].index0 + 150);
397                                         buffer->write((msf >> 16) & 0xff);
398                                         buffer->write((msf >>  8) & 0xff);
399                                         buffer->write((msf >>  0) & 0xff);
400                                         buffer->write(0x04); // correct ?
401                                 } else {
402                                         int track = max(FROM_BCD(command[2]), 1);
403                                         uint32_t frame = toc_table[track - 1].index0;
404                                         // PCE wants the start sector for data tracks to *not* include the pregap
405                                         if(!toc_table[track - 1].is_audio) {
406                                                 frame += toc_table[track - 1].pregap;
407                                         }
408                                         uint32_t msf = lba_to_msf(toc_table[track - 1].index1 + 150);
409                                         buffer->write((msf >> 16) & 0xff); // M
410                                         buffer->write((msf >>  8) & 0xff); // S
411                                         buffer->write((msf >>  0) & 0xff); // F
412                                         buffer->write(toc_table[track - 1].is_audio ? 0x00 : 0x04);
413                                 }
414                                 break;
415                         }
416                         // transfer length
417                         remain = buffer->count();
418                         // set first data
419                         set_dat(buffer->read());
420                         // change to data in phase
421                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
422                 } else {
423                         // change to status phase
424                         set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
425                         set_phase_delay(SCSI_PHASE_STATUS, 10.0);
426                 }
427                 return;
428         }
429         
430         // start standard command
431         SCSI_DEV::start_command();
432 }
433
434 void SCSI_CDROM::read_buffer(int length)
435 {
436         if(fio_img->IsOpened()) {
437                 uint32_t offset = (uint32_t)(position % 2352);
438                 
439                 fio_img->Fseek((long)position, FILEIO_SEEK_SET);
440                 while(length > 0) {
441                         uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
442                         int tmp_length = min(length, (int)sizeof(tmp_buffer));
443                         
444                         fio_img->Fread(tmp_buffer, tmp_length, 1);
445                         for(int i = 0; i < tmp_length && length > 0; i++) {
446                                 if(offset >= 16 && offset < 16 + 2048) {
447                                         int value = tmp_buffer[i];
448                                         buffer->write(value);
449                                         length--;
450                                 }
451                                 position++;
452                                 offset = (offset + 1) % 2352;
453                         }
454                 }
455         }
456 }
457
458 int get_frames_from_msf(const char *string)
459 {
460         const char *ptr = string;
461         int frames[3] = {0};
462         int index = 0;
463         
464         while(1) {
465                 if(*ptr >= '0' && *ptr <= '9') {
466                         frames[index] = frames[index] * 10 + (*ptr - '0');
467                 } else if(*ptr == ':') {
468                         if(++index == 3) {
469                                 // abnormal data
470                                 break;
471                         }
472                 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
473                         // end of line
474                         break;
475                 }
476                 ptr++;
477         }
478         return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
479 }
480
481 int hexatoi(const char *string)
482 {
483         const char *ptr = string;
484         int value = 0;
485         
486         while(1) {
487                 if(*ptr >= '0' && *ptr <= '9') {
488                         value = value * 16 + (*ptr - '0');
489                 } else if(*ptr >= 'a' && *ptr <= 'f') {
490                         value = value * 16 + (*ptr - 'a' + 10);
491                 } else if(*ptr >= 'A' && *ptr <= 'F') {
492                         value = value * 16 + (*ptr - 'A' + 10);
493                 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
494                         break;
495                 }
496                 ptr++;
497         }
498         return value;
499 }
500
501 void SCSI_CDROM::open_disc(const _TCHAR* file_path)
502 {
503         _TCHAR img_file_path[_MAX_PATH];
504         
505         close_disc();
506         
507         if(check_file_extension(file_path, _T(".cue"))) {
508                 // get image file name
509                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
510                 if(!FILEIO::IsFileExisting(img_file_path)) {
511                         my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
512                 }
513                 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
514                         // get image file size
515                         if((max_logical_block_addr = fio_img->FileLength() / 2352) > 0) {
516                                 max_logical_block_addr--;
517                         }
518                         if(max_logical_block_addr > 0) {
519                                 // read cue file
520                                 FILEIO* fio = new FILEIO();
521                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
522                                         char line[1024], *ptr;
523                                         int track = -1;
524                                         while(fio->Fgets(line, 1024) != NULL) {
525                                                 if(strstr(line, "FILE") != NULL) {
526                                                         // do nothing
527                                                 } else if((ptr = strstr(line, "TRACK")) != NULL) {
528                                                         // "TRACK 01 AUDIO"
529                                                         // "TRACK 02 MODE1/2352"
530                                                         ptr += 6;
531                                                         while(*ptr == ' ' || *ptr == 0x09) {
532                                                                 ptr++;
533                                                         }
534                                                         if((track = atoi(ptr)) > 0) {
535                                                                 if(track > track_num) {
536                                                                         track_num = track;
537                                                                 }
538                                                                 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
539                                                         }
540                                                 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
541                                                         // "PREGAP 00:02:00"
542                                                         if(track > 0) {
543                                                                 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
544                                                         }
545                                                 } else if((ptr = strstr(line, "INDEX")) != NULL) {
546                                                         // "INDEX 01 00:00:00"
547                                                         if(track > 0) {
548                                                                 ptr += 6;
549                                                                 while(*ptr == ' ' || *ptr == 0x09) {
550                                                                         ptr++;
551                                                                 }
552                                                                 int num = atoi(ptr);
553                                                                 while(*ptr >= '0' && *ptr <= '9') {
554                                                                         ptr++;
555                                                                 }
556                                                                 if(num == 0) {
557                                                                         toc_table[track - 1].index0 = get_frames_from_msf(ptr);
558                                                                 } else if(num == 1) {
559                                                                         toc_table[track - 1].index1 = get_frames_from_msf(ptr);
560                                                                 }
561                                                         }
562                                                 }
563                                         }
564                                         if(track_num != 0) {
565                                                 for(int i = 1; i < track_num; i++) {
566                                                         if(toc_table[i].index0 == 0) {
567                                                                 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
568                                                         } else if(toc_table[i].pregap == 0) {
569                                                                 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
570                                                         }
571                                                 }
572                                                 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
573                                                 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block_addr + 1;
574                                         } else {
575                                                 fio_img->Fclose();
576                                         }
577                                         fio->Fclose();
578                                 }
579                                 delete fio;
580                         }
581                 }
582         } else if(check_file_extension(file_path, _T(".ccd"))) {
583                 // get image file name
584                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
585                 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
586                         // get image file size
587                         if((max_logical_block_addr = fio_img->FileLength() / 2352) > 0) {
588                                 max_logical_block_addr--;
589                         }
590                         if(max_logical_block_addr > 0) {
591                                 // read cue file
592                                 FILEIO* fio = new FILEIO();
593                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
594                                         char line[1024], *ptr;
595                                         int track = -1;
596                                         while(fio->Fgets(line, 1024) != NULL) {
597                                                 if(strstr(line, "[Session ") != NULL) {
598                                                         track = -1;
599                                                 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
600                                                         if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
601                                                                 if(track > track_num) {
602                                                                         track_num = track;
603                                                                 }
604                                                         }
605                                                 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
606                                                         if(track > 0 && track < 0xa0) {
607                                                                 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
608                                                         }
609                                                 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
610                                                         if(track > 0 && track < 0xa0) {
611                                                                 toc_table[track - 1].pregap = atoi(ptr + 6);
612                                                         }
613                                                 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
614                                                         if(track > 0 && track < 0xa0) {
615                                                                 toc_table[track - 1].index1 = atoi(ptr + 5);
616                                                         }
617                                                 }
618                                         }
619                                         if(track_num != 0) {
620                                                 for(int i = 1; i < track_num; i++) {
621                                                         toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
622                                                 }
623                                                 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
624                                                 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block_addr + 1;
625                                         } else {
626                                                 fio_img->Fclose();
627                                         }
628                                         fio->Fclose();
629                                 }
630                                 delete fio;
631                         }
632                 }
633         }
634 #ifdef _SCSI_DEBUG_LOG
635         if(is_disc_inserted()) {
636                 for(int i = 0; i < track_num + 1; i++) {
637                         uint32_t idx0_msf = lba_to_msf(toc_table[i].index0);
638                         uint32_t idx1_msf = lba_to_msf(toc_table[i].index1);
639                         uint32_t pgap_msf = lba_to_msf(toc_table[i].pregap);
640                         this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGpap=%02x:%02x:%02x\n"), i + 1,
641                         (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
642                         (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
643                         (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
644                 }
645         }
646 #endif
647 }
648
649 void SCSI_CDROM::close_disc()
650 {
651         if(fio_img->IsOpened()) {
652                 fio_img->Fclose();
653         }
654         memset(toc_table, 0, sizeof(toc_table));
655         track_num = 0;
656         set_cdda_status(CDDA_OFF);
657 }
658
659 bool SCSI_CDROM::is_disc_inserted()
660 {
661         return fio_img->IsOpened();
662 }
663
664 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
665 {
666         if(cdda_status == CDDA_PLAYING) {
667                 if(mix_loop_num != 0) {
668                         int tmp_l = 0, tmp_r = 0;
669                         for(int i = 0; i < mix_loop_num; i++) {
670                                 event_callback(EVENT_CDDA, 0);
671                                 tmp_l += cdda_sample_l;
672                                 tmp_r += cdda_sample_r;
673                         }
674                         cdda_sample_l = tmp_l / mix_loop_num;
675                         cdda_sample_r = tmp_r / mix_loop_num;
676                 }
677                 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
678                 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
679                 
680                 for(int i = 0; i < cnt; i++) {
681                         *buffer++ += val_l; // L
682                         *buffer++ += val_r; // R
683                 }
684         }
685 }
686
687 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
688 {
689         volume_l = decibel_to_volume(decibel_l);
690         volume_r = decibel_to_volume(decibel_r);
691 }
692
693 void SCSI_CDROM::set_volume(int volume)
694 {
695         volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
696 }
697
698 #define STATE_VERSION   2
699
700 void SCSI_CDROM::save_state(FILEIO* state_fio)
701 {
702         state_fio->FputUint32(STATE_VERSION);
703         state_fio->FputInt32(this_device_id);
704         
705         state_fio->FputUint32(cdda_start_frame);
706         state_fio->FputUint32(cdda_end_frame);
707         state_fio->FputUint32(cdda_playing_frame);
708         state_fio->FputUint8(cdda_status);
709         state_fio->FputBool(cdda_repeat);
710         state_fio->FputBool(cdda_interrupt);
711         state_fio->Fwrite(cdda_buffer, sizeof(cdda_buffer), 1);
712         state_fio->FputInt32(cdda_buffer_ptr);
713         state_fio->FputInt32(cdda_sample_l);
714         state_fio->FputInt32(cdda_sample_r);
715         state_fio->FputInt32(event_cdda);
716 //      state_fio->FputInt32(mix_loop_num);
717         state_fio->FputInt32(volume_m);
718         if(fio_img->IsOpened()) {
719                 state_fio->FputUint32(fio_img->Ftell());
720         } else {
721                 state_fio->FputUint32(0);
722         }
723         SCSI_DEV::save_state(state_fio);
724 }
725
726 bool SCSI_CDROM::load_state(FILEIO* state_fio)
727 {
728         if(state_fio->FgetUint32() != STATE_VERSION) {
729                 return false;
730         }
731         if(state_fio->FgetInt32() != this_device_id) {
732                 return false;
733         }
734         cdda_start_frame = state_fio->FgetUint32();
735         cdda_end_frame = state_fio->FgetUint32();
736         cdda_playing_frame = state_fio->FgetUint32();
737         cdda_status = state_fio->FgetUint8();
738         cdda_repeat = state_fio->FgetBool();
739         cdda_interrupt = state_fio->FgetBool();
740         state_fio->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
741         cdda_buffer_ptr = state_fio->FgetInt32();
742         cdda_sample_l = state_fio->FgetInt32();
743         cdda_sample_r = state_fio->FgetInt32();
744         event_cdda = state_fio->FgetInt32();
745 //      mix_loop_num = state_fio->FgetInt32();
746         volume_m = state_fio->FgetInt32();
747         uint32_t offset = state_fio->FgetUint32();
748         
749         // post process
750         if(fio_img->IsOpened()) {
751                 fio_img->Fseek(offset, FILEIO_SEEK_SET);
752         }
753         return SCSI_DEV::load_state(state_fio);
754 }
755