#include <InterfaceKit.h>
#include "Player.h"
#include "SID.h"
#include "CPU.h"
#include "ZButton.h"
#include "ZSlider.h"
#include "panprop.h"
#include "volprop.h"
#include "SliderBits.h"

const uint32 SID_FILTER = 'filt';
const uint32 SID_MORE	 = 'more';
const uint32 SID_LESS	 = 'less';

static uchar large_icon[] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x07,0x07,0x07,0x14,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x14,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,
0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x14,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,
0x07,0x07,0x07,0x3f,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x14,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,
0x07,0x07,0x3f,0x0f,0x0f,0x19,0x3f,0x3f,0x0e,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,
0x07,0x0e,0x3f,0x3f,0x19,0x0f,0x0f,0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,
0x07,0x07,0x07,0x07,0x07,0x07,0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x3f,
0x3f,0x19,0x0f,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x3f,0x0f,
0x07,0x0f,0x19,0x3f,0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x3f,0x3f,0x19,
0x0f,0x07,0x07,0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x14,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0f,
0x19,0x3f,0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x14,0x14,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x3f,0x3f,0x19,0x0f,0x07,
0x07,0x07,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x00,0x14,0x14,0xff,
0xff,0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x0f,0x3f,0x07,0x0f,0x19,0x3f,
0x3f,0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x14,0xff,0x14,0x14,0xff,
0xff,0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x0f,0x3f,0x3f,0x07,0x07,0x07,0x07,
0x07,0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x14,0x14,0xff,0x14,0x14,0xff,
0xff,0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x3f,0x3f,0x19,0x0f,0x07,0x07,0x07,0x07,
0x07,0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x00,0x14,0x14,0xff,0x14,0x14,0xff,
0xff,0xff,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x0f,0x19,0x3f,0x3f,0x07,0x07,
0x07,0x07,0x0a,0x0f,0x1e,0x0a,0x00,0x00,0x14,0xff,0x14,0x14,0xff,0x14,0x14,0xff,
0xff,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x00,0x00,0x00,0x07,0x07,0x07,0x07,0x07,
0x07,0x07,0x0f,0xff,0x0a,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0x3f,0x3f,0x3f,0x12,0x09,0x07,0x07,0x00,0x00,0x00,0x00,0x1e,0x07,0x07,0x07,0x07,
0x07,0x0f,0xff,0x00,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0x00,0x09,0x12,0x3f,0x3f,0x3f,0x00,0x00,0x00,0x00,0x00,0x1e,0x07,0x07,0x07,0x0a,
0x0f,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x07,0x07,0x07,0x0a,0x0f,
0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x12,0x07,0x0a,0x0f,0x1e,
0xff,0x00,0xd2,0xd2,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x3f,0x3f,0x3f,0x1e,0x0a,
0x00,0xd2,0xd2,0xd2,0x00,0x00,0x11,0x11,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,
0xff,0x14,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x18,0x14,0x00,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0x11,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,
0xff,0x14,0x14,0xff,0xff,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x12,0x09,0x00,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x11,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x14,0x00,0x00,0x00,0x07,0x12,0x09,0x00,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0x00,0x2c,0x2c,0x2c,0x2c,0x00,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x14,0x07,0x12,0x09,0xff,
0x00,0xd2,0xd2,0xd2,0x00,0x00,0xff,0xff,0x00,0x2c,0x2c,0x2c,0x2c,0x2c,0x00,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0xd2,0xd2,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0x11,0x11,0x11,0x11,0x11,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0x11,0x00,0x00,0x00,0x00,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x11,0x11,0x11,0x11,0xff,0xff,0xff,0xff,0xff,
};
static uchar small_icon[] = {
0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0x00,0xd2,0xd2,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,
0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0x11,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0x11,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x11,0xff,0xff,
0x00,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0x00,0x2c,0x2c,0x2c,0x2c,0x00,0xff,0xff,
0x00,0xd2,0xd2,0xd2,0x00,0x00,0xff,0xff,0x00,0x2c,0x2c,0x2c,0x2c,0x2c,0x00,0xff,
0xff,0x00,0xd2,0xd2,0xd2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x11,
0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0x11,0x11,0x11,0x11,0x11,
0xff,0xff,0x00,0xd2,0xd2,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0xd2,0xd2,0xd2,0xd2,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0x11,0x00,0x00,0x00,0x00,0x00,0x11,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0x11,0x11,0x11,0x11,0x11,0xff,0xff,0xff,0xff,0xff,
};

char *mimelist[]=
{
	"audio/psid",
	NULL
};

enum						// Private methods
{
	SID_SELECTSONG=0x1000,
	SID_LOADPSID,
};

class SIDView;

struct PSIDHeader {
	ULONG	id;				// 'PSID'
	UWORD	version;		// Version 1 or 2
	UWORD	length;			// Length of header
	UWORD	start;			// C64 load address
	UWORD	init;			// C64 address of init routine
	UWORD	main;			// C64 address of replay routine
	UWORD	number;			// Number of subsongs
	UWORD	defsong;		// Main subsong number (1..255)
	UWORD	speedhi;		// Speed flags (1 bit/song)
	UWORD	speedlo;
	UBYTE	name[32];		// Module name
	UBYTE	author[32];		// Author
	UBYTE	copyright[32];	// Copyright info
	UWORD	flags;			// Flags (only in version 2 header)
	ULONG	reserved;
};

struct instance_struct {	// We really do not need globals...
	BMessage *prefs;		// BMessage containg the preferences.
	SIDView *view;
	char ModuleName[256];
	float panset[4];
	float volset[4];
	float globalvolume;		// Globalvolume set by DualPlayer
	Object *dual_object;	// Callback object to DualPlayer
	UBYTE *the_ram;			// Pointer to 64K RAM
	MOS6510 *the_cpu;		// Pointer to 6510
	MOS6581 *the_sid;		// Pointer to 6581
	int current_song;		// Song currently playing
	int number_of_songs;	// Number of songs in module
	UWORD init_adr,play_adr;// C64 init/play routine addresses
	ULONG song_speeds;		// Speed flags
	int current_freq;		// Current replay frequency in Hz
	char module_name[32];	// Module name
	char author[32];		// Author
	char copyright[32];		// Copyright info
	PSIDHeader header;		// Complete header
};

class SIDView : public BView
{
	private:
	
		BCheckBox *filter;
		ZButton	*more;
		ZButton	*less;
		bool first;
		BStringView *label1;
		BStringView *label2;
		BView *top;
		instance_struct *inst;

	public:
				SIDView(instance_struct *i):BView(BRect(0,0,199,189),"MODView",NULL,B_WILL_DRAW)
				{
					inst=i;
					
					filter = new BCheckBox(BRect(0,0,199,10),"","SID Filter",new BMessage(SID_FILTER));
					more = new ZButton(BRect(0,129,99,147),"Slower","Slower",NULL,new BMessage(SID_LESS),B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
					less = new ZButton(BRect(101,129,199,147),"Faster","Faster",NULL,new BMessage(SID_MORE),B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);

					// Create slider knobs
					BBitmap *myknob;
					BBitmap *myknob2;
					BRect kr;
				
					kr.Set(0, 0, KNOB_WIDTH-1, KNOB_HEIGHT-1);
					myknob = new BBitmap(kr, B_COLOR_8_BIT);
					myknob->SetBits((char*)knob, myknob->BitsLength(), 0, B_COLOR_8_BIT);
				
					kr.Set(0, 0, KNOB_WIDTH2-1, KNOB_HEIGHT2-1);
					myknob2 = new BBitmap(kr, B_COLOR_8_BIT);
					myknob2->SetBits((char*)knob2, myknob2->BitsLength(), 0, B_COLOR_8_BIT);

					top = new BView(BRect(2, 20, 199, 140), "", B_FOLLOW_ALL, B_WILL_DRAW);

					// Volume/panning sliders
					top->AddChild(label1 = new BStringView(BRect(0, 1, 100, 14), "", "Panning"));					
					top->AddChild(inst->the_sid->panprop[0] = new panprop(0.01, 0.374, -1, -1, myknob));
					top->AddChild(inst->the_sid->panprop[1] = new panprop(0.01, 0.5, -1, -1, myknob));
					top->AddChild(inst->the_sid->panprop[2] = new panprop(0.01, 0.627, -1, -1, myknob));
					top->AddChild(inst->the_sid->panprop[3] = new panprop(0.01, 0.5, -1, -1, myknob));
					
					top->AddChild(label2 = new BStringView(BRect(110, 1, 170+KNOB_WIDTH, 14), "", "Volume"));					
					top->AddChild(inst->the_sid->volprop[0] = new volprop(-1, -1, 0.01, 1, myknob2));
					top->AddChild(inst->the_sid->volprop[1] = new volprop(-1, -1, 0.01, 1, myknob2));
					top->AddChild(inst->the_sid->volprop[2] = new volprop(-1, -1, 0.01, 1, myknob2));
					top->AddChild(inst->the_sid->volprop[3] = new volprop(-1, -1, 0.01, 1, myknob2));
					
					first=TRUE;

					AddChild(filter);
					AddChild(more);
					AddChild(less);
				};
		
	void		Draw(BRect frame)
				{
					const char *str1="©1997 by Christian Bauer";
					const char *str2="Stereo support by Marco Nelissen"; 
					const char *str3="DualPlayer implementation by Tinic Urou";
					SetFont(be_plain_font);
					SetFontSize(9);
					SetDrawingMode(B_OP_OVER);
					DrawString(str1,BPoint((200.0-StringWidth(str1))/2.0,164.0));
					DrawString(str2,BPoint((200.0-StringWidth(str2))/2.0,175.0));
					DrawString(str3,BPoint((200.0-StringWidth(str3))/2.0,186.0));
					BView::Draw(frame);
				}
				
	void		AttachedToWindow(void)
				{
					Window()->Lock();
					if(first) {
						AddChild(top);	
						first=FALSE;
					}
					
					// Boring... But it must be set 8-|
					
					top->SetViewColor(Parent()->ViewColor());
					top->SetLowColor(Parent()->ViewColor());

					label1->SetViewColor(Parent()->ViewColor());
					label1->SetLowColor(Parent()->ViewColor());
					label1->SetAlignment(B_ALIGN_CENTER);
					label1->SetFont(be_plain_font);
					label1->SetFontSize(10.0);
				
					inst->the_sid->panprop[0]->layoutprefs();
					inst->the_sid->panprop[0]->layout(BRect(0, 15, 100, 15+KNOB_HEIGHT));
					inst->the_sid->panprop[1]->layoutprefs();
					inst->the_sid->panprop[1]->layout(BRect(0, 35, 100, 35+KNOB_HEIGHT));
					inst->the_sid->panprop[2]->layoutprefs();
					inst->the_sid->panprop[2]->layout(BRect(0, 55, 100, 55+KNOB_HEIGHT));
					inst->the_sid->panprop[3]->layoutprefs();
					inst->the_sid->panprop[3]->layout(BRect(0, 75, 100, 75+KNOB_HEIGHT));

					label2->SetViewColor(Parent()->ViewColor());
					label2->SetLowColor(Parent()->ViewColor());
					label2->SetAlignment(B_ALIGN_CENTER);
					label2->SetFont(be_plain_font);
					label2->SetFontSize(10.0);

					inst->the_sid->volprop[0]->layoutprefs();
					inst->the_sid->volprop[0]->layout(BRect(110, 15, 110+KNOB_WIDTH, 75+KNOB_HEIGHT));
					inst->the_sid->volprop[1]->layoutprefs();
					inst->the_sid->volprop[1]->layout(BRect(130, 15, 130+KNOB_WIDTH, 75+KNOB_HEIGHT));
					inst->the_sid->volprop[2]->layoutprefs();
					inst->the_sid->volprop[2]->layout(BRect(150, 15, 150+KNOB_WIDTH, 75+KNOB_HEIGHT));
					inst->the_sid->volprop[3]->layoutprefs();
					inst->the_sid->volprop[3]->layout(BRect(170, 15, 170+KNOB_WIDTH, 75+KNOB_HEIGHT));

					if(inst->prefs)	{
						filter->SetValue(inst->prefs->FindBool("SIDFilter"));
					}

					filter->SetTarget(this);
					more->SetTarget(this);
					less->SetTarget(this);						

					Window()->Unlock();
				};
	
	void 		MessageReceived(BMessage *msg)
				{
					switch(msg->what) {
						case	SID_FILTER:
								if(inst->prefs)	{
									inst->prefs->ReplaceBool("SIDFilter",filter->Value());
									inst->the_sid->EnableFilters(inst->prefs->FindBool("SIDFilter"));	
								}
								break;
						case	SID_MORE:
								inst->current_freq += 5;
								inst->the_sid->SetReplayFreq(inst->current_freq);
								break;
						case	SID_LESS:
								if (inst->current_freq > 5) {
									inst->current_freq -= 5;
									inst->the_sid->SetReplayFreq(inst->current_freq);
								}
								break;
					}
				};
					
};

long class_dispatcher(Class *c, Object *o, ulong MethodID, ulong *data)
{
	instance_struct *inst=(instance_struct *)o->Instance(c);

	o->SetError(B_NO_ERROR);
	
	switch(MethodID)
	{
		case	B_METHOD_CONSTRUCT:
				{					
					// Create objects for emulation
					inst->the_ram = new UBYTE[0x10000];
					inst->the_cpu = new MOS6510();
					inst->the_cpu->RAM = inst->the_ram;
					inst->the_sid = new MOS6581(&inst->globalvolume);
					inst->the_cpu->TheSID = inst->the_sid;
					inst->the_sid->TheCPU = inst->the_cpu;
					inst->the_sid->TheRAM = inst->the_ram;
					
					inst->volset[0]=1.0;
					inst->volset[1]=1.0;
					inst->volset[2]=1.0;
					inst->volset[3]=1.0;
					
					inst->panset[0]=0.374;
					inst->panset[1]=0.5;
					inst->panset[2]=0.627;
					inst->panset[3]=0.5;
					
					inst->globalvolume=1.0;					
					inst->current_freq = 50;
					inst->number_of_songs = 0;
					inst->current_song = 0;
					
					inst->view = new SIDView(inst);
					
					BMimeType *mime = new BMimeType("audio/psid");
					if(!mime->IsInstalled())
					{
						BBitmap *large = new BBitmap(BRect(0,0,31,31),B_COLOR_8_BIT);
						large->SetBits(large_icon,32*32,0,B_COLOR_8_BIT);
						BBitmap *mini = new BBitmap(BRect(0,0,15,15),B_COLOR_8_BIT);
						mini->SetBits(small_icon,16*16,0,B_COLOR_8_BIT);
						
						BMessage *msg = new BMessage();
						msg->AddData("extensions",'MXTT',"sid",4);
						mime->SetIcon(large,B_LARGE_ICON);
						mime->SetIcon(mini,B_MINI_ICON);
						mime->SetFileExtensions((const BMessage *)msg);
						mime->SetPreferredApp("application/x-tinic.DualPlayer");
						mime->SetShortDescription("PlaySID file");
						mime->SetLongDescription("Most common C64 music file format.");
						mime->Install();

						delete msg;
						delete large;
						delete mini;
					}
					delete mime;

					return 0;
				}

		case	B_METHOD_DESTROY:
				{
					if (inst->the_sid != NULL)
						delete inst->the_sid;
					if (inst->the_cpu != NULL)
						delete inst->the_cpu;
					if (inst->the_ram != NULL)
						delete[] inst->the_ram;

					return 0;
				}				
				
		case	P_LOAD:				// bool (char *path)
				{			
					inst->the_sid->EnableFilters(B_CONTROL_ON);
					inst->the_sid->PauseSound();
		
					if (o->DoMethod(SID_LOADPSID,(char *)data[0])) {
					
						BNode *node = new BNode((char *)data[0]);
						BNodeInfo *nodeinfo = new BNodeInfo(node);
						nodeinfo->SetType("audio/psid");
						delete nodeinfo;
						delete node;

						o->DoMethod(SID_SELECTSONG,inst->current_song);
						inst->the_sid->EnableFilters(inst->prefs->FindBool("SIDFilter"));	
		
						if (inst->play_adr)
							inst->the_sid->PlayAdr = inst->play_adr;
						else
							if (inst->the_ram[1] & 2)
								inst->the_sid->PlayAdr = (inst->the_ram[0x0315] << 8) | inst->the_ram[0x0314];
							else
								inst->the_sid->PlayAdr = (inst->the_ram[0xffff] << 8) | inst->the_ram[0xfffe];
						
						// Print message in DualPlayer
						inst->dual_object->DoMethod(D_NEW_MESSAGE,"SIDPlayer V2.30 by Christian Bauer");
		
						inst->the_sid->ResumeSound();
						return 1;
					}
					return 0;
				}
				
		case	P_STREAM:			// bool (void *arg, char *buffer, int size)
				{
					inst->the_sid->stream_func(inst->the_sid,(char *)data[1],data[2]);
					return 1;
				}
				
		case	P_SETATTR:			// void (int, int)
				{
					ulong ATTR =(ulong)data[0];
					ulong DATA =(ulong)data[1];

					switch(ATTR) {
						case	P_ATTR_TRACK:
								o->DoMethod(SID_SELECTSONG,DATA);
								break;
						case	P_ATTR_PREFS:
								inst->prefs=(BMessage *)DATA;
								if(!inst->prefs->HasBool("SIDFilter")) inst->prefs->AddBool("SIDFilter",TRUE);
								break;
						case	P_ATTR_BOOST:
								inst->globalvolume=((float)DATA)/128;
								break;
						case	P_ATTR_MODPOS:
								break;
						case	P_ATTR_LOOP:
								break;
						case	P_ATTR_DUALOBJECT:
								inst->dual_object=(Object *)DATA;
								break;
					}

					o->DoSuperMethod(c,P_SETATTR,data);

					return 0;
				}
				
		case	P_GETATTR:			// int (int, int)
				{
					ulong ATTR =(ulong)data[0];
					ulong DATA =(ulong)data[1];
					
					switch(ATTR) {
						case	P_ATTR_STREAMARG:
								*((MOS6581 **)DATA)=NULL;
								return 0;
						case	P_ATTR_ID:
								*((char **)DATA)="SIDPlayer V2.30";
								return 0;
						case	P_ATTR_VIEW:
								*((SIDView **)DATA)=inst->view;
								return 0;
						case	P_ATTR_MODLENGTH:
								*((int *)DATA)=0;
								return 0;
						case	P_ATTR_MODPOS:
								*((int *)DATA)=0;
								return 0;
						case	P_ATTR_TRACK:
								*((int *)DATA)=inst->current_song;
								return 0;
						case	P_ATTR_TRACKNUM:
								*((int *)DATA)=inst->number_of_songs;
								return 0;
						case	P_ATTR_RECOGNITION:
								*((int *)DATA)=95;
								return 0;
						case	P_ATTR_MIME:
								*((char ***)DATA)=mimelist;
						default:
								return o->DoSuperMethod(c,P_GETATTR,data);
					}
				}
				
		case	P_DRAWSTATUS:		// void (BView *view)
				{
					char str[1024];
					BView *view=(BView *)data[0];
					view->MovePenTo(BPoint(4, 11));
					view->DrawString("Sidname");
					view->MovePenTo(BPoint(52, 11));
					sprintf(str,": %s",inst->module_name);
					view->DrawString(str);
					view->MovePenTo(BPoint(4, 22));
					view->DrawString("Author");
				 	view->MovePenTo(BPoint(52, 22));
				 	sprintf(str,": %s",inst->author);
					view->DrawString(str);
					view->MovePenTo(BPoint(4, 33));
					view->DrawString("Copyright");
					view->MovePenTo(BPoint(52, 33));
					sprintf(str,": %s",inst->copyright);
					view->DrawString(str);
					sprintf(str,"Song %d/%d",inst->current_song+1,inst->number_of_songs);
					view->MovePenTo(BPoint(206-view->StringWidth(str),33));
					view->DrawString(str);
					return 0;
				}
				
		case	P_CREATEINFO: 		// void (char ***info)
				{													
					char ***info=(char ***)data[0];
					char **out;
					int32 c=0;
	
					*info=out=(char **)malloc(sizeof(char *)*(17));

					out[c]=(char *)malloc(2);								
					sprintf(out[c++],"-");
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Name     : %s",inst->module_name);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Author   : %s",inst->author);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Copyright: %s",inst->copyright);
					out[c]=(char *)malloc(2);								
					sprintf(out[c++],"-");
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"PSID Version        : %d",inst->header.version);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Header length       : $%04x",inst->header.length);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"C64 load address    : $%04x",inst->header.start);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"C64 init address    : $%04x",inst->header.init);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"C64 replayer address: $%04x",inst->header.main);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Number of subsongs  : %d",inst->header.number);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Main subsong        : %d",inst->header.defsong);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Speed Low flag      : %d",inst->header.speedlo);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Speed High flag     : %d",inst->header.speedhi);
					out[c]=(char *)malloc(64);								
					sprintf(out[c++],"Flags               : $%04x",inst->header.flags);
					out[c]=(char *)malloc(2);								
					sprintf(out[c++],"-");
					
					out[c++]=NULL;

					return 0;
				}

		case	P_FREEINFO: 		// void (char ***info)
				{
					char **info=*((char ***)data[0]);
					
					for(int32 c=0;info[c];c++)	free(info[c]);
					free(info);

					return 0;
				}
								
		case	SID_SELECTSONG:		// void (int num)
				{
					bool need_resume = FALSE;
					int num = data[0];
					
					// Pause sound if running
					if (!inst->the_sid->IsPaused()) {
						need_resume = TRUE;
						inst->the_sid->PauseSound();
					}
				
					inst->current_song = num;
					inst->the_sid->Reset();
				
					// Set replay frequency
					if (num < 32)
						inst->current_freq = inst->song_speeds & (1 << num) ? 60 : 50;
					else
						inst->current_freq = 50;
					inst->the_sid->SetReplayFreq(inst->current_freq);
				
					inst->the_cpu->Emulate(inst->init_adr, inst->current_song, 0, 0);
				
					// Resume sound if it was running
					if (need_resume)
						inst->the_sid->ResumeSound();
					return 0;
				}

		case	SID_LOADPSID:		// bool (char *filename)
				{
					char *filename = (char *)data[0];

					// Safety check, should never happen.
					if(filename && filename[0])
					{
						// Open PSID file
						FILE *file = fopen(filename, "rb");
						if (file) {
					
							// Read header
							if (fread(&inst->header, 1, sizeof(PSIDHeader), file) == sizeof(PSIDHeader)) {
					
								// Check header ID and version
								if (inst->header.id == 'PSID' && (inst->header.version == 1 || inst->header.version == 2)) {
					
									// OK, seek to start of module data
									fseek(file, inst->header.length, SEEK_SET);
					
									// Extract data
									inst->current_song = inst->header.defsong;
									if (inst->current_song)
										inst->current_song--;
					
									inst->number_of_songs = inst->header.number;
									if (!inst->number_of_songs)
										inst->number_of_songs = 1;
									if (inst->current_song >= inst->number_of_songs)
										inst->current_song = 0;
					
									inst->init_adr = inst->header.init;
									inst->play_adr = inst->header.main;
					
									inst->song_speeds = (inst->header.speedhi << 16) | inst->header.speedlo;
					
									memcpy(inst->module_name, inst->header.name, 32);
									memcpy(inst->author, inst->header.author, 32);
									memcpy(inst->copyright, inst->header.copyright, 32);
									inst->module_name[31] = 0;
									inst->author[31] = 0;
									inst->copyright[31] = 0;
					
									// Initialize memory
									memset(inst->the_ram, 0, 0xe000);
									memset(inst->the_ram + 0xe000, 0x40, 0x2000);
									inst->the_ram[1] = 7;
					
									// Load module into memory
									UWORD load_adr = inst->header.start;
									if (!load_adr) {
										UBYTE hi, lo;
										fread(&lo, 1, 1, file);
										fread(&hi, 1, 1, file);
										load_adr = (hi << 8) | lo;
									}
									fread(inst->the_ram + load_adr, 1, 0x10000 - load_adr, file);
									fclose(file);
					
									if (!inst->init_adr)
										inst->init_adr = load_adr;
					
									return TRUE;
								}
							}
							fclose(file);
						}
					}
					return FALSE;
				}				
				
		default:
				return o->DoSuperMethod(c,MethodID,data);
	}
}

char *class_superclass()
{
	return "dualplayer/player/baseplayer.zclass";
};

long class_instancesize()
{
	return sizeof(struct instance_struct);
};

long class_superversion()
{
	return 2;
};

long class_version()
{
	return 2;
};
