OSDN Git Service

[VM][SCSI_CDROM][PCENGINE] Implement *correctness* CUE parser.Some softwares still...
[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
21 void SCSI_CDROM::initialize()
22 {
23         SCSI_DEV::initialize();
24         fio_img = new FILEIO();
25         
26         if(44100 % emu->get_sound_rate() == 0) {
27                 mix_loop_num = 44100 / emu->get_sound_rate();
28         } else {
29                 mix_loop_num = 0;
30         }
31         event_cdda = -1;
32         cdda_status = CDDA_OFF;
33         is_cue = false;
34         current_track = 0;
35         for(int i = 0; i < 99; i++) {
36                 memset(track_data_path[i], 0x00, _MAX_PATH * sizeof(_TCHAR));
37         }
38 }
39
40 void SCSI_CDROM::release()
41 {
42         if(fio_img->IsOpened()) {
43                 fio_img->Fclose();
44         }
45         delete fio_img;
46         SCSI_DEV::release();
47 }
48
49 void SCSI_CDROM::reset()
50 {
51         touch_sound();
52         SCSI_DEV::reset();
53         set_cdda_status(CDDA_OFF);
54         // Q: Does not seek to track 0? 20181118 K.O
55         //current_track = 0;
56 }
57
58 uint32_t SCSI_CDROM::read_signal(int id)
59 {
60         switch(id) {
61         case SIG_SCSI_CDROM_PLAYING:
62                 return (cdda_status == CDDA_PLAYING && cdda_interrupt) ? 0xffffffff : 0;
63                 
64         case SIG_SCSI_CDROM_SAMPLE_L:
65                 return (uint32_t)abs(cdda_sample_l);
66                 
67         case SIG_SCSI_CDROM_SAMPLE_R:
68                 return (uint32_t)abs(cdda_sample_r);
69         }
70         return SCSI_DEV::read_signal(id);
71 }
72
73 void SCSI_CDROM::event_callback(int event_id, int err)
74 {
75         switch (event_id) {
76         case EVENT_CDDA:
77                 // read 16bit 2ch samples in the cd-da buffer, called 44100 times/sec
78                 cdda_sample_l = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 0] + cdda_buffer[cdda_buffer_ptr + 1] * 0x100);
79                 cdda_sample_r = (int)(int16_t)(cdda_buffer[cdda_buffer_ptr + 2] + cdda_buffer[cdda_buffer_ptr + 3] * 0x100);
80                 
81                 if((cdda_buffer_ptr += 4) % 2352 == 0) {
82                         // one frame finished
83                         if(++cdda_playing_frame == cdda_end_frame) {
84                                 // reached to end frame
85                                 if(cdda_repeat) {
86                                         // reload buffer
87                                         fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
88                                         fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
89                                         cdda_buffer_ptr = 0;
90                                         cdda_playing_frame = cdda_start_frame;
91                                         access = true;
92                                 } else {
93                                         // stop
94                                         if(cdda_interrupt) {
95                                                 write_signals(&outputs_done, 0xffffffff);
96                                         }
97                                         set_cdda_status(CDDA_OFF);
98                                 }
99                         } else if(cdda_buffer_ptr == array_length(cdda_buffer)) {
100                                 // refresh buffer
101                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
102                                 cdda_buffer_ptr = 0;
103                                 access = true;
104                         }
105                 }
106                 break;
107         default:
108                 SCSI_DEV::event_callback(event_id, err);
109                 break;
110         }
111 }
112
113 void SCSI_CDROM::set_cdda_status(uint8_t status)
114 {
115         if(status == CDDA_PLAYING) {
116                 if(mix_loop_num == 0) {
117                         if(event_cdda == -1) {
118                                 register_event(this, EVENT_CDDA, 1000000.0 / 44100.0, true, &event_cdda);
119                         }
120                 }
121                 if(cdda_status != CDDA_PLAYING) {
122                         touch_sound();
123                         set_realtime_render(this, true);
124                 }
125         } else {
126                 //if(status == CDDA_OFF) {
127                 //      if((status != cdda_status) && (is_cue)) {
128                 //              if(fio_img->IsOpened()) fio_img->Fclose();
129                 //      }
130                 //}
131                 if(event_cdda != -1) {
132                         cancel_event(this, event_cdda);
133                         event_cdda = -1;
134                 }
135                 if(cdda_status == CDDA_PLAYING) {
136                         touch_sound();
137                         set_realtime_render(this, false);
138                 }
139         }
140         cdda_status = status;
141 }
142
143 void SCSI_CDROM::reset_device()
144 {
145         set_cdda_status(CDDA_OFF);
146         SCSI_DEV::reset_device();
147 }
148
149 bool SCSI_CDROM::is_device_ready()
150 {
151         return mounted();
152 }
153
154 int SCSI_CDROM::get_command_length(int value)
155 {
156         switch(value) {
157         case 0xd8:
158         case 0xd9:
159         case 0xda:
160         case 0xdd:
161         case 0xde:
162                 return 10;
163         }
164         return SCSI_DEV::get_command_length(value);
165 }
166
167 void SCSI_CDROM::get_track_by_track_num(int track)
168 {
169         if((track <= 0) || (track > track_num)) {
170                 if(is_cue) {
171                         if(fio_img->IsOpened()) fio_img->Fclose();
172                 }
173                 if(track <= 0) current_track = 0;
174                 if(track >= track_num)current_track = track_num;
175                 return;
176         }
177         if(is_cue) {
178                 // ToDo: Apply audio with some codecs.
179                 if(current_track != track) {
180                         if(fio_img != NULL) {
181                                 if(fio_img->IsOpened()) {
182                                         fio_img->Fclose();
183                                 }
184                                 current_track = track;
185                         }
186                 #ifdef _CDROM_DEBUG_LOG
187                         this->out_debug_log(_T("LOAD TRK #%02d from %s\n"), track, track_data_path[track - 1]);
188                 #endif
189                         
190                         if((track > 0) && (track < 100) && (track <= track_num)) {
191                                 if((strlen(track_data_path[track - 1]) <= 0) ||
192                                    !(fio_img->Fopen(track_data_path[track - 1], FILEIO_READ_BINARY))) {
193                                         track = 0;
194                                 }
195                         } else {
196                                 track = 0;
197                         }
198                 }
199         }
200 }
201
202 int SCSI_CDROM::get_track(uint32_t lba)
203 {
204         int track = 0;
205
206         if(is_cue) {
207                 for(int i = 1; i <= track_num; i++) {
208                         if(lba >= toc_table[i - 1].lba_offset) {
209                                 track = i;
210                         } else {
211                                 break;
212                         }
213                 }
214         } else {
215                 for(int i = 0; i < track_num; i++) {
216                         if(lba >= toc_table[i].index0) {
217                                 track = i;
218                         } else {
219                                 break;
220                         }
221                 }
222         }
223         if(is_cue) {
224                 get_track_by_track_num(track);
225         } else {
226                 current_track = track;
227         }
228         return track;
229 }
230
231 double SCSI_CDROM::get_seek_time(uint32_t lba)
232 {
233         if(fio_img->IsOpened()) {
234                 uint32_t cur_position = (int)fio_img->Ftell();
235                 int distance = abs((int)(lba * physical_block_size()) - (int)cur_position);
236                 double ratio = (double)distance / 333000 / physical_block_size(); // 333000: sectors in media
237                 return max(10, (int)(400000 * 2 * ratio));
238         } else {
239                 return 400000; // 400msec
240         }
241 }
242
243 uint32_t SCSI_CDROM::lba_to_msf(int trk, uint32_t lba)
244 {
245         if(is_cue) {
246                 if(trk > 0) {
247                         lba = lba + toc_table[trk - 1].lba_offset;
248                 }
249         }
250         uint8_t m = lba / (60 * 75);
251         lba -= m * (60 * 75);
252         uint8_t s = lba / 75;
253         uint8_t f = lba % 75;
254
255         return ((m / 10) << 20) | ((m % 10) << 16) | ((s / 10) << 12) | ((s % 10) << 8) | ((f / 10) << 4) | ((f % 10) << 0);
256 }
257
258 uint32_t SCSI_CDROM::lba_to_msf_alt(int trk, uint32_t lba)
259 {
260         if(is_cue) {
261                 if(trk > 0) {
262                         lba = lba + toc_table[trk - 1].lba_offset;
263                 }
264         }
265         uint32_t ret = 0;
266         ret |= ((lba / (60 * 75)) & 0xff) << 16;
267         ret |= (((lba / 75) % 60) & 0xff) <<  8;
268         ret |= ((lba % 75)        & 0xff) <<  0;
269         return ret;
270 }
271
272 void SCSI_CDROM::start_command()
273 {
274         touch_sound();
275         #ifdef _SCSI_DEBUG_LOG
276         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: #%02x %02x %02x %02x %02x %02x\n"), scsi_id, command[0], command[1], command[2], command[3], command[4], command[5]);
277         #endif
278         switch(command[0]) {
279         case SCSI_CMD_READ6:
280                 seek_time = 10;//get_seek_time((command[1] & 0x1f) * 0x10000 + command[2] * 0x100 + command[3]);
281                 set_cdda_status(CDDA_OFF);
282                 break;
283                 
284         case SCSI_CMD_READ10:
285         case SCSI_CMD_READ12:
286                 seek_time = 10;//get_seek_time(command[2] * 0x1000000 + command[3] * 0x10000 + command[4] * 0x100 + command[5]);
287                 set_cdda_status(CDDA_OFF);
288                 break;
289                 
290         case 0xd8:
291                 #ifdef _SCSI_DEBUG_LOG
292                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback Start Position\n"), scsi_id);
293                 #endif
294                 if(is_device_ready()) {
295                         if(command[2] == 0 && command[3] == 0 && command[4] == 0) {
296                                 // stop cd-da if all params are zero
297                                 cdda_start_frame = 0;
298                                 cdda_end_frame = toc_table[track_num].index0; // end of disc
299                                 if(is_cue) {
300                                         get_track_by_track_num(track_num);
301                                 }
302                                 set_cdda_status(CDDA_OFF);
303                         } else {
304                                 uint32_t seek_offset = 0;
305                                 switch(command[9] & 0xc0) {
306                                 case 0x00:
307                                         cdda_start_frame = (command[2] << 16) | (command[3] << 8) | command[4];
308                                         break;
309                                 case 0x40:
310                                         {
311                                                 uint8_t m = FROM_BCD(command[2]);
312                                                 uint8_t s = FROM_BCD(command[3]);
313                                                 uint8_t f = FROM_BCD(command[4]);
314                                                 cdda_start_frame = f + 75 * (s + m * 60);
315                                                 
316                                                 // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
317                                                 // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
318                                                 int track =get_track(cdda_start_frame);
319                                                 if((is_cue) && (track > 1)) {
320                                                         seek_offset = toc_table[current_track - 1].lba_offset;
321                                                         if(cdda_start_frame >= toc_table[track - 1].lba_offset) {
322                                                                 cdda_start_frame = cdda_start_frame - toc_table[track - 1].lba_offset;
323                                                         } else {
324                                                                 cdda_start_frame = 0;
325                                                         }
326                                                 }
327                                                 cdda_start_frame -= toc_table[track].pregap;
328                                                 
329                                                 if(cdda_start_frame < toc_table[track].index1) {
330                                                         cdda_start_frame = toc_table[track].index1; // don't play pregap
331                                                 } else if(cdda_start_frame > max_logical_block) {
332                                                         cdda_start_frame = 0;
333                                                 }
334                                         }
335                                         break;
336                                 case 0x80:
337                                         if(is_cue) {
338                                                 int trk = FROM_BCD(command[2]) - 1;
339                                                 seek_offset = toc_table[current_track - 1].lba_offset;
340                                                 if(trk != current_track) {
341                                                         get_track_by_track_num(trk);
342                                                 }
343                                         }
344                                         cdda_start_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
345                                         break;
346                                 default:
347                                         cdda_start_frame = 0;
348                                         break;
349                                 }
350 //                              if(cdda_status == CDDA_PAUSED) {
351 //                                      cdda_end_frame = toc_table[track_num].index0; // end of disc
352 //                                      set_cdda_status(CDDA_OFF);
353 //                              } else
354                                 if((command[1] & 3) != 0) {
355                                         if((is_cue) && (current_track != track_num)){
356                                                 seek_offset = toc_table[current_track - 1].lba_offset;
357                                                 get_track(toc_table[track_num].lba_offset + 1);
358                                         }
359                                         cdda_end_frame = toc_table[track_num].index0; // end of disc
360                                         set_cdda_status(CDDA_PLAYING);
361                                 } else {
362                                         uint32_t _sframe = cdda_start_frame;
363                                         int _trk;
364                                         if((is_cue) && (current_track > 0)) {
365                                                 seek_offset = toc_table[current_track - 1].lba_offset;
366                                                 _sframe = _sframe + toc_table[current_track - 1].lba_offset;
367                                         }
368                                         cdda_end_frame = toc_table[get_track(_sframe) + 1].index1; // end of this track
369                                         set_cdda_status(CDDA_PAUSED);
370                                 }
371                                 cdda_repeat = false;
372                                 cdda_interrupt = ((command[1] & 3) == 2);
373                                 
374                                 // read buffer
375                                 double seek_time = get_seek_time(cdda_start_frame);
376                                 fio_img->Fseek(cdda_start_frame * 2352, FILEIO_SEEK_SET);
377                                 fio_img->Fread(cdda_buffer, sizeof(cdda_buffer), 1);
378                                 cdda_buffer_ptr = 0;
379                                 cdda_playing_frame = cdda_start_frame;
380                                 access = true;
381                                 
382                                 // change to status phase
383                                 set_dat(SCSI_STATUS_GOOD);
384                                 set_phase_delay(SCSI_PHASE_STATUS, seek_time);
385                                 return;
386                         }
387                 }
388                 // change to status phase
389                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
390                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
391                 return;
392                 
393         case 0xd9:
394                 #ifdef _SCSI_DEBUG_LOG
395                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Set Audio Playback End Position\n"), scsi_id);
396                 #endif
397                 if(is_device_ready()) {
398                         switch(command[9] & 0xc0) {
399                         case 0x00:
400                                 cdda_end_frame = (command[3] << 16) | (command[4] << 8) | command[5];
401                                 break;
402                         case 0x40:
403                                 {
404                                         uint8_t m = FROM_BCD(command[2]);
405                                         uint8_t s = FROM_BCD(command[3]);
406                                         uint8_t f = FROM_BCD(command[4]);
407                                         cdda_end_frame = f + 75 * (s + m * 60);
408                                         
409                                         // PCE tries to be clever here and set (start of track + track pregap size) to skip the pregap
410                                         // (I guess it wants the TOC to have the real start sector for data tracks and the start of the pregap for audio?)
411                                         int track =get_track(cdda_end_frame);
412                                         if((is_cue) && (track > 1)) {
413                                                 if(cdda_end_frame >= toc_table[track - 1].lba_offset) {
414                                                         cdda_end_frame = cdda_end_frame - toc_table[track - 1].lba_offset;
415                                                 } else {
416                                                         cdda_end_frame = 0;
417                                                 }
418                                         }
419                                         
420                                         if(cdda_end_frame > toc_table[track].index1 && (cdda_end_frame - toc_table[track].pregap) <= toc_table[track].index1) {
421                                                 cdda_end_frame = toc_table[track].index1;
422                                         }
423                                 }
424                                 break;
425                         case 0x80:
426                                 cdda_end_frame = toc_table[FROM_BCD(command[2]) - 1].index1;
427                                 break;
428                         }
429                         if((command[1] & 3) != 0) {
430                                 set_cdda_status(CDDA_PLAYING);
431                                 cdda_repeat = ((command[1] & 3) == 1);
432                                 cdda_interrupt = ((command[1] & 3) == 2);
433                         } else {
434                                 set_cdda_status(CDDA_OFF);
435                         }
436                 }
437                 // change to status phase
438                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
439                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
440                 return;
441                 
442         case 0xda:
443                 #ifdef _SCSI_DEBUG_LOG
444                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Pause\n"), scsi_id);
445                 #endif
446                 if(is_device_ready()) {
447                         if(cdda_status == CDDA_PLAYING) {
448                                 set_cdda_status(CDDA_OFF);
449                         }
450                 }
451                 if(is_cue) { // NEC PC-ENGINE ONLY.This expect to be temporally workaround.20181119 K.O
452                         get_track_by_track_num(2);
453                 }
454                 // change to status phase
455                 set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
456                 set_phase_delay(SCSI_PHASE_STATUS, 10.0);
457                 return;
458                 
459         case 0xdd:
460                 #ifdef _SCSI_DEBUG_LOG
461                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Read Sub Channel Q\n"), scsi_id);
462                 #endif
463                 if(is_device_ready()) {
464                         // create track info
465                         uint32_t frame = (cdda_status == CDDA_OFF) ? cdda_start_frame : cdda_playing_frame;
466                         uint32_t msf_abs = lba_to_msf_alt(current_track, frame);
467                         int track;
468                         if(is_cue) {
469                                 //track = get_track(frame + ((current_track > 0) ? toc_table[current_track - 1].lba_offset : 0));
470                                 track = current_track;
471                         } else {
472                                 track = get_track(frame);
473                         }
474                         uint32_t msf_rel = lba_to_msf_alt(track, frame - toc_table[track].index0);
475                         buffer->clear();
476                         buffer->write((cdda_status == CDDA_PLAYING) ? 0x00 : (cdda_status == CDDA_PAUSED) ? 0x02 : 0x03);
477                         buffer->write(0x01 | (toc_table[track].is_audio ? 0x00 : 0x40));
478                         buffer->write(TO_BCD(track + 1));               // Track
479                         buffer->write(0x01);                            // Index
480                         buffer->write(TO_BCD((msf_rel >> 16) & 0xff));  // M (relative)
481                         buffer->write(TO_BCD((msf_rel >>  8) & 0xff));  // S (relative)
482                         buffer->write(TO_BCD((msf_rel >>  0) & 0xff));  // F (relative)
483                         buffer->write(TO_BCD((msf_abs >> 16) & 0xff));  // M (absolute)
484                         buffer->write(TO_BCD((msf_abs >>  8) & 0xff));  // S (absolute)
485                         buffer->write(TO_BCD((msf_abs >>  0) & 0xff));  // F (absolute)
486                         // transfer length
487                         remain = buffer->count();
488                         // set first data
489                         set_dat(buffer->read());
490                         // change to data in phase
491                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
492                 } else {
493                         // change to status phase
494                         set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
495                         set_phase_delay(SCSI_PHASE_STATUS, 10.0);
496                 }
497                 return;
498                 
499         case 0xde:
500                 #ifdef _SCSI_DEBUG_LOG
501                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] Command: NEC Get Dir Info\n"), scsi_id);
502                 #endif
503                 if(is_device_ready()) {
504                         buffer->clear();
505                 #ifdef _SCSI_DEBUG_LOG
506                         this->out_debug_log(_T("[SCSI_DEV:ID=%d] CMD=%02x ARG=%02x \n"), scsi_id, command[1], command[2]);
507                 #endif
508                         switch(command[1]) {
509                         case 0x00:      /* Get first and last track numbers */
510                                 buffer->write(TO_BCD(1));
511                                 buffer->write(TO_BCD(track_num));
512                                 break;
513                         case 0x01:      /* Get total disk size in MSF format */
514                                 {
515                                         uint32_t msf = lba_to_msf(track_num, toc_table[track_num].index0 + 150);
516                                         buffer->write((msf >> 16) & 0xff);
517                                         buffer->write((msf >>  8) & 0xff);
518                                         buffer->write((msf >>  0) & 0xff);
519                                 }
520                                 break;
521                         case 0x02:      /* Get track information */
522                                 if(command[2] == 0xaa) {
523                                         uint32_t msf = lba_to_msf(track_num, toc_table[track_num].index0 + 150);
524                                         buffer->write((msf >> 16) & 0xff);
525                                         buffer->write((msf >>  8) & 0xff);
526                                         buffer->write((msf >>  0) & 0xff);
527                                         buffer->write(0x04); // correct ?
528                                 } else {
529                                         int track = max(FROM_BCD(command[2]), 1);
530                                         uint32_t frame = toc_table[track - 1].index0;
531                                         // PCE wants the start sector for data tracks to *not* include the pregap
532                                         if(!toc_table[track - 1].is_audio) {
533                                                 frame += toc_table[track - 1].pregap;
534                                         }
535                                         get_track_by_track_num(track);
536                                         
537                                         uint32_t msf = lba_to_msf(track - 1, toc_table[track - 1].index1 + 150);
538                                         buffer->write((msf >> 16) & 0xff); // M
539                                         buffer->write((msf >>  8) & 0xff); // S
540                                         buffer->write((msf >>  0) & 0xff); // F
541                                         buffer->write(toc_table[track - 1].is_audio ? 0x00 : 0x04);
542                                 }
543                                 break;
544                         }
545                         // transfer length
546                         remain = buffer->count();
547                         // set first data
548                         set_dat(buffer->read());
549                         // change to data in phase
550                         set_phase_delay(SCSI_PHASE_DATA_IN, 10.0);
551                 } else {
552                         // change to status phase
553                         set_dat(is_device_ready() ? SCSI_STATUS_GOOD : SCSI_STATUS_CHKCOND);
554                         set_phase_delay(SCSI_PHASE_STATUS, 10.0);
555                 }
556                 return;
557         }
558         
559         // start standard command
560         SCSI_DEV::start_command();
561 }
562
563 bool SCSI_CDROM::read_buffer(int length)
564 {
565         if(!fio_img->IsOpened()) {
566                 set_sense_code(SCSI_SENSE_NOTREADY);
567                 return false;
568         }
569         uint32_t offset = (uint32_t)(position % 2352);
570         
571         if(fio_img->Fseek((long)position, FILEIO_SEEK_SET) != 0) {
572                 set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_SEEKERR
573                 return false;
574         }
575         while(length > 0) {
576                 uint8_t tmp_buffer[SCSI_BUFFER_SIZE];
577                 int tmp_length = min(length, (int)sizeof(tmp_buffer));
578                 
579                 if(fio_img->Fread(tmp_buffer, tmp_length, 1) != 1) {
580                         set_sense_code(SCSI_SENSE_ILLGLBLKADDR); //SCSI_SENSE_NORECORDFND
581                         return false;
582                 }
583                 for(int i = 0; i < tmp_length && length > 0; i++) {
584                         if(offset >= 16 && offset < 16 + 2048) {
585                                 int value = tmp_buffer[i];
586                                 buffer->write(value);
587                                 length--;
588                         }
589                         position++;
590                         offset = (offset + 1) % 2352;
591                 }
592                 access = true;
593         }
594         set_sense_code(SCSI_SENSE_NOSENSE);
595         return true;
596 }
597
598 int get_frames_from_msf(const char *string)
599 {
600         const char *ptr = string;
601         int frames[3] = {0};
602         int index = 0;
603         
604         while(1) {
605                 if(*ptr >= '0' && *ptr <= '9') {
606                         frames[index] = frames[index] * 10 + (*ptr - '0');
607                 } else if(*ptr == ':') {
608                         if(++index == 3) {
609                                 // abnormal data
610                                 break;
611                         }
612                 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
613                         // end of line
614                         break;
615                 }
616                 ptr++;
617         }
618         return (frames[0] * 60 + frames[1]) * 75 + frames[2]; // 75frames/sec
619 }
620
621 int hexatoi(const char *string)
622 {
623         const char *ptr = string;
624         int value = 0;
625         
626         while(1) {
627                 if(*ptr >= '0' && *ptr <= '9') {
628                         value = value * 16 + (*ptr - '0');
629                 } else if(*ptr >= 'a' && *ptr <= 'f') {
630                         value = value * 16 + (*ptr - 'a' + 10);
631                 } else if(*ptr >= 'A' && *ptr <= 'F') {
632                         value = value * 16 + (*ptr - 'A' + 10);
633                 } else if(*ptr == '\r' || *ptr == '\n' || *ptr == '\0') {
634                         break;
635                 }
636                 ptr++;
637         }
638         return value;
639 }
640
641 bool SCSI_CDROM::open_cue_file(const _TCHAR* file_path)
642 {
643
644         _TCHAR line_buf[2048];
645         _TCHAR line_buf_shadow[2048];
646         _TCHAR full_path_cue[_MAX_PATH];
647         _TCHAR image_tmp_data_path[_MAX_PATH];
648         int ptr;
649         int line_count = 0;
650         int slen;
651         int nr_current_track = 0;
652         FILEIO* fio = new FILEIO();
653         if(fio == NULL) return false;
654
655         memset(full_path_cue, 0x00, sizeof(full_path_cue));
656         memset(image_tmp_data_path, 0x00, sizeof(image_tmp_data_path));
657
658         get_long_full_path_name(file_path, full_path_cue, sizeof(full_path_cue));
659         
660         _TCHAR *parent_dir = get_parent_dir(full_path_cue);     
661         if(fio->Fopen(file_path, FILEIO_READ_ASCII)) { // ToDo: Support not ASCII cue file (i.e. SJIS/UTF8).20181118 K.O
662                 memset(line_buf, 0x00, sizeof(line_buf));
663
664                 for(int i = 0; i < 100; i++) {
665                         memset(&(track_data_path[i][0]), 0x00, _MAX_PATH * sizeof(_TCHAR));
666                 }
667                 int _c;
668                 bool is_eof = false;
669                 int sptr = 0;
670                 while(1) {
671                         memset(line_buf, 0x00, sizeof(line_buf));
672                         int _np = 0;
673                         _c = EOF;
674                         do {
675                                 _c = fio->Fgetc();
676                                 if((_c == '\n') || (_c == EOF) || (_np >= 2048)) break;
677                                 if(_c != '\r') line_buf[_np++] = (_TCHAR)_c;
678                         } while(1);
679                         if(_c == EOF) is_eof = true;
680                         slen = strlen(line_buf);
681                         if(slen <= 0) goto _n_continue;
682                         // Trim head of Space or TAB
683                         ptr = 0;
684                         sptr = 0;
685                         while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
686                                 ptr++;
687                                 if(ptr >= slen) break;
688                         }
689                         if(ptr > slen) {
690                                 goto _n_continue;
691                         }
692                         memset(line_buf_shadow, 0x00, sizeof(line_buf_shadow));
693                         while((line_buf[ptr] != ' ') && (line_buf[ptr] != '\0') && (line_buf[ptr] != '\t')) {
694                                 line_buf_shadow[sptr++] = (_TCHAR)(toupper(line_buf[ptr++]));
695                                 if(ptr >= slen) break;
696                         }
697                         if((ptr > slen) || (sptr == 0)) {
698                                 goto _n_continue;
699                         }
700                         // Command
701                         if(strcmp(line_buf_shadow, "REM") == 0) {
702                                 // REM
703                                 goto _n_continue;
704                         } else if(strcmp(line_buf_shadow, "FILE") == 0) {
705                                 while((line_buf[ptr] != '"') && (line_buf[ptr] != '\0')) {
706                                         ptr++;
707                                         if(ptr > slen) break;
708                                 }
709                                 if(ptr >= slen) {
710                                         goto _n_continue;
711                                 }
712                                 // ARG1
713                                 ptr++;
714                                 if(ptr >= slen) goto _n_continue;
715                                 sptr = 0;
716                                 _TCHAR _tmps[_MAX_PATH];
717                                 memset(_tmps, 0x00, sizeof(_tmps));
718                                 while((line_buf[ptr] != '"') && (line_buf[ptr] != '\0')) {
719                                         _tmps[sptr++] = line_buf[ptr++];
720                                         if(ptr >= slen) break;
721                                 }
722                                 if(ptr >= slen) {
723                                         goto _n_continue;
724                                 }
725                                 if((sptr != 0) && (line_buf[ptr] == '"')) {
726                                         strncpy(image_tmp_data_path, parent_dir, _MAX_PATH);
727                                         strncat(image_tmp_data_path, _tmps, _MAX_PATH - 1);
728                                         #ifdef _SCSI_DEBUG_LOG
729                                                 this->out_debug_log(_T("**FILE %s\n"), image_tmp_data_path);
730                                         #endif
731 //                                      goto _n_continue; 
732                                 }
733                                 goto _n_continue; // ToDo: Check ARG2 (BINARY etc..) 20181118 K.O
734                         } else if(strcmp(line_buf_shadow, "TRACK") == 0) {
735                                 while((line_buf[ptr] == ' ') || (line_buf[ptr] == '\t')) {
736                                         ptr++;
737                                         if(ptr >= slen) break;
738                                 }
739                                 if(ptr > slen) goto _n_continue;
740                                 int _nr_num = 0;
741                                 _TCHAR _tmps[128];
742                                 int _n = 0;
743                                 memset(_tmps, 0x00, sizeof(_tmps));
744                                 while((line_buf[ptr] != ' ') && (line_buf[ptr] != '\t') && (line_buf[ptr] != '\0')) {
745                                         if(ptr >= slen) break;
746                                         _tmps[_n++] = line_buf[ptr++];
747                                 }
748                                 _nr_num = atoi(_tmps);
749                                 ptr++;
750                                 // Set image file
751                                 if((_nr_num > 0) && (_nr_num < 100)) {
752                                         nr_current_track = _nr_num;
753                                         if(strlen(image_tmp_data_path) > 0) {
754                                                 strncpy(track_data_path[_nr_num - 1], image_tmp_data_path, _MAX_PATH);
755                                                 // ARG2
756                                                 while((line_buf[ptr] == ' ') || (line_buf[ptr] == '\t')) {
757                                                         ptr++;
758                                                         if(ptr >= slen) break;
759                                                 }
760                                                 memset(_tmps, 0x00, sizeof(_tmps));
761                                                 for(int i = 0; i < 127; i++) {
762                                                         _tmps[i] = (_TCHAR)(toupper(line_buf[ptr++]));
763                                                         if(ptr > slen) break;
764                                                 }
765
766                                                 toc_table[nr_current_track - 1].is_audio = false;
767                                                 toc_table[nr_current_track - 1].index0 = 0;
768                                                 toc_table[nr_current_track - 1].index1 = 0;
769                                                 toc_table[nr_current_track - 1].pregap = 0;
770                                                 
771                                                 if(strcmp(_tmps, "AUDIO") == 0) {
772                                                         toc_table[nr_current_track - 1].is_audio = true;
773                                                 } else if(strcmp(_tmps, "MODE1/2352") == 0) {
774                                                         toc_table[nr_current_track - 1].is_audio = false;
775                                                 } else {
776                                                         // ToDo: Another data mode 20181118 K.Ohta
777                                                         memset(track_data_path[_nr_num - 1], 0x00, _MAX_PATH * sizeof(_TCHAR));
778                                                 }
779                                                 if(track_num < _nr_num) track_num = _nr_num;
780                                         }
781                                 } else {
782                                         // ToDo: 20181118 K.Ohta
783                                         nr_current_track = 0;
784                                 }
785                                 goto _n_continue;
786                         } else if(strcmp(line_buf_shadow, "INDEX") == 0) {
787
788                                 if((nr_current_track > 0) && (nr_current_track < 100)) {
789                                         while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
790                                                 ptr++;
791                                                 if(ptr >= slen) break;
792                                         }
793                                         // ARG1
794                                         int index_type = 0;
795                                         _TCHAR _tmps[32];
796                                         memset(_tmps, 0x00, sizeof(_tmps));
797                                         for(int i = 0; i < 31; i++) {
798                                                 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) break;
799                                                 _tmps[i] = line_buf[ptr++];
800                                                 if(ptr > slen) break;
801                                         }
802                                         index_type = atoi(_tmps);
803                                         if(ptr >= slen) goto _n_continue;
804                                         
805                                         // ARG2
806                                         while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
807                                                 ptr++;
808                                                 if(ptr > slen) break;
809                                         }
810                                         memset(_tmps, 0x00, sizeof(_tmps));
811                                         for(int i = 0; i < 31; i++) {
812                                                 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) {
813                                                         if(line_buf[ptr] != ':') break;
814                                                 }
815                                                 _tmps[i] = line_buf[ptr++];
816                                                 if(ptr > slen) break;
817                                         }
818
819                                         switch(index_type) {
820                                         case 0:
821                                                 toc_table[nr_current_track - 1].index0 = get_frames_from_msf(_tmps);
822                                                 break;
823                                         case 1:
824                                                 toc_table[nr_current_track - 1].index1 = get_frames_from_msf(_tmps);
825                                                 break;
826                                         default:
827                                                 break;
828                                         }
829                                         goto _n_continue;
830                                 } else {
831                                         goto _n_continue;
832                                 }
833                         } else if(strcmp(line_buf_shadow, "PREGAP") == 0) {
834                                 if((nr_current_track > 0) && (nr_current_track < 100)) {
835                                         while((line_buf[ptr] == '\t') || (line_buf[ptr] == ' ')) {
836                                                 ptr++;
837                                                 if(ptr >= slen) break;
838                                         }
839                                         // ARG1
840                                         _TCHAR _tmps[32];
841                                         memset(_tmps, 0x00, sizeof(_tmps));
842                                         for(int i = 0; i < 31; i++) {
843                                                 if((line_buf[ptr] < '0') || (line_buf[ptr] > '9')) {
844                                                         if(line_buf[ptr] != ':') break;
845                                                 }
846                                                 _tmps[i] = line_buf[ptr++];
847                                                 if(ptr >= slen) goto _n_continue;
848                                         }
849                                         toc_table[nr_current_track - 1].pregap = get_frames_from_msf(_tmps);
850                                         goto _n_continue;
851                                 } else {
852                                         goto _n_continue;
853                                 }
854                         }
855                 _n_continue:
856                         if(is_eof) break;
857                         memset(line_buf, 0x00, sizeof(line_buf));
858                         continue;
859                 }
860                 // Finish
861                 if(track_num > 0) {
862                         toc_table[0].lba_offset = 0;
863                         for(int i = 1; i < track_num; i++) {
864                                            
865                                 if(toc_table[i].index0 == 0) {
866                                         toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
867                                 } else if(toc_table[i].pregap == 0) {
868                                         toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
869                                 }
870                                 if(fio_img->IsOpened()) {
871                                         fio_img->Fclose();
872                                 }
873                                 // ToDo: Support unreguler (!CDDA, i.e. MP3/WAV...) codec.20181118 K.O
874                                 int _n = 0;
875                                 if(strlen(track_data_path[i - 1]) > 0) {
876                                         if(fio_img->Fopen(track_data_path[i - 1], FILEIO_READ_BINARY)) {
877                                                 if((_n = fio_img->FileLength() / 2352) > 0) {
878                                                         max_logical_block += _n;
879                                                 }
880                                         }
881                                 }
882                                 toc_table[i].lba_offset = toc_table[i - 1].lba_offset + _n;
883                                 #ifdef _CDROM_DEBUG_LOG
884                                 this->out_debug_log(_T("TRACK#%02d TYPE=%s PREGAP=%d INDEX0=%d INDEX1=%d LBA_SIZE=%d LBA_OFFSET=%d PATH=%s\n"),
885                                                                         i, (toc_table[i].is_audio) ? _T("AUDIO") : _T("MODE1/2352"),
886                                                                         toc_table[i].pregap, toc_table[i].index0, toc_table[i].index1,
887                                                                         _n, toc_table[i].lba_offset, track_data_path[i - 1]);
888                                 #endif
889                         }
890                         toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
891                         //toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
892                         toc_table[track_num].lba_offset = max_logical_block;
893                 }
894                 fio->Fclose();
895         }
896         delete fio;
897
898         is_cue = false;
899         if(track_num > 0) is_cue = true;
900         // Not Cue FILE.
901         return is_cue;
902 }
903
904 void SCSI_CDROM::open(const _TCHAR* file_path)
905 {
906         _TCHAR img_file_path[_MAX_PATH];
907         
908         close();
909         
910         // ToDo: Process multi track cue file.Most of CDROMs contain both audio and data and more than 2 tracks.
911         // 20181014 K.O
912         if(check_file_extension(file_path, _T(".cue"))) {
913 #if 1
914                 is_cue = true;
915                 current_track = 0;
916                 open_cue_file(file_path);
917 #else
918                 // get image file name
919                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
920                 if(!FILEIO::IsFileExisting(img_file_path)) {
921                         my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin"), get_file_path_without_extensiton(file_path));
922                         if(!FILEIO::IsFileExisting(img_file_path)) {
923                                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
924                                 if(!FILEIO::IsFileExisting(img_file_path)) {
925                                         my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
926                                         if(!FILEIO::IsFileExisting(img_file_path)) {
927                                                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.bin.gz"), get_file_path_without_extensiton(file_path));
928                                         }
929                                 }
930                         }
931                 }
932                 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
933                         // get image file size
934                         if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
935                                 // read cue file
936                                 FILEIO* fio = new FILEIO();
937                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
938                                         char line[1024], *ptr;
939                                         int track = -1;
940                                         while(fio->Fgets(line, 1024) != NULL) {
941                                                 if(strstr(line, "FILE") != NULL) {
942                                                         // do nothing
943                                                 } else if((ptr = strstr(line, "TRACK")) != NULL) {
944                                                         // "TRACK 01 AUDIO"
945                                                         // "TRACK 02 MODE1/2352"
946                                                         ptr += 6;
947                                                         while(*ptr == ' ' || *ptr == 0x09) {
948                                                                 ptr++;
949                                                         }
950                                                         if((track = atoi(ptr)) > 0) {
951                                                                 if(track > track_num) {
952                                                                         track_num = track;
953                                                                 }
954                                                                 toc_table[track - 1].is_audio = (strstr(line, "AUDIO") != NULL);
955                                                         }
956                                                 } else if((ptr = strstr(line, "PREGAP")) != NULL) {
957                                                         // "PREGAP 00:02:00"
958                                                         if(track > 0) {
959                                                                 toc_table[track - 1].pregap = get_frames_from_msf(ptr + 7);
960                                                         }
961                                                 } else if((ptr = strstr(line, "INDEX")) != NULL) {
962                                                         // "INDEX 01 00:00:00"
963                                                         if(track > 0) {
964                                                                 ptr += 6;
965                                                                 while(*ptr == ' ' || *ptr == 0x09) {
966                                                                         ptr++;
967                                                                 }
968                                                                 int num = atoi(ptr);
969                                                                 while(*ptr >= '0' && *ptr <= '9') {
970                                                                         ptr++;
971                                                                 }
972                                                                 if(num == 0) {
973                                                                         toc_table[track - 1].index0 = get_frames_from_msf(ptr);
974                                                                 } else if(num == 1) {
975                                                                         toc_table[track - 1].index1 = get_frames_from_msf(ptr);
976                                                                 }
977                                                         }
978                                                 }
979                                         }
980                                         if(track_num != 0) {
981                                                 for(int i = 1; i < track_num; i++) {
982                                                         if(toc_table[i].index0 == 0) {
983                                                                 toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
984                                                         } else if(toc_table[i].pregap == 0) {
985                                                                 toc_table[i].pregap = toc_table[i].index1 - toc_table[i].index0;
986                                                         }
987                                                 }
988                                                 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
989                                                 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
990                                         } else {
991                                                 fio_img->Fclose();
992                                         }
993                                         fio->Fclose();
994                                 }
995                                 delete fio;
996                         }
997                 }
998 #endif
999         } else if(check_file_extension(file_path, _T(".ccd"))) {
1000                 // get image file name
1001                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img"), get_file_path_without_extensiton(file_path));
1002                 if(!FILEIO::IsFileExisting(img_file_path)) {
1003                         my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.gz"), get_file_path_without_extensiton(file_path));
1004                         if(!FILEIO::IsFileExisting(img_file_path)) {
1005                                 my_stprintf_s(img_file_path, _MAX_PATH, _T("%s.img.gz"), get_file_path_without_extensiton(file_path));
1006                         }
1007                 }
1008                 if(fio_img->Fopen(img_file_path, FILEIO_READ_BINARY)) {
1009                         is_cue = false;
1010                         current_track = 0;
1011                         // get image file size
1012                         if((max_logical_block = fio_img->FileLength() / 2352) > 0) {
1013                                 // read cue file
1014                                 FILEIO* fio = new FILEIO();
1015                                 if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
1016                                         char line[1024], *ptr;
1017                                         int track = -1;
1018                                         while(fio->Fgets(line, 1024) != NULL) {
1019                                                 if(strstr(line, "[Session ") != NULL) {
1020                                                         track = -1;
1021                                                 } else if((ptr = strstr(line, "Point=0x")) != NULL) {
1022                                                         if((track = hexatoi(ptr + 8)) > 0 && track < 0xa0) {
1023                                                                 if(track > track_num) {
1024                                                                         track_num = track;
1025                                                                 }
1026                                                         }
1027                                                 } else if((ptr = strstr(line, "Control=0x")) != NULL) {
1028                                                         if(track > 0 && track < 0xa0) {
1029                                                                 toc_table[track - 1].is_audio = (hexatoi(ptr + 10) != 4);
1030                                                         }
1031                                                 } else if((ptr = strstr(line, "ALBA=-")) != NULL) {
1032                                                         if(track > 0 && track < 0xa0) {
1033                                                                 toc_table[track - 1].pregap = atoi(ptr + 6);
1034                                                         }
1035                                                 } else if((ptr = strstr(line, "PLBA=")) != NULL) {
1036                                                         if(track > 0 && track < 0xa0) {
1037                                                                 toc_table[track - 1].index1 = atoi(ptr + 5);
1038                                                         }
1039                                                 }
1040                                         }
1041                                         if(track_num != 0) {
1042                                                 toc_table[0].lba_offset = 0;
1043                                                 for(int i = 1; i < track_num; i++) {
1044                                                         toc_table[i].index0 = toc_table[i].index1 - toc_table[i].pregap;
1045                                                         toc_table[i].lba_offset = toc_table[i].index0;
1046                                                 }
1047                                                 toc_table[0].index0 = toc_table[0].index1 = toc_table[0].pregap = 0;
1048                                                 toc_table[track_num].index0 = toc_table[track_num].index1 = max_logical_block;
1049                                         } else {
1050                                                 fio_img->Fclose();
1051                                         }
1052                                         fio->Fclose();
1053                                 }
1054                                 delete fio;
1055                         }
1056                 }
1057         }
1058 #ifdef _SCSI_DEBUG_LOG
1059         if(mounted()) {
1060                 for(int i = 0; i < track_num + 1; i++) {
1061                         uint32_t idx0_msf = lba_to_msf(i, toc_table[i].index0);
1062                         uint32_t idx1_msf = lba_to_msf(i, toc_table[i].index1);
1063                         uint32_t pgap_msf = lba_to_msf(i, toc_table[i].pregap);
1064                         this->out_debug_log(_T("Track%02d: Index0=%02x:%02x:%02x Index1=%02x:%02x:%02x PreGpap=%02x:%02x:%02x\n"), i + 1,
1065                         (idx0_msf >> 16) & 0xff, (idx0_msf >> 8) & 0xff, idx0_msf & 0xff,
1066                         (idx1_msf >> 16) & 0xff, (idx1_msf >> 8) & 0xff, idx1_msf & 0xff,
1067                         (pgap_msf >> 16) & 0xff, (pgap_msf >> 8) & 0xff, pgap_msf & 0xff);
1068                 }
1069         }
1070 #endif
1071 }
1072
1073 void SCSI_CDROM::close()
1074 {
1075         if(fio_img->IsOpened()) {
1076                 fio_img->Fclose();
1077         }
1078         memset(toc_table, 0, sizeof(toc_table));
1079         track_num = 0;
1080         is_cue = false;
1081         current_track = 0;
1082         set_cdda_status(CDDA_OFF);
1083 }
1084
1085 bool SCSI_CDROM::mounted()
1086 {
1087         if(is_cue) return true;
1088         return fio_img->IsOpened();
1089 }
1090
1091 bool SCSI_CDROM::accessed()
1092 {
1093         bool value = access;
1094         access = false;
1095         return value;
1096 }
1097
1098 void SCSI_CDROM::mix(int32_t* buffer, int cnt)
1099 {
1100         if(cdda_status == CDDA_PLAYING) {
1101                 if(mix_loop_num != 0) {
1102                         int tmp_l = 0, tmp_r = 0;
1103                         for(int i = 0; i < mix_loop_num; i++) {
1104                                 event_callback(EVENT_CDDA, 0);
1105                                 tmp_l += cdda_sample_l;
1106                                 tmp_r += cdda_sample_r;
1107                         }
1108                         cdda_sample_l = tmp_l / mix_loop_num;
1109                         cdda_sample_r = tmp_r / mix_loop_num;
1110                 }
1111                 int32_t val_l = apply_volume(apply_volume(cdda_sample_l, volume_m), volume_l);
1112                 int32_t val_r = apply_volume(apply_volume(cdda_sample_r, volume_m), volume_r);
1113                 
1114                 for(int i = 0; i < cnt; i++) {
1115                         *buffer++ += val_l; // L
1116                         *buffer++ += val_r; // R
1117                 }
1118         }
1119 }
1120
1121 void SCSI_CDROM::set_volume(int ch, int decibel_l, int decibel_r)
1122 {
1123         volume_l = decibel_to_volume(decibel_l);
1124         volume_r = decibel_to_volume(decibel_r);
1125 }
1126
1127 void SCSI_CDROM::set_volume(int volume)
1128 {
1129         volume_m = (int)(1024.0 * (max(0, min(100, volume)) / 100.0));
1130 }
1131
1132 #define STATE_VERSION   3
1133
1134 // Q: If loading state when using another (saved) image? 20181013 K.O
1135 //    May need close() and open() (or ...?).
1136 bool SCSI_CDROM::process_state(FILEIO* state_fio, bool loading)
1137 {
1138         uint32_t offset = 0;
1139         
1140         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
1141                 return false;
1142         }
1143         if(!state_fio->StateCheckInt32(this_device_id)) {
1144                 return false;
1145         }
1146         state_fio->StateValue(cdda_start_frame);
1147         state_fio->StateValue(cdda_end_frame);
1148         state_fio->StateValue(cdda_playing_frame);
1149         state_fio->StateValue(cdda_status);
1150         state_fio->StateValue(cdda_repeat);
1151         state_fio->StateValue(cdda_interrupt);
1152         state_fio->StateArray(cdda_buffer, sizeof(cdda_buffer), 1);
1153         state_fio->StateValue(cdda_buffer_ptr);
1154         state_fio->StateValue(cdda_sample_l);
1155         state_fio->StateValue(cdda_sample_r);
1156         state_fio->StateValue(event_cdda);
1157 //      state_fio->StateValue(mix_loop_num);
1158         state_fio->StateValue(volume_m);
1159         if(loading) {
1160                 offset = state_fio->FgetUint32_LE();
1161         } else {
1162                 if(fio_img->IsOpened()) {
1163                         offset = fio_img->Ftell();
1164                 }
1165                 state_fio->FputUint32_LE(offset);
1166         }
1167         
1168         state_fio->StateValue(is_cue);
1169         state_fio->StateValue(current_track);
1170         // ToDo: Re-Open Image.20181118 K.O
1171         // post process
1172         if(loading && fio_img->IsOpened()) {
1173                 fio_img->Fseek(offset, FILEIO_SEEK_SET);
1174         }
1175         return SCSI_DEV::process_state(state_fio, loading);
1176 }