/*++ 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 "burn.h" #include "sptlib.h" #define MAX_CD_IMAGE_SIZE (700 * 1024 * 1024) #define MAX_DVD_IMAGE_SIZE (4700 * 1000 * 1000) #define POST_GAP_SIZE 150 #define IS_TEST_BURN FALSE 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 #else #define OUTPUT stdout #define FPRINTF(x) #endif int __cdecl main(int argc, char *argv[]) { int i = 0; HANDLE cdromHandle; HANDLE isoImageHandle; HACK_FLAGS hackFlags; char buffer[32]; if(argc < 3) { printf("Usage: burn \n"); return -1; } sprintf(buffer, "\\\\.\\%s", argv[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; } isoImageHandle = CreateFile(argv[2], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (isoImageHandle == INVALID_HANDLE_VALUE) { printf("Error %d opening image file %s\n", GetLastError(), argv[2]); CloseHandle(cdromHandle); return -2; } RtlZeroMemory(&hackFlags, sizeof(HACK_FLAGS)); hackFlags.TestBurn = 0; hackFlags.IgnoreModePageErrors = 1; BurnCommand(cdromHandle, isoImageHandle, hackFlags); CloseHandle(isoImageHandle); CloseHandle(cdromHandle); 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] = (UCHAR)((bufferSize-2) >> 8); buffer->ModeDataLength[1] = (UCHAR)((bufferSize-2) & 0xff); buffer->MediumType = mediumTypeCode; params->WriteType = WriteType; params->TestWrite = (TestBurn ? 0x01 : 0x00); params->Copy = 0x00; // original disc //params->TrackMode = 0x04; // control nibble in Q subchannel params->MultiSession = MultiSession; 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 ) { CDB cdb; TRACK_INFORMATION trackInfo; DWORD size = sizeof(TRACK_INFORMATION); LONG nwa, available; *NextWritableAddress = (LONG)-1; *AvailableBlocks = (LONG)0; FPRINTF((OUTPUT, "Verifying track info... ")); RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(&trackInfo, sizeof(TRACK_INFORMATION)); 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())); 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...")); 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; } BOOLEAN BurnThisSession( IN HANDLE CdromHandle, IN HANDLE IsoImageHandle, IN LONG NumberOfBlocks, IN LONG FirstLba ) { DWORD bufferSize = 0x800 * 0x10; PUCHAR buffer = NULL; LONG currentBlock; FPRINTF((OUTPUT, "Starting write: ")); buffer = LocalAlloc(LPTR, bufferSize); if (buffer == NULL) { FPRINTF((OUTPUT, "unable to allocate write buffer\n")); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } FPRINTF((OUTPUT, ".............")); for (currentBlock = 0; currentBlock < NumberOfBlocks + POST_GAP_SIZE; ) { CDB cdb; DWORD readSize; DWORD readBytes; { static CHAR progress[4] = { '|', '/', '-', '\\' }; DWORD percent; percent = (currentBlock*1000) / (NumberOfBlocks+POST_GAP_SIZE); // # # # . # % _ d o n e _ * printf("\b\b\b\b\b\b\b\b\b\b\b\b\b"); printf("%c %3d.%d%% done", progress[(currentBlock%0x40)/0x10], percent / 10, percent % 10 ); fflush(stdout); } if (NumberOfBlocks - currentBlock >= 0x10) { readSize = 0x800 * 0x10; } else if (NumberOfBlocks - currentBlock > 0) { readSize = (NumberOfBlocks - currentBlock) * 0x800; RtlZeroMemory(buffer, bufferSize); } else { readSize = 0; readBytes = 0; RtlZeroMemory(buffer, bufferSize); } if (readSize && !ReadFile(IsoImageHandle, buffer, readSize, &readBytes, NULL) ) { FPRINTF((OUTPUT, "error reading from file %d\n", GetLastError())); LocalFree(buffer); return FALSE; } if (readBytes != readSize) { FPRINTF((OUTPUT, "error only read %d of %d bytes from file\n", readBytes, readSize)); LocalFree(buffer); return FALSE; } { BOOL writeCompleted = FALSE; while (!writeCompleted) { BOOLEAN ignoreError; SENSE_DATA senseData; RtlZeroMemory(&senseData, sizeof(senseData)); RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CDB10.OperationCode = SCSIOP_WRITE; cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)¤tBlock)->Byte3; cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)¤tBlock)->Byte2; cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)¤tBlock)->Byte1; cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)¤tBlock)->Byte0; if ((currentBlock + 0x10) <= (NumberOfBlocks + POST_GAP_SIZE)) { cdb.CDB10.TransferBlocksLsb = 0x10; } else { cdb.CDB10.TransferBlocksLsb = (UCHAR)(NumberOfBlocks + POST_GAP_SIZE - currentBlock); } writeCompleted = SptSendCdbToDeviceEx(CdromHandle, &cdb, 10, buffer, &bufferSize, &senseData, sizeof(SENSE_DATA), FALSE, 50 // timeout seconds ); ignoreError = IsSenseDataInTable(AllowedBurnSense, AllowedBurnSenseEntries, &senseData); if ((!writeCompleted) && ignoreError) { printf("Continuing on %x/%x/%x\n", senseData.SenseKey & 0xf, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier ); 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) loop } // random block to have local variable writeCompleted // write completed success, so continue. currentBlock += 0x10; } 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, HACK_FLAGS HackFlags ) { LONG numberOfBlocks; LONG availableBlocks; LONG firstLba; LONG i; //////////////////////////////////////////////////////////////////////////////// // verify the iso image file looks correct //////////////////////////////////////////////////////////////////////////////// if (!VerifyIsoImage(IsoImageHandle, &numberOfBlocks)) { printf("Error verifying ISO image\n"); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // verify (as best as possible) that it's blank media //////////////////////////////////////////////////////////////////////////////// if (!VerifyBlankMedia(CdromHandle)) { printf("Error verifying blank media\n"); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // Setup the write mode page //////////////////////////////////////////////////////////////////////////////// if (!SetWriteModePage(CdromHandle, (BOOLEAN)HackFlags.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"); if (!HackFlags.IgnoreModePageErrors) { return GetLastError(); } else { printf("Ignoring error and attempting to continue...\n"); } } //////////////////////////////////////////////////////////////////////////////// // get next writable address //////////////////////////////////////////////////////////////////////////////// if (!GetNextWritableAddress(CdromHandle, 0x01, &firstLba, &availableBlocks)) { printf("Error verifying next writable address\n"); return GetLastError(); } if (availableBlocks < numberOfBlocks) { printf("Error verifying free blocks on media (%d needed, %d available)\n", numberOfBlocks, availableBlocks); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return GetLastError(); } if (firstLba != 0) { printf("Error verifying next writable address is zero\n"); SetLastError(ERROR_MEDIA_INCOMPATIBLE); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // set the cd speed to four for now, can later make a cmd-line switch //////////////////////////////////////////////////////////////////////////////// if (!SetRecordingSpeed(CdromHandle, 4)) { printf("Error setting the cd speed to %d\n", 4); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // calibrate the drive's power -- this is optional, so let it fail! //////////////////////////////////////////////////////////////////////////////// if (!SendOptimumPowerCalibration(CdromHandle)) { printf("WARNING: setting optimum power calibration failed\n"); //return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // start writing //////////////////////////////////////////////////////////////////////////////// if (!BurnThisSession(CdromHandle, IsoImageHandle, numberOfBlocks, firstLba)) { printf("Error burning ISO image\n"); return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // close the track -- ignore failures //////////////////////////////////////////////////////////////////////////////// if (!CloseTrack(CdromHandle, 0)) { printf("WARNING: error closing the track -- may be ignored?\n"); //return GetLastError(); } //////////////////////////////////////////////////////////////////////////////// // close the session //////////////////////////////////////////////////////////////////////////////// if (!CloseSession(CdromHandle)) { printf("Error closing session\n"); 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; }