#include <windows.h>
#include "main.h"
#include <sys/stat.h>

#ifdef NO_WASABI
#include "../../jnetlib/httpget.h"
api_httpreceiver *CreateGet() 
{
	return new JNL_HTTPGet;
}

void ReleaseGet(api_httpreceiver *&get)
{
	delete (JNL_HTTPGet *)get;
	get=0;
}
#else
#include "../..\Components\wac_network\wac_network_http_receiver_api.h"
#include <api.h>
#include <api/service/waservicefactory.h>
#include "../../Winamp/in2.h"
extern In_Module mod;

waServiceFactory *httpFactory = 0;
api_httpreceiver *CreateGet()
{
	api_httpreceiver *get = 0;
	if (!httpFactory && mod.service)
		httpFactory = mod.service->service_getServiceByGuid(httpreceiverGUID);

	if (httpFactory)
		get = (api_httpreceiver *)httpFactory->getInterface();

	return get;
}

void ReleaseGet(api_httpreceiver *&get)
{
	if (!get)
		return ;

	if (!httpFactory && mod.service)
		waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);

	if (httpFactory)
		httpFactory->releaseInterface(get);

	get = 0;
}


#endif
#define MAX_MULTICONNECTS 8

class HTTPReader : public IDataReader
{
public:
	HTTPReader(const char *url);
	~HTTPReader();
	size_t read(char *buf, size_t len);
	bool iseof() { return !!m_eof; }
	char *gettitle() { return m_title; }
	char *geterror() { return m_err; }
	bool canseek() { return m_content_length != 0xFFFFFFFF && m_accept_ranges && !m_meta_interval; }
	int seek(unsigned __int64 newpos)
	{
		if (!canseek()) return 1;
		doConnect((int)newpos);
		return 0;
	}
unsigned __int64 getsize() { return m_content_length; }
	char *getheader(char *header_name)
	{
		return m_get ? (char *)m_get->getheader(header_name) : NULL;
	}
private:

	int serialconnect( int seekto , int timeout);
	void doConnect(int seekto);
	int getProxyInfo(const char *url, char *out);
	char *m_url;
	char *m_err;
	char *m_title;
	int m_eof;
	int m_meta_init, m_meta_interval, m_meta_pos, m_meta_size, m_meta_buf_pos;
	char m_meta_buf[4096];
	int m_read_headers;
	unsigned int m_content_length;
	int m_accept_ranges;
	int m_is_uvox;
	int m_uvox_readpos;
	int m_uvox_enough_bytes;
	char proxybuf[8400], *proxy;

	api_httpreceiver *m_get;

	// this structure is allocated once, and freed once
	struct
	{
		char *url; //pointers into m_url
		// these two are only active temporarily.
		api_httpreceiver *get;
		char *error;
	}
	m_cons[MAX_MULTICONNECTS];
	int m_numcons;
	int m_newcons;
	int m_serialized;
	int	m_mstimeout;
	int	m_contryptr;
	int m_serialfailed;
	int m_useaproxy;
};

HTTPReader::HTTPReader(const char *url)
{
	m_title = 0;
	m_is_uvox = m_uvox_readpos = 0;
	m_meta_init = m_meta_interval = m_meta_pos = m_meta_size = m_meta_buf_pos = 0;
	m_meta_buf[0] = 0;
	m_err = NULL;
	m_eof = 0;
	m_read_headers = 0;
	m_content_length = 0xFFFFFFFF;
	m_accept_ranges = 0;
	m_get = NULL;
	m_serialized = 0;
	m_mstimeout = 0;
	m_contryptr = 0;
	m_newcons = 0;
	m_serialfailed = 0;
	m_useaproxy = 0;

	// TCP multiconnect
	// JF> using ; as a delimiter is vomit inducing and breaks a lot of other
	//     code. I petition we use <> to delimit, and I'm making it do that.

	m_numcons = 0;
	m_url = _strdup(url);

	int allowproxy = 1;
	char *tmpurl = m_url;
	while (m_numcons < MAX_MULTICONNECTS)
	{
		char *next = strstr( tmpurl, "<>" );
		if ( next ) *next = '\0';

		if (tmpurl[0])
		{
			m_cons[m_numcons].error = NULL;
			m_cons[m_numcons].get = NULL;
			m_cons[m_numcons++].url = tmpurl;
			if (!_strnicmp(tmpurl, "uvox:", 5)) allowproxy = 0;
			if (!_strnicmp(tmpurl, "order://", 8))
			{
				char *p = tmpurl + 8;
				// serialized mctp
				m_serialized = 1;
				m_numcons--;
				m_mstimeout = atoi(p);
				if ( m_mstimeout < 1 )
				{
					m_serialized = 0;
					m_mstimeout = 0;
				}
			}
		}

		if (!next) break;

		tmpurl = next + 2;
	}

	memset(proxybuf, 0, sizeof(proxybuf));
	proxy = NULL;
	if (allowproxy && getProxyInfo(url, proxybuf))
	{
		proxy = strstr(proxybuf, "http=");
		if (!proxy) proxy = proxybuf;
		else
		{
			proxy += 5;
			char *tp = strstr(proxy, ";");
			if (tp) *tp = 0;
		}
	}
	m_is_uvox = 0;

	if ( m_serialized && m_numcons > 1 )  // sanity check
	{
		int rval = 0, i;
		m_newcons = 1;
		// walk the list, set the url such that m_cons[0].url points to each item. try to connect
		// serialconnect returns error codes -1 on error, 0 on timeout, 1 on successfull connect
		for ( i = 0; i < m_numcons; i++ )
		{
			if ( i )
			{
				m_cons[0].url = m_cons[i].url;
			}
			rval = serialconnect(0, m_mstimeout);
			if ( rval == 1 ) break;
		}
		if ( rval < 1 )
		{
			// we didnt get a connection so...
			m_serialfailed = 1;
		}

	}
	else
		doConnect(0);
}


void HTTPReader::doConnect(int seekto)
{
	ReleaseGet(m_get);
	m_uvox_readpos = 0;

	m_eof = 0;


	int i;
	for (i = 0; i < m_numcons; i++ )
	{
		free(m_cons[i].error);
		m_cons[i].error = NULL;
		ReleaseGet(m_cons[i].get);
		m_cons[i].get = CreateGet();
		if (!m_cons[i].get)
			break;
		m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
#ifdef WINAMP_PLUGIN
		m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
#else
 # ifdef WINAMPX
		m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
#  else
		m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
#  endif
#endif
		m_cons[i].get->addheader("Accept:*/*");
		m_cons[i].get->addheader("Connection:close");
		m_cons[i].get->addheader("Ultravox-transport-type: TCP");

		if (seekto)
		{
			char buf[64] = {0};
			wsprintfA(buf, "Range:bytes=%d-", seekto);
			m_cons[i].get->addheader(buf);
		}
		else
			m_cons[i].get->addheader("icy-metadata:1");

		m_cons[i].get->connect(m_cons[i].url, !!seekto);
	}
	m_uvox_enough_bytes = 1;
}

HTTPReader::~HTTPReader()
{
	ReleaseGet(m_get);
	free(m_title);
	free(m_err);
	free(m_url);

	int i;
	for (i = 0; i < m_numcons; i++)
	{
		ReleaseGet(m_cons[i].get);
		free(m_cons[i].error);
	}
}

int HTTPReader::serialconnect(int seekto , int timeout)
{

	ReleaseGet(m_get);
	m_uvox_readpos = 0;

	m_eof = 0;

	int64_t mythen, mynow , myref;
	LARGE_INTEGER then, now, ref;


	QueryPerformanceFrequency( &ref);
	myref = ref.QuadPart;


	QueryPerformanceCounter( &then );
	mythen = then.QuadPart;


	int timer = 0;

	int i = 0;

	{
		ReleaseGet(m_cons[i].get);
		m_cons[i].get = CreateGet();
		if (m_cons[i].get == NULL)
			return 0;
		m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
#ifdef WINAMP_PLUGIN
		m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
#else
# ifdef WINAMPX
		m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
#  else
		m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
#  endif
#endif
		m_cons[i].get->addheader("Accept:*/*");
		m_cons[i].get->addheader("Connection:close");
		m_cons[i].get->addheader("Ultravox-transport-type: TCP");

		if (seekto)
		{
			char buf[64] = {0};
			wsprintfA(buf, "Range:bytes=%d-", seekto);
			m_cons[i].get->addheader(buf);
		}
		else m_cons[i].get->addheader("icy-metadata:1");

		m_cons[i].get->connect(m_cons[i].url, !!seekto);
	}
	m_uvox_enough_bytes = 1;

	int ret, status;

	if (!m_get)
	{
		if (m_err) return 0;
		int i;
		int found = 0;
		i = 0;
		while ( timer < timeout )
		{
			if (!m_cons[i].get) return 0;
			found = 1;

			QueryPerformanceCounter( &now );
			mynow = now.QuadPart;

			float profiletime = (float)(mynow - mythen);
			profiletime /= myref;
			profiletime *= 1000.0;
			timer = (int) profiletime;

			ret = m_cons[i].get->run();
			status = m_cons[i].get->get_status();

			if (ret < 0 || status < 0)
			{
				const char *t = m_cons[i].get->geterrorstr();
				if (t)
				{}
				ReleaseGet(m_cons[i].get);
				break;
			}

			if ( status > 0 )
			{
				int code = m_cons[i].get->getreplycode();
				if ( code < 200 || code > 299 )
				{
					ReleaseGet(m_cons[i].get);
					//wsprintf( m_cons[i].error, "Error: Server returned %d", code );
					break;
				}
				else
				{
					// we're in good shape, make our getter current, and delete all the gay ones
					ReleaseGet(m_get); // just in case, probably zero anyway
					m_get = m_cons[i].get;
					m_cons[i].get = NULL;

					// trash i here, but we are breaking anyway :)
					/* for (i = 0; i < m_numcons; i++)
					    {
						  delete m_cons[i].get;
					  m_cons[i].get = NULL;
					 free( m_cons[i].error );
					 m_cons[i].error = NULL;
					 }*/
					break;
				}
			}
#ifdef _WIN32
			Sleep(1);
#else
			usleep(1000);
#endif
		} // while
		if ( timer > timeout )
		{
			ReleaseGet(m_cons[i].get);
			ReleaseGet(m_get);
			return 0;
		}


		if (!m_get)
		{
			return 0;
		}
	}
	if ( m_get ) return 1;
	else return 0;
}


size_t HTTPReader::read(char *buffer, size_t len)
{
	int ret, status;

	if (!m_get)
	{
		if (m_err) return 0;
		int i;
		int found = 0;
		for (i = 0; !m_get && i < m_numcons; i++)
		{
			if (!m_cons[i].get) continue;
			found = 1;

			ret = m_cons[i].get->run();
			status = m_cons[i].get->get_status();

			if (ret < 0 || status < 0)
			{
				const char *t = m_cons[i].get->geterrorstr();
				if (t)
				{
					free(m_cons[i].error);
					m_cons[i].error = _strdup( t );
				}
				ReleaseGet(m_cons[i].get);

			}

			if ( status > 0 )
			{
				int code = m_cons[i].get->getreplycode();
				if ( code < 200 || code > 299 )
				{
					ReleaseGet(m_cons[i].get);
					m_cons[i].get = NULL;

					free(m_cons[i].error);
					m_cons[i].error = (char *)malloc( 100 );
					wsprintfA( m_cons[i].error, "Error: Server returned %d", code );
				}
				else
				{
					// we're in good shape, make our getter current, and delete all the gay ones
					ReleaseGet(m_get); // just in case, probably zero anyway
					m_get = m_cons[i].get;
					m_cons[i].get = NULL;

					// trash i here, but we are breaking anyway :)
					for (i = 0; i < m_numcons; i++)
					{
						ReleaseGet(m_cons[i].get);
						free( m_cons[i].error );
						m_cons[i].error = NULL;
					}
					break; // exit loop of connections
				}
			}
		} // loop of connections

		if (!found) // out of attempted connections heh
		{
			free( m_err );
			if (m_numcons > 1)
			{
				size_t size = 0;
				for (i = 0; i < m_numcons; i++)
					if ( m_cons[i].error ) size += strlen( m_cons[i].error ) + 1;

				m_err = (char *)malloc(size + 100);
				wsprintfA( m_err, "No Valid Multiconnect URLs (%d);", m_numcons );
				for (i = 0; i < m_numcons; i++)
				{
					strcat( m_err, m_cons[i].error );
					strcat( m_err, ";" );
					free(m_cons[i].error);
					m_cons[i].error = NULL;

				}
			}
			else
			{
				m_err = m_cons[0].error;
				m_cons[0].error = NULL;
				if (!m_err) m_err = _strdup("Connection error (Invalid URL?)");
			}
		}
		if (!m_get) return 0;
	}

	ret = m_get->run();
	status = m_get->get_status();

	if (ret > 0 && (!m_get->bytes_available() || !m_uvox_enough_bytes) && status > 1)
	{
		m_eof = 1;
	}

	if (ret < 0 || status < 0)
	{
		const char *t = m_get->geterrorstr();
		if (t)
		{
			free( m_err );
			m_err = (char *)malloc( strlen( t) + 16 );
			wsprintfA( m_err, "Error: %s", t );
			return 0;
		}
	}

	if (status > 0)
	{
		if (!m_read_headers)
		{
			if (status > 1)
			{
				const char *v = m_get->getheader("Content-Length");
				if (v) m_content_length = atoi(v);
				v = m_get->getheader("Accept-Ranges");
				if (v) while (v && *v)
					{
						if (!_strnicmp(v, "bytes", 5))
						{
							m_accept_ranges = 1;
							break;
						}
						v++;
					}
				v = m_get->getheader("icy-metaint");
				if (v)
				{
					m_meta_interval = atoi(v);
				}
				if (!m_title)
				{
					v = m_get->getheader("icy-name");
					if (v)
						m_title = _strdup(v);
				}
#ifdef WINAMP_PLUGIN
				extern void process_url(char *url);
				v = m_get->getheader("icy-url");
				if (v && !strstr(v, "shoutcast.com"))
				{
					char *p = (char *)v; while (p && *p && *p == ' ') p++;
					process_url(p);
				}
#endif

				v = m_get->getheader("content-type");
				if (v && !_stricmp(v, "misc/ultravox"))
				{
					v = m_get->getheader("ultravox-max-msg");
					if (v) m_is_uvox = atoi(v);
					if (!m_is_uvox) m_is_uvox = 16000;
				}
				if (!m_title)
				{
					v = m_get->getheader("content-disposition");
					if (v) v = strstr(v, "filename=");
					if (v)
					{
						m_title = _strdup(v + 9);
					}
				}
				m_read_headers = 1;
			}
		}

		size_t l = m_get->bytes_available();
		if (m_is_uvox)
		{
		again:
			if (l >= 6)
			{
				unsigned char buf[32768*2] = {0};
				m_get->peek_bytes((char *)buf, 6);
				if (buf[0] != 0x5A)
				{
					l--;
					m_get->get_bytes((char *)buf, 1);
					goto again;
				}
				int resqos = buf[1];
				int classtype = (buf[2] << 8) | buf[3];
				int msglen = (buf[4] << 8) | buf[5];
				if (msglen > m_is_uvox) // length is too long
				{
					m_get->get_bytes((char *)buf, 1);
					l--;
					goto again;
				}
				if (msglen + 7 <= (int)l)
				{
					m_uvox_enough_bytes = 1;

					m_get->peek_bytes((char *)buf, msglen + 7);
					if (buf[msglen + 6])
					{
						m_get->get_bytes((char *)buf, 1);
						l--;
						goto again;
					}
					if (classtype == 0x7777) // take any data for now, ignore all other frames
					{
						l = msglen - m_uvox_readpos;
						if (l > len) l = len;
						memcpy(buffer, buf + 6 + m_uvox_readpos, l);
						m_uvox_readpos += (int)l;

						if (m_uvox_readpos >= msglen)
						{
							m_uvox_readpos = 0;
							m_get->get_bytes((char *)buf, msglen + 7);

						}
						return l;

#ifdef WINAMP_PLUGIN

					}
					else if ( classtype == 0x3001 )
					{
						extern void process_metadata(char *buf, int size);
						m_get->get_bytes((char *)buf, msglen + 7);
						process_metadata((char*)buf + 6, msglen + 1);
#endif

					}
					else
					{
						m_get->get_bytes((char *)buf, msglen + 7);
					}
				}
				else
				{
					m_uvox_enough_bytes = 0;
				}
			}
			return 0;
		}
		else
		{
			if (l > len) l = len;
			m_get->get_bytes(buffer, (int)l);

			if (m_meta_interval)
			{
				int x = (int)l;
				unsigned char *buf = (unsigned char *)buffer;
				if (m_meta_size) // already in meta block
				{
					int len = min(x, m_meta_size - m_meta_buf_pos);

					memcpy(m_meta_buf + m_meta_buf_pos, buf, len);
					m_meta_buf_pos += len;

					if (m_meta_buf_pos == m_meta_size)
					{
						//	if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
						m_meta_buf_pos = 0;
						m_meta_size = 0;
						m_meta_pos = 0;
					}

					x -= len;
					if (x) memcpy(buf, buf + len, x);
				}
				else if (m_meta_pos + x > m_meta_interval) // block contains meta data somewhere in it, and we're not alreayd reading a block
				{
					int start_offs = m_meta_interval - m_meta_pos;
					int len;
					m_meta_size = ((unsigned char *)buf)[start_offs] * 16;

					len = min(x - start_offs - 1, m_meta_size);

					if (len) memcpy(m_meta_buf, buf + start_offs + 1, len);
					m_meta_buf_pos = len;

					if (m_meta_buf_pos == m_meta_size) // full read of metadata successful
					{
						x -= m_meta_size + 1;
						if (x > start_offs) memcpy(buf + start_offs, buf + start_offs + 1 + m_meta_size, x - start_offs);
#ifdef WINAMP_PLUGIN
						extern void process_metadata(char *buf, int size);
						process_metadata(m_meta_buf, m_meta_size);
#endif
						//if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
						m_meta_buf_pos = 0;
						m_meta_pos = -start_offs;
						m_meta_size = 0;
					}
					else
					{
						x = start_offs; // otherwise, there's only the first block of data
					}
				}
				if (x > 0)
				{
					m_meta_pos += x;
				}
				l = x;
			} // end of poopie metadata
		} // !uvox

#if 0
		{
			FILE *fh = fopen("c:\\dump.nsv", "ab");
			fwrite(buffer, 1, l, fh);
			fclose(fh);
		}
#endif

		return l;
	}
	return 0;
}

static void parseURL(char *url, char *lp, char *host, int *port, char *req)
{
	char *p, *np;
	/*  if (_strnicmp(url,"http://",4) &&
		    _strnicmp(url,"icy://",6) &&
		    _strnicmp(url,"sc://",6) &&
		    _strnicmp(url,"shoutcast://",12)) return;
	      */
	np = p = strstr(url, "://");
	if (!np) np = (char*)url;
	else np += 3;
	if (!p) p = (char*)url;
	else p += 3;

	while (np && *np != '/' && *np) *np++;
	if (np && *np)
	{
		lstrcpynA(req, np, 2048);
		*np++ = 0;
	}
	else strcpy(req, "/");
	np = p;
	while (np && *np != '@' && *np) np++;
	if (np && *np)
	{
		*np++ = 0;
		lstrcpynA(lp, p, 256);
		p = np;
	}
	else lp[0] = 0;
	np = p;
	while (np && *np != ':' && *np) np++;
	if (*np)
	{
		*np++ = 0;
		*port = atoi(np);
	}
	else *port = 80;
	lstrcpynA(host, p, 256);
}

int HTTPReader::getProxyInfo(const char *url, char *out)
{
#ifndef WINAMPX
	char INI_FILE[MAX_PATH] = {0};
	char *p;
	GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE));
	p = INI_FILE + strlen(INI_FILE);
	while (p >= INI_FILE && *p != '.') p--;
	strcpy(++p, "ini");
	GetPrivateProfileStringA("Winamp", "proxy", "", out, 8192, INI_FILE);
	return !!out[0];
#else
	DWORD v = 0;
	HKEY hKey;


	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\AOL\\Unagi", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
	{
		DWORD l = 4;
		DWORD t;
		if (RegQueryValueEx(hKey, "ProxyEnable", NULL, &t, (unsigned char *)&v, &l) == ERROR_SUCCESS && t == REG_DWORD)
		{
			if ( v != 2 )
			{
				l = 8192;
				if (RegQueryValueEx(hKey, "ProxyServer", NULL, &t, (unsigned char *)out, &l ) != ERROR_SUCCESS || t != REG_SZ)
				{
					v = 0;
					*out = 0;
				}
			}
			else return 0;
		}
		else v = 0;
		out[512 - 1] = 0;
		RegCloseKey(hKey);
	}
	if ( !v && m_useaproxy )
	{
		char blah[8192] = "";
		lstrcpyn(blah, url, 8192);
		char plp[512] = {0};
		char phost[512] = {0};
		int pport = 80;
		char pthereq[1024] = {0};
		parseURL(blah, plp, phost, &pport, pthereq);
		v = ResolvProxyFromURL(url, phost, out);
		if ( v < 0 ) v = 0;  // error getting proxy
	}
	if ( v > 0)
	{
		char prox[1024] = {0};
		wsprintf(prox, "PROXY: %s", out);
		SendMetadata(prox, 1);
	}
	return v;
#endif
}



class Win32FileReader : public IDataReader
{
public:
	Win32FileReader(HANDLE file) { m_hFile = file; m_eof = 0; m_err = NULL; }
	~Win32FileReader() { CloseHandle(m_hFile); }
	size_t read(char *buf, size_t len)
	{
		DWORD ob = 0;
		if (!len) return 0;
		if (!ReadFile(m_hFile, buf, (DWORD)len, &ob, NULL))
		{
			m_err = "Error calling ReadFile()!";
			return 0;
		}
		else if (!ob) m_eof = true;
		return ob;
	}
bool iseof() { return m_eof; }
	bool canseek() { return 1; }
	int seek(uint64_t newpos)
	{
		LARGE_INTEGER li;
		li.QuadPart = newpos;
		li.LowPart = SetFilePointer (m_hFile, li.LowPart, &li.HighPart, SEEK_SET);

		if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
		{
			li.QuadPart = -1;
		}

		return li.QuadPart== ~0;
	}

	uint64_t getsize()
	{
		LARGE_INTEGER position;
		position.QuadPart=0;
		position.LowPart = GetFileSize(m_hFile, (LPDWORD)&position.HighPart); 	

		if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
			return INVALID_FILE_SIZE;
		else
			return position.QuadPart;
	}

	char *geterror() { return m_err; }
private:
	HANDLE m_hFile;
	bool m_eof;
	char *m_err;
};


#define VAR_TO_FPOS(fpos, var) (fpos) = (var)
#define FPOS_TO_VAR(fpos, typed, var) (var) = (typed)(fpos)
class FileReader : public IDataReader
{
public:
	FileReader(FILE *file) { fp = file;  m_err = NULL; }
	~FileReader() { fclose(fp); }
	size_t read(char *buf, size_t len)
	{
		size_t ob;
		if (!len) return 0;
		ob = fread(buf, 1, len, fp);
		if (!ob && ferror(fp))
		{
			m_err = "Error calling fread()!";
			return 0;
		}
		return ob;
	}
	bool iseof() { return !!feof(fp); }
	bool canseek() { return 1; }
	int seek(uint64_t newpos)
	{
		fpos_t pos= newpos;
		VAR_TO_FPOS(pos, newpos);
		return fsetpos(fp, &pos);
	}

	unsigned __int64 getsize()
	{
		struct stat s;
		if (fstat(fileno(fp), &s) < 0) 
		{ 
			m_err = "Error calling fread()!";
			return 0;
		}
		return s.st_size;
	}

	char *geterror() { return m_err; }
private:
	FILE *fp;
	char *m_err;
};


IDataReader *CreateReader(const char *url)
{
	if (strstr(url, "://")) return new HTTPReader(url);
#ifdef _WIN32
	HANDLE hFile = CreateFileA(url, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
	                          OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
		return new Win32FileReader(hFile);
#else
	FILE *fp = fopen(url, "r");
	if (fp)
		return new FileReader(fp);
#endif
	return NULL;
}