1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 namespace CDReaderHelpers
30 {
31 
32 #define FILE_ANY_ACCESS 0
33 #ifndef FILE_READ_ACCESS
34  #define FILE_READ_ACCESS 1
35 #endif
36 #ifndef FILE_WRITE_ACCESS
37  #define FILE_WRITE_ACCESS 2
38 #endif
39 
40 #define METHOD_BUFFERED 0
41 #define IOCTL_SCSI_BASE 4
42 #define SCSI_IOCTL_DATA_OUT          0
43 #define SCSI_IOCTL_DATA_IN           1
44 #define SCSI_IOCTL_DATA_UNSPECIFIED  2
45 
46 #define CTL_CODE2(DevType, Function, Method, Access) (((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
47 #define IOCTL_SCSI_PASS_THROUGH_DIRECT  CTL_CODE2( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
48 #define IOCTL_SCSI_GET_ADDRESS          CTL_CODE2( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS )
49 
50 #define SENSE_LEN         14
51 #define SRB_ENABLE_RESIDUAL_COUNT 0x04
52 #define SRB_DIR_IN        0x08
53 #define SRB_DIR_OUT       0x10
54 #define SRB_EVENT_NOTIFY  0x40
55 #define SC_HA_INQUIRY     0x00
56 #define SC_GET_DEV_TYPE   0x01
57 #define SC_EXEC_SCSI_CMD  0x02
58 #define SS_PENDING        0x00
59 #define SS_COMP           0x01
60 #define SS_ERR            0x04
61 
62 enum
63 {
64     READTYPE_ANY = 0,
65     READTYPE_ATAPI1 = 1,
66     READTYPE_ATAPI2 = 2,
67     READTYPE_READ6 = 3,
68     READTYPE_READ10 = 4,
69     READTYPE_READ_D8 = 5,
70     READTYPE_READ_D4 = 6,
71     READTYPE_READ_D4_1 = 7,
72     READTYPE_READ10_2 = 8
73 };
74 
75 struct SCSI_PASS_THROUGH
76 {
77     USHORT Length;
78     UCHAR ScsiStatus;
79     UCHAR PathId;
80     UCHAR TargetId;
81     UCHAR Lun;
82     UCHAR CdbLength;
83     UCHAR SenseInfoLength;
84     UCHAR DataIn;
85     ULONG DataTransferLength;
86     ULONG TimeOutValue;
87     ULONG DataBufferOffset;
88     ULONG SenseInfoOffset;
89     UCHAR Cdb[16];
90 };
91 
92 struct SCSI_PASS_THROUGH_DIRECT
93 {
94     USHORT Length;
95     UCHAR ScsiStatus;
96     UCHAR PathId;
97     UCHAR TargetId;
98     UCHAR Lun;
99     UCHAR CdbLength;
100     UCHAR SenseInfoLength;
101     UCHAR DataIn;
102     ULONG DataTransferLength;
103     ULONG TimeOutValue;
104     PVOID DataBuffer;
105     ULONG SenseInfoOffset;
106     UCHAR Cdb[16];
107 };
108 
109 struct SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
110 {
111     SCSI_PASS_THROUGH_DIRECT spt;
112     ULONG Filler;
113     UCHAR ucSenseBuf[32];
114 };
115 
116 struct SCSI_ADDRESS
117 {
118     ULONG Length;
119     UCHAR PortNumber;
120     UCHAR PathId;
121     UCHAR TargetId;
122     UCHAR Lun;
123 };
124 
125 #pragma pack(1)
126 
127 struct SRB_GDEVBlock
128 {
129     BYTE SRB_Cmd;
130     BYTE SRB_Status;
131     BYTE SRB_HaID;
132     BYTE SRB_Flags;
133     DWORD SRB_Hdr_Rsvd;
134     BYTE SRB_Target;
135     BYTE SRB_Lun;
136     BYTE SRB_DeviceType;
137     BYTE SRB_Rsvd1;
138     BYTE pad[68];
139 };
140 
141 
142 struct SRB_ExecSCSICmd
143 {
144     BYTE SRB_Cmd;
145     BYTE SRB_Status;
146     BYTE SRB_HaID;
147     BYTE SRB_Flags;
148     DWORD SRB_Hdr_Rsvd;
149     BYTE SRB_Target;
150     BYTE SRB_Lun;
151     WORD SRB_Rsvd1;
152     DWORD SRB_BufLen;
153     BYTE *SRB_BufPointer;
154     BYTE SRB_SenseLen;
155     BYTE SRB_CDBLen;
156     BYTE SRB_HaStat;
157     BYTE SRB_TargStat;
158     VOID *SRB_PostProc;
159     BYTE SRB_Rsvd2[20];
160     BYTE CDBByte[16];
161     BYTE SenseArea[SENSE_LEN + 2];
162 };
163 
164 struct SRB
165 {
166     BYTE SRB_Cmd;
167     BYTE SRB_Status;
168     BYTE SRB_HaId;
169     BYTE SRB_Flags;
170     DWORD SRB_Hdr_Rsvd;
171 };
172 
173 struct TOCTRACK
174 {
175     BYTE rsvd;
176     BYTE ADR;
177     BYTE trackNumber;
178     BYTE rsvd2;
179     BYTE addr[4];
180 };
181 
182 struct TOC
183 {
184     WORD tocLen;
185     BYTE firstTrack;
186     BYTE lastTrack;
187     TOCTRACK tracks[100];
188 };
189 
190 #pragma pack()
191 
192 //==============================================================================
193 struct CDDeviceDescription
194 {
CDDeviceDescriptionjuce::CDReaderHelpers::CDDeviceDescription195     CDDeviceDescription()  : ha (0), tgt (0), lun (0), scsiDriveLetter (0)
196     {
197     }
198 
createDescriptionjuce::CDReaderHelpers::CDDeviceDescription199     void createDescription (const char* data)
200     {
201         description << String (data + 8, 8).trim() // vendor
202                     << ' ' << String (data + 16, 16).trim() // product id
203                     << ' ' << String (data + 32, 4).trim(); // rev
204     }
205 
206     String description;
207     BYTE ha, tgt, lun;
208     char scsiDriveLetter; // will be 0 if not using scsi
209 };
210 
211 //==============================================================================
212 class CDReadBuffer
213 {
214 public:
CDReadBuffer(const int numberOfFrames)215     CDReadBuffer (const int numberOfFrames)
216         : startFrame (0), numFrames (0), dataStartOffset (0),
217           dataLength (0), bufferSize (2352 * numberOfFrames), index (0),
218           buffer (bufferSize), wantsIndex (false)
219     {
220     }
221 
isZero() const222     bool isZero() const noexcept
223     {
224         for (int i = 0; i < dataLength; ++i)
225             if (buffer [dataStartOffset + i] != 0)
226                 return false;
227 
228         return true;
229     }
230 
231     int startFrame, numFrames, dataStartOffset;
232     int dataLength, bufferSize, index;
233     HeapBlock<BYTE> buffer;
234     bool wantsIndex;
235 };
236 
237 class CDDeviceHandle;
238 
239 //==============================================================================
240 class CDController
241 {
242 public:
CDController()243     CDController() : initialised (false) {}
~CDController()244     virtual ~CDController() {}
245 
246     virtual bool read (CDReadBuffer&) = 0;
shutDown()247     virtual void shutDown() {}
248 
249     bool readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer = 0);
250     int getLastIndex();
251 
252 public:
253     CDDeviceHandle* deviceInfo;
254     int framesToCheck, framesOverlap;
255     bool initialised;
256 
257     void prepare (SRB_ExecSCSICmd& s);
258     void perform (SRB_ExecSCSICmd& s);
259     void setPaused (bool paused);
260 };
261 
262 
263 //==============================================================================
264 class CDDeviceHandle
265 {
266 public:
CDDeviceHandle(const CDDeviceDescription & device,HANDLE scsiHandle_)267     CDDeviceHandle (const CDDeviceDescription& device, HANDLE scsiHandle_)
268         : info (device), scsiHandle (scsiHandle_), readType (READTYPE_ANY)
269     {
270     }
271 
~CDDeviceHandle()272     ~CDDeviceHandle()
273     {
274         if (controller != nullptr)
275         {
276             controller->shutDown();
277             controller = 0;
278         }
279 
280         if (scsiHandle != 0)
281             CloseHandle (scsiHandle);
282     }
283 
284     bool readTOC (TOC* lpToc);
285     bool readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer = 0);
286     void openDrawer (bool shouldBeOpen);
287     void performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s);
288 
289     CDDeviceDescription info;
290     HANDLE scsiHandle;
291     BYTE readType;
292 
293 private:
294     std::unique_ptr<CDController> controller;
295 
296     bool testController (int readType, CDController* newController, CDReadBuffer& bufferToUse);
297 };
298 
299 //==============================================================================
createSCSIDeviceHandle(const char driveLetter)300 HANDLE createSCSIDeviceHandle (const char driveLetter)
301 {
302     TCHAR devicePath[] = { L'\\', L'\\', L'.', L'\\', static_cast<TCHAR> (driveLetter), L':', 0, 0 };
303     DWORD flags = GENERIC_READ | GENERIC_WRITE;
304     HANDLE h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
305 
306     if (h == INVALID_HANDLE_VALUE)
307     {
308         flags ^= GENERIC_WRITE;
309         h = CreateFile (devicePath, flags, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
310     }
311 
312     return h;
313 }
314 
findCDDevices(Array<CDDeviceDescription> & list)315 void findCDDevices (Array<CDDeviceDescription>& list)
316 {
317     for (char driveLetter = 'b'; driveLetter <= 'z'; ++driveLetter)
318     {
319         TCHAR drivePath[] = { static_cast<TCHAR> (driveLetter), L':', L'\\', 0, 0 };
320 
321         if (GetDriveType (drivePath) == DRIVE_CDROM)
322         {
323             HANDLE h = createSCSIDeviceHandle (driveLetter);
324 
325             if (h != INVALID_HANDLE_VALUE)
326             {
327                 char buffer[100] = { 0 };
328 
329                 SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER p = { 0 };
330                 p.spt.Length             = sizeof (SCSI_PASS_THROUGH);
331                 p.spt.CdbLength          = 6;
332                 p.spt.SenseInfoLength    = 24;
333                 p.spt.DataIn             = SCSI_IOCTL_DATA_IN;
334                 p.spt.DataTransferLength = sizeof (buffer);
335                 p.spt.TimeOutValue       = 2;
336                 p.spt.DataBuffer         = buffer;
337                 p.spt.SenseInfoOffset    = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
338                 p.spt.Cdb[0]             = 0x12;
339                 p.spt.Cdb[4]             = 100;
340 
341                 DWORD bytesReturned = 0;
342 
343                 if (DeviceIoControl (h, IOCTL_SCSI_PASS_THROUGH_DIRECT,
344                                      &p, sizeof (p), &p, sizeof (p),
345                                      &bytesReturned, 0) != 0)
346                 {
347                     CDDeviceDescription dev;
348                     dev.scsiDriveLetter = driveLetter;
349                     dev.createDescription (buffer);
350 
351                     SCSI_ADDRESS scsiAddr = { 0 };
352                     scsiAddr.Length = sizeof (scsiAddr);
353 
354                     if (DeviceIoControl (h, IOCTL_SCSI_GET_ADDRESS,
355                                          0, 0, &scsiAddr, sizeof (scsiAddr),
356                                          &bytesReturned, 0) != 0)
357                     {
358                         dev.ha = scsiAddr.PortNumber;
359                         dev.tgt = scsiAddr.TargetId;
360                         dev.lun = scsiAddr.Lun;
361                         list.add (dev);
362                     }
363                 }
364 
365                 CloseHandle (h);
366             }
367         }
368     }
369 }
370 
performScsiPassThroughCommand(SRB_ExecSCSICmd * const srb,const char driveLetter,HANDLE & deviceHandle,const bool retryOnFailure)371 DWORD performScsiPassThroughCommand (SRB_ExecSCSICmd* const srb, const char driveLetter,
372                                      HANDLE& deviceHandle, const bool retryOnFailure)
373 {
374     SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER s = { 0 };
375     s.spt.Length = sizeof (SCSI_PASS_THROUGH);
376     s.spt.CdbLength = srb->SRB_CDBLen;
377 
378     s.spt.DataIn = (BYTE) ((srb->SRB_Flags & SRB_DIR_IN)
379                             ? SCSI_IOCTL_DATA_IN
380                             : ((srb->SRB_Flags & SRB_DIR_OUT)
381                                 ? SCSI_IOCTL_DATA_OUT
382                                 : SCSI_IOCTL_DATA_UNSPECIFIED));
383 
384     s.spt.DataTransferLength = srb->SRB_BufLen;
385     s.spt.TimeOutValue = 5;
386     s.spt.DataBuffer = srb->SRB_BufPointer;
387     s.spt.SenseInfoOffset = offsetof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
388 
389     memcpy (s.spt.Cdb, srb->CDBByte, srb->SRB_CDBLen);
390 
391     srb->SRB_Status = SS_ERR;
392     srb->SRB_TargStat = 0x0004;
393 
394     DWORD bytesReturned = 0;
395 
396     if (DeviceIoControl (deviceHandle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
397                          &s, sizeof (s), &s, sizeof (s), &bytesReturned, 0) != 0)
398     {
399         srb->SRB_Status = SS_COMP;
400     }
401     else if (retryOnFailure)
402     {
403         const DWORD error = GetLastError();
404 
405         if ((error == ERROR_MEDIA_CHANGED) || (error == ERROR_INVALID_HANDLE))
406         {
407             if (error != ERROR_INVALID_HANDLE)
408                 CloseHandle (deviceHandle);
409 
410             deviceHandle = createSCSIDeviceHandle (driveLetter);
411 
412             return performScsiPassThroughCommand (srb, driveLetter, deviceHandle, false);
413         }
414     }
415 
416     return srb->SRB_Status;
417 }
418 
419 
420 //==============================================================================
421 // Controller types..
422 
423 class ControllerType1  : public CDController
424 {
425 public:
ControllerType1()426     ControllerType1() {}
427 
read(CDReadBuffer & rb)428     bool read (CDReadBuffer& rb)
429     {
430         if (rb.numFrames * 2352 > rb.bufferSize)
431             return false;
432 
433         SRB_ExecSCSICmd s;
434         prepare (s);
435         s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
436         s.SRB_BufLen = rb.bufferSize;
437         s.SRB_BufPointer = rb.buffer;
438         s.SRB_CDBLen = 12;
439         s.CDBByte[0] = 0xBE;
440         s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
441         s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
442         s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
443         s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
444         s.CDBByte[9] = (BYTE) (deviceInfo->readType == READTYPE_ATAPI1 ? 0x10 : 0xF0);
445         perform (s);
446 
447         if (s.SRB_Status != SS_COMP)
448             return false;
449 
450         rb.dataLength = rb.numFrames * 2352;
451         rb.dataStartOffset = 0;
452         return true;
453     }
454 };
455 
456 //==============================================================================
457 class ControllerType2  : public CDController
458 {
459 public:
ControllerType2()460     ControllerType2() {}
461 
shutDown()462     void shutDown()
463     {
464         if (initialised)
465         {
466             BYTE bufPointer[] = { 0, 0, 0, 8, 83, 0, 0, 0, 0, 0, 8, 0 };
467 
468             SRB_ExecSCSICmd s;
469             prepare (s);
470             s.SRB_Flags = SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT;
471             s.SRB_BufLen = 0x0C;
472             s.SRB_BufPointer = bufPointer;
473             s.SRB_CDBLen = 6;
474             s.CDBByte[0] = 0x15;
475             s.CDBByte[4] = 0x0C;
476             perform (s);
477         }
478     }
479 
init()480     bool init()
481     {
482         SRB_ExecSCSICmd s;
483         s.SRB_Status = SS_ERR;
484 
485         if (deviceInfo->readType == READTYPE_READ10_2)
486         {
487             BYTE bufPointer1[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 35, 6, 0, 0, 0, 0, 0, 128 };
488             BYTE bufPointer2[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48, 1, 6, 32, 7, 0, 0, 0, 0 };
489 
490             for (int i = 0; i < 2; ++i)
491             {
492                 prepare (s);
493                 s.SRB_Flags = SRB_EVENT_NOTIFY;
494                 s.SRB_BufLen = 0x14;
495                 s.SRB_BufPointer = (i == 0) ? bufPointer1 : bufPointer2;
496                 s.SRB_CDBLen = 6;
497                 s.CDBByte[0] = 0x15;
498                 s.CDBByte[1] = 0x10;
499                 s.CDBByte[4] = 0x14;
500                 perform (s);
501 
502                 if (s.SRB_Status != SS_COMP)
503                     return false;
504             }
505         }
506         else
507         {
508             BYTE bufPointer[] = { 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 9, 48 };
509 
510             prepare (s);
511             s.SRB_Flags = SRB_EVENT_NOTIFY;
512             s.SRB_BufLen = 0x0C;
513             s.SRB_BufPointer = bufPointer;
514             s.SRB_CDBLen = 6;
515             s.CDBByte[0] = 0x15;
516             s.CDBByte[4] = 0x0C;
517             perform (s);
518         }
519 
520         return s.SRB_Status == SS_COMP;
521     }
522 
read(CDReadBuffer & rb)523     bool read (CDReadBuffer& rb)
524     {
525         if (rb.numFrames * 2352 > rb.bufferSize)
526             return false;
527 
528         if (! initialised)
529         {
530             initialised = init();
531 
532             if (! initialised)
533                 return false;
534         }
535 
536         SRB_ExecSCSICmd s;
537         prepare (s);
538         s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
539         s.SRB_BufLen = rb.bufferSize;
540         s.SRB_BufPointer = rb.buffer;
541         s.SRB_CDBLen = 10;
542         s.CDBByte[0] = 0x28;
543         s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
544         s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
545         s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
546         s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
547         s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
548         perform (s);
549 
550         if (s.SRB_Status != SS_COMP)
551             return false;
552 
553         rb.dataLength = rb.numFrames * 2352;
554         rb.dataStartOffset = 0;
555         return true;
556     }
557 };
558 
559 //==============================================================================
560 class ControllerType3  : public CDController
561 {
562 public:
ControllerType3()563     ControllerType3() {}
564 
read(CDReadBuffer & rb)565     bool read (CDReadBuffer& rb)
566     {
567         if (rb.numFrames * 2352 > rb.bufferSize)
568             return false;
569 
570         if (! initialised)
571         {
572             setPaused (false);
573             initialised = true;
574         }
575 
576         SRB_ExecSCSICmd s;
577         prepare (s);
578         s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
579         s.SRB_BufLen = rb.numFrames * 2352;
580         s.SRB_BufPointer = rb.buffer;
581         s.SRB_CDBLen = 12;
582         s.CDBByte[0] = 0xD8;
583         s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
584         s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
585         s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
586         s.CDBByte[9] = (BYTE) (rb.numFrames & 0xFF);
587         perform (s);
588 
589         if (s.SRB_Status != SS_COMP)
590             return false;
591 
592         rb.dataLength = rb.numFrames * 2352;
593         rb.dataStartOffset = 0;
594         return true;
595     }
596 };
597 
598 //==============================================================================
599 class ControllerType4  : public CDController
600 {
601 public:
ControllerType4()602     ControllerType4() {}
603 
selectD4Mode()604     bool selectD4Mode()
605     {
606         BYTE bufPointer[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 48 };
607 
608         SRB_ExecSCSICmd s;
609         prepare (s);
610         s.SRB_Flags = SRB_EVENT_NOTIFY;
611         s.SRB_CDBLen = 6;
612         s.SRB_BufLen = 12;
613         s.SRB_BufPointer = bufPointer;
614         s.CDBByte[0] = 0x15;
615         s.CDBByte[1] = 0x10;
616         s.CDBByte[4] = 0x08;
617         perform (s);
618 
619         return s.SRB_Status == SS_COMP;
620     }
621 
read(CDReadBuffer & rb)622     bool read (CDReadBuffer& rb)
623     {
624         if (rb.numFrames * 2352 > rb.bufferSize)
625             return false;
626 
627         if (! initialised)
628         {
629             setPaused (true);
630 
631             if (deviceInfo->readType == READTYPE_READ_D4_1)
632                 selectD4Mode();
633 
634             initialised = true;
635         }
636 
637         SRB_ExecSCSICmd s;
638         prepare (s);
639         s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
640         s.SRB_BufLen = rb.bufferSize;
641         s.SRB_BufPointer = rb.buffer;
642         s.SRB_CDBLen = 10;
643         s.CDBByte[0] = 0xD4;
644         s.CDBByte[3] = (BYTE) ((rb.startFrame >> 16) & 0xFF);
645         s.CDBByte[4] = (BYTE) ((rb.startFrame >> 8) & 0xFF);
646         s.CDBByte[5] = (BYTE) (rb.startFrame & 0xFF);
647         s.CDBByte[8] = (BYTE) (rb.numFrames & 0xFF);
648         perform (s);
649 
650         if (s.SRB_Status != SS_COMP)
651             return false;
652 
653         rb.dataLength = rb.numFrames * 2352;
654         rb.dataStartOffset = 0;
655         return true;
656     }
657 };
658 
659 
660 //==============================================================================
prepare(SRB_ExecSCSICmd & s)661 void CDController::prepare (SRB_ExecSCSICmd& s)
662 {
663     zerostruct (s);
664     s.SRB_Cmd = SC_EXEC_SCSI_CMD;
665     s.SRB_HaID = deviceInfo->info.ha;
666     s.SRB_Target = deviceInfo->info.tgt;
667     s.SRB_Lun = deviceInfo->info.lun;
668     s.SRB_SenseLen = SENSE_LEN;
669 }
670 
perform(SRB_ExecSCSICmd & s)671 void CDController::perform (SRB_ExecSCSICmd& s)
672 {
673     s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
674 
675     deviceInfo->performScsiCommand (s.SRB_PostProc, s);
676 }
677 
setPaused(bool paused)678 void CDController::setPaused (bool paused)
679 {
680     SRB_ExecSCSICmd s;
681     prepare (s);
682     s.SRB_Flags = SRB_EVENT_NOTIFY;
683     s.SRB_CDBLen = 10;
684     s.CDBByte[0] = 0x4B;
685     s.CDBByte[8] = (BYTE) (paused ? 0 : 1);
686     perform (s);
687 }
688 
readAudio(CDReadBuffer & rb,CDReadBuffer * overlapBuffer)689 bool CDController::readAudio (CDReadBuffer& rb, CDReadBuffer* overlapBuffer)
690 {
691     if (overlapBuffer != nullptr)
692     {
693         const bool canDoJitter = (overlapBuffer->bufferSize >= 2352 * framesToCheck);
694         const bool doJitter = canDoJitter && ! overlapBuffer->isZero();
695 
696         if (doJitter
697              && overlapBuffer->startFrame > 0
698              && overlapBuffer->numFrames > 0
699              && overlapBuffer->dataLength > 0)
700         {
701             const int numFrames = rb.numFrames;
702 
703             if (overlapBuffer->startFrame == (rb.startFrame - framesToCheck))
704             {
705                 rb.startFrame -= framesOverlap;
706 
707                 if (framesToCheck < framesOverlap
708                      && numFrames + framesOverlap <= rb.bufferSize / 2352)
709                     rb.numFrames += framesOverlap;
710             }
711             else
712             {
713                 overlapBuffer->dataLength = 0;
714                 overlapBuffer->startFrame = 0;
715                 overlapBuffer->numFrames = 0;
716             }
717         }
718 
719         if (! read (rb))
720             return false;
721 
722         if (doJitter)
723         {
724             const int checkLen = framesToCheck * 2352;
725             const int maxToCheck = rb.dataLength - checkLen;
726 
727             if (overlapBuffer->dataLength == 0 || overlapBuffer->isZero())
728                 return true;
729 
730             BYTE* const p = overlapBuffer->buffer + overlapBuffer->dataStartOffset;
731             bool found = false;
732 
733             for (int i = 0; i < maxToCheck; ++i)
734             {
735                 if (memcmp (p, rb.buffer + i, checkLen) == 0)
736                 {
737                     i += checkLen;
738                     rb.dataStartOffset = i;
739                     rb.dataLength -= i;
740                     rb.startFrame = overlapBuffer->startFrame + framesToCheck;
741                     found = true;
742                     break;
743                 }
744             }
745 
746             rb.numFrames = rb.dataLength / 2352;
747             rb.dataLength = 2352 * rb.numFrames;
748 
749             if (! found)
750                 return false;
751         }
752 
753         if (canDoJitter)
754         {
755             memcpy (overlapBuffer->buffer,
756                     rb.buffer + rb.dataStartOffset + 2352 * (rb.numFrames - framesToCheck),
757                     2352 * framesToCheck);
758 
759             overlapBuffer->startFrame = rb.startFrame + rb.numFrames - framesToCheck;
760             overlapBuffer->numFrames = framesToCheck;
761             overlapBuffer->dataLength = 2352 * framesToCheck;
762             overlapBuffer->dataStartOffset = 0;
763         }
764         else
765         {
766             overlapBuffer->startFrame = 0;
767             overlapBuffer->numFrames = 0;
768             overlapBuffer->dataLength = 0;
769         }
770 
771         return true;
772     }
773 
774     return read (rb);
775 }
776 
getLastIndex()777 int CDController::getLastIndex()
778 {
779     char qdata[100];
780 
781     SRB_ExecSCSICmd s;
782     prepare (s);
783     s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
784     s.SRB_BufLen = sizeof (qdata);
785     s.SRB_BufPointer = (BYTE*) qdata;
786     s.SRB_CDBLen = 12;
787     s.CDBByte[0] = 0x42;
788     s.CDBByte[1] = (BYTE) (deviceInfo->info.lun << 5);
789     s.CDBByte[2] = 64;
790     s.CDBByte[3] = 1; // get current position
791     s.CDBByte[7] = 0;
792     s.CDBByte[8] = (BYTE) sizeof (qdata);
793     perform (s);
794 
795     return s.SRB_Status == SS_COMP ? qdata[7] : 0;
796 }
797 
798 //==============================================================================
readTOC(TOC * lpToc)799 bool CDDeviceHandle::readTOC (TOC* lpToc)
800 {
801     SRB_ExecSCSICmd s = { 0 };
802     s.SRB_Cmd = SC_EXEC_SCSI_CMD;
803     s.SRB_HaID = info.ha;
804     s.SRB_Target = info.tgt;
805     s.SRB_Lun = info.lun;
806     s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
807     s.SRB_BufLen = 0x324;
808     s.SRB_BufPointer = (BYTE*) lpToc;
809     s.SRB_SenseLen = 0x0E;
810     s.SRB_CDBLen = 0x0A;
811     s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
812     s.CDBByte[0] = 0x43;
813     s.CDBByte[1] = 0x00;
814     s.CDBByte[7] = 0x03;
815     s.CDBByte[8] = 0x24;
816 
817     performScsiCommand (s.SRB_PostProc, s);
818     return (s.SRB_Status == SS_COMP);
819 }
820 
performScsiCommand(HANDLE event,SRB_ExecSCSICmd & s)821 void CDDeviceHandle::performScsiCommand (HANDLE event, SRB_ExecSCSICmd& s)
822 {
823     ResetEvent (event);
824     DWORD status = performScsiPassThroughCommand ((SRB_ExecSCSICmd*) &s, info.scsiDriveLetter, scsiHandle, true);
825 
826     if (status == SS_PENDING)
827         WaitForSingleObject (event, 4000);
828 
829     CloseHandle (event);
830 }
831 
readAudio(CDReadBuffer & buffer,CDReadBuffer * overlapBuffer)832 bool CDDeviceHandle::readAudio (CDReadBuffer& buffer, CDReadBuffer* overlapBuffer)
833 {
834     if (controller == 0)
835     {
836            testController (READTYPE_ATAPI2,    new ControllerType1(), buffer)
837         || testController (READTYPE_ATAPI1,    new ControllerType1(), buffer)
838         || testController (READTYPE_READ10_2,  new ControllerType2(), buffer)
839         || testController (READTYPE_READ10,    new ControllerType2(), buffer)
840         || testController (READTYPE_READ_D8,   new ControllerType3(), buffer)
841         || testController (READTYPE_READ_D4,   new ControllerType4(), buffer)
842         || testController (READTYPE_READ_D4_1, new ControllerType4(), buffer);
843     }
844 
845     buffer.index = 0;
846 
847     if (controller != nullptr && controller->readAudio (buffer, overlapBuffer))
848     {
849         if (buffer.wantsIndex)
850             buffer.index = controller->getLastIndex();
851 
852         return true;
853     }
854 
855     return false;
856 }
857 
openDrawer(bool shouldBeOpen)858 void CDDeviceHandle::openDrawer (bool shouldBeOpen)
859 {
860     if (shouldBeOpen)
861     {
862         if (controller != nullptr)
863         {
864             controller->shutDown();
865             controller = nullptr;
866         }
867 
868         if (scsiHandle != 0)
869         {
870             CloseHandle (scsiHandle);
871             scsiHandle = 0;
872         }
873     }
874 
875     SRB_ExecSCSICmd s = { 0 };
876     s.SRB_Cmd = SC_EXEC_SCSI_CMD;
877     s.SRB_HaID = info.ha;
878     s.SRB_Target = info.tgt;
879     s.SRB_Lun = info.lun;
880     s.SRB_SenseLen = SENSE_LEN;
881     s.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
882     s.SRB_BufLen = 0;
883     s.SRB_BufPointer = 0;
884     s.SRB_CDBLen = 12;
885     s.CDBByte[0] = 0x1b;
886     s.CDBByte[1] = (BYTE) (info.lun << 5);
887     s.CDBByte[4] = (BYTE) (shouldBeOpen ? 2 : 3);
888     s.SRB_PostProc = CreateEvent (0, TRUE, FALSE, 0);
889 
890     performScsiCommand (s.SRB_PostProc, s);
891 }
892 
testController(const int type,CDController * const newController,CDReadBuffer & rb)893 bool CDDeviceHandle::testController (const int type, CDController* const newController, CDReadBuffer& rb)
894 {
895     controller.reset (newController);
896     readType = (BYTE) type;
897 
898     controller->deviceInfo = this;
899     controller->framesToCheck = 1;
900     controller->framesOverlap = 3;
901 
902     bool passed = false;
903     memset (rb.buffer, 0xcd, rb.bufferSize);
904 
905     if (controller->read (rb))
906     {
907         passed = true;
908         int* p = (int*) (rb.buffer + rb.dataStartOffset);
909         int wrong = 0;
910 
911         for (int i = rb.dataLength / 4; --i >= 0;)
912         {
913             if (*p++ == (int) 0xcdcdcdcd)
914             {
915                 if (++wrong == 4)
916                 {
917                     passed = false;
918                     break;
919                 }
920             }
921             else
922             {
923                 wrong = 0;
924             }
925         }
926     }
927 
928     if (! passed)
929     {
930         controller->shutDown();
931         controller = nullptr;
932     }
933 
934     return passed;
935 }
936 
937 
938 //==============================================================================
939 struct CDDeviceWrapper
940 {
CDDeviceWrapperjuce::CDReaderHelpers::CDDeviceWrapper941     CDDeviceWrapper (const CDDeviceDescription& device, HANDLE scsiHandle)
942         : deviceHandle (device, scsiHandle), overlapBuffer (3), jitter (false)
943     {
944         // xxx jitter never seemed to actually be enabled (??)
945     }
946 
947     CDDeviceHandle deviceHandle;
948     CDReadBuffer overlapBuffer;
949     bool jitter;
950 };
951 
952 //==============================================================================
getAddressOfTrack(const TOCTRACK & t)953 int getAddressOfTrack (const TOCTRACK& t) noexcept
954 {
955     return (((DWORD) t.addr[0]) << 24) + (((DWORD) t.addr[1]) << 16)
956             + (((DWORD) t.addr[2]) << 8) + ((DWORD) t.addr[3]);
957 }
958 
959 const int samplesPerFrame = 44100 / 75;
960 const int bytesPerFrame = samplesPerFrame * 4;
961 const int framesPerIndexRead = 4;
962 
963 }
964 
965 //==============================================================================
getAvailableCDNames()966 StringArray AudioCDReader::getAvailableCDNames()
967 {
968     using namespace CDReaderHelpers;
969     StringArray results;
970 
971     Array<CDDeviceDescription> list;
972     findCDDevices (list);
973 
974     for (int i = 0; i < list.size(); ++i)
975     {
976         String s;
977         if (list[i].scsiDriveLetter > 0)
978             s << String::charToString (list[i].scsiDriveLetter).toUpperCase() << ": ";
979 
980         s << list[i].description;
981         results.add (s);
982     }
983 
984     return results;
985 }
986 
createReaderForCD(const int deviceIndex)987 AudioCDReader* AudioCDReader::createReaderForCD (const int deviceIndex)
988 {
989     using namespace CDReaderHelpers;
990 
991     Array<CDDeviceDescription> list;
992     findCDDevices (list);
993 
994     if (isPositiveAndBelow (deviceIndex, list.size()))
995     {
996         HANDLE h = createSCSIDeviceHandle (list [deviceIndex].scsiDriveLetter);
997 
998         if (h != INVALID_HANDLE_VALUE)
999         {
1000             std::unique_ptr<AudioCDReader> cd (new AudioCDReader (new CDDeviceWrapper (list [deviceIndex], h)));
1001 
1002             if (cd->lengthInSamples > 0)
1003                 return cd.release();
1004         }
1005     }
1006 
1007     return nullptr;
1008 }
1009 
AudioCDReader(void * handle_)1010 AudioCDReader::AudioCDReader (void* handle_)
1011     : AudioFormatReader (0, "CD Audio"),
1012       handle (handle_),
1013       indexingEnabled (false),
1014       lastIndex (0),
1015       firstFrameInBuffer (0),
1016       samplesInBuffer (0)
1017 {
1018     using namespace CDReaderHelpers;
1019     jassert (handle_ != nullptr);
1020 
1021     refreshTrackLengths();
1022 
1023     sampleRate = 44100.0;
1024     bitsPerSample = 16;
1025     numChannels = 2;
1026     usesFloatingPointData = false;
1027 
1028     buffer.setSize (4 * bytesPerFrame, true);
1029 }
1030 
~AudioCDReader()1031 AudioCDReader::~AudioCDReader()
1032 {
1033     using namespace CDReaderHelpers;
1034     CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
1035     delete device;
1036 }
1037 
readSamples(int ** destSamples,int numDestChannels,int startOffsetInDestBuffer,int64 startSampleInFile,int numSamples)1038 bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
1039                                  int64 startSampleInFile, int numSamples)
1040 {
1041     using namespace CDReaderHelpers;
1042     CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
1043 
1044     bool ok = true;
1045 
1046     while (numSamples > 0)
1047     {
1048         const int bufferStartSample = firstFrameInBuffer * samplesPerFrame;
1049         const int bufferEndSample = bufferStartSample + samplesInBuffer;
1050 
1051         if (startSampleInFile >= bufferStartSample
1052              && startSampleInFile < bufferEndSample)
1053         {
1054             const int toDo = (int) jmin ((int64) numSamples, bufferEndSample - startSampleInFile);
1055 
1056             int* const l = destSamples[0] + startOffsetInDestBuffer;
1057             int* const r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
1058             const short* src = (const short*) buffer.getData();
1059             src += 2 * (startSampleInFile - bufferStartSample);
1060 
1061             for (int i = 0; i < toDo; ++i)
1062             {
1063                 l[i] = src [i << 1] << 16;
1064 
1065                 if (r != nullptr)
1066                     r[i] = src [(i << 1) + 1] << 16;
1067             }
1068 
1069             startOffsetInDestBuffer += toDo;
1070             startSampleInFile += toDo;
1071             numSamples -= toDo;
1072         }
1073         else
1074         {
1075             const int framesInBuffer = (int) (buffer.getSize() / bytesPerFrame);
1076             const int frameNeeded = (int) (startSampleInFile / samplesPerFrame);
1077 
1078             if (firstFrameInBuffer + framesInBuffer != frameNeeded)
1079             {
1080                 device->overlapBuffer.dataLength = 0;
1081                 device->overlapBuffer.startFrame = 0;
1082                 device->overlapBuffer.numFrames = 0;
1083                 device->jitter = false;
1084             }
1085 
1086             firstFrameInBuffer = frameNeeded;
1087             lastIndex = 0;
1088 
1089             CDReadBuffer readBuffer (framesInBuffer + 4);
1090             readBuffer.wantsIndex = indexingEnabled;
1091 
1092             int i;
1093             for (i = 5; --i >= 0;)
1094             {
1095                 readBuffer.startFrame = frameNeeded;
1096                 readBuffer.numFrames = framesInBuffer;
1097 
1098                 if (device->deviceHandle.readAudio (readBuffer, device->jitter ? &device->overlapBuffer : 0))
1099                     break;
1100                 else
1101                     device->overlapBuffer.dataLength = 0;
1102             }
1103 
1104             if (i >= 0)
1105             {
1106                 buffer.copyFrom (readBuffer.buffer + readBuffer.dataStartOffset, 0, readBuffer.dataLength);
1107                 samplesInBuffer = readBuffer.dataLength >> 2;
1108                 lastIndex = readBuffer.index;
1109             }
1110             else
1111             {
1112                 int* l = destSamples[0] + startOffsetInDestBuffer;
1113                 int* r = numDestChannels > 1 ? (destSamples[1] + startOffsetInDestBuffer) : nullptr;
1114 
1115                 while (--numSamples >= 0)
1116                 {
1117                     *l++ = 0;
1118 
1119                     if (r != nullptr)
1120                         *r++ = 0;
1121                 }
1122 
1123                 // sometimes the read fails for just the very last couple of blocks, so
1124                 // we'll ignore and errors in the last half-second of the disk..
1125                 ok = startSampleInFile > (trackStartSamples [getNumTracks()] - 20000);
1126                 break;
1127             }
1128         }
1129     }
1130 
1131     return ok;
1132 }
1133 
isCDStillPresent() const1134 bool AudioCDReader::isCDStillPresent() const
1135 {
1136     using namespace CDReaderHelpers;
1137     TOC toc = { 0 };
1138     return static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc);
1139 }
1140 
refreshTrackLengths()1141 void AudioCDReader::refreshTrackLengths()
1142 {
1143     using namespace CDReaderHelpers;
1144     trackStartSamples.clear();
1145     zeromem (audioTracks, sizeof (audioTracks));
1146 
1147     TOC toc = { 0 };
1148 
1149     if (static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc))
1150     {
1151         int numTracks = 1 + toc.lastTrack - toc.firstTrack;
1152 
1153         for (int i = 0; i <= numTracks; ++i)
1154         {
1155             trackStartSamples.add (samplesPerFrame * getAddressOfTrack (toc.tracks [i]));
1156             audioTracks [i] = ((toc.tracks[i].ADR & 4) == 0);
1157         }
1158     }
1159 
1160     lengthInSamples = getPositionOfTrackStart (getNumTracks());
1161 }
1162 
isTrackAudio(int trackNum) const1163 bool AudioCDReader::isTrackAudio (int trackNum) const
1164 {
1165     return trackNum >= 0 && trackNum < getNumTracks() && audioTracks [trackNum];
1166 }
1167 
enableIndexScanning(bool b)1168 void AudioCDReader::enableIndexScanning (bool b)
1169 {
1170     indexingEnabled = b;
1171 }
1172 
getLastIndex() const1173 int AudioCDReader::getLastIndex() const
1174 {
1175     return lastIndex;
1176 }
1177 
getIndexAt(int samplePos)1178 int AudioCDReader::getIndexAt (int samplePos)
1179 {
1180     using namespace CDReaderHelpers;
1181     auto* device = static_cast<CDDeviceWrapper*> (handle);
1182 
1183     const int frameNeeded = samplePos / samplesPerFrame;
1184 
1185     device->overlapBuffer.dataLength = 0;
1186     device->overlapBuffer.startFrame = 0;
1187     device->overlapBuffer.numFrames = 0;
1188     device->jitter = false;
1189 
1190     firstFrameInBuffer = 0;
1191     lastIndex = 0;
1192 
1193     CDReadBuffer readBuffer (4 + framesPerIndexRead);
1194     readBuffer.wantsIndex = true;
1195 
1196     int i;
1197     for (i = 5; --i >= 0;)
1198     {
1199         readBuffer.startFrame = frameNeeded;
1200         readBuffer.numFrames = framesPerIndexRead;
1201 
1202         if (device->deviceHandle.readAudio (readBuffer))
1203             break;
1204     }
1205 
1206     if (i >= 0)
1207         return readBuffer.index;
1208 
1209     return -1;
1210 }
1211 
findIndexesInTrack(const int trackNumber)1212 Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber)
1213 {
1214     using namespace CDReaderHelpers;
1215     Array<int> indexes;
1216 
1217     const int trackStart = getPositionOfTrackStart (trackNumber);
1218     const int trackEnd = getPositionOfTrackStart (trackNumber + 1);
1219 
1220     bool needToScan = true;
1221 
1222     if (trackEnd - trackStart > 20 * 44100)
1223     {
1224         // check the end of the track for indexes before scanning the whole thing
1225         needToScan = false;
1226         int pos = jmax (trackStart, trackEnd - 44100 * 5);
1227         bool seenAnIndex = false;
1228 
1229         while (pos <= trackEnd - samplesPerFrame)
1230         {
1231             const int index = getIndexAt (pos);
1232 
1233             if (index == 0)
1234             {
1235                 // lead-out, so skip back a bit if we've not found any indexes yet..
1236                 if (seenAnIndex)
1237                     break;
1238 
1239                 pos -= 44100 * 5;
1240 
1241                 if (pos < trackStart)
1242                     break;
1243             }
1244             else
1245             {
1246                 if (index > 0)
1247                     seenAnIndex = true;
1248 
1249                 if (index > 1)
1250                 {
1251                     needToScan = true;
1252                     break;
1253                 }
1254 
1255                 pos += samplesPerFrame * framesPerIndexRead;
1256             }
1257         }
1258     }
1259 
1260     if (needToScan)
1261     {
1262         auto* device = static_cast<CDDeviceWrapper*> (handle);
1263 
1264         int pos = trackStart;
1265         int last = -1;
1266 
1267         while (pos < trackEnd - samplesPerFrame * 10)
1268         {
1269             const int frameNeeded = pos / samplesPerFrame;
1270 
1271             device->overlapBuffer.dataLength = 0;
1272             device->overlapBuffer.startFrame = 0;
1273             device->overlapBuffer.numFrames = 0;
1274             device->jitter = false;
1275 
1276             firstFrameInBuffer = 0;
1277 
1278             CDReadBuffer readBuffer (4);
1279             readBuffer.wantsIndex = true;
1280 
1281             int i;
1282             for (i = 5; --i >= 0;)
1283             {
1284                 readBuffer.startFrame = frameNeeded;
1285                 readBuffer.numFrames = framesPerIndexRead;
1286 
1287                 if (device->deviceHandle.readAudio (readBuffer))
1288                     break;
1289             }
1290 
1291             if (i < 0)
1292                 break;
1293 
1294             if (readBuffer.index > last && readBuffer.index > 1)
1295             {
1296                 last = readBuffer.index;
1297                 indexes.add (pos);
1298             }
1299 
1300             pos += samplesPerFrame * framesPerIndexRead;
1301         }
1302 
1303         indexes.removeFirstMatchingValue (trackStart);
1304     }
1305 
1306     return indexes;
1307 }
1308 
ejectDisk()1309 void AudioCDReader::ejectDisk()
1310 {
1311     using namespace CDReaderHelpers;
1312     static_cast<CDDeviceWrapper*> (handle)->deviceHandle.openDrawer (true);
1313 }
1314 
1315 } // namespace juce
1316