/* Example code from http://forums.techguy.org/software-development/809038-scsi-programming-pointer-help.html */
#include <stdio.h>
#include <windows.h>
#include <ntddscsi.h>
#include <ddk/ntddcdvd.h>
#include "dsk_struct.h"

USHORT ShortSwap( USHORT s ) {
  unsigned char b1, b2;
  b1 = s & 255;
  b2 = (s >> 8) & 255;
  return (b1 << 8) + b2; }

DWORD dwordSwap (DWORD i) {
  unsigned char b1, b2, b3, b4;
  b1 = (char)(i & 255);
  b2 = (char)(( i >> 8 ) & 255);
  b3 = (char)(( i>>16 ) & 255);
  b4 = (char)(( i>>24 ) & 255);
  return ((DWORD)b1 << 24) + ((DWORD)b2 << 16) + ((DWORD)b3 << 8) + b4;}


void dumpLayer(DVD_LAYER_DESCRIPTOR *p)
{
    static const char *DiskCats[] = {
	"DVD-ROM", "DVD-RAM", "DVD-R", "DVD-RW", "HD DVD-ROM",
	"HD DVD-RAM", "HD DVD-R", "Reserved",
	"Reserved", "DVD+RW", "DVD+R", "Reserved", "Reserved", "DVD+RW DL",
	"DVD+R DL", "Reserved"
    };

    printf("BookVersion        : 0x%X\n", p->BookVersion);
    printf("BookType           : 0x%X ==> %s MediaType\n", p->BookType, DiskCats[p->BookType]);
    printf("MinimumRate        : 0x%X\n", p->MinimumRate);
    printf("DiskSize           : %u\n", p->DiskSize);
    printf("LayerType          : %u\n", p->LayerType);
    printf("TrackPath          : 0x%X\n", p->TrackPath);
    printf("NumberOfLayers     : %u\n", p->NumberOfLayers);
    printf("TrackDensity       : %u\n", p->TrackDensity);
    printf("LinearDensity      : %u\n", p->LinearDensity);
    printf("StartingDataSector : %#08x\n", dwordSwap(p->StartingDataSector));
    printf("EndDataSector      : %#08x\n", dwordSwap(p->EndDataSector));
    printf("EndLayerZeroSector : 0x%X\n", dwordSwap(p->EndLayerZeroSector));
    printf("BCAFlag            : %u\n", p->BCAFlag);
}

void discStructure(HANDLE device)
{
    READ_DVD_STRUCTURE readStruct = { 0 };	//Creates CDB structure
    // ddk's struct has zero-length member, doesn't compile
    typedef struct my_DVD_DESCRIPTOR_HEADER {
	short Length;
	short pad;
    } my_DVD_DESCRIPTOR_HEADER;
    struct {
	my_DVD_DESCRIPTOR_HEADER Header;
	DVD_LAYER_DESCRIPTOR layer;
    } recvStruct;
    DWORD returnedBuf;		//Bytes received through DeviceIoControl
    BOOL bRet;
    DWORD error;
    unsigned char cmdLen[sizeof(SCSI_PASS_THROUGH_DIRECT) + 96] = { 0 };
    SCSI_PASS_THROUGH_DIRECT *discStructureSCSIptd =
	(SCSI_PASS_THROUGH_DIRECT *) cmdLen;

    readStruct.OperationCode = SCSIOP_discStructure;
    readStruct.AllocationLength = sizeof(READ_DVD_STRUCTURE);
    readStruct.Format = 0x0;	// just for clarity
    memcpy(discStructureSCSIptd->Cdb, &readStruct, sizeof(readStruct));

    discStructureSCSIptd->DataBuffer = &recvStruct;
    discStructureSCSIptd->DataTransferLength = sizeof(recvStruct);
    discStructureSCSIptd->DataIn = SCSI_IOCTL_DATA_IN;
    discStructureSCSIptd->CdbLength = sizeof(readStruct);
    discStructureSCSIptd->Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    discStructureSCSIptd->SenseInfoLength = sizeof(cmdLen) - sizeof(SCSI_PASS_THROUGH_DIRECT);
    discStructureSCSIptd->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
    discStructureSCSIptd->TimeOutValue = 6000;

    bRet = DeviceIoControl(device, IOCTL_SCSI_PASS_THROUGH_DIRECT,
			(LPVOID) & cmdLen, sizeof(cmdLen),
			(LPVOID) & cmdLen, sizeof(cmdLen), &returnedBuf,
			NULL);

    error = GetLastError();
    if (error != ERROR_SUCCESS) {
	printf("carry_cdb(): last error = %08X\n", error);
    }

    if (bRet) {
	printf("\n");
	printf("Length             : %i bytes\n", ShortSwap(recvStruct.Header.Length));
	dumpLayer(&recvStruct.layer);
    }
}

int main()
{
    HANDLE hdevice;
    char deviceName[20];
    strcpy(deviceName, "\\\\.\\e:");
printf("Opening e:\n");
    hdevice = CreateFile(deviceName, GENERIC_READ | GENERIC_WRITE,
		   FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
printf("hdevice %x\n", hdevice);
#if 1
    if (hdevice == INVALID_HANDLE_VALUE) {	//Error opening drive
	printf("Error accessing rom drive - creating handle (%d)\n",
	       GetLastError());
	return 1;
    }
    printf("Device : %s\n\n", deviceName);
#endif
    discStructure(hdevice);
    CloseHandle(hdevice);
    return 0;
}
