#define __USE_SYSBASE
#include <stdio.h>
#include <sys/types.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <proto/asyncio.h>
#include <proto/ahi.h>

#ifdef __PPC__
#include <powerup/gcclib/powerup_protos.h>
#undef SetSignal
#define SetSignal PPCSetSignal
#undef Open
#define Open PPCOpen
#undef Close
#define Close PPCClose
#undef Read
#define Read PPCRead
#undef Write
#define Write PPCWrite
#undef CreatePool
#define CreatePool PPCCreatePool
#undef DeletePool
#define DeletePool PPCDeletePool
#undef AllocPooled
#define AllocPooled PPCAllocPooled
#undef FreePooled
#define FreePooled PPCFreePooled
#endif

#include "common.h"


static const char version[]="$VER: RAPlay 3.1 (14.05.02) The Amiga RealAudio v1/2/3 player/decoder, thanks to all!";
ULONG __nocommandline = 0;
ULONG __stack = 32768;

struct Library    *AsyncIOBase = NULL;
AsyncFile         *InputFile   = NULL;
AsyncFile         *OutputFile  = NULL;
Real_144          *Real144     = NULL;
Real_288          *Real288     = NULL;
Real_dnet         *Realdnet    = NULL;

struct MsgPort    *AHImp       = NULL;
struct AHIRequest *AHIio       = NULL;
struct AHIRequest *AHIio2      = NULL;
BYTE               AHIDevice   = -1;

struct RDArgs     *rdargs      = NULL;
STRPTR Template = "INPUT,OUTPUT,U=UNIT/K/N,AIFF/S,VERBOSE/S";
enum { TEM_INPUT, TEM_OUTPUT, TEM_UNIT, TEM_AIFF, TEM_VERBOSE, TEM_NUMARGS };

signed short *outbuf=NULL,*dubbuf=NULL;

BPTR StdIn, StdOut, StdErr;
void *MemPool = NULL;


#ifndef __GNUC__
static int break_cleanup(void)
{
	/* Just a dummy so atexit() will work */
	return 1;
}
#endif

static void exit_cleanup(void)
{
	if(Real144);
	{
		free_144(Real144);
		Real144 = NULL;
	}

	if(Real288);
	{
		free_288(Real288);
		Real288 = NULL;
	}

	if(Realdnet);
	{
		free_dnet(Realdnet);
		Realdnet = NULL;
	}

	if(InputFile);
	{
		CloseAsync(InputFile);
		InputFile = NULL;
	}

	if(OutputFile);
	{
		CloseAsync(OutputFile);
		OutputFile = NULL;
	}

	if(AsyncIOBase)
	{
		CloseLibrary(AsyncIOBase);
		AsyncIOBase = NULL;
	}

	if(!AHIDevice)
	{
		CloseDevice((struct IORequest *)AHIio);
		AHIDevice = -1;
	}

	if(AHIio)
	{
		DeleteIORequest((struct IORequest *)AHIio);
		AHIio = NULL;
	}

	if(AHIio2)
	{
		DeleteIORequest((struct IORequest *)AHIio2);
		AHIio2 = NULL;
	}

	if(AHImp)
	{
		DeleteMsgPort(AHImp);
		AHImp = NULL;
	}

	if(StdErr);
	{
		Close(StdErr);
		StdErr = NULL;
	}

	if(MemPool)
	{
		DeletePool(MemPool);
		MemPool = NULL;
	}

	if(rdargs)
	{
		FreeArgs(rdargs);
		rdargs = NULL;
	}
}

static void Fail(STRPTR reason)
{
	FPrintf(StdErr, "%s\n", (ULONG)reason);
	exit(20);
}

static LONG DOSRead(APTR buf, LONG len)
{
	return Read(StdIn, buf, len);
}

static LONG DOSWrite(APTR buf, LONG len)
{
	return Write(StdOut, buf, len);
}

static LONG ASNRead(APTR buf, LONG len)
{
	return ReadAsync(InputFile, buf, len);
}

static LONG ASNWrite(APTR buf, LONG len)
{
	return WriteAsync(OutputFile, buf, len);
}

static void uint2tenbytefloat(unsigned int num, unsigned char *bytes)
{
	int count, mask = 0x40000000;

	memset(bytes, 0, 10);

	if (num <= 1)
	{
		bytes[0] = 0x3F;
		bytes[1] = 0xFF;
		bytes[2] = 0x80;
		return;
	}

	bytes[0] = 0x40;

	if (num >= mask)
	{
		bytes[1] = 0x1D;
		return;
	}

	for (count=0; count<=32; count++)
	{
		if (num & mask) break;
		mask >>= 1;
	}

	num <<= count + 1;
	bytes[1] = 29 - count;
	bytes[2] = (num>>24) & 0xFF;
	bytes[3] = (num>>16) & 0xFF;
	bytes[4] = (num>>8) & 0xFF;
	bytes[5] = num & 0xFF;
}

static int GetCodec(unsigned char *buf, unsigned int *freq, unsigned int *chans, unsigned int *blocksize, unsigned short verbose)
{
	int codec = 0;

	if (buf[0]=='.' && buf[1]=='r' && buf[2]=='a' && buf[3]==0xfd)
	{
		if (buf[5]==4)
		{
			int i;

			i = buf[56] + 58;
			if (buf[i]=='2' && buf[i+1]=='8' && buf[i+2]=='_' && buf[i+3]=='8') codec = 2;
			else if (buf[i]=='d' && buf[i+1]=='n' && buf[i+2]=='e' && buf[i+3]=='t') codec = 3;
//			else if (buf[i]=='s' && buf[i+1]=='i' && buf[i+2]=='p' && buf[i+3]=='r') codec = 4;

			*freq = (buf[48]<<8 | buf[49]);
			*chans = (buf[54]<<8 | buf[55]);
			*blocksize = (buf[24]<<24 | buf[25]<<16 | buf[26]<<8 | buf[27]);

			if (verbose)
			{
				int j;
				char *c, *s, *s2;

				s = buf + 57;
				j = buf[56] + 57;
				c = buf + j;
				j += *c + 1;
				*c = 0;
				s2 = c + 1;
				c = buf + j;
				*c = 0;
				FPrintf(StdErr, "Codec:     %s/%s (v%ld)\n", (ULONG)s, (ULONG)s2, (ULONG)codec);
				s = buf + 70;
				j = buf[69] + 70;
				c = buf + j;
				j += *c + 1;
				*c = 0;
				FPrintf(StdErr, "Title:     %s\n", (ULONG)s);
				s = c + 1;
				c = buf + j;
				j += *c + 1;
				*c = 0;
				FPrintf(StdErr, "Author:    %s\n", (ULONG)s);
				s = c + 1;
				c = buf + j;
				*c = 0;
				FPrintf(StdErr, "Copyright: %s\n\n", (ULONG)s);
			}
		}
		else if (buf[5]==3)
		{
			codec = 1;
			*freq = 8000;
			*chans = (buf[8]<<8 | buf[9]);
			*blocksize = (buf[10]<<24 | buf[11]<<16 | buf[12]<<8 | buf[13]);

			if (verbose)
			{
				int j;
				char *c, *s;

				FPrintf(StdErr, "Codec:     14_4 (v%ld)\n", (ULONG)codec);
				s = buf + 23;
				j = buf[22] + 23;
				c = buf + j;
				j += *c + 1;
				*c = 0;
				FPrintf(StdErr, "Title:     %s\n", (ULONG)s);
				s = c + 1;
				c = buf + j;
				j += *c + 1;
				*c = 0;
				FPrintf(StdErr, "Author:    %s\n", (ULONG)s);
				s = c + 1;
				c = buf + j;
				*c = 0;
				FPrintf(StdErr, "Copyright: %s\n\n", (ULONG)s);
			}
		}
	}

	return codec;
}

static LONG ReadPackets(LONG (*ReadFunc)(APTR,LONG), unsigned char *buf, unsigned int chunksize, unsigned int *packets, unsigned short *streamid, unsigned short *rmf)
{
	LONG ret = 0;

	if (*rmf)
	{
		int i;
		unsigned short len, id;

		while ((ret < chunksize) && (*packets))
		{
			do {
				if (ReadFunc(buf, 12) <= 0) Fail("Unexpected EOF or error while reading.");
				if (buf[0] != 0 || buf[1] != 0) Fail("Unsupported packet-version in RealMedia file!");

				len = (buf[2]<<8 | buf[3]) - 12;
				id = (buf[4]<<8 | buf[5]);

				if ((ret + len) > chunksize)
				{
					if (id == *streamid)
					{
						FPrintf(StdErr, "RealMedia packet won't fit in buffer, skipping...\n");
					}

					i = chunksize - ret;

					while (len)
					{
						if (len < i) i = len;

						if (ReadFunc(buf, i) <= 0) Fail("Unexpected EOF or error while reading.");
						len -= i;
					}

					i = 0;
				} else {
					if ((i = ReadFunc(buf, len)) <= 0) Fail("Unexpected EOF or error while reading.");
				}

				--*packets;
			} while ((id != *streamid) && (*packets));

			if (id == *streamid)
			{
				ret += i;
				buf += i;
			}
		}
	} else {
		ret = ReadFunc(buf, chunksize);
	}

	if ((ret > 0) && (ret < chunksize))
	{
		int i = chunksize - ret;

		/* Clear the rest of the buffer */
		memset(buf + ret, 0, i);
	}

	return ret;
}

static int ReadHeader(LONG (*ReadFunc)(APTR,LONG), unsigned char *buf, unsigned int *freq, unsigned int *chans, unsigned int *blocksize, unsigned int *packets, unsigned short *streamid, unsigned short *rmf, unsigned short verbose)
{
	int i;
	int codec = 0;
	unsigned char *in = buf;

	if ((i = ReadFunc(in, 8)) <= 0) Fail("Unexpected EOF or error while reading.");
	in += i;

	if (buf[0]=='.' && buf[1]=='R' && buf[2]=='M' && buf[3]=='F')
	{
		int j;
		int size;
		int headers;

		*rmf = 1;
		size = (buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]) - 8;

		if ((i = ReadFunc(in, size)) <= 0) Fail("Unexpected EOF or error while reading.");
		in += i;

		if (buf[8] != 0 || buf[9] != 0) Fail("Unsupported chunk-version in RealMedia file!");

		headers = (buf[14]<<24 | buf[15]<<16 | buf[16]<<8 | buf[17]);

		for (j=0; j<headers; j++)
		{
			in = buf;

			if ((i = ReadFunc(in, 10)) <= 0) Fail("Unexpected EOF or error while reading.");
			in += i;

			if (buf[0]=='D' && buf[1]=='A' && buf[2]=='T' && buf[3]=='A')
			{
				if (ReadFunc(in, 8) <= 0) Fail("Unexpected EOF or error while reading.");
				*packets = (in[0]<<24 | in[1]<<16 | in[2]<<8 | in[3]);

				break;
			}

			size = (buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]) - 10;

			if ((size + 10) > DATACHUNK2)
			{
				if (buf[0]=='M' && buf[1]=='D' && buf[2]=='P' && buf[3]=='R')
				{
					FPrintf(StdErr, "RealMedia MDPR-chunk won't fit in buffer, skipping...\n");
				}

				i = DATACHUNK2 - 10;

				while (size)
				{
					if (size < i) i = size;

					if (ReadFunc(buf, i) <= 0) Fail("Unexpected EOF or error while reading.");
					size -= i;
				}
			} else {
				if (ReadFunc(in, size) <= 0) Fail("Unexpected EOF or error while reading.");

				if (verbose)
				{
					if (buf[0]=='C' && buf[1]=='O' && buf[2]=='N' && buf[3]=='T')
					{
						if (buf[8] == 0 && buf[9] == 0)
						{
							int j;
							char *c, *s;

							s = buf + 12;
							j = (buf[10]<<8) + buf[11] + 12;
							c = buf + j;
							j += (c[0]<<8) + c[1] + 2;
							*c = 0;
							FPrintf(StdErr, "Title:     %s\n", (ULONG)s);
							s = c + 2;
							c = buf + j;
							j += (c[0]<<8) + c[1] + 2;
							*c = 0;
							FPrintf(StdErr, "Author:    %s\n", (ULONG)s);
							s = c + 2;
							c = buf + j;
							j += (c[0]<<8) + c[1] + 2;
							*c = 0;
							FPrintf(StdErr, "Copyright: %s\n", (ULONG)s);
							s = c + 2;
							c = buf + j;
							*c = 0;
							FPrintf(StdErr, "Comment:   %s\n\n", (ULONG)s);
						}
					}
				}

				if (buf[0]=='M' && buf[1]=='D' && buf[2]=='P' && buf[3]=='R')
				{
					if (codec) continue;
					if (buf[8] != 0 || buf[9] != 0) Fail("Unsupported chunk-version in RealMedia file!");

					*streamid = (buf[10]<<8 | buf[11]);

					i = buf[40] + 1;
					i += buf[40+i] + 1;
					in = buf+40+i+4;

					codec = GetCodec(in, freq, chans, blocksize, verbose);
				}
			}
		}
	} else if (buf[0]=='.' && buf[1]=='r' && buf[2]=='a' && buf[3]==0xfd) {
		if (buf[5]==4)
		{
			int size;

			if ((i = ReadFunc(in, 14)) <= 0) Fail("Unexpected EOF or error while reading.");
			in += i;

			size = (buf[18]<<24 | buf[19]<<16 | buf[20]<<8 | buf[21]) - 6;

			if (ReadFunc(in, size) <= 0) Fail("Unexpected EOF or error while reading.");

			codec = GetCodec(buf, freq, chans, blocksize, verbose);
		}
		else if (buf[5]==3)
		{
			in = buf + 8;

			while (buf[6])
			{
				if (ReadFunc(in, 256) <= 0) Fail("Unexpected EOF or error while reading.");
			}

			if (ReadFunc(in, buf[7]) <= 0) Fail("Unexpected EOF or error while reading.");

			codec = GetCodec(buf, freq, chans, blocksize, verbose);
		}
	} else {
		Fail("Unrecognized format!!!");
	}

	return codec;
}

int main(void)
{
  LONG (*ReadFunc)(APTR,LONG);
  LONG (*WriteFunc)(APTR,LONG);
  ULONG unit=0;
  BOOL terminated=FALSE;
  ULONG signals,AHIcount=0;
  struct AHIRequest *link = NULL;
  LONG ArgArray[TEM_NUMARGS];
  unsigned short streamid=0,rmf=0,verbose=0;
  unsigned int s,x,bufsiz,size,blocksize,chunksize,freq,chans,type,codec=0,packets=0;
  unsigned char *in;
  unsigned char *deint;
  void *tmp;

  for(x=0;x<TEM_NUMARGS;ArgArray[x++]=NULL);
  if(!(rdargs=ReadArgs(Template,ArgArray,NULL))) Fail("Unable to read arguments.");

  StdIn  = Input();
  StdOut = Output();

  if (!(StdErr = Open("CONSOLE:", MODE_NEWFILE))) StdErr = Open("NIL:", MODE_NEWFILE);

  if(!ArgArray[TEM_INPUT])
  {
    if(IsInteractive(StdIn)) Fail("No input specified.");
    if(IsInteractive(StdOut)) Fail("Please redirect output or use proper arguments instead.");
  }

  if(ArgArray[TEM_VERBOSE]) verbose=1;

  if(ArgArray[TEM_UNIT]) unit=*((ULONG *)ArgArray[TEM_UNIT]);
  if(unit>3) Fail("Illegal unit number!");

  if(!(MemPool = CreatePool(MEMF_PUBLIC, 65536, 65536))) Fail("Unable to allocate needed memory.");
  if(!(in=(unsigned char *)AllocPooled(MemPool, DATACHUNK2))) Fail("Unable to allocate needed memory.");

  if(ArgArray[TEM_INPUT])
  {
    if(!(AsyncIOBase=OpenLibrary("asyncio.library", 39))) Fail("Unable to open asyncio.library.");

#ifndef __GNUC__
    onbreak(break_cleanup);
#endif
    atexit(exit_cleanup);

    if(!(InputFile=OpenAsync((STRPTR)ArgArray[TEM_INPUT], MODE_READ, 16384))) Fail("Unable to open input file.");
    ReadFunc = ASNRead;

    if(ArgArray[TEM_OUTPUT])
    {
      if(!(OutputFile=OpenAsync((STRPTR)ArgArray[TEM_OUTPUT], MODE_WRITE, 32768))) Fail("Unable to open output file.");
      WriteFunc = ASNWrite;
    }
    else
    {
      if(AHImp=CreateMsgPort())
        if(AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest)))
        {
          AHIio->ahir_Version = 4;
          AHIDevice=OpenDevice(AHINAME,unit,(struct IORequest *)AHIio,NULL);
        }
      if(AHIDevice) Fail("Unable to open ahi.device v4");

      if(!(AHIio2=AllocMem(sizeof(struct AHIRequest), MEMF_PUBLIC))) Fail("Unable to allocate needed memory.");
      CopyMem(AHIio, AHIio2, sizeof(struct AHIRequest));
    }
  } else {
    ReadFunc = DOSRead;
    WriteFunc = DOSWrite;
  }

  codec = ReadHeader(ReadFunc,in,&freq,&chans,&blocksize,&packets,&streamid,&rmf,verbose);

  if(verbose)
  {
    FPrintf(StdErr, "Frequency: %luHz\n", (ULONG)freq);
    FPrintf(StdErr, "Channels:  %lu\n", (ULONG)chans);
  }

  switch(codec)
  {
    case 1:
      chunksize=blocksize*72;
      if(!(Real144 = init_144())) Fail("Decoder init failed!!!");
      break;

    case 2:
      chunksize=blocksize*72;
      if(!(Real288 = init_288())) Fail("Decoder init failed!!!");
      break;

    case 3:
      chunksize=blocksize*((chans==1)?8:4);
      if(!(Realdnet = init_dnet())) Fail("Decoder init failed!!!");
      break;

    default:
      Fail("Unsupported codec!!!");
      break;
  }

  if(ArgArray[TEM_AIFF] && !AHImp)
  {
    int c = ((chans==1)?1:2);

    /* Write AIFF header */
    in[0]='F'; in[1]='O'; in[2]='R'; in[3]='M';
    in[4]=0x7F; in[5]=0xFF; in[6]=0xFF; in[7]=0xFC;	/* fake */
    in[8]='A'; in[9]='I'; in[10]='F'; in[11]='F';
    in[12]='C'; in[13]='O'; in[14]='M'; in[15]='M';
    in[16]=0; in[17]=0; in[18]=0; in[19]=18;

    in[20]=((c>>8)&0xFF); in[21]=(c&0xFF);
    in[22]=0x3F; in[23]=0xFF; in[24]=0xFF; in[25]=0xFE;	/* fake */
    in[26]=0; in[27]=16;

    uint2tenbytefloat(freq,in+28); 

    in[38]='S'; in[39]='S'; in[40]='N'; in[41]='D';
    in[42]=0x7F; in[43]=0xFF; in[44]=0xFF; in[45]=0xFC;	/* fake */

    WriteFunc(in,46);
  }

  if(chunksize>DATACHUNK2)
  {
    FreePooled(MemPool, in, DATACHUNK2);
    if(!(in=(unsigned char *)AllocPooled(MemPool, chunksize))) Fail("Unable to allocate needed memory.");
  }

  if(codec>1)
  {
    if(!(deint=(unsigned char *)AllocPooled(MemPool, chunksize+8))) Fail("Unable to allocate needed memory.");
    memset(deint,0,chunksize+8);	/* Prevent the decoder from reading junk */
  }

  if(chans==1) type=AHIST_M16S;
  else type=AHIST_S16S;

  if(AHImp) if(!(dubbuf=(signed short *)AllocPooled(MemPool, AUDIOBUFFER*sizeof(short)))) Fail("Unable to allocate needed memory.");
  if(!(outbuf=(signed short *)AllocPooled(MemPool, AUDIOBUFFER*sizeof(short)))) Fail("Unable to allocate needed memory.");

  while((s=size=ReadPackets(ReadFunc,in,chunksize,&packets,&streamid,&rmf))>0)
  {
    bufsiz=0;
    if (!(size/=blocksize)) break;

	switch (codec)
	{
      case 1:
        for(x=0;x<size;x++)
        {
          decode_144(Real144,in+blocksize*x,outbuf+bufsiz);
          bufsiz+=AUDIOBLOCK;
        }
        break;

      case 2:
        deinterleave(in,deint,s);
        for(x=0;x<size;x++)
        {
          decode_288(Real288,deint+blocksize*x,outbuf+bufsiz);
          bufsiz+=AUDIOBLOCK;
        }
        break;

      case 3:
        swapbytes(in,deint,s);
        for(x=0;x<size;x++)
        {
          bufsiz+=decode_dnet(Realdnet,deint+blocksize*x,outbuf+bufsiz,&freq,chans);
        }
        break;

      default:
        /* huh? */
        break;
    }

    if(AHImp)
    {
      AHIio->ahir_Std.io_Message.mn_Node.ln_Pri = 0;
      AHIio->ahir_Std.io_Command  = CMD_WRITE;
      AHIio->ahir_Std.io_Data     = outbuf;
      AHIio->ahir_Std.io_Length   = bufsiz*sizeof(short);
      AHIio->ahir_Std.io_Offset   = 0;
      AHIio->ahir_Frequency       = freq;
      AHIio->ahir_Type            = type;
      AHIio->ahir_Volume          = 0x10000;
      AHIio->ahir_Position        = 0x8000;
      AHIio->ahir_Link            = link;
      SendIO((struct IORequest *) AHIio);
      AHIcount++;

      if(link)
      {
        signals=Wait(SIGBREAKF_CTRL_C | (1L << AHImp->mp_SigBit));
#ifdef __PPC__
        if((signals & SIGBREAKF_CTRL_C) || (SetSignal(0,0) & SIGBREAKF_CTRL_C))
#else
        if(signals & SIGBREAKF_CTRL_C)
#endif
        {
          terminated=TRUE;
          break;
        }
        if(WaitIO((struct IORequest *) link)) Fail("I/O request failed.");
      }

      link   = AHIio;

      tmp    = outbuf;
      outbuf = dubbuf;
      dubbuf = tmp;

      tmp    = AHIio;
      AHIio  = AHIio2;
      AHIio2 = tmp;
    }
    else
    {
      WriteFunc(outbuf,bufsiz*sizeof(short));
      if(SetSignal(0,0) & SIGBREAKF_CTRL_C) break;
    }
  }

  if(AHIcount)
  {
    if(terminated)
    {
      AbortIO((struct IORequest *) AHIio);
      WaitIO((struct IORequest *) AHIio);

      if(AHIcount>1)
      {
        AbortIO((struct IORequest *) AHIio2);
        WaitIO((struct IORequest *) AHIio2);
      }
    }
    else WaitIO((struct IORequest *) AHIio2);
  }

  if(ArgArray[TEM_AIFF] && !AHImp)
  {
    LONG size;

    if(OutputFile)
    {
      if((size=SeekAsync(OutputFile, 0, MODE_CURRENT)-46)>0)
      {
        if(SeekAsync(OutputFile, 4, MODE_START)!=-1)
        {
          ULONG samples;

          /* SSND size */
          in[0] = (size & 0xFF000000) >> 24;
          in[1] = (size & 0x00FF0000) >> 16;
          in[2] = (size & 0x0000FF00) >> 8;
          in[3] = (size & 0x000000FF) >> 0;

          samples = size / 2 / ((chans==1)?1:2);
          size += 38;

          /* Number of samples */
          in[4] = (samples & 0xFF000000) >> 24;
          in[5] = (samples & 0x00FF0000) >> 16;
          in[6] = (samples & 0x0000FF00) >> 8;
          in[7] = (samples & 0x000000FF) >> 0;

          /* FORM size */
          in[8] = (size & 0xFF000000) >> 24;
          in[9] = (size & 0x00FF0000) >> 16;
          in[10] = (size & 0x0000FF00) >> 8;
          in[11] = (size & 0x000000FF) >> 0;

          WriteFunc(in+8, 4);
          if(SeekAsync(OutputFile, 22, MODE_START)!=-1) WriteFunc(in+4, 4);
          if(SeekAsync(OutputFile, 42, MODE_START)!=-1) WriteFunc(in, 4);
        }
      }
    } else {
      /* Worth a shot? */
      if((size=Seek(StdOut, 0, OFFSET_CURRENT)-46)>0)
      {
        if(Seek(StdOut, 4, OFFSET_BEGINNING)!=-1)
        {
          ULONG samples;

          /* SSND size */
          in[0] = (size & 0xFF000000) >> 24;
          in[1] = (size & 0x00FF0000) >> 16;
          in[2] = (size & 0x0000FF00) >> 8;
          in[3] = (size & 0x000000FF) >> 0;

          samples = size / 2 / ((chans==1)?1:2);
          size += 38;

          /* Number of samples */
          in[4] = (samples & 0xFF000000) >> 24;
          in[5] = (samples & 0x00FF0000) >> 16;
          in[6] = (samples & 0x0000FF00) >> 8;
          in[7] = (samples & 0x000000FF) >> 0;

          /* FORM size */
          in[8] = (size & 0xFF000000) >> 24;
          in[9] = (size & 0x00FF0000) >> 16;
          in[10] = (size & 0x0000FF00) >> 8;
          in[11] = (size & 0x000000FF) >> 0;

          WriteFunc(in+8, 4);
          if(Seek(StdOut, 22, OFFSET_BEGINNING)!=-1) WriteFunc(in+4, 4);
          if(Seek(StdOut, 42, OFFSET_BEGINNING)!=-1) WriteFunc(in, 4);
        }
      }
    }
  }

  return 0;
}
