Files
Rocky5 921f9fe9f8 Clean Copy
If ever adding the XBMC4Xbox source to GitHub use "git add -f -A" or it wont add all the files.
2024-01-08 19:01:29 +00:00

1396 lines
40 KiB
C++

/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, 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 General Public License
* along with XBMC; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
//-----------------------------------------------------------------------------
// File: cddb.cpp
//
// Desc: Make a connection to the CDDB database
//
// Hist: 00.00.01
//
//-----------------------------------------------------------------------------
#include "system.h"
#include "utils/log.h"
#include "cddb.h"
#include "network/DNSNameCache.h"
#include "music/tags/Id3Tag.h"
#include "settings/AdvancedSettings.h"
#include "utils/URIUtils.h"
#include "FileSystem/File.h"
#include "utils/CharsetConverter.h"
using namespace std;
using namespace MUSIC_INFO;
using namespace MEDIA_DETECT;
using namespace AUTOPTR;
using namespace CDDB;
//-------------------------------------------------------------------------------------------------------------------
Xcddb::Xcddb()
: m_cddb_socket(INVALID_SOCKET)
{
m_lastError = 0;
m_cddb_ip_adress = g_advancedSettings.m_cddbAddress;
cCacheDir = "";
m_strNull = "";
}
//-------------------------------------------------------------------------------------------------------------------
Xcddb::~Xcddb()
{
closeSocket();
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::openSocket()
{
unsigned int port = CDDB_PORT;
sockaddr_in service;
service.sin_family = AF_INET;
// connect to site directly
CStdString strIpadres;
CDNSNameCache::Lookup(m_cddb_ip_adress, strIpadres);
if (strIpadres == "")
{
strIpadres = "130.179.31.49"; //"64.71.163.204";
CLog::Log(LOGERROR, "Xcddb::openSocket DNS lookup for %s failed. Trying to use %s instead", m_cddb_ip_adress.c_str(), strIpadres.c_str());
}
service.sin_addr.s_addr = inet_addr(strIpadres.c_str());
service.sin_port = htons(port);
m_cddb_socket.attach( socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
// attempt to connection
if (connect((SOCKET)m_cddb_socket, (sockaddr*) &service, sizeof(struct sockaddr)) == SOCKET_ERROR)
{
m_cddb_socket.reset();
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::closeSocket()
{
if ( m_cddb_socket.isValid() )
{
m_cddb_socket.reset();
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::Send( const void *buffer, int bytes )
{
auto_aptr<char> tmp_buffer (new char[bytes + 10]);
strcpy(tmp_buffer.get(), (const char*)buffer);
tmp_buffer.get()[bytes] = '.';
tmp_buffer.get()[bytes + 1] = 0x0d;
tmp_buffer.get()[bytes + 2] = 0x0a;
tmp_buffer.get()[bytes + 3] = 0x00;
int iErr = send((SOCKET)m_cddb_socket, (const char*)tmp_buffer.get(), bytes + 3, 0);
if (iErr <= 0)
{
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::Send( const char *buffer)
{
int iErr = Send(buffer, strlen(buffer));
if (iErr <= 0)
{
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
string Xcddb::Recv(bool wait4point)
{
char tmpbuffer[1];
char prevChar;
int counter = 0;
string str_buffer;
//##########################################################
// Read the buffer. Character by character
tmpbuffer[0]=0;
do
{
int lenRead;
prevChar=tmpbuffer[0];
lenRead = recv((SOCKET)m_cddb_socket, (char*) & tmpbuffer, 1, 0);
//Check if there was any error reading the buffer
if(lenRead == 0 || lenRead == SOCKET_ERROR || WSAGetLastError() == WSAECONNRESET)
{
CLog::Log(LOGERROR, "Xcddb::Recv Error reading buffer. lenRead = [%d] and WSAGetLastError = [%d]", lenRead, WSAGetLastError());
break;
}
//Write received data to the return string
str_buffer.push_back(tmpbuffer[0]);
counter++;
}while(wait4point ? prevChar != '\n' || tmpbuffer[0] != '.' : tmpbuffer[0] != '\n');
//##########################################################
// Write captured data information to the xbmc log file
CLog::Log(LOGDEBUG,"Xcddb::Recv Captured %d bytes // Buffer= %"PRIdS" bytes. Captured data follows on next line\n%s", counter, str_buffer.size(),(char *)str_buffer.c_str());
return str_buffer;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::queryCDinfo(CCdInfo* pInfo, int inexact_list_select)
{
if ( pInfo == NULL )
{
m_lastError = E_PARAMETER_WRONG;
return false;
}
unsigned long discid = pInfo->GetCddbDiscId();
//##########################################################
// Compose the cddb query string
CStdString read_buffer = getInexactCommand(inexact_list_select);
if (read_buffer.size() == 0)
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Size of inexaxt_list_select are 0");
m_lastError = E_PARAMETER_WRONG;
return false;
}
//##########################################################
// Read the data from cddb
Recv(false); //erstmal den Müll abholen
if ( !Send(read_buffer) )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error sending \"%s\"", read_buffer.c_str());
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select pInfo == NULL");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
CStdString recv_buffer = Recv(true);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 210: //OK, CDDB database entry follows (until terminating marker)
// Cool, I got it ;-)
writeCacheFile( recv_buffer.c_str(), discid );
parseData(recv_buffer.c_str());
break;
case 401: //Specified CDDB entry not found.
case 402: //Server error.
case 403: //Database entry is corrupt.
case 409: //No handshake.
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Quit
if ( ! Send("quit") )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error sending \"%s\"", "quit");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 0: //By some reason, also 0 is a valid value. This is not documented, and might depend on that no string was found and atoi then returns 0
case 230: //Closing connection. Goodbye.
break;
case 530: //error, closing connection.
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Close connection
if ( !closeSocket() )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo_inexaxt_list_select Error closing socket");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
//int Xcddb::queryCDinfo(int real_track_count, toc cdtoc[])
//{
// // //writeLog("Xcddb::queryCDinfo - Start");
//
// /* //writeLog("getHostByName start");
// struct hostent* hp=gethostbyname("freedb.freedb.org");
// //writeLog("hp->h_name=%s",hp->h_name);
// //writeLog("hp->h_aliases=%s",hp->h_aliases);
// //writeLog("hp->h_addr_list=%s",hp->h_addr_list);
// //writeLog("getHostByName end");
// */
// int lead_out = real_track_count;
// unsigned long discid = calc_disc_id(real_track_count, cdtoc);
// unsigned long frames[100];
//
// bool bLoaded = queryCache( discid );
//
// if ( bLoaded )
// return true;
//
// for (int i = 0;i <= lead_out;i++)
// {
// frames[i] = (cdtoc[i].min * 75 * 60) + (cdtoc[i].sec * 75) + cdtoc[i].frame;
// if (i > 0 && frames[i] < frames[i - 1])
// {
// m_lastError = E_TOC_INCORRECT;
// return false;
// }
// }
// unsigned long complete_length = frames[lead_out] / 75;
//
// // Socket öffnen
// if ( !openSocket() )
// {
// //writeLog("openSocket Failed");
// m_lastError = E_NETWORK_ERROR_OPEN_SOCKET;
// return false;
// }
//
// // Erst mal was empfangen
// string recv_buffer = Recv(false);
// /*
// 200 OK, read/write allowed
// 201 OK, read only
// 432 No connections allowed: permission denied
// 433 No connections allowed: X users allowed, Y currently active
// 434 No connections allowed: system load too high
// */
// if (recv_buffer.c_str()[0] == '2')
// {
// //OK
// // //writeLog("Connection 2 cddb: OK");
// m_lastError = IN_PROGRESS;
// }
// else if (recv_buffer.c_str()[0] == '4')
// {
// //No connections allowed
// //writeLog("Connection 2 cddb: No connections allowed");
// m_lastError = 430 + (recv_buffer.c_str()[3] - 48);
// return false;
// }
//
// // Jetzt hello Senden
// if ( ! Send("cddb hello xbox xbox xcddb 00.00.01"))
// {
// //writeLog("Send Failed");
// m_lastError = E_NETWORK_ERROR_SEND;
// return false;
// }
//
// // hello Antwort
// recv_buffer = Recv(false);
// /*
// 200 Handshake successful
// 431 Handshake not successful, closing connection
// 402 Already shook hands
// */
// if (recv_buffer.c_str()[0] == '2')
// {
// //OK
// ////writeLog("Hello 2 cddb: OK");
// m_lastError = IN_PROGRESS;
// }
// else if (recv_buffer.c_str()[0] == '4' && recv_buffer.c_str()[1] == '3')
// {
// //No connections allowed
// //writeLog("Hello 2 cddb: Handshake not successful, closing connection");
// m_lastError = E_CDDB_Handshake_not_successful;
// return false;
// }
// else if (recv_buffer.c_str()[0] == '4' && recv_buffer.c_str()[1] == '0')
// {
// // //writeLog("Hello 2 cddb: Already shook hands, but it's OK");
// m_lastError = W_CDDB_already_shook_hands;
// }
//
//
// // Hier jetzt die CD abfragen
// //##########################################################
// char query_buffer[1024];
// strcpy(query_buffer, "");
// strcat(query_buffer, "cddb query");
// {
// char tmp_buffer[256];
// sprintf(tmp_buffer, " %08x", discid);
// strcat(query_buffer, tmp_buffer);
// }
// {
// char tmp_buffer[256];
// sprintf(tmp_buffer, " %u", real_track_count);
// strcat(query_buffer, tmp_buffer);
// }
// for (int i = 0;i < lead_out;i++)
// {
// char tmp_buffer[256];
// sprintf(tmp_buffer, " %u", frames[i]);
// strcat(query_buffer, tmp_buffer);
// }
// {
// char tmp_buffer[256];
// sprintf(tmp_buffer, " %u", complete_length);
// strcat(query_buffer, tmp_buffer);
// }
//
// //cddb query
// if ( ! Send(query_buffer))
// {
// //writeLog("Send Failed");
// m_lastError = E_NETWORK_ERROR_SEND;
// return false;
// }
//
// // Antwort
// // 200 rock d012180e Soundtrack / Hackers
// char read_buffer[1024];
// recv_buffer = Recv(false);
// // Hier antwort auswerten
// /*
// 200 Found exact match
// 211 Found inexact matches, list follows (until terminating marker)
// 202 No match found
// 403 Database entry is corrupt
// 409 No handshake
// */
// char *tmp_str;
// tmp_str = (char *)recv_buffer.c_str();
// switch (tmp_str[0] - 48)
// {
// case 2:
// switch (tmp_str[1] - 48)
// {
// case 0:
// switch (tmp_str[2] - 48)
// {
// case 0: //200
// strtok(tmp_str, " ");
// strcpy(read_buffer, "");
// strcat(read_buffer, "cddb read ");
// // categ
// strcat(read_buffer, strtok(0, " "));
// {
// char tmp_buffer[256];
// sprintf(tmp_buffer, " %08x", discid);
// strcat(read_buffer, tmp_buffer);
// }
// m_lastError = IN_PROGRESS;
// break;
// case 2: //202
// m_lastError = E_NO_MATCH_FOUND;
// return false;
// break;
// default:
// m_lastError = false;
// return false;
// }
// break;
// case 1:
// switch (tmp_str[2] - 48)
// {
// case 1: //211
// m_lastError = E_INEXACT_MATCH_FOUND;
// /*
// 211 Found inexact matches, list follows (until terminating `.')
// soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
// rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
// misc de0d020f Modern Talking / Ready for the victory
// rock e00d080f Modern Talking / Album: Victory (The 11th Album)
// rock c10d150f Modern Talking / Victory (The 11th Album)
// .
// */
// addInexactList(tmp_str);
// m_lastError = E_WAIT_FOR_INPUT;
// return false;
// break;
// default:
// m_lastError = -1;
// return false;
// }
// break;
// default:
// m_lastError = -1;
// return false;
// }
// break;
// case 4:
// switch (tmp_str[2] - 48)
// {
// case 3: //403
// m_lastError = 403;
// break;
// case 9: //409
// m_lastError = 409;
// break;
// default:
// m_lastError = -1;
// return false;
// }
// break;
// default:
// m_lastError = -1;
// return false;
// }
//
//
// //##########################################################
// if ( !Send(read_buffer) )
// {
// //writeLog("Send Failed");
// //writeLog(read_buffer);
// return false;
// }
//
//
// // cddb read Antwort
// recv_buffer = Recv(true);
// /*
// 210 OK, CDDB database entry follows (until terminating marker)
// 401 Specified CDDB entry not found.
// 402 Server error.
// 403 Database entry is corrupt.
// 409 No handshake.
// */
// char *tmp_str2;
// tmp_str2 = (char *)recv_buffer.c_str();
// switch (tmp_str2[0] - 48)
// {
// case 2:
// // //writeLog("2-- XXXXXXXXXXXXXXXX");
// // Cool, I got it ;-)
// writeCacheFile( tmp_str2, discid );
// parseData(tmp_str2);
// break;
// case 4:
// // //writeLog("4-- XXXXXXXXXXXXXXXX");
// switch (tmp_str2[2] - 48)
// {
// case 1: //401
// // //writeLog("401 XXXXXXXXXXXXXXXX");
// m_lastError = 401;
// break;
// case 2: //402
// // //writeLog("402 XXXXXXXXXXXXXXXX");
// m_lastError = 402;
// break;
// case 3: //403
// // //writeLog("403 XXXXXXXXXXXXXXXX");
// m_lastError = 403;
// break;
// case 9: //409
// // //writeLog("409 XXXXXXXXXXXXXXXX");
// m_lastError = 409;
// break;
// default:
// m_lastError = -1;
// return false;
// }
// break;
// default:
// m_lastError = -1;
// return false;
// }
//
// //##########################################################
// // Abmelden 2x Senden kommt sonst zu fehler
// if ( ! Send("quit") )
// {
// //writeLog("Send Failed");
// return false;
// }
//
// // quit Antwort
// Recv(false);
//
// // Socket schliessen
// if ( !closeSocket() )
// {
// //writeLog("closeSocket Failed");
// return false;
// }
// else
// {
// // //writeLog("closeSocket OK");
// }
// m_lastError = QUERRY_OK;
// return true;
//}
//-------------------------------------------------------------------------------------------------------------------
int Xcddb::getLastError() const
{
return m_lastError;
}
//-------------------------------------------------------------------------------------------------------------------
const char *Xcddb::getLastErrorText() const
{
switch (getLastError())
{
//Can be removed if/when removing Xcddb::queryCDinfo(int real_track_count, toc cdtoc[])
// case IN_PROGRESS:
// return "in Progress";
// break;
case E_TOC_INCORRECT:
return "TOC Incorrect";
break;
case E_NETWORK_ERROR_OPEN_SOCKET:
return "Error open Socket";
break;
case E_NETWORK_ERROR_SEND:
return "Error send PDU";
break;
case E_WAIT_FOR_INPUT:
return "Wait for Input";
break;
case E_PARAMETER_WRONG:
return "Error Parameter Wrong";
break;
//Can be removed if/when removing Xcddb::queryCDinfo(int real_track_count, toc cdtoc[])
// case E_NO_MATCH_FOUND:
// return "No Match found";
// break;
// case E_INEXACT_MATCH_FOUND:
// return "Inexact Match found";
// break;
// case W_CDDB_already_shook_hands:
// return "Warning already shook hands";
// break;
// case E_CDDB_Handshake_not_successful:
// return "Error Handshake not successful";
// break;
// case QUERRY_OK:
// return "Query OK";
// break;
case 202: return "No match found";
case 210: return "Found exact matches, list follows (until terminating marker)";
case 211: return "Found inexact matches, list follows (until terminating marker)";
case 401: return "Specified CDDB entry not found";
case 402: return "Server error";
case 403: return "Database entry is corrupt";
case 408: return "CGI environment error";
case 409: return "No handshake";
case 431: return "Handshake not successful, closing connection";
case 432: return "No connections allowed: permission denied";
case 433: return "No connections allowed: X users allowed, Y currently active";
case 434: return "No connections allowed: system load too high";
case 500: return "Command syntax error, command unknown, command unimplemented";
case 501: return "Illegal protocol level";
case 530: return "error, closing connection, Server error, server timeout";
default: return "Unknown Error";
}
}
//-------------------------------------------------------------------------------------------------------------------
int Xcddb::cddb_sum(int n)
{
int ret;
/* For backward compatibility this algorithm must not change */
ret = 0;
while (n > 0)
{
ret = ret + (n % 10);
n = n / 10;
}
return (ret);
}
//-------------------------------------------------------------------------------------------------------------------
unsigned long Xcddb::calc_disc_id(int tot_trks, toc cdtoc[])
{
int i = 0, t = 0, n = 0;
while (i < tot_trks)
{
n = n + cddb_sum((cdtoc[i].min * 60) + cdtoc[i].sec);
i++;
}
t = ((cdtoc[tot_trks].min * 60) + cdtoc[tot_trks].sec) - ((cdtoc[0].min * 60) + cdtoc[0].sec);
return ((n % 0xff) << 24 | t << 8 | tot_trks);
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::addTitle(const char *buffer)
{
char value[2048];
int trk_nr = 0;
//TTITLEN
if (buffer[7] == '=')
{ //Einstellig
trk_nr = buffer[6] - 47;
strcpy(value, buffer + 8);
}
else if (buffer[8] == '=')
{ //Zweistellig
trk_nr = ((buffer[6] - 48) * 10) + buffer[7] - 47;
strcpy(value, buffer + 9);
}
else if (buffer[9] == '=')
{ //Dreistellig
trk_nr = ((buffer[6] - 48) * 100) + ((buffer[7] - 48) * 10) + buffer[8] - 47;
strcpy(value, buffer + 10);
}
else
{
return ;
}
// track artist" / "track title
char artist[1024];
char title[1024];
unsigned int len = (unsigned int)strlen(value);
bool found = false;
unsigned int index;
for (index = 0;index < len;index++)
{
if ((index + 2) <= len && value[index] == ' ' && value[index + 1] == '/' && value[index + 2] == ' ')
{
// Jep found
found = true;
break;
}
}
if (found)
{
strncpy(artist, value, index);
artist[index] = '\0';
strcpy(title, value + index + 3);
}
else
{
artist[0] = '\0';
strcpy(title, value);
}
CStdString strArtist=artist;
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(artist, strArtist);
m_mapArtists[trk_nr] += strArtist;
CStdString strTitle=title;
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(title, strTitle);
m_mapTitles[trk_nr] += strTitle;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getInexactCommand(int select) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapInexact_cddb_command_list.find(select);
if (i == m_mapInexact_cddb_command_list.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getInexactArtist(int select) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapInexact_artist_list.find(select);
if (i == m_mapInexact_artist_list.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getInexactTitle(int select) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapInexact_title_list.find(select);
if (i == m_mapInexact_title_list.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getTrackArtist(int track) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapArtists.find(track);
if (i == m_mapArtists.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getTrackTitle(int track) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapTitles.find(track);
if (i == m_mapTitles.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::getDiskTitle(CStdString& strdisk_title) const
{
strdisk_title = m_strDisk_title;
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::getDiskArtist(CStdString& strdisk_artist) const
{
strdisk_artist = m_strDisk_artist;
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::parseData(const char *buffer)
{
//writeLog("parseData Start");
char *line;
const char trenner[3] = {'\n', '\r', '\0'};
line = strtok((char*)buffer, trenner);
int line_cnt = 0;
while ((line = strtok(0, trenner)))
{
if (line[0] != '#')
{
if (0 == strncmp(line, "DTITLE", 6))
{
// DTITLE=Modern Talking / Album: Victory (The 11th Album)
unsigned int len = (unsigned int)strlen(line) - 6;
bool found = false;
unsigned int i = 5;
for (;i < len;i++)
{
if ((i + 2) <= len && line[i] == ' ' && line[i + 1] == '/' && line[i + 2] == ' ')
{
// Jep found
found = true;
break;
}
}
if (found)
{
CStdString strLine = (char*)(line + 7);
CStdString strDisk_artist = strLine.Left(i - 7);
CStdString strDisk_title = (char*)(line + i + 3);
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strDisk_artist, m_strDisk_artist);
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strDisk_title, m_strDisk_title);
}
else
{
CStdString strDisk_title = (char*)(line + 7);
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strDisk_title, m_strDisk_title);
}
}
else if (0 == strncmp(line, "DYEAR", 5))
{
CStdString strYear = (char*)(line + 5);
strYear.TrimLeft("= ");
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strYear, m_strYear);
}
else if (0 == strncmp(line, "DGENRE", 6))
{
CStdString strGenre = (char*)(line + 6);
strGenre.TrimLeft("= ");
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strGenre, m_strGenre);
}
else if (0 == strncmp(line, "TTITLE", 6))
{
addTitle(line);
}
else if (0 == strncmp(line, "EXTD", 4))
{
CStdString strExtd((char*)(line + 4));
if (m_strYear.IsEmpty())
{
// Extract Year from extended info
// as a fallback
int iPos = strExtd.Find("YEAR:");
if (iPos > -1)
{
CStdString strYear;
strYear = strExtd.Mid(iPos + 6, 4);
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strYear, m_strYear);
}
}
if (m_strGenre.IsEmpty())
{
// Extract ID3 Genre
// as a fallback
int iPos = strExtd.Find("ID3G:");
if (iPos > -1)
{
CStdString strGenre;
strGenre = strExtd.Mid(iPos + 5, 4);
strGenre.TrimLeft(' ');
if (StringUtils::IsNaturalNumber(strGenre))
{
CID3Tag tag;
m_strGenre=tag.ParseMP3Genre(strGenre);
}
}
}
}
else if (0 == strncmp(line, "EXTT", 4))
{
addExtended(line);
}
}
line_cnt++;
}
//writeLog("parseData Ende");
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::addExtended(const char *buffer)
{
char value[2048];
int trk_nr = 0;
//TTITLEN
if (buffer[5] == '=')
{ //Einstellig
trk_nr = buffer[4] - 47;
strcpy(value, buffer + 6);
}
else if (buffer[6] == '=')
{ //Zweistellig
trk_nr = ((buffer[4] - 48) * 10) + buffer[5] - 47;
strcpy(value, buffer + 7);
}
else if (buffer[7] == '=')
{ //Dreistellig
trk_nr = ((buffer[4] - 48) * 100) + ((buffer[5] - 48) * 10) + buffer[6] - 47;
strcpy(value, buffer + 8);
}
else
{
return ;
}
CStdString strValue;
CStdString strValueUtf8=value;
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(strValueUtf8, strValue);
m_mapExtended_track[trk_nr] = strValue;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getTrackExtended(int track) const
{
typedef map<int, CStdString>::const_iterator iter;
iter i = m_mapExtended_track.find(track);
if (i == m_mapExtended_track.end())
return m_strNull;
return i->second;
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::addInexactList(const char *list)
{
/*
211 Found inexact matches, list follows (until terminating `.')
soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
misc de0d020f Modern Talking / Ready for the victory
rock e00d080f Modern Talking / Album: Victory (The 11th Album)
rock c10d150f Modern Talking / Victory (The 11th Album)
.
*/
/*
m_mapInexact_cddb_command_list;
m_mapInexact_artist_list;
m_mapInexact_title_list;
*/
int start = 0;
int end = 0;
bool found = false;
int line_counter = 0;
// //writeLog("addInexactList Start");
for (unsigned int i = 0;i < strlen(list);i++)
{
if (list[i] == '\n')
{
end = i;
found = true;
}
if (found)
{
if (line_counter > 0)
{
addInexactListLine(line_counter, list + start, end - start - 1);
}
start = i + 1;
line_counter++;
found = false;
}
}
// //writeLog("addInexactList End");
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::addInexactListLine(int line_cnt, const char *line, int len)
{
// rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
int search4 = 0;
char genre[100]; // 0
char discid[10]; // 1
char artist[1024]; // 2
char title[1024];
char cddb_command[1024];
int start = 0;
// //writeLog("addInexactListLine Start");
for (int i = 0;i < len;i++)
{
switch (search4)
{
case 0:
if (line[i] == ' ')
{
strncpy(genre, line, i);
genre[i] = 0x00;
search4 = 1;
start = i + 1;
}
break;
case 1:
if (line[i] == ' ')
{
strncpy(discid, line + start, i - start);
discid[i - start] = 0x00;
search4 = 2;
start = i + 1;
}
break;
case 2:
if (i + 2 <= len && line[i] == ' ' && line[i + 1] == '/' && line[i + 2] == ' ')
{
strncpy(artist, line + start, i - start);
artist[i - start] = 0x00;
strncpy(title, line + (i + 3), len - (i + 3));
title[len - (i + 3)] = 0x00;
}
break;
}
}
sprintf(cddb_command, "cddb read %s %s", genre, discid);
m_mapInexact_cddb_command_list[line_cnt] = cddb_command;
CStdString strArtist=artist;
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(artist, strArtist);
m_mapInexact_artist_list[line_cnt] = strArtist;
CStdString strTitle=title;
// You never know if you really get UTF-8 strings from cddb
g_charsetConverter.unknownToUTF8(title, strTitle);
m_mapInexact_title_list[line_cnt] = strTitle;
// char log_string[1024];
// sprintf(log_string,"%u: %s - %s",line_cnt,artist,title);
// //writeLog(log_string);
// //writeLog("addInexactListLine End");
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::setCDDBIpAdress(const CStdString& ip_adress)
{
m_cddb_ip_adress = ip_adress;
}
//-------------------------------------------------------------------------------------------------------------------
void Xcddb::setCacheDir(const CStdString& pCacheDir )
{
cCacheDir = pCacheDir;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::queryCache( unsigned long discid )
{
if (cCacheDir.size() == 0)
return false;
XFILE::CFile file;
if (file.Open(GetCacheFile(discid)))
{
// Got a cachehit
char buffer[4096];
OutputDebugString ( "cddb local cache hit.\n" );
file.Read(buffer, 4096);
file.Close();
parseData( buffer );
return true;
}
return false;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::writeCacheFile( const char* pBuffer, unsigned long discid )
{
if (cCacheDir.size() == 0)
return false;
XFILE::CFile file;
if (file.OpenForWrite(GetCacheFile(discid), true))
{
OutputDebugString ( "Current cd saved to local cddb.\n" );
file.Write( (void*) pBuffer, strlen( pBuffer ) + 1 );
file.Close();
return true;
}
return false;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::isCDCached( int nr_of_tracks, toc cdtoc[] )
{
if (cCacheDir.size() == 0)
return false;
return XFILE::CFile::Exists(GetCacheFile(calc_disc_id(nr_of_tracks, cdtoc)));
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getYear() const
{
return m_strYear;
}
//-------------------------------------------------------------------------------------------------------------------
const CStdString& Xcddb::getGenre() const
{
return m_strGenre;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::queryCDinfo(CCdInfo* pInfo)
{
if ( pInfo == NULL )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo pInfo == NULL");
m_lastError = E_PARAMETER_WRONG;
return false;
}
int lead_out = pInfo->GetTrackCount();
int real_track_count = pInfo->GetTrackCount();
unsigned long discid = pInfo->GetCddbDiscId();
unsigned long frames[100];
//##########################################################
//
if ( queryCache(discid) )
{
CLog::Log(LOGDEBUG, "Xcddb::queryCDinfo discid [%08lx] already cached", discid);
return true;
}
//##########################################################
//
for (int i = 0;i < lead_out;i++)
{
frames[i] = pInfo->GetTrackInformation( i + 1 ).nFrames;
if (i > 0 && frames[i] < frames[i - 1])
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo E_TOC_INCORRECT");
m_lastError = E_TOC_INCORRECT;
return false;
}
}
unsigned long complete_length = pInfo->GetDiscLength();
//##########################################################
// Open socket to cddb database
if ( !openSocket() )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error opening socket");
m_lastError = E_NETWORK_ERROR_OPEN_SOCKET;
return false;
}
CStdString recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 200: //OK, read/write allowed
case 201: //OK, read only
break;
case 432: //No connections allowed: permission denied
case 433: //No connections allowed: X users allowed, Y currently active
case 434: //No connections allowed: system load too high
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Send the Hello message
if ( ! Send("cddb hello xbox xbox XboxMediaCenter pre-2.1"))
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", "cddb hello xbox xbox XboxMediaCenter pre-2.1");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 200: //Handshake successful
case 402: //Already shook hands
break;
case 431: //Handshake not successful, closing connection
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Set CDDB protocol-level to 5
if ( ! Send("proto 5"))
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", "proto 5");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 200: //CDDB protocol level: current cur_level, supported supp_level
case 201: //OK, protocol version now: cur_level
case 502: //Protocol level already cur_level
break;
case 501: //Illegal protocol level.
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Compose the cddb query string
char query_buffer[1024];
strcpy(query_buffer, "");
strcat(query_buffer, "cddb query");
{
char tmp_buffer[256];
sprintf(tmp_buffer, " %08lx", discid);
strcat(query_buffer, tmp_buffer);
}
{
char tmp_buffer[256];
sprintf(tmp_buffer, " %i", real_track_count);
strcat(query_buffer, tmp_buffer);
}
for (int i = 0;i < lead_out;i++)
{
char tmp_buffer[256];
sprintf(tmp_buffer, " %lu", frames[i]);
strcat(query_buffer, tmp_buffer);
}
{
char tmp_buffer[256];
sprintf(tmp_buffer, " %lu", complete_length);
strcat(query_buffer, tmp_buffer);
}
//##########################################################
// Query for matches
if ( ! Send(query_buffer))
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", query_buffer);
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
// 200 rock d012180e Soundtrack / Hackers
CStdString read_buffer;
recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 200: //Found exact match
strtok((char *)recv_buffer.c_str(), " ");
read_buffer.Format("cddb read %s %08x", strtok(NULL, " "), discid);
break;
case 210: //Found exact matches, list follows (until terminating marker)
case 211: //Found inexact matches, list follows (until terminating marker)
/*
soundtrack bf0cf90f Modern Talking / Victory - The 11th Album
rock c90cf90f Modern Talking / Album: Victory (The 11th Album)
misc de0d020f Modern Talking / Ready for the victory
rock e00d080f Modern Talking / Album: Victory (The 11th Album)
rock c10d150f Modern Talking / Victory (The 11th Album)
.
*/
recv_buffer += Recv(true);
addInexactList(recv_buffer.c_str());
m_lastError=E_WAIT_FOR_INPUT;
return false; //This is actually good. The calling method will handle this
case 202: //No match found
CLog::Log(LOGNOTICE, "Xcddb::queryCDinfo No match found in CDDB database when doing the query shown below:\n%s",query_buffer);
case 403: //Database entry is corrupt
case 409: //No handshake
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Read the data from cddb
if ( !Send(read_buffer) )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", read_buffer.c_str());
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
recv_buffer = Recv(true);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 210: //OK, CDDB database entry follows (until terminating marker)
// Cool, I got it ;-)
writeCacheFile( recv_buffer.c_str(), discid );
parseData(recv_buffer.c_str());
break;
case 401: //Specified CDDB entry not found.
case 402: //Server error.
case 403: //Database entry is corrupt.
case 409: //No handshake.
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Quit
if ( ! Send("quit") )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error sending \"%s\"", "quit");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
recv_buffer = Recv(false);
m_lastError = atoi(recv_buffer.c_str());
switch(m_lastError)
{
case 0: //By some reason, also 0 is a valid value. This is not documented, and might depend on that no string was found and atoi then returns 0
case 230: //Closing connection. Goodbye.
break;
case 530: //error, closing connection.
default:
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error: \"%s\"", recv_buffer.c_str());
return false;
}
//##########################################################
// Close connection
if ( !closeSocket() )
{
CLog::Log(LOGERROR, "Xcddb::queryCDinfo Error closing socket");
m_lastError = E_NETWORK_ERROR_SEND;
return false;
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
bool Xcddb::isCDCached( CCdInfo* pInfo )
{
if (cCacheDir.size() == 0)
return false;
if ( pInfo == NULL )
return false;
return XFILE::CFile::Exists(GetCacheFile(pInfo->GetCddbDiscId()));
}
CStdString Xcddb::GetCacheFile(unsigned int disc_id) const
{
CStdString strFileName;
strFileName.Format("%x.cddb", disc_id);
return URIUtils::AddFileToFolder(cCacheDir, strFileName);
}