aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_mp3/giofile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_mp3/giofile.cpp')
-rw-r--r--Src/Plugins/Input/in_mp3/giofile.cpp2475
1 files changed, 2475 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp3/giofile.cpp b/Src/Plugins/Input/in_mp3/giofile.cpp
new file mode 100644
index 00000000..4949c268
--- /dev/null
+++ b/Src/Plugins/Input/in_mp3/giofile.cpp
@@ -0,0 +1,2475 @@
+/***************************************************************************\
+*
+* (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 \ No newline at end of file