This repository has been archived on 2025-05-23. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cdtoimg/cdtoimg.cpp

889 lines
30 KiB
C++
Raw Normal View History

2013-10-13 01:51:01 +01:00
/*
CDToImg v1.01.
22 Oct 2006.
Written by Truman.
Language and type: MS Visual Studio .NET 2002, Visual C++ v7, mixed C and C++,
Application type : Win32 console.
A program that reads an entire CD-ROM (mode 1 or mode 2 form 1) 2048 bytes per
sector and writes to a file - this would be an .ISO file. The CD must be CD-ROM
mode 1 or mode 2 and non-multisession. Note, it does not check if the CD is
valid. TAO writing mode creates link blocks and this program does not detect
them so if encountered will be considered as a normal sector or unreadable.
Unreadable sectors are not supported and will terminate the process. Only up to
80 minutes reading is supported for the moment. Note that some drives will
ignore certain read speeds, e.g. my Plextor 755A can only be set to a minimum
of 4x read speed. On some drives, reading the last few sectors or ejecting the
disc when reading in 2048 byte/sector mode results in a hang (e.g.: LG CDROM
drive: HL-DT-ST CDROM GCR-8485B 1.05).
As always I do not take any responsibilities if this tool destroys your drive or
even anything else.
This code uses:
- Win32 IOCTL function with SCSI_PASS_THROUGH_DIRECT.
- The SCSI codes used in this source were taken from the draft documents MMC1,
SPC1 and SAM1.
- MMC1 Read CD command (0xBE, CDB 12) to read sectors (2048 user bytes mode).
- Determine errors, retrieve and decode a few sense data.
Normally you need the ntddscsi.h file from Microsoft DDK CD, but I shouldn't
distribute it, so instead I have written my own my_ntddscsi.h.
If you don't have windows.h some of the define constants are listed as comments.
This is a Win32 console program and only runs in a DOS prompt under Windows
NT4/2K/XP/2003 with appropriate user rights, i.e. you need to log in as
administrator.
Read readme.txt for more info.
*/
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include "my_ntddscsi.h"
#include "sam.h"
#include "FloatUtils.h"
//Global variables..
T_SPDT_SBUF sptd_sb; //Includes sense buffer
unsigned char *data_buf; //Buffer for holding transfer data from or to drive.
/*
1. Check the drive type to see if it's identified as a CDROM drive
2. Uses Win32 CreateFile function to get a handle to a drive
that is specified by cDriveLetter.
If you don't have windows.h, some of the define constants are
listed as comments.
*/
HANDLE open_volume(char drive_letter)
{
HANDLE hVolume;
UINT uDriveType;
char szVolumeName[8];
char szRootName[5];
//Make drive_letter as a null terminated string in the form of e.g.: d:\.
szRootName[0]=drive_letter;
szRootName[1]=':';
szRootName[2]='\\';
szRootName[3]='\0';
uDriveType = GetDriveType(szRootName);
//#define GENERIC_READ 0x80000000L
//#define GENERIC_WRITE 0x40000000L
switch(uDriveType)
{
case DRIVE_CDROM:
{
printf("Drive type is recognised as CDROM/DVD.\n");
break;
}
default:
{
printf("Drive type is not CDROM/DVD, aborting.\n");
return INVALID_HANDLE_VALUE; //#define INVALID_HANDLE_VALUE (long int *)-1
}
}
//Make drive_letter as a null terminated string in the form of e.g.: \\.\d:.
szVolumeName[0]='\\';
szVolumeName[1]='\\';
szVolumeName[2]='.';
szVolumeName[3]='\\';
szVolumeName[4]=drive_letter;
szVolumeName[5]=':';
szVolumeName[6]='\0';
//#define FILE_SHARE_READ 0x00000001
//#define FILE_SHARE_WRITE 0x00000002
//#define OPEN_EXISTING 3
//#define FILE_ATTRIBUTE_NORMAL 0x00000080
//This will only work for CD/DVD device on W2K and higher.
hVolume = CreateFile(szVolumeName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hVolume == INVALID_HANDLE_VALUE)
{
//Try again for NT4..
hVolume = CreateFile(szVolumeName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hVolume == INVALID_HANDLE_VALUE)
{
printf("Could not create handle for CD/DVD device.\n");
}
}
return hVolume;
}
/* Displays sense error information. */
void disp_sense()
{
unsigned char key;
unsigned char ASC;
unsigned char ASCQ;
key=sptd_sb.SenseBuf[2] & 0x0F; //Sense key is only the lower 4 bits
ASC=sptd_sb.SenseBuf[12];
ASCQ=sptd_sb.SenseBuf[13];
printf("Sense data, key:ASC:ASCQ: %02X:%02X:%02X", key, ASC, ASCQ);
//Decode sense key:ASC:ASCQ.
//It's a very short list - I'm just trying to show you how to decode into text.
//You really need to look into MMC document and change this into an exhaustive list from
//the sense error table that is found in there.
if(key==SEN_KEY_NO_SEN)
{
if(ASC==0x00)
{
if(ASCQ==0x00)
{
printf(" - No additional sense info."); //No errors
}
}
}
else
if(key==SEN_KEY_NOT_READY)
{
if(ASC==0x3A)
{
if(ASCQ==0x00)
{
printf(" - Medium not present.");
}
else
if(ASCQ==0x01)
{
printf(" - Medium not present-tray closed.");
}
else
if(ASCQ==0x02)
{
printf(" - Medium not present-tray open.");
}
}
}
printf("\n");
}
/*
1. Set up the sptd values.
2. Set up the CDB for SPC1 test unit ready command.
3. Send the request to the drive.
*/
BOOL test_unit_ready(HANDLE hVolume)
{
DWORD dwBytesReturned;
sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.PathId=0; //SCSI card ID will be filled in automatically.
sptd_sb.sptd.TargetId=0; //SCSI target ID will also be filled in.
sptd_sb.sptd.Lun=0; //SCSI lun ID will also be filled in.
sptd_sb.sptd.CdbLength=6; //CDB size.
sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN; //Maximum length of sense data to retrieve.
sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_UNSPECIFIED; //There will be no buffer data to/from drive.
sptd_sb.sptd.DataTransferLength=0; //Size of buffer transfer data.
sptd_sb.sptd.TimeOutValue=108000; //SCSI timeout value (max 108000 sec = time 30 min).
sptd_sb.sptd.DataBuffer=(PVOID)data_buf;
sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
//CDB with values for Test Unit Ready CDB6 command.
//The values were taken from SPC1 draft paper.
sptd_sb.sptd.Cdb[0]=0x00; //Code for Test Unit Ready CDB6 command.
sptd_sb.sptd.Cdb[1]=0;
sptd_sb.sptd.Cdb[2]=0;
sptd_sb.sptd.Cdb[3]=0;
sptd_sb.sptd.Cdb[4]=0;
sptd_sb.sptd.Cdb[5]=0;
sptd_sb.sptd.Cdb[6]=0;
sptd_sb.sptd.Cdb[7]=0;
sptd_sb.sptd.Cdb[8]=0;
sptd_sb.sptd.Cdb[9]=0;
sptd_sb.sptd.Cdb[10]=0;
sptd_sb.sptd.Cdb[11]=0;
sptd_sb.sptd.Cdb[12]=0;
sptd_sb.sptd.Cdb[13]=0;
sptd_sb.sptd.Cdb[14]=0;
sptd_sb.sptd.Cdb[15]=0;
ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);
//Send the command to drive.
return DeviceIoControl(hVolume,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
&dwBytesReturned,
NULL);
}
/*
1. Set up the sptd values.
2. Set up the CDB for MMC1 set CD speed command.
3. Send the request to the drive.
*/
BOOL set_cd_speed(HANDLE hVolume,
unsigned short int in_read_speed,
unsigned short int in_write_speed)
{
DWORD dwBytesReturned;
sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.PathId=0; //SCSI card ID will be filled in automatically.
sptd_sb.sptd.TargetId=0; //SCSI target ID will also be filled in.
sptd_sb.sptd.Lun=0; //SCSI lun ID will also be filled in.
sptd_sb.sptd.CdbLength=12; //CDB size
sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN; //Return sense buffer length.
sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_UNSPECIFIED; //There will be no buffer data to/from drive.
sptd_sb.sptd.DataTransferLength=0; //Size of buffer transfer data.
sptd_sb.sptd.TimeOutValue=108000; //SCSI timeout value (max 108000 sec = time 30 min).
sptd_sb.sptd.DataBuffer=(PVOID)data_buf;
sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
//CDB with values for set cd speed command.
//The values were taken from MMC1 draft paper.
sptd_sb.sptd.Cdb[0]=0xBB; //Code for set cd speed command.
sptd_sb.sptd.Cdb[1]=0;
sptd_sb.sptd.Cdb[2]=(unsigned char)(in_read_speed>>8);
sptd_sb.sptd.Cdb[3]=(unsigned char)in_read_speed;
sptd_sb.sptd.Cdb[4]=(unsigned char)(in_write_speed>>8);
sptd_sb.sptd.Cdb[5]=(unsigned char)in_write_speed;
sptd_sb.sptd.Cdb[6]=0;
sptd_sb.sptd.Cdb[7]=0;
sptd_sb.sptd.Cdb[8]=0;
sptd_sb.sptd.Cdb[9]=0;
sptd_sb.sptd.Cdb[10]=0;
sptd_sb.sptd.Cdb[11]=0;
sptd_sb.sptd.Cdb[12]=0;
sptd_sb.sptd.Cdb[13]=0;
sptd_sb.sptd.Cdb[14]=0;
sptd_sb.sptd.Cdb[15]=0;
ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);
//Send the command to drive
return DeviceIoControl(hVolume,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
&dwBytesReturned,
NULL);
}
/*
1. Set up the sptd values.
2. Set up the CDB for MMC1 read TOC/PMA/ATIP command.
3. Send the request to the drive.
*/
BOOL read_TOC_PMA_ATIP(HANDLE hVolume,
unsigned char in_format,
unsigned char in_trk_sess_no,
unsigned short int in_data_trans_len)
{
DWORD dwBytesReturned;
sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.PathId=0; //SCSI card ID will be filled in automatically.
sptd_sb.sptd.TargetId=0; //SCSI target ID will also be filled in.
sptd_sb.sptd.Lun=0; //SCSI lun ID will also be filled in.
sptd_sb.sptd.CdbLength=10; //CDB size.
sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN; //Maximum length of sense data to retrieve.
sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_IN; //There will be data from drive.
sptd_sb.sptd.DataTransferLength=in_data_trans_len; //Size of input data from drive.
sptd_sb.sptd.TimeOutValue=108000; //SCSI timeout value (max 108000 sec = time 30 min).
sptd_sb.sptd.DataBuffer=(PVOID)data_buf;
sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
//CDB with values for READ TOC/PMA/ATIP CDB10 command.
//The values were taken from MMC draft paper.
sptd_sb.sptd.Cdb[0]=0x43; //Code for READ TOC/PMA/ATIP CDB10 command.
sptd_sb.sptd.Cdb[1]=0;
sptd_sb.sptd.Cdb[2]=in_format; //Format code.
sptd_sb.sptd.Cdb[3]=0;
sptd_sb.sptd.Cdb[4]=0;
sptd_sb.sptd.Cdb[5]=0;
sptd_sb.sptd.Cdb[6]=in_trk_sess_no;
sptd_sb.sptd.Cdb[7]=(unsigned char)(in_data_trans_len >> 8); //MSB of max length of bytes to receive.
sptd_sb.sptd.Cdb[8]=(unsigned char)in_data_trans_len; //LSB of max length of bytes to receive.
sptd_sb.sptd.Cdb[9]=0;
sptd_sb.sptd.Cdb[10]=0;
sptd_sb.sptd.Cdb[11]=0;
sptd_sb.sptd.Cdb[12]=0;
sptd_sb.sptd.Cdb[13]=0;
sptd_sb.sptd.Cdb[14]=0;
sptd_sb.sptd.Cdb[15]=0;
ZeroMemory(data_buf, in_data_trans_len);
ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);
//Send the command to drive.
return DeviceIoControl(hVolume,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
&dwBytesReturned,
NULL);
}
/*
1. Set up the sptd values.
2. Set up the CDB for MMC1 read CD (0xBE, CDB12) command.
3. Send the request to the drive.
*/
BOOL read_cd_2048(HANDLE hVolume,
long int MMC_LBA_sector,
unsigned long int n_sectors,
unsigned char subch_sel_bits)
{
DWORD dwBytesReturned;
long int MMC_LBA_sector2;
unsigned long int n_sectors2;
sptd_sb.sptd.Length=sizeof(SCSI_PASS_THROUGH_DIRECT);
sptd_sb.sptd.PathId=0; //SCSI card ID will be filled in automatically.
sptd_sb.sptd.TargetId=0; //SCSI target ID will also be filled in.
sptd_sb.sptd.Lun=0; //SCSI lun ID will also be filled in.
sptd_sb.sptd.CdbLength=12; //CDB size.
sptd_sb.sptd.SenseInfoLength=MAX_SENSE_LEN; //Maximum length of sense data to retrieve.
sptd_sb.sptd.DataIn=SCSI_IOCTL_DATA_IN; //There will be data coming from the drive.
sptd_sb.sptd.DataTransferLength=2048*n_sectors; //Size of read data.
sptd_sb.sptd.TimeOutValue=108000; //SCSI timeout value (max 108000 sec = time 30 min).
sptd_sb.sptd.DataBuffer=(PVOID)data_buf;
sptd_sb.sptd.SenseInfoOffset=sizeof(SCSI_PASS_THROUGH_DIRECT);
//CDB with values for Read CD command. The values were taken from MMC1 draft paper.
sptd_sb.sptd.Cdb[0]=0xBE; //Code for Read CD command.
sptd_sb.sptd.Cdb[1]=0;
//Fill in starting MMC sector (CDB[2] to CDB[5])..
sptd_sb.sptd.Cdb[5]=(unsigned char)MMC_LBA_sector; //Least sig byte of LBA sector no. to read from CD.
MMC_LBA_sector2=MMC_LBA_sector>>8;
sptd_sb.sptd.Cdb[4]=(unsigned char)MMC_LBA_sector2; //2nd byte.
MMC_LBA_sector2=MMC_LBA_sector2>>8;
sptd_sb.sptd.Cdb[3]=(unsigned char)MMC_LBA_sector2; //3rd byte.
MMC_LBA_sector2=MMC_LBA_sector2>>8;
sptd_sb.sptd.Cdb[2]=(unsigned char)MMC_LBA_sector2; //Most significant byte.
//Fill in no. of sectors to read (CDB[6] to CDB[8])..
sptd_sb.sptd.Cdb[8]=(unsigned char)n_sectors; //No. of sectors to read from CD byte 0 (LSB).
n_sectors2=n_sectors>>8;
sptd_sb.sptd.Cdb[7]=(unsigned char)n_sectors2; //No. of sectors to read from CD byte 1.
n_sectors2=n_sectors2>>8;
sptd_sb.sptd.Cdb[6]=(unsigned char)n_sectors2; //No. of sectors to read from CD byte 2 (MSB).
sptd_sb.sptd.Cdb[9]=0x10; //Read user data only, 2048 bytes per sector from CDROM.
sptd_sb.sptd.Cdb[10]=subch_sel_bits; //Sub-channel selection bits.
sptd_sb.sptd.Cdb[11]=0;
sptd_sb.sptd.Cdb[12]=0;
sptd_sb.sptd.Cdb[13]=0;
sptd_sb.sptd.Cdb[14]=0;
sptd_sb.sptd.Cdb[15]=0;
ZeroMemory(data_buf, 2048*n_sectors);
ZeroMemory(sptd_sb.SenseBuf, MAX_SENSE_LEN);
//Send the command to drive.
return DeviceIoControl(hVolume,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
(PVOID)&sptd_sb, (DWORD)sizeof(sptd_sb),
&dwBytesReturned,
NULL);
}
/*
Calculating data rate of digital stereo (2 channels) audio at 44.1 KHz with 16 BITs per channel.
------------------------------------------------------------------------------------------------
Number of bytes taken up by 1 sample of audio:
16 BIT = 1 word = 2 bytes, but there are 2 channels, so 1 sample takes up
2*2=4 bytes.
Data rate in kilobytes/sec will be:
s=Number of samples per second
b=Number of bytes per sample
s*b
---- = data rate kb/s
1000
So data rate of the digital audio is:
44100*4
------- = 176.4 kb/s
1000
This data rate is used by CDROM/CD writers as 1X speed. This will of course have to be rounded to
a whole number: 176 kb/s, making prog. easier. Note, kb is in units of 1000.
*/
unsigned short int kbytes_2_x_speed(unsigned short int speed_kbytes)
{
return RoundDouble(speed_kbytes/176.4, 0);
}
unsigned short int x_2_kbytes_speed(unsigned short int x_speed)
{
return RoundDouble(x_speed*176.4, 0);
}
BOOL verified_set_cd_speed(HANDLE hVolume,
unsigned short int in_read_speed,
unsigned short int in_write_speed)
{
BOOL success;
printf("Sending MMC1 CD speed command ");
if(in_read_speed==0xFFFF)
{
printf("(read: max speed, ");
}
else
{
printf("(read: %ukbytes (%ux)", in_read_speed, kbytes_2_x_speed(in_read_speed));
}
if(in_write_speed==0xFFFF)
{
printf("write: max speed)..");
}
else
{
printf("write: %ukbytes (%ux))..", in_write_speed, kbytes_2_x_speed(in_write_speed));
}
//Sends MMC1 set CD speed command to drive.
success=set_cd_speed(hVolume, in_read_speed, in_write_speed);
if(success)
{
printf("done.\n");
if(sptd_sb.sptd.ScsiStatus==STATUS_GOOD)
{
//Do nothing.
}
else
{
success=FALSE;
if(sptd_sb.sptd.ScsiStatus==STATUS_CHKCOND)
{
disp_sense();
}
else
{
printf("Command sent but returned with an unhandled status code: %02X\n", sptd_sb.sptd.ScsiStatus);
}
}
}
else
{
printf("failed.\n");
printf("DeviceIOControl returned with failed status.\n");
}
return success;
}
/* Sends Read TOC/PMA/ATIP command to read TOC, check & display errors and return the success state. */
BOOL verified_read_TOC(HANDLE hVolume,
unsigned long int data_buffer_size)
{
BOOL success;
unsigned short int alloc_len=0;
printf("Sending read TOC command..");
//Sends MMC1 READ TOC/PMA/ATIP command to drive to get 4 byte header.
success=read_TOC_PMA_ATIP(hVolume, 0, 0, 4);
if(success)
{
if(sptd_sb.sptd.ScsiStatus==STATUS_GOOD)
{
alloc_len=data_buf[0] << 8;
alloc_len=alloc_len | data_buf[1];
}
else
{
success=FALSE;
printf("done.\n");
if(sptd_sb.sptd.ScsiStatus==STATUS_CHKCOND)
{
disp_sense();
}
else
{
printf("Command sent but returned with an unhandled status code: %02X\n", sptd_sb.sptd.ScsiStatus);
}
}
}
else
{
printf("failed.\n");
printf("Could not return 4 byte Read TOC header.\n");
}
if(success && (alloc_len>0))
{
//Limit alloc len to maximum allowed by size of data transfer buffer length.
if((alloc_len+2)>data_buffer_size)
{
alloc_len=data_buffer_size-2;
}
//Sends MMC1 READ TOC/PMA/ATIP command to drive to get full data.
success=read_TOC_PMA_ATIP(hVolume, 0, 0, alloc_len+2);
if(success)
{
printf("done.\n");
if(sptd_sb.sptd.ScsiStatus==STATUS_GOOD)
{
alloc_len=data_buf[0] << 8;
alloc_len=alloc_len | data_buf[1];
}
else
{
success=FALSE;
if(sptd_sb.sptd.ScsiStatus==STATUS_CHKCOND)
{
disp_sense();
}
else
{
printf("Command sent but returned with an unhandled status code: %02X\n", sptd_sb.sptd.ScsiStatus);
}
}
}
else
{
printf("failed.\n");
printf("Could only return 4 byte Read TOC header.\n");
}
}
return success;
}
/* Sends Test Unit Ready command 3 times, check for errors & display error info. */
BOOL verified_test_unit_ready3(HANDLE hVolume)
{
unsigned char i=3;
BOOL success;
/*
Before sending the required command, here we clear any pending sense info from the drive
which may interfere by sending Test Unit Ready command at least 3 times if neccessary.
----------------------------------------------------------------------------------------*/
do
{
printf("Sending SPC1 Test Unit CDB6 command..");
//Sends SPC1 Test Unit Ready command to drive
success=test_unit_ready(hVolume);
if(success)
{
printf("done.\n");
if(sptd_sb.sptd.ScsiStatus==STATUS_GOOD)
{
printf("Returned good status.\n");
i=1;
}
else
{
if(sptd_sb.sptd.ScsiStatus==STATUS_CHKCOND)
{
disp_sense();
}
else
{
printf("Command sent but returned with an unhandled status code: %02X\n", sptd_sb.sptd.ScsiStatus);
}
success=FALSE;
}
}
else
{
printf("failed.\n");
printf("DeviceIOControl returned with failed status.\n");
}
i--;
}while(i>0);
return success;
}
//Find lead-out from read TOC data.
BOOL find_leadout_from_TOC(unsigned char *data_buf,
unsigned char &out_n_tracks,
unsigned long int &out_n_sectors)
{
unsigned long int i;
unsigned long int sector;
unsigned short int alloc_len;
unsigned short int TOC_response_data_len;
BOOL found_leadout;
alloc_len=data_buf[0] << 8;
alloc_len=alloc_len | data_buf[1];
TOC_response_data_len=alloc_len+2;
found_leadout=FALSE;
out_n_tracks=0;
//Iterate thru TOC entries to find track 0xAA (leadout track)..
for(i=4;i<TOC_response_data_len;i=i+8)
{
//Check if we have a complete 8 byte TOC track descriptor page.
if((i+7)<TOC_response_data_len)
{
//Look for ADR Q mode 1 entries only.
if((data_buf[i+1] & 0xF0)==0x10)
{
//Look for track no. 0xAA (lead-out track).
if(data_buf[i+2]==0xAA)
{
//Get the starting sector of track no. 0xAA.
sector=data_buf[i+4];
sector=sector<<8;
sector=sector | data_buf[i+5];
sector=sector<<8;
sector=sector | data_buf[i+6];
sector=sector<<8;
sector=sector | data_buf[i+7];
//Check if sector is 0.
if(sector>0)
{
//Total sectors before lead-out.
out_n_sectors=sector;
}
else
{
//Error, this should never happen.
out_n_sectors=0;
}
out_n_tracks--; //Don't include this track.
found_leadout=TRUE;
}
out_n_tracks++;
}
}
}
return found_leadout;
}
/*
Main loop of reading the CD and writing to image file.
Various error checking are also done here.
*/
BOOL read_cd_to_image(char drive_letter, char *file_pathname, unsigned short int x_speed, unsigned long int data_buffer_size)
{
HANDLE hVolume;
BOOL success;
unsigned char n_tracks; //Total tracks.
unsigned long int n_sectors; //Total sectors on CD.
FILE *file_ptr;
unsigned long int LBA_i; //For counting "from" LBA (starts from 0).
unsigned long int LBA_i2; //For calculating "to" LBA.
unsigned long int n_sectors_to_read; //No. of sectors to read per read command.
unsigned short int speed_kbytes; //Write speed in kbytes.
hVolume = open_volume(drive_letter);
if (hVolume != INVALID_HANDLE_VALUE)
{
printf("\n");
success=verified_test_unit_ready3(hVolume);
printf("\n");
if(success)
{
//Get TOC from CD.
success=verified_read_TOC(hVolume, data_buffer_size);
if(success)
{
if(find_leadout_from_TOC(data_buf, n_tracks, n_sectors))
{
printf("Total user tracks : %u\n", n_tracks);
printf("Total sectors : %u\n", n_sectors);
//Do we need to set CD speed?
if(x_speed!=0)
{
if(x_speed==0xFFFF)
{
speed_kbytes=0xFFFF; //Use maximum write speed.
}
else
{
//Convert x speed to kbytes speed.
speed_kbytes=x_2_kbytes_speed(x_speed);
}
//Attempt to set the cd read speed to specified and write speed to max.
success=verified_set_cd_speed(hVolume, speed_kbytes, 0xFFFF);
}
else
{
//No need to set CD speed.
success=TRUE;
}
if(success)
{
file_ptr=fopen(file_pathname, "wb");
if(file_ptr!=NULL)
{
n_sectors_to_read=data_buffer_size / 2048; //Block size: E.g.: 65536 / 2048 = 32.
LBA_i=0; //Starting LBA address.
while(LBA_i<n_sectors)
{
//Check if block size is suitable for the remaining sectors.
if(n_sectors_to_read>(n_sectors-LBA_i))
{
//Alter to the remaining sectors.
n_sectors_to_read=n_sectors-LBA_i;
}
LBA_i2=LBA_i+n_sectors_to_read-1;
printf("Reading sector %u to %u (total: %u, progress: %.1f%%)\n", LBA_i, LBA_i2, n_sectors, (double)LBA_i2/n_sectors*100);
if(read_cd_2048(hVolume, LBA_i, n_sectors_to_read, 0))
{
if(sptd_sb.sptd.ScsiStatus==STATUS_GOOD)
{
fwrite(data_buf, 2048*n_sectors_to_read, 1, file_ptr);
if(ferror(file_ptr))
{
printf("Write file error!\n");
printf("Aborting process.\n");
success=FALSE;
break; //Stop while loop.
}
}
else
{
if(sptd_sb.sptd.ScsiStatus==STATUS_CHKCOND)
{
disp_sense();
}
else
{
printf("Command sent but returned with an unhandled status code: %02X\n", sptd_sb.sptd.ScsiStatus);
}
printf("Aborting process.\n");
success=FALSE;
break; //Stop while loop.
}
}
LBA_i=LBA_i+n_sectors_to_read;
}
fclose(file_ptr);
}
else
{
printf("Could not create file!\n");
printf("Aborting process.\n");
success=FALSE;
}
}
else
{
printf("Could not set read speed!\n");
printf("Aborting process.\n");
}
}
else
{
printf("Could not find lead-out entry in TOC to determine total sectors on CD!\n");
printf("Aborting process.\n");
success=FALSE;
}
}
else
{
printf("Could not read TOC!\n");
printf("Aborting process.\n");
}
}
else
{
printf("Drive is not ready!\n");
printf("Aborting process.\n");
}
CloseHandle(hVolume); //Win32 function.
}
else
{
return FALSE;
}
return success;
}
void usage()
{
printf("CDToImg v1.01. 22 Oct 2006.\n");
printf("Usage: cdtoimg <drive letter> <output file> [x read speed]\n");
printf("x speed is one of the following:\n");
printf(" - Enter CD x speed value.\n");
printf(" - Ommit or enter 0 to use currently set speed.\n");
printf(" - Enter m for max speed.\n");
return ; //Exit program here.
}
int main(int argc, char *argv[])
{
unsigned short int x_speed;
if((argc == 3) || (argc == 4))
{
//65536 data transfer buffer.
data_buf=(unsigned char *)malloc(65536);
if(argc == 3)
{
x_speed=0; //Use currently set write speed.
}
else
{
if(x_speed=argv[3][0]=='m')
{
x_speed=0xFFFF; //Use maximum write speed.
}
else
{
x_speed=atoi(argv[3]); //Use specified write speed.
}
}
if(read_cd_to_image(argv[1][0], argv[2], x_speed, 65536))
{
printf("Process finished.");
}
else
{
printf("Could not create image from drive %c.", argv[1][0]);
}
free(data_buf);
return 0;
}
else
{
usage();
return -1; //Exit program here.
}
}