There follows technical code on how AccurateRip TOC Identifiers and CRCs are calculated, the code is presented in c++.
Generate a AccurateRip disk ident from CD TOC
(code and structures shown later)
Fetch an online track record, the records are in a standard HTTP get, url is saved in Fetch - is read from www.accuraterip.com :
Read the track CRC records (multiple CRCs from different pressings are saved in the same file, walk the list until one matches):
Calculate AccurateRip CRC from a tracks worth of audio data, AR_CRC is filled in with the track CRC:
Advanced Usage: Offset finding CRC
The offset CRC is a drive offset finding CRC, it is an AccurateRip CRC calculation 450 frames in and is calculated with one frames data. This can be used when offset adjusted for the drive to find the matching CRCs of other possible pressings, finding their offset. When the offset of a pressing is known an additional CRC can be calculated using data which is offsetted by the pressing amount (using data from the adjacent tracks). The maximum offset can be +-5 frames, otherwise the first or last track would fail to verify against the pressing.
Functions and structures
Generate a AccurateRip disk ident from CD TOC
Code:
ARTOC TOC; // filled from CD drive STAcRipDiscIdent DiscIdent; AccurateRipFillInDiscIdent (&DiscIdent, &TOC);
Fetch an online track record, the records are in a standard HTTP get, url is saved in Fetch - is read from www.accuraterip.com :
Code:
char TrackOffsetsAdded[100], Fetch[255]; wsprintf(TrackOffsetsAdded, "%08x", DiscIdent.TrackOffsetsAdded); wsprintf(Fetch, "/accuraterip/%c/%c/%c/dBAR-%03d-%08x-%08x-%08x.bin", TrackOffsetsAdded[7], TrackOffsetsAdded[6],TrackOffsetsAdded[5], DiscIdent.TrackCount, DiscIdent.TrackOffsetsAdded, DiscIdent.TrackOffsetsMultiplied,DiscIdent.FreedBIdent);
Code:
// NB DiscIdent is filled with the disc ID needed, Filename is the filename of the fetched dBAR-xxx-xxxxxxx-xxxxxxx-xxxxxxx.bin file HANDLE hFile = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { //-----find the record---- STAcRipDiscIdent AcRipDiscIdent; STAcRipATrack AcRipATrack; while (true) { DWORD Read; if (!ReadFile(hFile, &AcRipDiscIdent, sizeof(STAcRipDiscIdent), &Read, NULL) || Read != sizeof(STAcRipDiscIdent)) break; bool IsTheOne = true; if (AcRipDiscIdent.TrackCount != DiscIdent.TrackCount || AcRipDiscIdent.TrackOffsetsAdded != DiscIdent.TrackOffsetsAdded || AcRipDiscIdent.TrackOffsetsMultiplied != DiscIdent.TrackOffsetsMultiplied || AcRipDiscIdent.FreedBIdent != DiscIdent.FreedBIdent) IsTheOne = false; //---entries--- for (int i = 0; i < AcRipDiscIdent.TrackCount; i++) { if (!ReadFile(hFile, &AcRipATrack, sizeof(STAcRipATrack), &Read, NULL) || Read != sizeof(STAcRipATrack)) { IsTheOne = false; break; } //----save it?---- if (IsTheOne) { // here we have one filled AcRipATrack with a track CRC, there could be many CRCs for one track, walk the list until get a matching } } } }
Calculate AccurateRip CRC from a tracks worth of audio data, AR_CRC is filled in with the track CRC:
Code:
DWORD *pAudioData = ; // this should point entire track audio data int DataSize = ; // size of the data int TrackNumber = ; // actual track number on disc, note that for the first & last track the first and last 5 sectors are skipped int AudioTrackCount = ; // CD track count //---------AccurateRip CRC checks------------ DWORD AR_CRC = 0, AR_CRCPosMulti = 1; DWORD AR_CRCPosCheckFrom = 0; DWORD AR_CRCPosCheckTo = DataSize / sizeof(DWORD); *blooper*define SectorBytes 2352 // each sector if (TrackNumber == 1) // first? AR_CRCPosCheckFrom+= ((SectorBytes * 5) / sizeof(DWORD)); if (TrackNumber == AudioTrackCount) // last? AR_CRCPosCheckTo-=((SectorBytes * 5) / sizeof(DWORD)); int DataDWORDSize = DataSize / sizeof(DWORD); for (int i = 0; i < DataDWORDSize; i++) { if (AR_CRCPosMulti >= AR_CRCPosCheckFrom && AR_CRCPosMulti <= AR_CRCPosCheckTo) AR_CRC+=(AR_CRCPosMulti * pAudioData[i]); AR_CRCPosMulti++; }
The offset CRC is a drive offset finding CRC, it is an AccurateRip CRC calculation 450 frames in and is calculated with one frames data. This can be used when offset adjusted for the drive to find the matching CRCs of other possible pressings, finding their offset. When the offset of a pressing is known an additional CRC can be calculated using data which is offsetted by the pressing amount (using data from the adjacent tracks). The maximum offset can be +-5 frames, otherwise the first or last track would fail to verify against the pressing.
Functions and structures
Code:
//-----1 byte align----- *blooper*pragma pack (1) struct ARTOCTRACK // a Track in the CD table of contents { BYTE Reserved; BYTE Adr; BYTE TrackNumber; BYTE Reserved2; BYTE Address[4]; }; struct ARTOC // a CD Table of Contents (matches exactly with TOC taken off CD drive) { WORD TOCLength; BYTE FirstTrack; BYTE LastTrack; ARTOCTRACK Tracks[100]; }; // A disc idenfifier struct STAcRipDiscIdent { unsigned char TrackCount; DWORD TrackOffsetsAdded; DWORD TrackOffsetsMultiplied; DWORD FreedBIdent; }; // A track AccurateRip CRC struct STAcRipATrack { unsigned char Confidence; // AccurateRip Confidence: times ripped, 200 = over 200 DWORD TrackCRC; // AccurateRip Track CRC: 0 = invalid (ie never been ripped) DWORD OffsetFindCRC; // AccurateRip Track Offset finding CRC (can be ignored) }; *blooper*pragma pack () //----------------------------------------------------------------- //----------------------------------------------------------------- void MSB2DWORD( DWORD *d, BYTE *b ) { DWORD retVal; retVal = (DWORD)b[0]; retVal = (retVal<<8) + (DWORD)b[1]; retVal = (retVal<<8) + (DWORD)b[2]; retVal = (retVal<<8) + (DWORD)b[3]; *d = retVal; } //--------------------------------------------------------------- //--------------------------------------------------------------- DWORD CDDBSum( DWORD n ) { DWORD retVal = 0; while( n > 0 ) { retVal+= ( n % 10 ); n/= 10; } return retVal; } //----------------------------------------------------------------- // MSF to LBA //----------------------------------------------------------------- void MSFToLBA( BYTE *b ) { DWORD LBA = ( ( ( (DWORD)b[1] * (DWORD)60 ) + (DWORD)b[2] ) * (DWORD)75 ) + (DWORD)b[3] - (DWORD)150; BYTE *pB = (BYTE *)&LBA; b[3] = pB[0]; b[2] = pB[1]; b[1] = pB[2]; b[0] = pB[3]; } //------------------------------------------------------------------------------- //------------------------------------------------------------------------------- void LBA2MSF (BYTE *b ) { DWORD LBA; MSB2DWORD(&LBA, b); LBA+=150; int Mins = LBA / (60 * 75); LBA-=(Mins * (60 * 75)); int Sec = (LBA / 75); LBA-=(Sec * 75); int Frames = LBA; b[0] = 0; b[1] = (BYTE)Mins; b[2] = (BYTE)Sec; b[3] = (BYTE)Frames; } //------------------------------------------------------------------------------- void AccurateRipFillInDiscIdent (STAcRipDiscIdent *pRDI, ARTOC *pTOCSend) { ZeroMemory(pRDI, sizeof(STAcRipDiscIdent)); //----we dont want to touch orig TOC---- ARTOC TOC = *pTOCSend; ARTOC *pTOC = &TOC; //------create track lengths----- DWORD lstRetTrackLBA[256]; // not possible to have more than 100, but set to 256 for safety int lstRetTrackLBACount = 0; DWORD dwStart, dwLen; bool WereSomeTracks = false; int i; for( i = pTOC->FirstTrack; i <= pTOC->LastTrack; i++ ) { WereSomeTracks = true; //---only add if is audio!----- if ( (pTOC->Tracks[i - 1].Adr & 4) == 0) // not a data track? { MSB2DWORD( &dwStart, pTOC->Tracks[i - 1].Address ); MSB2DWORD( &dwLen, pTOC->Tracks[i].Address ); dwLen-= dwStart; lstRetTrackLBA[lstRetTrackLBACount] = dwStart; lstRetTrackLBACount++; } } //-------last track also------ if (WereSomeTracks) // if want addresses then give address of last END { MSB2DWORD( &dwStart, pTOC->Tracks[i - 1].Address ); lstRetTrackLBA[lstRetTrackLBACount] = dwStart; lstRetTrackLBACount++; } pRDI->TrackCount = (unsigned char)lstRetTrackLBACount; if (pRDI->TrackCount) pRDI->TrackCount--; // as are addresses the last one is the end address for (i = 0; i < lstRetTrackLBACount; i++) { DWORD LBA = lstRetTrackLBA[i]; pRDI->TrackOffsetsAdded+=LBA; if (LBA == 0) LBA++; pRDI->TrackOffsetsMultiplied+=(LBA * (i+1)); } //-----calc freedb----- int NumTracks = pTOC->LastTrack - pTOC->FirstTrack + 1; DWORD n = 0, j = 2; DWORD pID[200]; for (int fr = pTOC->FirstTrack - 1; fr <= pTOC->LastTrack; fr++) LBA2MSF(pTOC->Tracks[fr].Address); // conv to MSF for( i = pTOC->FirstTrack - 1; i < pTOC->LastTrack; i++, j++ ) { pID[j] = ((( pTOC->Tracks[i].Address[1] * 60) + pTOC->Tracks[i].Address[2]) * 75) + pTOC->Tracks[i].Address[3]; n+= CDDBSum( 60 * pTOC->Tracks[i].Address[1] + pTOC->Tracks[i].Address[2] ); } int t = ((60 * pTOC->Tracks[NumTracks].Address[1]) + pTOC->Tracks[NumTracks].Address[2]) - ((60 * pTOC->Tracks[0].Address[1]) + pTOC->Tracks[0].Address[2]); pID[0] = ((n%0xFF) << 24) | (t << 8) | NumTracks; pRDI->FreedBIdent = pID[0]; }
Comment