//	VirtualDub - Video processing and capture application
//
//	file added by pulco 29/08/2002
//	some parts heavily inspired from mpeg.cpp
//
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "../VirtualDub.h"
#include <stdio.h>
#include <crtdbg.h>
#include <process.h>
#include <windows.h>
#include <commctrl.h>
#include <fcntl.h>
#include <io.h>
#include <math.h>
#include <sstream>

#include "../AudioSource.h"
#include "../VideoSource.h"
#include "../Error.h"
#include "../InputFile.h"
#include "../resource.h"

#include "../misc.h"


#define DVD2AVI_GLOBAL_SYMBOLS


#include "conf.h"
#include "SmartRipperInfoFile.h"
#include "dvdfilters.h"
#include "alloc.h"
#include "memcopy.h"
#include "mpeg2dec.h"
#include "wavefs44.h"
#include "norm.h"
#include "store.h"


#define TRACK_HEIGHT	30
#define TRACK_WIDTH_PRECISION 3
//	width will 2^WIDTH_PRECISION pixels aligned

static char szTemp[_MAX_PATH];

typedef enum
{
	OUTPUT_NOWHERE,
	OUTPUT_SCREEN,
	OUTPUT_D2VFILE
} DvdOutputType;

void Initialize_FPU_IDCT(void);
void FPU_IDCT(short *block);
void Initialize_REF_IDCT(void);
void REF_IDCT(short *block);


void cb_empty_DVD2aviCallBackFunction(const char *){}
void cb_empty_DVD2aviCallBackFunction2(int){}
void cb_empty_DVD2aviCallBackFunction3(const char *,int){}
void cb_empty_DVD2aviCallBackFunction4(void){}
void cb_empty_DVD2aviCallBackFunction5(unsigned char *data){}
void cb_empty_DVD2aviCallBackFunction6(int,int){}

static std::vector<DVDFrame> g_DVDFrames;

void cb_D2VOutput(const char * p_format,int p_info)
{
	fprintf(g_GuiGlobals.D2VFile, p_format, p_info);
}
void cb_D2VOutputImage(int p_type,int p_info,__int64 p_info2,int p_info3)
{
	switch(p_type)
	{
	case I_TYPE:
		{
			fprintf(g_GuiGlobals.D2VFile,"\n7 %d %I64X %d",p_info,p_info2,p_info3);
			g_DVDFrames.push_back(DVDFrame(I_TYPE,p_info,p_info2,p_info3));
			g_PicInfo.kframe_count++;
			break;
		}
	case B_TYPE:
		{
			fprintf(g_GuiGlobals.D2VFile," 6 %d",p_info);
			g_DVDFrames.push_back(DVDFrame(B_TYPE,p_info));
			break;
		}
	case P_TYPE:
		{
			fprintf(g_GuiGlobals.D2VFile," 5 %d",p_info);
			g_DVDFrames.push_back(DVDFrame(P_TYPE,p_info));
			break;
		}
	}
}
void cb_D2VOutputString(const char * p_info) {fprintf(g_GuiGlobals.D2VFile, "%s", p_info);}

void AutoSize(void);
void RenderYUY2(unsigned char *data);
void ResizeOverlay(void);
void RenderRGB24(unsigned char *data);
void ResizeWindow(int width, int height);
void SetOutput(DvdOutputType p_type);
void DumpBmp(void);

static void CheckINI();
static void Recovery(void);

static HANDLE hThread = 0;
static DWORD threadId;

static POINT m_PreviewPos;
static POINT m_MousePos;
static SmartRipperInfoFile l_File;


using namespace std;

//////////////////////////////////////////////////////////////////////////
//
//
//					InputFileMPEG2Options
//
//		not really a class. all variables are "global" here
//
//////////////////////////////////////////////////////////////////////////
class InputFileMPEG2;

class InputFileMPEG2Options : public InputFileOptions
{
private:
	
	static void Play(void);
	static InputFileMPEG2 * m_TheMovie;
public:
	struct InputFileMPEG2Opts
	{
		int len;
		char d2vfile[_MAX_PATH];
	} opts;

	InputFileMPEG2Options(InputFileMPEG2 * p_TheMovie) { m_TheMovie = p_TheMovie;};
	~InputFileMPEG2Options();

	bool read(const char *buf);
	int write(char *buf, int buflen);

	static BOOL APIENTRY SetupDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
	static void UpdateSizes();

};
InputFileMPEG2 * InputFileMPEG2Options::m_TheMovie = 0;

InputFileMPEG2Options::~InputFileMPEG2Options()
{
	Recovery();
	g_DVDFrames.clear();
}

bool InputFileMPEG2Options::read(const char *buf)
{
	const InputFileMPEG2Opts *pp = (const InputFileMPEG2Opts *)buf;

	if (pp->len != sizeof(InputFileMPEG2Opts))
		return false;

	opts = *pp;

	return true;
}

int InputFileMPEG2Options::write(char *buf, int buflen)
{
	InputFileMPEG2Opts *pp = (InputFileMPEG2Opts *)buf;

	if (buflen<sizeof(InputFileMPEG2Opts))
		return 0;

	opts.len = sizeof(InputFileMPEG2Opts);
	*pp = opts;

	return sizeof(InputFileMPEG2Opts);
}

///////

static void RefreshWindow()
{
	if (hThread == 0 || WaitForSingleObject(hThread, 0)==WAIT_OBJECT_0)
	{
		g_Flags.Fault_Flag = false; g_Flags.Display_Flag = true;
		Write_Frame(g_PicInfo.backward_reference_frame, g_DVDGlobals.d2v_backward, 0);
	}
}

static void D2vProgress(int p_progress)
{
	if (g_GuiGlobals.hWnd)
	{
		p_progress = (p_progress * 100)/TRACK_PITCH ;
		ostringstream o;
		o << p_progress << " %" << ends;
		SetWindowText(GetDlgItem(g_GuiGlobals.hWnd, IDOK),o.str().c_str());
	}
}

static void AutoSize()
{
	if (g_PicInfo.Aspect_Change == ASPECT_FREE)
		return;

	double width = g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right - g_PicInfo.Squeeze_Width;

	double input_aspect = 1.0;
	double movie_aspect = 1.0;
	double pc_aspect = (4./3.) * (width / (double)g_PicInfo.Coded_Picture_Width);

	switch(g_DVDGlobals.aspect_ratio_information)
	{
	case 2:
		input_aspect = (4./3.) * ((double)(g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right) / (double)g_PicInfo.Coded_Picture_Width);
		break;
	case 3:
		input_aspect = (16./9.) * ((double)(g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right) / (double)g_PicInfo.Coded_Picture_Width);
		break;
	case 4:
		input_aspect = (2.21) * ((double)(g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right) / (double)g_PicInfo.Coded_Picture_Width);
		break;
	}

	switch(g_PicInfo.Aspect_Change)
	{
	case ASPECT_43:
		movie_aspect = 4./3.;
		break;
	case ASPECT_185:
		movie_aspect = 1.85;
		break;
	case ASPECT_235:
		movie_aspect = 2.35;
		break;
	}

	double crop = ((double)g_PicInfo.Coded_Picture_Height - ((double)g_PicInfo.Coded_Picture_Height / (movie_aspect/input_aspect)));
	if (crop<0)
		crop=0;
	double crop_err = crop;
	crop = ((int)crop) & ~15;
	double height = ((double)g_PicInfo.Coded_Picture_Height - crop - (crop_err - crop))	/	(input_aspect/pc_aspect);

	g_PicInfo.Clip_Top = g_PicInfo.Clip_Bottom = (int)(crop / 2);
	g_PicInfo.Squeeze_Height = g_PicInfo.Coded_Picture_Height - g_PicInfo.Clip_Top - g_PicInfo.Clip_Bottom - (((int)height) & ~7);
	InputFileMPEG2Options::UpdateSizes();
}

void ResizeWindow(int width, int height)
{	
	if (IsWindow(g_GuiGlobals.hTrack))
		MoveWindow(g_GuiGlobals.hTrack, 0, height, width, TRACK_HEIGHT, true);
	if (IsWindow(g_GuiGlobals.Preview))
	{
		GetWindowRect(g_GuiGlobals.Preview, &g_GuiGlobals.wrect);
		GetClientRect(g_GuiGlobals.Preview, &g_GuiGlobals.crect);
		g_PicInfo.Edge_Width = g_GuiGlobals.wrect.right - g_GuiGlobals.wrect.left - g_GuiGlobals.crect.right;
		g_PicInfo.Edge_Height = g_GuiGlobals.wrect.bottom - g_GuiGlobals.wrect.top - g_GuiGlobals.crect.bottom;

		MoveWindow(g_GuiGlobals.Preview, g_GuiGlobals.wrect.left, g_GuiGlobals.wrect.top, width+g_PicInfo.Edge_Width, height+g_PicInfo.Edge_Height+TRACK_HEIGHT, true);
	}
}

static void CheckINI()
{
	
	__asm
	{
		mov			eax, 1
		cpuid
		test		edx, 0x00800000		// STD MMX
		jz			TEST_SSE
		mov			[cpu.mmx], 1
TEST_SSE:
		test		edx, 0x02000000		// STD SSE
		jz			TEST_3DNOW
		mov			[cpu.ssemmx], 1
		mov			[cpu.ssefpu], 1

		test		edx, 0x04000000	// STD SSE2	TRB 12/30/2001
		jz			TEST_3DNOW
		mov			[cpu.sse2mmx], 1

TEST_3DNOW:
		mov			eax, 0x80000001
		cpuid
		test		edx, 0x80000000		// 3D NOW
		jz			TEST_EXTENDED_3DNOW
		mov			[cpu._3dnow], 1
TEST_EXTENDED_3DNOW:
		mov			eax, 0x80000001
		cpuid
		test		edx, 0x40000000		// EXTENDED 3D NOW : pulco 20/09/2002
		jz			TEST_SSEMMX
		mov			[cpu.extended_3dnow], 1
TEST_SSEMMX:
		test		edx, 0x00400000		// SSE MMX
		jz			TEST_END
		mov			[cpu.ssemmx], 1
TEST_END:
	}


	if (cpu.sse2mmx)
		g_Flags.iDCT_Flag = IDCT_SSE2MMX;
	else if (cpu.ssemmx)
		g_Flags.iDCT_Flag = IDCT_SSEMMX;
	else
		g_Flags.iDCT_Flag = IDCT_MMX;

	g_Flags.FO_Flag = FO_NONE;
	g_Flags.Track_Flag = TRACK_NONE;
	g_Flags.Track_Flag2 = TRACK_NONE;
	g_Flags.Format_Flag = FORMAT_AUTO;
	g_Ac3Context.AC3_Flag = AUDIO_DEMUXONE;
	g_Ac3Context.DRC_Flag = DRC_NORMAL;
	g_Ac3Context.DSDown_Flag = true;
	g_Flags.MPA_Flag = AUDIO_DEMUXONE;
//	g_Ac3Context.SRC_Flag = SRC_NONE;
	g_Ac3Context2 = g_Ac3Context;
	g_Flags.Norm_Ratio = 100;
	g_PicInfo.Resize_Function = FILTER_BILINEAR;
	g_Flags.MPEG2_Transport_VideoPID = 0; // arbitrary init value
	g_Flags.MPEG2_Transport_AudioPID = 0; // arbitrary init value
	g_Flags.Store_Flag = STORE_RGB24;
	g_PicInfo.frame_count = 0;
	g_PicInfo.RGB_Scale = 0x1000254310002543;
	g_PicInfo.RGB_Offset = 0x0010001000100010;
	g_PicInfo.RGB_CBU = 0x0000408D0000408D;
	g_PicInfo.RGB_CGX = 0xF377E5FCF377E5FC;
	g_PicInfo.RGB_CRV = 0x0000331300003313;
	g_Flags.Scale_Flag = true;
	g_Flags.Quit = false;
	g_Flags.D2vExists = false;
	g_Flags.Play_Flag = true;
	g_Flags.InitDecoder = true;
}

void RenderRGB24(unsigned char* data)
{
	if (data)
	{
		BITMAPINFOHEADER birgb;

		ZeroMemory(&birgb, sizeof(BITMAPINFOHEADER));
		birgb.biSize = sizeof(BITMAPINFOHEADER);
		birgb.biWidth = g_PicInfo.Resize_Width;
		birgb.biHeight = g_PicInfo.Resize_Height;
		birgb.biPlanes = 1;
		birgb.biBitCount = 24;
		birgb.biCompression = BI_RGB;
		birgb.biSizeImage = g_PicInfo.Resize_Width * g_PicInfo.Resize_Height * 3;
		
		SetDIBitsToDevice(g_GuiGlobals.hDC, 0, 0, g_PicInfo.Resize_Width, g_PicInfo.Resize_Height, 0, 0, 0, g_PicInfo.Resize_Height, data, (LPBITMAPINFO)&birgb, DIB_RGB_COLORS);
	}
}

BOOL CALLBACK PreviewDialogProc(
  HWND hwndDlg,  // handle to dialog box
  UINT uMsg,     // message
  WPARAM wParam, // first message parameter
  LPARAM lParam  // second message parameter
)
{
	static bool dragging =false;
	switch(uMsg)
	{
		case WM_CREATE:
			m_PreviewPos.x = 0;
			m_MousePos.x = 0;
			m_PreviewPos.y = 0;
			m_MousePos.y = 0;
			dragging = false;
			break;
		case WM_INITDIALOG:
			{
				RECT parent;
				GetClientRect(hwndDlg,&parent);
				g_GuiGlobals.hTrack = CreateWindow(TRACKBAR_CLASS, NULL,
					WS_CHILD | WS_VISIBLE /*| WS_DISABLED*/ | TBS_ENABLESELRANGE | TBS_NOTICKS | TBS_TOP,
					0, 0, TRACK_HEIGHT*10, TRACK_HEIGHT, hwndDlg, NULL, GetModuleHandle(0), NULL);
				SendMessage(g_GuiGlobals.hTrack, TBM_SETRANGE, (WPARAM) true, (LPARAM) MAKELONG(0, TRACK_PITCH));
				SetOutput(OUTPUT_SCREEN);
				AutoSize();
				g_OutFunctions.ResizeWindow(g_PicInfo.Resize_Width, g_PicInfo.Resize_Height);
				g_GuiGlobals.hDC = GetDC(hwndDlg);
			}
			return TRUE;
		case WM_PAINT:
			{
				if (g_Flags.Check_Flag && (g_Flags.Store_Flag == STORE_RGB24))
					RenderRGB24(GetFilteredOutput());
			}
			return FALSE;
		case WM_DESTROY:
			ReleaseDC(hwndDlg, g_GuiGlobals.hDC);
			g_GuiGlobals.hDC = 0;
			break;
		case WM_LBUTTONUP:
			{
				dragging = false;
				break;
			}
		case WM_LBUTTONDOWN:
			{
				dragging = true ;
				m_MousePos.x = LOWORD(lParam);
				m_MousePos.y = HIWORD(lParam);
				break;
			}
		case WM_MOUSEMOVE:
			{
				if (dragging)
				{
					m_PreviewPos.x -= (m_MousePos.x - LOWORD(lParam));
					m_PreviewPos.y -= (m_MousePos.y - HIWORD(lParam));

					RECT parent;
					GetWindowRect(g_GuiGlobals.hWnd,&parent);
					MoveWindow(g_GuiGlobals.Preview, parent.left+m_PreviewPos.x, parent.bottom+m_PreviewPos.y,
						g_PicInfo.Resize_Width,g_PicInfo.Resize_Height+TRACK_HEIGHT,TRUE);
					ShowWindow(g_GuiGlobals.Preview, SW_SHOW);
				}
				break;
			}
		case WM_HSCROLL:
			//if (hThread && WaitForSingleObject(hThread, 0)==WAIT_OBJECT_0)
			{
				g_Flags.Display_Flag = true;
				int trackpos = SendMessage(g_GuiGlobals.hTrack, TBM_GETPOS, 0, 0);
				DVD_T("Track pos" << trackpos);
				process.startloc = process.start = process.total*trackpos/TRACK_PITCH;

				process.startfile = 0; process.run = 0;
				while (process.startloc > process.length[process.startfile])
				{
					process.startloc -= process.length[process.startfile];
					process.run += process.length[process.startfile];
					process.startfile++;
				}

				process.end = process.total - BUFFER_SIZE;
				process.endfile = g_Flags.File_Limit - 1;
				process.endloc = (process.length[g_Flags.File_Limit-1]/BUFFER_SIZE-1)*BUFFER_SIZE;

				process.locate = LOCATE_SCROLL;

				g_Flags.ThreadMode = false;
				try{
				MPEG2Dec(0);
				}catch(DVD_EOF){}
				//	try/catch : end of file raises an exception sometimes
				//	can be avoided by using the thread mechanism

				//hThread = CreateThread(NULL, 0, MPEG2Dec, 0, 0, &threadId);
				RefreshWindow();
			}
			break;

	}
	return FALSE;
}


void DumpBmp(void)
{
	static int count=0;
	int width, height;

	width = g_PicInfo.Coded_Picture_Width;
	height = g_PicInfo.Coded_Picture_Height;

	width -= g_PicInfo.Clip_Left+g_PicInfo.Clip_Right;
	height -= g_PicInfo.Clip_Top+g_PicInfo.Clip_Bottom;

	SYSTEMTIME st;
	GetLocalTime(&st);

	sprintf(szTemp, "%smpeg2 %02d%02d%02d%02d%02d_%03d.bmp", l_File.GetName().c_str(),
		st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,count++);
	FILE * BMPFile = fopen(szTemp, "wb");

	static unsigned char BMPHeader[54] =
	{
		0x42, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0b,
		0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};

	int i = fwrite(BMPHeader, 1, sizeof(BMPHeader), BMPFile);
	i += fwrite(g_PicInfo.rgb24, 1, width * height * 3, BMPFile);

	int j = i & 3;

	while (j>0)
	{
		i += fputc(0, BMPFile);
		j--;
	}

	fseek(BMPFile, 2, SEEK_SET);
	fwrite(&i, sizeof(int), 1, BMPFile);
	fseek(BMPFile, 18, SEEK_SET);
	fwrite(&width, sizeof(int), 1, BMPFile);
	fwrite(&height, sizeof(int), 1, BMPFile);
	
	fclose(BMPFile);
}


void EnableAudioDemux(bool p_Enable = true)
{
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP), p_Enable);
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE), p_Enable);
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX), p_Enable);
}
void EnableAudio2Demux(bool p_Enable = true)
{
	if (g_Flags.Track_Flag2 == g_Flags.Track_Flag && g_Flags.Track_Flag != TRACK_NONE)
	{
		g_Flags.Track_Flag2 = TRACK_NONE;
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_TRACK_NUMBER2, CB_SETCURSEL, 0, 0);
	}
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP2), p_Enable && (g_Flags.Track_Flag2 != TRACK_NONE));
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE2), p_Enable && (g_Flags.Track_Flag2 != TRACK_NONE));
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX2), p_Enable && (g_Flags.Track_Flag2 != TRACK_NONE));
}

void EnableAudio2GUI(bool p_Enable = true)
{
	EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_TRACK_NUMBER2), p_Enable);
	int l_Track2 = p_Enable ? SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_TRACK_NUMBER2, CB_GETCURSEL, 0, 0) : 0;
	g_Flags.Track_Flag2 = (l_Track2 == 0) ? TRACK_NONE : l_Track2-1;
	EnableAudio2Demux(p_Enable);
}


void InputFileMPEG2Options::UpdateSizes()
{
	if (g_GuiGlobals.hWnd != 0)
	{
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_WIDTH_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right)>>TRACK_WIDTH_PRECISION));
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom)>>3));
		//	without these two lines , the trackbars pos at startup are not good (still can't figure why since these
		//	initializations are done in another function with the same values ...)


		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_LEFT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 32));
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_RIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 32));
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 128));
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 128));
		//	same comment for these lines


		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_LEFT_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Left>>3);
		sprintf(szTemp, "%d", g_PicInfo.Clip_Left);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_LEFT, szTemp);
		
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_RIGHT_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Right>>3);
		sprintf(szTemp, "%d", g_PicInfo.Clip_Right);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_RIGHT, szTemp);
		
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Top>>1);
		sprintf(szTemp, "%d", g_PicInfo.Clip_Top);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_TOP, szTemp);
		
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Bottom>>1);
		sprintf(szTemp, "%d", g_PicInfo.Clip_Bottom);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM, szTemp);
		
		int width = g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right-g_PicInfo.Squeeze_Width;
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_WIDTH_SLIDER, TBM_SETPOS, 1,(width)>>TRACK_WIDTH_PRECISION);
		sprintf(szTemp, "%d", width);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_WIDTH, szTemp);
		
		int height = g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height;
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETPOS, 1,(height)>>3);
		sprintf(szTemp, "%d", height);
		SetDlgItemText(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT, szTemp);
		
		switch(g_PicInfo.Aspect_Change)
		{
		case ASPECT_FREE:
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER), true);
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER), true);
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER), true);
			break;
		case ASPECT_43:
		case ASPECT_185:
		case ASPECT_235:
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER), false);
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER), false);
			EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER), false);
			break;
		}
		SendDlgItemMessage(g_GuiGlobals.hWnd, IDC_MPEG2_ASPECT, CB_SETCURSEL, g_PicInfo.Aspect_Change, 0);
	}
}




//////////////////////////////////////////////////////////////////////////
//
//
//					InputFileMPEG2
//
//
//////////////////////////////////////////////////////////////////////////

class AudioSourceMPEG2;
class VideoSourceMPEG2;

class InputFileMPEG2 : public InputFile
{

friend VideoSourceMPEG2;
friend AudioSourceMPEG2;

public:
//	AudioSource *audioSrc;
//	AudioSource *audio2Src;
//	VideoSource *videoSrc;
//	List2<InputFilenameNode> listFiles;

	static InputFileMPEG2 * GetNewInstance(void) {m_CurrentInstance = new InputFileMPEG2();return m_CurrentInstance;}
	static InputFileMPEG2 * GetLastInstance(void) {return m_CurrentInstance;}//	ugly,ugly,ugly

	virtual ~InputFileMPEG2();
	virtual void Init(char *szFile);

	virtual void setOptions(InputFileOptions *);
	virtual InputFileOptions *promptForOptions(HWND);
	virtual InputFileOptions *createOptions(const char *buf);
	virtual void InfoDialog(HWND hwndParent) {}

	virtual bool isOptimizedForRealtime() {return false;}
	virtual bool isStreaming() {return false;}
	void ThreadKill();
	void SaveD2v(void);
	bool LoadD2v(void);
private:
	InputFileMPEG2();
	void InitSources(void);
	int init;
	static InputFileMPEG2 * m_CurrentInstance;
};
InputFileMPEG2 * InputFileMPEG2::m_CurrentInstance = 0;
void ThreadKill(void)
{
	InputFileMPEG2::GetLastInstance()->ThreadKill();
}
void SetOutput(DvdOutputType p_type)
{
	switch(p_type)
	{
	case OUTPUT_NOWHERE:
		g_OutFunctions.D2VOutput = cb_empty_DVD2aviCallBackFunction3;
		g_OutFunctions.D2VOutputString = cb_empty_DVD2aviCallBackFunction;
		g_OutFunctions.RenderRGB24 = cb_empty_DVD2aviCallBackFunction5;
		g_OutFunctions.ResizeWindow = cb_empty_DVD2aviCallBackFunction6;
		g_Flags.Display_Flag = false;
		g_Flags.D2V_Flag = false;
		g_InfoFunctions.TrackProgress = cb_empty_DVD2aviCallBackFunction2;
		break;
	case OUTPUT_SCREEN:
		g_OutFunctions.D2VOutput = cb_empty_DVD2aviCallBackFunction3;
		g_OutFunctions.D2VOutputString = cb_empty_DVD2aviCallBackFunction;
		g_OutFunctions.RenderRGB24 = RenderRGB24;
		g_OutFunctions.ResizeWindow = ResizeWindow;
		g_Flags.Display_Flag = true;
		g_Flags.D2V_Flag = false;
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_LEFT_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_RIGHT_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_WIDTH_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_FILTMODE),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_ASPECT),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_SIZE_RESET),true);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDOK),true);
		SetWindowText(GetDlgItem(g_GuiGlobals.hWnd, IDOK),"OK");
		EnableWindow(g_GuiGlobals.hTrack,true);
		g_InfoFunctions.TrackProgress = cb_empty_DVD2aviCallBackFunction2;
		break;
	case OUTPUT_D2VFILE:
		g_OutFunctions.D2VOutput = cb_D2VOutput;
		g_OutFunctions.D2VOutputString = cb_D2VOutputString;
		g_OutFunctions.RenderRGB24 = cb_empty_DVD2aviCallBackFunction5;
		g_OutFunctions.ResizeWindow = cb_empty_DVD2aviCallBackFunction6;
		g_Flags.Display_Flag = false;
		g_Flags.D2V_Flag = true;
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_LEFT_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_RIGHT_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_TOP_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_BOTTOM_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_WIDTH_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_HEIGHT_SLIDER),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_FILTMODE),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_ASPECT),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_SIZE_RESET),false);
		EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDOK),false);
		SetWindowText(GetDlgItem(g_GuiGlobals.hWnd, IDOK),"...");
		EnableWindow(g_GuiGlobals.hTrack,false);
		g_InfoFunctions.TrackProgress = D2vProgress;
		break;
	}
}


BOOL APIENTRY InputFileMPEG2Options::SetupDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam)
{
	InputFileMPEG2Options *thisPtr = (InputFileMPEG2Options *)GetWindowLong(hDlg, DWL_USER);
	g_GuiGlobals.hWnd = hDlg;
	switch(message)
	{
		case WM_INITDIALOG:
			{
				SetWindowLong(hDlg, DWL_USER, lParam);
				CheckDlgButton(hDlg, IDC_CHECK_SAVE_D2V, TRUE);
				g_GuiGlobals.Preview = CreateDialog(GetModuleHandle(0),MAKEINTRESOURCE(IDD_MPEG2_PREVIEW),hDlg,PreviewDialogProc);
				ShowWindow(g_GuiGlobals.Preview, SW_HIDE);
				EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_SAVE_BMP), false);
				EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, ID_FILE_PLAY), g_Flags.D2vExists);

				SetWindowText(hDlg,l_File.GetName().c_str());
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_ADDSTRING, 0, (LPARAM)"Bilinear (MMX)");
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_ADDSTRING, 0, (LPARAM)"Soft Bicubic (MMX)");
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_ADDSTRING, 0, (LPARAM)"Precise Bicubic (MMX)");
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_ADDSTRING, 0, (LPARAM)"Neutral Bicubic (MMX)");
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_ADDSTRING, 0, (LPARAM)"Custom Bicubic (MMX)");
				SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_SETCURSEL, g_PicInfo.Resize_Function, 0);

				SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_ADDSTRING, 0, (LPARAM)"Free");
				SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_ADDSTRING, 0, (LPARAM)"TV 4:3");
				SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_ADDSTRING, 0, (LPARAM)"Cinema 1.85:1");
				SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_ADDSTRING, 0, (LPARAM)"Cinema 2.35:1");
				SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_SETCURSEL, g_PicInfo.Aspect_Change, 0);

				SendDlgItemMessage(hDlg, IDC_MPEG2_LEFT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 32));
				SendDlgItemMessage(hDlg, IDC_MPEG2_RIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 32));
				SendDlgItemMessage(hDlg, IDC_MPEG2_TOP_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 128));
				SendDlgItemMessage(hDlg, IDC_MPEG2_BOTTOM_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, 128));
				SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right)>>TRACK_WIDTH_PRECISION));
				SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom)>>3));
				
				int i = 0;
				SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER, CB_ADDSTRING, 0, (LPARAM)"No audio");
				for (;i < l_File.GetAudioStreams().size();i++)
					SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER, CB_ADDSTRING, 0, (LPARAM)l_File.GetAudioStreams()[i].c_str());
				SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER, CB_SETCURSEL, 0, 0);
				g_Flags.Track_Flag = TRACK_NONE;
				if ( i > 1 )
				{
					SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER2, CB_ADDSTRING, 0, (LPARAM)"No audio");
					for (i = 0;i < l_File.GetAudioStreams().size();i++)
						SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER2, CB_ADDSTRING, 0, (LPARAM)l_File.GetAudioStreams()[i].c_str());
					SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER2, CB_SETCURSEL, 0, 0);
					g_Flags.Track_Flag2 = TRACK_NONE;
				}
				//else
				EnableAudio2GUI(false);

				CheckDlgButton(hDlg,IDC_MPEG2_AUDIO_KEEP,true);
				CheckDlgButton(hDlg,IDC_MPEG2_AUDIO_KEEP2,true);
				CheckDlgButton(hDlg,IDC_MPEG2_AUDIO_DSDOWNMIX,true);
				CheckDlgButton(hDlg,IDC_MPEG2_AUDIO_DSDOWNMIX2,true);
				EnableAudioDemux(false);
				EnableAudio2Demux(false);
				//	(m_TheMovie->videoSrc)->getFrame(0);
				//	videoSrc is null if the .d2v still not exists
				//	Thanks to Jacek Dojnikowski for reporting this bug
				SendMessage(g_GuiGlobals.hTrack, TBM_SETRANGE, (WPARAM) true, (LPARAM) MAKELONG(0, TRACK_PITCH));
				SendMessage(g_GuiGlobals.Preview, WM_HSCROLL,  0, 0);


				AutoSize();
				UpdateSizes();
				ShowWindow(hDlg, SW_SHOW);
				RefreshWindow();
				return TRUE;
			}
		case WM_COMMAND:
				//if (IsDlgButtonChecked(hDlg, IDC_MPEG_ALL_FRAMES	))
				//	thisPtr->opts.iDecodeMode = 0;
			switch (LOWORD(wParam))
			{
			case IDC_MPEG2_FILTMODE:
				{
					g_PicInfo.Resize_Function = (ResizeType)SendDlgItemMessage(hDlg, IDC_MPEG2_FILTMODE, CB_GETCURSEL, 0, 0);
					KillFilters();
					RefreshWindow();
				}
				break;

			case IDC_MPEG2_ASPECT:
				{
					g_PicInfo.Aspect_Change = SendDlgItemMessage(hDlg, IDC_MPEG2_ASPECT, CB_GETCURSEL, 0, 0);
					AutoSize();
					UpdateSizes();
					KillFilters();
					RefreshWindow();
				}
				break;

			case IDC_MPEG2_SIZE_RESET:
				{
					g_PicInfo.Clip_Top=g_PicInfo.Clip_Bottom=g_PicInfo.Clip_Left=g_PicInfo.Clip_Right=g_PicInfo.Squeeze_Width=g_PicInfo.Squeeze_Height=0;
					g_PicInfo.Aspect_Change = ASPECT_FREE;
					AutoSize();
					UpdateSizes();
					KillFilters();
					RefreshWindow();
				}
				break;
			case IDC_MPEG2_PREVIEW:
				{
					if (IsDlgButtonChecked(hDlg,IDC_MPEG2_PREVIEW))
					{
						RECT parent;
						GetWindowRect(hDlg,&parent);
						MoveWindow(g_GuiGlobals.Preview,parent.left+m_PreviewPos.x,parent.bottom+m_PreviewPos.y,
							g_PicInfo.Resize_Width,g_PicInfo.Resize_Height+TRACK_HEIGHT,TRUE);
						ShowWindow(g_GuiGlobals.Preview, SW_SHOW);
						EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_SAVE_BMP), true);
					}
					else
					{
						ShowWindow(g_GuiGlobals.Preview, SW_HIDE);
						EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_SAVE_BMP), false);
					}
				}
				break;
			case IDC_MPEG2_TRACK_NUMBER:
				{
					int l_Track = SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER, CB_GETCURSEL, 0, 0);
					g_Flags.Track_Flag = (l_Track == 0) ? TRACK_NONE : l_Track-1;
					if (g_Flags.Track_Flag == TRACK_NONE)
					{
						EnableAudio2GUI(false);
						EnableAudioDemux(false);
					}
					else
					{
						EnableAudioDemux(true);
						if (l_File.GetAudioStreams()[g_Flags.Track_Flag].find("AC3") != string::npos)
						{
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP),true);
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE,false);
							g_Ac3Context.AC3_Flag = AUDIO_DEMUXONE;
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP,true);
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX),false);
						}
						else
						{
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE,true);
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP,false);
							g_Ac3Context.AC3_Flag = AUDIO_DECODE;
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP),false);
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX),true);
						}
						if (l_File.GetAudioStreams().size() > 1)
							EnableAudio2GUI(true);
					}
					break;
				}
			case IDC_MPEG2_TRACK_NUMBER2:
				{
					int l_Track2 = SendDlgItemMessage(hDlg, IDC_MPEG2_TRACK_NUMBER2, CB_GETCURSEL, 0, 0);
					g_Flags.Track_Flag2 = (l_Track2 == 0) ? TRACK_NONE : l_Track2-1;
					EnableAudio2Demux(true);
					if (g_Flags.Track_Flag2 != TRACK_NONE)
					{
						if (l_File.GetAudioStreams()[g_Flags.Track_Flag2].find("AC3") != string::npos)
						{

							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP2),true);
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE2,false);
							g_Ac3Context2.AC3_Flag = AUDIO_DEMUXONE;
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP2,true);
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX2),false);
						}
						else
						{
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DECODE2,true);
							CheckDlgButton(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP2,false);
							g_Ac3Context2.AC3_Flag = AUDIO_DECODE;
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_KEEP2),false);
							EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX2),true);
						}
					}
					break;
				}
			case IDC_MPEG2_AUDIO_KEEP:
				{
					g_Ac3Context.AC3_Flag = AUDIO_DEMUXONE;
					EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX),false);
					break;
				}
			case IDC_MPEG2_AUDIO_KEEP2:
				{
					g_Ac3Context2.AC3_Flag = AUDIO_DEMUXONE;
					EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX2),false);
					break;
				}
			case IDC_MPEG2_AUDIO_DECODE:
				{
					g_Ac3Context.AC3_Flag = AUDIO_DECODE;
					EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX),true);
					break;
				}
			case IDC_MPEG2_AUDIO_DECODE2:
				{
					g_Ac3Context2.AC3_Flag = AUDIO_DECODE;
					EnableWindow(GetDlgItem(g_GuiGlobals.hWnd, IDC_MPEG2_AUDIO_DSDOWNMIX2),true);
					break;
				}
			case IDC_MPEG2_AUDIO_DSDOWNMIX:
				{
					g_Ac3Context.DSDown_Flag = (IsDlgButtonChecked(hDlg,IDC_MPEG2_AUDIO_DSDOWNMIX));
					break;
				}
			case IDC_MPEG2_AUDIO_DSDOWNMIX2:
				{
					g_Ac3Context2.DSDown_Flag = (IsDlgButtonChecked(hDlg,IDC_MPEG2_AUDIO_DSDOWNMIX2));
					break;
				}
			case IDC_MPEG2_SAVE_BMP:
				{
					DumpBmp();
					break;
				}
			case ID_FILE_PLAY:
				Play();
				break;
			case IDOK:
				{
					DVD_T("IDOK button");
					g_Flags.Quit = true;
					m_TheMovie->SaveD2v();
					return true;
				}
			case IDCANCEL:
				{
					g_Flags.Quit = true;
					g_Flags.Stop_Flag = true;
					ThreadKill();
					EndDialog(g_GuiGlobals.hWnd,1);
					//throw MyError("MPEG2 : aborted by user");
					return true;
				}
			}
			break;
		case WM_MOVE:
			if (IsDlgButtonChecked(hDlg,IDC_MPEG2_PREVIEW))
			{
				RECT parent;
				GetWindowRect(hDlg,&parent);
				MoveWindow(g_GuiGlobals.Preview, parent.left+m_PreviewPos.x, parent.bottom+m_PreviewPos.y,
					g_PicInfo.Resize_Width,g_PicInfo.Resize_Height+TRACK_HEIGHT,TRUE);
				ShowWindow(g_GuiGlobals.Preview, SW_SHOW);
			}
			break;
		case WM_DESTROY:
			{
				g_Flags.ThreadMode = false;
				if (thisPtr->m_TheMovie->videoSrc)
					thisPtr->m_TheMovie->videoSrc->init();
				g_GuiGlobals.Preview = 0;
				g_GuiGlobals.hWnd = 0;
				break;
			}
		case WM_HSCROLL:
			{
				int i = 0;
				switch (GetWindowLong((HWND)lParam, GWL_ID))
				{
					case IDC_MPEG2_LEFT_SLIDER:
						i = SendDlgItemMessage(hDlg, IDC_MPEG2_LEFT_SLIDER, TBM_GETPOS, 0, 0)<<3;
						if (g_PicInfo.Coded_Picture_Width - i - g_PicInfo.Clip_Right - g_PicInfo.Squeeze_Width >= MIN_WIDTH)
						{
							g_PicInfo.Clip_Left = i;
							sprintf(szTemp, "%d", g_PicInfo.Clip_Left);
							SetDlgItemText(hDlg, IDC_MPEG2_LEFT, szTemp);

							SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right)>>TRACK_WIDTH_PRECISION));
							SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right-g_PicInfo.Squeeze_Width)>>TRACK_WIDTH_PRECISION);
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right-g_PicInfo.Squeeze_Width);
							SetDlgItemText(hDlg, IDC_MPEG2_WIDTH, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_LEFT_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Left>>3);
						AutoSize();
						UpdateSizes();
						break;

					case IDC_MPEG2_RIGHT_SLIDER:
						i = SendDlgItemMessage(hDlg, IDC_MPEG2_RIGHT_SLIDER, TBM_GETPOS, 0, 0)<<3;
						if (g_PicInfo.Coded_Picture_Width - i - g_PicInfo.Clip_Left - g_PicInfo.Squeeze_Width >= MIN_WIDTH)
						{
							g_PicInfo.Clip_Right = i;
							sprintf(szTemp, "%d", g_PicInfo.Clip_Right);
							SetDlgItemText(hDlg, IDC_MPEG2_RIGHT, szTemp);

							SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right)>>TRACK_WIDTH_PRECISION));
							SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right-g_PicInfo.Squeeze_Width)>>TRACK_WIDTH_PRECISION);
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Width-g_PicInfo.Clip_Left-g_PicInfo.Clip_Right-g_PicInfo.Squeeze_Width);
							SetDlgItemText(hDlg, IDC_MPEG2_WIDTH, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_RIGHT_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Right>>3);
						AutoSize();
						UpdateSizes();
						break;

					case IDC_MPEG2_TOP_SLIDER:
						i = SendDlgItemMessage(hDlg, IDC_MPEG2_TOP_SLIDER, TBM_GETPOS, 0, 0)<<1;
						if (g_PicInfo.Coded_Picture_Height - i - g_PicInfo.Clip_Bottom - g_PicInfo.Squeeze_Height >= MIN_HEIGHT)
						{
							g_PicInfo.Clip_Top = i;
							sprintf(szTemp, "%d", g_PicInfo.Clip_Top);
							SetDlgItemText(hDlg, IDC_MPEG2_TOP, szTemp);
			
							SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom)>>3));
							SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height)>>3);
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height);
							SetDlgItemText(hDlg, IDC_MPEG2_HEIGHT, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_TOP_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Top>>1);
						break;

					case IDC_MPEG2_BOTTOM_SLIDER:
						i = SendDlgItemMessage(hDlg, IDC_MPEG2_BOTTOM_SLIDER, TBM_GETPOS, 0, 0)<<1;
						if (g_PicInfo.Coded_Picture_Height - i - g_PicInfo.Clip_Top - g_PicInfo.Squeeze_Height >= MIN_HEIGHT)
						{
							g_PicInfo.Clip_Bottom = i;
							sprintf(szTemp, "%d", g_PicInfo.Clip_Bottom);
							SetDlgItemText(hDlg, IDC_MPEG2_BOTTOM, szTemp);
							
							SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETRANGE, 0, MAKELPARAM(0, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom)>>3));
							SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height)>>3);
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height);
							SetDlgItemText(hDlg, IDC_MPEG2_HEIGHT, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_BOTTOM_SLIDER, TBM_SETPOS, 1, g_PicInfo.Clip_Bottom>>1);
						break;

					case IDC_MPEG2_WIDTH_SLIDER:
						i = g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right - (SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_GETPOS, 0, 0)<<TRACK_WIDTH_PRECISION);
						if (MIN_WIDTH + i + g_PicInfo.Clip_Left + g_PicInfo.Clip_Right <= g_PicInfo.Coded_Picture_Width)
						{
							g_PicInfo.Squeeze_Width = i;
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right - g_PicInfo.Squeeze_Width);
							SetDlgItemText(hDlg, IDC_MPEG2_WIDTH, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_WIDTH_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Width - g_PicInfo.Clip_Left - g_PicInfo.Clip_Right - g_PicInfo.Squeeze_Width)>>TRACK_WIDTH_PRECISION);
						AutoSize();
						UpdateSizes();
						break;

					case IDC_MPEG2_HEIGHT_SLIDER:
						i = g_PicInfo.Coded_Picture_Height - g_PicInfo.Clip_Top - g_PicInfo.Clip_Bottom - (SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_GETPOS, 0, 0)<<3);
						if (MIN_HEIGHT + i + g_PicInfo.Clip_Top + g_PicInfo.Clip_Bottom <= g_PicInfo.Coded_Picture_Height)
						{
							g_PicInfo.Squeeze_Height = i;
							sprintf(szTemp, "%d", g_PicInfo.Coded_Picture_Height - g_PicInfo.Clip_Top - g_PicInfo.Clip_Bottom - g_PicInfo.Squeeze_Height);
							SetDlgItemText(hDlg, IDC_MPEG2_HEIGHT, szTemp);
						}
						SendDlgItemMessage(hDlg, IDC_MPEG2_HEIGHT_SLIDER, TBM_SETPOS, 1, (g_PicInfo.Coded_Picture_Height-g_PicInfo.Clip_Top-g_PicInfo.Clip_Bottom-g_PicInfo.Squeeze_Height)>>3);
						break;
				} // end switch (GetWindowLong((HWND)lParam, GWL_ID))
				KillFilters();
				RefreshWindow();
			} // end case WM_HSCROLL:
			break;
		default:
			// nothing
			break;

	} // end switch(message)

	return FALSE;
}



void InputFileMPEG2::setOptions(InputFileOptions *p_ifo)
{
	DVD_T("setOptions");
	InputFileMPEG2Options *ifo = (InputFileMPEG2Options *)p_ifo;

	//	todo : fill g_globalXXX structs with received values

}

InputFileOptions *InputFileMPEG2::createOptions(const char *buf)
{
	DVD_T("createOptions");
	InputFileMPEG2Options *ifo = new InputFileMPEG2Options(this);

	if (!ifo) throw MyMemoryError();

	if (!ifo->read(buf))
	{
		delete ifo;
		return NULL;
	}

	return ifo;
}

InputFileOptions *InputFileMPEG2::promptForOptions(HWND hwnd)
{
	DVD_T("promptForOptions");
	// (!g_Flags.D2vExists)
	{
		InputFileMPEG2Options *ifo = new InputFileMPEG2Options(this);
		if (!ifo)
			throw MyMemoryError();
		DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_EXTOPENOPTS_MPEG2), hwnd, InputFileMPEG2Options::SetupDlgProc, (LPARAM)ifo);
		return ifo;
	}
	InitSources();
	g_Flags.ThreadMode = false;
	return 0;
}



#if 0
class DubSource {
private:
	void *	format;
	int		format_len;

protected:
	virtual BOOL _isKey(LONG lSample);

public:
	LONG lSampleFirst, lSampleLast;
	AVISTREAMINFO	streamInfo;

	DubSource();
	virtual ~DubSource();

	virtual BOOL init();
	int read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lBytesRead, LONG *lSamplesRead);
	virtual int _read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lBytesRead, LONG *lSamplesRead) = 0;

	void *getFormat() const { return format; }
	int getFormatLen() const { return format_len; }

	virtual bool isStreaming();

	BOOL isKey(LONG lSample);
	virtual LONG nearestKey(LONG lSample);
	virtual LONG prevKey(LONG lSample);
	virtual LONG nextKey(LONG lSample);

	virtual void streamBegin( bool fRealTime);
	virtual void streamEnd();

	LONG msToSamples(LONG lMs) const {
		return (LONG)(((__int64)lMs * streamInfo.dwRate + (__int64)500 * streamInfo.dwScale) / ((__int64)1000 * streamInfo.dwScale));
	}
	LONG samplesToMs(LONG lSamples) const {
		return (LONG)(
				(((__int64)lSamples * streamInfo.dwScale) * 1000 + streamInfo.dwRate/2) / streamInfo.dwRate
			);
	}

	// This is more accurate than AVIStreamSampleToSample(), which does a conversion to
	// milliseconds and back.

	static LONG samplesToSamples(const AVISTREAMINFO *dest, const AVISTREAMINFO *source, LONG lSamples) {
		__int64 divisor = (__int64)source->dwRate * dest->dwScale;

		return (LONG)((((__int64)lSamples * source->dwScale) * dest->dwRate + divisor/2)
				/ divisor);
	}

	LONG samplesToSamples(const DubSource *source, LONG lSamples) const {
		return samplesToSamples(&streamInfo, &source->streamInfo, lSamples);
	}
};


class AudioSource : public DubSource {
public:
	WAVEFORMATEX *getWaveFormat() {
		return (WAVEFORMATEX *)getFormat();
	}
};
#endif



class VideoSourceMPEG2 : public VideoSource
{
protected:
/*	HANDLE		hBufferObject;
	LONG		lBufferOffset;
	void		*lpvBuffer;
	BITMAPINFOHEADER *bmihDecompressedFormat;
	long		stream_desired_frame;
	long		stream_current_frame;

	void *AllocFrameBuffer(long size);
	void FreeFrameBuffer();

	VideoSource();
*/
	static char FrameType[4];
	bool m_BufferValid;
	void *decodeFrame(LONG frameNum);
	LONG m_LastSoughtFrame;
public:
/*	enum {
		IFMODE_NORMAL		=0,
		IFMODE_SWAP			=1,
		IFMODE_SPLIT1		=2,
		IFMODE_SPLIT2		=3,
		IFMODE_DISCARD1		=4,
		IFMODE_DISCARD2		=5,
	};
*/
	VideoSourceMPEG2(void) : m_BufferValid(true){bmihDecompressedFormat = 0;}
	virtual ~VideoSourceMPEG2(void) {lpvBuffer=0;}
/*
	BITMAPINFOHEADER *getImageFormat() const {
		return (BITMAPINFOHEADER *)getFormat();
	}

	void *getFrameBuffer() {
		return lpvBuffer;
	}

	HANDLE getFrameBufferObject() {
		return hBufferObject;
	}

	LONG getFrameBufferOffset() {
		return lBufferOffset;
	}
*/

	virtual LONG nearestKey(LONG lSample);
	virtual LONG prevKey(LONG lSample);
	virtual LONG nextKey(LONG lSample);
	virtual BOOL _isKey(LONG lSample);

	void SeekToIFrame(LONG frameNum);

	virtual bool setDecompressedFormat(int depth);
	virtual bool setDecompressedFormat(BITMAPINFOHEADER *pbih);


	int _read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lSamplesRead, LONG *lBytesRead);
	BOOL init(void);

/*
	BITMAPINFOHEADER *getDecompressedFormat() {
		return bmihDecompressedFormat;
	}
*/
	//virtual void streamSetDesiredFrame(long frame_num);
	//virtual long streamGetNextRequiredFrame(BOOL *is_preroll);
	//virtual int	streamGetRequiredCount(long *pSize);
	virtual void *streamGetFrame(void *inputBuffer, long data_len, BOOL is_key, BOOL is_preroll, long frame_num);

	//virtual void streamBegin(bool fRealTime);

	void invalidateFrameBuffer() {m_BufferValid = false;}
	BOOL isFrameBufferValid() {return m_BufferValid;}

	virtual void *getFrame(LONG frameNum);

	virtual char getFrameTypeChar(long lFrameNum) {return FrameType[g_DVDFrames[lFrameNum].type];}
/*
	enum eDropType {
		kDroppable		= 0,
		kDependant,
		kIndependent,
	};
*/
	virtual eDropType getDropType(long lFrameNum) {return kIndependent;};

	//virtual bool isKeyframeOnly();
	//virtual bool isType1();

	//virtual long streamToDisplayOrder(long sample_num) { return sample_num; }
	//virtual long displayToStreamOrder(long display_num) { return display_num; }

	virtual bool isDecodable(long sample_num) {return (sample_num >= lSampleFirst && sample_num < lSampleLast);}
};

char VideoSourceMPEG2::FrameType[4] = {0,'I','P','B'};

void VideoSourceMPEG2::SeekToIFrame(LONG frameNum)
{
	if (isDecodable(frameNum))
	{
		g_Flags.InitDecoder = true;
		g_Flags.ThreadMode = false;
		m_LastSoughtFrame = nearestKey(frameNum);
		__int64 lba = g_DVDFrames[m_LastSoughtFrame].data.lba;

		//	alpha 0.22 bugfix : adjust process.startloc
		//	if we are not in the first file
		int file_number = g_DVDFrames[m_LastSoughtFrame].data.file;
		__int64 file_offset = 0;
		for (int i=0;i< file_number;i++)
			file_offset += process.length[i];
		process.startloc = process.start = lba * BUFFER_SIZE + file_offset;

		process.startfile = 0; process.run = 0;
		while (process.startloc > process.length[process.startfile])
		{
			process.startloc -= process.length[process.startfile];
			process.run += process.length[process.startfile];
			process.startfile++;
		}

		process.end = process.total - BUFFER_SIZE;
		process.endfile = g_Flags.File_Limit - 1;
		process.endloc = (process.length[g_Flags.File_Limit-1]/BUFFER_SIZE-1)*BUFFER_SIZE;
		process.locate = LOCATE_SCROLL;

		MPEG2Dec(LPVOID(1));



		if ((process.file < process.rightfile) || (process.file==process.rightfile && process.lba<process.rightlba))
		{
			process.leftfile = process.file;
			process.leftlba = process.lba;

			process.run = 0;
			for (int i=0; i<process.leftfile; i++)
				process.run += process.length[i];

		}
		process.locate = LOCATE_RIP;
		MPEG2Dec(LPVOID(2));
	}
}

void * VideoSourceMPEG2::decodeFrame(LONG frameNum)
{
	static LONG l_Frame =0;
	if (l_Frame != frameNum)
	{
		try
		{
			if (frameNum == l_Frame+1 && l_Frame > 0)
			// particular case of going to frame # 1 when frame # 0 has never been shown
			{
				process.locate = LOCATE_RIP;
				g_Flags.InitDecoder = false;
				MPEG2Dec(LPVOID(frameNum-m_LastSoughtFrame+2));
				//	+2 and not +1 because of a > that should be a >= in MPEG2Dec
			}
			else
			{
				SeekToIFrame(frameNum);
				process.locate = LOCATE_RIP;
				//MPEG2Dec(LPVOID(0));
				//g_Flags.InitDecoder = false;
				if (frameNum > m_LastSoughtFrame)
					MPEG2Dec(LPVOID(frameNum-m_LastSoughtFrame+2));
				g_Flags.InitDecoder = true;

			}
		}
		catch(DVD_EOF&)
		{}
		//	EOF may happen a few frames before the actual end of file
		//	it'll be fixed later
		l_Frame = frameNum;
	}
	return (lpvBuffer = GetFilteredOutput());
}

void * VideoSourceMPEG2::getFrame(LONG frameNum)
{
	g_Flags.Store_Flag = STORE_RGB24;
	return decodeFrame(frameNum);
}
void * VideoSourceMPEG2::streamGetFrame(void *inputBuffer, long data_len, BOOL is_key, BOOL is_preroll, long frame_num)
{
	// we'll optimize I/O buffering later
	return decodeFrame(frame_num);
}

BOOL VideoSourceMPEG2::_isKey(LONG lSample)
{
	return(g_DVDFrames[lSample].type == I_TYPE);
}

LONG VideoSourceMPEG2::nearestKey(LONG lSample)
{
	return (_isKey(lSample)) ? lSample : prevKey(lSample);
}
LONG VideoSourceMPEG2::prevKey(LONG lSample)
{
	while (lSample > lSampleFirst)
	{
		lSample--;
		if (_isKey(lSample))
			return lSample;
	}
	return -1;
}
LONG VideoSourceMPEG2::nextKey(LONG lSample)
{
	while (lSample < lSampleLast-1)
	{
		lSample++;
		if (_isKey(lSample))
			return lSample;
	}
	return -1;
}

BOOL VideoSourceMPEG2::init()
{
	BITMAPINFOHEADER *bmih;


	g_PicInfo.frame_count = g_DVDFrames.size()-1; 
	lSampleFirst = 0;
	lSampleLast = g_PicInfo.frame_count; 
	if (!(bmih = (BITMAPINFOHEADER *)allocFormat(sizeof(BITMAPINFOHEADER))))
		throw MyMemoryError();
	if (!bmihDecompressedFormat)
		if (!(bmihDecompressedFormat = (BITMAPINFOHEADER *)allocmem(getFormatLen())))
			throw MyMemoryError();

	bmih->biSize		= sizeof(BITMAPINFOHEADER);
	bmih->biWidth		= g_PicInfo.Resize_Width;
	bmih->biHeight		= g_PicInfo.Resize_Height;
	bmih->biPlanes		= 1;
	bmih->biBitCount	= 32;
	bmih->biCompression	= 0xffffffff;
	bmih->biSizeImage	= 0;
	bmih->biXPelsPerMeter	= 0;
	bmih->biYPelsPerMeter	= 0;
	bmih->biClrUsed		= 0;
	bmih->biClrImportant	= 0;

	streamInfo.fccType					= streamtypeVIDEO;
	streamInfo.fccHandler				= 0;
	streamInfo.dwFlags					= 0;
	streamInfo.wPriority				= 0;
	streamInfo.wLanguage				= 0;
	streamInfo.dwScale					= 1000;
	streamInfo.dwRate					= g_DVDGlobals.Frame_Rate*1000;
	streamInfo.dwStart					= 0;
	streamInfo.dwLength					= g_PicInfo.frame_count;
	streamInfo.dwInitialFrames			= 0;
	streamInfo.dwSuggestedBufferSize	= 0;
	streamInfo.dwQuality				= 0xffffffffL;
	streamInfo.dwSampleSize				= 0;
	streamInfo.rcFrame.left				= 0;
	streamInfo.rcFrame.top				= 0;
	streamInfo.rcFrame.right			= g_PicInfo.Resize_Width;
	streamInfo.rcFrame.bottom			= g_PicInfo.Resize_Height;
	m_LastSoughtFrame = 0;
	lpvBuffer = GetFilteredOutput();
	return TRUE;
}


int VideoSourceMPEG2::_read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lBytesRead, LONG *lSamplesRead)
{
	return AVIERR_OK;
}

bool VideoSourceMPEG2::setDecompressedFormat(int depth)
{
	if (depth != 32 && depth != 24 && depth != 16)
		return false;
	return VideoSource::setDecompressedFormat(depth);
}

bool VideoSourceMPEG2::setDecompressedFormat(BITMAPINFOHEADER *pbih)
{
	if (pbih->biCompression == BI_RGB)
	{
		g_Flags.Store_Flag = STORE_RGB24;
		return setDecompressedFormat(pbih->biBitCount);
	}

	// Sanity-check format.

	BITMAPINFOHEADER *pbihInput = getImageFormat();

	if (pbih->biWidth != pbihInput->biWidth
		|| pbih->biHeight != pbihInput->biHeight
		|| pbih->biPlanes != 1)

		return false;

	if (pbih->biBitCount == 16 && (isEqualFOURCC(pbih->biCompression, '2YUY')))
	{
		// Looks good!
		g_Flags.Store_Flag = STORE_YUY2;
		memcpy(bmihDecompressedFormat, pbih, sizeof(BITMAPINFOHEADER));
		invalidateFrameBuffer();
		return true;
	}
	if (cpu.mmx &&
		(
			(g_PicInfo.Resize_Function == FILTER_BILINEAR && (g_PicInfo.Resize_Width & 0xF) == 0 ) // 16 multiple for mmx chroma resize
			||
			(g_PicInfo.Resize_Function == FILTER_PRECISE_BICUBIC || g_PicInfo.Resize_Function == FILTER_NEUTRAL_BICUBIC)
					&& (g_PicInfo.Resize_Width & 7) == 0
		)
		&&
		g_DVDGlobals.chroma_format==CHROMA420 && 
		pbih->biBitCount == 12 &&
		(isEqualFOURCC(pbih->biCompression, '21VY')))
	{
		// Looks good!
		if (!(g_PicInfo.Squeeze_Width))
		{
			//	only without h-resize
			g_Flags.Store_Flag = STORE_YUV;
			memcpy(bmihDecompressedFormat, pbih, sizeof(BITMAPINFOHEADER));
			invalidateFrameBuffer();
			return true;
		}
	}
	// Damn.
	return false;
}

static void Recovery(void)
{
	int i;

	if (g_Flags.Check_Flag)
	{
		for (i=0; i<3; i++)
		{
			aligned_free(g_PicInfo.backward_reference_frame[i]);
			aligned_free(g_PicInfo.forward_reference_frame[i]);
			aligned_free(g_PicInfo.auxframe[i]);
		}

		aligned_free(g_PicInfo.u422);
		aligned_free(g_PicInfo.v422);
		aligned_free(g_PicInfo.u444);
		aligned_free(g_PicInfo.v444);
		aligned_free(g_PicInfo.rgb24);
		aligned_free(g_PicInfo.rgb24_temp);
		aligned_free(g_PicInfo.rgb24_temp2);
		aligned_free(g_PicInfo.yuy2);
		aligned_free(g_PicInfo.yuy2_temp);
		//aligned_free(g_PicInfo.lum);
	}

	g_Flags.Check_Flag = false;


	g_PicInfo.LumGain = 128; g_PicInfo.LumOffset = 0;

	g_PicInfo.Clip_Left = g_PicInfo.Clip_Right = g_PicInfo.Clip_Top = g_PicInfo.Clip_Bottom = g_PicInfo.Squeeze_Width = g_PicInfo.Squeeze_Height = 0;
	g_PicInfo.Aspect_Change = ASPECT_UNKNOWN;

	g_Ac3Context.PreScale_Ratio = 1.0;
	g_Ac3Context2.PreScale_Ratio = 1.0;


	if (g_Flags.File_Limit)
	{
		ZeroMemory(&process, sizeof(PROCESS));
		process.trackright = TRACK_PITCH;

		g_Flags.SystemStream_Flag = false;
		g_Flags.Display_Flag = true;

		for (i=0; i<g_Flags.File_Limit; i++)
		{
			process.length[i] = _filelengthi64(g_DVDGlobals.Infile[i]);
			process.total += process.length[i];
		}
	}

//	ResizeWindow(INIT_WIDTH, INIT_HEIGHT);
}


InputFileMPEG2::InputFileMPEG2()
{
	audioSrc = 0;
	audio2Src = 0;
	videoSrc = 0;
	init =0;
	g_DVDFrames.clear();
}
InputFileMPEG2::~InputFileMPEG2()
{
	for(int i = 0; i < g_Flags.File_Limit; i++)
		_close(g_DVDGlobals.Infile[g_Flags.File_Limit]);
	g_Flags.File_Limit = 0;
	g_DVDFrames.clear();
	delete audioSrc;
	delete audio2Src;
	delete videoSrc;
}
void InputFileMPEG2::Init(char *szFile)
{
	DVD_T("InputFileMPEG2::init " << szFile);
	if (!init)
	{
		init++;
		l_File.Init(szFile);
		strcpy(g_GuiGlobals.szOutput,l_File.GetName().c_str());
		// open d2v file if it exists
		string path = l_File.GetFiles()[0].substr(0,l_File.GetFiles()[0].rfind("\\")+1);
		strcpy(g_GuiGlobals.szPath,path.c_str());
		path = l_File.GetFiles()[0].substr(0,l_File.GetFiles()[0].rfind("."));
		path += ".d2v";

		//g_PicInfo.frame_count = l_File.GetFrames();
		//useless and not reliable

		for (int j=0; j<MAX_FILE_NUMBER; j++)
			g_DVDGlobals.Infilename[j] = (char*)aligned_malloc(_MAX_PATH, 4);
		for(vector<string>::const_iterator i = l_File.GetFiles().begin(); i != l_File.GetFiles().end(); i++)
		{
			strcpy(g_DVDGlobals.Infilename[g_Flags.File_Limit],(*i).c_str());
			g_DVDGlobals.Infile[g_Flags.File_Limit] = _open((*i).c_str(), _O_RDONLY | _O_BINARY | _O_SEQUENTIAL);
			if (g_DVDGlobals.Infile[g_Flags.File_Limit] == -1)
			{
				throw MyError("Could not open file : %s",g_DVDGlobals.Infilename[g_Flags.File_Limit]);
			}
			g_Flags.File_Limit++;
		}

		memcopy_init();
		CheckINI();
		g_Ac3Context.PreScale_Ratio = 1.0;
		g_Ac3Context2.PreScale_Ratio = 1.0;
		g_PicInfo.LumGain = 128;

		g_GuiGlobals.hDC = 0; //GetDC(hWnd);
		g_GuiGlobals.Preview = 0;


		for (j=0; j<8; j++)
		{
			g_DVDGlobals.block[j] = (short *)aligned_malloc(sizeof(short)*64, 128);
		}

		g_DVDGlobals.fTempArray = (void *)aligned_malloc(sizeof(float)*128, 128);

		Initialize_REF_IDCT();
		Initialize_FPU_IDCT();


		//	init callbacks
		//	I should get rid of these ...
		g_InfoFunctions.Info = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.VobId = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.CellId = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.AspectRatio = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FrameRate = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.AudioType = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.TimeStamp = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.VideoType = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.Elapsed = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.Remain = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FileSize = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FileAvgBitRate = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FileKFRatio = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FileAvgRes = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FileEstSize = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.AvgFPS = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.FrameType = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.CodedNumber = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.File = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.Progress = cb_empty_DVD2aviCallBackFunction2;
		g_InfoFunctions.FPS = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.PlaybackNumber = cb_empty_DVD2aviCallBackFunction;
		g_InfoFunctions.TrackProgress = cb_empty_DVD2aviCallBackFunction2;
		g_InfoFunctions.TitleInfo = cb_empty_DVD2aviCallBackFunction;

		g_OutFunctions.AutoSize = AutoSize;
		g_OutFunctions.ThreadKill = ::ThreadKill;
		g_OutFunctions.D2VOutputImage = cb_D2VOutputImage;

		SetOutput(OUTPUT_NOWHERE);
		Recovery();
		if (LoadD2v())
			g_Flags.D2vExists = true;
		if (g_Flags.File_Limit)
		{
			process.rightfile = g_Flags.File_Limit-1;
			process.rightlba = (int)(process.length[g_Flags.File_Limit-1]/BUFFER_SIZE);

			process.end = process.total - BUFFER_SIZE;
			process.endfile = g_Flags.File_Limit - 1;
			process.endloc = (process.length[g_Flags.File_Limit-1]/BUFFER_SIZE - 1)*BUFFER_SIZE;

			KillFilters();
			process.locate = LOCATE_INIT;
			g_Flags.ThreadMode = false;
			//g_Flags.ThreadMode = true;
			MPEG2Dec(0);
			//hThread = CreateThread(NULL, 0, MPEG2Dec, 0, 0, &threadId);
			//WaitForSingleObject(hThread, INFINITE);
			g_Flags.ThreadMode = true;
		}
		if (g_Flags.D2vExists)
			InitSources();
	}
	else
	{
		if (g_DVDFrames.size() == 0)
		{
			SaveD2v();
			WaitForSingleObject(hThread, INFINITE);
			InitSources();
		}
		else if (!videoSrc)
			InitSources();
	}
}
void InputFileMPEG2::InitSources(void)
{
	videoSrc = new VideoSourceMPEG2();
	videoSrc->init();
	//g_Flags.Store_Flag = STORE_YUY2;
	((VideoSourceMPEG2*)videoSrc)->streamGetFrame(0,0,0,0,g_PicInfo.frame_count/2);
	audioSrc = 0;
	audio2Src = 0;
	KillFilters();
}
void InputFileMPEG2::ThreadKill()
{
	KillFilters();

	if ((g_Flags.AVI_Flag || g_Flags.D2V_Flag)
		&& g_Flags.Track_Flag!=TRACK_NONE
		&& 
		((g_DVDGlobals.ac3[g_Flags.Track_Flag].rip && g_Ac3Context.AC3_Flag==AUDIO_DECODE) || pcm.rip))
	{
		Normalize(NULL, 44, pcm.filename, pcm.file, 44, pcm.size,g_Flags.Statistics_Flag,g_Ac3Context.Normalization_Flag);
		EndWAV(pcm.file, pcm.size);

		if ((g_Flags.AVI_Flag || g_Flags.D2V_Flag)
			&& g_Flags.Track_Flag2!=TRACK_NONE
			&& 
			((g_DVDGlobals.ac3[g_Flags.Track_Flag2].rip && g_Ac3Context2.AC3_Flag==AUDIO_DECODE) || pcm2.rip))
		{
			Normalize(NULL, 44, pcm2.filename, pcm2.file, 44, pcm2.size,g_Flags.Statistics_Flag,g_Ac3Context2.Normalization_Flag);
			EndWAV(pcm2.file, pcm2.size);
		}
	}

	if (process.locate==LOCATE_INIT || process.locate==LOCATE_RIP)
	{
		if (g_Flags.D2V_Flag)
		{
			if (g_Flags.Stop_Flag)
				fprintf(g_GuiGlobals.D2VFile, " 10\n\nINTERRUPTED");
			else
				fprintf(g_GuiGlobals.D2VFile, " 9\n\nFINISHED");
			g_DVDFrames.push_back(DVDFrame(P_TYPE,0));//see comment in LoadD2v
			SetOutput(OUTPUT_SCREEN);
			g_GuiGlobals.D2VFile = 0;
		//	InitSources();
		//	g_Flags.ThreadMode = false;
		}

		_fcloseall();// i'm suspicious about the need for this call...
		//for(int i = 0;i < g_Flags.File_Limit; i++)
		//	_close(g_DVDGlobals.Infile[i]);
/*
		if (g_Ac3Context.Decision_Flag)
		{
			if (g_DVDGlobals.Sound_Max > 1)
			{
				g_Ac3Context.PreScale_Ratio = 327.68 * g_Flags.Norm_Ratio / g_DVDGlobals.Sound_Max;

				if (g_Ac3Context.PreScale_Ratio > 1.0 && g_Ac3Context.PreScale_Ratio < 1.01)
					g_Ac3Context.PreScale_Ratio = 1.0;

				g_Ac3Context.Normalization_Flag = false;
			}
		}
*/
		g_Flags.AVI_Flag = false;
		g_Flags.D2V_Flag = false;
		//g_Ac3Context.Decision_Flag = false;
		g_Flags.Display_Flag = false;
		g_Flags.Play_Flag = true;
		//SetOutput(OUTPUT_SCREEN);
	}
	bool exitThread = g_Flags.ThreadMode;
	if (g_Flags.Quit && g_GuiGlobals.hWnd)
	{
		EndDialog(g_GuiGlobals.hWnd,1);
	}
	if (exitThread)
		ExitThread(0);
}
void InputFileMPEG2::SaveD2v(void)
{
	//	translate GUI track number into real track number
	if (g_Flags.Track_Flag !=TRACK_NONE)
		g_Flags.Track_Flag = l_File.GetAudioTracks()[g_Flags.Track_Flag];
	if (g_Flags.Track_Flag2 !=TRACK_NONE)
		g_Flags.Track_Flag2 = l_File.GetAudioTracks()[g_Flags.Track_Flag2];

	if (!g_Flags.D2vExists)
	{
		DVD_T("SaveD2v");
		SetOutput(OUTPUT_D2VFILE);
		string path = l_File.GetFiles()[0].substr(0,l_File.GetFiles()[0].rfind("."));
		path += ".d2v";

		g_GuiGlobals.D2VFile = fopen(path.c_str(), "w+");

		KillFilters();
		process.locate = LOCATE_RIP;
		g_Flags.ThreadMode = true;
		g_DVDFrames.clear();
		g_Flags.D2vExists = true;

		//if (hThread && (WaitForSingleObject(hThread, INFINITE)==WAIT_OBJECT_0))
			hThread = CreateThread(NULL, 0, MPEG2Dec, 0, 0, &threadId);
	}
	else
	{
		g_Flags.ThreadMode = false;
		ThreadKill();
	}
}
#define LOADD2V_EXIT(a) {int scan_result = a if (scan_result == 0 || scan_result == EOF) return false;}

bool InputFileMPEG2::LoadD2v(void)//	true if already exists
{

	DVD_T("LoadD2v");
	g_GuiGlobals.D2VFile = 0;
	string path = l_File.GetFiles()[0].substr(0,l_File.GetFiles()[0].rfind("."));
	path += ".d2v";
	g_GuiGlobals.D2VFile = fopen(path.c_str(), "r");
	if (g_GuiGlobals.D2VFile == 0)
		return false;


	int i=0,j=0;
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "DVD2AVIProjectFile\n%d\n", &i);)

	while (i)
	{
		fscanf(g_GuiGlobals.D2VFile, "%d ", &j);
		fgets(szTemp, j+1, g_GuiGlobals.D2VFile);
		i--;
	}

	//Recovery();

	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "\nStream_Type=%d,%X,%X\n", &g_Flags.SystemStream_Flag, &i, &j);)

	if ( g_Flags.SystemStream_Flag == 2 )  // if MPEG-2 transport stream is indicated...
		LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "MPEG2_Transport_PID=%X,%X\n", 
			&g_Flags.MPEG2_Transport_VideoPID, &g_Flags.MPEG2_Transport_AudioPID );)
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "iDCT_Algorithm=%d\n", &g_Flags.iDCT_Flag);)

	// trb 4/2002 - readjust for maybe different CPU type
	if (g_Flags.iDCT_Flag == IDCT_MMX || g_Flags.iDCT_Flag == IDCT_SSEMMX ||g_Flags.iDCT_Flag == IDCT_SSE2MMX)
	{

		if (cpu.sse2mmx)
			g_Flags.iDCT_Flag = IDCT_SSE2MMX;
		else if (cpu.ssemmx)
			g_Flags.iDCT_Flag = IDCT_SSEMMX;
		else g_Flags.iDCT_Flag = IDCT_MMX;
	}

	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "YUVRGB_Scale=%d\n", &g_Flags.Scale_Flag);)
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Luminance=%d,%d\n", &g_PicInfo.LumGain, &g_PicInfo.LumOffset);)

	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Picture_Size=%d,%d,%d,%d,%d,%d\n", &g_PicInfo.Clip_Top, &g_PicInfo.Clip_Bottom, 
		&g_PicInfo.Clip_Left, &g_PicInfo.Clip_Right, &g_PicInfo.Squeeze_Width, &g_PicInfo.Squeeze_Height);)

	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Field_Operation=%d\n", &g_Flags.FO_Flag);)
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Frame_Rate=%d\n", &i);)
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Aspect_Change=%d\n", &g_PicInfo.Aspect_Change);)
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "FilterType=%d\n", &g_PicInfo.Resize_Function);)
	switch(g_PicInfo.Resize_Function)
	{
	case FILTER_BILINEAR:
	case FILTER_SOFT_BICUBIC:
	case FILTER_PRECISE_BICUBIC:
	case FILTER_NEUTRAL_BICUBIC:
	case FILTER_CUSTOM_BICUBIC:
		break;
	default:
		g_PicInfo.Resize_Function = FILTER_BILINEAR;
	}
	LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "Location=%d,%d,%d,%X\n\n", &i, &i, &i, &i);)

	g_DVDFrames.clear();
	int type;
	DVDFrame l_Frame;
	while (fscanf(g_GuiGlobals.D2VFile, "%d", &type) && type<9)
	{
		if (type==7)	// I frame
		{
			long toto;
			LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "%d %X %d", &l_Frame.data.trf, &toto, &l_Frame.data.file);)
			l_Frame.data.lba = toto;
			DVD_T("loading i-frame " << g_DVDFrames.size() << " with lba :" << toto);
			l_Frame.type = I_TYPE;
		}
		else if (type==6)	// B frame
		{
			LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "%d", &l_Frame.data.trf);)
			l_Frame.type = B_TYPE;
		}
		else if (type==5)	// P frame
		{
			LOADD2V_EXIT(fscanf(g_GuiGlobals.D2VFile, "%d", &l_Frame.data.trf);)
			l_Frame.type = P_TYPE;
		}
		else
			throw MyError("Unsupported frame type in d2v file");
		g_DVDFrames.push_back(l_Frame);
	}
	g_DVDFrames.push_back(DVDFrame(P_TYPE,0));
	//	there are say n frames, from 0 to n-1 but a last frame is added,
	//  because VirtualDub asks for information of the frame number n (the n+1 th).
	if (type == 10) // trying to load an interrupted file ?
	{
		g_DVDFrames.clear();
		return false;
	}
	return true;
}

void InputFileMPEG2Options::Play(void)
{

	g_Flags.D2V_Flag = false;
	g_Flags.Display_Flag = true;

	((VideoSourceMPEG2*)(m_TheMovie->videoSrc))->SeekToIFrame(52);

	//KillFilters();
	process.locate = LOCATE_RIP;
	//WaitForSingleObject(hThread, INFINITE);
//	g_Flags.ThreadMode = true;
	g_Flags.Play_Flag = true;
//	hThread = CreateThread(NULL, 0, MPEG2Dec, LPVOID(200), 0, &threadId);
	MPEG2Dec(LPVOID(4));
}

InputFile *CreateInputFileMPEG2()
{
	return InputFileMPEG2::GetNewInstance();
}


