/***************************************************************************

  M.A.M.E.32  -  Multiple Arcade Machine Emulator for Win32
  Win32 Portions Copyright (C) 1997-2001 Michael Soderstrom and Chris Kirmse

  This file is part of MAME32, and may only be used, modified and
  distributed under the terms of the MAME license, in "readme.txt".
  By continuing to use, modify or distribute this file you indicate
  that you have read the license and understand and accept it fully.

***************************************************************************/

#if defined(MAME_AVI)

/***************************************************************************

  Avi.c

  AVI capture code contributed by John Zissopoulos
                <zissop@theseas.softlab.ece.ntua.gr>

  How it works
  ==================================================

    AviStartCapture(filename, width, height, depth);

    do {
        AviAddBitmap(tBitmap, pPalEntries)
    } while (!done);

    AviEndCapture();

***************************************************************************/

#include "driver.h"

#include <windows.h>
#include <shellapi.h>
#include <windowsx.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <commctrl.h>
#include <commdlg.h>
//#include <strings.h>
#include <sys/stat.h>
#include <wingdi.h>
#include <tchar.h>
/* to shutup the warnings about functions were NOT calling in vfw.h */
#ifdef CDECL
#undef CDECL
#define CDECL CLIB_DECL
#endif
#include <vfw.h>

#include "Win32ui.h"
#include "config.h"	// tRect structure defined
#include "video.h"
#include "Avi.h"

#ifdef MAME32JP
#include "resource.h"
#endif

#ifndef BMP_PALSIZE
#define BMP_PALSIZE 256
#endif

//#define AVI_AUDIO

/***************************************************************************
    function prototypes
 ***************************************************************************/

static BOOL     AVI_Check_Version(void);

static void     AVI_Close(char* filename);
static char*    AVI_Convert_Bitmap(struct mame_bitmap* tBitmap, PALETTEENTRY* palette_entries);
static void     AVI_Message_Error(void);

static BOOL AVI_ReInit(char* filename);
static void AVI_CloseFile(char* filename);

static BOOL     AVI_Init(HWND hwnd ,char* filename, int width, int height, int depth, double fps, tRect *rect);

void AVI_Create_8bit_Bitmap(void);
static void AVI_Create_24bit_Bitmap(void);
static void AVI_Convert_16to8_Bitmap(char *pSrcBitmap);
static void AVI_Convert_16to24_Bitmap(char *pSrcBitmap);


static void AVI_Convert_16to24_TVInterlace_Bitmap(char *pSrcBitmap, int l);
static void AVI_Convert_16to24_TVInterlace_s_Bitmap(char *pSrcBitmap, int l);
static void AVI_Convert_16to24_TVInterlace_sxy_Bitmap(char *pSrcBitmap, int l);
static void AVI_Convert_16to24_TVInterlace_sy_Bitmap(char *pSrcBitmap, int l);

/***************************************************************************
    External variables
 ***************************************************************************/

/***************************************************************************
    Internal variables
 ***************************************************************************/

static PAVIFILE     pfile        = NULL;
static PAVISTREAM   psCompressed = NULL;
static PAVISTREAM   ps           = NULL;
//#ifdef AVI_AUDIO
static PAVISTREAM   audio_ps     = NULL;
static struct RIFF_WAVE_HEADER
{
    FOURCC     ckid;
    DWORD      cksize;
    WAVEFORMAT wfmt;
    WORD       nBitRate;
    FOURCC     data;
    DWORD      datasize;
} audio_wh;


static int          nAviAudioSamples   = 0;
static BOOL         bAviAudioRecord  = FALSE;
static char*		pAviAudioBuf = NULL;
static int			nAviAudioBufSize	= 0;

static LONG			lAviAudioBytesWritten = 0;
static LONG			lAviAudioBytesWrittenMax = 0;
static unsigned __int64	nAviAudioFileSize = 0;
//#endif
static BOOL         bAviError    = FALSE;
static int          nAviFrames;
static BOOL         bAviCapture  = FALSE;

static DWORD        nAviFPS = 0;
static char         aviFilename[MAX_PATH * 2];
static char         aviBaseFilename[_MAX_PATH];

static char         wavFilename[MAX_PATH * 2];
static char         wavBaseFilename[_MAX_PATH];

#define AVI_MAX_FRAME_CMP 64

static tRect        rectAvi, rectAviSrc;
static int			nAviWidth, nAviHeight;
static int			nAviSrcWidth, nAviSrcHeight;
static int			nAviDepthAvi, nAviDepthSrc;
static int			nAviBitmapSize = 0;

static char         *pAviBitmapTemp[AVI_MAX_FRAME_CMP];
static int			nAviTempSize = 0;
static int			nAviTempCount;
static int			nAviCountSub;
static double		AviTempFps;
static double		AviSlFps;
static double		fAviTempNext;
static int			AviTempNext;
static int			AviTempS, AviTempE, AviTempN;
static int         nAviFrameCmp  = 0;
static int         nAviFrameResize  = 0;
static BOOL			bAviFile_Divides = FALSE;
static int         nAviFile_DividesCount  = 0;
static BOOL			bAvi_TVInterlace;
static int         nAvi_TVInterlace_Count;
static LONG lSampWritten = 0;
static LONG lBytesWritten = 0;
static LONG lAviBytesWrittenMax = 0;

static unsigned __int64         nAviFileSize;
static unsigned __int64         nAviFileSizeMax;

static struct MAME_AVI_STATUS Avi_StatusAvi;

static char			*pAviBitmap_bitConv = NULL;
static char         AviConvertBitmap16to8[32768];

static double		AviFps = 0;
static DWORD Avi_setfps,Avi_setfpsleft, Avi_setfpsright;



static void delete_file(const char *filename)
{
	struct stat s;
	if (stat(filename, &s) == 0)
		DeleteFile(filename);
}


//#ifdef AVI_AUDIO
#include "Wav.h"

void* Avi_Get_AudioStreamInfo(LONG *formatSt,LONG *formatLen, AVISTREAMINFO *sinfo,
							  const char *filename, struct WAV_WAVEFORMAT *wfm)
{
	PAVISTREAM pstm;
	int wavfile;
	void *format;

	memset(sinfo,0,sizeof(AVISTREAMINFO));
	sinfo->fccType = streamtypeAUDIO;
	sinfo->dwFlags = AVIF_ISINTERLEAVED;
	sinfo->dwScale = wfm->channel*(wfm->bitrate/8);
	sinfo->dwRate = wfm->samplespersec * sinfo->dwScale;
	sinfo->dwInitialFrames = 1;
	sinfo->dwQuality = 0;
	sinfo->dwSampleSize = wfm->channel*(wfm->bitrate/8);

 
	wavfile = wav_wavefile_create(filename, wfm->samplespersec, wfm->bitrate, wfm->channel);
	if (wavfile == -1)
		return NULL;
	wav_wavefile_close(wavfile);

	AVIStreamOpenFromFile(&pstm,filename,streamtypeAUDIO,0,
										OF_READ | OF_SHARE_DENY_NONE,NULL);

	*formatSt=AVIStreamStart(pstm);


	if (AVIStreamReadFormat(pstm,*formatSt,NULL,formatLen)!=0)
		goto ERR;
	if ((format=malloc(*formatLen))==NULL)
		goto ERR;
	if (AVIStreamReadFormat(pstm,*formatSt,format,formatLen)!=0)
	{
		free(format);
		goto ERR;
	}

	AVIStreamRelease(pstm);
	delete_file(filename);
	return format;
ERR:
	AVIStreamRelease(pstm);
	delete_file(filename);
	return NULL;

}



int CopyStream(PAVIFILE pavi)
{
	AVISTREAMINFO si;
	LONG st,leng;
	LPVOID p;
	PAVISTREAM ptmp;

	extern struct WAV_WAVEFORMAT mame_mixer_dstwfm;
	p = Avi_Get_AudioStreamInfo(&st, &leng, &si, "avi_audio_temp.wav", &mame_mixer_dstwfm);
	if (p == NULL)
		goto ERR;

	if (AVIFileCreateStream(pavi,&ptmp,&si)!=0)
	{	free(p);	goto ERR;	}

	if (AVIStreamSetFormat(ptmp,st,p,leng)!=0)
	{	free(p);	goto ERR;	}

	audio_ps = ptmp;
	nAviAudioSamples = st;
	
	free(p);
	return 0;
ERR:
	return -1;
}


//#endif


/***************************************************************************
    External variables
 ***************************************************************************/

/* Set frames per second */
void SetAviFPS(int fps)
{
    nAviFPS = fps;
}

/* Is AVI capturing initialized? */
BOOL GetAviCapture(void)
{
    return bAviCapture;
}

/* Start capture, initialize */
BOOL AviStartCapture(HWND hwnd, char* filename, struct MAME_AVI_STATUS *AviStatus)
{
	int i;
	double fps, cnvfps;

    if (bAviCapture || (!AVI_Check_Version()))
        return FALSE;

	Avi_StatusAvi = *AviStatus;
	
	fps = AviStatus->def_fps * ((double)(12-AviStatus->frame_skip) / 12.0);
	cnvfps = AviStatus->fps * ((double)(12-AviStatus->frame_skip) / 12.0);


	if (cnvfps <= 0) cnvfps = fps;
	AviSlFps = cnvfps / fps;

	AviFps = fps;
	for(i=0; i<AVI_MAX_FRAME_CMP; i++)
		pAviBitmapTemp[i] = NULL;


	nAviFrameCmp=0;
	if (AviStatus->frame_cmp == TRUE)
	{
		nAviFrameCmp=3;
		if (AviStatus->frame_cmp_pre15 == TRUE)	nAviFrameCmp=1;
		if (AviStatus->frame_cmp_few == TRUE)	nAviFrameCmp=2;
	}
	if (AviStatus->fps == AviStatus->def_fps)		nAviFrameCmp=0;

	AviTempFps = AviFps * AviSlFps;
	fAviTempNext = (AviFps/AviTempFps) * 65536.0;
	{
		int dummy;
		dummy = fAviTempNext;
		AviTempNext = fAviTempNext;

		dummy ^= 0xffffffff;
		if(dummy) dummy = 0;
		dummy++;
	}
	AviFps *= AviSlFps;

	Avi_setfps = AviFps;
	Avi_setfpsleft = AviFps * 1000000.0;


	AviTempS		= 0;
	AviTempN		= AviTempS;
	AviTempE		= AviTempS + AviTempNext;

	nAviTempCount = 0;
	nAviCountSub = 0;
	nAviTempSize = (AviTempNext + 0x1ffff) >> 16;
	nAviBitmapSize = 0;
    
	nAviFrames   = 0;
    bAviError    = FALSE;
	nAvi_TVInterlace_Count = 0;

	if (AviTempNext < 0x10000)
	{
		if (nAviFrameCmp) {nAviFrameCmp = 1;	nAviTempSize = 3;}
		AviTempE = 0;
	}

	nAviDepthSrc = AviStatus->depth;
	if (nAviDepthSrc > 8) nAviDepthSrc = 16;
	if (nAviDepthSrc <= 8)	{nAviFrameCmp = 0;}
	if (nAviFrameCmp == 0) nAviTempSize = 1;

	nAviDepthAvi = AviStatus->avi_depth;

	nAviSrcWidth = AviStatus->width;
	nAviSrcHeight = AviStatus->height;
	rectAviSrc = AviStatus->rect;
	
	
	if (nAviDepthAvi == 24 && nAviDepthSrc == 16)
	{
		bAvi_TVInterlace = AviStatus->interlace;
		rectAvi = AviStatus->avi_rect;
		nAviWidth = AviStatus->avi_width;
		nAviHeight = AviStatus->avi_height;
	}
	else
	{
		bAvi_TVInterlace = FALSE;
		rectAvi = AviStatus->rect;
		nAviWidth = AviStatus->rect.m_Width;
		nAviHeight = AviStatus->rect.m_Height;
	}
//ek
	if (rectAvi.m_Width > nAviWidth) rectAvi.m_Width = nAviWidth;
	if (bAvi_TVInterlace == TRUE)
	{
		if (rectAvi.m_Height > nAviHeight/2) 
		{
			rectAvi.m_Height = nAviHeight/2; 
			rectAvi.m_Width  = nAviHeight*(3/4);
		}
		rectAvi.m_Top = (nAviHeight/2 - rectAvi.m_Height)/2;
		rectAvi.m_Left = (nAviWidth - rectAvi.m_Width)/2;

		Avi_setfps = AviFps / 2;
		Avi_setfpsleft = AviFps * 500000.0;
	} 
	else
	{
		if (rectAvi.m_Height > nAviHeight) 
		{
			rectAvi.m_Height = nAviHeight; 
			rectAvi.m_Width  = nAviHeight*(3/4);
		}
		rectAvi.m_Left = (nAviWidth - rectAvi.m_Width)/2;
		rectAvi.m_Top = (nAviHeight - rectAvi.m_Height)/2;
	}


	nAviFrameResize = 0;
	if (rectAvi.m_Width != rectAviSrc.m_Width || rectAvi.m_Height != rectAviSrc.m_Height) 
		nAviFrameResize = 1;

	if (rectAvi.m_Width > nAviWidth) rectAvi.m_Width = nAviWidth;
	if (bAvi_TVInterlace == TRUE)
	{
		if (rectAvi.m_Height > nAviHeight/2)
		{
			rectAvi.m_Height = nAviHeight/2; rectAvi.m_Width = nAviHeight*0.75;}
			rectAvi.m_Top = (nAviHeight/2 - rectAvi.m_Height)/2;

		rectAvi.m_Left = (nAviWidth - rectAvi.m_Width)/2;
		Avi_setfps = AviFps / 2;
		Avi_setfpsleft = AviFps * 500000.0;
	}
	else
	{
		if (rectAvi.m_Height > nAviHeight) {rectAvi.m_Height = nAviHeight; rectAvi.m_Width = nAviHeight*(3/4);}
		rectAvi.m_Left = (nAviWidth - rectAvi.m_Width)/2;
		rectAvi.m_Top = (nAviHeight - rectAvi.m_Height)/2;
	}
	

	nAviFrameResize = 0;
	if (rectAvi.m_Width != rectAviSrc.m_Width || rectAvi.m_Height != rectAviSrc.m_Height) nAviFrameResize = 1;

	nAviFile_DividesCount = 0;
	nAviFileSize = 0;
	lSampWritten = 0;
	lBytesWritten = 0;
	lAviBytesWrittenMax = 0;
	nAviAudioFileSize = 0;
	lAviAudioBytesWritten = 0;
	lAviAudioBytesWrittenMax = 0;

	nAviFileSizeMax = (unsigned __int64)AviStatus->avi_filesize * 1000000;
	if (AviStatus->avi_filesize)	bAviFile_Divides = TRUE;
	else							bAviFile_Divides = FALSE;

	strcpy(aviFilename, filename);

	if (Avi_StatusAvi.avi_audio_record_type == 1)
		strcpy(wavFilename, Avi_StatusAvi.wav_filename);
	
	if (bAviFile_Divides == TRUE)
	{
		char drive[_MAX_DRIVE];
		char dir[_MAX_DIR];
		char fname[_MAX_FNAME];
		char ext[_MAX_EXT];
		_splitpath(filename, drive, dir, fname, ext);
		_makepath(aviBaseFilename, drive, dir, fname, NULL);
		sprintf(aviFilename, "%s.%s", aviBaseFilename, "avi");
		
		if (Avi_StatusAvi.avi_audio_record_type == 1)
		{
			_splitpath(wavFilename, drive, dir, fname, ext);
			_makepath(wavBaseFilename, drive, dir, fname, NULL);
			sprintf(wavFilename, "%s.%s", wavBaseFilename, "wav");
		}
	}

	delete_file(aviFilename);
    if (AVI_Init(hwnd, aviFilename, nAviWidth, nAviHeight, nAviDepthAvi, AviFps, &rectAviSrc) == FALSE)
	{
		AVI_Close(aviFilename);
		strcpy(aviFilename, "");
		return FALSE;
	}

//#ifdef AVI_AUDIO
	if (Avi_StatusAvi.avi_audio_record_type == 2)
		if (AVI_AudioStream_Start())
		{
	        AVI_Message_Error();
			AVI_Close(aviFilename);
			strcpy(aviFilename, "");
			return FALSE;
	}
//#endif

    bAviCapture = TRUE;
    

	if (nAviDepthAvi ==  8 && nAviDepthSrc == 16)AVI_Create_8bit_Bitmap();
	if (nAviDepthAvi == 24 && nAviDepthSrc == 16)AVI_Create_24bit_Bitmap();
	if (nAviDepthAvi == 16 && nAviDepthSrc == 16 && nAviFrameResize) ;//AVI_Create_16bit_Bitmap();

    return TRUE;
}

/* End capture, close file */
void AviEndCapture(void)
{
	int i;
    if (!bAviCapture)
        return;

	nAviBitmapSize = 0;
	for(i=0; i<AVI_MAX_FRAME_CMP; i++)
	{
		if (pAviBitmapTemp[i] != NULL)
		{
			free(pAviBitmapTemp[i]);
			pAviBitmapTemp[i] = NULL;
		}
	}
	nAviTempSize = 0;

	if (pAviBitmap_bitConv != NULL) free(pAviBitmap_bitConv);

//#ifdef AVI_AUDIO
	if (pAviAudioBuf != NULL)	free(pAviAudioBuf);
	nAviAudioBufSize = 0;
	bAviAudioRecord = FALSE;
//#endif

    AVI_Close(aviFilename);
    bAviCapture = FALSE;
    strcpy(aviFilename, "");
}

void AviFrameConv_StreamWrite(char *bitmap)
{
	HRESULT hr;
	LPBITMAPINFOHEADER lpbit;
	int frames	= nAviFrames;


	if (nAviDepthAvi == 8 && nAviDepthSrc == 16)
	{
		AVI_Convert_16to8_Bitmap(bitmap);
		lpbit = (LPBITMAPINFOHEADER)pAviBitmap_bitConv;
	}
	else if (nAviDepthAvi == 24 && nAviDepthSrc == 16)
	{
		if (bAvi_TVInterlace == TRUE)
		{
			const int field = (Avi_StatusAvi.interlace_odd_number_field == TRUE) ? nAvi_TVInterlace_Count^1:nAvi_TVInterlace_Count;
			
			if (Avi_StatusAvi.avi_smooth_resize_x == TRUE)
			{
				if (Avi_StatusAvi.avi_smooth_resize_y == TRUE)
					AVI_Convert_16to24_TVInterlace_sxy_Bitmap(bitmap, field);
				else
					AVI_Convert_16to24_TVInterlace_s_Bitmap(bitmap, field);
			}
			else if (Avi_StatusAvi.avi_smooth_resize_y == TRUE)
				AVI_Convert_16to24_TVInterlace_sy_Bitmap(bitmap, field);
			else
				AVI_Convert_16to24_TVInterlace_Bitmap(bitmap, field);

			nAvi_TVInterlace_Count = (nAvi_TVInterlace_Count+1) & 0x1;
			if (nAvi_TVInterlace_Count!=0) return;

		} else
		{
			AVI_Convert_16to24_Bitmap(bitmap);
		}
		lpbit = (LPBITMAPINFOHEADER)pAviBitmap_bitConv;
	}
	else lpbit = (LPBITMAPINFOHEADER)bitmap;

	hr = AVIStreamWrite(psCompressed,           /* stream pointer */
                         frames,             /* time of this frame */
                         1,                      /* number to write */
                         (LPBYTE) lpbit +        /* pointer to data */
                         lpbit->biSize +
                         lpbit->biClrUsed * sizeof(RGBQUAD),
                         lpbit->biSizeImage,     /* size of this frame */
                         AVIIF_KEYFRAME,         /* flags.... */
                         &lSampWritten,
                         &lBytesWritten);

	if (lBytesWritten > lAviBytesWrittenMax) lAviBytesWrittenMax = lBytesWritten;
	nAviFileSize += (unsigned __int64)lBytesWritten;
	nAviFrames++;


	return;
}

LPBITMAPINFOHEADER AviFrameBlend(void)
{
	LPBITMAPINFOHEADER lpbit;
	HRESULT hr;

	if (nAviFrameCmp) nAviTempCount++;
	AviTempN += 0x10000;
	if ((AviTempN >= AviTempE) && !bAviError)
	{
		int i,j;


		if (!nAviFrames)
		{
			if ((nAviDepthAvi == 8 || nAviDepthAvi == 24) && nAviDepthSrc == 16)
			{
				lpbit = (LPBITMAPINFOHEADER)pAviBitmap_bitConv;
			}
			else lpbit = (LPBITMAPINFOHEADER)(pAviBitmapTemp[0]);

			hr = AVIStreamSetFormat(psCompressed, 0,
									lpbit,              /* stream format */
									lpbit->biSize +     /* format size */
	                                lpbit->biClrUsed * sizeof(RGBQUAD));

			if (hr != AVIERR_OK)
			{
	            bAviError = TRUE;
			}
		}

		j = ((AviTempN + 0xffff) >> 16) + nAviCountSub;
		nAviTempCount = 0;



		switch (nAviFrameCmp)
		{
		case 1:
		for(i=1; i<j; i++)
		{
			int x,y,line;
			unsigned int r,g,b,alpha;
			unsigned int rd,gd,bd;
			short col;
			line = (rectAviSrc.m_Width * (16/8)+3)&~3;

			alpha = 32768;
			{
				int s,e;
				s = (AviTempS & 0xffff);
				e = (AviTempE & 0xffff);
				if (e==0) e=0x10000;

				if(i==1) 
				{
					alpha = 65536-(32768 - (s >> 1));
				}
				else 
				{
					if(i==(j-1)) alpha = (e >> 1);
				}
			}

			for(y=0; y<rectAviSrc.m_Height; y++)
			{
				unsigned short* dst = (unsigned short*)(pAviBitmapTemp[0] + sizeof(BITMAPINFOHEADER) + y * line);
				const unsigned short* src = (unsigned short*)(pAviBitmapTemp[i] + sizeof(BITMAPINFOHEADER) + y * line);
				for(x=0; x<rectAviSrc.m_Width; x++)
				{
					col = src[x];
					b = (col >>  0) & 0x1f;
					g = (col >>  5) & 0x1f;
					r = (col >> 10) & 0x1f;
					col = dst[x];
					bd = (col >>  0) & 0x1f;
					gd = (col >>  5) & 0x1f;
					rd = (col >> 10) & 0x1f;

					r = (r * alpha + rd * (65536-alpha)) >> 16;
					g = (g * alpha + gd * (65536-alpha)) >> 16;
					b = (b * alpha + bd * (65536-alpha)) >> 16;

					dst[x] =	(r << 10) |
								(g << 5) |
								(b << 0);
				}
			}
		}
		break;
		case 2:
		{
			int x,y,line;
			unsigned int r,g,b;
			unsigned int rs,gs,bs;
			short col;
			unsigned int alp[AVI_MAX_FRAME_CMP];
			line = (rectAviSrc.m_Width * (16/8)+3)&~3;

			for(i=1; i<j; i++)
			{
				int s,e;
				alp[i] = 32768;
				
				s = (AviTempS & 0xffff);
				e = (AviTempE & 0xffff);
				if (e==0) e=0x10000;

				if(i==1) 
				{
					alp[i] = 65536-(32768 - (s >> 1));
				}
				else 
				{
					if(i==(j-1)) alp[i] = (e >> 1);
				}
			}

			for(y=0; y<rectAviSrc.m_Height; y++)
			{
				const int bmpline = sizeof(BITMAPINFOHEADER) + y * line;
				unsigned short* dst = (unsigned short*)(pAviBitmapTemp[0] + bmpline);
				for(x=0; x<rectAviSrc.m_Width; x++)
				{
					col = dst[x];
					b = (col >>  0) & 0x1f;
					g = (col >>  5) & 0x1f;
					r = (col >> 10) & 0x1f;

					b |= (b<<5) | (b<<10); 
					g |= (g<<5) | (g<<10); 
					r |= (r<<5) | (r<<10);

					for(i=1; i<j; i++)
					{
					const unsigned short* src = (unsigned short*)(pAviBitmapTemp[i] + bmpline);
					const unsigned int alpha = alp[i];
					col = src[x];
					bs = (col >>  0) & 0x1f;
					gs = (col >>  5) & 0x1f;
					rs = (col >> 10) & 0x1f;

					bs |= (bs<<5) | (bs<<10); 
					gs |= (gs<<5) | (gs<<10); 
					rs |= (rs<<5) | (rs<<10);

					r = (rs * alpha + r * (65536-alpha)) >> 16;
					g = (gs * alpha + g * (65536-alpha)) >> 16;
					b = (bs * alpha + b * (65536-alpha)) >> 16;
					}

					if(r>32767) r=32767;
					if(g>32767) g=32767;
					if(b>32767) b=32767;
					dst[x] =	( r			& (0x1f<<10))		|
								((g >> 5)	& (0x1f<<5))	|
								((b >> 10)	&  0x1f);

				}
			}
		}
		break;
		case 3:
		{
			int x,y,line;
			unsigned int r,g,b;
			unsigned int rs,gs,bs;
			short col;
			unsigned int alp[AVI_MAX_FRAME_CMP];
			int s,e;
			unsigned int t;
			line = (rectAviSrc.m_Width * (16/8)+3)&~3;
			s = (AviTempS & 0xffff);
			e = (AviTempE & 0xffff);

			t=j << 16;

			if (e==0) e=0x10000;
			t = t - s;
			t = t - (65536-e);


			for(i=0; i<j; i++)
			{
				alp[i] = 65536;
				if(i==0) 
				{
					alp[i] = 65536 - s;
				}
				else 
				{
					if(i==(j-1)) alp[i] = e;
				}

				alp[i] = (unsigned int)(((unsigned __int64)alp[i] << 16) / t);
			}

			for(y=0; y<rectAviSrc.m_Height; y++)
			{
				const int bmpline = sizeof(BITMAPINFOHEADER) + y * line;
				unsigned short* dst = (unsigned short*)(pAviBitmapTemp[0] + bmpline);
				for(x=0; x<rectAviSrc.m_Width; x++)
				{
					unsigned int alpha = alp[0];
					col = dst[x];
					b = (col >>  0) & 0x1f;
					g = (col >>  5) & 0x1f;
					r = (col >> 10) & 0x1f;

					b |= (b<<5) | (b<<10); 
					g |= (g<<5) | (g<<10); 
					r |= (r<<5) | (r<<10);

					r = r * alpha;
					g = g * alpha;
					b = b * alpha;

					for(i=1; i<j; i++)
					{
					const unsigned short* src = (unsigned short*)(pAviBitmapTemp[i] + bmpline);
					alpha = alp[i];
					col = src[x];
					bs = (col >>  0) & 0x1f;
					gs = (col >>  5) & 0x1f;
					rs = (col >> 10) & 0x1f;

					bs |= (bs<<5) | (bs<<10);
					gs |= (gs<<5) | (gs<<10);
					rs |= (rs<<5) | (rs<<10);

					r += (rs * alpha);
					g += (gs * alpha);
					b += (bs * alpha);
					}

					if(r>0x7fffffff) r=0x7fffffff;
					if(g>0x7fffffff) g=0x7fffffff;
					if(b>0x7fffffff) b=0x7fffffff;
					dst[x] =	((r	>> (0+16))	& (0x1f<<10))		|
								((g >> (5+16))	& (0x1f<<5))	|
								((b >> (10+16))	&  0x1f);
				}
			}
		}
		break;
		default:;
			j=0;
		}
		


		AviFrameConv_StreamWrite(pAviBitmapTemp[0]);

		if((AviTempE & 0xffff) && (j>1))
		{
			char *lp = pAviBitmapTemp[0];
			pAviBitmapTemp[0] = pAviBitmapTemp[j-1];
			pAviBitmapTemp[j-1] = lp;
			nAviTempCount++;
			nAviCountSub = 1;
		} else
		{
			nAviCountSub = 0;
		}
		AviTempS = AviTempE & 0xffff;
		AviTempE = AviTempE + AviTempNext - AviTempN;
		AviTempN = AviTempN & 0xffff;
	}

	return lpbit;
}

LPBITMAPINFOHEADER AviFrameSmooth(void)
{
	HRESULT hr;
	LPBITMAPINFOHEADER lpbit;

	if (nAviFrameCmp) nAviTempCount++;

	if (!bAviError)
	{
		int j;

		if (AviTempE)
		{
		for(j=AviTempS; j<0x10000; j+=AviTempNext)
		{

		switch (nAviFrameCmp)
		{
		case 1:
		{
			int x,y,line;
			unsigned int r,g,b;
			unsigned int rs,gs,bs;
			short col;
			const unsigned int alpha = j & 0xffff;

			line = (rectAviSrc.m_Width * (16/8)+3)&~3;
			for(y=0; y<rectAviSrc.m_Height; y++)
			{
				const int bmpline = sizeof(BITMAPINFOHEADER) + y * line;
				unsigned short* dst = (unsigned short*)(pAviBitmapTemp[2] + bmpline);
				const unsigned short* src1 = (unsigned short*)(pAviBitmapTemp[1] + bmpline);
				const unsigned short* src2 = (unsigned short*)(pAviBitmapTemp[0] + bmpline);
				for(x=0; x<rectAviSrc.m_Width; x++)
				{
					col = src1[x];
					b = (col >>  0) & 0x1f;
					g = (col >>  5) & 0x1f;
					r = (col >> 10) & 0x1f;

					b |= (b<<5) | (b<<10); 
					g |= (g<<5) | (g<<10); 
					r |= (r<<5) | (r<<10);

					r = r * alpha;
					g = g * alpha;
					b = b * alpha;

					col = src2[x];
					bs = (col >>  0) & 0x1f;
					gs = (col >>  5) & 0x1f;
					rs = (col >> 10) & 0x1f;

					bs |= (bs<<5) | (bs<<10);
					gs |= (gs<<5) | (gs<<10);
					rs |= (rs<<5) | (rs<<10);

					r += (rs * (0x10000-alpha));
					g += (gs * (0x10000-alpha));
					b += (bs * (0x10000-alpha));

					if(r>0x7fffffff) r=0x7fffffff;
					if(g>0x7fffffff) g=0x7fffffff;
					if(b>0x7fffffff) b=0x7fffffff;
					dst[x] =	((r	>> (0+16))	& (0x1f<<10))	|
								((g >> (5+16))	& (0x1f<<5))	|
								((b >> (10+16))	&  0x1f);
				}
			}
		}
		break;

		default:;
		}

		if (!nAviFrames)
		{
			if ((nAviDepthAvi == 8 || nAviDepthAvi == 24) && nAviDepthSrc == 16)
				lpbit = (LPBITMAPINFOHEADER)pAviBitmap_bitConv;
			else lpbit = (LPBITMAPINFOHEADER)(pAviBitmapTemp[0]);

			hr = AVIStreamSetFormat(psCompressed, 0,
									lpbit,              /* stream format */
									lpbit->biSize +     /* format size */
	                                lpbit->biClrUsed * sizeof(RGBQUAD));

			if (hr != AVIERR_OK)
			{
	            bAviError = TRUE;
			}
		}


		AviFrameConv_StreamWrite(pAviBitmapTemp[nAviTempCount]);


		AviTempN += AviTempNext;

		}
		

		AviTempN -= 0x10000;
		AviTempS = AviTempN;

		{
			char *lp = pAviBitmapTemp[0];
			pAviBitmapTemp[0] = pAviBitmapTemp[1];
			pAviBitmapTemp[1] = lp;
			nAviTempCount=1;
		}
		}


		AviTempE = 1;
	}



	return lpbit;
}

/* Add a frame to an open AVI */
void AviAddBitmap(struct mame_bitmap* tBitmap, PALETTEENTRY* palette_entries)
{
	extern unsigned __int64 mame_mixer_wave_FileSize;
    LPBITMAPINFOHEADER lpbit;
    char* bitmap;

    if (!bAviCapture)
        return;



    bitmap = AVI_Convert_Bitmap(tBitmap, palette_entries);


	if (AviTempNext < 0x10000)	lpbit = AviFrameSmooth();
	else	lpbit = AviFrameBlend();


	if (bAviFile_Divides == TRUE)
	{
		unsigned __int64 audio_size = 0;
		if (Avi_StatusAvi.avi_audio_record_type == 1) audio_size = mame_mixer_wave_FileSize;
		if (Avi_StatusAvi.avi_audio_record_type == 2) audio_size = nAviAudioFileSize;

		if (nAviFileSize + audio_size > nAviFileSizeMax)
		{
			lSampWritten = 0;
			lBytesWritten = 0;
			lAviAudioBytesWritten = 0;

			if (Avi_StatusAvi.avi_audio_record_type)
			{
				mame_mixer_wave_FileSize = 0;
				nAviAudioFileSize = 0;
			}

			{
				extern BOOL avi_nextfile(void);
				extern struct WAV_WAVEFORMAT mame_mixer_dstwfm;
				int br = TRUE;

				AVI_CloseFile(aviFilename);

				if (nAviFile_DividesCount == 0)
				{
					int nResult;
					char buf[_MAX_PATH];

					sprintf(buf, "%s_%03d.%s", aviBaseFilename, 0, "avi");
					delete_file(buf);
					nResult = MoveFile(aviFilename, buf);
				}
				nAviFile_DividesCount++;

				sprintf(aviFilename, "%s_%03d.%s", aviBaseFilename, nAviFile_DividesCount, "avi");
				if (Avi_StatusAvi.avi_audio_record_type == 1)
				{
					wav_stop_log_wave();

					if (nAviFile_DividesCount-1 == 0)
					{
						char buf[_MAX_PATH];
						int nResult;
						
						sprintf(buf, "%s_%03d.%s", wavBaseFilename, 0, "wav");
						delete_file(buf);
						nResult = MoveFile(wavFilename, buf);
					}
					sprintf(wavFilename, "%s_%03d.%s", wavBaseFilename, nAviFile_DividesCount, "wav");
				}

				if (Avi_StatusAvi.avi_savefile_pause == TRUE)
					br = avi_nextfile();

				if (AVI_ReInit(aviFilename) == FALSE) {bAviError = TRUE; return;}

				if (Avi_StatusAvi.avi_audio_record_type == 1)
				{
					if (wav_start_log_wave(wavFilename, &mame_mixer_dstwfm) == 0)
					{;

					} else
					{
						Avi_StatusAvi.avi_audio_record_type = 0;
						usrintf_showmessage("'%s' を有効なwavファイルとしてオープンできません", wavFilename );
					}


				}
//#ifdef AVI_AUDIO

				if (Avi_StatusAvi.avi_audio_record_type == 2)
					if (AVI_AudioStream_Start())
					{
						bAviError = TRUE;
						return;
				}
//#endif
				if (br == FALSE)	//録画中止
				{
					extern BOOL bAviRun;
					bAviRun = 0;
					if (Avi_StatusAvi.avi_audio_record_type)
					{
						extern int mame_mixer_wave_loging;
						mame_mixer_wave_loging = 0;
					}
					return;
				}
			}
		}
	}
}

/***************************************************************************
    External variables
 ***************************************************************************/
static void AVI_SetBitmapinfoHeader(int width, int height, int depth)
{
	BITMAPINFOHEADER    bmiHeader;
    DWORD dwSize;
    DWORD wColSize;
    UINT wLineLen;
	int i;


    wLineLen = (width * depth + 31) / 32 * 4;
    wColSize = sizeof(RGBQUAD) * ((depth <= 8) ? BMP_PALSIZE : 0);
    dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
                (DWORD)(UINT)wLineLen * (DWORD)(UINT)height;

    /* This is the BITMAPINFOHEADER structure */
    bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmiHeader.biWidth           = width;
    bmiHeader.biHeight          = height;
    bmiHeader.biPlanes          = 1;
    bmiHeader.biBitCount        = (depth <= 8) ? 8 : 16;
    bmiHeader.biCompression     = BI_RGB;
    bmiHeader.biSizeImage       = dwSize - sizeof(BITMAPINFOHEADER) - wColSize;
    bmiHeader.biXPelsPerMeter   = 0;
    bmiHeader.biYPelsPerMeter   = 0;
    bmiHeader.biClrUsed         = (depth == 8) ? BMP_PALSIZE : 0;
    bmiHeader.biClrImportant    = 0;

    /* Write the BITMAPINFOHEADER */
	for(i=0; i<nAviTempSize; i++)
		memcpy(pAviBitmapTemp[i], (void*)&bmiHeader, sizeof(BITMAPINFOHEADER));
}

static AVICOMPRESSOPTIONS AviCompressOpts;
static AVICOMPRESSOPTIONS AviAudio_CompressOpts;

static BOOL AVI_Init(HWND hwnd, char* filename, int width, int height, int depth, double fps, tRect *rect)
{
    /* Must pass the following parameters */
    /* width  = drivers[index]->drv->screen_width */
    /* height = drivers[index]->drv->screen_height */
    /* depth  = bits per pixel */

    HRESULT hr;
    AVISTREAMINFO strhdr;
    DWORD dwSize;
    DWORD wColSize;
    DWORD biSizeImage;
    UINT wLineLen;
    AVICOMPRESSOPTIONS* aopts[1];

    aopts[0] = &AviCompressOpts;

    AVIFileInit();

    hr = AVIFileOpen(&pfile,                      /* returned file pointer */
                     filename,                    /* file name */
                     OF_WRITE | OF_CREATE,        /* mode to open file with */
                     NULL);                       /* use handler determined */
                                                  /* from file extension.... */
    if (hr != AVIERR_OK)
    {
        AVI_Message_Error();
        return FALSE;
    }

	nAviWidth	= width;
	nAviHeight	= height;

    wLineLen = ((DWORD)nAviWidth * depth + 31) / 32 * 4;

    wColSize = sizeof(RGBQUAD) * ((depth <= 8) ? BMP_PALSIZE : 0);

    dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
                (DWORD)(UINT)wLineLen * (DWORD)(UINT)nAviHeight;

    biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize;

    /*
       Fill in the header for the video stream....
       The video stream will run in 60ths of a second....To be changed
    */

    _fmemset(&strhdr, 0, sizeof(strhdr));
    strhdr.fccType                = streamtypeVIDEO;
    strhdr.fccHandler             = 0;
	strhdr.dwFlags = AVIF_ISINTERLEAVED;
	if ((Avi_setfpsleft % 1000000) == 0)
	{
		strhdr.dwScale                = 1;
		strhdr.dwRate                 = Avi_setfps;
	} else
	{
		strhdr.dwScale                = 1000000;
		strhdr.dwRate                 = Avi_setfpsleft;
	}
	strhdr.dwInitialFrames		= 0;
    strhdr.dwSuggestedBufferSize  = biSizeImage;

    SetRect(&strhdr.rcFrame, 0, 0,          /* rectangle for stream */
            (int) nAviWidth,
            (int) nAviHeight);

    /* And create the stream */
    hr = AVIFileCreateStream(pfile,         /* file pointer */
                             &ps,           /* returned stream pointer */
                             &strhdr);      /* stream header */
    if (hr != AVIERR_OK)
    {
        AVI_Message_Error();
        return FALSE;
    }



    _fmemset(&AviCompressOpts, 0, sizeof(AviCompressOpts));
    if (!AVISaveOptions(hwnd, 0, 1, &ps, aopts))	/* CodecSelect dialog */
    {
        AVI_Message_Error();
        return FALSE;
    }

    hr = AVIMakeCompressedStream(&psCompressed, ps, &AviCompressOpts, NULL);
    if (hr != AVIERR_OK)
    {
        AVI_Message_Error();
        return FALSE;
    }



    return TRUE;
}

static BOOL AVI_ReInit(char* filename)
{
    /* Must pass the following parameters */
    /* width  = drivers[index]->drv->screen_width */
    /* height = drivers[index]->drv->screen_height */
    /* depth  = bits per pixel */

    HRESULT hr;
    AVISTREAMINFO strhdr;
    DWORD dwSize;
    DWORD wColSize;
    DWORD biSizeImage;
    UINT wLineLen;
    AVICOMPRESSOPTIONS* aopts[1];
	const int depth = nAviDepthAvi;

    aopts[0] = &AviCompressOpts;

	nAviFileSize = 0;

	delete_file(filename);

    hr = AVIFileOpen(&pfile,                      /* returned file pointer */
                     filename,                    /* file name */
                     OF_WRITE | OF_CREATE,        /* mode to open file with */
                     NULL);                       /* use handler determined */
                                                  /* from file extension.... */
    if (hr != AVIERR_OK)
    {
        return FALSE;
    }


    wLineLen = ((DWORD)nAviWidth * depth + 31) / 32 * 4;
    wColSize = sizeof(RGBQUAD) * ((depth <= 8) ? BMP_PALSIZE : 0);

    dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
                (DWORD)(UINT)wLineLen * (DWORD)(UINT)nAviHeight;

    biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize;

    /*
       Fill in the header for the video stream....
       The video stream will run in 60ths of a second....To be changed
    */

    _fmemset(&strhdr, 0, sizeof(strhdr));
    strhdr.fccType                = streamtypeVIDEO;
    strhdr.fccHandler             = 0;
	if ((Avi_setfpsleft % 1000000) == 0)
	{
		strhdr.dwScale                = 1;
		strhdr.dwRate                 = Avi_setfps;
	} else
	{
		strhdr.dwScale                = 1000000;
		strhdr.dwRate                 = Avi_setfpsleft;
	}


    strhdr.dwSuggestedBufferSize  = biSizeImage;

    SetRect(&strhdr.rcFrame, 0, 0,          /* rectangle for stream */
            (int) nAviWidth,
            (int) nAviHeight);

    /* And create the stream */
    hr = AVIFileCreateStream(pfile,         /* file pointer */
                             &ps,           /* returned stream pointer */
                             &strhdr);      /* stream header */
    if (hr != AVIERR_OK)
    {
        return FALSE;
    }


    hr = AVIMakeCompressedStream(&psCompressed, ps, &AviCompressOpts, NULL);
    if (hr != AVIERR_OK)
    {
        return FALSE;
    }

    return TRUE;
}

static void AVI_CloseFile(char* filename)
{
    FILE* fp;

    if (ps)
        AVIStreamClose(ps);

    if (psCompressed)
        AVIStreamClose(psCompressed);

//#ifdef AVI_AUDIO
    if (audio_ps)	AVIStreamClose(audio_ps);

	audio_ps			= NULL;
	nAviAudioSamples	= 0;
//#endif

    if (pfile)
        AVIFileClose(pfile);

    pfile        = NULL;
    ps           = NULL;
    psCompressed = NULL;
    nAviFrames   = 0;
    bAviError    = FALSE;



	if ((fp = fopen(filename, "rb+")) != NULL)
	{
			fseek(fp, 0x70, SEEK_SET);
			fwrite(&AviCompressOpts.fccHandler, 1, 4, fp);
			fclose(fp);
	}
}
static void AVI_Close(char* filename)
{
    FILE* fp;

    if (ps)
        AVIStreamClose(ps);

    if (psCompressed)
        AVIStreamClose(psCompressed);

//#ifdef AVI_AUDIO
    if (audio_ps)
        AVIStreamClose(audio_ps);
	audio_ps			= NULL;
	nAviAudioSamples	= 0;
//#endif

    if (pfile)
        AVIFileClose(pfile);

    AVIFileExit();

    pfile        = NULL;
    ps           = NULL;
    psCompressed = NULL;
    nAviFrames   = 0;
    bAviError    = FALSE;



	if ((fp = fopen(filename, "rb+")) != NULL)
	{
			fseek(fp, 0x70, SEEK_SET);
			fwrite(&AviCompressOpts.fccHandler, 1, 4, fp);
			fclose(fp);
	}


	/* Directly edit the frame rate information */
	nAviTempSize = 0;
	nAviTempCount = 0;

}


static BOOL AVI_Check_Version(void)
{
    WORD wVer;

    /* first let's make sure we are running on 1.1 */
    wVer = HIWORD(VideoForWindowsVersion());
    if (wVer < 0x010a)
    {
        return FALSE;
    }
    return TRUE;
}

static void AVI_Message_Error(void)
{
#ifdef MAME32JP
    MessageBox(NULL, "AVIファイルのエラー！", "エラー", MB_OK);
#else
    MessageBox(NULL, "AVI File Error!", "Error", MB_OK);
#endif
}

static char* AVI_Convert_Bitmap(struct mame_bitmap* tBitmap, PALETTEENTRY* palette_entries)
{
    RGBQUAD             bmiColors[BMP_PALSIZE];
    char *  bitmap;
    int     i;
    UINT    wLineLen;

	int bmihSize;
	int depth = nAviDepthSrc;

	wLineLen = (rectAviSrc.m_Width * depth + 31) / 32 * 4;

	{
		int size;

		if (depth <= 8)
			size = BMP_PALSIZE * sizeof(RGBQUAD) + rectAviSrc.m_Height * wLineLen * depth / 8;
	    else
			size = rectAviSrc.m_Height * wLineLen * depth / 8;
	
		if( size != nAviBitmapSize)
		{
			for(i=0; i<nAviTempSize; i++)
			{
				if(pAviBitmapTemp[i] != NULL)	free(pAviBitmapTemp[i]);
				pAviBitmapTemp[i] = (char*)malloc(size + sizeof(BITMAPINFOHEADER));
				if(pAviBitmapTemp[i] == NULL)
				{
					bAviError = TRUE;
					return NULL;
				}
			}
			nAviBitmapSize = size;
			AVI_SetBitmapinfoHeader(rectAviSrc.m_Width, rectAviSrc.m_Height, depth);

		}

		bitmap = pAviBitmapTemp[nAviTempCount];
		bmihSize = sizeof(BITMAPINFOHEADER);
	}

    /* This is the RGBQUAD structure */
    if (depth <= 8)
    {
        for (i = 0; i < BMP_PALSIZE; i++)
        {
            bmiColors[i].rgbRed      = palette_entries->peRed;
            bmiColors[i].rgbGreen    = palette_entries->peGreen;
            bmiColors[i].rgbBlue     = palette_entries->peBlue;
            bmiColors[i].rgbReserved = 0;
            palette_entries++;
        }
    }

    /* Write the Color Table */
    if (depth <= 8)
    {
        memcpy(bitmap + sizeof(BITMAPINFOHEADER), (void*)&bmiColors, BMP_PALSIZE * sizeof(RGBQUAD));
    }

    /* Write the actual Bitmap Data */
	if (tBitmap->depth == 32)
	{
		extern UINT16* DDraw_Avi_PaletteLookup(void);
		unsigned short* pal = (unsigned short*)DDraw_Avi_PaletteLookup();

		for (i = rectAviSrc.m_Height - 1; 0 <= i; i--)
		{
			UINT16 *dst = (UINT16 *)(bitmap + bmihSize + (rectAviSrc.m_Height - i - 1) *
								((rectAviSrc.m_Width * (depth/8)+3)&~3));
			UINT32 *ptr = (UINT32 *)tBitmap->line[i + rectAviSrc.m_Top] + rectAviSrc.m_Left;
			int x;
			UINT32 p;
            for (x = 0; x < rectAviSrc.m_Width; x++)
            {
            	p = (*ptr++);
				dst[x] = ((p>>(8*0+3))&0x1f) + (((p>>(8*1+3))&0x1f)<<5) + (((p>>(8*2+3))&0x1f)<<10);
			}
		}

	}
	else if (depth <= 8)
	{
		for (i = rectAviSrc.m_Height - 1; 0 <= i; i--)
		{
				memcpy(bitmap + sizeof(BITMAPINFOHEADER) + BMP_PALSIZE * sizeof(RGBQUAD) + (rectAviSrc.m_Height - i - 1) *
					((rectAviSrc.m_Width * (depth/8)+3)&~3), (char*)tBitmap->line[i + rectAviSrc.m_Top] + rectAviSrc.m_Left,
					(rectAviSrc.m_Width * depth / 8));
		}
	}
	else
	{
		extern UINT16* DDraw_Avi_PaletteLookup(void);
		unsigned short* pal = (unsigned short*)DDraw_Avi_PaletteLookup();

		for (i = rectAviSrc.m_Height - 1; 0 <= i; i--)
		{
			unsigned short* dst = (unsigned short*)(bitmap + bmihSize + (rectAviSrc.m_Height - i - 1) *
								((rectAviSrc.m_Width * (depth/8)+3)&~3));
			unsigned short* ptr = (unsigned short*)tBitmap->line[i + rectAviSrc.m_Top] + rectAviSrc.m_Left;
			int x;
            for (x = 0; x < rectAviSrc.m_Width; x++)
            {
				dst[x] = pal[(*ptr++)];
			}
		}

	}
    return bitmap;
}


void AVI_Create_8bit_Bitmap(void)
{
	RGBQUAD             bmiColors[BMP_PALSIZE];
	int size;
	int depth = 8;
	UINT wLineLen;
	wLineLen = (nAviWidth * depth + 31) / 32 * 4;

	if (pAviBitmap_bitConv != NULL) free(pAviBitmap_bitConv);
	size = BMP_PALSIZE * sizeof(RGBQUAD) + nAviHeight * wLineLen; // * depth / 8;

	pAviBitmap_bitConv = (char*)malloc(size + sizeof(BITMAPINFOHEADER));
	if (pAviBitmap_bitConv == NULL)
	{
		bAviError = TRUE;
		return;
	}

	memset(pAviBitmap_bitConv, 0, size + sizeof(BITMAPINFOHEADER));

	{
	BITMAPINFOHEADER    bmiHeader;
    DWORD dwSize;
    DWORD wColSize;
	int width = nAviWidth;
	int height = nAviHeight;

    wColSize = sizeof(RGBQUAD) * ((depth <= 8) ? BMP_PALSIZE : 0);
    dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
                (DWORD)(UINT)wLineLen * (DWORD)(UINT)height;
    /* This is the BITMAPINFOHEADER structure */
    bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmiHeader.biWidth           = width;
    bmiHeader.biHeight          = height;
    bmiHeader.biPlanes          = 1;
    bmiHeader.biBitCount        = (depth <= 8) ? 8 : 16;
    bmiHeader.biCompression     = BI_RGB;
    bmiHeader.biSizeImage       = dwSize - sizeof(BITMAPINFOHEADER) - wColSize;
    bmiHeader.biXPelsPerMeter   = 0;
    bmiHeader.biYPelsPerMeter   = 0;
    bmiHeader.biClrUsed         = (depth == 8) ? BMP_PALSIZE : 0;
    bmiHeader.biClrImportant    = 0;

	memcpy(pAviBitmap_bitConv, (void*)&bmiHeader, sizeof(BITMAPINFOHEADER));
	}

	{
		extern int load_16to8_bitmap(void* filename, RGBQUAD *rgbq, char* cnv);
		if (load_16to8_bitmap("pl16to8.bmp", &bmiColors[0], AviConvertBitmap16to8) == -1) {bAviError = TRUE; return;}
	}

    memcpy(pAviBitmap_bitConv + sizeof(BITMAPINFOHEADER), &bmiColors[0], BMP_PALSIZE * sizeof(RGBQUAD));


}

static void AVI_Create_24bit_Bitmap(void)
{
	int size;
	int depth = 24;
	UINT wLineLen;
	DWORD wColSize;
	wLineLen = (nAviWidth * depth + 31) / 32 * 4;

	if (pAviBitmap_bitConv != NULL) free(pAviBitmap_bitConv);

    wColSize = sizeof(RGBQUAD) * ((depth <= 8) ? BMP_PALSIZE : 0);
    size = wColSize + (DWORD)(UINT)wLineLen * (DWORD)(UINT)nAviHeight;

	pAviBitmap_bitConv = (char*)malloc(size + sizeof(BITMAPINFOHEADER));
	if (pAviBitmap_bitConv == NULL)
	{
		bAviError = TRUE;
		return;
	}
	memset(pAviBitmap_bitConv, 0, size + sizeof(BITMAPINFOHEADER));

	{
	BITMAPINFOHEADER    bmiHeader;
    DWORD dwSize;
	int width = nAviWidth;
	int height = nAviHeight;

    dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
                (DWORD)(UINT)wLineLen * (DWORD)(UINT)height;
    /* This is the BITMAPINFOHEADER structure */
    bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmiHeader.biWidth           = width;
    bmiHeader.biHeight          = height;
    bmiHeader.biPlanes          = 1;
    bmiHeader.biBitCount        = depth;
    bmiHeader.biCompression     = BI_RGB;
    bmiHeader.biSizeImage       = dwSize - sizeof(BITMAPINFOHEADER) - wColSize;
    bmiHeader.biXPelsPerMeter   = 0;
    bmiHeader.biYPelsPerMeter   = 0;
    bmiHeader.biClrUsed         = (depth == 8) ? BMP_PALSIZE : 0;
    bmiHeader.biClrImportant    = 0;

	memcpy(pAviBitmap_bitConv, (void*)&bmiHeader, sizeof(BITMAPINFOHEADER));
	}
}
static void AVI_Convert_16to8_Bitmap(char *pSrcBitmap)
{
	int x,y;

	const int line8 = (nAviWidth * (8/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	for(y=0; y<rectAviSrc.m_Height; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + y * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + y * line8 + (sizeof(BITMAPINFOHEADER) + BMP_PALSIZE * sizeof(RGBQUAD)));
		for(x=0; x<rectAviSrc.m_Width; x++)
		{
			dst[x] = AviConvertBitmap16to8[src[x]];
		}

	}
}

static void AVI_Convert_16to24_Bitmap(char *pSrcBitmap)
{
	int x,y;

	const int line24 = (nAviWidth * (24/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	for(y=0; y<rectAviSrc.m_Height; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + y * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + y * line24 + sizeof(BITMAPINFOHEADER));
		for(x=0; x<rectAviSrc.m_Width; x++)
		{
			const unsigned short col = src[x];
			unsigned char t;

			t = (col >>  0) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);
			t = (col >>  5) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);
			t = (col >> 10) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);
		}

	}
}

static void AVI_Convert_16to24_TVInterlace_Bitmap(char *pSrcBitmap, int l)
{
	int x,y;

	const int line24 = (nAviWidth * (24/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	int srcx,srcy;
	int dstx,dsty;

	int addx, addy;

	srcx = rectAviSrc.m_Width << 16;
	srcy = rectAviSrc.m_Height << 16;

	dstx = rectAvi.m_Width;
	dsty = rectAvi.m_Height;

	addx = srcx / dstx;
	addy = 0x10000;
	if (dstx > 720) dstx = 720;
	
	if (dsty != (srcy>>16))
	{
		dsty = 240;
		addy = srcy / dsty;
		rectAvi.m_Top = 0;
	}

	
	

	l &= 0x1;

	srcy = 0;
	for(y=0; y<dsty; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + (srcy >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + (((y+rectAvi.m_Top)<<1) + l) * line24 + rectAvi.m_Left*3 + sizeof(BITMAPINFOHEADER));
		srcx = 0;
		for(x=0; x<dstx; x++)
		{
			const unsigned short col = src[(srcx+0x7fff) >> 16];
			unsigned char t;

			t = (col >>  0) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);
			t = (col >>  5) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);
			t = (col >> 10) & 0x1f;
			*(dst++) = (t<<3) | (t>>2);


			srcx += addx;
		}
		srcy += addy;
	}
}


static void AVI_Convert_16to24_TVInterlace_s_Bitmap(char *pSrcBitmap, int l)
{
	int x,y;

	const int line24 = (nAviWidth * (24/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	int srcx,srcy;
	int dstx,dsty;

	int addx, addy;

	srcx = rectAviSrc.m_Width << 16;
	srcy = rectAviSrc.m_Height << 16;

	dstx = rectAvi.m_Width;
	dsty = rectAvi.m_Height;

	addx = (srcx-0xffff) / (dstx-1);
	addy = 0x10000;
	if (dstx > 720) dstx = 720;

	if (dsty != (srcy>>16))
	{
		dsty = 240;
		addy = srcy / dsty;
		rectAvi.m_Top = 0;
	}

	
	

	l &= 0x1;

	srcy = 0;
	for(y=0; y<dsty; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + (srcy >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + (((y+rectAvi.m_Top)<<1) + l) * line24 + rectAvi.m_Left*3 + sizeof(BITMAPINFOHEADER));
		srcx = 0;
		for(x=0; x<dstx; x++)
		{
			const unsigned short col1 = src[srcx >> 16];
			const unsigned short col2 = src[(srcx+0xffff) >> 16];
			const int alpha = srcx & 0xffff;
			unsigned int r,g,b;
			unsigned int rr,gg,bb;

			b = (col1 >>  0) & 0x1f;
			g = (col1 >>  5) & 0x1f;
			r = (col1 >> 10) & 0x1f;
			b |= (b<<5) | (b<<10);
			g |= (g<<5) | (g<<10);
			r |= (r<<5) | (r<<10);

			bb = (col2 >>  0) & 0x1f;
			gg = (col2 >>  5) & 0x1f;
			rr = (col2 >> 10) & 0x1f;
			bb |= (bb<<5) | (bb<<10);
			gg |= (gg<<5) | (gg<<10);
			rr |= (rr<<5) | (rr<<10);

			r = (r * (0x10000-alpha)) + (rr * alpha);
			g = (g * (0x10000-alpha)) + (gg * alpha);
			b = (b * (0x10000-alpha)) + (bb * alpha);


			if(r>0x7fffffff) r=0x7fffffff;
			if(g>0x7fffffff) g=0x7fffffff;
			if(b>0x7fffffff) b=0x7fffffff;

			*(dst++) = (unsigned char)(b>>23);
			*(dst++) = (unsigned char)(g>>23);
			*(dst++) = (unsigned char)(r>>23);


			srcx += addx;
		}
		srcy += addy;
	}
}


static void AVI_Convert_16to24_TVInterlace_sxy_Bitmap(char *pSrcBitmap, int l)
{
	int x,y;

	const int line24 = (nAviWidth * (24/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	int srcx,srcy;
	int dstx,dsty;

	int addx, addy;
	int starty = 0;

	l &= 0x1;

	srcx = rectAviSrc.m_Width << 16;
	srcy = rectAviSrc.m_Height << 16;

	dstx = rectAvi.m_Width;
	dsty = rectAvi.m_Height;

	addx = (srcx-0xffff) / (dstx-1);
	addy = 0x10000;
	if (dstx > 720) dstx = 720;

	if (dsty != (srcy>>16))
	{
		int sy;
		sy = (srcy-0xffff) / (dsty*2-1);
		addy = sy*2; starty = (sy*l);
		rectAvi.m_Top = 0;
	}


	

	srcy = starty;
	for(y=0; y<dsty; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + (srcy >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		const unsigned short* srca = (unsigned short*)(pSrcBitmap + ((srcy+0xffff) >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + (((y+rectAvi.m_Top)<<1) + l) * line24 + rectAvi.m_Left*3 + sizeof(BITMAPINFOHEADER));
		const int alphay = srcy & 0xffff;
		srcx = 0;
		for(x=0; x<dstx; x++)
		{
			const unsigned short col1 = src[srcx >> 16];
			const unsigned short col2 = src[(srcx+0xffff) >> 16];
			const unsigned short col1a = srca[srcx >> 16];
			const unsigned short col2a = srca[(srcx+0xffff) >> 16];
			const int alpha = srcx & 0xffff;
			unsigned int r,g,b;
			unsigned int rr,gg,bb;
			unsigned int ra,ga,ba;

			b = (col1 >>  0) & 0x1f;
			g = (col1 >>  5) & 0x1f;
			r = (col1 >> 10) & 0x1f;
			b |= (b<<5) | (b<<10);
			g |= (g<<5) | (g<<10);
			r |= (r<<5) | (r<<10);

			bb = (col2 >>  0) & 0x1f;
			gg = (col2 >>  5) & 0x1f;
			rr = (col2 >> 10) & 0x1f;
			bb |= (bb<<5) | (bb<<10);
			gg |= (gg<<5) | (gg<<10);
			rr |= (rr<<5) | (rr<<10);

			r = (r * (0x10000-alpha)) + (rr * alpha);
			g = (g * (0x10000-alpha)) + (gg * alpha);
			b = (b * (0x10000-alpha)) + (bb * alpha);


			ba = (col1a >>  0) & 0x1f;
			ga = (col1a >>  5) & 0x1f;
			ra = (col1a >> 10) & 0x1f;
			ba |= (ba<<5) | (ba<<10);
			ga |= (ga<<5) | (ga<<10);
			ra |= (ra<<5) | (ra<<10);

			bb = (col2a >>  0) & 0x1f;
			gg = (col2a >>  5) & 0x1f;
			rr = (col2a >> 10) & 0x1f;
			bb |= (bb<<5) | (bb<<10);
			gg |= (gg<<5) | (gg<<10);
			rr |= (rr<<5) | (rr<<10);

			ra = (ra * (0x10000-alpha)) + (rr * alpha);
			ga = (ga * (0x10000-alpha)) + (gg * alpha);
			ba = (ba * (0x10000-alpha)) + (bb * alpha);

			r = ((r>>16) * (0x10000-alphay)) + ((ra>>16) * alphay);
			g = ((g>>16) * (0x10000-alphay)) + ((ga>>16) * alphay);
			b = ((b>>16) * (0x10000-alphay)) + ((ba>>16) * alphay);

			if(r>0x7fffffff) r=0x7fffffff;
			if(g>0x7fffffff) g=0x7fffffff;
			if(b>0x7fffffff) b=0x7fffffff;

			*(dst++) = (unsigned char)(b>>23);
			*(dst++) = (unsigned char)(g>>23);
			*(dst++) = (unsigned char)(r>>23);


			srcx += addx;
		}
		srcy += addy;
	}
}


static void AVI_Convert_16to24_TVInterlace_sy_Bitmap(char *pSrcBitmap, int l)
{
	int x,y;

	const int line24 = (nAviWidth * (24/8)+3)&~3;
	const int line16 = (rectAviSrc.m_Width * (16/8)+3)&~3;

	int srcx,srcy;
	int dstx,dsty;

	int addx, addy;
	int starty = 0;

	l &= 0x1;

	srcx = rectAviSrc.m_Width << 16;
	srcy = rectAviSrc.m_Height << 16;

	dstx = rectAvi.m_Width;
	dsty = rectAvi.m_Height;

	addx = srcx / dstx;
	addy = 0x10000;
	if (dstx > 720) dstx = 720;
	if (dsty > 240)
	{
		dsty = 240;
	}
	if (dsty != (srcy>>16))
	{
		int sy;
		sy = (srcy-0xffff) / (dsty*2-1);
		addy = sy*2; starty = (sy*l);
		rectAvi.m_Top = 0;
	}


	

	srcy = starty;
	for(y=0; y<dsty; y++)
	{
		const unsigned short* src = (unsigned short*)(pSrcBitmap + (srcy >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		const unsigned short* srca = (unsigned short*)(pSrcBitmap + ((srcy+0xffff) >> 16) * line16 + sizeof(BITMAPINFOHEADER));
		unsigned char* dst = (unsigned char*)(pAviBitmap_bitConv + (((y+rectAvi.m_Top)<<1) + l) * line24 + rectAvi.m_Left*3 + sizeof(BITMAPINFOHEADER));
		const int alphay = srcy & 0xffff;
		srcx = 0;
		for(x=0; x<dstx; x++)
		{
			const unsigned short col = src[(srcx+0x7fff) >> 16];
			const unsigned short cola = srca[(srcx+0x7fff) >> 16];
			const int alpha = srcx & 0xffff;
			unsigned int r,g,b;
			unsigned int ra,ga,ba;

			b = (col >>  0) & 0x1f;
			g = (col >>  5) & 0x1f;
			r = (col >> 10) & 0x1f;
			b |= (b<<5) | (b<<10);
			g |= (g<<5) | (g<<10);
			r |= (r<<5) | (r<<10);


			ba = (cola >>  0) & 0x1f;
			ga = (cola >>  5) & 0x1f;
			ra = (cola >> 10) & 0x1f;
			ba |= (ba<<5) | (ba<<10);
			ga |= (ga<<5) | (ga<<10);
			ra |= (ra<<5) | (ra<<10);


			r = (r * (0x10000-alphay)) + (ra * alphay);
			g = (g * (0x10000-alphay)) + (ga * alphay);
			b = (b * (0x10000-alphay)) + (ba * alphay);

			if(r>0x7fffffff) r=0x7fffffff;
			if(g>0x7fffffff) g=0x7fffffff;
			if(b>0x7fffffff) b=0x7fffffff;

			*(dst++) = (unsigned char)(b>>23);
			*(dst++) = (unsigned char)(g>>23);
			*(dst++) = (unsigned char)(r>>23);


			srcx += addx;
		}
		srcy += addy;
	}
}





/* ------------------------AUDIO---------------------------------*/
//#ifdef AVI_AUDIO

static void (__stdcall *wavecnv)(void* dst, void* src, unsigned int samples);
static unsigned int (__stdcall *wavecnv_resize)(void* dst, void* src, const unsigned int samples, unsigned int *lppos, const unsigned int adder);

static unsigned int AVI_Audio_ReSampleAdder, AVI_Audio_ReSamplePos;


int AVI_AudioStream_Start(void)
{
	nAviAudioBufSize = 0;
	nAviAudioFileSize = 0;
	lAviAudioBytesWritten = 0;
	lAviAudioBytesWrittenMax = 0;

	if (pfile == NULL) return -1;

	if (CopyStream(pfile))
	{
        return -1;
	}
	bAviAudioRecord = TRUE;
	return 0;
}

void AVI_AudioStream_WriteWaveData(char* lpwav, int samples)
{
	int dstlen;
	unsigned int samplepos = 0;

	if (bAviAudioRecord == FALSE || bAviError == TRUE) return;


		dstlen = samples * (Avi_StatusAvi.avi_audio_channel * (Avi_StatusAvi.avi_audio_bitrate/8));

	if (AVIStreamWrite(audio_ps,nAviAudioSamples,samples, lpwav, dstlen ,AVIIF_KEYFRAME,NULL,&lAviAudioBytesWritten)!=0)
	{
		bAviError = TRUE;
	}

	if (lAviAudioBytesWritten > lAviAudioBytesWrittenMax) lAviAudioBytesWrittenMax = lAviAudioBytesWritten;

	nAviAudioFileSize += (unsigned __int64)lAviAudioBytesWritten;
	nAviAudioSamples += samples;

}


//#endif	/* AUDIO */

#endif /* MAME_AVI */
