#pragma warning ( disable : 4115 )
#include <windows.h>
#pragma warning ( default : 4115 )
#include <assert.h>
#include <string.h>
#include <float.h>
#include "vfile.h"
#include "jeProperty.h"
#include "ram.h"
#include "jeResource.h"
#include "jeWorld.h"
#include "Mp3Object.h"
#include "jeParticle.h"
#include "jeVersion.h"
#include "errorlog.h"

#include "mp3mgr.h"

FILE		*f;


////////////////////////////////////////////////////////////////////////////////////////
//	Property list stuff
////////////////////////////////////////////////////////////////////////////////////////
enum
{
	MP3_LOOP_ID = PROPERTY_LOCAL_DATATYPE_START,
	MP3_FILE_ID,
	MP3_DRAWEXTBOXDISPLAY_ID,
	MP3_DRAWEXTBOXMINX_ID,
	MP3_DRAWEXTBOXMINY_ID,
	MP3_DRAWEXTBOXMINZ_ID,
	MP3_DRAWEXTBOXMAXX_ID,
	MP3_DRAWEXTBOXMAXY_ID,
	MP3_DRAWEXTBOXMAXZ_ID,
	MP3_LAST_ID
};

enum
{
	MP3_LOOP_INDEX = 0,
	MP3_FILE_INDEX,
	MP3_DRAWEXTBOXDISPLAY_INDEX,
	MP3_DRAWEXTBOXMINX_INDEX,
	MP3_DRAWEXTBOXMINY_INDEX,
	MP3_DRAWEXTBOXMINZ_INDEX,
	MP3_DRAWEXTBOXMAXX_INDEX,
	MP3_DRAWEXTBOXMAXY_INDEX,
	MP3_DRAWEXTBOXMAXZ_INDEX,
	MP3_LAST_INDEX
};


////////////////////////////////////////////////////////////////////////////////////////
//	Globals
////////////////////////////////////////////////////////////////////////////////////////
static HINSTANCE		hClassInstance = NULL;
static int				*BitmapWidth, *BitmapHeight;
static jeProperty		SpoutProperties[MP3_LAST_INDEX];
static jeProperty_List	SpoutPropertyList = { MP3_LAST_INDEX, &( SpoutProperties[0] ) };
static char				*NoSelection = "< none >";


////////////////////////////////////////////////////////////////////////////////////////
//	Defaults
////////////////////////////////////////////////////////////////////////////////////////
#define MP3_DEFAULT_LOOP					0.0f
#define MP3_DEFAULT_FILE					"null.mp3"

#define MP3_DEFAULT_MINSPEED				10.0f
#define MP3_DEFAULT_MAXSPEED				20.0f
#define MP3_DEFAULT_MINSCALE				1.0f
#define MP3_DEFAULT_MAXSCALE				1.0f
#define MP3_DEFAULT_MINUNITLIFE			2.0f
#define MP3_DEFAULT_MAXUNITLIFE			4.0f
#define MP3_DEFAULT_COLORRED				255.0f
#define MP3_DEFAULT_COLORGREEN			255.0f
#define MP3_DEFAULT_COLORBLUE				255.0f
#define MP3_DEFAULT_DRAWEXTBOXDISPLAY		JE_FALSE
#define MP3_DEFAULT_DRAWEXTBOXMINX		-16.0f
#define MP3_DEFAULT_DRAWEXTBOXMINY		-16.0f
#define MP3_DEFAULT_DRAWEXTBOXMINZ		-16.0f
#define MP3_DEFAULT_DRAWEXTBOXMAXX		16.0f
#define MP3_DEFAULT_DRAWEXTBOXMAXY		16.0f
#define MP3_DEFAULT_DRAWEXTBOXMAXZ		16.0f


////////////////////////////////////////////////////////////////////////////////////////
//	Object data
////////////////////////////////////////////////////////////////////////////////////////
typedef struct Spout
{
	jeParticle_System	*Ps;
	jeWorld				*World;
	jeResourceMgr		*ResourceMgr;
	jeEngine			*Engine;
	jeXForm3d			Xf;
	int					RefCount;
	int					CurWidth;
	jeBitmap			*Art;
	char				*ArtName;
	char				*BitmapName;
	char				*AlphaName;
	float				TimeElapsed;
	float				Rate;
	JE_RGBA				MinColor, MaxColor;
	float				MinSpeed, MaxSpeed;
	float				MinScale, MaxScale;
	float				MinUnitLife, MaxUnitLife;
	float				Angle;
	jeVec3d				Gravity;
	jeBoolean			DrawExtBoxDisplay;
	jeExtBox			DrawExtBox;
	jeBoolean			LoadedFromDisk;

	jeFloat				Loop;
	char				*File;

} Spout;



////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_StrDup()
//
////////////////////////////////////////////////////////////////////////////////////////
static char * Util_StrDup(
	const char	*const psz )	// string to copy
{

	// copy string
	char * p = (char *)jeRam_Allocate( strlen( psz ) + 1 );
	if ( p ) 
	{
		strcpy( p, psz );
	}
	else
	{
		jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
	}

	// return string
	return p;

} // Util_StrDup()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_DrawPoly()
//
///////////////////////////////////////////////////////////////////////////////////////
static void Util_DrawPoly(
	jeWorld		*World,	// world in which to draw poly
	jeLVertex	*V1,	// top left
	jeLVertex	*V2,	// top right
	jeLVertex	*V3,	// bottom right
	jeLVertex	*V4 )	// bottom left
{

	// locals
	jeUserPoly	*Poly;

	// ensure valid data
	assert( World != NULL );
	assert( V1 != NULL );
	assert( V2 != NULL );
	assert( V3 != NULL );
	assert( V4 != NULL );

	// draw poly
	Poly = jeUserPoly_CreateQuad( V1, V2, V3, V4, NULL, JE_RENDER_FLAG_ALPHA );
	jeWorld_AddUserPoly( World, Poly, JE_TRUE );
	jeUserPoly_Destroy( &Poly );

} // Util_DrawPoly()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_DrawExtBox()
//
///////////////////////////////////////////////////////////////////////////////////////
static void Util_DrawExtBox(
	jeWorld		*World,		// world to draw it in
	JE_RGBA		*Color,		// color to draw it in
	jeExtBox	*ExtBox )	// extent box to draw
{

	// locals
	jeLVertex	Vertex[4];
	int			i;

	// ensure valid data
	assert( World != NULL );
	assert( Color != NULL );
	assert( ExtBox != NULL );

	// init vert struct
	for ( i = 0; i < 4; i++ )
	{
		Vertex[i].a = Color->a;
		Vertex[i].r = Color->r;
		Vertex[i].g = Color->g;
		Vertex[i].b = Color->b;
	}
	Vertex[0].u = 0.0f;
	Vertex[0].v = 0.0f;
	Vertex[1].u = 1.0f;
	Vertex[1].v = 0.0f;
	Vertex[2].u = 1.0f;
	Vertex[2].v = 1.0f;
	Vertex[3].u = 0.0f;
	Vertex[3].v = 1.0f;

	// side 1
	Vertex[0].X = ExtBox->Min.X;
	Vertex[0].Y = ExtBox->Max.Y;
	Vertex[0].Z = ExtBox->Min.Z;
	Vertex[1].X = ExtBox->Max.X;
	Vertex[1].Y = ExtBox->Max.Y;
	Vertex[1].Z = ExtBox->Min.Z;
	Vertex[2].X = ExtBox->Max.X;
	Vertex[2].Y = ExtBox->Min.Y;
	Vertex[2].Z = ExtBox->Min.Z;
	Vertex[3].X = ExtBox->Min.X;
	Vertex[3].Y = ExtBox->Min.Y;
	Vertex[3].Z = ExtBox->Min.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

	// side 2
	Vertex[0].X = ExtBox->Min.X;
	Vertex[0].Y = ExtBox->Max.Y;
	Vertex[0].Z = ExtBox->Max.Z;
	Vertex[1].X = ExtBox->Max.X;
	Vertex[1].Y = ExtBox->Max.Y;
	Vertex[1].Z = ExtBox->Max.Z;
	Vertex[2].X = ExtBox->Max.X;
	Vertex[2].Y = ExtBox->Min.Y;
	Vertex[2].Z = ExtBox->Max.Z;
	Vertex[3].X = ExtBox->Min.X;
	Vertex[3].Y = ExtBox->Min.Y;
	Vertex[3].Z = ExtBox->Max.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

	// side 3
	Vertex[0].X = ExtBox->Min.X;
	Vertex[0].Y = ExtBox->Max.Y;
	Vertex[0].Z = ExtBox->Min.Z;
	Vertex[1].X = ExtBox->Min.X;
	Vertex[1].Y = ExtBox->Max.Y;
	Vertex[1].Z = ExtBox->Max.Z;
	Vertex[2].X = ExtBox->Max.X;
	Vertex[2].Y = ExtBox->Max.Y;
	Vertex[2].Z = ExtBox->Max.Z;
	Vertex[3].X = ExtBox->Max.X;
	Vertex[3].Y = ExtBox->Max.Y;
	Vertex[3].Z = ExtBox->Min.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

	// side 4
	Vertex[0].X = ExtBox->Max.X;
	Vertex[0].Y = ExtBox->Max.Y;
	Vertex[0].Z = ExtBox->Min.Z;
	Vertex[1].X = ExtBox->Max.X;
	Vertex[1].Y = ExtBox->Max.Y;
	Vertex[1].Z = ExtBox->Max.Z;
	Vertex[2].X = ExtBox->Max.X;
	Vertex[2].Y = ExtBox->Min.Y;
	Vertex[2].Z = ExtBox->Max.Z;
	Vertex[3].X = ExtBox->Max.X;
	Vertex[3].Y = ExtBox->Min.Y;
	Vertex[3].Z = ExtBox->Min.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

	// side 5
	Vertex[0].X = ExtBox->Max.X;
	Vertex[0].Y = ExtBox->Min.Y;
	Vertex[0].Z = ExtBox->Min.Z;
	Vertex[1].X = ExtBox->Max.X;
	Vertex[1].Y = ExtBox->Min.Y;
	Vertex[1].Z = ExtBox->Max.Z;
	Vertex[2].X = ExtBox->Min.X;
	Vertex[2].Y = ExtBox->Min.Y;
	Vertex[2].Z = ExtBox->Max.Z;
	Vertex[3].X = ExtBox->Min.X;
	Vertex[3].Y = ExtBox->Min.Y;
	Vertex[3].Z = ExtBox->Min.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

	// side 6
	Vertex[0].X = ExtBox->Min.X;
	Vertex[0].Y = ExtBox->Min.Y;
	Vertex[0].Z = ExtBox->Min.Z;
	Vertex[1].X = ExtBox->Min.X;
	Vertex[1].Y = ExtBox->Min.Y;
	Vertex[1].Z = ExtBox->Max.Z;
	Vertex[2].X = ExtBox->Min.X;
	Vertex[2].Y = ExtBox->Max.Y;
	Vertex[2].Z = ExtBox->Max.Z;
	Vertex[3].X = ExtBox->Min.X;
	Vertex[3].Y = ExtBox->Max.Y;
	Vertex[3].Z = ExtBox->Min.Z;
	Util_DrawPoly( World, &( Vertex[0] ), &( Vertex[1] ), &( Vertex[2] ), &( Vertex[3] ) );

} // Util_DrawExtBox()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Spout_DestroyArt()
//
////////////////////////////////////////////////////////////////////////////////////////
static void Spout_DestroyArt(
	Spout	*Object )	// object whose bitmap will be destroyed
{

	// ensure valid data
	assert( Object != NULL );

	// destroy art
	if ( Object->Art != NULL )
	{
		assert( Object->Engine != NULL );
		assert( Object->ResourceMgr != NULL );
		jeEngine_RemoveBitmap( Object->Engine, Object->Art );
		assert( Object->ArtName != NULL );
		if ( jeResource_Delete( Object->ResourceMgr, Object->ArtName ) == 0 )
		{
			jeBitmap_Destroy( &( Object->Art ) );
		}
		Object->Art = NULL;
	}

} // Spout_DestroyArt()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_WriteString()
//
////////////////////////////////////////////////////////////////////////////////////////
static jeBoolean Util_WriteString(
	jeVFile	*File,		// file to write to
	char	*String )	// string to write out
{

	// locals
	int			Size;
	jeBoolean	Result = JE_TRUE;

	// ensure valid data
	assert( File != NULL );
	assert( String != NULL );

	// write out complete
	Size = strlen( String ) + 1;
	assert( Size > 0 );
	Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	Result &= jeVFile_Write( File, String, Size );

	// all done
	return Result;

} // Util_WriteString()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_CreateBitmapFromFileName()
//
//	Create a bitmap from a file.
//
////////////////////////////////////////////////////////////////////////////////////////
static jeBitmap * Util_CreateBitmapFromFileName(
	jeVFile		*File,			// file system to use
	const char	*Name,			// name of the file
	const char	*AlphaName )	// name of the alpha file
{

	// locals
	jeVFile		*BmpFile;
	jeBitmap	*Bmp;
	jeBoolean	Result;

	// ensure valid data
	assert( Name != NULL );

	// open the bitmap
	if ( File == NULL )
	{
		BmpFile = jeVFile_OpenNewSystem( NULL, JE_VFILE_TYPE_DOS, Name, NULL, JE_VFILE_OPEN_READONLY );
	}
	else
	{
		BmpFile = jeVFile_Open( File, Name, JE_VFILE_OPEN_READONLY );
	}
	if ( BmpFile == NULL )
	{
		jeErrorLog_Add( JE_ERR_FILEIO_OPEN, NULL );
		return NULL;
	}

	// create the bitmap
	Bmp = jeBitmap_CreateFromFile( BmpFile );
	jeVFile_Close( BmpFile );
	if ( Bmp == NULL )
	{
		jeErrorLog_Add( JE_ERR_SUBSYSTEM_FAILURE, NULL );
		return NULL;
	}

	// add alpha if required...
	if ( AlphaName != NULL )
	{

		// locals
		jeBitmap	*AlphaBmp;
		jeVFile		*AlphaFile;

		// open alpha file
		if ( File == NULL )
		{
			AlphaFile = jeVFile_OpenNewSystem( NULL, JE_VFILE_TYPE_DOS, AlphaName, NULL, JE_VFILE_OPEN_READONLY );
		}
		else
		{
			AlphaFile = jeVFile_Open( File, AlphaName, JE_VFILE_OPEN_READONLY );
		}
		if( AlphaFile == NULL )
		{
			jeErrorLog_Add( JE_ERR_FILEIO_OPEN, NULL );
			jeBitmap_Destroy( &Bmp );
			return NULL;
		}

		// create alpha bitmap
		AlphaBmp = jeBitmap_CreateFromFile( AlphaFile );
		jeVFile_Close( AlphaFile );
		if ( AlphaBmp == NULL )
		{
			jeErrorLog_Add( JE_ERR_SUBSYSTEM_FAILURE, NULL );
			jeBitmap_Destroy( &Bmp );
			return NULL;
		}

		// fail if alpha isn't same size as main bitmap
		if (	( jeBitmap_Width( Bmp ) != jeBitmap_Width( AlphaBmp ) ) ||
				( jeBitmap_Height( Bmp ) != jeBitmap_Height( AlphaBmp ) ) )
		{
			jeErrorLog_Add( JE_ERR_BAD_PARAMETER, NULL );
			jeBitmap_Destroy( &AlphaBmp );
			jeBitmap_Destroy( &Bmp );
			return NULL;
		}

		// set its alpha
		Result = jeBitmap_SetAlpha( Bmp, AlphaBmp );
		if ( Result == JE_FALSE )
		{
			jeErrorLog_Add( JE_ERR_SUBSYSTEM_FAILURE, NULL );
			jeBitmap_Destroy( &AlphaBmp );
			jeBitmap_Destroy( &Bmp );
			return NULL;
		}

		// don't need the alpha anymore
		jeBitmap_Destroy( &AlphaBmp );
	}
	// ...or just set the color key
	else
	{
		Result = jeBitmap_SetColorKey( Bmp, JE_TRUE, 255, JE_FALSE );
		assert( Result );
	}

	// all done
	return Bmp;

} // Util_CreateBitmapFromFileName()




////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_LoadLibraryString()
//
////////////////////////////////////////////////////////////////////////////////////////
static char * Util_LoadLibraryString(
	HINSTANCE		hInstance,
	unsigned int	ID )
{

	// locals
	#define		MAX_STRING_SIZE	255
	static char	StringBuf[MAX_STRING_SIZE];
	char		*NewString;
	int			Size;

	// ensure valid data
	assert( hInstance != NULL );
	assert( ID >= 0 );

	// get resource string
	Size = LoadString( hInstance, ID, StringBuf, MAX_STRING_SIZE );
	if ( Size <= 0 )
	{
		jeErrorLog_Add( JE_ERR_WINDOWS_API_FAILURE, NULL );
		return NULL;
	}

	// copy resource string
	NewString = jeRam_Allocate( Size + 1 );
	if ( NewString == NULL )
	{
		jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
		return NULL;
	}
	strcpy( NewString, StringBuf );

	// all done
	return NewString;

} // Util_LoadLibraryString()


////////////////////////////////////////////////////////////////////////////////////////
//
//	Util_Frand()
//
//	Picks a random float within the supplied range.
//
////////////////////////////////////////////////////////////////////////////////////////
static float Util_Frand(
	float Low,		// minimum value
	float High )	// maximum value
{

	// locals
	float	Range;

	// ensure valid data
	assert( High >= Low );

	// if they are the same then just return one of them
	if ( High == Low )
	{
		return Low;
	}

	// pick a random float from whithin the range
	Range = High - Low;
	return ( (float)( ( ( rand() % 1000 ) + 1 ) ) ) / 1000.0f * Range + Low;

} // Util_Frand()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Init_Class()
//
////////////////////////////////////////////////////////////////////////////////////////
void Init_Class(
	HINSTANCE	hInstance )	// dll instance handle
{

	// ensure valid data
	assert( hInstance != NULL );

	// save hinstance
	hClassInstance = hInstance;

	play = JE_FALSE;
	f = fopen("mp3obj_log.txt", "wt");
	
	// setup loop property
	jeProperty_FillFloat(	&( SpoutProperties[MP3_LOOP_INDEX] ),
							"Loop? 1=true 0=false",
							MP3_DEFAULT_LOOP,
							MP3_LOOP_ID,
							0.0f, 1.0f, 1.0f );

	// setup angle property
	jeProperty_FillString(	&( SpoutProperties[MP3_FILE_INDEX] ),
							"filename.mp3",
							MP3_DEFAULT_FILE,
							MP3_FILE_ID);

	////////////////////////////////////////////////////////////////////////////////////////
	//	Draw box properties
	////////////////////////////////////////////////////////////////////////////////////////

	// start draw box group
	/*jeProperty_FillGroup(	&( SpoutProperties[MP3_DRAWEXTBOXGROUP_INDEX] ),
							Util_LoadLibraryString( hClassInstance, IDS_DRAWEXTBOXGROUP ),
							MP3_DRAWEXTBOXGROUP_ID );*/

	// setup draw box display property
	jeProperty_FillCheck(	&( SpoutProperties[MP3_DRAWEXTBOXDISPLAY_INDEX] ),
							"DRAWEXTBOXDISPLAY",
							MP3_DEFAULT_DRAWEXTBOXDISPLAY,
							MP3_DRAWEXTBOXDISPLAY_ID );

	// setup draw box adjustment properties
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMINX_INDEX] ),
							"DRAWEXTBOXMINX" ,
							MP3_DEFAULT_DRAWEXTBOXMINX,
							MP3_DRAWEXTBOXMINX_ID,
							-FLT_MAX, FLT_MAX, 8.0f );
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMINY_INDEX] ),
							"DRAWEXTBOXMINY" ,
							MP3_DEFAULT_DRAWEXTBOXMINY,
							MP3_DRAWEXTBOXMINY_ID,
							-FLT_MAX, FLT_MAX, 8.0f );
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMINZ_INDEX] ),
							"DRAWEXTBOXMINZ" ,
							MP3_DEFAULT_DRAWEXTBOXMINZ,
							MP3_DRAWEXTBOXMINZ_ID,
							-FLT_MAX, FLT_MAX, 8.0f );
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMAXX_INDEX] ),
							"DRAWEXTBOXMAXX" ,
							MP3_DEFAULT_DRAWEXTBOXMAXX,
							MP3_DRAWEXTBOXMAXX_ID,
							-FLT_MAX, FLT_MAX, 8.0f );
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMAXY_INDEX] ),
							"DRAWEXTBOXMAXY" ,
							MP3_DEFAULT_DRAWEXTBOXMAXY,
							MP3_DRAWEXTBOXMAXY_ID,
							-FLT_MAX, FLT_MAX, 8.0f );
	jeProperty_FillFloat(	&( SpoutProperties[MP3_DRAWEXTBOXMAXZ_INDEX] ),
							"DRAWEXTBOXMAXZ" ,
							MP3_DEFAULT_DRAWEXTBOXMAXZ,
							MP3_DRAWEXTBOXMAXZ_ID,
							-FLT_MAX, FLT_MAX, 8.0f );

	// end draw box group

	////////////////////////////////////////////////////////////////////////////////////////
	//	Misc properties
	////////////////////////////////////////////////////////////////////////////////////////

	// final init
	SpoutPropertyList.jePropertyN = MP3_LAST_INDEX;

} // Init_Class()



////////////////////////////////////////////////////////////////////////////////////////
//
//	DeInit_Class()
//
////////////////////////////////////////////////////////////////////////////////////////
void DeInit_Class(
	void )	// no parameters
{

	// zap instance pointer
	fclose(f);
	hClassInstance = NULL;

} // DeInit_Class()



////////////////////////////////////////////////////////////////////////////////////////
//
//	CreateInstance()
//
////////////////////////////////////////////////////////////////////////////////////////
void * JETCC CreateInstance(
	void )	// no parameters
{

	// locals
	Spout	*Object;

	play = JE_FALSE;

	// allocate struct
	Object = jeRam_AllocateClear( sizeof( *Object ) );
	if ( Object == NULL )
	{
		jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
		return NULL;
	}

	// create particle system
	Object->Ps = jeParticle_SystemCreate( JET_MAJOR_VERSION, JET_MINOR_VERSION );
	if ( Object->Ps == NULL )
	{
		jeErrorLog_Add( JE_ERR_SUBSYSTEM_FAILURE, NULL );
		jeRam_Free( Object );
		return NULL;
	}

	// init remaining fields
	jeXForm3d_SetIdentity( &Object->Xf );
	Object->RefCount = 1;
	Object->Loop = MP3_DEFAULT_LOOP;
	Object->File = MP3_DEFAULT_FILE;
	Object->DrawExtBoxDisplay = MP3_DEFAULT_DRAWEXTBOXDISPLAY;
	Object->DrawExtBox.Min.X = MP3_DEFAULT_DRAWEXTBOXMINX;
	Object->DrawExtBox.Min.Y = MP3_DEFAULT_DRAWEXTBOXMINY;
	Object->DrawExtBox.Min.Z = MP3_DEFAULT_DRAWEXTBOXMINZ;
	Object->DrawExtBox.Max.X = MP3_DEFAULT_DRAWEXTBOXMAXX;
	Object->DrawExtBox.Max.Y = MP3_DEFAULT_DRAWEXTBOXMAXY;
	Object->DrawExtBox.Max.Z = MP3_DEFAULT_DRAWEXTBOXMAXZ;

	// setup art size property
	/*jeProperty_FillCombo(	&( SpoutPropertyList.pjeProperty[MP3_ARTSIZE_INDEX] ),
							Util_LoadLibraryString( hClassInstance, IDS_ARTSIZE ),
							0,
							MP3_ARTSIZE_ID,
							0,
							NULL );

	// setup bitmap list property
	Object->BitmapName = Util_StrDup( NoSelection );
	jeProperty_FillCombo(	&( SpoutPropertyList.pjeProperty[MP3_BITMAPLIST_INDEX] ),
							Util_LoadLibraryString( hClassInstance, IDS_BITMAPLIST ),
							NoSelection,
							MP3_BITMAPLIST_ID,
							1,
							&NoSelection );

	// setup alpha list property
	Object->AlphaName = Util_StrDup( NoSelection );
	jeProperty_FillCombo(	&( SpoutPropertyList.pjeProperty[MP3_ALPHALIST_INDEX] ),
							Util_LoadLibraryString( hClassInstance, IDS_ALPHALIST ),
							NoSelection,
							MP3_ALPHALIST_ID,
							1,
							&NoSelection );

	// all done
	*/
	return Object;

} // CreateInstance()



////////////////////////////////////////////////////////////////////////////////////////
//
//	CreateRef()
//
////////////////////////////////////////////////////////////////////////////////////////
void JETCC CreateRef(
	void	*Instance )	// instance data
{

	// locals
	Spout	*Object;
	
	// get object
	Object = (Spout *)Instance;
	assert( Object != NULL );

	// adjust object ref count
	Object->RefCount++;

} // CreateRef()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Destroy()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC Destroy(
	void	**Instance )	// pointer to instance data
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );

	// get object
	Object = (Spout *)*Instance;
	assert( Object->RefCount > 0 );

	// do nothing if ref count is not at zero
	Object->RefCount--;
	if ( Object->RefCount > 0 )
	{
		return JE_FALSE;
	}

	// make sure everything has been properly destroyed
	assert( Object->World == NULL );
	assert( Object->Engine == NULL );
	assert( Object->ResourceMgr == NULL );
	assert( Object->Ps == NULL );
	assert( Object->Art == NULL );
	assert( Object->ArtName == NULL );
	//assert( Object->SizeName == NULL );
	assert( Object->BitmapName == NULL );
	assert( Object->AlphaName == NULL );

	// free struct
	jeRam_Free( Object );

	// zap pointer
	*Instance = NULL;

	// all done
	return JE_TRUE;

} // Destroy()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Render()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC Render(
	const void				*Instance,				// object instance data
	const jeWorld			*World,					// world
	const jeEngine			*Engine,				// engine
	const jeCamera			*Camera,				// camera
	const jeFrustum			*CameraSpaceFrustum,	// frustum
	jeObject_RenderFlags	RenderFlags)	
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );

	// get object
	Object = (Spout *)Instance;

	// display draw ext box
	if ( Object->DrawExtBoxDisplay == JE_TRUE )
	{

		// locals
		JE_RGBA		Color = { 255.0f, 0.0f, 0.0f, 64.0f };
		jeExtBox	ExtBox;

		// copy ext box and translate it
		ExtBox = Object->DrawExtBox;
		jeExtBox_Translate( &ExtBox, Object->Xf.Translation.X, Object->Xf.Translation.Y, Object->Xf.Translation.Z );

		// draw it
		Util_DrawExtBox( (jeWorld *)World, &Color, &ExtBox );
	}

	// all done
	return JE_TRUE;

	// eliminate warnings
	Engine;
	Camera;
	CameraSpaceFrustum;

} // Render()



////////////////////////////////////////////////////////////////////////////////////////
//
//	AttachWorld()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC AttachWorld(
	void	*Instance,	// object instance data
	jeWorld	*World )	// world
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( World != NULL );

	// get object data
	Object = (Spout *)Instance;

	// save world pointer
	Object->World = World;

	// save an instance of the resource manager
	Object->ResourceMgr = jeWorld_GetResourceMgr( World );
	assert( Object->ResourceMgr != NULL );

	// all done
	return JE_TRUE;

} // AttachWorld()



////////////////////////////////////////////////////////////////////////////////////////
//
//	DettachWorld()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC DettachWorld(
	void	*Instance,	// object instance data
	jeWorld	*World )	// world
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( World != NULL );

	// get object data
	Object = (Spout *)Instance;
	assert( Object->World == World );

	// destroy its particle system
	if ( Object->Ps != NULL )
	{
		jeParticle_SystemDestroy( Object->Ps );
		Object->Ps = NULL;
	}

	// destroy our instance of the resource manager
	jeResource_MgrDestroy( &( Object->ResourceMgr ) );

	// zap world pointer
	Object->World = NULL;

	// all done
	return JE_TRUE;

} // DettachWorld()



////////////////////////////////////////////////////////////////////////////////////////
//
//	AttachEngine()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC AttachEngine(
	void		*Instance,	// object instance data
	jeEngine	*Engine )	// engine
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( Engine != NULL );

	// get object data
	Object = (Spout *)Instance;

	// save engine pointer
	Object->Engine = Engine;

	// set properties if object was loaded from disk
	if ( Object->LoadedFromDisk == JE_TRUE )
	{

		// reset loaded from disk flag
		Object->LoadedFromDisk = JE_FALSE;

	}

	// flag property list as dirty
	SpoutPropertyList.bDirty = JE_TRUE;

	// all done
	return JE_TRUE;

} // AttachEngine()



////////////////////////////////////////////////////////////////////////////////////////
//
//	DettachEngine()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC DettachEngine(
	void		*Instance,	// object instance data
	jeEngine	*Engine )	// engine
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( Engine != NULL );

	// get object data
	Object = (Spout *)Instance;
	assert( Object->Engine == Engine );

	// destroy object bitmap
	Spout_DestroyArt( Object );

	// destroy bitmap names
	if ( Object->AlphaName != NULL )
	{
		jeRam_Free( Object->AlphaName );
		Object->AlphaName = NULL;
	}
	if ( Object->BitmapName != NULL )
	{
		jeRam_Free( Object->BitmapName );
		Object->BitmapName = NULL;
	}
	if ( Object->ArtName != NULL )
	{
		jeRam_Free( Object->ArtName );
		Object->ArtName = NULL;
	}

	// zap engine pointer
	Object->Engine = NULL;

	// all done
	return JE_TRUE;

} // DettachEngine()



////////////////////////////////////////////////////////////////////////////////////////
//
//	AttachSoundSystem()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC AttachSoundSystem(
	void			*Instance,		// object instance data
	jeSound_System	*SoundSystem )	// sound system
{

	// ensure valid data
	assert( Instance != NULL );
	assert( SoundSystem != NULL );

	// all done
	return JE_TRUE;

	// elminate warnings
	Instance;
	SoundSystem;

} // AttachSoundSystem()



////////////////////////////////////////////////////////////////////////////////////////
//
//	DettachSoundSystem()
//
////////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC DettachSoundSystem(
	void			*Instance,		// object instance data
	jeSound_System	*SoundSystem )	// sound system
{

	// ensure valid data
	assert( Instance != NULL );
	assert( SoundSystem != NULL );

	// all done
	return JE_TRUE;

	// elminate warnings
	Instance;
	SoundSystem;

} // DettachSoundSystem()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Collision()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC Collision(
	const jeObject	*Object,
	const jeExtBox	*Box,
	const jeVec3d	*Front,
	const jeVec3d	*Back,
	jeVec3d			*Impact,
	jePlane			*Plane )
{

	// ensure valid data
	assert( Object != NULL );
	//assert( Box != NULL );  Removed by Incarnadine.  Box CAN be NULL.
	assert( Front != NULL );
	assert( Back != NULL );
	//assert( Impact != NULL );  Removed by Incarnadine. Impact&Plane CAN be NULL.
	//assert( Plane != NULL );

	// all done
	return JE_FALSE;

	// eliminate warnings
	Object;
	Box;
	Front;
	Back;
	Impact;
	Plane;

} // Collision()



////////////////////////////////////////////////////////////////////////////////////////
//
//	GetExtBox()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC GetExtBox(
	const void	*Instance,	// object instance data
	jeExtBox	*BBox )		// where to store extent box
{

	// locals
	Spout		*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( BBox != NULL );

	// get object data
	Object = (Spout *)Instance;

	// save extent box
	*BBox = Object->DrawExtBox;
	jeExtBox_Translate( BBox, Object->Xf.Translation.X, Object->Xf.Translation.Y, Object->Xf.Translation.Z );

	// all done
	return JE_TRUE;

} // GetExtBox()



////////////////////////////////////////////////////////////////////////////////////////
//
//	CreateFromFile()
//
///////////////////////////////////////////////////////////////////////////////////////
#if NEWLOAD_SPT
void * JETCC CreateFromFile(
	jeVFile		*File,		// vfile to use
	jeNameMgr *NM )	// pointer manager
{

	// locals
	Spout		*Object;
	int			Size;
	jeBoolean	Result = JE_TRUE;

	// ensure valid data
	assert( File != NULL );

	// create new object
	Object = CreateInstance();
	if ( Object == NULL )
	{
		return NULL;
	}

	// read art name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->ArtName = jeRam_Allocate( Size );
		if ( Object->ArtName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->ArtName, Size );
	}

	// read bitmap name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->BitmapName = jeRam_Allocate( Size );
		if ( Object->BitmapName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->BitmapName, Size );
	}

	// read alpha name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->AlphaName = jeRam_Allocate( Size );
		if ( Object->AlphaName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->AlphaName, Size );
	}

	// read xform
	Result &= jeVFile_Read( File, &( Object->Xf ), sizeof( Object->Xf ) );

	// read gravity
	Result &= jeVFile_Read( File, &( Object->Gravity ), sizeof( Object->Gravity ) );

	// read angle
	Result &= jeVFile_Read( File, &( Object->Angle ), sizeof( Object->Angle ) );

	// read rate
	Result &= jeVFile_Read( File, &( Object->Rate ), sizeof( Object->Rate ) );

	// read min color
	Result &= jeVFile_Read( File, &( Object->MinColor ), sizeof( Object->MinColor ) );

	// read max color
	Result &= jeVFile_Read( File, &( Object->MaxColor ), sizeof( Object->MaxColor ) );

	// read min speed
	Result &= jeVFile_Read( File, &( Object->MinSpeed ), sizeof( Object->MinSpeed ) );

	// read max speed
	Result &= jeVFile_Read( File, &( Object->MaxSpeed ), sizeof( Object->MaxSpeed ) );

	// read min scale
	Result &= jeVFile_Read( File, &( Object->MinScale ), sizeof( Object->MinScale ) );

	// read max scale
	Result &= jeVFile_Read( File, &( Object->MaxScale ), sizeof( Object->MaxScale ) );

	// read min unit life
	Result &= jeVFile_Read( File, &( Object->MinUnitLife ), sizeof( Object->MinUnitLife ) );

	// read max unit life
	Result &= jeVFile_Read( File, &( Object->MaxUnitLife ), sizeof( Object->MaxUnitLife ) );

	// read out draw box
	Result &= jeVFile_Read( File, &( Object->DrawExtBox ), sizeof( Object->DrawExtBox ) );

	// fail if there was an error
	if ( Result == JE_FALSE )
	{
		jeErrorLog_Add( JE_ERR_FILEIO_READ, NULL );
		goto ERROR_CreateFromFile;
	}

	// all done
	Object->LoadedFromDisk = JE_TRUE;
	return Object;

	// handle errors
	ERROR_CreateFromFile:

	// free all strings
	if ( Object->ArtName != NULL )
	{
		jeRam_Free( Object->ArtName );
	}
	if ( Object->BitmapName != NULL )
	{
		jeRam_Free( Object->BitmapName );
	}
	if ( Object->AlphaName != NULL )
	{
		jeRam_Free( Object->AlphaName );
	}

	// free object
	jeRam_Free( Object );

	// return error
	return NULL;
	NM;

} // CreateFromFile()
#else
void * JETCC CreateFromFile(
	jeVFile		*File,		// vfile to use
	jePtrMgr *PtrMgr )	// pointer manager
{

	// locals
	Spout		*Object;
	int			Size;
	jeBoolean	Result = JE_TRUE;

	// ensure valid data
	assert( File != NULL );

	// create new object
	Object = CreateInstance();
	if ( Object == NULL )
	{
		return NULL;
	}

	// read art name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->ArtName = jeRam_Allocate( Size );
		if ( Object->ArtName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->ArtName, Size );
	}

	// read bitmap name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->BitmapName = jeRam_Allocate( Size );
		if ( Object->BitmapName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->BitmapName, Size );
	}

	// read alpha name
	Result &= jeVFile_Read( File, &( Size ), sizeof( Size ) );
	if ( ( Size > 0 ) && ( Result == JE_TRUE ) )
	{
		Object->AlphaName = jeRam_Allocate( Size );
		if ( Object->AlphaName == NULL )
		{
			jeErrorLog_Add( JE_ERR_MEMORY_RESOURCE, NULL );
			goto ERROR_CreateFromFile;
		}
		Result &= jeVFile_Read( File, Object->AlphaName, Size );
	}

	// read xform
	Result &= jeVFile_Read( File, &( Object->Xf ), sizeof( Object->Xf ) );

	// read gravity
	Result &= jeVFile_Read( File, &( Object->Gravity ), sizeof( Object->Gravity ) );

	// read angle
	Result &= jeVFile_Read( File, &( Object->Angle ), sizeof( Object->Angle ) );

	// read rate
	Result &= jeVFile_Read( File, &( Object->Rate ), sizeof( Object->Rate ) );

	// read min color
	Result &= jeVFile_Read( File, &( Object->MinColor ), sizeof( Object->MinColor ) );

	// read max color
	Result &= jeVFile_Read( File, &( Object->MaxColor ), sizeof( Object->MaxColor ) );

	// read min speed
	Result &= jeVFile_Read( File, &( Object->MinSpeed ), sizeof( Object->MinSpeed ) );

	// read max speed
	Result &= jeVFile_Read( File, &( Object->MaxSpeed ), sizeof( Object->MaxSpeed ) );

	// read min scale
	Result &= jeVFile_Read( File, &( Object->MinScale ), sizeof( Object->MinScale ) );

	// read max scale
	Result &= jeVFile_Read( File, &( Object->MaxScale ), sizeof( Object->MaxScale ) );

	// read min unit life
	Result &= jeVFile_Read( File, &( Object->MinUnitLife ), sizeof( Object->MinUnitLife ) );

	// read max unit life
	Result &= jeVFile_Read( File, &( Object->MaxUnitLife ), sizeof( Object->MaxUnitLife ) );

	// read out draw box
	Result &= jeVFile_Read( File, &( Object->DrawExtBox ), sizeof( Object->DrawExtBox ) );

	// fail if there was an error
	if ( Result == JE_FALSE )
	{
		jeErrorLog_Add( JE_ERR_FILEIO_READ, NULL );
		goto ERROR_CreateFromFile;
	}

	// all done
	Object->LoadedFromDisk = JE_TRUE;
	return Object;

	// handle errors
	ERROR_CreateFromFile:

	// free all strings
	if ( Object->ArtName != NULL )
	{
		jeRam_Free( Object->ArtName );
	}
	if ( Object->BitmapName != NULL )
	{
		jeRam_Free( Object->BitmapName );
	}
	if ( Object->AlphaName != NULL )
	{
		jeRam_Free( Object->AlphaName );
	}

	// free object
	jeRam_Free( Object );

	// return error
	return NULL;

} // CreateFromFile()
#endif


////////////////////////////////////////////////////////////////////////////////////////
//
//	WriteToFile()
//
///////////////////////////////////////////////////////////////////////////////////////
#if NEWSAVE_SPT
jeBoolean JETCC WriteToFile(
	const void	*Instance,
	jeVFile		*File,
	jeNameMgr *NM )
{

	// locals
	Spout		*Object;
	jeBoolean	Result = JE_TRUE;
	int			Size;

	// ensure valid data
	assert( Instance != NULL );
	assert( File != NULL );

	// get object data
	Object = (Spout *)Instance;

	// write art name
	if ( Object->ArtName != NULL )
	{
		Util_WriteString( File, Object->ArtName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write bitmap name
	if ( Object->BitmapName != NULL )
	{
		Util_WriteString( File, Object->BitmapName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write alpha name
	if ( Object->AlphaName != NULL )
	{
		Util_WriteString( File, Object->AlphaName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write xform
	Result &= jeVFile_Write( File, &( Object->Xf ), sizeof( Object->Xf ) );

	// write gravity
	Result &= jeVFile_Write( File, &( Object->Gravity ), sizeof( Object->Gravity ) );

	// write angle
	Result &= jeVFile_Write( File, &( Object->Angle ), sizeof( Object->Angle ) );

	// write rate
	Result &= jeVFile_Write( File, &( Object->Rate ), sizeof( Object->Rate ) );

	// write min color
	Result &= jeVFile_Write( File, &( Object->MinColor ), sizeof( Object->MinColor ) );

	// write max color
	Result &= jeVFile_Write( File, &( Object->MaxColor ), sizeof( Object->MaxColor ) );

	// write min speed
	Result &= jeVFile_Write( File, &( Object->MinSpeed ), sizeof( Object->MinSpeed ) );

	// write max speed
	Result &= jeVFile_Write( File, &( Object->MaxSpeed ), sizeof( Object->MaxSpeed ) );

	// write min scale
	Result &= jeVFile_Write( File, &( Object->MinScale ), sizeof( Object->MinScale ) );

	// write max scale
	Result &= jeVFile_Write( File, &( Object->MaxScale ), sizeof( Object->MaxScale ) );

	// write min unit life
	Result &= jeVFile_Write( File, &( Object->MinUnitLife ), sizeof( Object->MinUnitLife ) );

	// write max unit life
	Result &= jeVFile_Write( File, &( Object->MaxUnitLife ), sizeof( Object->MaxUnitLife ) );

	// write out draw box
	Result &= jeVFile_Write( File, &( Object->DrawExtBox ), sizeof( Object->DrawExtBox ) );

	// log errors
	if ( Result != JE_TRUE )
	{
		jeErrorLog_Add( JE_ERR_FILEIO_WRITE, NULL );
	}

	// all done
	return Result;
	NM;

} // WriteToFile()
#else
	jeBoolean JETCC WriteToFile(
	const void	*Instance,
	jeVFile		*File,
	jePtrMgr *PtrMgr )
{

	// locals
	Spout		*Object;
	jeBoolean	Result = JE_TRUE;
	int			Size;

	// ensure valid data
	assert( Instance != NULL );
	assert( File != NULL );

	// get object data
	Object = (Spout *)Instance;

	// write art name
	if ( Object->ArtName != NULL )
	{
		Util_WriteString( File, Object->ArtName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write bitmap name
	if ( Object->BitmapName != NULL )
	{
		Util_WriteString( File, Object->BitmapName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write alpha name
	if ( Object->AlphaName != NULL )
	{
		Util_WriteString( File, Object->AlphaName );
	}
	else
	{
		Size = 0;
		Result &= jeVFile_Write( File, &Size, sizeof( Size ) );
	}

	// write xform
	Result &= jeVFile_Write( File, &( Object->Xf ), sizeof( Object->Xf ) );

	// write gravity
	Result &= jeVFile_Write( File, &( Object->Gravity ), sizeof( Object->Gravity ) );

	// write angle
	Result &= jeVFile_Write( File, &( Object->Angle ), sizeof( Object->Angle ) );

	// write rate
	Result &= jeVFile_Write( File, &( Object->Rate ), sizeof( Object->Rate ) );

	// write min color
	Result &= jeVFile_Write( File, &( Object->MinColor ), sizeof( Object->MinColor ) );

	// write max color
	Result &= jeVFile_Write( File, &( Object->MaxColor ), sizeof( Object->MaxColor ) );

	// write min speed
	Result &= jeVFile_Write( File, &( Object->MinSpeed ), sizeof( Object->MinSpeed ) );

	// write max speed
	Result &= jeVFile_Write( File, &( Object->MaxSpeed ), sizeof( Object->MaxSpeed ) );

	// write min scale
	Result &= jeVFile_Write( File, &( Object->MinScale ), sizeof( Object->MinScale ) );

	// write max scale
	Result &= jeVFile_Write( File, &( Object->MaxScale ), sizeof( Object->MaxScale ) );

	// write min unit life
	Result &= jeVFile_Write( File, &( Object->MinUnitLife ), sizeof( Object->MinUnitLife ) );

	// write max unit life
	Result &= jeVFile_Write( File, &( Object->MaxUnitLife ), sizeof( Object->MaxUnitLife ) );

	// write out draw box
	Result &= jeVFile_Write( File, &( Object->DrawExtBox ), sizeof( Object->DrawExtBox ) );

	// log errors
	if ( Result != JE_TRUE )
	{
		jeErrorLog_Add( JE_ERR_FILEIO_WRITE, NULL );
	}

	// all done
	return Result;

} // WriteToFile()

#endif


////////////////////////////////////////////////////////////////////////////////////////
//
//	GetPropertyList()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC GetPropertyList(
	void			*Instance,	// object instance data
	jeProperty_List	**List)		// where to save property list pointer
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( List != NULL );

	// get object data
	Object = (Spout *)Instance;

	// setup property list
	SpoutProperties[MP3_LOOP_INDEX].Data.Float = Object->Loop;
	SpoutProperties[MP3_FILE_INDEX].Data.String = Object->File;

	// get property list
	*List = jeProperty_ListCopy( &SpoutPropertyList );
	if ( *List == NULL )
	{
		jeErrorLog_Add( JE_ERR_SUBSYSTEM_FAILURE, NULL );
		return JE_FALSE;
	}

	// reset dirty flag
	SpoutPropertyList.bDirty = JE_FALSE;

	// all done
	return JE_TRUE;

} // GetPropertyList()



////////////////////////////////////////////////////////////////////////////////////////
//
//	SetProperty()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC SetProperty(
	void				*Instance,	// object instance data
	int32				FieldID,	// id of field to be changed
	PROPERTY_FIELD_TYPE	DataType,	// type of data
	jeProperty_Data		*pData )	// new data
{

	// locals
	Spout		*Object;
	jeBoolean	Result = JE_TRUE;

	// ensure valid data
	assert( Instance != NULL );
	assert( pData != NULL );

	// get object data
	Object = (Spout *)Instance;

	// process field id
	switch ( FieldID )
	{

		// adjust loop
		case MP3_LOOP_ID:
		{
			assert( DataType == PROPERTY_FLOAT_TYPE );
			Object->Loop = pData->Float;
			break;
		}

		// adjust filename
		case MP3_FILE_ID:
		{
			assert( DataType == PROPERTY_STRING_TYPE );
			Object->File = pData->String;
			break;
		}

		// toggle draw box
		case MP3_DRAWEXTBOXDISPLAY_ID:
		{
			assert( DataType == PROPERTY_CHECK_TYPE );
			Object->DrawExtBoxDisplay = pData->Bool;
			break;
		}

		// adjust draw extent box size
		case MP3_DRAWEXTBOXMINX_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Min.X;
			Object->DrawExtBox.Min.X = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Min.X = OldVal;
			}
			break;
		}
		case MP3_DRAWEXTBOXMINY_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Min.Y;
			Object->DrawExtBox.Min.Y = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Min.Y = OldVal;
			}
			break;
		}
		case MP3_DRAWEXTBOXMINZ_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Min.Z;
			Object->DrawExtBox.Min.Z = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Min.Z = OldVal;
			}
			break;
		}
		case MP3_DRAWEXTBOXMAXX_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Max.X;
			Object->DrawExtBox.Max.X = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Max.X = OldVal;
			}
			break;
		}
		case MP3_DRAWEXTBOXMAXY_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Max.Y;
			Object->DrawExtBox.Max.Y = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Max.Y = OldVal;
			}
			break;
		}
		case MP3_DRAWEXTBOXMAXZ_ID:
		{
			float	OldVal;
			assert( DataType == PROPERTY_FLOAT_TYPE );
			OldVal = Object->DrawExtBox.Max.Z;
			Object->DrawExtBox.Max.Z = pData->Float;
			if ( jeExtBox_IsValid( &( Object->DrawExtBox ) ) == JE_FALSE )
			{
				Object->DrawExtBox.Max.Z = OldVal;
			}
			break;
		}
		// if we got to here then its an unsupported field
		default:
		{
			assert( 0 );
			Result = JE_FALSE;
			break;
		}
	}

	// all done
	return Result;

	// eliminate warnings
	DataType;

} // SetProperty()



////////////////////////////////////////////////////////////////////////////////////////
//
//	SetXForm()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC SetXForm(
	void			*Instance,	// object instance data
	const jeXForm3d	*Xf )		// new xform
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( Xf != NULL );

	// get object data
	Object = (Spout *)Instance;

	// save xform
	Object->Xf = *Xf;

	// all done
	return JE_TRUE;

} // SetXForm()



////////////////////////////////////////////////////////////////////////////////////////
//
//	GetXForm()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC GetXForm(
	const void	*Instance,	// object instance data
	jeXForm3d	*Xf )		// where to store xform
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
	assert( Xf != NULL );

	// get object data
	Object = (Spout *)Instance;

	// save xform
	*Xf = Object->Xf;

	// all done
	return JE_TRUE;

} // GetXForm()



////////////////////////////////////////////////////////////////////////////////////////
//
//	GetXFormModFlags()
//
///////////////////////////////////////////////////////////////////////////////////////
int	JETCC GetXFormModFlags(
	const void	*Instance )	// object instance data
{

	// return xform mod flags
	return ( JE_OBJECT_XFORM_TRANSLATE | JE_OBJECT_XFORM_ROTATE );

	// eliminate warnings
	Instance;

} // GetXFormModFlags()



////////////////////////////////////////////////////////////////////////////////////////
//
//	GetChildren()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC GetChildren(
	const void	*Instance,
	jeObject	*Children,
	int			MaxNumChildren )
{

	// all done
	return JE_TRUE;

	// eliminate warnings
	Instance;
	Children;
	MaxNumChildren;

} // GetChildren()



////////////////////////////////////////////////////////////////////////////////////////
//
//	AddChild()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC AddChild(
	void			*Instance,
	const jeObject	*Child )
{

	// all done
	return JE_TRUE;

	// eliminate warnings
	Instance;
	Child;

} // AddChild()



////////////////////////////////////////////////////////////////////////////////////////
//
//	RemoveChild()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC RemoveChild(
	void			*Instance,
	const jeObject	*Child )
{

	// all done
	return JE_TRUE;

	// eliminate warnings
	Instance;
	Child;

} // RemoveChild()



////////////////////////////////////////////////////////////////////////////////////////
//
//	EditDialog()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC EditDialog(
	void	*Instance,
	HWND	Parent )
{

	// all done
	return JE_TRUE;

	// eliminate warnings
	Instance;
	Parent;

} // EditDialog()



////////////////////////////////////////////////////////////////////////////////////////
//
//	Frame()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC Frame(
	void	*Instance,
	float	TimeDelta )
{

	// locals
	Spout	*Object;

	// ensure valid data
	assert( Instance != NULL );
		
	// do nothing if no time has elapsed
	if ( TimeDelta == 0.0f )
	{
		if (f)
		{
			fprintf(f, "Exit->TimeDelta");
		}
		play = JE_FALSE;
		return JE_TRUE;
	}

	// get object
	Object = (Spout *)Instance;

	// do nothing if no bitmap has been selected
	//if ( Object->File == NULL )
	//{
	//	return JE_TRUE;
	//}

	// add to elapsed time
	Object->TimeElapsed += TimeDelta;

	
	//do nothing more if an MP3 is playing
	//if (!Mp3Playing())
	//{
		//OpenMediaFile(Object->File);
	
	if(play == JE_FALSE)
	{
		OpenMediaFile("c:\\blue.mp3");
		PlayMp3(0, JE_FALSE);
		play = JE_TRUE;
	}
	//}

	// all done
	return JE_TRUE;
	
} // Frame()



////////////////////////////////////////////////////////////////////////////////////////
//
//	SendAMessage()
//
///////////////////////////////////////////////////////////////////////////////////////
jeBoolean JETCC SendAMessage(
	void	*Instance,	// object instance data
	int32	Msg,		// message id
	void	*Data )		// message data
{

	// all done
	return JE_FALSE;

	// eliminate warnings
	Instance;
	Msg;
	Data;

} // SendAMessage()
