/*++ Copyright (c) 1995 Microsoft Corporation Module Name: burn.c Abstract: A user mode app that allows simple commands to be sent to a selected scsi device. Environment: User mode only Revision History: 03-26-96 : Created --*/ #include #include #include #include #include #include #include "burn.h" #include "sptlib.h" OPTIONS gOptions; #define MAX_CD_IMAGE_SIZE (700 * 1024 * 1024) #define LEAD_IN_SIZE 150 #define POST_GAP_SIZE 150 #define DEFAULT_WRITE_SIZE (64 * 1024) #define IS_TEST_BURN FALSE #define BLOCKS_FROM_BYTES(B) ((B) >> 11) #define BYTES_FROM_BLOCKS(B) ((B) << 11) typedef struct _SENSE_STUFF { UCHAR Sense; UCHAR Asc; UCHAR Ascq; UCHAR Reserved; } SENSE_STUFF, *PSENSE_STUFF; SENSE_STUFF AllowedBurnSense[] = { {SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS, 0}, {SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_OPERATION_IN_PROGRESS, 0} }; #define AllowedBurnSenseEntries (sizeof(AllowedBurnSense)/sizeof(SENSE_STUFF)) SENSE_STUFF AllowedReadDiscInfo[] = { { SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_OPERATION_IN_PROGRESS, 0 }, { SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS, 0 }, { SCSI_SENSE_NOT_READY, SCSI_ADSENSE_LUN_NOT_READY, SCSI_SENSEQ_FORMAT_IN_PROGRESS, 0 }, { SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ADSENSE_ILLEGAL_MODE_FOR_THIS_TRACK, 0, 0 }, { SCSI_SENSE_UNIT_ATTENTION, SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION, 0, 0 } }; #define AllowedReadDiscInfoEntries (sizeof(AllowedReadDiscInfo)/sizeof(SENSE_STUFF)) BOOLEAN IsSenseDataInTable( IN PSENSE_STUFF Table, IN LONG Entries, // in table IN PSENSE_DATA SenseData ) { LONG i; UCHAR sense = SenseData->SenseKey & 0xf; UCHAR asc = SenseData->AdditionalSenseCode; UCHAR ascq = SenseData->AdditionalSenseCodeQualifier; for (i = 0; i < Entries; i++ ) { if ((Table[i].Sense == sense) && (Table[i].Ascq == ascq ) && (Table[i].Asc == asc ) ) { return TRUE; } } return FALSE; } __inline DWORD MakeCdSpeed( IN DWORD Speed ) { Speed *= (75 * 2352); // this makes it the proper speed Speed += 500; // rounding... Speed /= 1000; // yes, this is by 1000, not 1024! return Speed; } #if DBG #define OUTPUT stderr #define FPRINTF(x) fprintf x #define PRINTBUFFER(x) PrintBuffer x #else #define OUTPUT stdout #define FPRINTF(x) #define PRINTBUFFER(x) #endif VOID InitializeOptions( ) { RtlZeroMemory(&gOptions, sizeof(OPTIONS)); gOptions.BurnSpeed = OPTIONS_FLAG_BURN_SPEED_DEFAULT; return; } BOOLEAN ParseCommandLine( IN DWORD Count, IN PUCHAR Arguments[] ) { DWORD i; HRESULT hr; InitializeOptions(); for(i = 0; i < Count; i++) { // // If the first character of the argument is a - or a / then // treat it as an option. // if ((Arguments[i][0] == '/') || (Arguments[i][0] == '-')) { BOOLEAN validArgument = FALSE; Arguments[i][0] = '-'; // allow use of both dash and slash if (_strnicmp(Arguments[i], "-speed", strlen("-speed")) == 0) { LONG tempSpeed; // // requires another argument, which is the requested speed // i++; // increment i due to use of second arg if (i >= Count) { printf("Argument required for '-speed ' option, either " "'max' or a decimal number\n"); } else if (_strnicmp(Arguments[i], "max", strlen("max")) == 0) { tempSpeed = OPTIONS_FLAG_BURN_SPEED_MAX; validArgument = TRUE; } else { tempSpeed = atoi(Arguments[i]); if (tempSpeed > 0) { validArgument = TRUE; } else { printf("%s is not a valid speed. Either 'max' or a positive " "decimal value is requred\n", Arguments[i]); } } // if if (validArgument) { gOptions.BurnSpeed = tempSpeed; if (tempSpeed > OPTIONS_FLAG_BURN_SPEED_MAX) { tempSpeed = OPTIONS_FLAG_BURN_SPEED_MAX; } if (gOptions.BurnSpeed == OPTIONS_FLAG_BURN_SPEED_MAX) { printf("Requesting burn at maximum speed\n"); } else { printf("Requesting burn at %d speed\n", gOptions.BurnSpeed); } } // end speed adjustment } else if (_strnicmp(Arguments[i], "-test", strlen("-test")) == 0) { printf("Test burn only\n"); gOptions.TestBurn = 1; validArgument = TRUE; } else if (_strnicmp(Arguments[i], "-erase", strlen("-erase")) == 0) { printf("Erasing media before burning\n"); gOptions.Erase = TRUE; validArgument = TRUE; } else if (_strnicmp(Arguments[i], "-sao", strlen("-sao")) == 0) { printf("Burning image in Session-At-Once (cue-sheet) mode\n"); gOptions.SessionAtOnce = TRUE; validArgument = TRUE; } else if (_strnicmp(Arguments[i], "-print", strlen("-print")) == 0) { printf("Printing writes to screen rather than sending them to device\n"); gOptions.PrintWrites = TRUE; validArgument = TRUE; } else if (_strnicmp(Arguments[i], "-imagehaspostgap", strlen("-imagehaspostgap")) == 0) { printf("Not adding 150 sector postgap (must be part of image)\n"); gOptions.NoPostgap = TRUE; validArgument = TRUE; } else if (_strnicmp(Arguments[i], "-?", strlen("-?")) == 0) { printf("Requesting help\n"); } else { printf("Unknown option -- %s\n", Arguments[i]); } if(!validArgument) { return FALSE; } } else if(gOptions.DeviceName == NULL) { // // The first non-flag argument is the device name. // gOptions.DeviceName = Arguments[i]; } else if(gOptions.ImageName == NULL) { // // The second non-flag argument is the image name. This is // optional if the -erase flag has been provided. // gOptions.ImageName = Arguments[i]; } else { // // Too many non-flag arguments provided. This must be an error. // printf("Error: extra argument %s not expected\n", Arguments[i]); return FALSE; } } // // Validate the command-line arguments. // if(gOptions.DeviceName == NULL) { return FALSE; } if((gOptions.ImageName == NULL) && (!gOptions.Erase)) { printf("Error: must supply image name if not erasing media\n"); return FALSE; } return TRUE; } int __cdecl main(int argc, char *argv[]) { int i = 0; HANDLE cdromHandle; HANDLE isoImageHandle; HRESULT hr; char buffer[120]; // ~50 chars for mountvol names if(argc < 3) { usage: printf("Usage:\n" "\tcdburn -erase [image [options]]\n" "\tcdburn image [options]\n" "Options:\n" "\t-erase Erases the disk before burning (valid for R/W only)\n" "\t-sao Writes the image out in \"session at once\", or cue\n" "\t sheet, mode (default is \"track at once\")\n" "\t-speed Speed of burn, or 'max' for maximum speed\n" //"\t-test Test burn only, no actual burning\n" //"\t-print [DEBUG] print writes, but not send (UNSUPPORTED)\n" "\t-imagehaspostgap Use if your image already contains a 150 sector postgap\n" "\tThe [image] must be provided unless the -erase flag is set.\n" "\tIf both an image and -erase are provided, the media will be\n" "\terased prior to burning the image to the disc.\n" ); return -1; } // // Parse the command line options. // if(!ParseCommandLine(argc - 1, argv + 1)) { goto usage; } hr = StringCchPrintf(buffer, sizeof(buffer)/sizeof(buffer[0]), "\\\\.\\%s", gOptions.DeviceName); if (!SUCCEEDED(hr)) { printf("Device name too long\n"); return -1; } cdromHandle = CreateFile(buffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(cdromHandle == INVALID_HANDLE_VALUE) { printf("Error %d opening device %s\n", GetLastError(), buffer); return -2; } if (!SptUtilLockVolumeByHandle(cdromHandle, TRUE)) { printf("Unable to lock the volume for exclusive access %d\n", GetLastError()); return -3; } // // If an image name was provided then attempt to open it too // if(gOptions.ImageName != NULL) { isoImageHandle = CreateFile(gOptions.ImageName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED, NULL); if (isoImageHandle == INVALID_HANDLE_VALUE) { printf("Error %d opening image file %s\n", GetLastError(), gOptions.ImageName); CloseHandle(cdromHandle); return -4; } } else { isoImageHandle = INVALID_HANDLE_VALUE; } BurnCommand(cdromHandle, isoImageHandle); if (isoImageHandle != INVALID_HANDLE_VALUE) { CloseHandle(isoImageHandle); isoImageHandle = INVALID_HANDLE_VALUE; } if (cdromHandle != INVALID_HANDLE_VALUE) { CloseHandle(cdromHandle); cdromHandle = INVALID_HANDLE_VALUE; } return 0; } VOID PrintBuffer( IN PVOID Buffer, IN DWORD Size ) { DWORD offset = 0; PUCHAR buf = Buffer; while (Size > 0x10) { printf("%08x:" " %02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x" "\n", offset, *(buf + 0), *(buf + 1), *(buf + 2), *(buf + 3), *(buf + 4), *(buf + 5), *(buf + 6), *(buf + 7), *(buf + 8), *(buf + 9), *(buf + 10), *(buf + 11), *(buf + 12), *(buf + 13), *(buf + 14), *(buf + 15) ); Size -= 0x10; offset += 0x10; buf += 0x10; } if (Size != 0) { DWORD spaceIt; printf("%08x:", offset); for (spaceIt = 0; Size != 0; Size--) { if ((spaceIt%8)==0) { printf(" "); // extra space every eight chars } printf(" %02x", *buf); spaceIt++; buf++; } printf("\n"); } return; } BOOLEAN VerifyIsoImage( IN HANDLE IsoImageHandle, OUT PLONG NumberOfBlocks ) { BY_HANDLE_FILE_INFORMATION isoImageInfo; if (!GetFileInformationByHandle(IsoImageHandle, &isoImageInfo)) { FPRINTF((OUTPUT, "Error %d getting file info for iso image\n", GetLastError())); return FALSE; } if (isoImageInfo.nFileSizeHigh != 0) { FPRINTF((OUTPUT, "Error: File too large\n")); SetLastError(ERROR_INVALID_DATA); return FALSE; } if ((isoImageInfo.nFileSizeLow % 2048) != 0) { FPRINTF((OUTPUT, "Error: The file size is not a multiple of 2048 (%I64d)\n", isoImageInfo.nFileSizeLow)); SetLastError(ERROR_INVALID_DATA); return FALSE; } FPRINTF((OUTPUT, "File size is %d bytes (%d blocks)\n", isoImageInfo.nFileSizeLow, isoImageInfo.nFileSizeLow / 2048 )); *NumberOfBlocks = isoImageInfo.nFileSizeLow / 2048; return TRUE; } BOOLEAN VerifyBlankMedia( IN HANDLE CdromHandle ) { CDB cdb; PDISK_INFORMATION diskInfo; DWORD maxSize = sizeof(DISK_INFORMATION); DWORD size; FPRINTF((OUTPUT, "Verifying blank disc... ")); diskInfo = LocalAlloc(LPTR, maxSize); if (diskInfo == NULL) { FPRINTF((OUTPUT, "\nError allocating diskinfo\n")); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(diskInfo, sizeof(DISK_INFORMATION)); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.READ_DISK_INFORMATION.OperationCode = SCSIOP_READ_DISK_INFORMATION; cdb.READ_DISK_INFORMATION.AllocationLength[0] = (UCHAR)(maxSize >> 8); cdb.READ_DISK_INFORMATION.AllocationLength[1] = (UCHAR)(maxSize & 0xff); size = maxSize; if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)diskInfo, &size, TRUE)) { FPRINTF((OUTPUT, "\nError %d getting disk info\n", GetLastError())); LocalFree(diskInfo); return FALSE; } if (diskInfo->LastSessionStatus != 0x00) { FPRINTF((OUTPUT, "disc is not blank!\n")); SetLastError(ERROR_MEDIA_INCOMPATIBLE); LocalFree(diskInfo); return FALSE; } FPRINTF((OUTPUT, "pass.\n")); LocalFree(diskInfo); return TRUE; } BOOLEAN SetWriteModePage( IN HANDLE CdromHandle, IN BOOLEAN TestBurn, IN UCHAR WriteType, IN UCHAR MultiSession, IN UCHAR DataBlockType, IN UCHAR SessionFormat ) { PCDVD_WRITE_PARAMETERS_PAGE params = NULL; MODE_PARAMETER_HEADER10 header; PMODE_PARAMETER_HEADER10 buffer; UCHAR mediumTypeCode; CDB cdb; DWORD bufferSize; DWORD maxSize; FPRINTF((OUTPUT, "Setting WriteParameters mode page... ")); bufferSize = sizeof(MODE_PARAMETER_HEADER10); RtlZeroMemory(&header, sizeof(MODE_PARAMETER_HEADER10)); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.PageCode = 0x5; cdb.MODE_SENSE10.Dbd = 1; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff); if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)&header, &bufferSize, TRUE)) { FPRINTF((OUTPUT, "\nError %d getting mode page 0x05 from device(1)\n", GetLastError())); return FALSE; } bufferSize = (header.ModeDataLength[0] << 8) + (header.ModeDataLength[1] & 0xff); bufferSize += 2; // sizeof area that tells the remaining size maxSize = bufferSize; buffer = LocalAlloc(LPTR, bufferSize); if (!buffer) { FPRINTF((OUTPUT, "\nError -- unable to alloc %d bytes for mode parameters page\n", bufferSize)); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(&cdb, sizeof(CDB)); cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.PageCode = 0x5; cdb.MODE_SENSE10.Dbd = 1; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff); if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)buffer, &bufferSize, TRUE)) { FPRINTF((OUTPUT, "\nError %d getting mode page 0x05 from device(2)\n", GetLastError())); LocalFree(buffer); return FALSE; } mediumTypeCode = buffer->MediumType; // // bufferSize now holds the amount of data returned // this should be enough... // { DWORD t = (buffer->BlockDescriptorLength[0] >> 8) + (buffer->BlockDescriptorLength[1] & 0xff); if (t != 0) { fprintf(stderr, "BlockDescriptor non-zero! (%x)\n", t); SetLastError(1); return FALSE; } } // // pointer arithmetic here. (buffer+1) points just past the // end of the mode_parameter_header10. // params = (PCDVD_WRITE_PARAMETERS_PAGE)(buffer + 1); FPRINTF((OUTPUT, "buffer = %p params = %p\n", buffer, params)); // // zero the header, but don't modify any settings that don't // need to be modified! // RtlZeroMemory(buffer, FIELD_OFFSET(MODE_PARAMETER_HEADER10, BlockDescriptorLength[0])); buffer->ModeDataLength[0] = 0; buffer->ModeDataLength[1] = 0; buffer->MediumType = mediumTypeCode; buffer->DeviceSpecificParameter = 0; buffer->BlockDescriptorLength[0] = 0; buffer->BlockDescriptorLength[1] = 0; params->PageLength = (UCHAR) (bufferSize - sizeof(MODE_PARAMETER_HEADER10) - RTL_SIZEOF_THROUGH_FIELD( CDVD_WRITE_PARAMETERS_PAGE, PageLength ) ); params->LinkSizeValid = 0; // params->BufferUnderrunFreeEnabled = 1; params->TestWrite = (TestBurn ? 0x01 : 0x00); params->WriteType = WriteType; params->MultiSession = MultiSession; params->Copy = 0x00; // original disc params->FixedPacket = 0; params->TrackMode = 0x4; // data track, uninterrupted, copy prohibited params->DataBlockType = DataBlockType; params->SessionFormat = SessionFormat; params->MediaCatalogNumberValid = 0x00; params->ISRCValid = 0x00; RtlZeroMemory(&cdb, sizeof(CDB)); cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR)(bufferSize >> 8); cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR)(bufferSize & 0xff); cdb.MODE_SELECT10.PFBit = 1; if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)buffer, &bufferSize, FALSE)) { FPRINTF((OUTPUT, "\nError %d sending mode page 0x05 to device\n", GetLastError())); LocalFree(buffer); return FALSE; } LocalFree(buffer); FPRINTF((OUTPUT, "pass.\n")); return TRUE; } BOOLEAN GetNextWritableAddress( IN HANDLE CdromHandle, IN UCHAR Track, OUT PLONG NextWritableAddress, OUT PLONG AvailableBlocks ) { TRACK_INFORMATION2 trackInfo; LONG nwa, available; DWORD size; CDB cdb; *NextWritableAddress = (LONG)MAXLONG; *AvailableBlocks = (LONG)0; FPRINTF((OUTPUT, "Verifying track info... ")); RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(&trackInfo, sizeof(TRACK_INFORMATION2)); size = sizeof(TRACK_INFORMATION2); cdb.READ_TRACK_INFORMATION.OperationCode = SCSIOP_READ_TRACK_INFORMATION; cdb.READ_TRACK_INFORMATION.Track = 0x01; cdb.READ_TRACK_INFORMATION.BlockAddress[3] = Track; cdb.READ_TRACK_INFORMATION.AllocationLength[0] = (UCHAR)(size >> 8); cdb.READ_TRACK_INFORMATION.AllocationLength[1] = (UCHAR)(size & 0xff); if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)&trackInfo, &size, TRUE)) { FPRINTF((OUTPUT, "\nError %d getting track info\n", GetLastError())); PRINTBUFFER(( &trackInfo, sizeof(trackInfo) )); return FALSE; } if (!trackInfo.NWA_V) { FPRINTF((OUTPUT, "invalid NextWritableAddress -- may be invalid media?\n")); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } nwa = (trackInfo.NextWritableAddress[0] << 24) | (trackInfo.NextWritableAddress[1] << 16) | (trackInfo.NextWritableAddress[2] << 8) | (trackInfo.NextWritableAddress[3] << 0); available = (trackInfo.FreeBlocks[0] << 24) | (trackInfo.FreeBlocks[1] << 16) | (trackInfo.FreeBlocks[2] << 8) | (trackInfo.FreeBlocks[3] << 0); FPRINTF((OUTPUT, "pass.\n")); *NextWritableAddress = nwa; *AvailableBlocks = available; return TRUE; } BOOLEAN SendOptimumPowerCalibration( IN HANDLE CdromHandle ) { CDB cdb; DWORD size; FPRINTF((OUTPUT, "Setting OPC_INFORMATION...")); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION; cdb.SEND_OPC_INFORMATION.DoOpc = 1; size = 0; if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, NULL, &size, TRUE)) { FPRINTF((OUTPUT, "\nFailed to send SET_OPC_INFORMATION (%d)\n", GetLastError())); return FALSE; } FPRINTF((OUTPUT, "pass.\n")); return TRUE; } BOOLEAN SetRecordingSpeed( IN HANDLE CdromHandle, IN DWORD Speed ) { CDB cdb; DWORD size; DWORD kbSpeed; FPRINTF((OUTPUT, "Setting CD Speed...")); if (Speed == -1) { kbSpeed = -1; } else { kbSpeed = MakeCdSpeed(Speed); } RtlZeroMemory(&cdb, sizeof(CDB)); cdb.SET_CD_SPEED.OperationCode = SCSIOP_SET_CD_SPEED; cdb.SET_CD_SPEED.ReadSpeed[0] = 0xff; cdb.SET_CD_SPEED.ReadSpeed[1] = 0xff; cdb.SET_CD_SPEED.WriteSpeed[0] = (UCHAR)(kbSpeed >> 8); cdb.SET_CD_SPEED.WriteSpeed[1] = (UCHAR)(kbSpeed & 0xff); size = 0; if (!SptSendCdbToDevice(CdromHandle, &cdb, 12, NULL, &size, TRUE)) { FPRINTF((OUTPUT, "\nFailed to send SET_CD_SPEED (%d)\n", GetLastError())); return FALSE; } FPRINTF((OUTPUT, "pass.\n")); return TRUE; } VOID WaitForReadDiscInfoToWork( IN HANDLE CdromHandle ) { CDB cdb; DWORD size; DISK_INFORMATION diskInfo; DWORD i; // // loop using SCSIOP_READ_DISK_INFORMATION (0x51) since // that seems to fail for *ALL* drives until the drive is ready // for (i=0; ; i++) { size = sizeof(DISK_INFORMATION); RtlZeroMemory(&diskInfo, sizeof(DISK_INFORMATION)); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.READ_DISK_INFORMATION.OperationCode = SCSIOP_READ_DISK_INFORMATION; cdb.READ_DISK_INFORMATION.AllocationLength[0] = (UCHAR)(size >> 8); cdb.READ_DISK_INFORMATION.AllocationLength[1] = (UCHAR)(size & 0xff); if (SptSendCdbToDevice(CdromHandle, &cdb, 10, (PUCHAR)&diskInfo, &size, TRUE)) { FPRINTF((OUTPUT, "ReadDiscInfo Succeeded! (%d seconds)\n", i)); return; } // should verify the errors are valid errors (AllowedReadDiscInfo[])? // need to sleep here so we don't overload the unit! Sleep(1000); // one second } return; } DWORD G_BytesRead; DWORD G_ErrorCode; VOID ReadComplete( IN DWORD errorcode, IN DWORD bytesread, IN LPOVERLAPPED OverL ) { G_BytesRead = bytesread; G_ErrorCode = errorcode; SetEvent( OverL->hEvent); } BOOLEAN BurnThisSession( IN HANDLE CdromHandle, IN HANDLE IsoImageHandle, IN ULONG NumberOfBlocks, IN ULONG FirstLba, IN ULONG AdditionalZeroSectors ) { #define NUMBER_OF_SECTORS_PER_READ (0x140) // 640k #define NUMBER_OF_SECTORS_PER_WRITE (0x20) // 64k C_ASSERT( NUMBER_OF_SECTORS_PER_READ % NUMBER_OF_SECTORS_PER_WRITE == 0 ); DWORD bufferSize = NUMBER_OF_SECTORS_PER_READ*2048; // 640k DWORD writeUnit = NUMBER_OF_SECTORS_PER_WRITE*2048; // 64k PUCHAR buffer = NULL; PUCHAR buffer2 = NULL; PUCHAR BufPtr; ULONG postGapSize; OVERLAPPED OverL; HANDLE ReadEvent; ULONG CurrentBuffer = 0; ULONG BlocksToWrite; BOOLEAN OutstandingRead = FALSE; ULONG currentReadBlock; ULONG currentWriteBlock = FirstLba; DWORD readSize; DWORD readBytes; if( AdditionalZeroSectors ) { postGapSize = AdditionalZeroSectors; } else { postGapSize = 0; } FPRINTF((OUTPUT, "Starting write: ")); buffer = LocalAlloc(LPTR, 2 * bufferSize); if (buffer == NULL) { FPRINTF((OUTPUT, "unable to allocate write buffer\n")); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } buffer2 = buffer + bufferSize; ReadEvent = CreateEvent( NULL, FALSE, FALSE, NULL); if (ReadEvent == NULL) { FPRINTF((OUTPUT, "Failed to create event %d\n",GetLastError())); return FALSE; } FPRINTF((OUTPUT, ".............")); SetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT ); RtlZeroMemory( &OverL, sizeof( OverL)); for (currentReadBlock = 0; currentWriteBlock < NumberOfBlocks + postGapSize + FirstLba; //((currentReadBlock < (NumberOfBlocks + postGapSize)) || OutstandingRead); // NOTHING for third part of the loop.... ) { CDB cdb; SetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT ); if (!gOptions.PrintWrites) { static CHAR progress[4] = { '|', '/', '-', '\\' }; DWORD percent; percent = (currentReadBlock < (NumberOfBlocks + postGapSize)) ? ((currentReadBlock * 1000) / (NumberOfBlocks + postGapSize)) : 1000; // # # # . # % _ d o n e _ * printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); printf("%c %3d.%d%% done", progress[(currentReadBlock/NUMBER_OF_SECTORS_PER_READ) % 4 ], percent / 10, percent % 10 ); fflush(stdout); } // // Wait for any previously issued read to complete and check the result. // if (OutstandingRead) { DWORD Result = WaitForSingleObjectEx( ReadEvent, INFINITE, TRUE); if (Result != WAIT_OBJECT_0) { if (Result == WAIT_IO_COMPLETION) { Result = WaitForSingleObjectEx( ReadEvent, INFINITE, TRUE); } if (Result != WAIT_OBJECT_0) { FPRINTF((OUTPUT, "Unexpected result from waitforsingleobjectex %d\n", Result)); LocalFree(buffer); return FALSE; } } // check the status if ((G_BytesRead != readSize) || G_ErrorCode) { FPRINTF((OUTPUT, "error %d or only read %d of %d bytes from file\n", G_ErrorCode, G_BytesRead, readSize)); LocalFree(buffer); return FALSE; } readBytes = G_BytesRead; OutstandingRead = FALSE; } // // Calculate the amount of data to read into the secondary buffer, if any. // if (currentReadBlock >= NumberOfBlocks) { readSize = 0; readBytes = 0; RtlZeroMemory( CurrentBuffer ? buffer2 : buffer, bufferSize); } else if ((NumberOfBlocks - currentReadBlock) >= BLOCKS_FROM_BYTES(bufferSize)) { readSize = bufferSize; } else { readSize = BYTES_FROM_BLOCKS(NumberOfBlocks - currentReadBlock ); RtlZeroMemory( CurrentBuffer ? buffer2 : buffer, bufferSize); } // // Issue an async. read for the secondary buffer, hopefully this will // complete before our active buffer write finishes. // if (readSize) { OverL.Offset = (DWORD) BYTES_FROM_BLOCKS(currentReadBlock ); OverL.OffsetHigh = (DWORD) (BYTES_FROM_BLOCKS((ULONG64)currentReadBlock) >> 32); OverL.hEvent = ReadEvent; if (!ReadFileEx( IsoImageHandle, CurrentBuffer ? buffer2 : buffer, readSize, &OverL, ReadComplete)) { FPRINTF((OUTPUT, "Error %d issuing overlapped read read\n", GetLastError())); LocalFree(buffer); return FALSE; } OutstandingRead = TRUE; } // // Toggle to the other buffer // CurrentBuffer ^= 1; // // First pass we just want to fill the first buffer, since we've nothing to write // yet, so just loop. // if ((readSize != 0) && (currentReadBlock == 0)) { // // Note this can put us >= NumberOfBlocks + postGapSize for absurdly // small images. // currentReadBlock += BLOCKS_FROM_BYTES(bufferSize ); continue; } // // Remember we're writng a buffer behind what we're reading... // currentWriteBlock = currentReadBlock + FirstLba - BLOCKS_FROM_BYTES(bufferSize); BlocksToWrite = NumberOfBlocks + FirstLba + postGapSize - currentWriteBlock; if (BlocksToWrite > BLOCKS_FROM_BYTES(bufferSize)) { BlocksToWrite = BLOCKS_FROM_BYTES(bufferSize); } BufPtr = CurrentBuffer ? buffer2 : buffer; while (BlocksToWrite) { ULONG ThisWriteSize; BOOL writeCompleted = FALSE; while (!writeCompleted) { BOOLEAN ignoreError; SENSE_DATA senseData; RtlZeroMemory(&senseData, sizeof(senseData)); ThisWriteSize = (BlocksToWrite <= BLOCKS_FROM_BYTES(writeUnit)) ? BYTES_FROM_BLOCKS(BlocksToWrite) : writeUnit; writeCompleted = SendWriteCommand(CdromHandle, currentWriteBlock, BufPtr, ThisWriteSize, &senseData); ignoreError = IsSenseDataInTable(AllowedBurnSense, AllowedBurnSenseEntries, &senseData); if ((!writeCompleted) && ignoreError) { #if 0 FPRINTF((OUTPUT, "Continuing on %x/%x/%x\n", senseData.SenseKey & 0xf, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier )); #endif Sleep(100); // 100ms == .1 seconds } if (!writeCompleted && !ignoreError) { FPRINTF((OUTPUT, "\nError %d in writing LBA 0x%x\n", GetLastError(), currentWriteBlock)); LocalFree(buffer); return FALSE; } } // while(!writeCompleted) loop assert( ThisWriteSize <= BlocksToWrite ); BlocksToWrite -= BLOCKS_FROM_BYTES(ThisWriteSize); currentReadBlock += BLOCKS_FROM_BYTES(ThisWriteSize); currentWriteBlock += BLOCKS_FROM_BYTES(ThisWriteSize); BufPtr += ThisWriteSize; } // random block to have local variable writeCompleted } printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); LocalFree(buffer); printf("Finished Writing\nSynchronizing Cache: "); fflush(stdout); // // do the FLUSH_CACHE immediate // { DWORD size; CDB cdb; RtlZeroMemory(&cdb, sizeof(CDB)); cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE; cdb.SYNCHRONIZE_CACHE10.Immediate = 1; size = 0; if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, NULL, &size, TRUE)) { FPRINTF((OUTPUT, "\nError %d Synchronizing Cache\n", GetLastError())); return FALSE; } } WaitForReadDiscInfoToWork(CdromHandle); return TRUE; } BOOLEAN CloseTrack( IN HANDLE CdromHandle, IN LONG Track ) { CDB cdb; DWORD size; FPRINTF((OUTPUT, "Closing the track...")); if (Track > 0xffff) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CLOSE_TRACK.OperationCode = SCSIOP_CLOSE_TRACK_SESSION; cdb.CLOSE_TRACK.Immediate = 0; cdb.CLOSE_TRACK.Track = 1; cdb.CLOSE_TRACK.Session = 0; cdb.CLOSE_TRACK.TrackNumber[0] = (UCHAR)(Track >> 8); cdb.CLOSE_TRACK.TrackNumber[1] = (UCHAR)(Track & 0xff); size = 0; if (!SptSendCdbToDevice(CdromHandle, &cdb, 10, NULL, &size, TRUE)) { FPRINTF((OUTPUT, "\nError %d Closing Track\n", GetLastError())); return FALSE; } WaitForReadDiscInfoToWork(CdromHandle); FPRINTF((OUTPUT, "pass.\n")); return TRUE; } BOOLEAN CloseSession( IN HANDLE CdromHandle ) { CDB cdb; DWORD size; FPRINTF((OUTPUT, "Closing the disc...")); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CLOSE_TRACK.OperationCode = SCSIOP_CLOSE_TRACK_SESSION; cdb.CLOSE_TRACK.Immediate = 1; cdb.CLOSE_TRACK.Track = 0; cdb.CLOSE_TRACK.Session = 1; size = 0; if (!SptSendCdbToDeviceEx(CdromHandle, &cdb, 10, NULL, &size, NULL, 0, TRUE, 240)) { // four minutes to close session FPRINTF((OUTPUT, "\nError %d Synchronizing Cache\n", GetLastError())); return FALSE; } WaitForReadDiscInfoToWork(CdromHandle); FPRINTF((OUTPUT, "pass.\n")); return TRUE; } BOOLEAN SendStartStopUnit( IN HANDLE CdromHandle, IN BOOLEAN Start, IN BOOLEAN Eject ) { CDB cdb; DWORD size; RtlZeroMemory(&cdb, sizeof(CDB)); cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb.START_STOP.LoadEject = Eject; cdb.START_STOP.Start = Start; size = 0; if (!SptSendCdbToDevice(CdromHandle, &cdb, 6, NULL, &size, TRUE)) { return FALSE; } return TRUE; } /* ERROR_BAD_COMMAND ERROR_INVALID_DATA ERROR_INVALID_PARAMETER ERROR_MEDIA_INCOMPATIBLE ERROR_NOT_ENOUGH_MEMORY ERROR_OUTOFMEMORY */ /*++ Routine Description: burns an ISO image to cdrom Arguments: CdromHandle - a file handle to send the ioctl to argc - the number of additional arguments (2) Return Value: ERROR_SUCCESS if successful The value of GetLastError() from the point of failure --*/ DWORD BurnCommand( HANDLE CdromHandle, HANDLE IsoImageHandle ) { LONG numberOfBlocks; LONG i; BOOLEAN b; DWORD status; //////////////////////////////////////////////////////////////////////////////// // verify the iso image file looks correct //////////////////////////////////////////////////////////////////////////////// if ((IsoImageHandle != INVALID_HANDLE_VALUE) && (VerifyIsoImage(IsoImageHandle, &numberOfBlocks) == FALSE)) { printf("Error verifying ISO image\n"); return GetLastError(); } else { assert(gEraseTargetFirst == TRUE); } printf("Number of blocks in ISO image is %x\n", numberOfBlocks); //////////////////////////////////////////////////////////////////////////////// // Erase the target media if it's been requested we do so. //////////////////////////////////////////////////////////////////////////////// if (gOptions.Erase) { printf("Erasing target media\n"); if(!EraseTargetMedia(CdromHandle)) { printf("Error %d erasing target\n", GetLastError()); return GetLastError(); } printf("Media erased\n"); } //////////////////////////////////////////////////////////////////////////////// // verify (as best as possible) that it's blank media //////////////////////////////////////////////////////////////////////////////// if(!VerifyBlankMedia(CdromHandle)) { printf("Error verifying blank media\n"); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // If there's no image file to be written then we're done. //////////////////////////////////////////////////////////////////////////////// if(IsoImageHandle == INVALID_HANDLE_VALUE) { return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // set the cd speed to four for now, can later make a cmd-line switch //////////////////////////////////////////////////////////////////////////////// if (gOptions.BurnSpeed == OPTIONS_FLAG_BURN_SPEED_MAX) { if (!SetRecordingSpeed(CdromHandle, -1)) { printf("Error setting the cd speed to max\n"); return GetLastError(); } } else { if (!SetRecordingSpeed(CdromHandle, gOptions.BurnSpeed)) { printf("Error setting the cd speed to %d\n", gOptions.BurnSpeed); return GetLastError(); } } //////////////////////////////////////////////////////////////////////////////// // calibrate the drive's power -- this is optional, so let it fail! //////////////////////////////////////////////////////////////////////////////// if (!(gOptions.TestBurn)) { // don't calibrate for test burns.... if (!SendOptimumPowerCalibration(CdromHandle)) { printf("WARNING: setting optimum power calibration failed\n"); //return GetLastError(); } } //////////////////////////////////////////////////////////////////////////////// // start writing //////////////////////////////////////////////////////////////////////////////// if (gOptions.SessionAtOnce) { b = BurnDisk(CdromHandle, IsoImageHandle, numberOfBlocks); } else { b = BurnTrack(CdromHandle, IsoImageHandle, numberOfBlocks); } if(!b) { return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // eject the newly burned cd! //////////////////////////////////////////////////////////////////////////////// if (!SendStartStopUnit(CdromHandle, FALSE, TRUE) || !SendStartStopUnit(CdromHandle, TRUE, TRUE)) { printf("Error ejecting/reinserting disc\n"); return GetLastError(); } printf("burn successful!\n"); return 0; } BOOLEAN BurnTrack( HANDLE CdromHandle, HANDLE IsoImageHandle, LONG NumberOfBlocks ) { LONG availableBlocks; LONG firstLba; //////////////////////////////////////////////////////////////////////////////// // Setup the write mode page //////////////////////////////////////////////////////////////////////////////// if (!SetWriteModePage(CdromHandle, (BOOLEAN)gOptions.TestBurn, 0x01, // track-at-once 0x03, // we close the session/disc ourselves 0x08, // 0x08 == Mode 1 (ISO/IEC 10149 == 2048 bytes) // 0x0a == Mode 2 (CDROM XA, Form 1, 2048 bytes) 0x00 // 0x00 == CD-DA, CD-ROM, or other data disc // 0x20 == CDROM XA )) { printf("Error setting write mode page\n"); return FALSE; } //////////////////////////////////////////////////////////////////////////////// // get next writable address //////////////////////////////////////////////////////////////////////////////// if (!GetNextWritableAddress(CdromHandle, 0xff, &firstLba, &availableBlocks)) { printf("Error verifying next writable address\n"); return FALSE; } if (firstLba != 0) { printf("Error verifying next writable address is zero\n"); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } //////////////////////////////////////////////////////////////////////////////// // also verify the number of blocks left on the media is sufficiently large //////////////////////////////////////////////////////////////////////////////// if (availableBlocks < NumberOfBlocks) { printf("Error verifying free blocks on media (%d needed, %d available)\n", NumberOfBlocks, availableBlocks); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } //////////////////////////////////////////////////////////////////////////////// // burn the main section of data to disc //////////////////////////////////////////////////////////////////////////////// { ULONG additionalBlocks; if ( gOptions.NoPostgap ) { additionalBlocks = 0; } else { additionalBlocks = POST_GAP_SIZE; } if (!BurnThisSession(CdromHandle, IsoImageHandle, NumberOfBlocks, 0, additionalBlocks)) { return FALSE; } } //////////////////////////////////////////////////////////////////////////////// // set mode page to finalize the disc //////////////////////////////////////////////////////////////////////////////// if (!SetWriteModePage(CdromHandle, (BOOLEAN)gOptions.TestBurn, 0x01, // track-at-once 0x00, // we close the session/disc ourselves 0x08, // 0x08 == Mode 1 (ISO/IEC 10149 == 2048 bytes) // 0x0a == Mode 2 (CDROM XA, Form 1, 2048 bytes) 0x00 // 0x00 == CD-DA, CD-ROM, or other data disc // 0x20 == CDROM XA )) { SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } //////////////////////////////////////////////////////////////////////////////// // close the session //////////////////////////////////////////////////////////////////////////////// if (!(gOptions.TestBurn)) { // don't close anything for test burns. if (!CloseSession(CdromHandle)) { // if couldn't close session, try closing the track first and // then retry closing the session. if (!CloseTrack(CdromHandle, 1)) { printf("WARNING: error closing the track when session close " "also failed.\n"); printf(" The disc may or may not be usable -- " "no guarantees\n"); } if (!CloseSession(CdromHandle)) { printf("Error closing session -- the disc is almost definitely " "unusable on most drives. YMMV.\n"); return FALSE; } } } return TRUE; } #define ERASE_TIMEOUT (2 * 60) BOOLEAN EraseTargetMedia( IN HANDLE CdromHandle ) { CDB cdb; ULONG zero = 0; BOOL b; DWORD status; // // Send the blank command to the device. // memset(&cdb, 0, sizeof(cdb)); cdb.BLANK_MEDIA.OperationCode = SCSIOP_BLANK; cdb.BLANK_MEDIA.BlankType = 0x1; // quick erase cdb.BLANK_MEDIA.Immediate = TRUE; b = SptSendCdbToDevice(CdromHandle, &cdb, 12, NULL, &zero, FALSE); if (!b) { return FALSE; } WaitForReadDiscInfoToWork(CdromHandle); return TRUE; } BOOLEAN BurnDisk( HANDLE CdromHandle, HANDLE IsoImageHandle, LONG NumberOfBlocks ) { LONG availableBlocks; LONG firstLba; // // Setup for a disk-at-once burn. // if (!SetWriteModePage(CdromHandle, (BOOLEAN)gOptions.TestBurn, 0x02, // session-at-once 0x00, // no multisession allowed 0x08, // 0x08 == Mode 1 (ISO/IEC 10149 == 2048 bytes) // 0x0a == Mode 2 (CDROM XA, Form 1, 2048 bytes) 0x00 // 0x00 == CD-DA, CD-ROM, or other data disc // 0x20 == CDROM XA )) { printf("Error setting write mode page\n"); return FALSE; } // // get next writable address // if (!GetNextWritableAddress(CdromHandle, 0xff, &firstLba, &availableBlocks)) { printf("Error verifying next writable address\n"); return FALSE; } /* // // The first LBA should be -150 for SAO writes // if (firstLba != -150) { printf("Error verifying next writable address is -150\n"); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } */ // // verify the disc is large enough // if (availableBlocks < NumberOfBlocks + 150) { printf("Error verifying free blocks on media (%d needed, %d available)\n", NumberOfBlocks + 150, availableBlocks); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return FALSE; } // // Send the cue sheet for our burn. // if (!SendCueSheet(CdromHandle, NumberOfBlocks)) { printf("Error sending cue sheet\n"); return FALSE; } // // Burn the lead-in to disk. // if (!BurnLeadIn(CdromHandle)) { printf("Error writing lead-in\n"); return FALSE; } // // Burn the session to disk. // if (!BurnThisSession(CdromHandle, IsoImageHandle, NumberOfBlocks, 0, // start LBA (gOptions.NoPostgap ? 2 : 2 + POST_GAP_SIZE))) { return FALSE; } return TRUE; } BOOLEAN SendCueSheet( IN HANDLE CdromHandle, IN ULONG NumberOfBlocks ) { CDB cdb; CUE_SHEET_LINE cueSheet[] = { {CUE_ADR_TRACK_INDEX, CUE_CTL_DATA_TRACK, 0, 0, CUE_FORM_MODE1_GDATA_GECC_0, CUE_SCFORM_ZEROED_0, 0, 0, 0x00, 0x00, 0x00}, {CUE_ADR_TRACK_INDEX, CUE_CTL_DATA_TRACK, 1, 0, CUE_FORM_MODE1_SDATA_GECC_2048, CUE_SCFORM_ZEROED_0, 0, 0, 0x00, 0x00, 0x00}, {CUE_ADR_TRACK_INDEX, CUE_CTL_DATA_TRACK, 1, 1, CUE_FORM_MODE1_SDATA_GECC_2048, CUE_SCFORM_ZEROED_0, 0, 0, 0x00, 0x02, 0x00}, {CUE_ADR_TRACK_INDEX, CUE_CTL_DATA_TRACK, 0xaa, 1, CUE_FORM_MODE1_GDATA_GECC_0, CUE_SCFORM_ZEROED_0, 0, 0, 0xff, 0xff, 0xff} }; ULONG cueSheetSize = sizeof(cueSheet); SENSE_DATA senseData; MSF msf; MSF pregap = {0, 2, 0}; // // Need to add two sectors of runout blocks, and postgap as appropriate // NumberOfBlocks += 2; if ( !(gOptions.NoPostgap) ) { NumberOfBlocks += POST_GAP_SIZE; } memset(&cdb, 0, sizeof(CDB)); cdb.SEND_CUE_SHEET.OperationCode = SCSIOP_SEND_CUE_SHEET; cdb.SEND_CUE_SHEET.CueSheetSize[0] = (UCHAR)((cueSheetSize >> (8*2)) & 0xff); cdb.SEND_CUE_SHEET.CueSheetSize[1] = (UCHAR)((cueSheetSize >> (8*1)) & 0xff); cdb.SEND_CUE_SHEET.CueSheetSize[2] = (UCHAR)((cueSheetSize >> (8*0)) & 0xff); // // Calculate the correct time stamp for the start of the post-gap area. // msf = LbaToMsf(NumberOfBlocks); msf = AddMsf(msf, pregap); cueSheet[RTL_NUMBER_OF(cueSheet)-1].Min = msf.Min; cueSheet[RTL_NUMBER_OF(cueSheet)-1].Sec = msf.Sec; cueSheet[RTL_NUMBER_OF(cueSheet)-1].Frame = msf.Frame; printf("Cue Sheet:\n"); PrintBuffer((PUCHAR) cueSheet, cueSheetSize); // // Send the cue sheet to the device. // if(!SptSendCdbToDeviceEx(CdromHandle, &cdb, 10, (PUCHAR) cueSheet, &cueSheetSize, &senseData, sizeof(senseData), FALSE, 30)) { printf("Error: Cue sheet send failed\n"); return FALSE; } return TRUE; } BOOLEAN SendWriteCommand( IN HANDLE CdromHandle, IN LONG Block, IN PVOID Buffer, IN ULONG Length, OUT PSENSE_DATA SenseData ) { CDB cdb; FOUR_BYTE b; RtlZeroMemory(&cdb, sizeof(CDB)); b.AsULong = Block; if(gOptions.PrintWrites) { printf("Writing block %#010x length %#010x\n", Block, Length); return TRUE; } cdb.CDB10.OperationCode = SCSIOP_WRITE; cdb.CDB10.LogicalBlockByte0 = b.Byte3; cdb.CDB10.LogicalBlockByte1 = b.Byte2; cdb.CDB10.LogicalBlockByte2 = b.Byte1; cdb.CDB10.LogicalBlockByte3 = b.Byte0; cdb.CDB10.TransferBlocksLsb = (UCHAR)BLOCKS_FROM_BYTES(Length); if(SptSendCdbToDeviceEx(CdromHandle, &cdb, 10, Buffer, &Length, SenseData, sizeof(SENSE_DATA), FALSE, 50 // timeout seconds )) { return TRUE; } else { return FALSE; } } BOOLEAN BurnLeadIn( IN HANDLE CdromHandle ) { DWORD writeUnit = DEFAULT_WRITE_SIZE; // 64k PUCHAR buffer = NULL; LONG currentBlock; LONG blocksToWrite = LEAD_IN_SIZE; FPRINTF((OUTPUT, "Starting lead-in: ")); buffer = LocalAlloc(LPTR, writeUnit); if (buffer == NULL) { FPRINTF((OUTPUT, "unable to allocate write buffer\n")); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } ZeroMemory(buffer, writeUnit); FPRINTF((OUTPUT, ".............\n")); SetThreadExecutionState( ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT ); currentBlock = -LEAD_IN_SIZE; do { ULONG writeSize; BOOLEAN writeCompleted; writeSize = min(BLOCKS_FROM_BYTES(DEFAULT_WRITE_SIZE), blocksToWrite); do { BOOLEAN ignoreError; SENSE_DATA senseData; RtlZeroMemory(&senseData, sizeof(SENSE_DATA)); writeCompleted = SendWriteCommand(CdromHandle, currentBlock, buffer, BYTES_FROM_BLOCKS(writeSize), &senseData); ignoreError = IsSenseDataInTable(AllowedBurnSense, AllowedBurnSenseEntries, &senseData); if ((!writeCompleted) && ignoreError) { #if 0 FPRINTF((OUTPUT, "Continuing on %x/%x/%x\n", senseData.SenseKey & 0xf, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier )); #endif Sleep(100); // 100ms == .1 seconds } if (!writeCompleted && !ignoreError) { FPRINTF((OUTPUT, "\nError %d in writing LBA 0x%x\n", GetLastError(), currentBlock)); LocalFree(buffer); return FALSE; } } while(!writeCompleted); blocksToWrite -= writeSize; currentBlock += writeSize; } while(blocksToWrite > 0); printf("Finished LeadIn\n"); fflush(stdout); return TRUE; }