#include <precomp.h>
#include "wa2core.h"
#include "wa2frontend.h"
#include "../winamp/wa_ipc.h"
#include "wa2pledit.h"
#include "../nu/AutoWide.h"
#include "gen.h"
#include "../nu/ns_wc.h"
#include "../Agave/Language/api_language.h"
#include "../Winamp/in2.h"
#include "resource.h"
#include "../nu/AutoChar.h"
#include <shlwapi.h>



// {72409F84-BAF1-4448-8211-D84A30A1591A}
static const GUID eqConfigGroupGUID = 
{ 0x72409f84, 0xbaf1, 0x4448, { 0x82, 0x11, 0xd8, 0x4a, 0x30, 0xa1, 0x59, 0x1a } };

// -----------------------------------------------------------------------------------------------------------------
// Core implementation for wa2
// -----------------------------------------------------------------------------------------------------------------

#define TIMER_POLL 0x8971

Core *g_Core = NULL;
api_core *g_core = NULL;
int g_coreref = 0;

// this is called by the library when api->core_create is called

api_core *createCustomCoreApi()
{
	g_coreref++;
	if (g_core == NULL) { g_Core = new Core(); g_core = g_Core; }
	return g_core;
}

// this is called when api->core_destroy is called

void destroyCustomCoreApi(api_core *core)
{
	if (--g_coreref == 0)
	{
		delete g_Core;
		g_Core = NULL;
		g_core = NULL;
	}
}

Core::Core()
{
	waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID);
	if (sf)
		config = (Agave::api_config *)sf->getInterface();

	m_lastpeentry = -1;
	m_laststatus = -1;
	m_lastpos = -1;
	m_lastvol = -1;
	m_lastpan = -1;
	m_lasttitle = L"";
	m_lastsamplerate = -1;
	m_lastchan = -1;
	m_lastbitrate = -1;
	for (int i = 0;i < 10;i++)
		m_lasteqband[i] = 0xdead;
	m_lasteq = -1;
	m_lasteqauto = -1;
	m_lasteqpreamp = -1;
	m_lastfreqband = -1;
	// timerclient_setTimer(TIMER_POLL, 250);
	gotCallback(IPC_CB_MISC_TITLE, 1);
	gotCallback(IPC_CB_MISC_VOLUME, 1);
	gotCallback(IPC_CB_MISC_STATUS, 1);
	gotCallback(IPC_CB_MISC_EQ, 1);
	gotCallback(IPC_CB_MISC_INFO, 1);
	gotCallback(IPC_CB_MISC_TITLE_RATING, 1);
}

Core::~Core()
{
		waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID);
	if (sf)
		sf->releaseInterface(config);
	//timerclient_killTimer(TIMER_POLL);
}

void Core::addCallback(CoreCallback *cb)
{
	if (cb == NULL) return ;
	if (callbacks.haveItem(cb)) return ;
	callbacks.addItem(cb);
	cb->ccb_notify(CoreCallback::REGISTER);
}

void Core::delCallback(CoreCallback *cb)
{
	if (cb == NULL) return ;
	if (callbacks.haveItem(cb))
	{
		cb->ccb_notify(CoreCallback::DEREGISTER);
		callbacks.removeItem(cb);
	}
}

void Core::sendCoreCallback(int message, int param1, int param2)
{
	ReentryFilter filter(&rf, message);
	int l = callbacks.getNumItems();
	for (int i = 0; i < l;i++)
		callbacks[i]->ccb_notify(message, param1, param2);
}

int Core::getStatus()
{
	return m_laststatus;
}

void Core::gotCallback(int wParam, int forcecb)
{
	if(wParam == IPC_CB_MISC_TITLE_RATING){
		forcecb = 1;
	}
	switch (wParam)
	{
	case IPC_CB_MISC_TITLE:
	case IPC_CB_MISC_TITLE_RATING:
		{
			// check for title change
			wa2.invalidateCache();
			StringW cur_title=wa2.GetCurrentTitle();
			if (cur_title.isempty()) 
				cur_title = L"";

			StringW cur_file = wa2.GetCurrentFile();

			if (!m_lastfile.iscaseequal(cur_file) || forcecb)
			{
				m_lastfile.swap(cur_file);
				sendCoreCallback(CoreCallback::URLCHANGE, (intptr_t)m_lastfile.getValue());
			}

			if (!m_lasttitle.iscaseequal(cur_title) || forcecb)
			{
				m_lasttitle.swap(cur_title);
				//Martin> i dunno why we send a INFOCHANGE Callback here, but if we want to send this
				//			callback try to deliver the correct data 
				//sendCoreCallback((forcecb==2?CoreCallback::INFOCHANGE:CoreCallback::TITLECHANGE),
				//				 (int)m_lasttitle.getValue(),(wParam == IPC_CB_MISC_TITLE_RATING));

				if (forcecb==2) // this has led to INFOCHANGE Callback
				{
					sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)getSongInfoText(), (wParam == IPC_CB_MISC_TITLE_RATING));
				}
				sendCoreCallback(CoreCallback::TITLECHANGE, (intptr_t)m_lasttitle.getValue(), (wParam == IPC_CB_MISC_TITLE_RATING));
			}

			int plentry = wa2.getCurPlaylistEntry();
			int curpeentry = plentry + 1;
			if (curpeentry != m_lastpeentry)
			{
				Wa2PlaylistEditor::_onNewCurrentIndex(curpeentry);
				m_lastpeentry = curpeentry;
			}
		}
		break;
	case IPC_CB_MISC_STATUS:
		{
			int cur_status;
			int resume = 0;
			if (wa2.isPaused()) cur_status = STATUS_PAUSE;
			else if (wa2.isPlaying())
			{
				if (m_laststatus == STATUS_PAUSE)
					resume = 1;
				else
				{
					m_lastsamplerate = -1;
					m_lastbitrate = -1;
					m_lastchan = -1;
				}
				cur_status = STATUS_PLAY;
			}
			else cur_status = STATUS_STOP;

			if (m_laststatus != cur_status || forcecb)
			{
				m_laststatus = cur_status;
				switch (cur_status)
				{
				case STATUS_PLAY: sendCoreCallback(resume ? CoreCallback::UNPAUSED : CoreCallback::STARTED); break;
				case STATUS_STOP:
					{
						m_lastsamplerate = 0;
						m_lastchan = 0;
						m_lastbitrate = 0;
						sendCoreCallback(CoreCallback::STOPPED);
						break;
					}
				case STATUS_PAUSE: sendCoreCallback(CoreCallback::PAUSED); break;
				}
			}
		}
		break;
	case IPC_CB_MISC_VOLUME:
		{
			// check for volume change
			int cur_vol = wa2.getVolume();
			if (m_lastvol != cur_vol || forcecb)
			{
				m_lastvol = cur_vol;
				sendCoreCallback(CoreCallback::VOLCHANGE, cur_vol);
			}
			int cur_pan = wa2.getPanning();
			if (m_lastpan != cur_pan || forcecb)
			{
				m_lastpan = cur_pan;
				sendCoreCallback(CoreCallback::PANCHANGE, cur_pan);
			}
		}
		break;
	case IPC_CB_MISC_EQ:
		{
			// check if the band frequencies changed (winamp vs ISO)
			int new_freqband = config->GetInt(eqConfigGroupGUID, L"frequencies", 0);
			if (m_lastfreqband != new_freqband)
			{
				bool sendCallback = m_lastfreqband != -1;
				
				m_lastfreqband = new_freqband; // set before the callback so we don't run into any issues if the callback triggers another EQ change message
				if (sendCallback)
					sendCoreCallback(CoreCallback::EQFREQCHANGE, new_freqband, 0);
			}

			// check for eq change
			for (int i = 0;i < 10;i++)
			{
				int cur_band = wa2.getEqData(WA2_EQDATA_FIRSTBAND + i);
				if (cur_band == 63 - ((m_lasteqband[i] + 127) / 4)) cur_band = m_lasteqband[i];
				else cur_band = (63 - cur_band) * 4 - 127;
				if (m_lasteqband[i] != cur_band || forcecb)
				{
					m_lasteqband[i] = cur_band;
					sendCoreCallback(CoreCallback::EQBANDCHANGE, i, cur_band);
				}
			}
			int cur_eq = wa2.getEqData(WA2_EQDATA_ENABLED);
			if (m_lasteq != cur_eq)
			{
				m_lasteq = cur_eq;
				sendCoreCallback(CoreCallback::EQSTATUSCHANGE, cur_eq);
			}
			int cur_eqauto = wa2.getEqData(WA2_EQDATA_AUTO);
			if (m_lasteqauto != cur_eqauto || forcecb)
			{
				m_lasteqauto = cur_eqauto;
				sendCoreCallback(CoreCallback::EQAUTOCHANGE, cur_eqauto);
			}
			int cur_eqpreamp = wa2.getEqData(WA2_EQDATA_PREAMP);
			if (cur_eqpreamp == 63 - ((cur_eqpreamp + 127) / 4)) { /*cur_eqpreamp = cur_eqpreamp;*/ }
			else cur_eqpreamp = (63 - cur_eqpreamp) * 4 - 127;
			if (m_lasteqpreamp != cur_eqpreamp || forcecb)
			{
				m_lasteqpreamp = cur_eqpreamp;
				sendCoreCallback(CoreCallback::EQPREAMPCHANGE, cur_eqpreamp);
			}
		}
		break;

	case IPC_CB_MISC_INFO:
		{
			int realrate = wa2.getSamplerate();
			int brate = wa2.getBitrate();
			int ch = wa2.getChannels();
			int any = 0;
			if (realrate != m_lastsamplerate || forcecb)
			{
				m_lastsamplerate = realrate;
				sendCoreCallback(CoreCallback::SAMPLERATECHANGE, realrate);
				any = 1;
			}
			if (brate != m_lastbitrate || forcecb)
			{
				m_lastbitrate = brate;
				sendCoreCallback(CoreCallback::BITRATECHANGE, m_lastbitrate);
				any = 1;
			}
			if (ch != m_lastchan || forcecb)
			{
				m_lastchan = ch;
				sendCoreCallback(CoreCallback::CHANNELSCHANGE, m_lastchan);
				any = 1;
			}
			//DebugString("Got IPC_CB_MISC_INFO callback, numchans = %d, samplerate = %d, bitrate = %d\n", m_lastchan, m_lastsamplerate, m_lastbitrate);
			if (any)
			{
				StringW txt = getSongInfoText();
				sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)txt.getValue());
			}
			break;
		}
	}
}

StringW infotext;

const wchar_t *Core::getSongInfoText()
{
	infotext = L"";
	int srate = wa2.getSamplerate();
	int brate = wa2.getBitrate();
	int ch = wa2.getChannels();

	if (srate == 0 && brate == 0)
	{
		return L"";
	}

	infotext = StringPrintfW(L"%dkbps", brate);

	if (ch == 1)
	{
		infotext += L" mono";
	}
	else if (ch == 2)
	{
		infotext += L" stereo";
	}
	else if (ch > 2)
	{
		infotext += StringPrintfW(L" %d Channels", ch);
	}

	infotext += StringPrintfW(L" %.1fkHz", (float)srate/1000.0f);

	if (wa2.isPlayingVideo())
	{
		infotext.prepend(L"Video ");
	}
	return infotext;
}

StringW infotextTranslated;

const wchar_t *Core::getSongInfoTextTranslated()
{
	infotextTranslated = L"";
	int srate = wa2.getSamplerate();
	int brate = wa2.getBitrate();
	int ch = wa2.getChannels();

	if (srate == 0 && brate == 0)
	{
		return L"";
	}

	infotextTranslated = StringPrintfW(L"%d%s", brate,WASABI_API_LNGSTRINGW(IDS_KBPS));

	if (ch == 1)
	{
		infotextTranslated += WASABI_API_LNGSTRINGW(IDS_MONO);
	}
	else if (ch == 2)
	{
		infotextTranslated += WASABI_API_LNGSTRINGW(IDS_STEREO);
	}
	else if (ch > 2)
	{
		infotextTranslated += StringPrintfW(WASABI_API_LNGSTRINGW(IDS_X_CHANNELS), ch);
	}

	infotextTranslated += StringPrintfW(L" %.1f", (float)srate/1000.0f);
	infotextTranslated += WASABI_API_LNGSTRINGW(IDS_KHZ);// (doing this in the StringPrintfW(..) above causes a crash even in a seperate buffer)

	if (wa2.isPlayingVideo())
	{
		infotextTranslated.prepend(StringPrintfW(L"%s ",WASABI_API_LNGSTRINGW(IDS_VIDEO)));
	}

	return infotextTranslated;
}

void Core::userButton(int button)
{
	int mod = Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_SHIFT : (Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_CTRL : WA2_USERBUTTONMOD_NONE);
	switch (button)
	{
	case UserButton::PLAY: wa2.userButton(WA2_USERBUTTON_PLAY, mod); break;
	case UserButton::STOP: wa2.userButton(WA2_USERBUTTON_STOP, mod); break;
	case UserButton::PAUSE: wa2.userButton(WA2_USERBUTTON_PAUSE, mod); break;
	case UserButton::NEXT: wa2.userButton(WA2_USERBUTTON_NEXT, mod); break;
	case UserButton::PREV: wa2.userButton(WA2_USERBUTTON_PREV, mod); break;
	}
}

void Core::setVolume(int vol)
{
	wa2.setVolume(vol);
}

int Core::getVolume()
{
	return wa2.getVolume();
}

void Core::setPosition(int ms)
{
	wa2.seekTo(ms);
	sendCoreCallback(CoreCallback::SEEKED, ms);
}

int Core::getPosition()
{
	if (m_laststatus == STATUS_STOP) return -1;
	return wa2.getPosition();
}

int Core::getLength()
{
	if (m_laststatus == STATUS_STOP) return -1;
	return wa2.getLength();
}

void Core::setPanning(int p)
{
	if (p > 127) p = 127;
	wa2.setPanning(p);
}

int Core::getPanning()
{
	return wa2.getPanning();
}

void Core::setShuffle(int shuffle)
{
	wa2.setShuffle(shuffle);
}

int Core::getShuffle()
{
	return wa2.getShuffle();
}

void Core::setRepeat(int repeat)
{
	static int myset = 0;
	if (!myset)
	{
		myset = 1;
		int manadv = 0;
		int rep = 0;
		if (repeat == 0)
		{
			rep = 0;
			manadv = 0;
		}
		else if (repeat > 0)
		{
			rep = 1;
			manadv = 0;
		}
		else if (repeat < 0)
		{
			rep = 1;
			manadv = 1;
		}
		if (!!wa2.getRepeat() != !!rep) wa2.setRepeat(rep);
		if (!!wa2.getManualPlaylistAdvance() != !!manadv) wa2.setManualPlaylistAdvance(manadv);
		myset = 0;
	}
}

int Core::getRepeat()
{
	int manadv = wa2.getManualPlaylistAdvance();
	int rep = wa2.getRepeat();
	int _v = (rep && manadv) ? -1 : rep;
	return _v;
}

int Core::getSamplerate(int wa2_getinfo)
{
	return wa2.getInfo(WA2_GETINFO_SAMPLERATE);
}

int Core::getBitrate(int wa2_getinfo)
{
	return wa2.getInfo(WA2_GETINFO_BITRATE);
}

int Core::getChannels(int wa2_getinfo)
{
	return wa2.getInfo(WA2_GETINFO_CHANNELS);
}

int Core::getEqBand(int band)
{
	//  int v = 63-wa2.getEqData(WA2_EQDATA_FIRSTBAND+band) * 4 - 127;
	//  v = MIN(-127, v);
	//  v = MAX(127, v);
	return m_lasteqband[band]; //(v);
}

void Core::setEqBand(int band, int val)
{
	val = MIN(127, MAX( -127, val));
	m_lasteqband[band] = val;
	int v = 63 - ((val + 127) / 4);
	v = MIN(63, v);
	v = MAX(0, v);
	wa2.setEqData(WA2_EQDATA_FIRSTBAND + band, v);
	sendCoreCallback(CoreCallback::EQBANDCHANGE, band, val);
}

int Core::getEQStatus()
{
	return wa2.getEqData(WA2_EQDATA_ENABLED);
}

void Core::setEQStatus(int enable)
{
	wa2.setEqData(WA2_EQDATA_ENABLED, enable);
}

int Core::getEQAuto()
{
	return wa2.getEqData(WA2_EQDATA_AUTO);
}

void Core::setEQAuto(int enable)
{
	wa2.setEqData(WA2_EQDATA_AUTO, enable);
}

int Core::getEQPreamp()
{
	//  int v = 63-wa2.getEqData(WA2_EQDATA_PREAMP) * 4 - 127;
	//  v = MIN(-127, v);
	//  v = MAX(127, v);
	return m_lasteqpreamp;
}

void Core::setEQPreamp(int val)
{
	val = MIN(127, MAX( -127, val));
	m_lasteqpreamp = val;
	int v = 63 - ((val + 127) / 4);
	v = MIN(63, v);
	v = MAX(0, v);
	wa2.setEqData(WA2_EQDATA_PREAMP, v);
	sendCoreCallback(CoreCallback::EQPREAMPCHANGE, val);
}

const wchar_t *Core::getTitle()
{
	return m_lasttitle; // faster to use our cached ver
	//  int plentry = wa2.getCurPlaylistEntry();
	//  if (plentry == -1) return NULL;
	//  return wa2.getTitle(plentry);
}

void Core::setTitle(const wchar_t * new_title)
{
	StringW title = new_title;
	if(title.isempty()) 
		title = L"";
	m_lasttitle = title;
}

const wchar_t *Core::getPlaystring()
{
	m_playstring = wa2.GetCurrentFile();
	if (m_playstring.getValue() == NULL) m_playstring = L"";
	return m_playstring;
}

int Core::getCurPlaylistEntry()
{
	return wa2.getCurPlaylistEntry();
}

// -----------------------------------------------------------------------------------------------------------------
// api_core implementation
// -----------------------------------------------------------------------------------------------------------------

// this pointer is set automagically by ApiInit
api_core *coreApi = NULL;

const wchar_t *Core::core_getSupportedExtensions()
{
	return L"mp3\x0";
}

const wchar_t *Core::core_getExtSupportedExtensions()
{
	return L"mp3\x0";
}

CoreToken Core::core_create()
{
	return 0;
}

int Core::core_free(CoreToken core)
{
	return 0;
}

int Core::core_setNextFile(CoreToken core, const wchar_t *playstring)
{
	return 0;
}

int Core::core_getStatus(CoreToken core)
{
	switch (getStatus())
	{
	case STATUS_STOP : return 0;
	case STATUS_PLAY : return 1;
	case STATUS_PAUSE : return -1;
	}
	return 0; // dunno, stopped i guess...
}

const wchar_t *Core::core_getCurrent(CoreToken core)
{
	return getPlaystring();
}

int Core::core_getCurPlaybackNumber(CoreToken core)
{
	return getCurPlaylistEntry();
}

int Core::core_getPosition(CoreToken core)
{
	return getPosition();
}

int Core::core_getWritePosition(CoreToken core)
{
	return getPosition();
}

int Core::core_setPosition(CoreToken core, int ms)
{
	setPosition(ms);
	return 1;
}

int Core::core_getLength(CoreToken core)
{
	return getLength();
}

int Core::core_getPluginData(const wchar_t *playstring, const wchar_t *name, wchar_t *data, int data_len, int data_type)
{
	return 0;
}

unsigned int Core::core_getVolume(CoreToken core)
{
	return getVolume();
}

void Core::core_setVolume(CoreToken core, unsigned int vol)
{
	setVolume(vol);
}

int Core::core_getPan(CoreToken core)
{
	return getPanning();
}

void Core::core_setPan(CoreToken core, int val)
{
	setPanning(val);
}

void Core::core_addCallback(CoreToken core, CoreCallback *cb)
{
	addCallback(cb);
}

void Core::core_delCallback(CoreToken core, CoreCallback *cb)
{
	delCallback(cb);
}

int Core::core_getVisData(CoreToken core, void *dataptr, int sizedataptr)
{
	int ret = 75*2;
	// added this to attempt to cope with some poor / old input plug-ins
	// which keep cropping up in the plug-in crash reports from users...
	try
	{
		// todo
		if (wa2.export_sa_get)
		{
			if (sizedataptr >= (75*2 + 8))
				wa2.export_sa_get((char *)dataptr);
			else
			{
				char data[75*2 + 8];
				char *p = wa2.export_sa_get(data);
				if (p) memcpy(dataptr, p, min(2*75, sizedataptr));
			}
		}
		else if (wa2.export_sa_get_deprecated)
		{
			char *p = wa2.export_sa_get_deprecated();
			if (p) memcpy(dataptr, p, min(2*75, sizedataptr));
		}
	}
	catch(...)
	{
		ret = 0;
	}
	return ret;
}

int Core::core_getLeftVuMeter(CoreToken core)
{
	if (wa2.export_vu_get)
	{
		int vu = wa2.export_vu_get(0);
		if (vu != -1)
			return vu;
	}
	 if (wa2.export_sa_get)
	{
		char data[75*2 + 8] = {0};
		char *p = (char *)wa2.export_sa_get(data);
		if (!p) return 0;

		int m = 0;
		for (int i = 75;i < 150;i++) // this is very gay but workish
			m = max(abs(m), p[i]);

		return MIN(255, m*16);
	}
	else if (wa2.export_sa_get_deprecated)
	{
		char *p = (char *)wa2.export_sa_get_deprecated();
		if (!p) return 0;

		int m = 0;
		for (int i = 75;i < 150;i++) // this is very gay but workish
			m = max(abs(m), p[i]);

		return MIN(255, m*16);
	}
	else return 0;

}

int Core::core_getRightVuMeter(CoreToken core)
{
	if (wa2.export_vu_get)
	{
		int vu = wa2.export_vu_get(1);
		if (vu == -1)
			vu = wa2.export_vu_get(0);
		if (vu != -1)
			return vu;
	}

	return core_getLeftVuMeter(core);
}

int Core::core_registerSequencer(CoreToken core, ItemSequencer *seq)
{
	return 0;
}

int Core::core_deregisterSequencer(CoreToken core, ItemSequencer *seq)
{
	return 0;
}

void Core::core_userButton(CoreToken core, int button)
{
	userButton(button);
}

int Core::core_getEqStatus(CoreToken core)
{
	return getEQStatus();
}

void Core::core_setEqStatus(CoreToken core, int enable)
{
	setEQStatus(enable);
}

int Core::core_getEqPreamp(CoreToken core)
{
	return getEQPreamp();
}

void Core::core_setEqPreamp(CoreToken core, int pre)
{
	setEQPreamp(pre);
}

int Core::core_getEqBand(CoreToken core, int band)
{
	return getEqBand(band);
}

void Core::core_setEqBand(CoreToken core, int band, int val)
{
	setEqBand(band, val);
}

int Core::core_getEqAuto(CoreToken core)
{
	return getEQAuto();
}

void Core::core_setEqAuto(CoreToken core, int enable)
{
	setEQAuto(enable);
}

void Core::core_setCustomMsg(CoreToken core, const wchar_t *text)
{}

void Core::core_registerExtension(const wchar_t *extensions, const wchar_t *extension_name, const wchar_t *family)
{}


const wchar_t *Core::core_getDecoderName(const wchar_t *filename)
{
	wchar_t fn[MAX_PATH+3] = {0};
	WCSNPRINTF(fn, MAX_PATH, L"hi.%s", filename);
	In_Module *player = (In_Module *)wa2.CanPlay(fn);
	if (player)
	{
		// cope nicely with the 5.64+ input plug-ins with unicode descriptions
		static wchar_t decoder_name[512];
		int ver = ((player->version & ~IN_UNICODE) & ~IN_INIT_RET);
		if (ver == IN_VER)
		{
			StringCchCopyW(decoder_name, 512, (wchar_t *)player->description);
		}
		else
		{
			MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, decoder_name, 512);
		}
		return decoder_name;
	}
	return NULL;
}

const wchar_t *Core::core_getExtensionFamily(const wchar_t *extension)
{
	wchar_t fn[MAX_PATH] = {0};
	WCSNPRINTF(fn, MAX_PATH, L"hi.%s", extension);
	In_Module *player = (In_Module *)wa2.CanPlay(fn);
	if (player)
	{
		/*
		char *p = player->FileExtensions;
		const wchar_t *realExt = PathFindExtensionW(fn);
		if (realExt && *realExt)
			realExt++;
		AutoChar ext(realExt);
		while (p && *p)
		{
			char *b = p;
			char *c;
			do
			{
				char d[20] = {0};
				lstrcpyn(d, b, 15);
				if ((c = strstr(b, ";")))
				{
					if ((c-b)<15)
						d[c - b] = 0;
				}

				if (!_stricmp(ext, d))
				{
					p += lstrlen(p) + 1; // skip to the name
					MultiByteToWideCharSZ(CP_ACP, 0, p, -1, family, 512);
					return family;
				}
				b = c + 1;
			}
			while (c);
			p += lstrlen(p) + 1;
			if (!*p) break;
			p += lstrlen(p) + 1;
		}
	
*/
		static wchar_t family[512];
		MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, family, 512);
		return family;
	}
	return NULL;
}

void Core::core_unregisterExtension(const wchar_t *extensions)
{}

const wchar_t *Core::core_getTitle(CoreToken core)
{
	return getTitle();
}

void Core::core_setTitle(const wchar_t *new_title)
{
	setTitle(new_title);
}

int Core::core_getRating()
{
	return wa2.getCurTrackRating();
}

void Core::core_setRating(int newRating)
{
	wa2.setCurTrackRating(newRating);
}