winamp/Src/Plugins/Input/in_mp3/giofile.cpp
2024-09-24 14:54:57 +02:00

2475 lines
68 KiB
C++

/***************************************************************************\
*
* (C) copyright Fraunhofer - IIS (1998)
* All Rights Reserved
*
* filename: giofile.cpp
* project : MPEG Decoder
* author : Martin Sieler
* date : 1998-02-11
* contents/description: file I/O class for MPEG Decoder
*
*
\***************************************************************************/
/* ------------------------ includes --------------------------------------*/
#include "main.h"
#include "api__in_mp3.h"
#include <time.h>
#include <locale.h>
#include "../Winamp/wa_ipc.h"
#include "LAMEinfo.h"
#include "OFL.h"
#include "../nu/AutoChar.h"
#include "../nu/AutoWide.h"
#include <api/service/waservicefactory.h>
#include <shlwapi.h>
#include "MP3Info.h"
#include "config.h"
#include <math.h>
#include "id3.h"
#include "../apev2/header.h"
#include "uvox_3902.h"
#include <foundation/error.h>
#include <strsafe.h>
#define MAX_REDIRECTS 10
#define SAFE_MALLOC_MATH(orig, x) (((orig)<x)?x:orig)
// seems like a reasonable limit, but we should check with tag first
#define UVOX_MAXMSG_CAP 1048576
static jnl_connection_t CreateConnection(const char *url, jnl_dns_t dns, size_t sendbufsize, size_t recvbufsize)
{
if (!_strnicmp(url, "https:", strlen("https:")))
return jnl_sslconnection_create(dns, sendbufsize, recvbufsize);
else
return jnl_connection_create(dns, sendbufsize, recvbufsize);
}
static int64_t Seek64(HANDLE hf, int64_t distance, DWORD MoveMethod)
{
LARGE_INTEGER li;
li.QuadPart = distance;
li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod);
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
{
li.QuadPart = -1;
}
return li.QuadPart;
}
HWND GetDialogBoxParent()
{
HWND parent = (HWND)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT);
if (!parent || parent == (HWND)1)
return mod.hMainWindow;
return parent;
}
/*-------------------------- defines --------------------------------------*/
/*-------------------------------------------------------------------------*/
//-------------------------------------------------------------------------*
//
// CGioFile
//
//-------------------------------------------------------------------------*
//-------------------------------------------------------------------------*
// constructor
//-------------------------------------------------------------------------*
static char ultravoxUserAgent[128] = "";
char *GetUltravoxUserAgent()
{
if (!ultravoxUserAgent[0])
{
StringCchPrintfA(ultravoxUserAgent, 128, "User-Agent: WinampMPEG/%01x.%02x, Ultravox/2.1\r\n"
"Ultravox-transport-type: TCP\r\n",
WINAMP_VERSION_MAJOR(winampVersion),
WINAMP_VERSION_MINOR(winampVersion));
}
return ultravoxUserAgent;
}
static char userAgent[128] = "";
char *GetUserAgent()
{
if (!userAgent[0])
{
StringCchPrintfA(userAgent, 128, "User-Agent: WinampMPEG/%01x.%02x\r\n",
WINAMP_VERSION_MAJOR(winampVersion),
WINAMP_VERSION_MINOR(winampVersion));
}
return userAgent;
}
extern int lastfn_status_err;
CGioFile::CGioFile()
{
req=0;
proxy_host=0;
host=0;
request=0;
proxy_lp=0;
lpinfo=0;
mpeg_length=0;
file_position=0;
mpeg_position=0;
no_peek_hack=false;
encodingMethod=0;
uvox_3901=0;
uvox_3902=0;
stream_url[0]=0;
stream_genre[0]=0;
stream_current_title[0]=0;
stream_name[0]=0;
ZeroMemory(&last_write_time, sizeof(last_write_time));
ZeroMemory(id3v1_data, sizeof(id3v1_data));
lengthVerified=false;
m_vbr_bytes = 0;
uvox_last_message = 0;
prepad = 0;
postpad = 0;
m_vbr_ms = 0;
m_vbr_hdr = 0;
stream_id3v2_buf = 0;
lyrics3_size=0;
lyrics3_data=0;
apev2_data=0;
m_content_type = 0;
ZeroMemory(uvox_meta, sizeof(uvox_meta));
is_uvox = 0;
uvox_sid = uvox_maxbr = uvox_avgbr = 0;
uvox_message_cnt = uvox_desyncs = 0;
uvox_stream_data = 0;
uvox_stream_data_len = 0;
ZeroMemory(&uvox_artwork, sizeof(uvox_artwork));
uvox_maxmsg = 65535;
force_lpinfo[0] = 0;
is_stream_seek = 0;
last_full_url[0] = 0;
m_is_stream = 0;
m_redircnt = 0;
m_auth_tries = 0;
m_full_buffer = NULL;
fEof = false;
m_connection = NULL;
m_dns = NULL;
hFile = INVALID_HANDLE_VALUE;
m_http_response = 0;
m_seek_reset = false;
m_is_stream_seek = false;
m_is_stream_seekable = false;
initTitleList();
m_vbr_frames = 0;
ZeroMemory(&m_vbr_toc, sizeof(m_vbr_toc));
m_vbr_frame_len = 0;
m_vbr_flag = 0;
m_id3v2_len = 0;
m_id3v1_len = 0;
port = 80;
constate = 0;
ZeroMemory(&save_filename, sizeof(save_filename));
ZeroMemory(&server_name, sizeof(server_name));
recvbuffersize = 32768;
timeout_start = GetTickCount();
meta_interval = 0;
meta_pos = 0;
ZeroMemory(&stream_title_save, sizeof(stream_title_save));
ZeroMemory(&last_title_sent, sizeof(last_title_sent));
ZeroMemory(&dlg_realm, sizeof(dlg_realm));
m_useaproxy = 0;
}
//-------------------------------------------------------------------------*
// destructor
//-------------------------------------------------------------------------*
CGioFile::~CGioFile()
{
int x, y;
for (x = 0; x < 2; x ++)
for (y = 0; y < 32; y ++)
free(uvox_meta[x][y]);
free(m_content_type);
free(uvox_stream_data);
free(stream_id3v2_buf);
free(lyrics3_data);
free(apev2_data);
free(uvox_3901);
free(uvox_3902);
Close();
delete m_vbr_hdr;
m_vbr_hdr = 0;
clearTitleList();
free(lpinfo);
free(proxy_lp);
free(request);
free(host);
free(req);
free(proxy_host);
}
static bool GetRG(const char *key, ID3v2 &info, APEv2::Tag &apev2, wchar_t *val, int len)
{
val[0]=0;
if (info.GetString(key, val, len) == 1 && val[0])
return true;
if (apev2.GetString(key, val, len) == APEv2::APEV2_SUCCESS && val[0])
return true;
return false;
}
float CGioFile::GetGain()
{
if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
{
// if (info.HasData())
{
float dB = 0, peak = 1.0f;
wchar_t gain[128] = L"", peakVal[128] = L"";
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
{
case 0: // track
if ((GetRG("replaygain_track_gain", info, apev2, gain, 128) == false || !gain[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetRG("replaygain_album_gain", info, apev2, gain, 128);
if ((GetRG("replaygain_track_peak", info, apev2, peakVal, 128) == false || !peakVal[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetRG("replaygain_album_peak", info, apev2, peakVal, 128);
break;
case 1:
if ((GetRG("replaygain_album_gain", info, apev2, gain, 128) == false || !gain[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetRG("replaygain_track_gain", info, apev2, gain, 128);
if ((GetRG("replaygain_album_peak", info, apev2, peakVal, 128) == false || !peakVal[0])
&& !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
GetRG("replaygain_track_peak", info, apev2, peakVal, 128);
break;
}
if (gain[0])
{
if (gain[0] == L'+')
dB = (float)_wtof_l(&gain[1], C_locale);
else
dB = (float)_wtof_l(gain, C_locale);
}
else
{
dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
return (float)pow(10.0f, dB / 20.0f);
}
if (peakVal[0])
{
peak = (float)_wtof_l(peakVal, C_locale);
}
switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
{
case 0: // apply gain
return (float)pow(10.0f, dB / 20.0f);
case 1: // apply gain, but don't clip
return min((float)pow(10.0f, dB / 20.0f), 1.0f / peak);
case 2: // normalize
return 1.0f / peak;
case 3: // prevent clipping
if (peak > 1.0f)
return 1.0f / peak;
else
return 1.0f;
}
}
/* else
{
float dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
return pow(10.0f, dB / 20.0f);
}*/
}
return 1.0f; // no gain
}
//-------------------------------------------------------------------------*
// Open
//-------------------------------------------------------------------------*
static char *jnl_strndup(const char *str, size_t n)
{
char *o = (char *)calloc(n+1, sizeof(char));
if (!o)
return 0;
strncpy(o, str, n);
o[n]=0;
return o;
}
static int parse_url(const char *url, char **prot, char **host, unsigned short *port, char **req, char **lp)
{
free(*prot); *prot=0;
free(*host); *host = 0;
free(*req); *req = 0;
free(*lp); *lp = 0;
*port = 0;
const char *p;
const char *protocol = strstr(url, "://");
if (protocol)
{
*prot = jnl_strndup(url, protocol-url);
p = protocol + 3;
}
else
{
p = url;
}
while (p && *p == '/') p++; // skip extra /
size_t end = strcspn(p, "@/");
// check for username
if (p[end] == '@')
{
*lp = jnl_strndup(p, end);
p = p+end+1;
end = strcspn(p, "[:/");
}
if (p[0] == '[') // IPv6 style address
{
p++;
const char *ipv6_end = strchr(p, ']');
if (!ipv6_end)
return 1;
*host = jnl_strndup(p, ipv6_end-p);
p = ipv6_end+1;
}
else
{
end = strcspn(p, ":/");
*host = jnl_strndup(p, end);
p += end;
}
// is there a port number?
if (p[0] == ':')
{
char *new_end;
*port = (unsigned short)strtoul(p+1, &new_end, 10);
p = new_end;
}
if (p[0])
{
*req = _strdup(p);
}
return 0;
}
static void parseURL(const char *url, char **host, unsigned short *port, char **req, char **lp)
{
char *prot=0;
parse_url(url, &prot, host, port, req, lp);
if (!*port)
{
if (prot)
{
if (!stricmp(prot, "https"))
*port = 443;
else
*port = 80;
}
else
*port=80;
}
free(prot);
if (!*req)
*req = _strdup("/");
}
static void encodeMimeStr(char *in, char *out)
{
char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int shift = 0;
int accum = 0;
while (in && *in)
{
if (*in)
{
accum <<= 8;
shift += 8;
accum |= *in++;
}
while (shift >= 6)
{
shift -= 6;
*out++ = alphabet[(accum >> shift) & 0x3F];
}
}
if (shift == 4)
{
*out++ = alphabet[(accum & 0xF) << 2];
*out++ = '=';
}
else if (shift == 2)
{
*out++ = alphabet[(accum & 0x3) << 4];
*out++ = '=';
*out++ = '=';
}
*out++ = 0;
}
int CGioFile::doConnect(const char *str, int start_offset)
{
char *http_ver_str = " HTTP/1.0\r\n";
unsigned short proxy_port = 80;
char str2[1024]={0};
if (!str)
str = last_full_url;
else
lstrcpynA( last_full_url, str, sizeof( last_full_url ) );
if (start_offset > 0)
{
http_ver_str = " HTTP/1.1\r\n";
}
lstrcpynA(str2, str, 1024);
is_stream_seek = start_offset || !str;
lstrcpynA(g_stream_title, str, 256);
meta_interval = meta_pos = 0;
server_name[0] = 0;
last_title_sent[0] = 0;
stream_bytes_read = start_offset;
stream_metabytes_read = 0;
m_is_stream = 1;
constate = 0;
parseURL(str2, &host, &port, &request, &lpinfo);
if (port == 80 || !GetPrivateProfileIntA("Winamp", "proxyonly80", 0, INI_FILE))
{
const char *p;
const char *winamp_proxy = (const char *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_PROXY_STRING);
if (!winamp_proxy || winamp_proxy == (char *)1)
{
char temp[256] = {0};
GetPrivateProfileStringA("Winamp", "proxy", "", temp, sizeof(temp), INI_FILE);
p = temp;
}
else
p = winamp_proxy;
while (p && (*p == ' ' || *p == '\t')) p++;
char config_proxy[512] = "http://";
StringCchCatA(config_proxy, 512, p);
parseURL(config_proxy, &proxy_host, &proxy_port, &req, &proxy_lp);
}
if (!host || !host[0])
return 1;
char *p = request + strlen(request);
while (p >= request && *p != '/') p--;
if (p[1])
lstrcpynA(g_stream_title, ++p, 256);
lstrcpynA(stream_title_save, g_stream_title, 580);
lstrcpynA(stream_name, g_stream_title, 256);
g_stream_title[255] = 0;
fEof = false;
timeout_start = GetTickCount();
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_CONNECTING,lastfn_status,256);
LeaveCriticalSection(&g_lfnscs);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
if (force_lpinfo[0])
{
free(lpinfo);
lpinfo = _strdup(force_lpinfo);
}
if (!m_dns) jnl_dns_create(&m_dns);
m_connection = CreateConnection(str, m_dns, 16384, recvbuffersize = max(32768, config_http_buffersize * 1024));
if (!m_connection)
return 1;
if (!proxy_host || !proxy_host[0])
{
jnl_connection_connect(m_connection, host, port);
jnl_connection_send_string(m_connection, "GET ");
jnl_connection_send_string(m_connection, request);
jnl_connection_send_string(m_connection, http_ver_str);
}
else
{
char s[32]={0};
jnl_connection_connect(m_connection, proxy_host, proxy_port);
jnl_connection_send_string(m_connection, "GET http://");
if (lpinfo && lpinfo[0])
{
jnl_connection_send_string(m_connection, lpinfo);
jnl_connection_send_string(m_connection, "@");
}
jnl_connection_send_string(m_connection, host);
StringCchPrintfA(s, 32, ":%d", port);
jnl_connection_send_string(m_connection, s);
jnl_connection_send_string(m_connection, request);
jnl_connection_send_string(m_connection, http_ver_str);
if (proxy_lp && proxy_lp[0])
{
char temp[1024]={0};
jnl_connection_send_string(m_connection, "Proxy-Authorization: Basic ");
encodeMimeStr(proxy_lp, temp);
jnl_connection_send_string(m_connection, temp);
jnl_connection_send_string(m_connection, "\r\n");
}
}
jnl_connection_send_string(m_connection, "Host: ");
jnl_connection_send_string(m_connection, host);
jnl_connection_send_string(m_connection, "\r\n");
if (!start_offset)
{
jnl_connection_send_string(m_connection, GetUltravoxUserAgent());
}
else
jnl_connection_send_string(m_connection, GetUserAgent());
jnl_connection_send_string(m_connection, "Accept: */*\r\n");
if (allow_sctitles && !start_offset) jnl_connection_send_string(m_connection, "Icy-MetaData:1\r\n");
if (lpinfo && lpinfo[0])
{
char str[512] = {0};
encodeMimeStr(lpinfo, str);
jnl_connection_send_string(m_connection, "Authorization: Basic ");
jnl_connection_send_string(m_connection, str);
jnl_connection_send_string(m_connection, "\r\n");
}
if (start_offset > 0)
{
char str[512] = {0};
StringCchPrintfA(str, 512, "Range: bytes=%d-\r\n", start_offset);
jnl_connection_send_string(m_connection, str);
}
jnl_connection_send_string(m_connection, "Connection: close\r\n");
jnl_connection_send_string(m_connection, "\r\n");
return 0;
}
int CGioFile::Open(const wchar_t *pszName, int maxbufsizek)
{
peekBuffer.reserve(16384);
m_vbr_flag = 0;
m_vbr_frames = 0;
m_vbr_samples = 0;
m_vbr_ms = 0;
m_vbr_frame_len = 0;
m_id3v2_len = 0;
m_id3v1_len = 0;
m_apev2_len = 0;
lyrics3_size = 0;
m_is_stream = 0;
mpeg_length = 0;
mpeg_position = 0;
file_position = 0;
if ( !_wcsnicmp( pszName, L"file://", 7 ) )
pszName += 7;
if ( PathIsURL( pszName ) )
{
wchar_t str[ 8192 ] = { 0 };
wchar_t *p;
hFile = INVALID_HANDLE_VALUE;
lstrcpyn( str, pszName, 8192 );
save_filename[ 0 ] = 0;
if ( p = wcsstr( str, L">save.file:" ) )
{
*p = 0;
p += 11;
if ( !wcsstr( p, L".." ) && !wcsstr( p, L"\\" ) && !wcsstr( p, L"/" ) )
{
lstrcpynA( save_filename, AutoChar( p ), 256 );
}
}
if ( doConnect( AutoChar( str ), 0 ) )
return NErr_ConnectionFailed;
}
else
{
hFile = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
GetFileTime( hFile, 0, 0, &last_write_time );
mpeg_length = Seek64( hFile, 0, FILE_END );
uint64_t startOffset = 0;
unsigned char buf[ 1448 ] = { 0 };
DWORD len = 0;
while ( 1 ) // read all tags (sometimes programs get stupid and make multiple tags)
{
len = 0;
Seek64( hFile, startOffset, FILE_BEGIN );
ReadFile( hFile, buf, 10, &len, NULL );
if ( len >= 10 &&
buf[ 0 ] == 'I' &&
buf[ 1 ] == 'D' &&
buf[ 2 ] == '3' &&
buf[ 3 ] != 255 &&
buf[ 4 ] != 255 &&
buf[ 6 ] < 0x80 &&
buf[ 7 ] < 0x80 &&
buf[ 8 ] < 0x80 &&
buf[ 9 ] < 0x80 )
{
DWORD thisLen = 10;
if ( buf[ 5 ] & 0x10 ) // check for footer flag
thisLen += 10;
thisLen += ( (int)buf[ 6 ] ) << 21;
thisLen += ( (int)buf[ 7 ] ) << 14;
thisLen += ( (int)buf[ 8 ] ) << 7;
thisLen += ( (int)buf[ 9 ] );
SetFilePointer( hFile, -10, NULL, FILE_CURRENT );
if ( stream_id3v2_buf ) // already read a tag?
{
startOffset += thisLen;
m_id3v2_len += thisLen;
mpeg_length -= thisLen;
mpeg_position += thisLen;
SetFilePointer( hFile, thisLen, NULL, FILE_CURRENT );
continue;
}
stream_id3v2_buf = (char *)malloc( thisLen );
if ( stream_id3v2_buf )
{
memcpy( stream_id3v2_buf, buf, 10 );
DWORD dummy = 0;
if ( !ReadFile( hFile, stream_id3v2_buf, thisLen, &dummy, 0 ) || dummy < thisLen )
{
free( stream_id3v2_buf );
stream_id3v2_buf = NULL;
thisLen = 0;
SetFilePointer( hFile, 0, NULL, FILE_END );
break;
}
else
{
mpeg_position += thisLen;
stream_id3v2_read = thisLen;
mpeg_length -= thisLen;
startOffset += thisLen;
info.Decode( stream_id3v2_buf, thisLen );
}
}
else
{
/* memory allocation failed, let's assume the ID3v2 tag was valid ... */
mpeg_position += thisLen;
stream_id3v2_read = thisLen;
mpeg_length -= thisLen;
startOffset += thisLen;
}
m_id3v2_len += thisLen;
}
else
{
// benski> unnecessary because we call SetFilePointer immediately after the loop:
// CUT: SetFilePointer(hFile, -10, NULL, FILE_CURRENT);
break;
}
}
/* Read ID3v1 Tag */
if ( mpeg_length >= 128 )
{
SetFilePointer( hFile, -128, NULL, FILE_END );
len = 0;
if ( ReadFile( hFile, id3v1_data, 128, &len, NULL ) && len == 128 )
{
if ( id3v1_data[ 0 ] == 'T' && id3v1_data[ 1 ] == 'A' && id3v1_data[ 2 ] == 'G' )
{
m_id3v1_len = 128;
mpeg_length -= m_id3v1_len;
}
}
}
/* read appended ID3v2.4 tag */
if ( mpeg_length >= 10 && Seek64( hFile, mpeg_position + mpeg_length - 10, FILE_BEGIN ) != -1 )
{
len = 0;
ReadFile( hFile, buf, 10, &len, NULL );
if ( len >= 10 &&
buf[ 0 ] == '3' &&
buf[ 1 ] == 'D' &&
buf[ 2 ] == 'I' &&
buf[ 3 ] != 255 &&
buf[ 4 ] != 255 &&
buf[ 6 ] < 0x80 &&
buf[ 7 ] < 0x80 &&
buf[ 8 ] < 0x80 &&
buf[ 9 ] < 0x80 )
{
DWORD thisLen = 10;
if ( buf[ 5 ] & 0x10 ) // check for header flag
thisLen += 10;
thisLen += ( (int)buf[ 6 ] ) << 21;
thisLen += ( (int)buf[ 7 ] ) << 14;
thisLen += ( (int)buf[ 8 ] ) << 7;
thisLen += ( (int)buf[ 9 ] );
mpeg_length -= thisLen;
}
}
/* Read Lyrics3 Tag */
if ( mpeg_length >= 15 )
{
free( lyrics3_data );
lyrics3_data = NULL;
lyrics3_size = 0;
char lyrics3_end_signature[ 15 ] = { 0 };
Seek64( hFile, ( mpeg_position + mpeg_length - 15 ), FILE_BEGIN );
len = 0;
if ( ReadFile( hFile, lyrics3_end_signature, 15, &len, NULL ) && len == 15 )
{
if ( !memcmp( lyrics3_end_signature + 6, "LYRICS200", 9 ) )
{
lyrics3_size = strtoul( lyrics3_end_signature, 0, 10 );
if ( lyrics3_size )
{
lyrics3_data = (char *)malloc( lyrics3_size );
if ( lyrics3_data )
{
SetFilePointer( hFile, -(LONG)( 15 + lyrics3_size ), NULL, FILE_CURRENT );
len = 0;
ReadFile( hFile, lyrics3_data, lyrics3_size, &len, 0 );
if ( len != lyrics3_size || memcmp( lyrics3_data, "LYRICSBEGIN", 11 ) )
{
free( lyrics3_data );
lyrics3_data = NULL;
lyrics3_size = 0;
}
else
{
mpeg_length -= lyrics3_size + 15;
}
}
}
}
}
}
if ( mpeg_length >= 32 )
{
/* Read APEv2 Tag */
free( apev2_data );
apev2_data = NULL;
char ape[ 32 ] = { 0 };
Seek64( hFile, ( mpeg_position + mpeg_length - 32 ), FILE_BEGIN );
len = 0;
if ( ReadFile( hFile, ape, 32, &len, NULL ) && len == 32 )
{
APEv2::Header footer( ape );
if ( footer.Valid() )
{
m_apev2_len = footer.TagSize();
if ( mpeg_length >= m_apev2_len )
{
Seek64( hFile, -(int64_t)( m_apev2_len ), FILE_CURRENT );
apev2_data = (char *)malloc( m_apev2_len );
if ( apev2_data )
{
len = 0;
ReadFile( hFile, apev2_data, m_apev2_len, &len, NULL );
if ( len != m_apev2_len || apev2.Parse( apev2_data, m_apev2_len ) != APEv2::APEV2_SUCCESS )
{
free( apev2_data );
apev2_data = NULL;
m_apev2_len = 0;
}
}
mpeg_length -= m_apev2_len;
}
}
}
}
{
Seek64( hFile, mpeg_position, FILE_BEGIN );
len = 0;
ReadFile( hFile, buf, sizeof( buf ), &len, NULL );
delete m_vbr_hdr;
m_vbr_hdr = NULL;
LAMEinfo lame;
lame.toc = m_vbr_toc;
m_vbr_frame_len = ReadLAMEinfo( buf, &lame );
if ( m_vbr_frame_len )
{
lengthVerified = false;
prepad = lame.encoderDelay;
postpad = lame.padding;
encodingMethod = lame.encodingMethod;
if ( !encodingMethod && lame.cbr )
encodingMethod = ENCODING_METHOD_CBR;
if ( lame.flags & TOC_FLAG )
{
int x;
for ( x = 0; x < 100; x++ )
if ( m_vbr_toc[ x ] ) break;
if ( x != 100 )
m_vbr_flag = 1;
}
if ( lame.flags & BYTES_FLAG )
{
// some programs are stupid and count the id3v2 length in the lame header
if ( mpeg_length + m_id3v2_len == lame.bytes || mpeg_length + m_id3v2_len + m_id3v1_len == lame.bytes )
{
m_vbr_bytes = mpeg_length;
lengthVerified = true;
}
else if ( abs( (int)mpeg_length - lame.bytes ) < MAX_ACCEPTABLE_DEVIANCE )
{
m_vbr_bytes = lame.bytes;
lengthVerified = true;
}
}
if ( lame.flags & FRAMES_FLAG
&& m_vbr_bytes && lengthVerified ) // only use the length if we're sure it's unmodified
{
m_vbr_frames = lame.frames;
m_vbr_samples = Int32x32To64( lame.frames, lame.h_id ? 1152 : 576 );
m_vbr_samples -= ( prepad + postpad );
m_vbr_ms = MulDiv( (int)m_vbr_samples, 1000, lame.samprate );
}
if ( !m_vbr_frames || encodingMethod == ENCODING_METHOD_CBR )
m_vbr_flag = 0;
mpeg_length -= m_vbr_frame_len;
mpeg_position += m_vbr_frame_len;
}
else
{
m_vbr_hdr = new CVbriHeader;
m_vbr_frame_len = m_vbr_hdr->readVbriHeader(buf);
if (m_vbr_frame_len)
{
m_vbr_bytes = m_vbr_hdr->getBytes();
m_vbr_frames = m_vbr_hdr->getNumFrames();
m_vbr_ms = m_vbr_hdr->getNumMS();
lengthVerified = true;
mpeg_length -= m_vbr_frame_len;
mpeg_position += m_vbr_frame_len;
}
else
{
delete m_vbr_hdr;
m_vbr_hdr = NULL;
}
}
}
// read OFL
{
Seek64( hFile, mpeg_position, FILE_BEGIN );
len = 0;
ReadFile( hFile, buf, sizeof( buf ), &len, NULL );
MPEGFrame frame;
frame.ReadBuffer( buf );
OFL ofl;
if ( ofl.Read( frame, &buf[ 4 ], len - 4 ) == NErr_Success )
{
m_vbr_ms = (int)ofl.GetLengthSeconds() * 1000;
m_vbr_samples = ofl.GetSamples();
m_vbr_frames = ofl.GetFrames();
size_t pre, post;
if ( ofl.GetGaps( &pre, &post ) == NErr_Success )
{
prepad = (int)pre - 529;
postpad = (int)post + 529;
}
}
}
ReadiTunesGaps();
Seek64( hFile, mpeg_position, FILE_BEGIN );
if ( maxbufsizek * 1024 >= mpeg_length )
{
DWORD m_full_buffer_len = 0;
m_full_buffer = new unsigned char[ (unsigned int)mpeg_length ];
if ( !ReadFile( hFile, m_full_buffer, (DWORD)mpeg_length, &m_full_buffer_len, NULL ) )
{
CloseHandle( hFile );
hFile = INVALID_HANDLE_VALUE;
delete[] m_full_buffer;
m_full_buffer = NULL;
return NErr_Error;
}
CloseHandle( hFile );
hFile = INVALID_HANDLE_VALUE;
m_full_buffer_pos = 0;
}
else
{
m_full_buffer = NULL;
}
fEof = false;
return NErr_Success;
}
else
{
//DWORD dwError = GetLastError();
return NErr_Error;
}
}
return NErr_Success;
}
//-------------------------------------------------------------------------*
// Close
//-------------------------------------------------------------------------*
int CGioFile::Close()
{
int dwReturn = NErr_Success;
if (m_is_stream)
{
jnl_connection_release(m_connection);
if (m_dns) jnl_dns_release(m_dns);
m_dns = 0;
if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
m_is_stream = 0;
}
else
{
delete [] m_full_buffer;
m_full_buffer = NULL;
if (hFile != INVALID_HANDLE_VALUE)
{
dwReturn = CloseHandle(hFile) ? NErr_Success : NErr_Error;
hFile = INVALID_HANDLE_VALUE;
}
}
return dwReturn;
}
void CGioFile::processMetaData(char *data, int len, int msgId)
{
if (len && *data)
{
char *ld = NULL;
int x;
if (len > 4096) return ;
for (x = 0; x < len; x ++)
if (!data[x]) break;
if (x == len) return ;
while ((ld = strstr(data, "='")))
{
char * n = data;
ld[0] = 0;
ld += 2;
data = strstr(ld, "';");
if (data)
{
data[0] = 0;
data += 2;
if (!_stricmp(n, "StreamTitle"))
{
lstrcpynA(g_stream_title, ld, sizeof(g_stream_title));
lstrcpynA(last_title_sent, g_stream_title, sizeof(last_title_sent));
lstrcpynA(stream_current_title, g_stream_title, sizeof(stream_current_title));
if (sctitle_format)
{
StringCchCatA(g_stream_title, 256, *stream_title_save ? *g_stream_title ? " (" : "(" : "");
StringCchCatA(g_stream_title, 256, stream_title_save);
StringCchCatA(g_stream_title, 256, *stream_title_save ? ")" : "");
}
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
}
else if (!_stricmp(n, "StreamUrl"))
{
lstrcpynA(stream_url, ld, sizeof(stream_url));
DWORD_PTR dw;
if (stream_url[0]) SendMessageTimeout(mod.hMainWindow, WM_USER, (WPARAM)stream_url, IPC_MBOPEN, SMTO_NORMAL, 500, &dw);
}
}
else break;
}
}
}
INT_PTR CALLBACK CGioFile::httpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CGioFile *_this;
switch (uMsg)
{
case WM_INITDIALOG:
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
_this = (CGioFile *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
if (_this->force_lpinfo[0])
SetDlgItemTextA(hwndDlg, IDC_EDIT1, _this->force_lpinfo);
else SetDlgItemTextA(hwndDlg, IDC_EDIT1, _this->lpinfo?_this->lpinfo:"");
SetDlgItemTextA(hwndDlg, IDC_REALM, _this->dlg_realm);
return 1;
case WM_COMMAND:
_this = (CGioFile *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
if (LOWORD(wParam) == IDOK)
{
GetDlgItemTextA(hwndDlg, IDC_EDIT1, _this->force_lpinfo, sizeof(_this->force_lpinfo));
EndDialog(hwndDlg, 1);
}
else if (LOWORD(wParam) == IDCANCEL)
{
EndDialog(hwndDlg, 0);
}
break;
}
return 0;
}
class GioFileFiller : public Filler
{
public:
GioFileFiller(CGioFile *_file)
{
ret=NErr_Success;
file=_file;
}
size_t Read(void *dest, size_t len)
{
int bytesRead=0;
ret = file->Read(dest, (int)len, &bytesRead);
return bytesRead;
}
CGioFile *file;
int ret;
};
int CGioFile::Peek(void *pBuffer, int cbToRead, int *pcbRead)
{
GioFileFiller filler(this);
// do we need to fill up?
if (cbToRead > (int)peekBuffer.size())
{
no_peek_hack = true;// benski> HACK ALERT! we have to set this or else Read() will try to use the peek buffer
peekBuffer.fill(&filler, cbToRead - peekBuffer.size());
no_peek_hack=false;
}
*pcbRead = (int)peekBuffer.peek(pBuffer, cbToRead);
return filler.ret;
}
//-------------------------------------------------------------------------*
// Read
//-------------------------------------------------------------------------*
int CGioFile::Read(void *pBuffer, int cbToRead, int *pcbRead)
{
TITLELISTTYPE *mylist = TitleLinkedList;
// these are used for SHOUTCAST2 metadata and artwork since it can provide
// multi-packet metadata chunks which are out of order or are received in
// a way which would otherwise cause these to be cleared incorrectly
static int title_parts = 0, stream_art_parts = 0, stream_art_parts_total_len = 0,
playing_art_parts = 0, playing_art_parts_total_len = 0;
static char **title_blocks = 0, **stream_art_blocks = 0, **playing_art_blocks = 0;
if (mylist != &TitleListTerminator && mod.outMod)
{
while (mylist->Next && mylist != &TitleListTerminator)
{
TITLELISTTYPE *next = (TITLELISTTYPE *)mylist->Next;
long now = mod.outMod->GetOutputTime();
if (mylist->title[0] || mylist->part_len)
{
if (!mylist->timer || now >= mylist->timer)
{
switch(mylist->style)
{
case UVOX_METADATA_STYLE_AOLRADIO:
{
EnterCriticalSection(&streamInfoLock);
free(uvox_3901);
uvox_3901 = _strdup(mylist->title);
LeaveCriticalSection(&streamInfoLock);
if (g_playing_file)
{
PostMessage(mod.hMainWindow, WM_USER, (WPARAM) "0x3901", IPC_METADATA_CHANGED);
PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
}
}
break;
case UVOX_METADATA_STYLE_SHOUTCAST:
{
processMetaData(mylist->title, (int)strlen(mylist->title) + 1);
}
break;
case UVOX_METADATA_STYLE_SHOUTCAST2:
{
EnterCriticalSection(&streamInfoLock);
// so we can recombine multi-packets we'll store things and
// when all of the packets have been read then we form them
if(!title_blocks)
title_blocks = (char **)malloc(sizeof(char*)*(mylist->total_parts));
title_blocks[mylist->part-1] = _strdup(mylist->title);
title_parts++;
// sanity check so we only try to get the metadata if all parts were processed
if (title_parts == mylist->total_parts)
{
free(uvox_3902);
uvox_3902 = (char *)malloc(16377 * mylist->total_parts);
uvox_3902[0] = 0;
title_parts = 0;
for(int i = 0; i < mylist->total_parts; i++)
{
StringCchCatA(uvox_3902, mylist->total_parts * 16384, title_blocks[i]);
free(title_blocks[i]);
}
free(title_blocks);
title_blocks = 0;
// attempt to form a title as 'artist - album - title' as sc_serv2 feeds
// changed for 5.61 to be just 'artist - title' to match v1 and v2 tools
Ultravox3902 uvox_metadata;
if (uvox_metadata.Parse(uvox_3902) != API_XML_FAILURE)
{
wchar_t stream_title[256] = {0};
char* fields[] = {"artist", "title"};
for(int i = 0; i < sizeof(fields)/sizeof(fields[0]); i++)
{
wchar_t temp[256] = {0};
int ret = uvox_metadata.GetExtendedData(fields[i], temp, 256);
if(ret && temp[0])
{
if(stream_title[0]) StringCchCatW(stream_title, 256, L" - ");
StringCchCatW(stream_title, 256, temp);
}
}
lstrcpynA(g_stream_title, AutoChar(stream_title, CP_UTF8), sizeof(g_stream_title));
lstrcpynA(last_title_sent, g_stream_title, sizeof(last_title_sent));
lstrcpynA(stream_current_title, g_stream_title, sizeof(stream_current_title));
}
}
else
{
g_stream_title[0] = 0;
last_title_sent[0] = 0;
}
if (sctitle_format)
{
StringCchCatA(g_stream_title, 256, *stream_title_save ? *g_stream_title ? " (" : "(" : "");
StringCchCatA(g_stream_title, 256, stream_title_save);
StringCchCatA(g_stream_title, 256, *stream_title_save ? ")" : "");
}
LeaveCriticalSection(&streamInfoLock);
if (g_playing_file)
{
PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
}
}
break;
case UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK:
{
EnterCriticalSection(&streamInfoLock);
// so we can recombine multi-packets we'll store things and
// when all of the packets have been read then we form them
if(!stream_art_blocks)
{
stream_art_parts_total_len = 0;
stream_art_blocks = (char **)malloc(sizeof(char*)*(mylist->total_parts));
}
stream_art_blocks[mylist->part-1] = (char *)malloc(mylist->part_len+1);
memcpy(stream_art_blocks[mylist->part-1], mylist->title, mylist->part_len);
stream_art_parts++;
stream_art_parts_total_len += mylist->part_len;
// sanity check so we only try to get the metadata if all parts were processed
if (stream_art_parts == mylist->total_parts)
{
free(uvox_artwork.uvox_stream_artwork);
if (stream_art_parts_total_len <= 0) break;
uvox_artwork.uvox_stream_artwork = (char *)malloc(stream_art_parts_total_len);
uvox_artwork.uvox_stream_artwork[0] = 0;
uvox_artwork.uvox_stream_artwork_len = stream_art_parts_total_len;
uvox_artwork.uvox_stream_artwork_type = mylist->type;
stream_art_parts = 0;
char *art = uvox_artwork.uvox_stream_artwork;
for(int i = 0; i < mylist->total_parts; i++)
{
int size = min(stream_art_parts_total_len, 16371);
if (size > 0 && size <= 16371)
{
memcpy(art, stream_art_blocks[i], size);
stream_art_parts_total_len -= size;
art += size;
}
free(stream_art_blocks[i]);
}
free(stream_art_blocks);
stream_art_blocks = 0;
}
LeaveCriticalSection(&streamInfoLock);
if (g_playing_file)
{
PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
}
}
break;
case UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK_PLAYING:
{
EnterCriticalSection(&streamInfoLock);
// so we can recombine multi-packets we'll store things and
// when all of the packets have been read then we form them
if(!playing_art_blocks)
{
playing_art_parts_total_len = 0;
playing_art_blocks = (char **)malloc(sizeof(char*)*(mylist->total_parts));
}
playing_art_blocks[mylist->part-1] = (char *)malloc(mylist->part_len+1);
memcpy(playing_art_blocks[mylist->part-1], mylist->title, mylist->part_len);
playing_art_parts++;
playing_art_parts_total_len += mylist->part_len;
// sanity check so we only try to get the metadata if all parts were processed
if (playing_art_parts == mylist->total_parts)
{
free(uvox_artwork.uvox_playing_artwork);
if (playing_art_parts_total_len <= 0) break;
uvox_artwork.uvox_playing_artwork = (char *)malloc(playing_art_parts_total_len);
uvox_artwork.uvox_playing_artwork[0] = 0;
uvox_artwork.uvox_playing_artwork_len = playing_art_parts_total_len;
uvox_artwork.uvox_playing_artwork_type = mylist->type;
playing_art_parts = 0;
char *art = uvox_artwork.uvox_playing_artwork;
for(int i = 0; i < mylist->total_parts; i++)
{
int size = min(playing_art_parts_total_len, 16371);
if (size > 0 && size <= 16371)
{
memcpy(art, playing_art_blocks[i], size);
playing_art_parts_total_len -= size;
art += size;
}
free(playing_art_blocks[i]);
}
free(playing_art_blocks);
playing_art_blocks = 0;
}
LeaveCriticalSection(&streamInfoLock);
if (g_playing_file)
{
PostMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
}
}
break;
}
removeTitleListEntry(mylist);
}
}
mylist = next;
}
}
if (!no_peek_hack && peekBuffer.size())
{
*pcbRead = (int)peekBuffer.read(pBuffer, cbToRead);
return NErr_Success;
}
BOOL bSuccess;
if (m_is_stream)
{
if (m_connection)
{
char str[4096]={0};
if (pcbRead) *pcbRead = 0;
jnl_connection_run(m_connection, -1, -1, 0, 0);
if (constate == 0)
{
if ( jnl_connection_receive_lines_available( m_connection ) > 0 )
{
char *p = str;
jnl_connection_receive_line( m_connection, str, 4096 );
// check http version and type of data, partial or full
if ( strlen( str ) >= 13 && !_strnicmp( str, "HTTP/1.1", 8 ) )
{
char *p = str + 8; while ( p && *p == ' ' ) p++;
m_http_response = ( p ? atoi( p ) : 0 );
if ( m_http_response == 200 )
m_seek_reset = true;
}
EnterCriticalSection( &g_lfnscs );
lstrcpynA( lastfn_status, str, 256 );
LeaveCriticalSection( &g_lfnscs );
PostMessage( mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE );
while ( p && *p && *p != ' ' ) p++;
if ( p && *p ) p++;
if ( p[ 0 ] == '2' ) constate = 1;
else if ( strstr( p, "301" ) == p || strstr( p, "302" ) == p ||
strstr( p, "303" ) == p || strstr( p, "307" ) == p )
{
constate = 69;
}
else if ( strstr( p, "401" ) == p && m_auth_tries++ < 3 )
{
constate = 75;
}
else if ( strstr( p, "403" ) == p )
{
EnterCriticalSection( &g_lfnscs );
lstrcpynA( lastfn_status, "access denied", 256 );
lastfn_status_err = 1;
LeaveCriticalSection( &g_lfnscs );
jnl_connection_close( m_connection, 1 );
}
else if ( strstr( p, "503" ) == p )
{
EnterCriticalSection( &g_lfnscs );
lstrcpynA( lastfn_status, "server full", 256 );
lastfn_status_err = 1;
LeaveCriticalSection( &g_lfnscs );
jnl_connection_close( m_connection, 1 );
}
else
{
lastfn_status_err = 1;
jnl_connection_close( m_connection, 1 );
}
}
else if ( jnl_connection_get_state( m_connection ) == JNL_CONNECTION_STATE_CLOSED || jnl_connection_get_state( m_connection ) == JNL_CONNECTION_STATE_ERROR || jnl_connection_get_state( m_connection ) == JNL_CONNECTION_STATE_NOCONNECTION )
{
const char *t = jnl_connection_get_error( m_connection );
if ( t && *t )
{
EnterCriticalSection( &g_lfnscs );
lstrcpynA( lastfn_status, t, 256 );
lastfn_status_err = 1;
LeaveCriticalSection( &g_lfnscs );
}
PostMessage( mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE );
}
}
if (constate == 75) // authorization required
{
while (jnl_connection_receive_lines_available(m_connection) > 0)
{
char *wwwa = "WWW-Authenticate:";
jnl_connection_receive_line(m_connection, str, 4096);
if (!str[0])
{
lastfn_status_err = 1; jnl_connection_close(m_connection, 1); break;
}
if (!_strnicmp(str, wwwa, strlen(wwwa)))
{
int has = 0;
char *s2 = "Basic realm=\"";
char *p = str + strlen(wwwa); while (p && *p == ' ') p++;
if (!_strnicmp(p, s2, strlen(s2)))
{
p += strlen(s2);
if (strstr(p, "\""))
{
if (p && *p)
{
strstr(p, "\"")[0] = 0;
extern char *get_inifile();
if (!force_lpinfo[0]) GetPrivateProfileStringA("HTTP-AUTH", p, "", force_lpinfo, sizeof(force_lpinfo), get_inifile());
if (!force_lpinfo[0] || (lpinfo && lpinfo[0]))
{
lstrcpynA(dlg_realm, p, sizeof(dlg_realm));
if (!WASABI_API_DIALOGBOXPARAM(IDD_HTTPAUTH, GetDialogBoxParent(), httpDlgProc, (LPARAM)this))
{
force_lpinfo[0] = 0;
}
else
{
WritePrivateProfileStringA("HTTP-AUTH", p, force_lpinfo, get_inifile());
}
}
if (force_lpinfo[0])
{
jnl_connection_release(m_connection);
m_connection = NULL;
doConnect(NULL, 0);
has = 1;
}
}
}
}
if (!has)
{
lastfn_status_err = 1;
jnl_connection_close(m_connection, 1);
}
break;
}
}
}
if (constate == 69) // redirect city
{
while (jnl_connection_receive_lines_available(m_connection) > 0)
{
jnl_connection_receive_line(m_connection, str, 4096);
if (!str[0])
{
jnl_connection_close(m_connection, 1); break;
}
if (!_strnicmp(str, "Location:", 9))
{
char *p = str + 9; while (p && *p == ' ') p++;
if (p && *p)
{
if (m_redircnt++ < MAX_REDIRECTS)
{
jnl_connection_release(m_connection);
m_connection = NULL;
doConnect(p, 0);
}
else
{
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_REDIRECT_LIMIT_EXCEEDED,lastfn_status,256);
lastfn_status_err = 1;
LeaveCriticalSection(&g_lfnscs);
jnl_connection_close(m_connection, 1);
}
break;
}
}
}
}
if (constate == 1)
{
while (jnl_connection_receive_lines_available(m_connection) > 0)
{
jnl_connection_receive_line(m_connection, str, 4096);
if (!str[0])
{
if (config_http_save_dir[0] && (config_miscopts&16))
{
if (!save_filename[0] && m_is_stream == 1 &&
strlen(stream_title_save) > 4 &&
!strstr(stream_title_save, "..") &&
!strstr(stream_title_save, "\\") &&
!strstr(stream_title_save, "/"))
{
lstrcpynA(save_filename, stream_title_save, 251);
char *p = strstr(save_filename, ".mp");
if (!p) p = strstr(save_filename, ".MP");
if (!p) p = strstr(save_filename, ".mP");
if (!p) p = strstr(save_filename, ".Mp");
if (!p)
{
StringCchCatA(save_filename, 256, ".mp3");
}
else
{
while (p && *p && *p != ' ') p++;
if (p) *p = 0;
}
}
if (save_filename[0])
{
char buf[4096] = {0};
StringCchPrintfA(buf, 4096, "%s%s%s", config_http_save_dir, config_http_save_dir[strlen(config_http_save_dir) - 1] == '\\' ? "" : "\\", save_filename);
hFile = CreateFileA(buf, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
}
else save_filename[0] = 0;
constate = meta_interval ? 4 : 2;
break;
}
// check if stream is seekable
if (strlen(str) >= 14 && !_strnicmp(str, "Accept-Ranges:", 14))
{
m_is_stream_seekable = true;
}
if (!_strnicmp(str, "Content-Length:", 15))
{
char *p = str + 15; while (p && *p == ' ') p++;
if (!is_stream_seek)
mpeg_length = atoi(p);
m_is_stream_seekable = true;
}
if (!_strnicmp(str, "content-type:", 13))
{
char *p = str + 13; while (p && *p == ' ') p++;
free(m_content_type);
m_content_type = _strdup(p);
if (!_strnicmp(m_content_type, "misc/ultravox",13))
{
stream_title_save[0] = 0;
stream_name[0]=0;
g_stream_title[0] = 0;
is_uvox = 1;
// TODO get this to id as SC2 stream if possible...
m_is_stream = 3;
}
}
if (!_strnicmp(str, "Server:", 7))
{
char *p = str + 7; while (p && *p == ' ') p++;
lstrcpynA(server_name, p, sizeof(server_name));
}
if (!_strnicmp(str, "ultravox-max-msg:", 17))
{
char *p = str + 17; while (p && *p == ' ') p++;
uvox_maxmsg = (p ? atoi(p) : 0);
if (uvox_maxmsg > UVOX_MAXMSG_CAP) // benski> security vulnerability fix, too high of value was causing an integer overflow (because we malloc uvox_maxmsg*2
uvox_maxmsg = UVOX_MAXMSG_CAP;
m_is_stream = 3;
}
else if (!_strnicmp(str, "Ultravox-SID:", 13))
{
char *p = str + 13; while (p && *p == ' ') p++;
uvox_sid = (p ? atoi(p) : 0);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Avg-Bitrate:", 21))
{
char *p = str + 21; while (p && *p == ' ') p++;
uvox_avgbr = (p ? atoi(p) : 0);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Max-Bitrate:", 21))
{
char *p = str + 21; while (p && *p == ' ') p++;
uvox_maxbr = (p ? atoi(p) : 0);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Bitrate:", 17))
{
char *p = str + 17; while (p && *p == ' ') p++;
uvox_avgbr = uvox_maxbr = (p ? atoi(p) : 0);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Title:", 15))
{
char *p = str + 15; while (p && *p == ' ') p++;
lstrcpynA(stream_title_save, p, 580);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Genre:", 15))
{
char *p = str + 15; while (p && *p == ' ') p++;
lstrcpynA(stream_genre, p, sizeof(stream_genre));
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-URL:", 13)/* && !strstr(str, "shoutcast.com")*/)
{
char *p = str + 13; while (p && *p == ' ') p++;
lstrcpynA(stream_url, p, sizeof(stream_url));
DWORD_PTR dw = 0;
if (stream_url[0]) SendMessageTimeout(mod.hMainWindow, WM_USER, (WPARAM)stream_url, IPC_MBOPEN, SMTO_NORMAL, 500, &dw);
m_is_stream = 3;
}
if (!_strnicmp(str, "Ultravox-Class-Type:", 19))
{
// id our stream for 'streamtype'
// added for 5.63 as is a better way to check for
// a SC2/UVOX2.1 stream when no metadata is sent
m_is_stream = 5;
}
if (!_strnicmp(str, "icy-notice2:", 12))
{
char *p = str + 12; while (p && *p == ' ') p++;
char *p2 = (p ? strstr(p, "<BR>") : 0);
if (p2)
{
*p2 = 0;
lstrcpynA(server_name, p, sizeof(server_name));
}
m_is_stream=2;
}
if (!_strnicmp(str, "content-disposition:", 20))
{
if (strstr(str, "filename="))
lstrcpynA(g_stream_title, strstr(str, "filename=") + 9, sizeof(g_stream_title));
lstrcpynA(stream_title_save, g_stream_title, 580);
}
if (!_strnicmp(str, "icy-name:", 9))
{
char *p = str + 9; while (p && *p == ' ') p++;
lstrcpynA(g_stream_title, p, sizeof(g_stream_title));
lstrcpynA(stream_title_save, g_stream_title, 580);
lstrcpynA(stream_name, g_stream_title, 256);
// m_is_stream = 2; // benski> cut: some things use icy-name as a hack to show a title - not a reliable indicator of a SHOUTcast stream
}
if (!_strnicmp(str, "icy-metaint:", 12))
{
char *p = str + 12; while (p && *p == ' ') p++;
meta_interval = (p ? atoi(p) : 0);
m_is_stream = 2;
}
if (!_strnicmp(str, "icy-genre:", 10))
{
char *p = str + 10; while (p && *p == ' ') p++;
lstrcpynA(stream_genre, p, sizeof(stream_genre));
m_is_stream = 2;
}
if (!_strnicmp(str, "icy-url:", 8) && !strstr(str, "shoutcast.com"))
{
char *p = str + 8; while (p && *p == ' ') p++;
lstrcpynA(stream_url, p, sizeof(stream_url));
//if (!strncmp(stream_url,"hTtP",4))
//{
// DWORD dw;
// SendMessageTimeout(mod.hMainWindow,WM_USER,(WPARAM)0,241,SMTO_NORMAL,500,&dw);
//} // Removed by Tag, Annoying.
DWORD_PTR dw = 0;
if (stream_url[0]) SendMessageTimeout(mod.hMainWindow, WM_USER, (WPARAM)stream_url, IPC_MBOPEN, SMTO_NORMAL, 500, &dw);
m_is_stream = 2;
}
}
}
if (constate == 2) // check for id3v2
{
if (jnl_connection_receive_bytes_available(m_connection) >= 10)
{
char buf[10]={0};
constate = 4;
jnl_connection_peek(m_connection, buf, 10);
if (buf[0] == 'I'
&& buf[1] == 'D'
&& buf[2] == '3'
&& buf[3] != 255
&& buf[4] != 255
&& buf[6] < 0x80
&& buf[7] < 0x80
&& buf[8] < 0x80
&& buf[9] < 0x80)
{
jnl_connection_receive(m_connection, buf, 10);
m_id3v2_len = 10;
if (buf[5] & 0x10) // check for footer flag
m_id3v2_len += 10;
m_id3v2_len += ((int)buf[6]) << 21;
m_id3v2_len += ((int)buf[7]) << 14;
m_id3v2_len += ((int)buf[8]) << 7;
m_id3v2_len += ((int)buf[9]);
if (m_id3v2_len < 0) m_id3v2_len = 0;
if (mpeg_length && m_id3v2_len > mpeg_length)
{
m_id3v2_len = 0;
}
if (m_id3v2_len)
{
constate = 3;
stream_id3v2_read = 0;
if (m_id3v2_len < 16*1024*1024)
{
stream_id3v2_buf = (char*)malloc(10 + m_id3v2_len);
stream_id3v2_read = 10;
memcpy(stream_id3v2_buf, buf, 10);
}
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_READING_ID3,lastfn_status,256);
LeaveCriticalSection(&g_lfnscs);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
}
}
}
}
if (constate == 3) // id3v2 found
{
while ((int)stream_id3v2_read < m_id3v2_len)
{
char buf[1024]={0};
int btoread = m_id3v2_len - stream_id3v2_read;
if (btoread > sizeof(buf)) btoread = sizeof(buf);
int avail = (int)jnl_connection_receive_bytes_available(m_connection);
if (btoread > avail) btoread = avail;
if (!btoread) break;
if (stream_id3v2_buf)
stream_id3v2_read += (uint32_t)jnl_connection_receive(m_connection, stream_id3v2_buf + stream_id3v2_read, btoread);
else
stream_id3v2_read += (uint32_t)jnl_connection_receive(m_connection, buf, btoread);
//stream_id3v2_read+=m_connection->ReceiveBytes(0,btoread);
}
if ((int)stream_id3v2_read >= m_id3v2_len)
{
if (stream_id3v2_buf)
{
if (m_is_stream_seekable /*m_is_stream != 2*/) /* don't want to do id3v2 on an internet stream */
{
EnterCriticalSection(&streamInfoLock);
info.Decode(stream_id3v2_buf, stream_id3v2_read);
// TODO: streamInfo = info;
LeaveCriticalSection(&streamInfoLock);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
}
}
constate = 4;
}
}
if (constate == 4) // check for xing header
{
if (GetContentLength() < 1 || is_stream_seek) constate = 5;
else
{
int avail = (int)jnl_connection_receive_bytes_available(m_connection);
if (avail > 4096 || jnl_connection_get_state(m_connection) == JNL_CONNECTION_STATE_CLOSED)
{
char buf[4096]={0};
jnl_connection_peek(m_connection, buf, sizeof(buf));
constate++;
{
delete m_vbr_hdr;
m_vbr_hdr = 0;
LAMEinfo lame;
lame.toc = m_vbr_toc;
m_vbr_frame_len = ReadLAMEinfo((unsigned char *)buf, &lame);
if (m_vbr_frame_len)
{
prepad = lame.encoderDelay;
postpad = lame.padding;
if (lame.flags&TOC_FLAG)
{
int x;
for (x = 0; x < 100; x++)
if (m_vbr_toc[x]) break;
if (x != 100)
m_vbr_flag = 1;
}
if (lame.flags&FRAMES_FLAG)
{
m_vbr_frames = lame.frames;
m_vbr_samples = Int32x32To64(lame.frames, lame.h_id ? 1152 : 576);
m_vbr_samples -= (prepad + postpad);
m_vbr_ms = MulDiv((int)m_vbr_samples, 1000, lame.samprate);
}
if (!m_vbr_frames) m_vbr_flag = 0;
jnl_connection_receive(m_connection, buf, m_vbr_frame_len);
}
else
{
m_vbr_hdr = new CVbriHeader;
m_vbr_frame_len = m_vbr_hdr->readVbriHeader((unsigned char *)buf);
if (m_vbr_frame_len)
{
m_vbr_bytes = m_vbr_hdr->getBytes();
m_vbr_frames = m_vbr_hdr->getNumFrames();
m_vbr_ms = m_vbr_hdr->getNumMS();
}
else
{
delete m_vbr_hdr;
m_vbr_hdr = 0;
}
}
}
// TODO OFL
}
}
}
if (constate == 5) // time to stream
{
while (1)
{
// Process any timed titles
int len = (int)jnl_connection_receive_bytes_available(m_connection);
if (meta_interval && meta_pos >= meta_interval)
{
unsigned char b;
if (len > 0 && jnl_connection_peek(m_connection, (char*)&b, 1) && len > (b << 4))
{
char metabuf[4096]={0};
jnl_connection_receive(m_connection, metabuf, 1);
jnl_connection_receive(m_connection, metabuf, b << 4);
processMetaData(metabuf, b << 4);
stream_metabytes_read += (b << 4) + 1;
meta_pos = 0;
}
else break;
}
else if (is_uvox)
{
if (!uvox_stream_data)
{
/* benski> this was a security vulnerability.
don't blindly multiply by 2 and pass to malloc
if uvox_maxmsg happens to be 2^31, multiplying by 2 turns it into 0!
CUT!!! uvox_stream_data = (unsigned char*)malloc(uvox_maxmsg * 2 + 4096);
*/
uvox_stream_data = (unsigned char*)malloc(SAFE_MALLOC_MATH(uvox_maxmsg * 2 + 4096, uvox_maxmsg));
}
if (uvox_stream_data_len < 1)
{
again:
if (len < 6) break;
jnl_connection_peek(m_connection, (char*)uvox_stream_data, 6);
int uvox_qos = uvox_stream_data[1];
int classtype = (uvox_stream_data[2] << 8) | uvox_stream_data[3];
int uvox_len = (uvox_stream_data[4] << 8) | uvox_stream_data[5];
int uvox_class = (classtype >> 12) & 0xF;
int uvox_type = classtype & 0xFFF;
if (uvox_stream_data[0] != 0x5A || uvox_len > uvox_maxmsg)
{
jnl_connection_receive(m_connection, NULL, 1);
len--;
uvox_desyncs++;
goto again;
}
if (uvox_len + 7 > len) break;
// process uvox chunk
jnl_connection_peek(m_connection, (char*)uvox_stream_data, 6 + uvox_len + 1);
if (uvox_stream_data[6 + uvox_len])
{
jnl_connection_receive(m_connection, NULL, 1);
uvox_desyncs++;
len--;
goto again;
}
else if (uvox_class == 0x2 && uvox_type == 0x003)
{
// failover gayness
}
else if (uvox_class == 0x2 && uvox_type == 0x002)
{
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_STREAM_TERMINATED,lastfn_status,256);
LeaveCriticalSection(&g_lfnscs);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
jnl_connection_close(m_connection, 0);
}
else if (uvox_class == 0x2 && uvox_type == 0x001)
{
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_STREAM_TEMPORARILY_INTERRUPTED,lastfn_status,256);
LeaveCriticalSection(&g_lfnscs);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
}
else if (uvox_class == 0x3 && (uvox_type == 0x902 || uvox_type == 0x901)) // SHOUTcast 2 metadata
{
// id our stream for 'streamtype'
m_is_stream = 5;
// this will allow us to cope with multi-packet metadata messages
// (used to be needed when the old SC2 spec used an APIC tag to
// to send the artwork in the metadata message - now it's sent in
// in the uvox_class == 0x4 messages for stream and playing cases)
if (!uvox_stream_data[8])
{
char *mbuf = (char*)uvox_stream_data + 12;
if (mbuf)
{
unsigned long delay = 0;
if (uvox_avgbr)
{
long byterate = (uvox_avgbr / 8);
long bytes = 1024 * 11;
float localdelay = (float)bytes / (float)byterate;
delay = (long)localdelay * 1000;
}
// make sure that we've got a packet which is within the specs
// as id can only be 1-32, total parts can only be 1-32,
// id cannot be more than total parts and if not then skip it.
if(uvox_stream_data[11] < 1 || uvox_stream_data[11] > 32 ||
uvox_stream_data[11] > uvox_stream_data[9] ||
uvox_stream_data[9] < 1 || uvox_stream_data[9] > 32)
{
break;
}
TITLELISTTYPE *newtitle = newTitleListEntry();
newtitle->style = (uvox_type == 0x902 ? UVOX_METADATA_STYLE_SHOUTCAST2 : UVOX_METADATA_STYLE_AOLRADIO);
// we make sure to only copy up to the maximum metadata payload size
newtitle->part_len = min((uvox_len - 6), 16371);
memcpy(newtitle->title, mbuf, newtitle->part_len);
newtitle->part = uvox_stream_data[11];
newtitle->total_parts = uvox_stream_data[9];
newtitle->timer = (stream_bytes_read && mod.outMod ? delay + mod.outMod->GetOutputTime() : 0);
}
}
}
else if (uvox_class == 0x4) // SHOUTcast 2 albumart
{
if (allow_scartwork && !uvox_stream_data[8])
{
char *mbuf = (char*)uvox_stream_data + 12;
if (mbuf)
{
// [0x4] [0|1] [00|01|02|03]
unsigned long delay = 0;
if (uvox_avgbr)
{
long byterate = (uvox_avgbr / 8);
long bytes = 1024 * 11;
float localdelay = (float)bytes / (float)byterate;
delay = (long)localdelay * 1000;
}
// make sure that we've got a packet which is within the specs
// as id can only be 1-32, total parts can only be 1-32,
// id cannot be more than total parts and if not then skip it.
if(uvox_stream_data[11] < 1 || uvox_stream_data[11] > 32 ||
uvox_stream_data[11] > uvox_stream_data[9] ||
uvox_stream_data[9] < 1 || uvox_stream_data[9] > 32)
{
break;
}
TITLELISTTYPE *newtitle = newTitleListEntry();
newtitle->style = (uvox_type & 0x100 ? UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK_PLAYING : UVOX_METADATA_STYLE_SHOUTCAST2_ARTWORK);
// we make sure to only copy up to the maximum metadata payload size
newtitle->part_len = min((uvox_len - 6), 16371);
memcpy(newtitle->title, mbuf, newtitle->part_len);
newtitle->part = uvox_stream_data[11];
newtitle->total_parts = uvox_stream_data[9];
newtitle->type = (uvox_type & 0x00FF);
newtitle->timer = (stream_bytes_read && mod.outMod ? delay + mod.outMod->GetOutputTime() : 0);
}
}
}
else if (uvox_class == 0x3 && (uvox_type == 0x001 || uvox_type == 0x002))
{
int w = uvox_type - 1; // should be ID?
int n = (uvox_stream_data[8] << 8) | uvox_stream_data[9];
if (n && n < 33)
{
int t = (uvox_stream_data[10] << 8) | uvox_stream_data[11];
if (t && t <= n)
{
int l = 0;
int a;
char *p = (char*)uvox_stream_data + 12; // this almost works
free(uvox_meta[w][t - 1]);
uvox_meta[w][t - 1] = _strdup(p);
for (a = 0;a < n;a++)
{
if (!uvox_meta[w][a]) break;
l += (int)strlen(uvox_meta[w][a]);
}
if (a == n)
{
char *outtext = (char*)malloc(l + 1);
p = outtext;
for (a = 0;a < n;a++)
{
lstrcpynA(p, uvox_meta[w][a], l + 1);
free(uvox_meta[w][a]);
uvox_meta[w][a] = 0;
p += strlen(p);
}
processMetaData(outtext, l + 1);
free(outtext);
}
}
}
}
else if ((uvox_class == 0x7 && (uvox_type == 0x0 || uvox_type == 0x1))
|| (uvox_class == 0x8 && (uvox_type == 0x0 || uvox_type == 0x1 || uvox_type == 0x3)))
{
memcpy(uvox_stream_data, uvox_stream_data + 6, uvox_len);
uvox_stream_data_len = uvox_len;
uvox_last_message = uvox_class << 12 | uvox_type;
}
jnl_connection_receive(m_connection, NULL, 6 + uvox_len + 1);
uvox_message_cnt++;
}
if (uvox_stream_data_len > 0)
{
len = min(cbToRead, uvox_stream_data_len);
memcpy(pBuffer, uvox_stream_data, len);
if (pcbRead) *pcbRead = len;
stream_bytes_read += len;
if (len < uvox_stream_data_len)
{
memcpy(uvox_stream_data, uvox_stream_data + len, uvox_stream_data_len - len);
}
uvox_stream_data_len -= len;
break;
}
}
else
{
len = min(cbToRead, len);
if (meta_interval) len = min(meta_interval - meta_pos, len);
if (len > 0)
{
DWORD dw = 0;
len = (int)jnl_connection_receive(m_connection, (char*)pBuffer, len);
if (hFile != INVALID_HANDLE_VALUE) WriteFile(hFile, pBuffer, len, &dw, NULL);
if (pcbRead) *pcbRead = len;
meta_pos += len;
stream_bytes_read += len;
}
else if (pcbRead) *pcbRead = 0;
break;
}
}
}
int state = m_connection ? jnl_connection_get_state(m_connection) : JNL_CONNECTION_STATE_CLOSED;
if (state == JNL_CONNECTION_STATE_ERROR || state == JNL_CONNECTION_STATE_CLOSED)
{
if ((!pcbRead || !*pcbRead) && (!m_connection || jnl_connection_receive_bytes_available(m_connection) < 1))
{
fEof = 1;
return NErr_Error;
}
}
else if (constate != 5 && constate != 4 && constate != 3 && timeout_start + 15000 < GetTickCount()) // 15 second net connect timeout
{
EnterCriticalSection(&g_lfnscs);
WASABI_API_LNGSTRING_BUF(IDS_TIMED_OUT,lastfn_status,256);
lastfn_status_err = 1;
LeaveCriticalSection(&g_lfnscs);
PostMessage(mod.hMainWindow, WM_USER, 0, IPC_UPDTITLE);
fEof = 1;
return NErr_Error;
}
return NErr_Success;
}
return NErr_Error;
}
else if (m_full_buffer)
{
int len = cbToRead;
if ((uint64_t)len > (mpeg_length - m_full_buffer_pos))
{
len = (int)(mpeg_length - m_full_buffer_pos);
}
if (pcbRead) *pcbRead = len;
if (len)
{
memcpy(pBuffer, m_full_buffer + m_full_buffer_pos, len);
m_full_buffer_pos += len;
}
else
{
fEof = true;
return NErr_EndOfFile;
}
return NErr_Success;
}
else
{
if (hFile != INVALID_HANDLE_VALUE)
{
if ((uint64_t)cbToRead >= (mpeg_length - file_position))
{
cbToRead = (int)(mpeg_length - file_position);
fEof = true;
}
if (cbToRead == 0)
{
if (pcbRead)
*pcbRead = 0;
return NErr_Success;
}
DWORD dwRead = 0;
bSuccess = ReadFile(hFile, pBuffer, cbToRead, &dwRead, NULL);
if (bSuccess)
{
file_position += dwRead;
// update pcbRead
if (pcbRead)
*pcbRead = dwRead;
// check for EOF
if (dwRead == 0)
fEof = true;
return NErr_Success;
}
else
{
// error reading from file
return NErr_Error;
}
}
else
{
// no valid file handle
return NErr_Error;
}
}
}
//-------------------------------------------------------------------------*
// IsEof
//-------------------------------------------------------------------------*
bool CGioFile::IsEof() const
{
return fEof;
}
//-------------------------------------------------------------------------*
// GetContentLength
//-------------------------------------------------------------------------*
DWORD CGioFile::GetContentLength(void) const
{
DWORD dwSize = 0 ;
dwSize = (DWORD)mpeg_length;
return dwSize ;
}
//-------------------------------------------------------------------------*
// GetCurrentPosition
//-------------------------------------------------------------------------*
DWORD CGioFile::GetCurrentPosition(void) const
{
DWORD dwPos = 0;
if (m_is_stream)
{
dwPos = (DWORD)stream_bytes_read;
}
else if (m_full_buffer)
{
dwPos = (DWORD)m_full_buffer_pos;
}
else if (hFile != INVALID_HANDLE_VALUE)
{
dwPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
dwPos -= (DWORD)(mpeg_position + peekBuffer.size());
}
return dwPos ;
}
//-------------------------------------------------------------------------*
// SetCurrentPosition
//-------------------------------------------------------------------------*
void CGioFile::SetCurrentPosition(long dwPos, int How) // GIO_FILE_BEGIN only
{
fEof = false;
if (m_full_buffer)
{
m_full_buffer_pos = dwPos;
if (m_full_buffer_pos < 0) m_full_buffer_pos = 0;
if (m_full_buffer_pos > mpeg_length) m_full_buffer_pos = mpeg_length;
}
else if (hFile != INVALID_HANDLE_VALUE)
{
Seek64(hFile, mpeg_position + dwPos, FILE_BEGIN);
file_position = dwPos;
peekBuffer.clear();
}
}
int CGioFile::GetHeaderOffset()
{
return (int)mpeg_position;
}
/*-------------------------------------------------------------------------*/
void CGioFile::Seek(int posms, int br)
{
int offs = 0;
if (m_vbr_hdr)
{
offs = m_vbr_hdr->seekPointByTime((float)posms);
}
else if (!m_vbr_flag)
offs = MulDiv(posms, br, 8);
else
{
int ms = 0;
int fl = GetContentLength();
if (!m_vbr_frames)
{
ms = MulDiv(fl, 8 * 1000, br);
}
else ms = m_vbr_ms;
offs = SeekPoint(m_vbr_toc, fl, (float)posms / ((float)ms / 100.0f));
}
if (m_is_stream)
{
if (GetContentLength() > 0)
{
if (m_connection && m_is_stream_seekable)
{
jnl_connection_release(m_connection);
m_connection = NULL;
m_is_stream_seek = true;
m_seek_reset = false;
doConnect(NULL, offs);
}
}
}
else
{
SetCurrentPosition(offs, GIO_FILE_BEGIN);
}
}
bool CGioFile::IsSeekable()
{
return !m_is_stream || GetContentLength() > 0;
}
void CGioFile::GetStreamInfo(wchar_t *obuf, size_t len)
{
if (m_is_stream)
{
wchar_t langbuf[2048]={0};
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_NETWORK_RECEIVED_X_BYTES, langbuf, 2048), stream_bytes_read + stream_metabytes_read);
if (server_name[0])
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_SERVER, langbuf, 2048), AutoWide(server_name,CP_UTF8));
if (m_content_type && m_content_type[0])
{
if(is_uvox)
{
// report the actual content type instead of just misc/ultravox to make it easier to see what the stream type is (helps debugging)
static const int MP3_DATA = 0x7000;
static const int VLB_DATA = 0x8000;
static const int AAC_LC_DATA = 0x8001;
static const int AACP_DATA = 0x8003;
static const int OGG_DATA = 0x8004;
switch(uvox_last_message)
{
case MP3_DATA:
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), L"audio/mpeg");
break;
case VLB_DATA:
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), L"audio/vlb");
break;
case AAC_LC_DATA:
case AACP_DATA:
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), L"audio/aacp");
break;
case OGG_DATA:
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), L"audio/ogg");
break;
default:
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), AutoWide(m_content_type,CP_UTF8));
break;
}
}
else
{
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_TYPE, langbuf, 2048), AutoWide(m_content_type,CP_UTF8));
}
}
if (is_uvox)
{
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ULTRAVOX_SYNC, langbuf, 2048), uvox_message_cnt, uvox_desyncs);
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ULTRAVOX_DATA_MESSAGE, langbuf, 2048), uvox_last_message);
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ULTRAVOX_SID_AVGBR_MAXBR, langbuf, 2048), uvox_sid, uvox_avgbr, uvox_maxbr);
}
if (stream_metabytes_read)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_METADATA_RECEIVED, langbuf, 2048), stream_metabytes_read);
if (meta_interval)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_METADATA_INTERVAL, langbuf, 2048), meta_interval);
if (m_id3v2_len)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_ID3v2_TAG, langbuf, 2048), m_id3v2_len);
if (m_vbr_frame_len)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_VBR_LEADING_FRAME, langbuf, 2048), m_vbr_frame_len);
if (stream_title_save[0])
{
wchar_t name[580]={0};
ConvertTryUTF8(stream_title_save, name, 580);
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_STREAM_NAME, langbuf, 2048), name/*AutoWide(stream_title_save,CP_UTF8)*/);
}
if (last_title_sent[0])
{
wchar_t title[256]={0};
ConvertTryUTF8(last_title_sent, title, 256);
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CURRENT_TITLE, langbuf, 2048), title);
}
if (mpeg_length)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_CONTENT_LENGTH, langbuf, 2048), mpeg_length);
if (save_filename[0] && hFile != INVALID_HANDLE_VALUE)
StringCchPrintfEx(obuf, len, &obuf, &len, 0, WASABI_API_LNGSTRINGW_BUF(IDS_SAVING_TO, langbuf, 2048), AutoWide(save_filename,CP_UTF8));
}
}
static inline const wchar_t *IncSafe(const wchar_t *val, int x)
{
while (x--)
{
if (val && *val)
val++;
}
return val;
}
void CGioFile::ReadiTunesGaps()
{
if (info.HasData() && !prepad && !postpad)
{
wchar_t str[128] = {0};
if (info.GetString("pregap", str, 128) == 1)
prepad = _wtoi(str);
str[0]=0;
if (info.GetString("postgap", str, 128) == 1)
postpad = _wtoi(str);
}
}
#define CBCLASS CGioFile
START_DISPATCH;
CB( MPEGSTREAM_PEEK, Peek )
CB( MPEGSTREAM_READ, Read )
CB( MPEGSTREAM_EOF, EndOf )
CB( MPEGSTREAM_GAIN, GetGain )
END_DISPATCH;
#undef CBCLASS