/*
**	MacSND DataType
**
**	Written by Olaf `Olsen' Barthel <olsen@sourcery.han.de>
**		Public domain
**
** :ts=4
*/

#include "Global.h"

	// We want to use the locals

#undef SysBase
#define SysBase SysBase

#undef DOSBase
#define DOSBase DOSBase

	// initCmd opcode controls

#define initMono			0x80	// A monophonic channel, please
#define initStereo			0xC0	// Two stereo channels, please

	// Opcodes to replay sound

#define soundCmd			0x50	// Play a sound or a note
#define bufferCmd			0x51	// Play a sound

	// Offset indicator

#define dataOffsetFlag		0x8000	// Data follows

	/* ReadMacBinaryHeader():
	 *
	 *	Read the MacBinary format header and seek to the
	 *	proper resource location
	 */

BOOL __regargs
ReadMacBinaryHeader(BPTR Handle,LONG *Error,struct ExecBase *SysBase,struct DosLibrary *DOSBase)
{
	MacBinaryHeader Header;

		// No error so far

	*Error = 0;

		// Read the header, this should be 128 bytes

	if(FRead(Handle,&Header,sizeof(MacBinaryHeader),1) == 1)
	{
		LONG Offset;

			// The data is found at an odd address...

		CopyMem(Header . dflen,&Offset,sizeof(Offset));

			// Pad the offset to a multiple of 128, I suppose
			// this is done in order to keep XModem happy

		Offset = 128 + ((Offset + 127) & ~127);

			// Seek to the resource data location

		if(Seek(Handle,Offset,OFFSET_BEGINNING) != -1)
			return(TRUE);
		else
			*Error = ERROR_SEEK_ERROR;
	}

		// No success

	return(FALSE);
}

	/* ReadSoundHeader():
	 *
	 *	Fill in the BufferCmd data structure
	 */

BOOL __regargs
ReadSoundHeader(BPTR Handle,LONG SoundResourceOffset,struct SoundDataHeader *SoundHeader,LONG *Error,struct ExecBase *SysBase,struct DosLibrary *DOSBase)
{
	WORD HeaderWords[2],NumCommands,i;

		// Read the two header words

	if(FRead(Handle,&HeaderWords,sizeof(HeaderWords),1) == 1)
	{
			// Is this a type 1 "snd " resource?

		if(HeaderWords[0] == 1)
		{
				// How many synthesizer entries are there?

			if(HeaderWords[1])
			{
				struct SynthHeader { WORD ID; LONG Init; } SynthHeader;

					// Check all synthesizer init opcodes

				for(i = 0 ; !(*Error) && i < HeaderWords[1] ; i++)
				{
						// Read the init header

					if(FRead(Handle,&SynthHeader,sizeof(SynthHeader),1) == 1)
					{
							// Stereo sounds are not supported

						if((SynthHeader . Init & 0xF0) == initStereo)
							return(FALSE);
					}
				}
			}
		}
		else
		{
				// Only type 1 and type 2 resources are supported

			if(HeaderWords[0] < 1 || HeaderWords[0] > 2)
				return(FALSE);
		}

			// Read the number of commands to follow

		if(FRead(Handle,&NumCommands,sizeof(NumCommands),1) == 1)
		{
				// Are there any commands following?

			if(NumCommands)
			{
				struct SoundCommand { UWORD Command; WORD Param1; LONG Param2; } SoundCommand;

					// Scan through the commands

				for(i = 0 ; !(*Error) && i < NumCommands ; i++)
				{
						// Read the next command

					if(FRead(Handle,&SoundCommand,sizeof(SoundCommand),1) == 1)
					{
							// Is this a soundCmd or bufferCmd and does it
							// have data following it?

						if(SoundCommand . Command == (dataOffsetFlag | soundCmd) || SoundCommand . Command == (dataOffsetFlag | bufferCmd))
						{
								// Seek to the bufferCmd header

							if(Seek(Handle,SoundResourceOffset + SoundCommand . Param2,OFFSET_BEGINNING) != -1)
							{
									// Read the sound header

								if(FRead(Handle,SoundHeader,sizeof(struct SoundDataHeader),1) == 1)
								{
										// Compressed sounds are not supported

									if(SoundHeader -> Encoding == stdSH)
									{
											// Seek to the location of the data

										if(Seek(Handle,SoundHeader -> DataOffset & 0x00FFFFFF,OFFSET_CURRENT) != -1)
											return(TRUE);
										else
											*Error = ERROR_SEEK_ERROR;
									}
								}
								else
									*Error = IoErr();
							}
							else
								*Error = ERROR_SEEK_ERROR;
						}
					}
					else
						*Error = IoErr();
				}
			}
		}
		else
			*Error = IoErr();
	}
	else
		*Error = IoErr();

	return(FALSE);
}

	/* ScanResource():
	 *
	 *	Scan a resource file for "snd " resources.
	 */

BOOL __regargs
ScanResource(BPTR Handle,struct SoundDataHeader *SoundHeader,LONG *Error,struct ExecBase *SysBase,struct DosLibrary *DOSBase)
{
	struct ResourceHeader		ResourceHeader;
	struct ResourceMapHeader	ResourceMapHeader;
	struct ResourceTypeHeader	ResourceTypeHeader;
	struct ResourceTypeEntry	ResourceTypeEntry;
	struct ResourceReference	ResourceReference;
	LONG						Origin = Seek(Handle,0,OFFSET_CURRENT),i,j;

		// No error so far

	*Error = 0;

		// Read the resource header

	if(FRead(Handle,&ResourceHeader,sizeof(ResourceHeader),1) == 1)
	{
			// Seek to the resource map

		if(Seek(Handle,ResourceHeader . ResourceMapOffset + Origin,OFFSET_BEGINNING) != -1)
		{
				// Read the resource map header

			if(FRead(Handle,&ResourceMapHeader,sizeof(ResourceMapHeader),1) == 1)
			{
					// Both the first entries of the resource header and the
					// resource map header must match

				if(!memcmp(&ResourceHeader,&ResourceMapHeader,4 * sizeof(LONG)))
				{
						// Seek to the resource type list

					if(Seek(Handle,Origin + ResourceHeader . ResourceMapOffset + ResourceMapHeader . ResourceTypeListOffset,OFFSET_BEGINNING) != -1)
					{
							// Read the resource type list header

						if(FRead(Handle,&ResourceTypeHeader,sizeof(ResourceTypeHeader),1) == 1)
						{
								// Examine all entries

							for(i = 0 ; !(*Error) && i <= ResourceTypeHeader . ResourceEntryCount ; i++)
							{
									// Seek to an entry

								if(Seek(Handle,Origin + ResourceHeader . ResourceMapOffset + ResourceMapHeader . ResourceTypeListOffset + sizeof(struct ResourceTypeHeader) + i * sizeof(struct ResourceTypeEntry),OFFSET_BEGINNING) != -1)
								{
										// Read a resource type list entry

									if(FRead(Handle,&ResourceTypeEntry,sizeof(ResourceTypeEntry),1) == 1)
									{
											// Examine all resources of this type

										for(j = 0 ; !(*Error) && j <= ResourceTypeEntry . ResourceCount ; j++)
										{
												// Seek to the resource reference

											if(Seek(Handle,Origin + ResourceHeader . ResourceMapOffset + ResourceMapHeader . ResourceTypeListOffset + ResourceTypeEntry . ResourceReferenceOffset + j * sizeof(struct ResourceReference),OFFSET_BEGINNING) != -1)
											{
													// Read the resource reference header

												if(FRead(Handle,&ResourceReference,sizeof(ResourceReference),1) == 1)
												{
														// Is this a "snd " type resource?

													if(!memcmp(ResourceTypeEntry . ResourceType,"snd ",4))
													{
															// Seek to the corresponding resource data entry

														if(Seek(Handle,Origin + ResourceMapHeader . ResourceDataOffset + (ResourceReference . ResourceInfo & 0xFFFFFF) + sizeof(LONG),OFFSET_BEGINNING) != -1)
														{
																// Process the sound header

															if(ReadSoundHeader(Handle,Origin + ResourceMapHeader . ResourceDataOffset + (ResourceReference . ResourceInfo & 0x00FFFFFF) + sizeof(LONG),SoundHeader,Error,SysBase,DOSBase))
																return(TRUE);
														}
														else
															*Error = ERROR_SEEK_ERROR;
													}
												}
												else
													*Error = IoErr();
											}
											else
												*Error = ERROR_SEEK_ERROR;
										}
									}
									else
										*Error = IoErr();
								}
								else
									*Error = ERROR_SEEK_ERROR;
							}
						}
						else
							*Error = IoErr();
					}
					else
						*Error = ERROR_SEEK_ERROR;
				}
			}
			else
				*Error = IoErr();
		}
		else
			*Error = ERROR_SEEK_ERROR;
	}
	else
		*Error = IoErr();

	return(FALSE);
}
