//
// by James L. Hammons
//
+// Currently, we use the ASPI layer for Win32, but this may or may not
+// work on NT based Windows. If necessary, we'll put in the required
+// NT based code, but for now, it's ASPI or nothing.
+//
-// OS dependent CDROM stuffola
+// *** OS dependent CDROM stuffola ***
#include <windows.h>
-#include <stdio.h>
#include "wnaspi32.h"
#include "scsidefs.h"
-// End OS dependent
-
+// *** End OS dependent ***
#include "log.h"
using namespace std;
// Local variables
-DWORD remain = 0, sector = 0;
-BYTE cdBuf[2532 * 10];
+static uint8 maxHostAdapters = 0, numCDDrives = 0, driveNum;
+static uint8 haID[8], tID[8];
+static uint8 driveName[8][26];
+static uint8 tracks[100][3]; // One-based index
+static uint32 numTracks = 0;
+static uint8 sessions[40][5]; // Zero-based index
+static uint32 numSessions = 0;
+static bool readTOC = false;
-// Private function prototypes
+static HINSTANCE hASPILib = NULL; // Handle to ASPI for Win32 (WNASPI.DLL)
+static uint32 (* ASPI_GetASPI32SupportInfo)(void); // WNASPI.DLL function pointers
+static uint32 (* ASPI_SendASPI32Command)(LPSRB);
+// Private function prototypes
-HINSTANCE WNASPI32_handle = NULL; //Handle to ASPI for Win32 (WNASPI.DLL)
-//WNASPI.DLL functions
-DWORD (*ASPI_GetASPI32SupportInfo)(VOID);
-DWORD (*ASPI_SendASPI32Command)(LPSRB);
-//BOOL (*ASPI_GetASPI32Buffer)(PASPI32BUFF);
-//BOOL (*ASPI_FreeASPI32Buffer)(PASPI32BUFF);
-//BOOL (*ASPI_TranslateASPI32Address)(PDWORD, PDWORD);
+static bool InitASPI(void);
+static bool SendAsyncASPICmd(uint8, uint8, uint8 *, uint8, uint8 *, uint32, uint8);
+/*static*/ bool GetRawTOC(void);
-byte DataBuf[2352]; //Buffer for holding data to/from drive
-//************************************************************
-//End of globals
//
-// Load the WNASPI32.DLL and import the required functions, then initialise
-// Win ASPI 32.
+// Initialize the Win32 ASPI layer
//
-BOOL InitASPI()
+static bool InitASPI(void)
{
- DWORD dwSupportInfo;
- SRB_GetSetTimeouts srbTimeouts;
+ hASPILib = LoadLibrary("WNASPI32");
+
+ if (!hASPILib)
+ {
+ WriteLog("CDINTF: Could not load WNASPI32.DLL!\n");
+ return false;
+ }
- WNASPI32_handle = LoadLibrary("WNASPI32"); //WNASPI32.DLL
+ ASPI_GetASPI32SupportInfo = (uint32 (*)(void))GetProcAddress(hASPILib, "GetASPI32SupportInfo");
+ ASPI_SendASPI32Command = (uint32 (*)(LPSRB))GetProcAddress(hASPILib, "SendASPI32Command");
- if (!WNASPI32_handle)
+ if (!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
{
- WriteLog("CDINTF: Could not load WNASPI32.DLL\n");
- return FALSE;
- };
-
- /*
- ** Get the ASPI entry points. Note that only two functions are mandatory:
- ** GetASPI32SupportInfo and SendASPI32Command. The code will run if the
- ** others are not present.
- */
- ASPI_GetASPI32SupportInfo = (DWORD (*)(void))GetProcAddress(WNASPI32_handle, "GetASPI32SupportInfo");
- ASPI_SendASPI32Command = (DWORD (*)(LPSRB))GetProcAddress(WNASPI32_handle, "SendASPI32Command");
- //ASPI_GetASPI32Buffer = (BOOL (*)(PASPI32BUFF))GetProcAddress(WNASPI32_handle, "GetASPI32Buffer");
- //ASPI_FreeASPI32Buffer = (BOOL (*)(PASPI32BUFF))GetProcAddress(WNASPI32_handle, "FreeASPI32Buffer");
- //ASPI_TranslateASPI32Address = (BOOL (*)(PDWORD, PDWORD))GetProcAddress(WNASPI32_handle, "TranslateASPI32Address");
-
- //Check if the 2 functions were imported.
- if(!ASPI_GetASPI32SupportInfo || !ASPI_SendASPI32Command)
- {
- WriteLog("Could not import GetASPI32SupportInfo & SendASPI32Command functions from WNASPI32.DLL\n");
- return FALSE;
- };
-
- //Initialise Win ASPI 32 by calling ASPI_GetASPI32SupportInfo().
- dwSupportInfo = ASPI_GetASPI32SupportInfo();
- if (HIBYTE(LOWORD(dwSupportInfo)) != SS_COMP && HIBYTE(LOWORD(dwSupportInfo)) != SS_NO_ADAPTERS)
- {
- WriteLog("Could not initialise using GetASPI32SupportInfo function or no adapters\n");
- return FALSE;
- };
-
- /*
- ** Set timeouts for ALL devices to 15 seconds. Nothing we deal with should
- ** take that long to do ANYTHING. We are just doing inquiries to most
- ** devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
- ** if they have to spin up) should be plenty.
- */
- memset(&srbTimeouts, 0, sizeof(SRB_GetSetTimeouts));
- srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
- srbTimeouts.SRB_HaId = 0xFF;
- srbTimeouts.SRB_Flags = SRB_DIR_OUT;
- srbTimeouts.SRB_Target = 0xFF;
- srbTimeouts.SRB_Lun = 0xFF;
- srbTimeouts.SRB_Timeout = 15 * 2;
- ASPI_SendASPI32Command(&srbTimeouts);
-
- return TRUE;
-};
-
-/*
- Sends a SRB (SCSI Request Block) to Win ASPI32 driver for processing and
- waits for completion.
-
- The SRB contains the CDB (Command Descriptor Block) which has the
- raw SCSI command/info you want to send to the drive. So effectively
- this is actually sending the SCSI command to the drive.
-
- Note that this uses a Windows event to wait for the drive to reply back
- which is efficient. If an event is not available (which should never
- happen really) a loop is used to poll for reply back.
-
- Both methods will wait infinitely - so the drive must reply back otherwise
- this software will appear to hang. There seems to be no mechanism for
- cancelling the SRB process - the Win ASPI 32 'SC_ABORT_SRB' command doesn't
- seem to work.
-*/
-BOOL SendASPICMD_and_wait(BYTE HA_ID, BYTE Target_ID, BYTE SRB_flags, DWORD Buffer_len,
- PBYTE Buffer, BYTE CDB_len, PBYTE CDB)
-{
- DWORD dwASPIStatus;
- HANDLE hevent_SRB; //A handle for a new Windows event
- SRB_ExecSCSICmd SRB; //The SRB variable with CDB included
- BOOL b_retry = TRUE;
-
- //Clear & setup the SRB for this command..
- memset(&SRB, 0, sizeof(SRB_ExecSCSICmd)); //Set it to zeroes
- memcpy(SRB.CDBByte, CDB, CDB_len); //Copy CDB into SRB
-
- SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
- SRB.SRB_HaId = HA_ID;
- SRB.SRB_Target = Target_ID;
- //SRB.SRB_Lun = 0; //It's already zero
- SRB.SRB_Flags = SRB_flags;
- SRB.SRB_BufLen = Buffer_len;
- SRB.SRB_BufPointer = Buffer;
- SRB.SRB_SenseLen = SENSE_LEN;
- SRB.SRB_CDBLen = CDB_len;
-
- do
- {
- /*
- ** Create an event (if possible) and issue the command. After sending
- ** the command, wait for completion.
- */
- hevent_SRB = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (hevent_SRB)
- {
- //Windows event method for waiting - efficient.
-
- SRB.SRB_Flags |= SRB_EVENT_NOTIFY;
- SRB.SRB_PostProc = (LPVOID)hevent_SRB;
-
- //Send the SRB for processing.
- dwASPIStatus = ASPI_SendASPI32Command((LPSRB)&SRB);
- if (dwASPIStatus == SS_PENDING)
- {
- //Wait for reply back.
- WaitForSingleObject(hevent_SRB, INFINITE);
- }
- CloseHandle(hevent_SRB);
- }
- else
- {
- //Polling method for waiting - not very efficient.
-
- //Send the SRB for processing.
- ASPI_SendASPI32Command((LPSRB)&SRB);
- //Wait for reply back.
- while(SRB.SRB_Status == SS_PENDING);
- };
-
- /*
- ** Check for errors. We'll retry on unit attention condition. Anything
- ** else will generate an error msg.
- */
- if (SRB.SRB_Status != SS_COMP)
- {
- if (b_retry && (SRB.SRB_TargStat != STATUS_CHKCOND
- || (SRB.SenseArea[2] & 0x0F) != KEY_UNITATT))
- b_retry = FALSE;
- else
- {
- WriteLog("SCSI command failed.\n");
- return FALSE;
- }
- }
- }
- while (b_retry == FALSE);
-
- return TRUE;
-};
-
-/*
- Lists all available CDROM type drives. This includes:
- - CDROM drives
- - CD rewriters/Combo
- - DVD drives
- - DVD rewriters
- etc
-
- It can only list the SCSI IDs not drive letters. Win ASPI 32 does
- not provide a mechanism for this. You will have to use windows API
- for this.
-*/
-VOID ListDevices()
-{
- BYTE HaId;
- BYTE Target;
- BYTE MaxHaId;
- BYTE MaxTarget;
- BYTE InquiryBuf[36];
- BYTE InquiryCDB[6];
- CHAR szVendor[9];
- CHAR szProduct[17];
- CHAR szRev[5];
- BOOL bSRB_exec;
- DWORD dwASPIStatus;
- DWORD dwMaxTransferBytes;
- SRB_HAInquiry srbHAInquiry;
- SRB_GDEVBlock srbGDEVBlock;
- DWORD n_CDROM_drives=0; //No of CDROM type drives found.
-
- //Use support info for host adapter count and loop over all of them.
- dwASPIStatus = ASPI_GetASPI32SupportInfo();
- if(HIBYTE(LOWORD(dwASPIStatus)) == SS_COMP)
- {
- MaxHaId = LOBYTE(LOWORD(dwASPIStatus));
- for(HaId = 0; HaId < MaxHaId; HaId++)
- {
- /*
- ** Do a host adapter inquiry to get max target count. If the
- ** target count isn't 8 or 16 then go with a default of 8.
- */
- memset(&srbHAInquiry, 0, sizeof(SRB_HAInquiry));
- srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
- srbHAInquiry.SRB_HaId = HaId;
-
- ASPI_SendASPI32Command((LPSRB)&srbHAInquiry);
- if(srbHAInquiry.SRB_Status != SS_COMP)
- {
- continue;
- };
-
- MaxTarget = srbHAInquiry.HA_Unique[3];
- if(MaxTarget != 8 && MaxTarget != 16)
- {
- MaxTarget = 8;
- };
-
- /*
- ** Loop over all the targets on this host adapter.
- */
- for(Target = 0; Target < MaxTarget; Target++ )
- {
- /*
- ** Issue get device type call to see if there is a device we're
- ** interested in at this address. We're interested in CDROMs.
- */
- memset(&srbGDEVBlock, 0, sizeof(SRB_GDEVBlock));
- srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
- srbGDEVBlock.SRB_HaId = HaId;
- srbGDEVBlock.SRB_Target = Target;
-
- ASPI_SendASPI32Command((LPSRB)&srbGDEVBlock);
- if(srbGDEVBlock.SRB_Status != SS_COMP ||
- (srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM))
- {
- continue;
- };
-
- /*
- ** Determine the max transfer count of this target. It will
- ** be the min of the host adapters min count and the size
- ** of our global transfer buffer.
- */
- dwMaxTransferBytes = (DWORD)&srbHAInquiry.HA_Unique[4];
-
- /*
- ** Issue an INQUIRY.
- */
- memset(InquiryCDB, 0, 6);
- InquiryCDB[0] = SCSI_INQUIRY;
- InquiryCDB[4] = 36; //Size in bytes of inquiry buffer.
-
- //Send SCSI device inquiry command and wait for completion.
- bSRB_exec = SendASPICMD_and_wait
- (
- HaId,
- Target,
- SRB_DIR_IN,
- 36,
- InquiryBuf,
- 6,
- InquiryCDB
- );
-
- /*
- ** Make sure the inquiry worked. If it failed, or if the
- ** inquiry data returns a different device type than we got
- ** before (guards against certain device drivers and against
- ** vendor unique devices).
- */
- if(!bSRB_exec || (InquiryBuf[0] != DTYPE_CDROM))
- continue;
-
- /*
- ** Add this target to the screen.
- */
- WriteLog("Host adapter ID: %ld\n", HaId);
- WriteLog("Target ID : %ld\n", Target);
- WriteLog("LUN ID : 0\n");
- WriteLog("Max buffer size: %ld bytes\n", dwMaxTransferBytes);
-
- memcpy(szVendor, InquiryBuf + 8, 8);
- szVendor[8] = '\0'; //Terminate the string
-
- memcpy(szProduct, InquiryBuf + 16, 16);
- szProduct[16] = '\0'; //Terminate the string
-
- memcpy(szRev, InquiryBuf + 32, 4);
- szRev[4] = '\0'; //Terminate the string
-
- WriteLog("Vendor : %s\n", szVendor);
- WriteLog("Product : %s\n", szProduct);
- WriteLog("Revision : %s\n\n", szRev);
-
- n_CDROM_drives++;
- }
- }
-
- if (n_CDROM_drives == 0)
- WriteLog("No CDROM type drives found.\n");
- }
+ WriteLog("CDINTF: Could not import functions from WNASPI32.DLL!\n");
+ return false;
+ }
+
+ uint32 supportInfo = ASPI_GetASPI32SupportInfo(); // Initialize ASPI layer
+ uint8 retCode = (supportInfo >> 8) & 0xFF;
+ maxHostAdapters = supportInfo & 0xFF;
+
+ if (retCode != SS_COMP && retCode != SS_NO_ADAPTERS)
+ {
+ WriteLog("CDINTF: Could not initialise using GetASPI32SupportInfo function!\n");
+ return false;
+ }
+
+ if (retCode == SS_NO_ADAPTERS)
+ {
+ WriteLog("CDINTF: ASPI initialized, but no host adapters were found!\n");
+ return false;
+ }
+
+/* // Set timeouts for ALL devices to 15 seconds. Nothing we deal with should
+ // take that long to do ANYTHING. We are just doing inquiries to most
+ // devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
+ // if they have to spin up) should be plenty.
+
+ SRB_GetSetTimeouts srbTimeouts;
+//This doesn't seem to do anything, and isn't even mentioned in Adaptec's ASPI paper...
+//(It *is* mentioned elsewhere, in other Adaptec documentation, and it does nothing because it
+// errors out!)
+//It *does* return code $81 (SS_INVALID_HA) which means it doesn't like $FF for the HAID...
+//OK, it works with Adaptec's driver, but not the default MS driver...
+//Looks like we really don't need it anyway.
+//If we really wanted to, we could do it in CDIntfInit()...!
+ memset(&srbTimeouts, 0, sizeof(SRB_GetSetTimeouts));
+ srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
+ srbTimeouts.SRB_Flags = SRB_DIR_OUT;
+ srbTimeouts.SRB_HaId = 0xFF;
+ srbTimeouts.SRB_Target = 0xFF;
+ srbTimeouts.SRB_Lun = 0xFF;
+ srbTimeouts.SRB_Timeout = 15 * 2;
+ ASPI_SendASPI32Command(&srbTimeouts);
+ WriteLog("CDINTF: Set Timeout command returned %02X...\n", srbTimeouts.SRB_Status);//*/
+
+ WriteLog("CDINTF: Successfully initialized.\n");
+ return true;
}
//
-// 1. Sets up the CDB for MMC readcd (CDB12) command.
-// 2. Send the request to the drive.
-// 3. If success displays the sector data as hex on the screen.
+// Sends the passed in Command Description Block to the APSI layer for processing.
+// Since this uses the asynchronous EXEC_SCSI_CMD, we also wait for completion by
+// using a semaphore.
//
-BOOL ReadCD(BYTE HA_ID, BYTE Target_ID, long int MMC_LBA_sector)
+static bool SendAsyncASPICmd(uint8 hostID, uint8 targID, uint8 * cdb, uint8 CDBLen,
+ uint8 * buffer, uint32 bufferLen, uint8 SRBFlags)
{
- BYTE read_CDB12[12];
- long int MMC_LBA_sector2;
-
- //CDB with values for ReadCD CDB12 command. The values were taken from MMC1 draft paper.
- read_CDB12[0] = 0xBE; //Code for ReadCD CDB12 command
- read_CDB12[1] = 0;
-
- read_CDB12[5] = byte(MMC_LBA_sector); //Least sig byte of LBA sector no. to read from CD
- MMC_LBA_sector2 = MMC_LBA_sector >> 8;
- read_CDB12[4] = byte(MMC_LBA_sector2); //2nd byte of:
- MMC_LBA_sector2 = MMC_LBA_sector2 >> 8;
- read_CDB12[3] = byte(MMC_LBA_sector2); //3rd byte of:
- MMC_LBA_sector2 = MMC_LBA_sector2 >> 8;
- read_CDB12[2] = byte(MMC_LBA_sector2); //Most significant byte
-
- read_CDB12[6] = 0; //No. of sectors to read from CD byte 2 (MSB)
- read_CDB12[7] = 0; //No. of sectors to read from CD byte 1
- read_CDB12[8] = 1; //No. of sectors to read from CD byte 0 (LSB)
- read_CDB12[9] = 0xF8; //Raw read, 2352 bytes per sector
- read_CDB12[10] = 0; //Sub-channel selection bits.
- read_CDB12[11] = 0;
-
- return SendASPICMD_and_wait(HA_ID, Target_ID, SRB_DIR_IN, 2352, DataBuf, 12, read_CDB12);
+ SRB_ExecSCSICmd SRB; // The SRB variable with CDB included
+
+ memset(&SRB, 0, sizeof(SRB));
+ memcpy(SRB.CDBByte, cdb, CDBLen); // Copy CDB into SRB's CDB
+
+ SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ SRB.SRB_Flags = SRBFlags | SRB_EVENT_NOTIFY;
+ SRB.SRB_HaId = hostID;
+ SRB.SRB_Target = targID;
+ SRB.SRB_BufPointer = buffer;
+ SRB.SRB_BufLen = bufferLen;
+ SRB.SRB_CDBLen = CDBLen;
+ SRB.SRB_SenseLen = SENSE_LEN;
+
+ HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ if (!hEvent)
+ {
+ WriteLog("CDINTF: Couldn't create event!\n");
+ return false;
+ }
+
+ SRB.SRB_PostProc = (void *)hEvent;
+ ASPI_SendASPI32Command(&SRB);
+
+ if (SRB.SRB_Status == SS_PENDING)
+ WaitForSingleObject(hEvent, INFINITE);
+
+ CloseHandle(hEvent);
+
+ if (SRB.SRB_Status != SS_COMP)
+ {
+ WriteLog("CDINTF: SCSI command %02X failed [Error: %02X].\n", SRB.CDBByte[0], SRB.SRB_Status);
+ return false;
+ }
+
+ return true;
}
//
-// Initialize the SDL sound system
+// OS specific implementation of OS agnostic functions
//
-void __CDInit(void)
+
+bool CDIntfInit(void)
{
if (!InitASPI())
{
- WriteLog("Sound: Failed to init ASPI layer!\n");
- exit(1);//bad!
+ WriteLog("CDINTF: Failed to init Win32 ASPI layer!\n");
+ return false;
+ }
+
+ SRB_HAInquiry srbHAInquiry;
+ SRB_GDEVBlock srbGDEVBlock;
+ uint8 inquiryCDB[6], inquiryBuf[36];
+
+ for(uint8 HAID=0; HAID<maxHostAdapters; HAID++)
+ {
+ memset(&srbHAInquiry, 0, sizeof(srbHAInquiry));
+ srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
+ srbHAInquiry.SRB_HaId = HAID;
+
+ ASPI_SendASPI32Command(&srbHAInquiry);
+
+ if (srbHAInquiry.SRB_Status != SS_COMP)
+ continue;
+
+ // Do a host adapter inquiry to get max target count. If the
+ // target count isn't 8 or 16 then go with a default of 8.
+
+ uint8 maxTargets = srbHAInquiry.HA_Unique[3];
+ if (maxTargets != 8 && maxTargets != 16)
+ maxTargets = 8;
+
+ // Loop over all the targets on this host adapter.
+
+ for(uint8 target=0; target<maxTargets; target++)
+ {
+ // Issue get device type call to see if there is a device we're
+ // interested in at this address. We're interested in CDROMs.
+
+ memset(&srbGDEVBlock, 0, sizeof(srbGDEVBlock));
+ srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
+ srbGDEVBlock.SRB_HaId = HAID;
+ srbGDEVBlock.SRB_Target = target;
+
+ ASPI_SendASPI32Command(&srbGDEVBlock);
+
+ if (srbGDEVBlock.SRB_Status != SS_COMP || srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM)
+ continue;
+
+ memset(inquiryCDB, 0, 6); // Issue an INQUIRY.
+ inquiryCDB[0] = SCSI_INQUIRY;
+ inquiryCDB[4] = 36; // Size in bytes of inquiry buffer.
+
+ bool successful = SendAsyncASPICmd(HAID, target, inquiryCDB, 6, inquiryBuf, 36, SRB_DIR_IN);
+
+ // Make sure the inquiry worked. Check if it failed, or if the inquiry data
+ // returns a different device type than we got before (guards against certain
+ // device drivers and against vendor unique devices).
+
+ if (!successful || inquiryBuf[0] != DTYPE_CDROM)
+ continue;
+
+ haID[numCDDrives] = HAID, tID[numCDDrives] = target;
+
+ // Here we do a 'stringTrimRight' on the vendor and product strings...
+
+ uint32 vendorSize = 0, productSize = 0;
+
+ for(int i=7; i>=0; i--)
+ {
+ if (inquiryBuf[8+i] != ' ')
+ {
+ vendorSize = i + 1;
+ break;
+ }
+ }
+
+ for(int i=15; i>=0; i--)
+ {
+ if (inquiryBuf[16+i] != ' ')
+ {
+ productSize = i + 1;
+ break;
+ }
+ }
+
+ memcpy(driveName[numCDDrives], inquiryBuf + 8, vendorSize);
+ driveName[numCDDrives][vendorSize] = ' ';
+ memcpy(driveName[numCDDrives] + vendorSize + 1, inquiryBuf + 16, productSize);
+ driveName[numCDDrives][vendorSize + productSize + 1] = 0;
+
+ WriteLog("CDINTF: Found CD-ROM device [%s]. HAID:%u, TID:%u LUN:0\n", driveName[numCDDrives], haID[numCDDrives], tID[numCDDrives]);
+
+ numCDDrives++;
+ }
+ }
+
+ if (numCDDrives == 0)
+ {
+ WriteLog("CDINTF: No CDROM type drives found.\n");
+ return false;
}
+
+//Most likely, will need to read a default out of the config file. But for now... !!! FIX !!!
+ driveNum = 0; // For now, default to first drive found
+
+ return true;
}
-//
-// Close down the SDL sound subsystem
-//
-void __CDDone(void)
+void CDIntfDone(void)
{
- //Unload ASPI if it has been loaded.
- if (WNASPI32_handle)
- FreeLibrary(WNASPI32_handle);
+ if (hASPILib)
+ FreeLibrary(hASPILib); // Unload ASPI library if it was loaded.
}
-/*//
-// Sound card callback handler
-//
-void SDLSoundCallback(void * userdata, Uint8 * buffer, int length)
+bool CDIntfReadBlock(uint32 sector, uint8 * buffer)
+{
+ uint8 cdb[12];
+
+ memset(cdb, 0, sizeof(cdb));
+ // 0: command, 2-5: block # (hi->lo) 6-8: number of blocks to read, 9: read type,
+ // 10: subchannel select
+ cdb[0] = 0xBE; // Code for ReadCD CDB12 command
+ cdb[2] = (sector >> 24) & 0xFF;
+ cdb[3] = (sector >> 16) & 0xFF;
+ cdb[4] = (sector >> 8) & 0xFF;
+ cdb[5] = sector & 0xFF;
+ cdb[8] = 1; // No. of sectors to read from CD (LSB)
+ cdb[9] = 0xF8; // Raw read, 2352 bytes per sector
+ cdb[10] = 1; // Selects read RAW 96 bytes/sector sub-channel data (Raw P-W)
+
+ return SendAsyncASPICmd(haID[driveNum], tID[driveNum], cdb, 12, buffer, 2352+96, SRB_DIR_IN);
+}
+
+uint32 CDIntfGetNumSessions(void)
{
- if (remain > 0)
+// WriteLog("CDINTF: GetNumSessions unimplemented!\n");
+ // Still need relevant code here... !!! FIX !!! [DONE]
+ if (!readTOC)
+ GetRawTOC();
+
+ return numSessions - 1;
+}
+
+void CDIntfSelectDrive(uint32 driveNum)
+{
+ if (driveNum < numCDDrives)
+ driveNum = driveNum;
+}
+
+uint32 CDIntfGetCurrentDrive(void)
+{
+ return driveNum;
+}
+
+const uint8 * CDIntfGetDriveName(uint32 driveNum)
+{
+ if (driveNum > numCDDrives)
+ return NULL;
+
+ return driveName[driveNum];
+}
+
+//This stuff could probably be OK in the unified cdintf.cpp file...
+uint8 CDIntfGetSessionInfo(uint32 session, uint32 offset)
+{
+// Need better error handling than this... !!! FIX !!!
+ if (!readTOC)
+ if (!GetRawTOC())
+ return 0xFF;
+
+ if (session >= numSessions || offset > 4)
+ return 0xFF; // Bad index passed in...
+
+ return sessions[session][offset];
+}
+
+uint8 CDIntfGetTrackInfo(uint32 track, uint32 offset)
+{
+// Need better error handling than this... !!! FIX !!!
+ if (!readTOC)
+ if (!GetRawTOC())
+ return 0xFF;
+
+ if (track > numTracks || offset > 2)
+ return 0xFF; // Bad index passed in...
+
+ return tracks[track][offset];
+}
+
+//OK, now the rest is OK, but this is still locking up like a MF!
+// Testing, testing...
+//Still don't know why this is locking up! Especially as the following function works! Aarrrgggghhhhh!
+//It was the dataLen. For some reason, it needs at *least* 11 more bytes (could be less!)
+/*static*/ bool GetRawTOC(void)
+{
+ uint8 cmd[10];
+ uint8 reqData[4];
+
+ // Read disk TOC length
+ memset(cmd, 0, 10);
+ cmd[0] = SCSI_READ_TOC;
+ cmd[2] = 2; // Get session info also
+ cmd[6] = 1; // Session # to start reading
+ cmd[8] = 4; // Buffer length
+
+ if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, reqData, 4, SRB_DIR_IN))
{
- memcpy(buffer, DataBuf + 2352 - remain, remain);
- length -= remain;
- buffer += remain;
- remain = 0;
+ WriteLog("TOC: Cannot read disk TOC length.\n");
+ return false;
}
- while (length > 0)
+ // Header is total TOC space needed + header (0-1), min session (2), max session (3)
+// uint32 dataLen = ((reqData[0] << 8) | reqData[1]);
+ uint32 dataLen = ((reqData[0] << 8) | reqData[1]) + 11; // Why the extra???
+
+ WriteLog("TOC: Raw TOC data len: %d\n", dataLen);
+
+ uint8 * data = new uint8[dataLen];
+
+ // Read disk TOC
+ cmd[7] = dataLen >> 8;
+ cmd[8] = dataLen;
+
+ if (!SendAsyncASPICmd(haID[driveNum], tID[driveNum], cmd, 10, data, dataLen, SRB_DIR_IN))
{
- ReadCD(0, 1, sector++);
- memcpy(buffer, DataBuf, (length >= 2352 ? 2352 : length));
- length -= 2352;
- buffer += 2352;
+ delete[] data;
+ WriteLog("TOC: Cannot read disk TOC.\n");
+ return false;
}
- if (length < 0)
- remain = -length;
-}*/
+ int numEntries = (((data[0] << 8) | data[1]) - 2) / 11;
+ uint8 * p = data + 4;
+
+ numSessions = data[3], numTracks = 0;
+ // Important entries are 0, 3, 8, 9, 10 (session #, track #, M, S, F)
+// WriteLog("TOC: [Sess] [adrCtl] [?] [point] [?] [?] [?] [?] [pmin] [psec] [pframe]\n");
+ uint32 firstTrackOffset = 0;
+ for(int i=0; i<numEntries; i++, p+=11)
+ {
+/* WriteLog("TOC: %d %02x %02d %2x %02d:%02d:%02d %02d %02d:%02d:%02d",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10]);
+
+ if (p[3] > 0 && p[3] < 99)
+ WriteLog(" <-- Track #%u", p[3]);
+
+ WriteLog("\n");//*/
+
+ // We do session # - 1 to make it zero-based, since this is what the Jaguar
+ // CD BIOS expects. We leave the tracks one-based.
+
+ uint32 s = p[0] - 1, t = p[3];
+
+ if (t < 100)
+ {
+ if (t == 1)
+ firstTrackOffset = (((p[8] * 60) + p[9]) * 75) + p[10];
+
+ tracks[t][0] = p[8], tracks[t][1] = p[9], tracks[t][2] = p[10];
+
+ // For some reason, the TOC returned from the "session TOC" command
+ // causes all tracks to have a 150 block (from what I've seen) offset
+ // from what's reported. Apparently it's not possible to read those
+ // first 150 blocks using the current incarnation of CDIntf_ReadBlock.
+ // So we subtract the offset out here...
+
+ uint32 curTrack = (((tracks[t][0] * 60) + tracks[t][1]) * 75) + tracks[t][2];
+ curTrack -= firstTrackOffset;
+ tracks[t][2] = curTrack % 75;
+ curTrack /= 75;
+ tracks[t][1] = curTrack % 60;
+ tracks[t][0] = curTrack / 60;
+
+ if (t > numTracks)
+ numTracks = t;
+ }
+ else if (t == 0xA0)
+ sessions[s][0] = p[8];
+ else if (t == 0xA1)
+ sessions[s][1] = p[8];
+ else if (t == 0xA2)
+ sessions[s][2] = p[8], sessions[s][3] = p[9], sessions[s][4] = p[10];
+ }
+
+WriteLog("CDINTF: Disc summary\n # of sessions: %u, # of tracks: %u\n", numSessions, numTracks);
+WriteLog(" Session info:\n");
+for(uint32 i=0; i<numSessions; i++)
+ WriteLog(" %u: min track=%2u, max track=%2u, lead out=%2d:%02d:%02d\n", i+1, sessions[i][0], sessions[i][1], sessions[i][2], sessions[i][3], sessions[i][4]);
+WriteLog(" Track info:\n");
+for(uint32 i=1; i<=numTracks; i++)
+ WriteLog(" %2u: start=%2d:%02d:%02d\n", i, tracks[i][0], tracks[i][1], tracks[i][2]);
+
+ delete[] data;
+ readTOC = true;
+
+ return true;
+}