2383 lines
72 KiB
C
2383 lines
72 KiB
C
|
/* Copyright (c) Microsoft Corporation. All rights reserved. */
|
|||
|
|
|||
|
#include <string.h>
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <assert.h>
|
|||
|
#include <sptlib.h>
|
|||
|
#include "burn.h"
|
|||
|
#include <winioctl.h>
|
|||
|
#include <strsafe.h>
|
|||
|
|
|||
|
#define MIN_WRITE_SECTORS (0x10)
|
|||
|
|
|||
|
#if DBG
|
|||
|
#define OUTPUT stdout
|
|||
|
#define FPRINTF(x) fprintf x
|
|||
|
#define PRINTBUFFER(x) PrintBuffer x
|
|||
|
#else
|
|||
|
#define OUTPUT stdout
|
|||
|
#define FPRINTF(x)
|
|||
|
#define PRINTBUFFER(x)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
#define _SECOND ((ULONGLONG) 10000000)
|
|||
|
#define _MINUTE (60 * _SECOND)
|
|||
|
#define _HOUR (60 * _MINUTE)
|
|||
|
#define _DAY (24 * _HOUR)
|
|||
|
|
|||
|
__inline
|
|||
|
ULONGLONG
|
|||
|
GetSystemTimeAsUlonglong(
|
|||
|
void
|
|||
|
)
|
|||
|
{
|
|||
|
FILETIME t;
|
|||
|
GetSystemTimeAsFileTime( &t );
|
|||
|
return (((ULONGLONG) t.dwHighDateTime) << 32) + t.dwLowDateTime;
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
InitializeOptions(
|
|||
|
IN PROGRAM_OPTIONS * Options
|
|||
|
)
|
|||
|
{
|
|||
|
RtlZeroMemory( Options, sizeof(PROGRAM_OPTIONS) );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ParseCommandLine(
|
|||
|
IN DWORD Count,
|
|||
|
IN PUCHAR Arguments[],
|
|||
|
OUT PROGRAM_OPTIONS * Options
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
HRESULT hr;
|
|||
|
|
|||
|
InitializeOptions(Options);
|
|||
|
|
|||
|
if ( Count < 3 )
|
|||
|
{
|
|||
|
// not enough args, just print help
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
for(i = 1; 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], "-erase", strlen("-erase")) == 0)
|
|||
|
{
|
|||
|
printf("Erasing media before burning\n");
|
|||
|
Options->Erase = 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(Options->DeviceName == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// The first non-flag argument is the device name.
|
|||
|
//
|
|||
|
|
|||
|
Options->DeviceName = Arguments[i];
|
|||
|
|
|||
|
} else if(Options->ImageName == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// The second non-flag argument is the image name. This is
|
|||
|
// optional if the -erase flag has been provided.
|
|||
|
//
|
|||
|
|
|||
|
Options->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(Options->DeviceName == NULL)
|
|||
|
{
|
|||
|
printf("Error: must supply device name.\n");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if(Options->ImageName == NULL)
|
|||
|
{
|
|||
|
printf("Error: must supply image name.\n");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int __cdecl main(int argc, char *argv[])
|
|||
|
{
|
|||
|
int i = 0;
|
|||
|
HANDLE cdromHandle;
|
|||
|
HANDLE isoImageHandle;
|
|||
|
char buffer[120]; // ~50 chars for mountvol names
|
|||
|
|
|||
|
PROGRAM_OPTIONS options;
|
|||
|
|
|||
|
if (!ParseCommandLine(argc, argv, &options))
|
|||
|
{
|
|||
|
printf("Usage: dvdburn <drive> <image> [/Erase]\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
HRESULT hr;
|
|||
|
hr = StringCchPrintf(buffer,
|
|||
|
RTL_NUMBER_OF(buffer),
|
|||
|
"\\\\.\\%s",
|
|||
|
options.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;
|
|||
|
}
|
|||
|
|
|||
|
isoImageHandle = CreateFile(options.ImageName,
|
|||
|
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 -4;
|
|||
|
}
|
|||
|
|
|||
|
BurnCommand(cdromHandle, isoImageHandle, options.Erase);
|
|||
|
|
|||
|
CloseHandle(isoImageHandle);
|
|||
|
CloseHandle(cdromHandle);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
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,
|
|||
|
BOOLEAN Erase
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD numberOfBlocks;
|
|||
|
DWORD availableBlocks;
|
|||
|
DVDBURN_MEDIA_TYPE mediaType;
|
|||
|
DWORD nwa;
|
|||
|
LONG i;
|
|||
|
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// verify the iso image file looks correct
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
if (!VerifyIsoImage(IsoImageHandle, &numberOfBlocks)) {
|
|||
|
printf("Error verifying ISO image\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// determine the probable media type from GET_CONFIG
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
mediaType = DvdBurnMediaUnknown;
|
|||
|
if (!GetMediaType(CdromHandle, &mediaType)) {
|
|||
|
printf("GetMediaType() failed (%d). Unable to continue.\n",
|
|||
|
GetLastError());
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
switch (mediaType) {
|
|||
|
|
|||
|
case DvdBurnMediaRam: {
|
|||
|
printf("Media type: %s\n", "random-access media (DVD-RAM, MRW)");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashR: {
|
|||
|
printf("Media type: %s\n", "DVD-R");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashRW: {
|
|||
|
printf("Media type: %s\n", "DVD-RW");
|
|||
|
break;
|
|||
|
}
|
|||
|
case DvdBurnMediaDashRWPacket: {
|
|||
|
printf("Media type: %s\n", "DVD-RW (packet)");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaPlusR: {
|
|||
|
printf("Media type: %s\n", "DVD+R");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaPlusRW: {
|
|||
|
printf("Media type: %s\n", "DVD+RW");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaUnknown: {
|
|||
|
printf("Media type: %s\n", "Unknown Media Type");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default: {
|
|||
|
printf("Unknown media type: 0x%02x\n", mediaType);
|
|||
|
mediaType = DvdBurnMediaUnknown;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
} // end switch(mediaType)
|
|||
|
|
|||
|
|
|||
|
if (mediaType == DvdBurnMediaUnknown) {
|
|||
|
printf("Media is unknown type, unsupported, or there is no media "
|
|||
|
"in the drive.\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// erase the media
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (Erase) {
|
|||
|
|
|||
|
printf("Erasing media\n");
|
|||
|
|
|||
|
switch (mediaType) {
|
|||
|
|
|||
|
case DvdBurnMediaRam: {
|
|||
|
printf("Erasing random-access media is not a supported option (%s)\n",
|
|||
|
"(not neccessary)");
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashR: {
|
|||
|
printf("Erasing DVD-R media is not a supported option (%s)\n",
|
|||
|
"(not possible)");
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashRW:
|
|||
|
case DvdBurnMediaDashRWPacket: {
|
|||
|
if (!EraseMedia(CdromHandle)) {
|
|||
|
printf("Error erasing media DVD-RW media (is it -R?)\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaPlusR: {
|
|||
|
printf("Erasing DVD+R media is not a supported option (%s)\n",
|
|||
|
"(not possible)");
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaPlusRW: {
|
|||
|
printf("Erasing DVD+RW media is not a supported option (%s)\n",
|
|||
|
"(not neccessary)");
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
}
|
|||
|
} // end switch(mediaType)
|
|||
|
|
|||
|
// re-acquire media type after the erase
|
|||
|
// required due to packet-written -RW media types
|
|||
|
mediaType = DvdBurnMediaUnknown;
|
|||
|
if (!GetMediaType(CdromHandle, &mediaType)) {
|
|||
|
printf("Drive failed GET CONFIGURATION command after erase? (%d)\n",
|
|||
|
GetLastError());
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
if (mediaType == DvdBurnMediaUnknown) {
|
|||
|
printf("Media is unknown type, unsupported, or there is "
|
|||
|
"no media in the drive after the erase.\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
} // end erase
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// pre-process the media (verify blank, format if needed, setup write)
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
printf("Preparing media...\n");
|
|||
|
|
|||
|
switch (mediaType) {
|
|||
|
|
|||
|
case DvdBurnMediaRam: {
|
|||
|
if (!VerifyMediaCapacity(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("This DVD+RW Media is not large enough to contain the "
|
|||
|
"entire image you selected.\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashRWPacket: {
|
|||
|
printf("Packet written DVD-RW media needs to be erased before use\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaDashR:
|
|||
|
case DvdBurnMediaDashRW: {
|
|||
|
|
|||
|
if (!VerifyBlankMedia(CdromHandle)) {
|
|||
|
printf("DVD-R/RW Media must be blank or erased before use by "
|
|||
|
"this utility.\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
// DVD-R/RW media never verified capacity with this utility because
|
|||
|
// it reserves a zone to burn to (which would fail) if the disc
|
|||
|
// was too small.
|
|||
|
|
|||
|
if (!SetWriteModePageDao(CdromHandle, TRUE)) {
|
|||
|
printf("DVD-R/RW media requires setting mode page to use "
|
|||
|
"DAO writing.\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
if (!SendOPC(CdromHandle)) {
|
|||
|
printf("DVD-R/RW media failed to set OPC via SEND_OPC command; "
|
|||
|
"this error will be ignored, some drives can work "
|
|||
|
"without this\n");
|
|||
|
}
|
|||
|
|
|||
|
// why send a timestamp? -- use a fun date.
|
|||
|
if (!SendTimeStamp(CdromHandle, "20021225000000")) { // YYYYMMDDHHMMSS format
|
|||
|
printf("Error setting timestamp; this error will be ignored, "
|
|||
|
"some drives can work without this\n");
|
|||
|
}
|
|||
|
|
|||
|
// Reserve the RZone for this burn
|
|||
|
if (!ReserveRZone(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("Error reserving zone for burn\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case DvdBurnMediaPlusRW:
|
|||
|
case DvdBurnMediaPlusR: {
|
|||
|
|
|||
|
if ( mediaType == DvdBurnMediaPlusRW )
|
|||
|
{
|
|||
|
// always re-format DVD+RW media for mastering an image
|
|||
|
printf("DVD+RW media will always be formatted.\n");
|
|||
|
printf("Formatting may take 1-2 minutes.\n");
|
|||
|
if (!QuickFormatPlusRWMedia(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("Error formatting the DVD+R/RW media\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
// verify media capacity
|
|||
|
if (!VerifyMediaCapacity(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("This DVD+RW Media is not large enough to contain the "
|
|||
|
"entire image you selected.\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DISC_INFORMATION discInfo;
|
|||
|
ULONG size = sizeof(DISC_INFORMATION);
|
|||
|
|
|||
|
if (!ReadDiscInformation(CdromHandle, &discInfo, &size)) {
|
|||
|
printf("Unable to read disc information for the DVD+R/RW "
|
|||
|
"media (%d)\n", GetLastError());
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
if ((discInfo.LastSessionStatus != 0) ||
|
|||
|
(discInfo.DiscStatus != 0)) {
|
|||
|
printf("Non-blank DVD+R disc is not supported\n");
|
|||
|
return ERROR_MEDIA_INCOMPATIBLE;
|
|||
|
}
|
|||
|
if (!ReserveRZone(CdromHandle, numberOfBlocks)) {
|
|||
|
printf("Error reserving zone for burn\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
}
|
|||
|
// mode page settings do not apply to DVD+R/RW media
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default: {
|
|||
|
printf("Coding error: unsupported media type %d, line %d\n", mediaType, __LINE__);
|
|||
|
return -1;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// get NWA via Read RZone Informationcommand, specifying RZone 1 for blank disk
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// Special case -- blank disc is always zero
|
|||
|
nwa = 0;
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// start writing
|
|||
|
// everything writes from LBA nwa through LBA nwa+NumberOfBlocks
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!BurnThisSession(CdromHandle, IsoImageHandle, numberOfBlocks, nwa)) {
|
|||
|
printf("Error burning ISO image\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// post-process the media
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
// wait for it to finish
|
|||
|
if (!WaitForBurnToCompleteAndFinalizeMedia(CdromHandle, mediaType, numberOfBlocks)) {
|
|||
|
printf("Error waiting for burn to complete\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
// eject the newly burned disc!
|
|||
|
////////////////////////////////////////////////////////////////////////////////
|
|||
|
|
|||
|
if (!SendStartStopUnit(CdromHandle, FALSE, TRUE)) {
|
|||
|
printf("Error ejecting disc\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
if (!SendStartStopUnit(CdromHandle, TRUE, TRUE)) {
|
|||
|
printf("Error reinserting disc\n");
|
|||
|
return GetLastError();
|
|||
|
}
|
|||
|
|
|||
|
printf("Burn successful!\n");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
BurnThisSession(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN HANDLE IsoImageHandle,
|
|||
|
IN DWORD NumberOfBlocks,
|
|||
|
IN DWORD FirstLba
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD bufferSize = 0x800 * MIN_WRITE_SECTORS; // sixteen blocks per...
|
|||
|
PUCHAR buffer = NULL;
|
|||
|
DWORD i;
|
|||
|
BOOLEAN sleptOnceAlready = FALSE;
|
|||
|
|
|||
|
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 (i = 0; i < NumberOfBlocks; i += MIN_WRITE_SECTORS) {
|
|||
|
|
|||
|
CDB cdb;
|
|||
|
DWORD currentSize;
|
|||
|
DWORD readBytes;
|
|||
|
DWORD j;
|
|||
|
SENSE_DATA senseData;
|
|||
|
DWORD tmp;
|
|||
|
|
|||
|
if ( i % (8*MIN_WRITE_SECTORS) == 0 ) {
|
|||
|
static CHAR progress[4] = { '|', '/', '-', '\\' };
|
|||
|
DWORD percent;
|
|||
|
percent = (i*1000) / NumberOfBlocks;
|
|||
|
// # # # . # % _ d o n e _ *
|
|||
|
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
|
|||
|
printf("%c %3d.%d%% done",
|
|||
|
progress[(i/(8*MIN_WRITE_SECTORS))%4],
|
|||
|
percent / 10, percent % 10
|
|||
|
);
|
|||
|
fflush(stdout);
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(buffer, bufferSize);
|
|||
|
|
|||
|
if (NumberOfBlocks - i >= MIN_WRITE_SECTORS) {
|
|||
|
currentSize = 0x800 * 0x10;
|
|||
|
} else if (NumberOfBlocks - i > 0) {
|
|||
|
// end of file case -- zero memory first!
|
|||
|
RtlZeroMemory(buffer, bufferSize);
|
|||
|
currentSize = (NumberOfBlocks - i) * 0x800;
|
|||
|
} else {
|
|||
|
FPRINTF((OUTPUT, "INTERNAL ERROR line %d\n", __LINE__));
|
|||
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!ReadFile(IsoImageHandle, buffer, currentSize, &readBytes, NULL)) {
|
|||
|
FPRINTF((OUTPUT, "error reading from file %d\n", GetLastError()));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (readBytes != currentSize) {
|
|||
|
FPRINTF((OUTPUT, "error only read %d of %d bytes\n",
|
|||
|
readBytes, currentSize));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// must write the full buffer each time for DVD-R,
|
|||
|
// since it's a RESTRICTED_OVERWRITE medium and seems
|
|||
|
// to choke otherwise
|
|||
|
//
|
|||
|
|
|||
|
j = 0;
|
|||
|
retryThisWrite:
|
|||
|
j++;
|
|||
|
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.CDB10.OperationCode = SCSIOP_WRITE;
|
|||
|
cdb.CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&i)->Byte3;
|
|||
|
cdb.CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&i)->Byte2;
|
|||
|
cdb.CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&i)->Byte1;
|
|||
|
cdb.CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&i)->Byte0;
|
|||
|
|
|||
|
cdb.CDB10.TransferBlocksLsb = MIN_WRITE_SECTORS;
|
|||
|
tmp = bufferSize;
|
|||
|
|
|||
|
//
|
|||
|
// NOTE: we always send full buffer size to ensure 32k alignment
|
|||
|
//
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
buffer,
|
|||
|
&tmp,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
FALSE,
|
|||
|
10)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT,
|
|||
|
"Sleeping .25 seconds, LBA %x sense %02x/%02x/%02x\n",
|
|||
|
i,
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier
|
|||
|
));
|
|||
|
|
|||
|
if (IsSenseDataInTable(AllowedBurnSense,
|
|||
|
AllowedBurnSenseEntries,
|
|||
|
&senseData)
|
|||
|
//&& (j<300) // 300*.1 seconds == 30 seconds to start writing
|
|||
|
)
|
|||
|
{
|
|||
|
// just sleep a bit...
|
|||
|
if ( sleptOnceAlready )
|
|||
|
{
|
|||
|
Sleep(100); // 100ms == .1 seconds
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sleptOnceAlready = TRUE;
|
|||
|
Sleep(1000); // 1000ms == 1 second
|
|||
|
}
|
|||
|
|
|||
|
goto retryThisWrite;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "\nError %d in writing LBA 0x%x (%x times)\n",
|
|||
|
GetLastError(), i, j));
|
|||
|
LocalFree(buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
// yes, the trailing spaces are neccessary
|
|||
|
printf("\b\b\b\b\b\b\b\b\b\b\b\b\b- 100.0%% done \n");
|
|||
|
printf("Finished Writing\n");
|
|||
|
fflush(stdout);
|
|||
|
LocalFree(buffer);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
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
|
|||
|
EraseMedia(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD bufferSize;
|
|||
|
|
|||
|
printf( "Attempting to blank media...");
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.AsByte[0] = 0xa1;
|
|||
|
cdb.AsByte[1] = 0x11; // minimal blank
|
|||
|
bufferSize = 0;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 12,
|
|||
|
NULL, &bufferSize, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d blanking media\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, 600))
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "\nError %d blanking media\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
printf("\n");
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SetWriteModePageDao(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN BOOLEAN FinalSession
|
|||
|
)
|
|||
|
{
|
|||
|
MODE_CDROM_WRITE_PARAMETERS_PAGE2 * params = NULL;
|
|||
|
ULONG paramsSize = 0;
|
|||
|
BOOLEAN error = FALSE;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Setting DAO mode in WriteParameters mode page... "));
|
|||
|
|
|||
|
if (!GetModePage(CdromHandle,
|
|||
|
(UCHAR **)¶ms,
|
|||
|
¶msSize,
|
|||
|
MODE_PAGE_WRITE_PARAMETERS,
|
|||
|
ModePageRequestTypeDefaultValues))
|
|||
|
{
|
|||
|
error = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if ( !error )
|
|||
|
{
|
|||
|
params->PageLength =
|
|||
|
(UCHAR)
|
|||
|
(paramsSize -
|
|||
|
RTL_SIZEOF_THROUGH_FIELD( MODE_CDROM_WRITE_PARAMETERS_PAGE2, PageLength )
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
params->LinkSizeValid = 0;
|
|||
|
params->TestWrite = 0;
|
|||
|
params->WriteType = 2; // Disc-at-once
|
|||
|
|
|||
|
if ( FinalSession )
|
|||
|
{
|
|||
|
params->MultiSession = 0x00; // no more sessions/borders allowed
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
params->MultiSession = 0x03; // allow more sessions
|
|||
|
}
|
|||
|
params->Copy = 0x00; // original disc
|
|||
|
params->FixedPacket = 0;
|
|||
|
params->TrackMode = 0x4; // data track, uninterrupted, copy prohibited
|
|||
|
|
|||
|
params->DataBlockType = 0x8; // Mode 1 -- ignored for DVD
|
|||
|
params->SessionFormat = 0x00; // Data Disc -- ignored for DVD
|
|||
|
params->MediaCatalogNumber[0] = 0x00;
|
|||
|
params->ISRC[0] = 0x00;
|
|||
|
params->BufferUnderrunFreeEnabled = 1;
|
|||
|
}
|
|||
|
|
|||
|
if ( !error )
|
|||
|
{
|
|||
|
if (!SetModePage(CdromHandle, (BYTE*)params, paramsSize))
|
|||
|
{
|
|||
|
params->BufferUnderrunFreeEnabled = 0;
|
|||
|
if (!SetModePage(CdromHandle, (BYTE*)params, paramsSize))
|
|||
|
{
|
|||
|
error = TRUE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "pass (no BUFE).\n"));
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( params != NULL )
|
|||
|
{
|
|||
|
LocalFree(params);
|
|||
|
}
|
|||
|
|
|||
|
return (!error);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ReserveRZone(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN DWORD numberOfBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size = 0;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Reserving RZone... "));
|
|||
|
|
|||
|
if (numberOfBlocks % MIN_WRITE_SECTORS) {
|
|||
|
FPRINTF((OUTPUT, "increasing size by 0x%x blocks... ",
|
|||
|
MIN_WRITE_SECTORS - (numberOfBlocks % MIN_WRITE_SECTORS)));
|
|||
|
numberOfBlocks /= MIN_WRITE_SECTORS;
|
|||
|
numberOfBlocks *= MIN_WRITE_SECTORS;
|
|||
|
numberOfBlocks += MIN_WRITE_SECTORS;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.RESERVE_TRACK_RZONE.OperationCode = SCSIOP_RESERVE_TRACK_RZONE;
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[0] = (UCHAR)(numberOfBlocks >> 24);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[1] = (UCHAR)(numberOfBlocks >> 16);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[2] = (UCHAR)(numberOfBlocks >> 8);
|
|||
|
cdb.RESERVE_TRACK_RZONE.ReservationSize[3] = (UCHAR)(numberOfBlocks >> 0);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 10,
|
|||
|
NULL, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Error reserving Track/RZone\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SendStartStopUnit(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN BOOLEAN Start,
|
|||
|
IN BOOLEAN Eject
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size = 0;
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
|||
|
cdb.START_STOP.LoadEject = Eject;
|
|||
|
cdb.START_STOP.Start = Start;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 6,
|
|||
|
NULL, &size, TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Error sending Start/Stop unit\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SendOPC(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size = 0;
|
|||
|
SENSE_DATA sense;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Sending OPC... "));
|
|||
|
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
RtlZeroMemory(&sense, sizeof(SENSE_DATA));
|
|||
|
|
|||
|
cdb.SEND_OPC_INFORMATION.OperationCode = SCSIOP_SEND_OPC_INFORMATION;
|
|||
|
cdb.SEND_OPC_INFORMATION.DoOpc = 1;
|
|||
|
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
&sense,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
FALSE,
|
|||
|
60*4)) // four minute timeout should be sufficient
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "Error sending OPC information: %02x/%02x/%02x\n",
|
|||
|
sense.SenseKey, sense.AdditionalSenseCode,
|
|||
|
sense.AdditionalSenseCodeQualifier));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SendTimeStamp(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN PUCHAR DateString
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
SEND_DVD_STRUCTURE_TIMESTAMP timeStamp;
|
|||
|
DWORD size;
|
|||
|
|
|||
|
size = sizeof(SEND_DVD_STRUCTURE_TIMESTAMP);
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Sending Timestamp... "));
|
|||
|
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
cdb.AsByte[0] = 0xbf;
|
|||
|
cdb.AsByte[7] = 0x0f; // format == time stamp
|
|||
|
cdb.AsByte[8] = (UCHAR)(size >> 8);
|
|||
|
cdb.AsByte[9] = (UCHAR)(size & 0xff);
|
|||
|
|
|||
|
RtlZeroMemory(&timeStamp, sizeof(SEND_DVD_STRUCTURE_TIMESTAMP));
|
|||
|
if (strlen(DateString) != 14) {
|
|||
|
FPRINTF((OUTPUT, "Incorrect string length for date\n"));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
RtlCopyMemory(timeStamp.Year, DateString+0x00, 4);
|
|||
|
RtlCopyMemory(timeStamp.Month, DateString+0x04, 2);
|
|||
|
RtlCopyMemory(timeStamp.Day, DateString+0x06, 2);
|
|||
|
RtlCopyMemory(timeStamp.Hour, DateString+0x08, 2);
|
|||
|
RtlCopyMemory(timeStamp.Minute, DateString+0x0a, 2);
|
|||
|
RtlCopyMemory(timeStamp.Second, DateString+0x0c, 2);
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle, &cdb, 12,
|
|||
|
(PUCHAR)&timeStamp, &size, FALSE)) {
|
|||
|
FPRINTF((OUTPUT, "Error sending dvd timestamp\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ReadDiscInformation(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN PDISC_INFORMATION DiscInfo,
|
|||
|
IN OUT PULONG UsableSize
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD maxSize = *UsableSize;
|
|||
|
DWORD size;
|
|||
|
SENSE_DATA senseData;
|
|||
|
|
|||
|
if ((UsableSize == NULL) ||
|
|||
|
(DiscInfo == NULL) ) {
|
|||
|
printf("CODE ERROR: Invalid argument to ReadDiscInfo\n");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Getting Disc Info... "));
|
|||
|
|
|||
|
*UsableSize = 0;
|
|||
|
RtlZeroMemory(DiscInfo, maxSize);
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
|
|||
|
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 (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
(PUCHAR)DiscInfo,
|
|||
|
&size,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
SPT_DEFAULT_TIMEOUT)) {
|
|||
|
FPRINTF((OUTPUT, "\nError %d getting disc info\n",
|
|||
|
GetLastError()));
|
|||
|
FPRINTF((OUTPUT, "Sense/ASC/ASCQ == %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER((&senseData, sizeof(SENSE_DATA)));
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// return the minimum of the set:
|
|||
|
// { input size, returned data, header informed size }
|
|||
|
{
|
|||
|
|
|||
|
ULONG t =
|
|||
|
(DiscInfo->Length[0] << (8*1)) |
|
|||
|
(DiscInfo->Length[1] << (8*0));
|
|||
|
t += RTL_SIZEOF_THROUGH_FIELD(DISC_INFORMATION, Length);
|
|||
|
// t is now the size based on the header
|
|||
|
|
|||
|
// take lesser of returned data and header info
|
|||
|
if (size > t) {
|
|||
|
size = t;
|
|||
|
}
|
|||
|
|
|||
|
// take lesser of input max and above
|
|||
|
if (size > maxSize) {
|
|||
|
size = maxSize;
|
|||
|
}
|
|||
|
}
|
|||
|
*UsableSize = size;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyBlankMedia(
|
|||
|
IN HANDLE CdromHandle
|
|||
|
)
|
|||
|
{
|
|||
|
DISC_INFORMATION discInfo;
|
|||
|
ULONG size = sizeof(DISC_INFORMATION);
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Verifying blank disc... "));
|
|||
|
if (!ReadDiscInformation(CdromHandle, &discInfo, &size)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Disc Info Buffer:\n"));
|
|||
|
PRINTBUFFER((&discInfo, size));
|
|||
|
|
|||
|
if (discInfo.LastSessionStatus != 0x00) {
|
|||
|
FPRINTF((OUTPUT, "disc is not blank!\n"));
|
|||
|
SetLastError(ERROR_MEDIA_INCOMPATIBLE);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
FPRINTF((OUTPUT, "pass.\n"));
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyIsoImage(
|
|||
|
IN HANDLE IsoImageHandle,
|
|||
|
OUT PDWORD NumberOfBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
BY_HANDLE_FILE_INFORMATION isoImageInfo;
|
|||
|
LONGLONG size;
|
|||
|
|
|||
|
if (!GetFileInformationByHandle(IsoImageHandle, &isoImageInfo)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d getting file info for iso image\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
size = ((LONGLONG)isoImageInfo.nFileSizeHigh) << 32;
|
|||
|
size |= (LONGLONG)isoImageInfo.nFileSizeLow;
|
|||
|
|
|||
|
if ((isoImageInfo.nFileSizeLow % 2048) != 0) {
|
|||
|
FPRINTF((OUTPUT, "Error: The file size is not a multiple of 2048 (%I64d)\n",
|
|||
|
size));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "File size is %I64d bytes (%d blocks)\n",
|
|||
|
size, size / 2048));
|
|||
|
|
|||
|
if ((LONGLONG)((size / 2048) >> 32) != 0) {
|
|||
|
FPRINTF((OUTPUT, "Error: The file is too large (%I64d)\n",
|
|||
|
size));
|
|||
|
SetLastError(ERROR_INVALID_DATA);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
*NumberOfBlocks = (DWORD)(size / 2048);
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
VerifyMediaCapacity(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN DWORD RequiredBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
DWORD size;
|
|||
|
FOUR_BYTE temp1;
|
|||
|
DWORD temp2;
|
|||
|
READ_CAPACITY_DATA capacity;
|
|||
|
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
RtlZeroMemory(&capacity, sizeof(READ_CAPACITY_DATA));
|
|||
|
size = sizeof(READ_CAPACITY_DATA);
|
|||
|
|
|||
|
cdb.CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
|||
|
|
|||
|
if (!SptSendCdbToDevice(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
(PUCHAR)&capacity,
|
|||
|
&size,
|
|||
|
TRUE)) {
|
|||
|
FPRINTF((OUTPUT, "Unable to successfully call READ_CAPACITY (%d)\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// translate into natural endian
|
|||
|
temp1.AsULong = capacity.BytesPerBlock;
|
|||
|
temp2 =
|
|||
|
(temp1.Byte0 << (8*3)) |
|
|||
|
(temp1.Byte1 << (8*2)) |
|
|||
|
(temp1.Byte2 << (8*1)) |
|
|||
|
(temp1.Byte3 << (8*0)) ;
|
|||
|
|
|||
|
if (temp2 != 2048) {
|
|||
|
FPRINTF((OUTPUT, "Invalid block size of %x\n", temp2));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// translate into natural endian
|
|||
|
temp1.AsULong = capacity.LogicalBlockAddress;
|
|||
|
temp2 =
|
|||
|
(temp1.Byte0 << (8*3)) |
|
|||
|
(temp1.Byte1 << (8*2)) |
|
|||
|
(temp1.Byte2 << (8*1)) |
|
|||
|
(temp1.Byte3 << (8*0)) ;
|
|||
|
temp2 += 1; // zero-based result from READ_CAPACITY
|
|||
|
|
|||
|
if (temp2 < RequiredBlocks) {
|
|||
|
FPRINTF((OUTPUT, "Disc size of %x too small (need %x blocks)\n",
|
|||
|
temp2, RequiredBlocks));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
#define LBA_FOR_FIRST_30_MM_OF_DATA (0x70DE0)
|
|||
|
#define SECTORS_FOR_LEADOUT_2_MM_OF_DATA (0xF000) // estimation -- not hard estimate
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
WaitForBurnToCompleteAndFinalizeMedia(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
DVDBURN_MEDIA_TYPE MediaType,
|
|||
|
ULONG SectorsUsedOnDisc // for time estimates
|
|||
|
)
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
SENSE_DATA senseData;
|
|||
|
DWORD size;
|
|||
|
DWORD i = 0;
|
|||
|
ULONG estimatedSecondsToCompletion;
|
|||
|
ULONGLONG startSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG failureSystemTime;
|
|||
|
|
|||
|
if ( MediaType == DvdBurnMediaDashRW )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 30 * 60;
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaDashR )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 30 * 60;
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaPlusR )
|
|||
|
{
|
|||
|
// time to close disc is the time to append the disc to a minimum
|
|||
|
// size apporoximately 30 mm written data and the time to write the leadout.
|
|||
|
if ( SectorsUsedOnDisc >= LBA_FOR_FIRST_30_MM_OF_DATA )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion =
|
|||
|
( LBA_FOR_FIRST_30_MM_OF_DATA - SectorsUsedOnDisc );
|
|||
|
}
|
|||
|
estimatedSecondsToCompletion +=
|
|||
|
SECTORS_FOR_LEADOUT_2_MM_OF_DATA;
|
|||
|
|
|||
|
// single-speed DVD is about 8x cdrom speed
|
|||
|
// could ask drive for speed, but just use maximum time of 1x speed
|
|||
|
estimatedSecondsToCompletion /= (8 * 75);
|
|||
|
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaPlusRW )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 4 * 60;
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaRam )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 4 * 60;
|
|||
|
}
|
|||
|
|
|||
|
// always allow at least four minutes
|
|||
|
if ( estimatedSecondsToCompletion < 4 * 60 )
|
|||
|
{
|
|||
|
estimatedSecondsToCompletion = 4 * 60;
|
|||
|
}
|
|||
|
|
|||
|
failureSystemTime = startSystemTime + (estimatedSecondsToCompletion * _SECOND);
|
|||
|
|
|||
|
printf("Waiting for drive to finalize disc (this may take "
|
|||
|
"up to %d minutes)...",
|
|||
|
(estimatedSecondsToCompletion+59)/60
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// send flush_cache to synchronize the media and the drive's cache
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|||
|
cdb.SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
|
|||
|
cdb.SYNCHRONIZE_CACHE10.Immediate = 1;
|
|||
|
size = 0;
|
|||
|
|
|||
|
//
|
|||
|
// wait up to ten minutes (600 seconds) for burn to complete
|
|||
|
// because some units ignore the immediate bit in this command
|
|||
|
//
|
|||
|
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
TRUE,
|
|||
|
600)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d sending SYNCHRONIZE_CACHE\n",
|
|||
|
GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, estimatedSecondsToCompletion)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
{
|
|||
|
ULONGLONG finishSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG totalSeconds =
|
|||
|
(finishSystemTime - startSystemTime) / _SECOND;
|
|||
|
FPRINTF((OUTPUT, "SYNCH_CACHE finished %I64d seconds into finalization process\n",
|
|||
|
totalSeconds));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( (MediaType == DvdBurnMediaDashRW) ||
|
|||
|
(MediaType == DvdBurnMediaDashR ) ||
|
|||
|
(MediaType == DvdBurnMediaRam )
|
|||
|
)
|
|||
|
{
|
|||
|
// DVD-R/RW media in Disc-at-Once mode just requires a SYNCH_CACHE
|
|||
|
// DVD-RAM media just requires a SYNC_CACHE
|
|||
|
printf(".");
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaPlusR )
|
|||
|
{
|
|||
|
//
|
|||
|
// DVD+R must close track and then close session
|
|||
|
// DVD+RW only requires a CLOSE_SESSION, and may not have a CLOSE_TRACK
|
|||
|
//
|
|||
|
{
|
|||
|
size = 0;
|
|||
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
|
|||
|
cdb.CLOSE_TRACK.OperationCode = SCSIOP_CLOSE_TRACK_SESSION;
|
|||
|
cdb.CLOSE_TRACK.Immediate = 0x1;
|
|||
|
cdb.CLOSE_TRACK.Track = 0x1;
|
|||
|
cdb.CLOSE_TRACK.TrackNumber[0] = 0;
|
|||
|
cdb.CLOSE_TRACK.TrackNumber[1] = 1;
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
10)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d sending CLOSE SESSION (ignoring)\n",
|
|||
|
GetLastError()));
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
} else if (senseData.SenseKey != SCSI_SENSE_NO_SENSE) {
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x (ignoring)\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
}
|
|||
|
//
|
|||
|
// spin doing a READ_DISC_INFO
|
|||
|
//
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, estimatedSecondsToCompletion)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
{
|
|||
|
ULONGLONG finishSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG totalSeconds =
|
|||
|
(finishSystemTime - startSystemTime) / _SECOND;
|
|||
|
FPRINTF((OUTPUT, "CLOSE_TRACK finished %I64d seconds into finalization process\n",
|
|||
|
totalSeconds));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// then finalize the disc to be -ROM compatible
|
|||
|
//
|
|||
|
{
|
|||
|
size = 0;
|
|||
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
|
|||
|
cdb.CLOSE_TRACK.OperationCode = SCSIOP_CLOSE_TRACK_SESSION;
|
|||
|
cdb.CLOSE_TRACK.Immediate = 0x1;
|
|||
|
cdb.AsByte[2] = ( cdb.AsByte[2] & 0xF8 ) | 0x5; // compatible close
|
|||
|
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
10)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d sending CLOSE SESSION\n",
|
|||
|
GetLastError()));
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
return FALSE;
|
|||
|
} else if (senseData.SenseKey != SCSI_SENSE_NO_SENSE) {
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
printf(".");
|
|||
|
|
|||
|
//
|
|||
|
// spin doing a READ_DISC_INFO
|
|||
|
//
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, estimatedSecondsToCompletion)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
{
|
|||
|
ULONGLONG finishSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG totalSeconds =
|
|||
|
(finishSystemTime - startSystemTime) / _SECOND;
|
|||
|
FPRINTF((OUTPUT, "CLOSE_SESSION finished %I64d seconds into finalization process\n",
|
|||
|
totalSeconds));
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( MediaType == DvdBurnMediaPlusRW )
|
|||
|
{
|
|||
|
//
|
|||
|
// then finalize the disc to be -ROM compatible
|
|||
|
//
|
|||
|
{
|
|||
|
size = 0;
|
|||
|
RtlZeroMemory(&cdb, sizeof(cdb));
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
|
|||
|
cdb.CLOSE_TRACK.OperationCode = SCSIOP_CLOSE_TRACK_SESSION;
|
|||
|
cdb.CLOSE_TRACK.Immediate = 0x1;
|
|||
|
cdb.CLOSE_TRACK.Session = 0x1;
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
NULL,
|
|||
|
&size,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
10)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d sending CLOSE SESSION\n",
|
|||
|
GetLastError()));
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
return FALSE;
|
|||
|
} else if (senseData.SenseKey != SCSI_SENSE_NO_SENSE) {
|
|||
|
FPRINTF((OUTPUT, "Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier));
|
|||
|
PRINTBUFFER(((PUCHAR)&senseData, sizeof(SENSE_DATA)));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
printf(".");
|
|||
|
|
|||
|
//
|
|||
|
// spin doing a READ_DISC_INFO
|
|||
|
//
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, estimatedSecondsToCompletion)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
{
|
|||
|
ULONGLONG finishSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG totalSeconds =
|
|||
|
(finishSystemTime - startSystemTime) / _SECOND;
|
|||
|
FPRINTF((OUTPUT, "CLOSE_SESSION finished %I64d seconds into finalization process\n",
|
|||
|
totalSeconds));
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("support for media type %d not written yet?\n", MediaType);
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
ULONGLONG finishSystemTime = GetSystemTimeAsUlonglong();
|
|||
|
ULONGLONG totalSeconds =
|
|||
|
(finishSystemTime - startSystemTime) / _SECOND;
|
|||
|
printf("\nSuccess: Finalizing media took %I64d seconds\n",
|
|||
|
totalSeconds);
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetFeature(
|
|||
|
IN HANDLE CdRomHandle,
|
|||
|
IN FEATURE_NUMBER Feature,
|
|||
|
IN PVOID Buffer,
|
|||
|
IN OUT PULONG InSize // IN = available, OUT = used
|
|||
|
)
|
|||
|
{
|
|||
|
GET_CONFIGURATION_IOCTL_INPUT input;
|
|||
|
ULONG returned;
|
|||
|
ULONG size = *InSize;
|
|||
|
|
|||
|
*InSize = 0;
|
|||
|
|
|||
|
RtlZeroMemory(&input, sizeof(GET_CONFIGURATION_IOCTL_INPUT));
|
|||
|
RtlZeroMemory(Buffer, size);
|
|||
|
|
|||
|
input.Feature = Feature;
|
|||
|
input.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE;
|
|||
|
|
|||
|
if (!DeviceIoControl(CdRomHandle,
|
|||
|
IOCTL_CDROM_GET_CONFIGURATION,
|
|||
|
&input,
|
|||
|
sizeof(GET_CONFIGURATION_IOCTL_INPUT),
|
|||
|
Buffer,
|
|||
|
size,
|
|||
|
&returned,
|
|||
|
FALSE)) {
|
|||
|
FPRINTF((OUTPUT, "Error %d requesting feature %04x\n",
|
|||
|
GetLastError(), Feature));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
*InSize = returned;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetMediaIsMediaDvdDashRorRW(
|
|||
|
IN HANDLE CdRomHandle
|
|||
|
)
|
|||
|
{
|
|||
|
UCHAR headerBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_DVD_RECORDABLE_WRITE)];
|
|||
|
PGET_CONFIGURATION_HEADER header = (PGET_CONFIGURATION_HEADER)headerBuffer;
|
|||
|
PFEATURE_DATA_DVD_RECORDABLE_WRITE data = (PFEATURE_DATA_DVD_RECORDABLE_WRITE)header->Data;
|
|||
|
ULONG size;
|
|||
|
|
|||
|
size = 0x10;
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
FeatureDvdRecordableWrite,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: GetFeature failed %x\n", GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: size too small\n"));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!(data->Header.Current)){
|
|||
|
|
|||
|
FEATURE_PROFILE_TYPE profile;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: feature not current\n"));
|
|||
|
|
|||
|
profile =
|
|||
|
(header->CurrentProfile[0] << (8*1)) |
|
|||
|
(header->CurrentProfile[1] << (8*0)) ;
|
|||
|
|
|||
|
//
|
|||
|
// not current but exists, check profile?
|
|||
|
//
|
|||
|
|
|||
|
if (profile == ProfileDvdRecordable) {
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: Profile is DvdRecordable, continuing\n"));
|
|||
|
}
|
|||
|
else if (profile == ProfileDvdRewritable) {
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: Profile is DvdRewritable, continuing\n"));
|
|||
|
}
|
|||
|
else if (profile == ProfileDvdRWSequential) {
|
|||
|
FPRINTF((OUTPUT, "IsMedia-RW: Profile is DvdRWSequential, continuing\n"));
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// the profile suggests the media *can* be written to, but only if blank'd
|
|||
|
|
|||
|
// todo: do more exhaustive search here.
|
|||
|
|
|||
|
return TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetMediaIsMediaDvdPlusRorRW(
|
|||
|
IN HANDLE CdRomHandle,
|
|||
|
IN BOOLEAN CheckForRW
|
|||
|
)
|
|||
|
{
|
|||
|
UCHAR headerBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_DVD_PLUS_RW)];
|
|||
|
PGET_CONFIGURATION_HEADER header = (PGET_CONFIGURATION_HEADER)headerBuffer;
|
|||
|
PFEATURE_DATA_DVD_PLUS_RW data = (PFEATURE_DATA_DVD_PLUS_RW)header->Data;
|
|||
|
ULONG feature;
|
|||
|
ULONG size;
|
|||
|
PUCHAR string;
|
|||
|
|
|||
|
// HACK -- overload use of FEATURE_DATA_DVD_PLUS_RW, which has same
|
|||
|
// structure as +R feature for checked bits.
|
|||
|
|
|||
|
size = sizeof(headerBuffer);
|
|||
|
feature = (CheckForRW ? FeatureDvdPlusRW : 0x2B);
|
|||
|
string = (CheckForRW ? "IsMedia+RW" : "IsMedia+R");
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
feature,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
FPRINTF((OUTPUT, "%s: GetFeature failed %x\n",
|
|||
|
string, GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
FPRINTF((OUTPUT, "%s: size too small\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!(data->Header.Current)){
|
|||
|
FPRINTF((OUTPUT, "%s: feature not current\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!(data->Write)) {
|
|||
|
FPRINTF((OUTPUT, "%s: write bit not set\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
BOOLEAN
|
|||
|
GetMediaIsMediaDvdPlusR(
|
|||
|
IN HANDLE CdRomHandle
|
|||
|
)
|
|||
|
{
|
|||
|
return GetMediaIsMediaDvdPlusRorRW(CdRomHandle, FALSE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetMediaIsMediaDvdPlusRW(
|
|||
|
IN HANDLE CdRomHandle
|
|||
|
)
|
|||
|
{
|
|||
|
return GetMediaIsMediaDvdPlusRorRW(CdRomHandle, TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetMediaIsMediaDvdRam(
|
|||
|
IN HANDLE CdRomHandle
|
|||
|
)
|
|||
|
{
|
|||
|
UCHAR randomReadBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_RANDOM_READABLE)];
|
|||
|
UCHAR defectManagementBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_DEFECT_MANAGEMENT)];
|
|||
|
UCHAR randomWriteBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_RANDOM_WRITABLE)];
|
|||
|
|
|||
|
PFEATURE_DATA_RANDOM_READABLE randomReadFeature = (PFEATURE_DATA_RANDOM_READABLE) (randomReadBuffer + sizeof(GET_CONFIGURATION_HEADER));
|
|||
|
PFEATURE_DATA_DEFECT_MANAGEMENT defectManagementFeature = (PFEATURE_DATA_DEFECT_MANAGEMENT)(defectManagementBuffer + sizeof(GET_CONFIGURATION_HEADER));
|
|||
|
PFEATURE_DATA_RANDOM_WRITABLE randomWriteFeature = (PFEATURE_DATA_RANDOM_WRITABLE) (randomWriteBuffer + sizeof(GET_CONFIGURATION_HEADER));
|
|||
|
|
|||
|
{
|
|||
|
PGET_CONFIGURATION_HEADER headerBuffer =
|
|||
|
(PGET_CONFIGURATION_HEADER)randomWriteBuffer;
|
|||
|
ULONG size = sizeof(randomWriteBuffer);
|
|||
|
FEATURE_NUMBER feature = FeatureRandomWritable;
|
|||
|
UCHAR * string = "IsMediaDvdRam (writable)";
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
feature,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
FPRINTF((OUTPUT, "%s: GetFeature failed %x\n",
|
|||
|
string, GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
FPRINTF((OUTPUT, "%s: size too small\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (!(randomWriteFeature->Header.Current))
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "%s: feature not current\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
PGET_CONFIGURATION_HEADER headerBuffer =
|
|||
|
(PGET_CONFIGURATION_HEADER)randomReadBuffer;
|
|||
|
ULONG size = sizeof(randomReadBuffer);
|
|||
|
FEATURE_NUMBER feature = FeatureRandomReadable;
|
|||
|
UCHAR * string = "IsMediaDvdRam (readable)";
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
feature,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
FPRINTF((OUTPUT, "%s: GetFeature failed %x\n",
|
|||
|
string, GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
FPRINTF((OUTPUT, "%s: size too small\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (!(randomReadFeature->Header.Current))
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "%s: feature not current\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
PGET_CONFIGURATION_HEADER headerBuffer =
|
|||
|
(PGET_CONFIGURATION_HEADER)defectManagementBuffer;
|
|||
|
ULONG size = sizeof(defectManagementBuffer);
|
|||
|
FEATURE_NUMBER feature = FeatureDefectManagement;
|
|||
|
UCHAR * string = "IsMediaDvdRam (defect managed)";
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
feature,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
FPRINTF((OUTPUT, "%s: GetFeature failed %x\n",
|
|||
|
string, GetLastError()));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
FPRINTF((OUTPUT, "%s: size too small\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if (!(defectManagementFeature->Header.Current))
|
|||
|
{
|
|||
|
FPRINTF((OUTPUT, "%s: feature not current\n", string));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetMediaType(
|
|||
|
IN HANDLE CdRomHandle,
|
|||
|
IN PDVDBURN_MEDIA_TYPE MediaType
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
*MediaType = DvdBurnMediaUnknown;
|
|||
|
|
|||
|
// figure out (the hard way) what media is in the drive
|
|||
|
// we need to search for each one in "best" possible order
|
|||
|
//
|
|||
|
if (GetMediaIsMediaDvdPlusRW(CdRomHandle)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Chose DVD+RW due to DVD+RW feature current\n"));
|
|||
|
*MediaType = DvdBurnMediaPlusRW;
|
|||
|
|
|||
|
} else
|
|||
|
if (GetMediaIsMediaDvdPlusR(CdRomHandle)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Chose DVD+R due to DVD+R feature current\n"));
|
|||
|
*MediaType = DvdBurnMediaPlusR;
|
|||
|
|
|||
|
} else
|
|||
|
if (GetMediaIsMediaDvdDashRorRW(CdRomHandle)) {
|
|||
|
|
|||
|
DISC_INFORMATION discInfo;
|
|||
|
ULONG size;
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Chose DVD-R or -RW due to DVD-R/RW feature current\n"));
|
|||
|
|
|||
|
// default to rewritable
|
|||
|
*MediaType = DvdBurnMediaDashRW;
|
|||
|
|
|||
|
// get the disc info to see if erasable bit is set to 1
|
|||
|
|
|||
|
size = sizeof(DISC_INFORMATION);
|
|||
|
if (!ReadDiscInformation(CdRomHandle, &discInfo, &size)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "unable to read disc info -- will use -RW "
|
|||
|
"media type as default\n"));
|
|||
|
|
|||
|
} else if (size < sizeof(DISK_INFORMATION)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "read disc info too small (%x) -- will use -RW "
|
|||
|
"media type as default\n", size));
|
|||
|
|
|||
|
} else if (discInfo.Erasable) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "disc is erasable -- will use -RW\n"));
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "disc is not erasable -- using -R\n"));
|
|||
|
*MediaType = DvdBurnMediaDashR;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// check if it's formatted for packet-writing
|
|||
|
if (*MediaType == DvdBurnMediaDashRW) {
|
|||
|
|
|||
|
UCHAR headerBuffer[sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_DATA_DVD_RW_RESTRICTED_OVERWRITE)];
|
|||
|
PGET_CONFIGURATION_HEADER header = (PGET_CONFIGURATION_HEADER)headerBuffer;
|
|||
|
PFEATURE_DATA_DVD_RW_RESTRICTED_OVERWRITE data = (PFEATURE_DATA_DVD_RW_RESTRICTED_OVERWRITE)header->Data;
|
|||
|
ULONG size;
|
|||
|
|
|||
|
size = sizeof(headerBuffer);
|
|||
|
|
|||
|
if (!GetFeature(CdRomHandle,
|
|||
|
FeatureRigidRestrictedOverwrite,
|
|||
|
headerBuffer,
|
|||
|
&size)) {
|
|||
|
// nothing;
|
|||
|
} else
|
|||
|
if (size <= sizeof(GET_CONFIGURATION_HEADER)) {
|
|||
|
|
|||
|
// nothing;
|
|||
|
|
|||
|
} else
|
|||
|
if (!(data->Header.Current)) {
|
|||
|
|
|||
|
// nothing;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Chose DVD-RW Packet due to Rigid Restricted "
|
|||
|
"Overwrite feature current\n"));
|
|||
|
*MediaType = DvdBurnMediaDashRWPacket;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else
|
|||
|
if (GetMediaIsMediaDvdRam(CdRomHandle)) {
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Chose DVD-RAM due to random read/write and target "
|
|||
|
"defect management features current\n"));
|
|||
|
*MediaType = DvdBurnMediaRam;
|
|||
|
|
|||
|
} else
|
|||
|
{
|
|||
|
|
|||
|
FPRINTF((OUTPUT, "Unknown media type\n"));
|
|||
|
*MediaType = DvdBurnMediaUnknown;
|
|||
|
|
|||
|
}
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
QuickFormatPlusRWMedia(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN ULONG NumberOfBlocks
|
|||
|
)
|
|||
|
{
|
|||
|
ULONG size = 0xc;
|
|||
|
UCHAR formatBuffer[0xc];
|
|||
|
SENSE_DATA senseData;
|
|||
|
CDB cdb;
|
|||
|
|
|||
|
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
RtlZeroMemory(&formatBuffer, size);
|
|||
|
|
|||
|
cdb.CDB6FORMAT.OperationCode = SCSIOP_FORMAT_UNIT;
|
|||
|
cdb.CDB6FORMAT.FormatControl = 0x11;
|
|||
|
|
|||
|
//formatBuffer[0x0] = 0x00;
|
|||
|
//formatBuffer[0x1] = 0x00; // (same as 0x82)
|
|||
|
//formatBuffer[0x2] = 0x00;
|
|||
|
formatBuffer[0x3] = 0x08;
|
|||
|
formatBuffer[0x4] = 0xff; //---vvv
|
|||
|
formatBuffer[0x5] = 0xff; // NumberOfBlocks must be set to 0xffffffff
|
|||
|
formatBuffer[0x6] = 0xff; //
|
|||
|
formatBuffer[0x7] = 0xff; //--^^^^
|
|||
|
formatBuffer[0x8] = (0x26 << 2); // format code. ends up 0x98.
|
|||
|
//formatBuffer[0x9] = 0x00;
|
|||
|
//formatBuffer[0xa] = 0x00;
|
|||
|
//formatBuffer[0xb] = 0x00;
|
|||
|
|
|||
|
if (!SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
6,
|
|||
|
formatBuffer,
|
|||
|
&size,
|
|||
|
&senseData,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
FALSE,
|
|||
|
120)) { // allow up to two minutes for this.
|
|||
|
printf("Unable to format, %x\n", GetLastError());
|
|||
|
printf("Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier);
|
|||
|
PrintBuffer((PUCHAR)&senseData, sizeof(SENSE_DATA));
|
|||
|
return FALSE;
|
|||
|
} else if (senseData.SenseKey != SCSI_SENSE_NO_SENSE) {
|
|||
|
printf("Sense Buffer: %02x/%02x/%02x\n",
|
|||
|
senseData.SenseKey,
|
|||
|
senseData.AdditionalSenseCode,
|
|||
|
senseData.AdditionalSenseCodeQualifier);
|
|||
|
PrintBuffer((PUCHAR)&senseData, sizeof(SENSE_DATA));
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if (!WaitForReadDiscInfoToSucceed(CdromHandle, 600)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
DISC_INFORMATION discInfo = {0};
|
|||
|
ULONG discInfoSize = sizeof(DISC_INFORMATION);
|
|||
|
if (!ReadDiscInformation(CdromHandle,
|
|||
|
&discInfo,
|
|||
|
&discInfoSize)) {
|
|||
|
printf("ReadDiscInfo failed after format\n");
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// ReadDiscInfo succeeded, check format status.
|
|||
|
// exit with error if it's wrong.
|
|||
|
|
|||
|
switch (discInfo.MrwStatus) {
|
|||
|
|
|||
|
case 0x0: {
|
|||
|
printf("DVD+RW Disc is not formatted -- format may have "
|
|||
|
"failed???\n");
|
|||
|
return FALSE;
|
|||
|
break;
|
|||
|
}
|
|||
|
case 0x1: {
|
|||
|
printf("DVD+RW Disc is partially formatted -- format "
|
|||
|
"succeeded\n");
|
|||
|
return TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
case 0x2: {
|
|||
|
printf("DVD+RW Disc is formatting in background -- format "
|
|||
|
"succeeded\n");
|
|||
|
return TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
case 0x3: {
|
|||
|
printf("DVD+RW Disc is fully formatted -- format "
|
|||
|
"succeeded\n");
|
|||
|
return TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
default: {
|
|||
|
printf("DVD+RW Disc has unknown MRW Status of %x\n",
|
|||
|
discInfo.MrwStatus);
|
|||
|
return FALSE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
WaitForReadDiscInfoToSucceed(
|
|||
|
IN HANDLE CdromHandle,
|
|||
|
IN ULONG SecondsToAllow
|
|||
|
)
|
|||
|
{
|
|||
|
DISC_INFORMATION discInfo;
|
|||
|
ULONG discInfoSize;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// spin here until READ_DISC_INFO says disc is partially formatted
|
|||
|
// (which implies it is ready to write to)
|
|||
|
//
|
|||
|
|
|||
|
for (i=0;i<SecondsToAllow;i++) { // up to 256 seconds for this to occur
|
|||
|
|
|||
|
Sleep(1000); // one second wait
|
|||
|
|
|||
|
discInfoSize = sizeof(DISC_INFORMATION);
|
|||
|
RtlZeroMemory(&discInfo, discInfoSize);
|
|||
|
|
|||
|
if (ReadDiscInformation(CdromHandle, &discInfo, &discInfoSize)) {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
printf(".");
|
|||
|
}
|
|||
|
|
|||
|
printf("Unable to get a ReadDiscInfo in %d seconds, treating as a failure\n",
|
|||
|
SecondsToAllow
|
|||
|
);
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
GetModePage(
|
|||
|
HANDLE CdromHandle,
|
|||
|
UCHAR ** ModePageData,
|
|||
|
ULONG * ValidSize,
|
|||
|
UCHAR ModePage,
|
|||
|
SPT_MODE_PAGE_REQUEST_TYPE IncomingModePageType
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
UCHAR scsiModePageType = 0;
|
|||
|
|
|||
|
if ( ModePageData == NULL )
|
|||
|
{
|
|||
|
hr = E_POINTER;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*ModePageData = NULL;
|
|||
|
}
|
|||
|
if ( ValidSize == NULL )
|
|||
|
{
|
|||
|
hr = E_POINTER;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*ValidSize = 0;
|
|||
|
}
|
|||
|
if ( ModePage > 0x3F )
|
|||
|
{
|
|||
|
hr = E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
if ( IncomingModePageType == ModePageRequestTypeCurrentValues )
|
|||
|
{
|
|||
|
scsiModePageType = 0;
|
|||
|
}
|
|||
|
else if ( IncomingModePageType == ModePageRequestTypeChangableValues )
|
|||
|
{
|
|||
|
scsiModePageType = 1;
|
|||
|
}
|
|||
|
else if ( IncomingModePageType == ModePageRequestTypeDefaultValues )
|
|||
|
{
|
|||
|
scsiModePageType = 2;
|
|||
|
}
|
|||
|
else if ( IncomingModePageType == ModePageRequestTypeSavedValues )
|
|||
|
{
|
|||
|
scsiModePageType = 3;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
hr = E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
// only now is it safe to use the arguments....
|
|||
|
UCHAR *tmpPage = NULL;
|
|||
|
ULONG requiredSize = 0;
|
|||
|
|
|||
|
// first get the size of the requested mode page values
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
SENSE_DATA sense;
|
|||
|
MODE_PARAMETER_HEADER10 header;
|
|||
|
ULONG bufferSize;
|
|||
|
|
|||
|
bufferSize = sizeof(MODE_PARAMETER_HEADER10);
|
|||
|
|
|||
|
RtlZeroMemory( &sense, sizeof(SENSE_DATA) );
|
|||
|
RtlZeroMemory( &header, sizeof(MODE_PARAMETER_HEADER10) );
|
|||
|
RtlZeroMemory( &cdb, sizeof(CDB) );
|
|||
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|||
|
cdb.MODE_SENSE10.Pc = scsiModePageType;
|
|||
|
cdb.MODE_SENSE10.PageCode = ModePage;
|
|||
|
cdb.MODE_SENSE10.Dbd = 1;
|
|||
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8);
|
|||
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff);
|
|||
|
|
|||
|
if ( !SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
(PUCHAR)&header,
|
|||
|
&bufferSize,
|
|||
|
&sense,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
30
|
|||
|
) )
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else if ( sense.SenseKey != SCSI_SENSE_NO_SENSE )
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else if ( (header.BlockDescriptorLength[0] != 0) ||
|
|||
|
(header.BlockDescriptorLength[1] != 0)
|
|||
|
)
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// success!
|
|||
|
bufferSize =
|
|||
|
(header.ModeDataLength[0] << (8*1)) +
|
|||
|
(header.ModeDataLength[1] << (8*0)) +
|
|||
|
RTL_SIZEOF_THROUGH_FIELD( MODE_PARAMETER_HEADER10, ModeDataLength );
|
|||
|
requiredSize = bufferSize;
|
|||
|
hr = S_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// allocate for the whole page
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
tmpPage = (UCHAR *)LocalAlloc(LPTR, requiredSize);
|
|||
|
if ( tmpPage == NULL )
|
|||
|
{
|
|||
|
hr = E_OUTOFMEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// then get the requested mode page values from the device
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
CDB cdb;
|
|||
|
SENSE_DATA sense;
|
|||
|
ULONG bufferSize = requiredSize;
|
|||
|
PMODE_PARAMETER_HEADER10 header = (PMODE_PARAMETER_HEADER10)tmpPage;
|
|||
|
|
|||
|
RtlZeroMemory(&sense, sizeof(SENSE_DATA));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
|
|||
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|||
|
cdb.MODE_SENSE10.Pc = scsiModePageType; // default values requested
|
|||
|
cdb.MODE_SENSE10.PageCode = ModePage;
|
|||
|
cdb.MODE_SENSE10.Dbd = 1;
|
|||
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferSize >> 8);
|
|||
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferSize & 0xff);
|
|||
|
|
|||
|
if ( !SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
(PUCHAR)header,
|
|||
|
&bufferSize,
|
|||
|
&sense,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
TRUE,
|
|||
|
30
|
|||
|
) )
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else if ( sense.SenseKey != SCSI_SENSE_NO_SENSE )
|
|||
|
{
|
|||
|
if (SUCCEEDED(hr)) { hr = E_FAIL; }
|
|||
|
}
|
|||
|
else if ( (header->BlockDescriptorLength[0] != 0) ||
|
|||
|
(header->BlockDescriptorLength[1] != 0)
|
|||
|
)
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// success, verify data length or error out.
|
|||
|
bufferSize =
|
|||
|
(header->ModeDataLength[0] << (8*1)) +
|
|||
|
(header->ModeDataLength[1] << (8*0)) +
|
|||
|
RTL_SIZEOF_THROUGH_FIELD( MODE_PARAMETER_HEADER10, ModeDataLength );
|
|||
|
if ( bufferSize != requiredSize )
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
// don't need nor want to pass the mode page header back to the
|
|||
|
// caller -- it just complexifies things for them. Copy the
|
|||
|
// memory such that they only get the actual page.
|
|||
|
|
|||
|
RtlMoveMemory( tmpPage,
|
|||
|
tmpPage+sizeof(MODE_PARAMETER_HEADER10),
|
|||
|
requiredSize-sizeof(MODE_PARAMETER_HEADER10) );
|
|||
|
|
|||
|
// requiredSize is now smaller
|
|||
|
requiredSize -= sizeof(MODE_PARAMETER_HEADER10);
|
|||
|
|
|||
|
// don't bother re-alloc'ing to free the extra 8-bytes.
|
|||
|
// adds another error path if the realloc fails.
|
|||
|
// tmpPage2 = CoTaskMemRealloc( tmpPage, requiredSize );
|
|||
|
// if ( tmpPage2 != NULL ) { tmpPage = tmpPage2; }
|
|||
|
|
|||
|
*ModePageData = tmpPage;
|
|||
|
*ValidSize = requiredSize;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( FAILED(hr) )
|
|||
|
{
|
|||
|
if ( tmpPage != NULL )
|
|||
|
{
|
|||
|
LocalFree( tmpPage );
|
|||
|
tmpPage = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( FAILED(hr) && GetLastError() == 0 )
|
|||
|
{
|
|||
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|||
|
}
|
|||
|
return SUCCEEDED(hr);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
SetModePage(
|
|||
|
HANDLE CdromHandle,
|
|||
|
UCHAR * ModePageData,
|
|||
|
ULONG ValidSize
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
PUCHAR tmpPage = NULL;
|
|||
|
ULONG tmpPageSize = 0;
|
|||
|
|
|||
|
if ( ModePageData == NULL )
|
|||
|
{
|
|||
|
hr = E_POINTER;
|
|||
|
}
|
|||
|
if ( (ValidSize > 0x400) || (ValidSize == 0) )
|
|||
|
{
|
|||
|
hr = E_INVALIDARG;
|
|||
|
}
|
|||
|
|
|||
|
// allocate the memory required
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
tmpPageSize = ValidSize + sizeof(MODE_PARAMETER_HEADER10);
|
|||
|
tmpPage = (PUCHAR)LocalAlloc( LPTR, tmpPageSize );
|
|||
|
if ( tmpPage == NULL )
|
|||
|
{
|
|||
|
tmpPageSize = 0;
|
|||
|
hr = E_OUTOFMEMORY;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
RtlZeroMemory( tmpPage, tmpPageSize );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// copy the mode page they provided and setup the header
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
PMODE_PARAMETER_HEADER10 header = (PMODE_PARAMETER_HEADER10)tmpPage;
|
|||
|
RtlCopyMemory( tmpPage + sizeof(MODE_PARAMETER_HEADER10),
|
|||
|
ModePageData,
|
|||
|
ValidSize );
|
|||
|
|
|||
|
header->ModeDataLength[0] = 0;
|
|||
|
header->ModeDataLength[1] = 0;
|
|||
|
header->MediumType = 0;
|
|||
|
header->DeviceSpecificParameter = 0;
|
|||
|
header->BlockDescriptorLength[0] = 0;
|
|||
|
header->BlockDescriptorLength[1] = 0;
|
|||
|
}
|
|||
|
|
|||
|
if ( SUCCEEDED(hr) )
|
|||
|
{
|
|||
|
// set the mode page with the given data
|
|||
|
CDB cdb;
|
|||
|
SENSE_DATA sense;
|
|||
|
ULONG tmp = tmpPageSize;
|
|||
|
RtlZeroMemory(&sense, sizeof(SENSE_DATA));
|
|||
|
RtlZeroMemory(&cdb, sizeof(CDB));
|
|||
|
|
|||
|
cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
|
|||
|
cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR)(tmpPageSize >> 8);
|
|||
|
cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR)(tmpPageSize & 0xff);
|
|||
|
cdb.MODE_SELECT10.PFBit = 1;
|
|||
|
|
|||
|
if ( !SptSendCdbToDeviceEx(CdromHandle,
|
|||
|
&cdb,
|
|||
|
10,
|
|||
|
tmpPage,
|
|||
|
&tmp,
|
|||
|
&sense,
|
|||
|
sizeof(SENSE_DATA),
|
|||
|
FALSE,
|
|||
|
30
|
|||
|
) )
|
|||
|
{
|
|||
|
hr = E_FAIL;
|
|||
|
}
|
|||
|
else if ( sense.SenseKey != SCSI_SENSE_NO_SENSE )
|
|||
|
{
|
|||
|
if (SUCCEEDED(hr)) { hr = E_FAIL; }
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
hr = S_OK;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if ( tmpPage != NULL )
|
|||
|
{
|
|||
|
LocalFree( tmpPage );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( FAILED(hr) && GetLastError() == 0 )
|
|||
|
{
|
|||
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|||
|
}
|
|||
|
return SUCCEEDED(hr);
|
|||
|
|
|||
|
}
|
|||
|
|