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