📄 cdcache.c
字号:
/*
* cdcache.c - Copyright (C) 1999 Jay A. Key
*
* Interface to CD title/artist/track names cache, and cddb. Can optionally
* use cdplayer.ini when internet access is not available.
*
* The code to access CDPLAYER.INI was adapted from code submitted by
* Blair Thompson
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
**********************************************************************
*
* $Id: cdcache.c,v 1.2 2000/02/25 10:47:37 akey Exp $
* $Date: 2000/02/25 10:47:37 $
* $Locker: $
* $Log: cdcache.c,v $
* Revision 1.2 2000/02/25 10:47:37 akey
* sync'ed with akrip32.dll v0.94
*
* Revision 1.6 2000/02/14 09:56:25 akey
* cleaned up #ifdef _DEBUG code considerably
*
* Revision 1.5 2000/02/11 10:00:35 akey
* added access to cdplayer.ini
*
* Revision 1.4 2000/01/31 15:35:40 akey
* v0.93: added CDDBGetServerList and fixed problem with Win2000 scsi pass through code
*
* Revision 1.3 2000/01/06 16:38:35 akey
* Added missing CDDB_OPT_HTTPPORT
*
* Revision 1.2 2000/01/03 12:29:43 akey
* v0.91 release -- added CDDB and bug fixes
*
*
*/
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <winsock.h>
#include "aspilib.h"
#include "cdcache.h"
DWORD CDDBPostCmd( char *cmd, char *retBuf, int retLen );
DWORD CDDBPostCmdProxy( char *cmd, char *retBuf, int retLen );
void urlEncodeString( char *s );
void GetLineFromBuf( char **src, char *tgt, int len );
void processCDDBQuery( char *buf, LPCDDBQUERY lpq );
void SkipHTTPHeaders( char **buf );
int extractCDDBQueryInfo( LPCDDBQUERYITEM lpq, char *linebuf );
void processSites( char *buf, LPCDDBSITELIST lps );
int extractCDDBSiteInfo( LPCDDBSITE s, char *buf );
void getWord( char **inBuf, char *outBuf, int len );
DWORD genCDPlayerIniIndex( HCDROM hCD );
void MSB2DWORD( DWORD *d, BYTE *b );
DWORD getDiskInfoCDPlayerIni( LPCDDBQUERYITEM lpq, char *szCDDBEntry, int maxLen );
BOOL isCDinCDPlayerIni( char *s );
void addCDPlayerCDDBIndex( DWORD cdpIdx, DWORD cddbId, DWORD numTracks );
void writeCDPlayerIniEntry( LPCDDBQUERYITEM lpq, char *szCDDBEntry );
extern CDHANDLEREC *cdHandles;
extern HANDLE *cdMutexes;
extern HANDLE hCacheMutex;
extern CRITICAL_SECTION csCache;
extern CRITICAL_SECTION getHandle;
extern int alErrCode;
extern BYTE alAspiErr;
extern DWORD (*pfnSendASPI32Command)(LPSRB);
//static BOOL bCacheInitMutex = FALSE;
static BOOL bCacheInit = FALSE;
static char szCacheDir[MAX_PATH+1];
//static HANDLE hCacheMutex = NULL;
//static CRITICAL_SECTION csCache;
static char szCDPlayerIni[] = "cdplayer.ini";
static char szProxyAddr[256] = "";
static int iProxyPort = 0;
static char szCDDBServer[256] = "www.freedb.org";
static BOOL bUseProxy = FALSE;
static char szAgent[61] = "akrip32dll 0.91";
static char szUser[65] = "user@akrip.sourceforge.net";
static char szCGI[81] = "/~cddb/cddb.cgi";
static int iHTTPPort = 80;
static BOOL bUseCDPlayerIni = TRUE;
static DWORD dwCDDB2CDPlayer[20][3];
static int iNextIndex = -1;
DWORD CDDBSum( DWORD n )
{
DWORD retVal = 0;
while( n > 0 )
{
retVal += ( n % 10 );
n /= 10;
}
return retVal;
}
/*
* Computes the CDDBID for the CD in the drive represented by hCD.
* Data which can be used to construct a CDDB query is stored in the
* array pID.
* pID[0] = CDDBID
* pID[1] = number of tracks
* pID[2..(2+n)] = starting MSF offset of tracks on CD
* pID will need to have (at least) n+3 entries, where n is the number
* of tracks on the CD. 102 should always be enough. Note that the lead-out
* track is included.
*/
DWORD GetCDDBDiskID( HCDROM hCD, DWORD *pID, int numEntries )
{
TOC toc;
TOCTRACK *t1, *t2;
int idx = (int)hCD - 1;
DWORD t;
DWORD n;
int i;
int j;
BOOL bMSF;
*pID = 0;
if ( (idx<0) || (idx>=MAXCDHAND) || !cdHandles[idx].used )
{
alErrCode = ALERR_INVHANDLE;
return SS_ERR;
}
if ( WaitForSingleObject( cdMutexes[idx], TIMEOUT ) != WAIT_OBJECT_0 )
{
alErrCode = ALERR_LOCK;
return SS_ERR;
}
bMSF = cdHandles[idx].bMSF;
cdHandles[idx].bMSF = TRUE;
memset( &toc, 0, sizeof(toc) );
ReadTOC( hCD, &toc );
n = t = 0;
for( j = 2, i = toc.firstTrack - 1; i < toc.lastTrack; i++, j++ )
{
t1 = &(toc.tracks[i]);
pID[j] = (((t1->addr[1]*60)+t1->addr[2])*75)+t1->addr[3];
n += CDDBSum( 60 * toc.tracks[i].addr[1] + toc.tracks[i].addr[2] );
}
t2 = &(toc.tracks[toc.lastTrack-toc.firstTrack+1]);
t = 60 * t2->addr[1] + t2->addr[2];
t2 = &(toc.tracks[0]);
t -= ( 60 * t2->addr[1] + t2->addr[2] );
pID[toc.lastTrack+2] = t;
#if 0
// fudge the total time to generate inexact matches for testing
t += 2;
#endif
pID[1] = toc.lastTrack - toc.firstTrack + 1;
*pID = ((n%0xFF) << 24) | (t << 8) | (toc.lastTrack - toc.firstTrack + 1);
cdHandles[idx].bMSF = bMSF;
n = genCDPlayerIniIndex( hCD );
addCDPlayerCDDBIndex( n, pID[0], pID[1] );
ReleaseMutex( cdMutexes[idx] );
return SS_COMP;
}
BOOL InitCache( LPCTSTR lpszDir )
{
if ( bCacheInit )
return FALSE;
if ( !lpszDir )
lpszDir = "";
strncpy( szCacheDir, lpszDir, MAX_PATH+1 );
bCacheInit = TRUE;
return TRUE;
}
/*
* Sends an HTTP POST command and waits for a response. The response is
* copied to retBuf. Uses a proxy. Returns the number of bytes copied to
* retBuf. Before returning, it should strip the HTTP header from the info
* if present.
*/
DWORD CDDBPostCmdProxy( char *cmd, char *retBuf, int retLen )
{
WSADATA wsaData;
SOCKET s;
struct hostent *h;
struct sockaddr_in sin;
int i;
char *postcmd, *p;
DWORD retVal = 0;
p = retBuf;
ZeroMemory( p, retLen );
retLen--; // reserve room for the terminating NULL
WSAStartup( MAKEWORD(1,1), &wsaData );
s = socket( AF_INET, SOCK_STREAM, 0 );
h = gethostbyname( szProxyAddr );
if ( !h )
{
#ifdef _DEBUG
dbprintf( "akrip32: CDDBPostCmdProxy: unable to resolve hostname" );
#endif
return 0;
}
sin.sin_family = AF_INET;
sin.sin_port = htons( iProxyPort );
memcpy( &sin.sin_addr, h->h_addr, h->h_length );
i = connect( s, (struct sockaddr *)&sin, sizeof(sin) );
p = postcmd = (char *)GlobalAlloc( GPTR, lstrlen(cmd) + 512 );
wsprintf( postcmd, "POST http://%s%s HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: AKRip090\r\n", szCDDBServer, szCGI );
p += lstrlen( postcmd );
wsprintf( p, "Content-Length: %d\r\n\r\n", lstrlen( cmd ) + 2 );
strcat( p, cmd );
strcat( p, "\r\n" );
#if 0
i = 120;
i = setsockopt( s, IPPROTO_TCP, SO_SNDTIMEO, &i, sizeof(int) );
if ( i == SOCKET_ERROR )
dbprintf( "Error setting rcv timeout on socket" );
else
dbprintf( "Receive socket timeout set" );
#endif
send( s, postcmd, lstrlen( postcmd ), 0 );
p = retBuf;
ZeroMemory( p, retLen );
i = -1;
while( (i != 0) && (retLen >= 128) )
{
i = recv( s, p, 128, 0 );
p += i;
retLen -= i;
retVal += (DWORD)i;
if ( i < 0 )
i = 0;
}
closesocket( s );
GlobalFree( (HGLOBAL)postcmd );
WSACleanup();
return retVal;
}
/*
* Sends an HTTP POST command and waits for a response. The response is
* copied to retBuf. Returns the number of bytes copied to retBuf.
*/
DWORD CDDBPostCmd( char *cmd, char *retBuf, int retLen )
{
WSADATA wsaData;
SOCKET s;
struct hostent *h;
struct sockaddr_in sin;
int i;
char *postcmd, *p;
DWORD retVal = 0;
p = retBuf;
ZeroMemory( p, retLen );
retLen--; // reserve room for the terminating NULL
WSAStartup( MAKEWORD(1,1), &wsaData );
s = socket( AF_INET, SOCK_STREAM, 0 );
h = gethostbyname( szCDDBServer );
if ( !h )
return 0;
sin.sin_family = AF_INET;
sin.sin_port = htons( iHTTPPort );
memcpy( &sin.sin_addr, h->h_addr, h->h_length );
i = connect( s, (struct sockaddr *)&sin, sizeof(sin) );
p = postcmd = (char *)GlobalAlloc( GPTR, lstrlen(cmd) + 512 );
wsprintf( p, "GET %s?", szCGI );
strcat( p, cmd );
strcat( p, "\r\n" );
send( s, postcmd, lstrlen( postcmd ), 0 );
p = retBuf;
ZeroMemory( p, retLen );
i = -1;
while( (i != 0) && (retLen >= 128) )
{
i = recv( s, p, 128, 0 );
p += i;
retLen -= i;
retVal += (DWORD)i;
if ( i < 0 )
i = 0;
}
closesocket( s );
GlobalFree( (HGLOBAL)postcmd );
WSACleanup();
return retVal;
}
/*
* Queries the CDDB for a given disk.
* Returns SS_COMP on success, SS_ERR on error. numEntries on entry contains
* the number of elements in the lpq array, and on return is set to the
* number of entries returned.
*
* If the no items are returned, or if no network connection is available,
* returns one item with category "cdplayerini" and the index stored in
* cddbId;
*/
DWORD CDDBQuery( HCDROM hCD, LPCDDBQUERY lpq )
{
//int numRead = 0;
LPDWORD pdwId;
char *cmd, *p, *retBuf;
int i;
if ( !lpq )
{
return SS_ERR;
}
pdwId = GlobalAlloc( GPTR, 103 * sizeof(DWORD) );
cmd = GlobalAlloc( GPTR, 1024 );
retBuf = GlobalAlloc( GPTR, 2048 );
if ( !cmd || !pdwId || !retBuf )
{
if ( cmd ) GlobalFree( (HGLOBAL)cmd );
if ( pdwId ) GlobalFree( (HGLOBAL)pdwId );
if ( retBuf ) GlobalFree( (HGLOBAL)retBuf );
lpq->num = 0;
return SS_ERR;
}
// Generate the cddb ID for the disc
if ( GetCDDBDiskID( hCD, pdwId, 102 ) == SS_COMP )
{
// Generate a query string
p = cmd;
wsprintf( p, "cmd=cddb+query+%08x+%d+", pdwId[0], pdwId[1] );
p += lstrlen( p );
for( i = 0; i < pdwId[1]; i++ )
{
wsprintf( p, "%d+", pdwId[i+2] );
p += lstrlen(p);
}
wsprintf( p, "%d&hello=%s+%s&proto=3",
pdwId[pdwId[1]+2], szUser, szAgent );
urlEncodeString( p );
// send the query string
if ( bUseProxy )
CDDBPostCmdProxy( cmd, retBuf, 2048 );
else
CDDBPostCmd( cmd, retBuf, 2048 );
// process the returned data (if any)
processCDDBQuery( retBuf, lpq );
// fall back to cdplayer.ini if no items matched
if ( (lpq->num == 0) && bUseCDPlayerIni )
{
pdwId[0] = genCDPlayerIniIndex( hCD );
wsprintf( retBuf, "%X", pdwId[0] );
if ( isCDinCDPlayerIni( retBuf ) )
{
wsprintf( lpq->q[0].cddbId, "%X", pdwId[0] );
lstrcpy( lpq->q[0].categ, "cdplayerini" );
lpq->q[0].bExact = TRUE;
lpq->num = 1;
}
}
// else if we have multiple/partial matches from CDDB, we need to
// associate all of the cddbId values with the cdPlayer.ini index
else if ( (lpq->num > 1) && bUseCDPlayerIni )
{
pdwId[2] = genCDPlayerIniIndex( hCD );
for( i = 0; i < lpq->num; i++ )
{
pdwId[0] = (DWORD)strtoul( lpq->q[i].cddbId, NULL, 16 );
addCDPlayerCDDBIndex( pdwId[2], pdwId[0], pdwId[1] );
}
}
}
GlobalFree( (HGLOBAL)pdwId );
GlobalFree( (HGLOBAL)cmd );
GlobalFree( (HGLOBAL)retBuf );
return SS_COMP;
}
void CDDBSetOption( int what, char *szVal, int iVal )
{
switch( what )
{
case CDDB_OPT_PROXY:
if ( szVal )
lstrcpyn( szProxyAddr, szVal, 255 );
szProxyAddr[255] = 0;
break;
case CDDB_OPT_SERVER:
if ( szVal )
lstrcpyn( szCDDBServer, szVal, 255 );
szCDDBServer[255] = 0;
break;
case CDDB_OPT_CGI:
if ( szVal )
lstrcpyn( szCGI, szVal, 80 );
szCGI[80] = 0;
break;
case CDDB_OPT_PROXYPORT:
iProxyPort = iVal;
break;
case CDDB_OPT_AGENT:
if ( szVal )
lstrcpyn( szAgent, szVal, 60 );
szAgent[60] = 0;
urlEncodeString( szAgent );
break;
case CDDB_OPT_USER:
if ( szVal )
lstrcpyn( szUser, szVal, 64 );
szUser[64] = 0;
urlEncodeString( szUser );
break;
case CDDB_OPT_USEPROXY:
bUseProxy = (BOOL)iVal;
break;
case CDDB_OPT_HTTPPORT:
iHTTPPort = iVal;
break;
case CDDB_OPT_USECDPLAYERINI:
bUseCDPlayerIni = (BOOL)iVal;
break;
}
}
void urlEncodeString( char *s )
{
if ( !s || !*s )
return;
while( *++s )
{
if ( (*s == '@') || (*s == ' ') )
*s = '+';
}
}
/*
* Process the return buffer from a cddb query. Verify that the return code
* is one that we expect (200, 211, 202, 403, 409).
*/
void processCDDBQuery( char *buf, LPCDDBQUERY lpq )
{
int total = 0;
int iRetCode, i;
int maxLines = 100;
char retCode[4] = "100";
char linebuf[81];
char *p = buf;
FILE *fp;
fp = fopen( "retbuf.txt", "wb" );
fwrite( p, 1, lstrlen( p ), fp );
SkipHTTPHeaders( &p );
GetLineFromBuf( &p, linebuf, 81 );
strncpy( retCode, linebuf, 3 );
iRetCode = atoi( retCode );
switch( iRetCode )
{
case 200:
// one exact match
if ( extractCDDBQueryInfo( &lpq->q[0], linebuf+4 ) )
{
lpq->q[0].bExact = TRUE;
total++;
}
break;
case 211:
// inexact match(es)
i = 0;
while ( p && lpq->num && (maxLines-- > 0) )
{
GetLineFromBuf( &p, linebuf, 81 );
if ( !strcmp( linebuf, "." ) )
break;
if ( extractCDDBQueryInfo( &lpq->q[i], linebuf ) )
{
lpq->q[i].bExact = FALSE;
total++;
i++;
lpq->num--;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -