MMC5r04 sections:
section 6.26.1: For media other than CD, information may be fabricated in order to emulate a CD structure for the specific
media.
section 6.26.3.2.4 Table 482: Shows the fabricated information. DVDs will ALWAYS return 01 for number of tracks even if they have more than one track.
My fix was to write a replacement for GrabTrack and GetTOCInformation using Execute Generic. Using my GrabTrackEx command which doesn't use the TOC track number to limit what tracks I can grab I have been able to rip a multi track DVD and then using the normal Starburn TAO command to burn those multiple tracks to a DVD and get an exact copy of the original. Note my command does not currently use the IsBadBlockIgnore or IsSingleLBTransferForced because I'm not exactly sure what is ment by those settings. Also My GrabtrackEx does not use the Starburn_Callback and it doesn't callback on read retries or bad blocks because I haven't figured out how to determine when a read retry has happened or a bad block has been encountered. I'm hoping this proof of concept will get the Starburn programmers to modify their GrabTrack and GetTrackInformation commands to NOT be limited by the number of tracks listed in the TOC.
Code:
LONG CDVDBurnerGrabber::CalculateSpeed(LARGE_INTEGER Processed,LARGE_INTEGER &LBsProcessed)
{
LBsProcessed.QuadPart=Processed.QuadPart-m_LastLB.QuadPart;
LARGE_INTEGER CurrentTime;
QueryPerformanceCounter(&CurrentTime);
LARGE_INTEGER Time;
Time.QuadPart=CurrentTime.QuadPart-m_LastTime.QuadPart;
m_AverageLB.QuadPart+=LBsProcessed.QuadPart;
m_AverageTime.QuadPart+=Time.QuadPart;
LONG speed=0;
if(m_LastTime.QuadPart!=0){
double TimeUsed = (double)Time.QuadPart/(double)m_Freq.QuadPart;
if(TimeUsed>0.0)
speed = (LONG)((double(LBsProcessed.QuadPart * m_LBSize)/1024.0)/TimeUsed);
}
m_LastTime=CurrentTime;
m_LastLB=Processed;
return speed;
}
LONG CDVDBurnerGrabber::CalculateAverageSpeed()
{
LONG speed=0;
double TimeUsed = (double)m_AverageTime.QuadPart/(double)m_Freq.QuadPart;
if(TimeUsed>0.0)
speed = (LONG)((double(m_AverageLB.QuadPart * m_LBSize)/1024.0)/TimeUsed);
m_AverageTime.QuadPart=0;
m_AverageLB.QuadPart=0;
return speed;
}
BOOL CDVDBurnerGrabber::SetReadWriteErrorRecoveryParameter(UCHAR *ReadRetry,
UCHAR *WriteRetry,
BOOL *AWRE,
BOOL *ARRE,
BOOL *TransferBlock,
BOOL *ReadContinuous,
BOOL *PostError,
BOOL *DisableTransferOnError,
BOOL *DisableCorrection,
UCHAR *EMCDR,
ULONG *ErrorReportingWindowSize)
{
READ_WRITE_ERROR_RECOVERY_PARAMETERS_MODE_PAGE pmp, pmp_verify;
if (!ModeSense(0x01, (void*) &pmp, sizeof(pmp)))
return FALSE;
pmp.ucPageCode = 0x01;
if(ReadRetry)
pmp.ucReadRetryCount=*ReadRetry;
if(WriteRetry)
pmp.ucWriteRetryCount=*WriteRetry;
if(AWRE)
pmp.AWRE=*AWRE;
if(ARRE)
pmp.ARRE=*ARRE;
if(TransferBlock)
pmp.TB=*TransferBlock;
if(ReadContinuous)
pmp.RC=*ReadContinuous;
if(PostError)
pmp.PER=*PostError;
if(DisableTransferOnError)
pmp.DTE=*DisableTransferOnError;
if(DisableCorrection)
pmp.DCR=*DisableCorrection;
if(EMCDR)
pmp.EMCDR=*EMCDR;
if(ErrorReportingWindowSize){
pmp.ucErrorReportingWindowSize[ 0 ] = HIBYTE( HIWORD( ErrorReportingWindowSize ) );
pmp.ucErrorReportingWindowSize[ 1 ] = LOBYTE( HIWORD( ErrorReportingWindowSize ) );
pmp.ucErrorReportingWindowSize[ 2 ] = HIBYTE( LOWORD( ErrorReportingWindowSize ) );
pmp.ucErrorReportingWindowSize[ 3 ] = LOBYTE( LOWORD( ErrorReportingWindowSize ) );
}
CopyMemory(&pmp_verify, &pmp, sizeof(pmp));
if (!ModeSelect((void*) &pmp, sizeof(pmp)))
return FALSE;
if (!ModeSense(0x01, (void*) &pmp, sizeof(pmp)))
return FALSE;
if (memcmp(&pmp_verify, &pmp, sizeof(pmp)))
return FALSE;
return TRUE;
}
BOOL CDVDBurnerGrabber::SetWriteParameter(UCHAR *WriteType,
BOOL *TestWrite,
BOOL *LS_V,
BOOL *BUFE,
UCHAR *TrackMode,
BOOL *Copy,
BOOL *FP,
UCHAR *MultiSession,
UCHAR *DataBlockType,
UCHAR *LinkSize,
UCHAR *HostApplicationCode,
UCHAR *SessionFormat,
ULONG *PacketSize,
UWORD *AudioPause,
UCHAR *MediaCatalogNumberArray,
UCHAR *ISRCArray,
UCHAR *SubHeaderByte0,
UCHAR *SubHeaderByte1,
UCHAR *SubHeaderByte2,
UCHAR *SubHeaderByte3)
{
WRITE_PARAMETERS_MODE_PAGE pmp, pmp_verify;
if (!ModeSense(0x05, (void*) &pmp, sizeof(pmp)))
return FALSE;
pmp.ucPageCode = 0x05;
if(WriteType)
pmp.WriteType=*WriteType;
if(TestWrite)
pmp.TestWrite=*TestWrite;
if(LS_V)
pmp.LS_V=*LS_V;
if(BUFE)
pmp.BUFE=*BUFE;
if(TrackMode)
pmp.TrackMode=*TrackMode;
if(Copy)
pmp.Copy=*Copy;
if(FP)
pmp.FP=*FP;
if(MultiSession)
pmp.MultiSession=*MultiSession;
if(DataBlockType)
pmp.DataBlockType=*DataBlockType;
if(LinkSize)
pmp.LinkSize=*LinkSize;
if(HostApplicationCode)
pmp.HostApplicationCode=*HostApplicationCode;
if(SessionFormat)
pmp.ucSessionFormat=*SessionFormat;
if(PacketSize){
pmp.ucPacketSize[ 0 ] = HIBYTE( HIWORD( PacketSize ) );
pmp.ucPacketSize[ 1 ] = LOBYTE( HIWORD( PacketSize ) );
pmp.ucPacketSize[ 2 ] = HIBYTE( LOWORD( PacketSize ) );
pmp.ucPacketSize[ 3 ] = LOBYTE( LOWORD( PacketSize ) );
}
if(AudioPause){
pmp.ucAudioPauseLength[ 0 ] = HIBYTE( LOWORD( AudioPause ) );
pmp.ucAudioPauseLength[ 1 ] = LOBYTE( LOWORD( AudioPause ) );
}
if(MediaCatalogNumberArray){
memcpy(pmp.ucMediaCatalogNumber,MediaCatalogNumberArray,16);
}
if(ISRCArray){
memcpy(pmp.ucISRC,ISRCArray,16);
}
if(SubHeaderByte0)
pmp.ucSubHeaderByte0=*SubHeaderByte0;
if(SubHeaderByte1)
pmp.ucSubHeaderByte1=*SubHeaderByte1;
if(SubHeaderByte2)
pmp.ucSubHeaderByte2=*SubHeaderByte2;
if(SubHeaderByte3)
pmp.ucSubHeaderByte3=*SubHeaderByte3;
CopyMemory(&pmp_verify, &pmp, sizeof(pmp));
if (!ModeSelect((void*) &pmp, sizeof(pmp)))
return FALSE;
if (!ModeSense(0x05, (void*) &pmp, sizeof(pmp)))
return FALSE;
if (memcmp(&pmp_verify, &pmp, sizeof(pmp)))
return FALSE;
return TRUE;
}
BOOL CDVDBurnerGrabber::ModeSense(unsigned char ucModePage, void* pBuffer, WORD wBufferSize)
{
CDB_MODE_SENSE cdb;
RtlZeroMemory(&cdb,sizeof(cdb));
cdb.Operation_Code = 0x5A; // Code for MODE SENSE (10) command
cdb.PageCode = ucModePage; // Bits7:6, 00=Current Values, 01=Changeable Values, 10=Default Values, 11=Saved Values
cdb.SubpageCode = 0x00; // Subpage not supported by MM devices. Always set 0x00
cdb.AllocLength[0] = HIBYTE(wBufferSize);
cdb.AllocLength[1] = LOBYTE(wBufferSize);
l__EXCEPTION_NUMBER =
StarBurn_CdvdBurnerGrabber_ExecuteGeneric(
l__PVOID__CdvdBurnerGrabber,
( PCHAR )( &l__CHAR__ExceptionText ),
sizeof( l__CHAR__ExceptionText ),
&l__ULONG__SystemError,
&l__CDB_FAILURE_INFORMATION,
( PUCHAR )( &cdb ),
sizeof( cdb ),
( PUCHAR )( pBuffer ),
wBufferSize,
FALSE
);
if (l__EXCEPTION_NUMBER != EN_SUCCESS){
return FALSE;
}
return TRUE;
}
BOOL CDVDBurnerGrabber::ModeSelect(void* pBuffer, WORD wBufferSize)
{
CDB_MODE_SELECT cdb;
RtlZeroMemory(&cdb,sizeof(cdb));
cdb.Operation_Code = 0x55; //Code for MODE SELECT (10) command
cdb.PF = 1; //Structured as page
cdb.ParamLength[0] = HIBYTE(wBufferSize);
cdb.ParamLength[1] = LOBYTE(wBufferSize);
l__EXCEPTION_NUMBER =
StarBurn_CdvdBurnerGrabber_ExecuteGeneric(
l__PVOID__CdvdBurnerGrabber,
( PCHAR )( &l__CHAR__ExceptionText ),
sizeof( l__CHAR__ExceptionText ),
&l__ULONG__SystemError,
&l__CDB_FAILURE_INFORMATION,
( PUCHAR )( &cdb ),
sizeof( cdb ),
( PUCHAR )( pBuffer ),
wBufferSize,
TRUE
);
if (l__EXCEPTION_NUMBER != EN_SUCCESS){
return FALSE;
}
return TRUE;
}
#define TransferSizeInLBs 0x20
EXCEPTION_NUMBER
CDVDBurnerGrabber::StarBurn_CdvdBurnerGrabber_GrabTrackEx(PVOID CdvdBurnerGrabber,
PCHAR ExceptionText,
ULONG ExceptionTextSizeInUCHARs,
PULONG SystemError,
PCDB_FAILURE_INFORMATION CDB_FAILURE_INFORMATION,
UCHAR TrackNumber,
PCHAR FileName,
LONG Retries,
BOOLEAN IsBadBlockIgnore,
BOOLEAN IsSingleLBTransferForced,
ULONG ReadReportDelayInSeconds)
{
UCHAR ReadRetry=Retries;
SetReadWriteErrorRecoveryParameter(&ReadRetry,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
LARGE_INTEGER startLBA,currentLBA,lastLBA,totalLBs,readLBs;
STARBURN_TRACK_INFORMATION trackinfo;
int tracksize;
RtlZeroMemory(
&trackinfo,
sizeof(trackinfo)
);
if(GetTrackInformationEx(TrackNumber,&trackinfo,&tracksize)){
totalLBs.QuadPart = tracksize;
startLBA.QuadPart = currentLBA.QuadPart = trackinfo.m__LONG__TrackStartAddress;
lastLBA.QuadPart = currentLBA.QuadPart+totalLBs.QuadPart;
FILE *file = fopen(FileName,"wb");
CDB_READ_10 cdb;
DWORD databuffer[(2048*TransferSizeInLBs)/4];
DWORD lasttime=GetTickCount();
DWORD currenttime=lasttime;
DWORD testunitreadyrun=0;
while(currentLBA.QuadPart<lastLBA.QuadPart){
if((lastLBA.QuadPart-currentLBA.QuadPart)>=TransferSizeInLBs){
readLBs.QuadPart=TransferSizeInLBs;
}
else{
readLBs.QuadPart=lastLBA.QuadPart-currentLBA.QuadPart;
}
RtlZeroMemory(&cdb,sizeof( cdb ));
cdb.Operation_Code = 0x28; // SCSI_OPCODE_READ;
//cdb.FUA = 0x1;
cdb.LBA[ 0 ] = HIBYTE( HIWORD( currentLBA.QuadPart ) );
cdb.LBA[ 1 ] = LOBYTE( HIWORD( currentLBA.QuadPart ) );
cdb.LBA[ 2 ] = HIBYTE( LOWORD( currentLBA.QuadPart ) );
cdb.LBA[ 3 ] = LOBYTE( LOWORD( currentLBA.QuadPart ) );
cdb.AllocLength[ 0 ] = HIBYTE( LOWORD( readLBs.QuadPart ) );
cdb.AllocLength[ 1 ] = LOBYTE( LOWORD( readLBs.QuadPart ) );
RtlZeroMemory(&databuffer,sizeof( databuffer ));
l__EXCEPTION_NUMBER =
StarBurn_CdvdBurnerGrabber_ExecuteGeneric(
l__PVOID__CdvdBurnerGrabber,
ExceptionText,
ExceptionTextSizeInUCHARs,
SystemError,
CDB_FAILURE_INFORMATION,
( PUCHAR )( &cdb ),
sizeof( cdb ),
( PUCHAR )( &databuffer ),
sizeof( databuffer ),
FALSE
);
if (l__EXCEPTION_NUMBER != EN_SUCCESS){
fclose(file);
return l__EXCEPTION_NUMBER;
}
fwrite(databuffer,sizeof( UCHAR ),(int)(2048*readLBs.QuadPart),file);
LARGE_INTEGER junk,processed;
processed.QuadPart = (currentLBA.QuadPart - startLBA.QuadPart);
LONG speed = CalculateSpeed(processed,junk);
if(speed<1352){
l__EXCEPTION_NUMBER =
StarBurn_CdvdBurnerGrabber_TestUnitReadyEx(
l__PVOID__CdvdBurnerGrabber,
ExceptionText,
ExceptionTextSizeInUCHARs,
SystemError,
CDB_FAILURE_INFORMATION,
1
);
// Check for correct reply
if ( l__EXCEPTION_NUMBER != EN_SUCCESS )
{
return l__EXCEPTION_NUMBER;
}
}
currenttime=GetTickCount();
if((currenttime-lasttime)>(ReadReportDelayInSeconds*1000)){
ULONG percent = (ULONG)(((currentLBA.QuadPart-startLBA.QuadPart)*100)/totalLBs.QuadPart);
LONG averagespeed = CalculateAverageSpeed();
if(m_pCallback)
if(!m_pCallback(m_Index,
m_Drive,
MODE_READING,
percent,
averagespeed,
m_Context)){
fclose(file);
return EN_USER_EXCEPTION;
}
lasttime=currenttime;
}
currentLBA.QuadPart+=readLBs.QuadPart;
}
fclose(file);
return EN_SUCCESS;
}
return EN_INVALID_INPUT_PARAMETER;
}
BOOL CDVDBurnerGrabber::GetTrackInformationEx(LONG tracknumber, PSTARBURN_TRACK_INFORMATION pTrackInformation,int *pTrackSize)
{
int index = ((m_Index+1)*(m_Drive+1))-1;
if(pTrackSize)
*pTrackSize = 0;
if(m_bIsCreated && IsUpStart()){
TRACK_INFORMATION_BLOCK tib;
RtlZeroMemory(
&tib,
sizeof( TRACK_INFORMATION_BLOCK )
);
CDB_READ_TRACK_INFORMATION cdb;
RtlZeroMemory(
&cdb,
sizeof( CDB_READ_TRACK_INFORMATION )
);
cdb.Operation_Code = 0x52;
cdb.AddressType = 0x01;
cdb.Open = 0;
cdb.LTN[0] = HIBYTE(HIWORD(tracknumber)); // MSB track number
cdb.LTN[1] = LOBYTE(HIWORD(tracknumber)); // B02 track number
cdb.LTN[2] = HIBYTE(LOWORD(tracknumber)); // B01 track number
cdb.LTN[3] = LOBYTE(LOWORD(tracknumber)); // LSB track number
cdb.AllocLength[0] = HIBYTE(sizeof( TRACK_INFORMATION_BLOCK )); // MSB allocation length
cdb.AllocLength[1] = LOBYTE(sizeof( TRACK_INFORMATION_BLOCK )); // LSB allocation length
//
// Try to execute command
//
l__EXCEPTION_NUMBER =
StarBurn_CdvdBurnerGrabber_ExecuteGeneric(
l__PVOID__CdvdBurnerGrabber,
( PCHAR )( &l__CHAR__ExceptionText ),
sizeof( l__CHAR__ExceptionText ),
&l__ULONG__SystemError,
&l__CDB_FAILURE_INFORMATION,
( PUCHAR )( &cdb ),
sizeof( CDB_READ_TRACK_INFORMATION ),
( PUCHAR )( &g_TRACKINFOBLOCK[index] ),
sizeof( TRACK_INFORMATION_BLOCK ),
FALSE
);
pTrackInformation->m__BOOLEAN__IsValid = false;
// Check for correct reply
if ( l__EXCEPTION_NUMBER != EN_SUCCESS )
{
WriteExceptionLog(l__EXCEPTION_NUMBER,l__ULONG__SystemError);
return FALSE;
}
pTrackInformation->m__BOOLEAN__IsValid = true;
pTrackInformation->m__UCHAR__TrackNumber = (g_TRACKINFOBLOCK[index].ucTrackNoMSB<<8) | g_TRACKINFOBLOCK[index].ucTrackNoLSB;
pTrackInformation->m__UCHAR__SessionNumber = (g_TRACKINFOBLOCK[index].ucSessionNoMSB<<8) | g_TRACKINFOBLOCK[index].ucSessionNoLSB;
pTrackInformation->m__UCHAR__TrackMode = g_TRACKINFOBLOCK[index].TrackMode;
pTrackInformation->m__BOOLEAN__IsCopy = g_TRACKINFOBLOCK[index].Copy;
pTrackInformation->m__BOOLEAN__IsDamage = g_TRACKINFOBLOCK[index].Damage;
pTrackInformation->m__UCHAR__DataMode = g_TRACKINFOBLOCK[index].DataMode;
pTrackInformation->m__BOOLEAN__IsFixedPacket = g_TRACKINFOBLOCK[index].FP;
pTrackInformation->m__BOOLEAN__IsPacket = g_TRACKINFOBLOCK[index].Packet;
pTrackInformation->m__BOOLEAN__IsBlank = g_TRACKINFOBLOCK[index].Blank;
pTrackInformation->m__BOOLEAN__IsReserved = g_TRACKINFOBLOCK[index].RT;
pTrackInformation->m__BOOLEAN__IsNextWritableAddressValid = g_TRACKINFOBLOCK[index].NWA_V;
pTrackInformation->m__LONG__TrackStartAddress = g_TRACKINFOBLOCK[index].ucTrackStartAddressMSB<<24 |
g_TRACKINFOBLOCK[index].ucTrackStartAddressB02<<16 |
g_TRACKINFOBLOCK[index].ucTrackStartAddressB01<<8 |
g_TRACKINFOBLOCK[index].ucTrackStartAddressLSB;
pTrackInformation->m__LONG__NextWritableAddress = g_TRACKINFOBLOCK[index].ucNextWritableAddressMSB<<24 |
g_TRACKINFOBLOCK[index].ucNextWritableAddressB02<<16 |
g_TRACKINFOBLOCK[index].ucNextWritableAddressB01<<8 |
g_TRACKINFOBLOCK[index].ucNextWritableAddressLSB;
pTrackInformation->m__LONG__FreeLBs = g_TRACKINFOBLOCK[index].ucFreeBlocksMSB<<24 |
g_TRACKINFOBLOCK[index].ucFreeBlocksB02<<16 |
g_TRACKINFOBLOCK[index].ucFreeBlocksB01<<8 |
g_TRACKINFOBLOCK[index].ucFreeBlocksLSB;
pTrackInformation->m__LONG__FixedPacketSizeInLBs = g_TRACKINFOBLOCK[index].ucFixedPacketSizeMSB<<24 |
g_TRACKINFOBLOCK[index].ucFixedPacketSizeB02<<16 |
g_TRACKINFOBLOCK[index].ucFixedPacketSizeB01<<8 |
g_TRACKINFOBLOCK[index].ucFixedPacketSizeLSB;
}
else{
return FALSE;
}
if(pTrackSize)
*pTrackSize = g_TRACKINFOBLOCK[index].ucTrackSizeMSB<<24 |
g_TRACKINFOBLOCK[index].ucTrackSizeB02<<16 |
g_TRACKINFOBLOCK[index].ucTrackSizeB01<<8 |
g_TRACKINFOBLOCK[index].ucTrackSizeLSB;
return TRUE;
}