You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1841 lines
54 KiB
1841 lines
54 KiB
/*++
|
|
|
|
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 <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <windows.h>
|
|
#include <strsafe.h>
|
|
|
|
#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 <n> required for '-speed <n>' 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 <drive> -erase [image [options]]\n"
|
|
"\tcdburn <drive> 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;
|
|
}
|