OSDN Git Service

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