#include "tags.h"
#include "tagprivate.h"

#include "soun.h"
#include "stli.h" // for misc string list functions

#include "sound_intermediate.h"
#include "adpcm.h"

#ifndef PandaWave_RESOURCES
#include "RevengeResources.h"
#endif

#include "RevengeApp.h"
#include <XMessages.h>
#include "tagpick.h"
#include <assert.h>
#include <XPopup.h>
#include <XWindow.h>
#include <XTextControls.h>
#include <XButtons.h>
#include <XList.h>
#include "formatters.h"
#include "vengeance_script.h"

#if OPT_WINOS
#include <mmsystem.h>
#include <dsound.h>

static LPDIRECTSOUND lpDirectSound = NULL;
static LPDIRECTSOUNDBUFFER lpDsb = NULL;
#endif


int LoadSoundFile( sound_definition &def, XGFileSpecifier *fs, int index = -1 );

// ==============================  BYTE SWAPPING DATA  ========================================
static byte_swap_code sound_definition_byte_swapping_data[]=
{
	_begin_bs_array, 1,
	
	// unsigned long flags;
	_4byte,

	// short loudness; // also used as priority
	// fixed_fraction play_fraction; // not played if .play_fraction<local_random()
	_2byte,
	_2byte,

	// short_fixed external_frequency_modifier; // cannot change frequencies if zero
	// short_fixed pitch_lower_bound, pitch_delta;
	// short_fixed volume_lower_bound, volume_delta; // only intended for random sounds
	_2byte,
	_2byte, _2byte,
	_2byte, _2byte,

	// short first_subtitle_within_string_list_index;
	_2byte,
	
	// long sound_offset;
	// long sound_size;
	_4byte,
	_4byte,
	
	// file_tag subtitle_string_list_tag;
	_4byte,
	
	// short subtitle_string_list_index;
	// word unused;
	2*sizeof(short),

	// long number_of_permutations, permutations_offset, permutations_size;
	// struct sound_permutation_data *sound_permutations;
	_4byte, _4byte, _4byte,
	sizeof(struct sound_permutation_data *),

	// short reference_count;
	// word permutations_played_flags;
	// unsigned long local_tick_last_played; // for theoretical disposing of least recently used sounds
	// struct sampled_sound_header *sampled_sound_headers; // for the current encoding, NULL if not loaded
	sizeof(short),
	sizeof(word),
	sizeof(unsigned long),
	sizeof(struct sampled_sound_header *),
	
	_end_bs_array
};

byte_swap_code sampled_sound_header_byte_swapping_data[]=
{
	_begin_bs_array, 1,
	
	// unsigned long flags;
	_4byte,
	
	// short logical_bits_per_sample; // 8 or 16
	// short physical_bytes_per_sample_minus_one;
	// short channels;
	// word pad;
	_2byte,
	_2byte,
	_2byte,
	sizeof(word),
	
	// fixed sample_rate;
	// long number_of_samples;
	_4byte,
	_4byte,

	// long loop_start, loop_end;
	_4byte, _4byte,

	// void *samples;
	sizeof(void *),
	
	_end_bs_array,
};

byte_swap_code sound_permutation_data_byte_swapping_data[]=
{
	_begin_bs_array, 1,
	
	// word flags;
	// fixed_fraction skip_fraction;
	_2byte, _2byte,
	
	// short unused[1];
	1*sizeof(short),
	
	// char name[MAXIMUM_SOUND_PERMUTATION_NAME_LENGTH+1];
	MAXIMUM_SOUND_PERMUTATION_NAME_LENGTH+1,
	
	_end_bs_array,
};

sound_definition default_soun =
{
	0,
	0,
	FIXED_ONE,
	SHORT_FIXED_ONE,
	SHORT_FIXED_ONE, 0,
	SHORT_FIXED_ONE, 0,
	0,
	sizeof(sound_definition),
	0,
	-1,
	-1,
	0,
	0, sizeof(sound_definition), 0,
	0,
	0,
	0,
	0,
	0
};


// ==============================  EDIT WINDOW  ========================================
void edit_soun( tag_entry* e )
{
	TsounEditForm *form = new TsounEditForm( e );
	form->AttachWindow( 1023 );
}

void defaultnew_soun( tag_entry *e )
{
	sound_definition def;
	byte_swap_move_be( "soun", &def, &default_soun, sizeof(sound_definition), 1, sound_definition_byte_swapping_data );
	set_entry_data( e, &def, sizeof(def), false );
}

void dependency_soun( tag_entry *e )
{
	sound_definition def;
	read_sound_definition( &def, e );
	add_tag_dependency( 'stli', def.subtitle_string_list_tag );
}


// ==============================  READ TAG DATA  ========================================
bool read_sound_definition( sound_definition* def, tag_entry* e )
{
	sound_permutation_data *tag_sound_permutation_data;
	sampled_sound_header *tag_sampled_sound_headers;
	int sample_offset = 0;
	int sample_size = 0;
	int ii;
	
	if( load_tag_data_into_struct( "soun", e, def, sizeof(sound_definition), 1, sound_definition_byte_swapping_data) )
	{
		// sanity checks
		if( def->number_of_permutations < 0 || def->number_of_permutations > MAXIMUM_PERMUTATIONS_PER_SOUND )
		{
			return FALSE;
		}
		
		assert( def->permutations_size == def->number_of_permutations * sizeof(sound_permutation_data) );
		assert( def->permutations_offset == sizeof(sound_definition) );
		assert( def->sound_offset == sizeof(sound_definition) + def->number_of_permutations * sizeof(sound_permutation_data) );
		
		// alloc our permutation and sample header buffers
		def->sound_permutations = (sound_permutation_data *)malloc( sizeof(sound_permutation_data) * MAXIMUM_PERMUTATIONS_PER_SOUND );
		def->sampled_sound_headers = (sampled_sound_header *)malloc( sizeof(sampled_sound_header) * MAXIMUM_PERMUTATIONS_PER_SOUND );
		
		memset( def->sound_permutations, 0x00, MAXIMUM_PERMUTATIONS_PER_SOUND * 5 );
		memset( def->sampled_sound_headers, 0x00, MAXIMUM_PERMUTATIONS_PER_SOUND * 5 );

		// build the permutation and sample offset pointers
		tag_sound_permutation_data = (sound_permutation_data *)((unsigned long)e->data + def->permutations_offset);
		tag_sampled_sound_headers = (sampled_sound_header *)((unsigned long)e->data + def->sound_offset);
		
		sample_offset = sizeof(sound_definition) + ((sizeof(sound_permutation_data) + sizeof(sampled_sound_header)) * def->number_of_permutations);
		
		// swap the permutation data, sampled sound headers, sound samples, and copy into our buffers
		for( ii = 0; ii < def->number_of_permutations; ii++ )
		{
			memcpy( &def->sound_permutations[ii], &tag_sound_permutation_data[ii], sizeof(sound_permutation_data) );
			byte_swap_data_be( "sound permutation", &def->sound_permutations[ii], sizeof(sound_permutation_data), 1, sound_permutation_data_byte_swapping_data );
			
			memcpy( &def->sampled_sound_headers[ii], &tag_sampled_sound_headers[ii], sizeof(sampled_sound_header) );
			byte_swap_data_be( "sample headers", &def->sampled_sound_headers[ii], sizeof(sampled_sound_header), 1, sampled_sound_header_byte_swapping_data );
			
			// alloc a buffer for the samples
			if( def->sampled_sound_headers[ii].flags & _sound_is_ima_compressed_flag )
			{
				sample_size = def->sampled_sound_headers[ii].number_of_samples * sizeof(apple_ima_packet_chunk);
				def->sampled_sound_headers[ii].samples = malloc( sample_size );
			}
			else
			{
				sample_size = def->sampled_sound_headers[ii].number_of_samples << def->sampled_sound_headers[ii].physical_bytes_per_sample_minus_one;
				def->sampled_sound_headers[ii].samples = malloc( sample_size );
			}
			
			memcpy( def->sampled_sound_headers[ii].samples, (void *)((unsigned long)e->data + sample_offset), sample_size  );
			sample_offset += sample_size;
		}
		
		return TRUE;
	}
	
	return FALSE;
}

// ==============================  SAVE TAG DATA  ========================================
bool save_sound_definition( sound_definition* def, tag_entry* e )
{
	void *sound_tag = NULL;
	
	sound_definition *definition;
	sound_permutation_data *permutations;
	sampled_sound_header *headers;
	byte *samples;
	
	int new_tag_size = 0;
	int sample_size = 0;
	int c_sample_sizes = 0;
	
	int ii;
	
	assert( def->permutations_offset == sizeof(sound_definition) );
	assert( def->permutations_size == sizeof(sound_permutation_data) * def->number_of_permutations );
	assert( def->sound_offset == sizeof(sound_definition) + sizeof(sound_permutation_data) * def->number_of_permutations );

	new_tag_size = sizeof(sound_definition) + (sizeof(sound_permutation_data) + sizeof(sampled_sound_header)) * def->number_of_permutations;
	
	for( ii = 0; ii < def->number_of_permutations; ii++ )
	{
		if( def->sampled_sound_headers[ii].flags & _sound_is_ima_compressed_flag )
			new_tag_size += def->sampled_sound_headers[ii].number_of_samples * sizeof(apple_ima_packet_chunk);
		else
			new_tag_size += def->sampled_sound_headers[ii].number_of_samples << def->sampled_sound_headers[ii].physical_bytes_per_sample_minus_one;
	}
	
	sound_tag = malloc( new_tag_size );
	definition = (sound_definition *)sound_tag;
	permutations = (sound_permutation_data *)((unsigned long)sound_tag + sizeof(sound_definition));
	headers = (sampled_sound_header *)((unsigned long)permutations + sizeof(sound_permutation_data) * def->number_of_permutations);

	// copy the sound tag header	
	memcpy( sound_tag, def, sizeof(sound_definition) );
	
	// don't save the offset pointers into the tag
	((sound_definition *)sound_tag)->sound_permutations = NULL;
	((sound_definition *)sound_tag)->sampled_sound_headers = NULL;
	
	// copy the permutation and sample headers
	memcpy( permutations, def->sound_permutations, sizeof(sound_permutation_data) * def->number_of_permutations );
	memcpy( headers, def->sampled_sound_headers, sizeof(sampled_sound_header) * def->number_of_permutations );

	// copy the samples into a linear buffer
	samples = (byte *)((unsigned long)sound_tag + sizeof(sound_definition));
	samples = (byte *)((unsigned long)samples + ((sizeof(sound_permutation_data) + sizeof(sampled_sound_header)) * def->number_of_permutations));
	
	for( ii = 0; ii < def->number_of_permutations; ii++ )
	{
		if( def->sampled_sound_headers[ii].flags & _sound_is_ima_compressed_flag )
			sample_size = def->sampled_sound_headers[ii].number_of_samples * sizeof(apple_ima_packet_chunk);
		else
			sample_size = def->sampled_sound_headers[ii].number_of_samples << def->sampled_sound_headers[ii].physical_bytes_per_sample_minus_one;
	
		memcpy( samples, def->sampled_sound_headers[ii].samples, sample_size );
		headers[ii].samples = samples;
		samples = (byte *)((unsigned long)(samples) + sample_size);
		
		c_sample_sizes += sample_size;
	}
	
	// update the sound size in the header
	assert( c_sample_sizes == new_tag_size - sizeof(sound_definition) - (sizeof(sound_permutation_data) + sizeof(sampled_sound_header)) * def->number_of_permutations );
	definition->sound_size = c_sample_sizes + (sizeof(sampled_sound_header) * def->number_of_permutations); // I think sound_size is supposed to include the sample headers

	// swap everything now
	byte_swap_data_be( "soun", definition, sizeof(sound_definition), 1, sound_definition_byte_swapping_data );
	byte_swap_data_be( "sound permutations", permutations, sizeof(sound_permutation_data), def->number_of_permutations, sound_permutation_data_byte_swapping_data );
	byte_swap_data_be( "sample headers", headers, sizeof(sampled_sound_header), def->number_of_permutations, sampled_sound_header_byte_swapping_data );

	// set the tag
	set_entry_data( e, sound_tag, new_tag_size, TRUE );

	// we need to re-read this so the def pointers get setup correctly
	read_sound_definition( def, e );
	
	return TRUE;
}

// ==============================  READ TAG DATA (WITH ID) ========================================
bool read_sound_definition( sound_definition* def, tag id )
{
	tag_entry* e;
	
	assert( def );
	
	e = get_entry( 'soun', id );
	if( 0 == e ) {
		return FALSE;
	}
	
	return read_sound_definition( def, e );	
}


// ==============================  SAVE TAG DATA (WITH ID) ========================================
bool save_sound_definition( sound_definition* def, tag id )
{
	tag_entry* e;
	
	assert( def );
	
	e = get_entry( 'soun', id );
	if( 0 == e ) {
		return FALSE;
	}

	return save_sound_definition( def, e );
}


// ==============================  DISPATCH  ========================================
BEGINDISPATCH(TsounEditForm,TTagEditForm)
	DISPATCHTABLE(KXGControlClick,TsounEditForm::DoControlClick)
	DISPATCHTABLE(KXGListValue,TsounEditForm::DoListSelect)
	DISPATCHTABLE(KXGKeyClick,TsounEditForm::DoKeyClick)
ENDDISPATCH

TsounEditForm::TsounEditForm( struct tag_entry *e ) : TTagEditForm( e )
{
	def.sound_permutations = NULL;
	def.sampled_sound_headers = NULL;

#if OPT_WINOS	
	lpDsb = NULL;

	if( !lpDirectSound )
	{
		if( DirectSoundCreate( NULL, &lpDirectSound, NULL ) != DS_OK )
		{
			// FIXME: notify the user of the error
		}
	}
#endif	
}

TsounEditForm::~TsounEditForm()
{
	int ii;

	if( def.sampled_sound_headers )
	{	
		for( ii = 0; ii < def.number_of_permutations; ii++ )
		{
			if( def.sampled_sound_headers[ii].samples )
				free( def.sampled_sound_headers[ii].samples );
		}
		
		free( def.sampled_sound_headers );
	}
			
	if( def.sound_permutations )
		free( def.sound_permutations );
		
#if OPT_WINOS
	if( lpDsb )
		lpDsb->Release();
#endif
}


// ==============================  INITIALIZE FORM =======================================
void TsounEditForm::DoFormInitialize()
{
	TTagEditForm::DoFormInitialize();

#if OPT_WINOS
	if( lpDirectSound )
		lpDirectSound->SetCooperativeLevel( GetWindow()->GetHWND(), DSSCL_NORMAL );
#endif
}


// ==============================  INPUT HANDLING  ========================================
long TsounEditForm::DoControlClick( long arg, void *ptr )
{
#ifdef __MWERKS__
	#pragma unused(ptr)
#endif

	XGListControl *pList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	int sel = pList->GetValue();

	tag_entry* e;
	tag new_id;
	
	switch( arg )
	{
		case 12302: // Choose a tag
			new_id = pick_tag_of_type( 'stli', GetTag( 12302 ), fEntry );
			if( new_id != 0 )
			{
				def.subtitle_string_list_tag = new_id;
				
				SetTag( 12302, 'stli', new_id );
				SetValues();
			}
			
		break; 
		
		case 12303: // Open tag window
			e = get_entry( 'stli', GetTag( 12302 ) );
			if( e )
				TRevengeApp::OpenItem( e );
		break;
		
		case 12315: // Add
			assert( def.number_of_permutations < MAXIMUM_PERMUTATIONS_PER_SOUND );
			
			XGFileSpecifier *fs = NULL;

#if OPT_MACOS
			fs = XGFileSpecifier::OpenDialog( 129 );
#endif
#if OPT_WINOS
			fs = XGFileSpecifier::OpenDialog( "Wave Audio File|*.wav","WAV" );
#endif
#if OPT_XWIN
			fs = XGFileSpecifier::OpenDialog( "wav" );
#endif
		
		if( !fs )
			return 0;
		
		if( LoadSoundFile( def, fs ) == LOAD_SOUND_ERROR_SUCCESS )
		{
			pList->Insert( def.number_of_permutations, def.sound_permutations[def.number_of_permutations].name );
			pList->SetValue( def.number_of_permutations );
			
			def.number_of_permutations++;
			
			// update perutations size and sound_offset
			def.permutations_size += sizeof(sound_permutation_data);
			def.sound_offset += sizeof(sound_permutation_data);
			
			DoListSelect( 12314, NULL );
		}
		
		delete fs;
			
		break;
		
		case 12316: // Duplicate
		{
			assert( def.number_of_permutations < MAXIMUM_PERMUTATIONS_PER_SOUND );
			assert( sel != -1 && sel < def.number_of_permutations && sel < MAXIMUM_PERMUTATIONS_PER_SOUND );
			
			int perm = def.number_of_permutations;
			int sample_size;
			
			// update our sound_size
			if( def.sampled_sound_headers[sel].flags & _sound_is_ima_compressed_flag )
				sample_size = def.sampled_sound_headers[sel].number_of_samples * sizeof(apple_ima_packet_chunk);
			else
				sample_size = def.sampled_sound_headers[sel].number_of_samples << def.sampled_sound_headers[sel].physical_bytes_per_sample_minus_one;
			
			def.sound_size += (sample_size + sizeof(sampled_sound_header));
			
			// copy the headers to the new permutaion slot
			def.sound_permutations[perm] = def.sound_permutations[sel];
			def.sampled_sound_headers[perm] = def.sampled_sound_headers[sel];
			
			// make a duplicate of the samples buffer
			assert( def.sampled_sound_headers[sel].samples );
			
			def.sampled_sound_headers[perm].samples = malloc( sample_size );
			memcpy( def.sampled_sound_headers[perm].samples, def.sampled_sound_headers[sel].samples, sample_size );

			def.number_of_permutations++;

			// update perutations size and sound_offset
			def.permutations_size += sizeof(sound_permutation_data);
			def.sound_offset += sizeof(sound_permutation_data);
			
			// update the permutation list			
			SetValues();
		}
		
		break;
		
		case 12317: // Delete
		{
			assert( def.number_of_permutations > 0 );
			assert( sel > -1 && sel < def.number_of_permutations && sel < MAXIMUM_PERMUTATIONS_PER_SOUND );
			
			int sample_size;
			int ii;
			
			// update our sound_size
			if( def.sampled_sound_headers[sel].flags & _sound_is_ima_compressed_flag )
				sample_size = def.sampled_sound_headers[sel].number_of_samples * sizeof(apple_ima_packet_chunk);
			else
				sample_size = def.sampled_sound_headers[sel].number_of_samples << def.sampled_sound_headers[sel].physical_bytes_per_sample_minus_one;
				
			def.sound_size -= (sample_size - sizeof(sampled_sound_header));
			assert( def.sound_size >= 0 );
			
			// free any data associated with our victim permutation
			assert( def.sampled_sound_headers[sel].samples ); // there is something wrong if no data here
			if( def.sampled_sound_headers[sel].samples )
				free( def.sampled_sound_headers[sel].samples );
			
			// move everything past the sel back one
			for( ii = sel; ii + 1 < def.number_of_permutations; ii++ )
			{
				def.sound_permutations[ii] = def.sound_permutations[ii + 1];
				def.sampled_sound_headers[ii] = def.sampled_sound_headers[ii + 1];
			}
			
			// zero out the extra permutation
			memset( &def.sound_permutations[ii], 0x00, sizeof(sound_permutation_data) );
			memset( &def.sampled_sound_headers[ii], 0x00, sizeof(sampled_sound_header) );
			
			def.number_of_permutations--;

			// update perutations size and sound_offset
			def.permutations_size -= sizeof(sound_permutation_data);
			def.sound_offset -= sizeof(sound_permutation_data);
			
			// remove the item from the permutation list
			SetValues();
		}

		break;
		
		case 12319: // Play sound
		{
			assert( def.number_of_permutations > 0 );
			assert( sel > -1 && sel < def.number_of_permutations && sel < MAXIMUM_PERMUTATIONS_PER_SOUND );
			
			PlaySoundFile();
		}
		
		break;
		
		default:
			return -1;
	}
	
	UpdatePermutationControls();
	
	return 0;
}

long TsounEditForm::DoListSelect( int arg, void *ptr )
{
#ifdef __MWERKS__
	#pragma unused(ptr)
#endif

	XGListControl *pList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	XGEditText *pEdit = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12318 ));
	XGEditText *pSkip = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12323 ));
	XGStaticText *pText;
	int sel;
	
	char string[256] = { 0 };
	float sample_size;
	
	sel = GetInt( arg );
	
	switch( arg )
	{
		case 12314: // permutation list
			if( sel >= 0 && sel < def.number_of_permutations )
			{
				// fill the controls that depend on the permutation
				// bits, sample rate
				memset( string, 0x00, sizeof(string) );
				sprintf( string, "%d bits, %d hz", def.sampled_sound_headers[sel].logical_bits_per_sample, (def.sampled_sound_headers[sel].sample_rate >> 16) & 0xffff );
				
				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12320 ));
				pText->SetTitle( string );

				// sound size
				memset( string, 0x00, sizeof(string) );
				
				if( def.sampled_sound_headers[sel].flags & _sound_is_ima_compressed_flag )
					sample_size = (float)(def.sampled_sound_headers[sel].number_of_samples * sizeof(apple_ima_packet_chunk));
				else
					sample_size = (float)(def.sampled_sound_headers[sel].number_of_samples << def.sampled_sound_headers[sel].physical_bytes_per_sample_minus_one);
				
				sprintf( string, "%.1f k", sample_size / 1024 );
				
				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12321 ));
				pText->SetTitle( string );
				
				// mono/stereo
				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12322 ));
				pText->SetTitle( (def.sampled_sound_headers[sel].channels == 1) ? "mono" : "stereo" );
				
				pEdit->SetTitle( def.sound_permutations[sel].name );
				
				// skip percentage
				memset( string, 0x00, sizeof(string) );
				
				sprintf( string, "%.1f", percentage_to_float( def.sound_permutations[sel].skip_fraction ) * 100 );
				pSkip->SetTitle( string );
			}
			else
			{
				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12320 ));
				pText->SetTitle( "" );
				
				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12321 ));
				pText->SetTitle( "" );

				pText = dynamic_cast<XGStaticText *>(GetWindow()->FindViewByID( 12322 ));
				pText->SetTitle( "" );
				
				pEdit->SetTitle( "" );
				
				pSkip->SetTitle( "" );
			}
			
			UpdatePermutationControls();
			
			return 0;
	}
	
	return -1;
}

long TsounEditForm::DoKeyClick( int arg, char *str )
{
	XGListControl *pList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	XGEditText *pEdit = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12318 ));
	XGEditText *pSkip = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12323 ));
	int sel = pList->GetValue();
	
	assert( sel >= -1 && sel < def.number_of_permutations );

	if( sel != -1 )
	{
		switch( arg )
		{
			case 12318: // Permutation Name
				// save the new name into our data and truncate it to maximum length
				strncpy( def.sound_permutations[sel].name, str, MAXIMUM_SOUND_PERMUTATION_NAME_LENGTH );

				pEdit->SetTitle( def.sound_permutations[sel].name );
				pList->SetItem( sel, def.sound_permutations[sel].name );
				
				return 0;
				
			case 12323: // Skip Fraction
				float skip = GetFloat( 12323 );
				def.sound_permutations[sel].skip_fraction = float_to_percentage( skip / 100 );
					
				return -1; // don't override any OS stuff with this
		}
	}
	
	return -1;
}

void TsounEditForm::UpdatePermutationControls()
{
	XGListControl *pList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	XGCommandButton *pButton;
	XGEditText *pEdit;

	// Add
	pButton = dynamic_cast<XGCommandButton *>(GetWindow()->FindViewByID( 12315 ));
	pButton->SetEnabled( (def.number_of_permutations < MAXIMUM_PERMUTATIONS_PER_SOUND) ? true : false );
	
	// Duplicate
	pButton = dynamic_cast<XGCommandButton *>(GetWindow()->FindViewByID( 12316 ));
	pButton->SetEnabled( ((pList->GetValue() != -1) && (def.number_of_permutations <= MAXIMUM_PERMUTATIONS_PER_SOUND)) ? true : false );

	// Delete
	pButton = dynamic_cast<XGCommandButton *>(GetWindow()->FindViewByID( 12317 ));
	pButton->SetEnabled( ((pList->GetValue() != -1) && (def.number_of_permutations > 0)) ? true : false );
	
	// Permutation Name
	pEdit = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12318 ));
	pEdit->SetEnabled( (pList->GetValue() != -1) ? true : false );
	
	// Play Sound
	pButton = dynamic_cast<XGCommandButton *>(GetWindow()->FindViewByID( 12319 ));
	pButton->SetEnabled( (pList->GetValue() != -1) ? true : false );
	
	// Skip Percentage
	pEdit = dynamic_cast<XGEditText *>(GetWindow()->FindViewByID( 12323 ));
	pEdit->SetEnabled( (pList->GetValue() != -1) ? true : false );
}

		
// ========================================  SAVE TO TAG  ==================================================
void TsounEditForm::CommitToTag()
{
	GetValues();
	save_sound_definition( &def, fEntry );
}


// ========================================  RELOAD TAG  ==================================================
void TsounEditForm::ReloadTag()
{
	read_sound_definition( &def, fEntry );
	SetValues();
}


// ========================================  TAG NEEDS UPDATING  ==================================================
bool TsounEditForm::NeedCommit()
{
	// make a copy of the new and old sound definitions, zero out the pointers and compare them
	sound_definition olddef;
	sound_definition newdef;
	int sample_size;
	int ii;
	
	bool bNeedCommit = false;


	GetValues();
	memcpy( &newdef, &def, sizeof(sound_definition) );

	read_sound_definition( &olddef, fEntry );
	
	if( newdef.number_of_permutations == olddef.number_of_permutations )
	{
		for( ii = 0; ii < olddef.number_of_permutations; ii++ )
		{
			// compare permutation data
			if( memcmp( &newdef.sound_permutations[ii], &olddef.sound_permutations[ii], sizeof(sound_permutation_data) ) )
			{
				bNeedCommit = true;
				break;
			}
			
			// compare sample headers
			if( memcmp( &newdef.sampled_sound_headers[ii], &olddef.sampled_sound_headers[ii], sizeof(sampled_sound_header) - 4 ) )
			{
				bNeedCommit = true;
				break;
			}

			// compare samples
			if( newdef.sampled_sound_headers[ii].number_of_samples != olddef.sampled_sound_headers[ii].number_of_samples )
			{
				bNeedCommit = true;
				break;
			}
				
			if( olddef.sampled_sound_headers[ii].flags & _sound_is_ima_compressed_flag )
				sample_size = olddef.sampled_sound_headers[ii].number_of_samples * sizeof(apple_ima_packet_chunk);
			else
				sample_size = olddef.sampled_sound_headers[ii].number_of_samples << olddef.sampled_sound_headers[ii].physical_bytes_per_sample_minus_one;
			
			if( memcmp( newdef.sampled_sound_headers[ii].samples, olddef.sampled_sound_headers[ii].samples, sample_size ) )
			{
				bNeedCommit = true;
				break;
			}
		}

		if( olddef.sampled_sound_headers )
		{	
			for( ii = 0; ii < olddef.number_of_permutations; ii++ )
			{
				if( olddef.sampled_sound_headers[ii].samples )
					free( olddef.sampled_sound_headers[ii].samples );
			}
			
			free( olddef.sampled_sound_headers );
		}
		
		if( olddef.sound_permutations )
			free( olddef.sound_permutations );
		
		newdef.sound_permutations = NULL;
		newdef.sampled_sound_headers = NULL;
		
		olddef.sound_permutations = NULL;
		olddef.sampled_sound_headers = NULL;

		if( memcmp( &newdef, &olddef, sizeof(sound_definition) ) )
			bNeedCommit = true;
	}
	else
		bNeedCommit = true;
	
	return bNeedCommit ? TRUE : FALSE;
}

		
// ========================================  SET INTERFACE VALUES  ==================================================
void TsounEditForm::SetValues()
{
	tag_entry *subtitle_strings;
	unsigned string_count;
	int ii;
	
	char string[1024];

	// subtitles tag
	SetTag( 12302, 'stli', def.subtitle_string_list_tag );

	// loudness
	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12301 ));
	assert(pList1);
	pList1->DeleteAll();
	pList1->Insert(0, "Quiet");
	pList1->Insert(1, "Normal");
	pList1->Insert(2, "Loud");
	pList1->Insert(3, "Narration");
	pList1->SetValue( def.loudness);

	// fill the list box with the actual strings from the string list, and select the proper index
	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12304 ));
	assert(pList2);
	pList2->DeleteAll();

	// none is always an option
	pList2->Insert( 0, "None" );

	subtitle_strings = get_entry( 'stli', def.subtitle_string_list_tag );
	if( subtitle_strings )
	{
		string_count = count_strings_in_list( subtitle_strings );
		if( string_count != -1 )
		{
			for( ii = 0; ii < string_count + 1; ii++ )
			{
				get_string_at_index( subtitle_strings, ii, string, sizeof(string) );
				pList2->Insert( ii + 1, string );
			}
			
			pList2->SetValue( def.first_subtitle_within_string_list_index + 1 );
		}
	}
	else
	{
		pList2->SetValue( 0 );
	}

	// set all those neat values	
	SetFloat( 12305, percentage_to_float( def.play_fraction ) * 100, 2 );
	SetShortFixed( 12306, def.external_frequency_modifier );
	SetShortFixed( 12307, def.pitch_lower_bound );
	SetShortFixed( 12308, get_upper_bound( def.pitch_lower_bound, def.pitch_delta ) );
	SetShortFixed( 12324, def.volume_lower_bound );
	SetShortFixed( 12325, get_upper_bound( def.volume_lower_bound, def.volume_delta ) );

	// set the flags
	SetFlag( 12309, FALSE );
	if(def.flags&_sound_cannot_be_restarted_flag ) SetFlag( 12309, TRUE );
	SetFlag( 12310, FALSE );
	if(def.flags&_sound_can_occupy_multiple_channels_flag ) SetFlag( 12310, TRUE );
	SetFlag( 12311, FALSE );
	if(def.flags&_sound_cannot_be_media_obstructed_flag ) SetFlag( 12311, TRUE );
	SetFlag( 12312, FALSE );
	if(def.flags&_sound_all_permutations_use_same_subtitle_flag ) SetFlag( 12312, TRUE );
	SetFlag( 12313, FALSE );
	if(def.flags&_sound_stopped_when_source_disappears_flag ) SetFlag( 12313, TRUE );
	
	// setup the permutation list
	XGListControl *pList3 = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	pList3->DeleteAll();
	
	for( ii = 0; ii < def.number_of_permutations; ii++ )
	{
		pList3->Insert( ii, def.sound_permutations[ii].name );
	}
	
	pList3->SetValue( (def.number_of_permutations > 0) ? 0 : -1 );
	DoListSelect( 12314, NULL );
}


// ========================================  GET INTERFACE VALUES  ==================================================
void TsounEditForm::GetValues()
{
	// subtitles tag
	def.subtitle_string_list_tag = GetTag( 12302 );

	// loudness
	XGPopControl* pList1;
	pList1 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12301 ));
	assert(pList1);
	def.loudness = pList1->GetValue();

	// acutal string in the subtitle list
	XGPopControl* pList2;
	pList2 = dynamic_cast<XGPopControl*>(GetWindow()->FindViewByID( 12304 ));
	assert(pList2);
	def.first_subtitle_within_string_list_index = pList2->GetValue() - 1;

	// get all those neat values	
	def.play_fraction = float_to_percentage( GetFloat( 12305 ) / 100 );
	def.external_frequency_modifier = GetShortFixed( 12306 );
	def.pitch_lower_bound = GetShortFixed( 12307 );
	def.pitch_delta = get_delta( GetShortFixed( 12307 ), GetShortFixed( 12308 ) );
	def.volume_lower_bound = GetShortFixed( 12324 );
	def.volume_delta = get_delta( GetShortFixed( 12324 ), GetShortFixed( 12325 ) );

	// get the flags
	def.flags = 0;
	if(GetFlag( 12309 )) def.flags |= _sound_cannot_be_restarted_flag;
	if(GetFlag( 12310 )) def.flags |= _sound_can_occupy_multiple_channels_flag;
	if(GetFlag( 12311 )) def.flags |= _sound_cannot_be_media_obstructed_flag;
	if(GetFlag( 12312 )) def.flags |= _sound_all_permutations_use_same_subtitle_flag;
	if(GetFlag( 12313 )) def.flags |= _sound_stopped_when_source_disappears_flag;

	// nothing to do on permutations, they are kept updated
}


// =====================================  SOUND PROCESSING / HANDLING  ===============================================
#if OPT_MACOS
#endif

#if OPT_WINOS
int LoadSoundFile( sound_definition &def, XGFileSpecifier *fs, int index )
{
	WAVEFORMAT *pwf = NULL;
	byte *pData = NULL;
	long lSize;

	WAVEFORMATEX *pwfx = NULL;

	intermediate_sound_data *isd;
	byte *pBits = NULL;
	
	int perm;
	char perm_name[_MAX_FNAME] = { 0 };
	
	int ii;
	
	if( !read_wave_file( fs, &pwf, &pData, &lSize ) )
		return LOAD_SOUND_ERROR_FAILURE;
	
	pwfx = reinterpret_cast<WAVEFORMATEX *>(pwf);
	
	if( pwfx->wBitsPerSample != 16 )
	{
		// FIXME: add a message box indicating that 16 bits is required
		return LOAD_SOUND_ERROR_NOT_16_BIT;
	}
	
	if( pwfx->nSamplesPerSec != 22050 )
	{
		// FIXME: add a message box indicating that 22050 khz is required
		return LOAD_SOUND_ERROR_NOT_22050;
	}
	
	isd = new_intermediate_sound();
	
	initialize_sound_header(
		isd,
		_no_compression,
		lSize / (pwfx->wBitsPerSample >> 3) / (pwfx->nChannels),
		pwfx->wBitsPerSample,
		pwfx->nSamplesPerSec,
		pwfx->nChannels
		);
		
	set_sound_samples( isd, pData );
	
	// compress this thing
	set_intermediate_sound_compression_type( isd, _apple_ima_adpcm_compression );
	pBits = reinterpret_cast<byte *>(get_intermediate_sound_samples( isd ));
	
	// save the data
	if( index == -1 )
		perm = def.number_of_permutations;
	else
		perm = index;
	
	def.sound_permutations[perm].flags = 0;
	def.sound_permutations[perm].skip_fraction = 0;
	
	fs->GetBaseName( perm_name );
	strncpy( def.sound_permutations[perm].name, perm_name, MAXIMUM_SOUND_PERMUTATION_NAME_LENGTH );
	def.sound_permutations[perm].name[MAXIMUM_SOUND_PERMUTATION_NAME_LENGTH] = 0;
	
	def.sampled_sound_headers[perm].flags = _sound_is_ima_compressed_flag;
	def.sampled_sound_headers[perm].logical_bits_per_sample = pwfx->wBitsPerSample;
	def.sampled_sound_headers[perm].physical_bytes_per_sample_minus_one = (pwfx->wBitsPerSample >> 3 ) - 1;
	def.sampled_sound_headers[perm].channels = pwfx->nChannels;
	def.sampled_sound_headers[perm].sample_rate = pwfx->nSamplesPerSec << 16;
	def.sampled_sound_headers[perm].number_of_samples = get_intermediate_sample_count( isd );
	def.sampled_sound_headers[perm].loop_start = 0;
	def.sampled_sound_headers[perm].loop_end = 0;
	def.sampled_sound_headers[perm].pad = 0;
	
	def.sound_size += (get_samples_size( isd ) + sizeof(sampled_sound_header));

	def.sampled_sound_headers[perm].samples = malloc( get_samples_size( isd ) );
	memcpy( def.sampled_sound_headers[perm].samples, pBits, get_samples_size( isd ) );
	
	// byte swamp the sucker
	apple_ima_packet_chunk *adpcm_chunk = reinterpret_cast<apple_ima_packet_chunk *>(def.sampled_sound_headers[perm].samples);
	
	for( ii = 0; ii < def.sampled_sound_headers[perm].number_of_samples; ii++ )
	{
		adpcm_chunk->state = SWAP2( adpcm_chunk->state );
		adpcm_chunk++;
	}
	
	dispose_intermediate_sound( isd );
	
	// free the buffer malloc'd in read_wave_file
	free( pwf );
	
	return LOAD_SOUND_ERROR_SUCCESS;
}

void TsounEditForm::PlaySoundFile()
{
	XGListControl *pList = dynamic_cast<XGListControl *>(GetWindow()->FindViewByID( 12314 ));
	int sel = pList->GetValue();
	
	PCMWAVEFORMAT pcmwf;
	
	assert( sel > -1 && sel < def.number_of_permutations && sel < MAXIMUM_PERMUTATIONS_PER_SOUND );
	
	memset( &pcmwf, 0x00, sizeof(pcmwf) );
	
	switch( def.sampled_sound_headers[sel].sample_rate )
	{
		case _sample_rate_22k:
			pcmwf.wf.nSamplesPerSec = 22050;
			break;
			
		default:
			// FIXME:
			return;
	}
	
	pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;
	pcmwf.wf.nChannels = def.sampled_sound_headers[sel].channels;
	pcmwf.wf.nBlockAlign =
		(def.sampled_sound_headers[sel].logical_bits_per_sample >> 3) *
		def.sampled_sound_headers[sel].channels;
	pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign;
	pcmwf.wBitsPerSample = def.sampled_sound_headers[sel].logical_bits_per_sample;
	
	DSBUFFERDESC dsbdesc;
	long buffer_size = def.sampled_sound_headers[sel].number_of_samples * BYTES_PER_SAMPLE;
	
	memset( &dsbdesc, 0x00, sizeof(dsbdesc) );
	dsbdesc.dwSize = sizeof(dsbdesc);
	dsbdesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY;
	dsbdesc.dwBufferBytes = buffer_size;
	dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
	
	if( lpDsb )
	{
		lpDsb->Release();
		lpDsb = NULL;
	}
	
	if( lpDirectSound->CreateSoundBuffer( &dsbdesc, &lpDsb, NULL ) != DS_OK )
	{
		// FIXME: let the user know
		return;
	}
	
	// lock the buffer to get the memory
	void *pvBuf1, *pvBuf2;
	unsigned long cBuf1, cBuf2;
	
//	unsigned long result = ;
	if( lpDsb->Lock( 0, buffer_size, &pvBuf1, &cBuf1, &pvBuf2, &cBuf2, 0 ) != DS_OK )
	{
		// FIXME: let the user know
		return;
	}
	
	if( def.sampled_sound_headers[sel].channels == 1 )
	{
		decompress_mono(
			def.sampled_sound_headers[sel].number_of_samples,
			def.sampled_sound_headers[sel].samples,
			pvBuf1,
			cBuf1,
			pvBuf2,
			cBuf2
			);
	}
	else
	{
		decompress_stereo(
			def.sampled_sound_headers[sel].number_of_samples,
			def.sampled_sound_headers[sel].samples,
			pvBuf1,
			cBuf1,
			pvBuf2,
			cBuf2
			);
	}
	
	if( lpDsb->Unlock( pvBuf1, cBuf1, pvBuf2, cBuf2 ) != DS_OK )
	{
		// FIXME: let the user know
		return;
	}
	
	if( lpDsb->Play( 0, 0, 0 ) != DS_OK )
	{
		// FIXME: let the user know
		return;
	}
}
#endif

#if OPT_XWIN
#endif




// I'm using some windows multimedia functions, it's easy - sue me
#if OPT_MACOS
#endif

#if OPT_WINOS
bool read_wave_file( XGFileSpecifier *fs, WAVEFORMAT **ppwf, byte **ppData, long *plSize )
{
	HMMIO hmmio;
	
	MMCKINFO mmckinfoParent;
	MMCKINFO mmckinfoSubchunk;
	
	XGString file_name = fs->GetFileName();
	
	// all these goto's! my old C professor would be sad
	hmmio = mmioOpen( (char *)file_name.Get(), NULL, MMIO_READ | MMIO_ALLOCBUF );
	if( !hmmio )
	{
		goto read_wave_file_error;
	}
	
	// look for the WAVE chunk
	mmckinfoParent.fccType = mmioFOURCC( 'W', 'A', 'V', 'E' );
	if( mmioDescend( hmmio, &mmckinfoParent, NULL, MMIO_FINDRIFF ) )
	{
		goto read_wave_file_error;
	}
	
	// look for the fmt chunk
	mmckinfoSubchunk.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
	if( mmioDescend( hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK ) )
	{
		goto read_wave_file_error;
	}
	
	*ppwf = (WAVEFORMAT *)malloc( mmckinfoSubchunk.cksize );
	
	if( mmioRead( hmmio, (HPSTR)*ppwf, mmckinfoSubchunk.cksize ) != mmckinfoSubchunk.cksize )
	{
		goto read_wave_file_error;
	}
	
	// Ascend out of the fmt chunk
	mmioAscend( hmmio, &mmckinfoSubchunk, 0 );
	
	// Descend into the data subchunk
	mmckinfoSubchunk.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
	if( mmioDescend( hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK ) )
	{
		goto read_wave_file_error;
	}
	
	// Read the data chunk
	*ppData = (byte *)malloc( mmckinfoSubchunk.cksize );
	*plSize = mmckinfoSubchunk.cksize;
	
	if( mmioRead( hmmio, (HPSTR)*ppData, mmckinfoSubchunk.cksize ) != mmckinfoSubchunk.cksize )
	{
		goto read_wave_file_error;
	}
	
	mmioClose( hmmio, 0 );
	
	return TRUE;
	
	read_wave_file_error:

	if( hmmio )
		mmioClose( hmmio, 0 );
		
	if( *ppwf )
	{
		free( *ppwf );
		*ppwf = NULL;
	}
	
	if( *ppData )
	{
		free( *ppData );
		*ppData = NULL;
	}
	
	return FALSE;
}
#endif

#if OPT_XWIN
#endif


void decompress_mono( long lPackets, void *samples, void *pvBuf1, unsigned long cBuf1, void *pvBuf2, unsigned long cBuf2 )
{
	char *destination = (char *)pvBuf1;
	long destination_length = cBuf1;
	apple_ima_packet_chunk *adpcm_chunk = reinterpret_cast<apple_ima_packet_chunk *>(samples);
	long packet_index;

	for( packet_index = 0; packet_index < lPackets; ++packet_index )
	{
		// Decompress each packet and stick them into the buffers we locked down.
		short decompressed_samples[NUMBER_OF_IMA_SAMPLES_PER_PACKET];
		long decompressed_size = sizeof(decompressed_samples);
		int decompressed_index = 0;

		// Decompress into the temporary buffer
		apple_adpcm_packet_decompress(
			SWAP2( adpcm_chunk->state ),
			adpcm_chunk->samples,
			decompressed_samples,
			(long)NUMBER_OF_IMA_SAMPLES_PER_PACKET
			);
		
		// copy into the proper buffer, handing overlaps between the two buffers returned by IDirectSoundBuffer_Lock()
		while( decompressed_size )
		{
			long size_to_copy = MIN( destination_length, sizeof(decompressed_samples) );

			memcpy( destination, decompressed_samples + decompressed_index, size_to_copy );
			destination += size_to_copy;
			destination_length -= size_to_copy;
			decompressed_size -= size_to_copy;
			decompressed_index += (size_to_copy >> 1);

			// If we filled up our first buffer, switch to the second one...
			if( size_to_copy != sizeof(decompressed_samples) )
			{
				if( destination == (char *)pvBuf2 )
				{
					// FIXME: warn the user of a buffer overflow
					break;
				}
				
				destination = (char *)pvBuf2;
				destination_length = cBuf2;
			}
		}

		adpcm_chunk++;
	}
}

void decompress_stereo( long lPackets, void *samples, void *pvBuf1, unsigned long cBuf1, void *pvBuf2, unsigned long cBuf2 )
{
	char *destination = (char *)pvBuf1;
	long destination_length = cBuf1;
	apple_ima_packet_chunk *adpcm_chunk= reinterpret_cast<apple_ima_packet_chunk *>(samples);
	long packet_index;

	// Remember, we're taking two packets each time
	for( packet_index = 0; packet_index < lPackets; packet_index += 2 )
	{
		short left_samples[NUMBER_OF_IMA_SAMPLES_PER_PACKET];
		short right_samples[NUMBER_OF_IMA_SAMPLES_PER_PACKET];
		short interleaved_samples[2 * NUMBER_OF_IMA_SAMPLES_PER_PACKET];

		// Decompress into the left and right temporary buffers
		apple_adpcm_packet_decompress(
			SWAP2( adpcm_chunk->state ),
			adpcm_chunk->samples,
			left_samples,
			(long)NUMBER_OF_IMA_SAMPLES_PER_PACKET
			);
			
		adpcm_chunk++;
		
		apple_adpcm_packet_decompress(
			SWAP2( adpcm_chunk->state ),
			adpcm_chunk->samples,
			right_samples,
			(long)NUMBER_OF_IMA_SAMPLES_PER_PACKET
			);
			
		adpcm_chunk++;
		
		// Interleave the samples from the left and right channels
		{
			short *dest = interleaved_samples;
			short *left = left_samples;
			short *right = right_samples;
			int index;

			for( index = 0; index < NUMBER_OF_IMA_SAMPLES_PER_PACKET; ++index )
			{
				*dest++ = *left++;
				*dest++ = *right++;
			}
		}

		// Copy the interleaved data into the proper buffer, handing overlaps between the two
		// buffers returned by IDirectSoundBuffer_Lock()
		{
			long interleaved_size = sizeof(interleaved_samples);
			int interleaved_index = 0;

			while( interleaved_size )
			{
				long size_to_copy = MIN( destination_length, sizeof(interleaved_samples) );

				memcpy( destination, interleaved_samples + interleaved_index, size_to_copy );
				destination += size_to_copy;
				destination_length -= size_to_copy;
				interleaved_size -= size_to_copy;
				interleaved_index += (size_to_copy >> 1);

				// We just filled up our first buffer, go to the second one...
				if( size_to_copy != sizeof(interleaved_samples) )
				{
					if( destination == (char *)pvBuf2 )
					{
						// FIXME: warn the user of a buffer overflow
						break;
					}
					
					destination = (char *)pvBuf2;
					destination_length = cBuf2;
				}
			}
		}
	}
}



int replace_sound_data_at_index( tag_entry *e, XGFileSpecifier *fs, float skip_percentage, int index )
{
	sound_definition def;

	int sample_size;
	bool bIncPermCount = false;
	
	if( index >= MAXIMUM_PERMUTATIONS_PER_SOUND )
		return FALSE;

	if( !read_sound_definition( &def, e ) )
	{		
		return LOAD_SOUND_ERROR_FAILURE;
	}
	
	// don't try to add past the first 'blank' spot
	if( index > def.number_of_permutations )
		return LOAD_SOUND_ERROR_FAILURE;
	
	if( index == def.number_of_permutations )
	{
		bIncPermCount = true;
	}
	else
	{
		// get the current size of the samples for this permutation
		if( def.sampled_sound_headers[index].flags & _sound_is_ima_compressed_flag )
			sample_size = def.sampled_sound_headers[index].number_of_samples * sizeof(apple_ima_packet_chunk);
		else
			sample_size = def.sampled_sound_headers[index].number_of_samples << def.sampled_sound_headers[index].physical_bytes_per_sample_minus_one;
			
		// subtract this sample size from the overall sound size
		def.sound_size -= (sample_size - sizeof(sampled_sound_header));
		
		// free the old sample buffer
		free( def.sampled_sound_headers[index].samples );
	}
	
	// load in the new sound samples
	if( LoadSoundFile( def, fs, index ) == LOAD_SOUND_ERROR_SUCCESS )
	{
		if( bIncPermCount )
		{
			def.number_of_permutations++;
			
			// update perutations size and sound_offset
			def.permutations_size += sizeof(sound_permutation_data);
			def.sound_offset += sizeof(sound_permutation_data);
		}
		
		// load in the new skip percentage
		def.sound_permutations[index].skip_fraction = float_to_percentage( skip_percentage / 100 );
			
		return (save_sound_definition( &def, e )) ? LOAD_SOUND_ERROR_SUCCESS : LOAD_SOUND_ERROR_FAILURE;
	}
	
	return LOAD_SOUND_ERROR_FAILURE;
}


bool create_sound_data( tag_file *tf, vengeance_script_soun *soun )
{
	sound_definition sound_def;
	tag_entry *te;
	
	memcpy( &sound_def, &default_soun, sizeof(sound_definition) );
	
	sound_def.loudness = soun->loudness - 1;
	sound_def.subtitle_string_list_tag = soun->subtitle_stringlist;
	sound_def.first_subtitle_within_string_list_index = soun->subtitle_index - 1;
	
	sound_def.play_fraction = float_to_percentage( soun->play_fraction / 100 );
	sound_def.external_frequency_modifier = float_to_short_fixed( soun->frequency_modifier );
	sound_def.pitch_lower_bound = float_to_short_fixed( soun->pitch_min );
	sound_def.pitch_delta = get_delta( float_to_short_fixed( soun->pitch_min ), float_to_short_fixed( soun->pitch_max ) );
	sound_def.volume_lower_bound = float_to_short_fixed( soun->volume_min );
	sound_def.volume_delta = get_delta( float_to_short_fixed( soun->volume_min ), float_to_short_fixed( soun->volume_max ) );
	sound_def.flags = soun->flags;

	te = add_tag_entry(
		tf,
		'soun',
		soun->id,
		get_type_version( 'soun' ),
		soun->name,
		0,
		sizeof(sound_definition),
		&sound_def,
		false
		);
		
	return save_sound_definition( &sound_def, te );
}
