//	VirtualDub - Video processing and capture application
//	Copyright (C) 1998-2001 Avery Lee
//
//	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.

// Added to disable std warnings. Tobias Minich 11/2002
#pragma warning(disable : 4786)

#include "VirtualDub.h"

#include <stdio.h>
#include <process.h>
#include <crtdbg.h>

#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <mmsystem.h>
#include <vfw.h>
#include <shellapi.h>
#include <shlobj.h>

#include "resource.h"
#include "convert.h"
#include "optdlg.h"
#include "prefs.h"
#include "filtdlg.h"
#include "filters.h"
#include "audio.h"
#include "oshelper.h"
#include "gui.h"
#include "ClippingControl.h"
#include "PositionControl.h"
#include "HexViewer.h"
// *******************************************************************
// *** REDRAW+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
#include "avs\AVSViewer.h"
#include "avs\cmdline.h"
#include "avs\genscript.h"
#include "avs\template.h"
#include <cderr.h>
#include "virtualdubmod_messages.h"
// END ***************************************************************
#include "cpuaccel.h"
#include "capture.h"
#include "auxdlg.h"
#include "dub.h"
#include "DubStatus.h"
#include "mpeg.h"
#include "ddrawsup.h"
#include "server.h"
#include "script.h"
#include "command.h"
#include "job.h"
#include "autodetect.h"
#include "crash.h"
#include "misc.h"

#include "AudioSource.h"
#include "VideoSource.h"
#include "AVIStripeSystem.h"
#include "AVIOutput.h"
#include "AVIOutputStriped.h"
#include "AVIOutputPreview.h"
#include "AVIOutputImages.h"
#include "AVIPipe.h"
#include "AsyncBlitter.h"
#include "InputFile.h"
#include "MRUList.h"
#include "SceneDetector.h"
#include "Error.h"
#include "FrameSubset.h"
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
#include "OGM/OGMDub.h"
#include "OGM/OGMOutput.h"
#include "OGM/OGMReadHandler.h"
#include "OGM/OGMDubStatus.h"
//  Cyrius 08/11/2002
#include "AVIOutputWAV.h"
#include "OGM/AudioOutput.h"
#include "OGM/SRTOutput.h"
//	Pulco 29/10/2002
#include <fstream>
using namespace std;
//END ==========================//
//==============================//
//=====  MODIFICATION PNG  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
#include "png/png.h"
//END ==========================//

///////////////////////////////////////////////////////////////////////////

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//=====  MODIFICATION MOD  =====//
//=====   -= Belgabor =-   =====//
//BEGIN ========================//
//#define MRU_LIST_POSITION		(22)
//#define MRU_LIST_POSITION		(23) //Added "Save as OGM" (Cyrius)
//#define MRU_LIST_POSITION		(24) //Added "Open via AviSynth" (Belgabor)
#define MRU_LIST_POSITION		(25) //Added "Demux audio" (Cyrius)
//END ==========================//

///////////////////////////////////////////////////////////////////////////

extern bool g_fJobMode;
extern bool g_fJobAborted;

HINSTANCE	g_hInst;
HWND		g_hWnd =NULL;
HMENU		hMenuNormal, hMenuDub, g_hmenuDisplay;
HACCEL		g_hAccelMain;
// *******************************************************************
// *** AVS Modification											   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
HACCEL		g_hAccelAVS;
StringList	gFileList;
extern AVSPreferences	g_AVSprefs;
extern TemplateInfoList gTemplateList;
extern HMODULE g_hScintilla;
//extern HMODULE g_hAVSLexer;
// END ***************************************************************


HDC					hDCWindow				= NULL;
HDRAWDIB			hDDWindow				= NULL;
HDRAWDIB			hDDWindow2				= NULL;

MRUList				*mru_list				= NULL;

SceneDetector		*g_sceneDetector		= NULL;

int					g_sceneShuttleMode		= 0;
int					g_sceneShuttleAdvance	= 0;
int					g_sceneShuttleCounter	= 0;

bool				g_fDropFrames			= false;
bool				g_fSwapPanes			= false;

IDubStatusHandler	*g_dubStatus			= NULL;

RECT	g_rInputFrame;
RECT	g_rOutputFrame;
int		g_iInputFrameShift = 0;
int		g_iOutputFrameShift = 0;

char g_szInputAVIFile[MAX_PATH];
char g_szInputAVIFileTitle[MAX_PATH];
// *******************************************************************
// *** Refresh Modification										   ***
// *** Tobias Minich, August 2002								   ***
// BEGIN *************************************************************
char g_szInputAVIFileLastWorking[MAX_PATH] = "";
// END ***************************************************************
char g_szInputWAVFile[MAX_PATH];
char g_szInputMP3File[MAX_PATH];
char g_szInputAC3File[MAX_PATH];
char g_szInputOGGFile[MAX_PATH];
char g_szInput2WAVFile[MAX_PATH];
char g_szInput2MP3File[MAX_PATH];
char g_szInput2AC3File[MAX_PATH];
char g_szInput2OGGFile[MAX_PATH];
char g_szFile[MAX_PATH];
char g_msgBuf[128];
char g_szFileDescription[256];

extern const char g_szError[]="VirtualDub Error";
extern const char g_szWarning[]="VirtualDub Warning";
extern const char g_szOutOfMemory[]="Out of memory";
static char szWAVOutput[]="WAVOutput";

///////////////////////////

extern bool Init(HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow);
extern void Deinit();
//*************************************************************************************************
//*** VirtualDubMod Modification by Tobias Minich 11/2002                                       ***
//begin *******************************************************************************************
//extern bool CoachCheckSaveOp(HWND hwndParent, DubOptions *dopt, COMPVARS *vcomp, WAVEFORMATEX *wfex, List *filter_list);
extern bool CoachCheckSaveOp(HWND hwndParent, DubOptions *dopt, COMPVARS *vcomp, WAVEFORMATEX *wfex, List *filter_list, char *filename);
//end *********************************************************************************************
extern void ChooseCompressor(HWND hwndParent, COMPVARS *lpCompVars, BITMAPINFOHEADER *bihInput);
extern void FreeCompressor(COMPVARS *pCompVars);
extern WAVEFORMATEX *AudioChooseCompressor(HWND hwndParent, WAVEFORMATEX *, WAVEFORMATEX *);
extern void DisplayLicense(HWND hwndParent);

LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam);
void SceneShuttleStop();
void SceneShuttleStep();

void SetTitleByFile(HWND hWnd);
void RecalcFrameSizes();

void SetAudioSource();
void OpenAVI(int index, bool extended_opt);
void AppendAVI();
void PreviewAVI(HWND, DubOptions *, int iPriority=0, bool fProp=false);
void SaveAVI(HWND, bool);
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
// Handle MP3 VBR as Nandub ?
bool nandub_compatibility = false;

long JumpToLastKeyBeforePos( __int64 pos );

void SaveOGM(HWND, bool);
ogm_stream *ogm_streams = NULL;
comment_list *video_comments = NULL;
comment_list *audio_comments = NULL;
comment_list *audio2_comments = NULL;
ogm_stream *GetOGMStream(int index);
BOOL APIENTRY OGMInfoDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
BOOL APIENTRY OGMInputsDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
static void Input_GetDispInfo(NMLVDISPINFO *nldi);
BOOL OpenOGMInputFile(char *fn, int type, HWND hwndItem, long offset=0);
int AddOGMStream(OGMAudioSource *source, int type, comment_list *comments, long offset=0);
void GetInputComments(void);
void MoveMainAudio(void);
BOOL AddInputAudioStream(char *szFile, int numStream, long offset=0);
BOOL AddInputTextStream(char *szFile, int numStream, long offset=0);
void AddInputFileStreams(char *szFile);
void RemoveInputStreams(void);
comment_list *GetOGMComments(comment_list *comments, int index);
BOOL APIENTRY OGMCommentsDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
static void Comments_GetDispInfo(NMLVDISPINFO *nldi, comment_list **p_comments);
void DeleteComments(void);
void AddVideoComment(char *tag, char *comment);
void AddAudioComment(char *tag, char *comment);
void AddAudio2Comment(char *tag, char *comment);
void AddComment(int index, int type, char *tag, char *comment);
void AddComment(comment_list **comments, char *tag, char *comment);

struct chapter_mesh {
	char         *info;
	chapter_mesh *next;

	void* operator new(unsigned int nAlloc) {
		chapter_mesh* new_mesh = (chapter_mesh*)malloc(nAlloc);
		if(new_mesh) {
			new_mesh->info = NULL;
			new_mesh->next = NULL;
		}
		return new_mesh;
	}

	void operator delete(void* old_mesh) {
		chapter_mesh *mesh = (chapter_mesh *)old_mesh;
		if(mesh->info)
			delete mesh->info;
		free(old_mesh);
	}
};

void add_chapter(chapter_mesh **pmesh, char *info);
BOOL import_chapters(char *file, chapter_mesh **chapter_names, chapter_mesh **times);
void delete_chapters(chapter_mesh **pmesh);

void InitDubOGM(char *szFile, BOOL fAudioOnly, DubOptions *quick_options, int iPriority=0, bool fPropagateErrors = false, long lSpillThreshold=0, long lSpillFrameThreshold=0);
//END ==========================//
//==============================//
//=====  MODIFICATION PNG  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
//static bool save_to_png = false;// pulco 04/11/2002 : merging with 1.4.11
//END ==========================//
void SaveSegmentedAVI(HWND);
void HandleDragDrop(HDROP hdrop);
void SaveStripedAVI(HWND);
void SaveStripeMaster(HWND);
void CPUTest();
void InitDubAVI(char *szFile, BOOL fAudioOnly, DubOptions *quick_options, int iPriority=0, bool fPropagateErrors = false, long lSpillThreshold=0, long lSpillFrameThreshold=0);
void OpenImageSeq(HWND hwnd);
void SaveImageSeq(HWND);
void SaveWAV(HWND,BOOL);
void DoDelete();
void DoMaskChange(bool bMask);
void SaveConfiguration(HWND);
void CreateExtractSparseAVI(HWND hwndParent, bool bExtract);

// *******************************************************************
// *** REDRAW+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
void OpenAVS();
// END ***************************************************************

void OpenWAV(bool secondary=false);
void OpenMP3(bool secondary=false);
void OpenAC3(bool secondary=false);
void OpenOGG(bool secondary=false);

BOOL APIENTRY StatusDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);
BOOL APIENTRY AVIInfoDlgProc( HWND hDlg, UINT message, UINT wParam, LONG lParam);

//
//  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
//
//  PURPOSE: Entry point for the application.
//
//  COMMENTS:
//
//	This function initializes the application and processes the
//	message loop.
//

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow )
{
	MSG msg;

	Init(hInstance, lpCmdLine, nCmdShow);

	// Load a file on the command line.

	VDCHECKPOINT;

	if (*g_szFile)
		try {
			char szFileTmp[MAX_PATH];
			char *t = NULL;

			strcpy(g_szInputAVIFile, g_szFile);
			GetFullPathName(g_szInputAVIFile, sizeof szFileTmp, szFileTmp, &t);
			if (t) {
				strcpy(g_szInputAVIFileTitle, t);

				OpenAVI(g_szFile, 0, false, false, NULL);
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
				// Add input streams automatically here
				// so that they won't be when using Job Control :)
				AddInputFileStreams(g_szFile);
//END ==========================//
				SetTitleByFile(g_hWnd);
				RecalcFrameSizes();
				InvalidateRect(g_hWnd, NULL, TRUE);
			} else
				g_szFile[0] = g_szInputAVIFile[0] = 0;
		} catch(const MyError& e) {
			e.post(g_hWnd, g_szError);
		}

    // Acquire and dispatch messages until a WM_QUIT message is received.
	VDCHECKPOINT;

    while (GetMessage(&msg,NULL,0,0)) {

		if (guiCheckDialogs(&msg)) continue;

		if (!g_dubber) {
			HWND hwnd = msg.hwnd, hwndParent;

			while(hwndParent = GetParent(hwnd))
				hwnd = hwndParent;

// *******************************************************************
// *** AVS Modification 										   ***
// *** Tobias Minich, August 2002								   ***
// BEGIN *************************************************************

			//if (hwnd == g_hWnd && TranslateAccelerator(g_hWnd, g_hAccelMain, &msg)) continue;
			if (hwnd == g_hWnd) {
				if (TranslateAccelerator(g_hWnd, g_hAccelMain, &msg)) continue;
			} else {
				if (TranslateAccelerator(hwnd, g_hAccelAVS, &msg)) continue;
			}

// END ***************************************************************

		}

		TranslateMessage(&msg);
		DispatchMessage(&msg);

		if (!g_dubber && inputVideoAVI && g_sceneShuttleMode) {
			if (!g_sceneDetector)
				if (!(g_sceneDetector = new SceneDetector(inputVideoAVI->getImageFormat()->biWidth, inputVideoAVI->getImageFormat()->biHeight)))
					continue;

			g_sceneDetector->SetThresholds(g_prefs.scene.iCutThreshold, g_prefs.scene.iFadeThreshold);

			while(g_sceneShuttleMode) {
				SceneShuttleStep();

				while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
					if (msg.message == WM_QUIT) goto wm_quit_detected;

					guiCheckDialogs(&msg);

					if (TranslateAccelerator(g_hWnd, g_hAccelMain, &msg)) continue;

					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}

			delete g_sceneDetector;
			g_sceneDetector = NULL;
		}
	}
wm_quit_detected:

	Deinit();

	VDCHECKPOINT;

    return (msg.wParam);           // Returns the value from PostQuitMessage.

}


//////////////////////////////////////////////////////////////////////

#define MENU_TO_HELP(x) ID_##x, IDS_##x
#define MENU_TO_HELP_AUDIO2(x) ID_AUDIO2_##x, IDS_AUDIO_##x

UINT iMainMenuHelpTranslator[]={
	MENU_TO_HELP(FILE_OPENAVI),
	MENU_TO_HELP(FILE_PREVIEWAVI),
	MENU_TO_HELP(FILE_SAVEAVI),
	MENU_TO_HELP(FILE_SAVECOMPATIBLEAVI),
	MENU_TO_HELP(FILE_SAVESTRIPEDAVI),
	MENU_TO_HELP(FILE_SAVEIMAGESEQ),
	MENU_TO_HELP(FILE_CLOSEAVI),
	MENU_TO_HELP(FILE_CAPTUREAVI),
	MENU_TO_HELP(FILE_STARTSERVER),
	MENU_TO_HELP(FILE_AVIINFO),
	MENU_TO_HELP(FILE_SAVEWAV),
	MENU_TO_HELP(FILE_QUIT),
	MENU_TO_HELP(FILE_LOADCONFIGURATION),
	MENU_TO_HELP(FILE_SAVECONFIGURATION),

	MENU_TO_HELP(VIDEO_SEEK_START),
	MENU_TO_HELP(VIDEO_SEEK_END),
	MENU_TO_HELP(VIDEO_SEEK_PREV),
	MENU_TO_HELP(VIDEO_SEEK_NEXT),
	MENU_TO_HELP(VIDEO_SEEK_KEYPREV),
	MENU_TO_HELP(VIDEO_SEEK_KEYNEXT),
	MENU_TO_HELP(VIDEO_SEEK_SELSTART),
	MENU_TO_HELP(VIDEO_SEEK_SELEND),
	MENU_TO_HELP(VIDEO_SEEK_PREVDROP),
	MENU_TO_HELP(VIDEO_SEEK_NEXTDROP),
	MENU_TO_HELP(EDIT_JUMPTO),
	MENU_TO_HELP(EDIT_DELETE),
	MENU_TO_HELP(EDIT_SETSELSTART),
	MENU_TO_HELP(EDIT_SETSELEND),

	MENU_TO_HELP(VIDEO_FILTERS),
	MENU_TO_HELP(VIDEO_FRAMERATE),
	MENU_TO_HELP(VIDEO_COLORDEPTH),
	MENU_TO_HELP(VIDEO_COMPRESSION),
	MENU_TO_HELP(VIDEO_CLIPPING),
	MENU_TO_HELP(VIDEO_MODE_DIRECT),
	MENU_TO_HELP(VIDEO_MODE_FASTRECOMPRESS),
	MENU_TO_HELP(VIDEO_MODE_NORMALRECOMPRESS),
	MENU_TO_HELP(VIDEO_MODE_FULL),

	MENU_TO_HELP(AUDIO_CONVERSION),
	MENU_TO_HELP(AUDIO_INTERLEAVE),
	MENU_TO_HELP(AUDIO_COMPRESSION),
	MENU_TO_HELP(AUDIO_SOURCE_NONE),
	MENU_TO_HELP(AUDIO_SOURCE_AVI),
	MENU_TO_HELP(AUDIO_SOURCE_WAV),
	MENU_TO_HELP(AUDIO_SOURCE_MP3),
	MENU_TO_HELP(AUDIO_SOURCE_AC3),
	MENU_TO_HELP(AUDIO_SOURCE_OGG),
	MENU_TO_HELP(AUDIO_MODE_DIRECT),
	MENU_TO_HELP(AUDIO_MODE_FULL),

	MENU_TO_HELP_AUDIO2(CONVERSION),
	MENU_TO_HELP_AUDIO2(INTERLEAVE),
	MENU_TO_HELP_AUDIO2(COMPRESSION),
	MENU_TO_HELP_AUDIO2(SOURCE_NONE),
	MENU_TO_HELP_AUDIO2(SOURCE_AVI),
	MENU_TO_HELP_AUDIO2(SOURCE_WAV),
	MENU_TO_HELP_AUDIO2(SOURCE_MP3),
	MENU_TO_HELP_AUDIO2(SOURCE_AC3),
	MENU_TO_HELP_AUDIO2(SOURCE_OGG),
	MENU_TO_HELP_AUDIO2(MODE_DIRECT),
	MENU_TO_HELP_AUDIO2(MODE_FULL),

	MENU_TO_HELP(OPTIONS_PREFERENCES),
	MENU_TO_HELP(OPTIONS_PERFORMANCE),
	MENU_TO_HELP(OPTIONS_DYNAMICCOMPILATION),
	MENU_TO_HELP(OPTIONS_DISPLAYINPUTVIDEO),
	MENU_TO_HELP(OPTIONS_DISPLAYOUTPUTVIDEO),
	MENU_TO_HELP(OPTIONS_DISPLAYDECOMPRESSEDOUTPUT),
	MENU_TO_HELP(OPTIONS_ENABLEMMX),
	MENU_TO_HELP(OPTIONS_SHOWSTATUSWINDOW),
	MENU_TO_HELP(OPTIONS_SYNCHRONOUSBLIT),
	MENU_TO_HELP(OPTIONS_VERTICALDISPLAY),
	MENU_TO_HELP(OPTIONS_DRAWHISTOGRAMS),
	MENU_TO_HELP(OPTIONS_SYNCTOAUDIO),
	MENU_TO_HELP(OPTIONS_DROPFRAMES),
	MENU_TO_HELP(OPTIONS_ENABLEDIRECTDRAW),

	MENU_TO_HELP(HELP_CONTENTS),
	MENU_TO_HELP(HELP_CHANGELOG),
	MENU_TO_HELP(HELP_RELEASENOTES),
	MENU_TO_HELP(HELP_ABOUT),
	NULL,NULL,
};

//////////////////////////////////////////////////////////////////////

char PositionFrameTypeCallback(HWND hwnd, void *pvData, long pos) {
	try {
		if (inputVideoAVI)
			if (inputSubset) {
				bool bMasked;
				long nFrame = inputSubset->lookupFrame(pos, bMasked);

				return bMasked ? 'M' : inputVideoAVI->getFrameTypeChar(nFrame);
			} else
				return inputVideoAVI->getFrameTypeChar(pos);
		else
			return 0;
	} catch(const MyError&) {
		return 0;
	}
}

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
// 13/11/2002, Cyrius : updated for AVI files
__int64 PositionFrameFileSizeCallback(HWND hwnd, void *pvData, long pos) {
	try {
		if(inputAVI && inputVideoAVI) {
			if(inputSubset)
				pos = inputSubset->lookupFrame(pos);
			__int64 size = 0;
			if(inputAVI->isOGM) {
				ogm_stream *stream = ogm_streams;
				while(stream) {
					if( (stream->type != AUDIO_TYPE_INPUTFILE) && (stream->use) )
						size += stream->audio->getFilePos(stream->audio->msToSamples(inputVideoAVI->samplesToMs(pos)));
					stream = stream->next;
				}
			}
			return (size + inputVideoAVI->sampleOffset(pos));
		} else
			return 0;
	} catch(const MyError&) {
		return 0;
	}
}
//END ==========================//

void DisplayFrame(HWND hWnd, LONG pos, bool bDispInput=true) {
	static FilterStateInfo fsi;
	long original_pos = pos;
	static int s_nLastFrame = -1;

	if (!g_dubOpts.video.fShowInputFrame && !g_dubOpts.video.fShowOutputFrame)
		return;


	try {
		BITMAPINFOHEADER *dcf;
		void *lpBits;
		long limit = inputVideoAVI->lSampleLast;

		if (inputSubset) {
			int len = 1;

			pos = inputSubset->lookupRange(pos, len);

			if (pos < 0)
				pos = inputVideoAVI->lSampleLast;
		}

		bool bShowOutput = !g_sceneShuttleMode && !g_dubber && g_dubOpts.video.fShowOutputFrame;

		if (s_nLastFrame != pos || !inputVideoAVI->isFrameBufferValid() || (bShowOutput && !filters.isRunning())) {

			if (bDispInput)
				s_nLastFrame = pos;

			dcf = inputVideoAVI->getDecompressedFormat();

			if (pos >= inputVideoAVI->lSampleLast) {
				FillRect(hDCWindow, &g_rInputFrame, (HBRUSH)GetClassLong(hWnd,GCL_HBRBACKGROUND));
			} else {
				BITMAPINFOHEADER bihOutput;
				VBitmap *out;

				VDCHECKPOINT;
				CHECK_FPU_STACK

				lpBits = inputVideoAVI->getFrame(pos);

				CHECK_FPU_STACK
				VDCHECKPOINT;

				if (!lpBits)
					return;

				if (g_dubOpts.video.fShowInputFrame && bDispInput)
					DrawDibDraw(
							hDDWindow,
							hDCWindow,
							g_rInputFrame.left, g_rInputFrame.top,
							g_rInputFrame.right-g_rInputFrame.left, g_rInputFrame.bottom-g_rInputFrame.top,
							dcf,
							lpBits,
							0, 0, 
							dcf->biWidth, dcf->biHeight,
							0);

				VDCHECKPOINT;

				if (bShowOutput) {
					if (!filters.isRunning()) {
						CPUTest();
						filters.initLinearChain(&g_listFA, (Pixel *)(dcf+1), dcf->biWidth, dcf->biHeight, 32, 16+8*g_dubOpts.video.outputDepth);
						if (filters.ReadyFilters(&fsi))
							throw MyError("can't initialize filters");
					}

					fsi.lCurrentFrame				= original_pos;
					fsi.lMicrosecsPerFrame			= MulDivUnsigned(inputVideoAVI->streamInfo.dwScale, 1000000, inputVideoAVI->streamInfo.dwRate);
					fsi.lCurrentSourceFrame			= pos;
					fsi.lMicrosecsPerSrcFrame		= MulDivUnsigned(inputVideoAVI->streamInfo.dwScale, 1000000, inputVideoAVI->streamInfo.dwRate);
					fsi.lSourceFrameMS				= MulDiv(fsi.lCurrentSourceFrame, fsi.lMicrosecsPerSrcFrame, 1000);
					fsi.lDestFrameMS				= MulDiv(fsi.lCurrentFrame, fsi.lMicrosecsPerFrame, 1000);

					filters.InputBitmap()->BitBlt(0, 0, &VBitmap(lpBits, dcf), 0, 0, -1, -1);

					filters.RunFilters();

					out = filters.LastBitmap();

					out->MakeBitmapHeader(&bihOutput);

					DrawDibDraw(
							hDDWindow2,
							hDCWindow,
							g_rOutputFrame.left, g_rOutputFrame.top,
							g_rOutputFrame.right-g_rOutputFrame.left, g_rOutputFrame.bottom-g_rOutputFrame.top,
							&bihOutput,
							out->data,
							0, 0, 
							out->w, out->h,
							0);
				}
				VDCHECKPOINT;
			}
		}

	} catch(const MyError& e) {
//		e.post(hWnd, szError);
		const char *src = e.gets();
		char *dst = strdup(src);

		if (!dst)
			guiSetStatus("%s", 255, e.gets());
		else {
			for(char *t = dst; *t; ++t)
				if (*t == '\n')
					*t = ' ';

			guiSetStatus("%s", 255, dst);
			free(dst);
		}
		SceneShuttleStop();
	}
}

void SceneShuttleStop() {
	if (g_sceneShuttleMode) {
		HWND hwndPosition = GetDlgItem(g_hWnd, IDC_POSITION);
		LONG lSample = SendMessage(hwndPosition, PCM_GETPOS, 0, 0);

		SendMessage(hwndPosition, PCM_RESETSHUTTLE, 0, 0);
		g_sceneShuttleMode = 0;
		g_sceneShuttleAdvance = 0;
		g_sceneShuttleCounter = 0;

		if (inputVideoAVI)
			DisplayFrame(g_hWnd, lSample);
	}
}

void SceneShuttleStep() {
	if (!inputVideoAVI)
		SceneShuttleStop();

	HWND hwndPosition = GetDlgItem(g_hWnd, IDC_POSITION);
	LONG lSample = SendMessage(hwndPosition, PCM_GETPOS, 0, 0) + g_sceneShuttleMode;
	long ls2 = inputSubset ? inputSubset->lookupFrame(lSample) : lSample;

	if (!inputVideoAVI || ls2 < inputVideoAVI->lSampleFirst || ls2 >= inputVideoAVI->lSampleLast) {
		SceneShuttleStop();
		return;
	}

	if (g_sceneShuttleAdvance < 1280)
		++g_sceneShuttleAdvance;

	g_sceneShuttleCounter += 32;

	if (g_sceneShuttleCounter >= g_sceneShuttleAdvance) {
		g_sceneShuttleCounter = 0;
		DisplayFrame(g_hWnd, lSample, true);
	} else
		DisplayFrame(g_hWnd, lSample, false);

	SendMessage(hwndPosition, PCM_SETPOS, 0, (LPARAM)lSample);

	if (g_sceneDetector->Submit(&VBitmap(inputVideoAVI->getFrameBuffer(), inputVideoAVI->getDecompressedFormat()))) {
		SceneShuttleStop();
	}
}

void MenuMRUListUpdate(HWND hwnd) {
	HMENU hmenuFile = GetSubMenu(GetMenu(hwnd), 0);
	MENUITEMINFO mii;
	char name[MAX_PATH], name2[MAX_PATH];
	int index=0;

#define WIN95_MENUITEMINFO_SIZE (offsetof(MENUITEMINFO, cch) + sizeof(UINT))

	memset(&mii, 0, sizeof mii);
	mii.cbSize	= WIN95_MENUITEMINFO_SIZE;
	for(;;) {
		mii.fMask			= MIIM_TYPE;
		mii.dwTypeData		= name2;
		mii.cch				= sizeof name2;

		if (!GetMenuItemInfo(hmenuFile, MRU_LIST_POSITION, TRUE, &mii)) break;

		if (mii.fType & MFT_SEPARATOR) break;

		RemoveMenu(hmenuFile, MRU_LIST_POSITION, MF_BYPOSITION);
	}

	while(!mru_list->get(index, name, sizeof name)) {
		char *s = name;

		while(*s) ++s;
		while(s>name && s[-1]!='\\' && s[-1]!=':') --s;
		wsprintf(name2, "&%d %s", (index+1)%10, s);

		mii.fMask		= MIIM_TYPE | MIIM_STATE | MIIM_ID;
		mii.fType		= MFT_STRING;
		mii.fState		= MFS_ENABLED;
		mii.wID			= ID_MRU_FILE0 + index;
		mii.dwTypeData	= name2;
		mii.cch				= sizeof name2;

		if (!InsertMenuItem(hmenuFile, MRU_LIST_POSITION+index, TRUE, &mii))
			break;

		++index;
	}

	if (!index) {
		mii.fMask		= MIIM_TYPE | MIIM_STATE | MIIM_ID;
		mii.fType		= MFT_STRING;
		mii.fState		= MFS_GRAYED;
		mii.wID			= ID_MRU_FILE0;
		mii.dwTypeData	= "Recent file list";
		mii.cch			= sizeof name2;

		InsertMenuItem(hmenuFile, MRU_LIST_POSITION+index, TRUE, &mii);
	}
}

void RecalcFrameSizes() {
	RECT &rInputFrame = g_fSwapPanes ? g_rOutputFrame : g_rInputFrame;
	RECT &rOutputFrame = g_fSwapPanes ? g_rInputFrame : g_rOutputFrame;

	memset(&rInputFrame, 0, sizeof(RECT));
	memset(&rOutputFrame, 0, sizeof(RECT));

	if (inputVideoAVI) {
		BITMAPINFOHEADER *formatIn = inputVideoAVI->getImageFormat();

		if (formatIn) {
			BITMAPINFOHEADER *dcf = inputVideoAVI->getDecompressedFormat();
			int w = dcf->biWidth;
			int h = dcf->biHeight;
			int w2, h2;

			if (g_iInputFrameShift<0) {
				w >>= -g_iInputFrameShift;
				h >>= -g_iInputFrameShift;
			} else {
				w <<= g_iInputFrameShift;
				h <<= g_iInputFrameShift;
			}

			g_rInputFrame.left		= 6;
			g_rInputFrame.top		= 6;
			g_rInputFrame.right		= 6 + w;
			g_rInputFrame.bottom	= 6 + h;

			// figure out output size too

			if (!filters.isRunning()) {
				if (g_dubber)
					return;

				filters.prepareLinearChain(&g_listFA, (Pixel *)(dcf+1), dcf->biWidth, dcf->biHeight, 32, 16+8*g_dubOpts.video.outputDepth);
			}

			w2 = filters.OutputBitmap()->w;
			h2 = filters.OutputBitmap()->h;

			if (g_iOutputFrameShift<0) {
				w2 >>= -g_iOutputFrameShift;
				h2 >>= -g_iOutputFrameShift;
			} else {
				w2 <<= g_iOutputFrameShift;
				h2 <<= g_iOutputFrameShift;
			}

			// If frames are reversed, swap the sizes.

			if (g_fSwapPanes) {
				int t;

				t=w; w=w2; w2=t;
				t=h; h=h2; h2=t;
			}

			// Layout frames.

			rInputFrame.left	= 6;
			rInputFrame.top		= 6;
			rInputFrame.right	= 6 + w;
			rInputFrame.bottom	= 6 + h;

			if (g_vertical) {
				rOutputFrame.left	= rInputFrame.left;
				rOutputFrame.top	= rInputFrame.bottom + 12;
				rOutputFrame.right	= rOutputFrame.left + w2;
				rOutputFrame.bottom	= rOutputFrame.top + h2;
			} else {
				rOutputFrame.left	= rInputFrame.right + 12;
				rOutputFrame.top	= rInputFrame.top;
				rOutputFrame.right	= rOutputFrame.left + w2;
				rOutputFrame.bottom	= rOutputFrame.top + h2;
			}
		}
	}
}

void SetTitleByFile(HWND hWnd) {
	if (inputAVI)
		if (g_szFileDescription[0])
			guiSetTitle(hWnd, IDS_TITLE_IDLE2, g_szInputAVIFileTitle, g_szFileDescription);
		else
			guiSetTitle(hWnd, IDS_TITLE_IDLE, g_szInputAVIFileTitle);
	else
		guiSetTitle(hWnd, IDS_TITLE_NOFILE);
}

BOOL MenuHit(HWND hWnd, UINT id) {
	bool bFilterReinitialize = !g_dubber;

	if (bFilterReinitialize) {
		switch(id) {
		case ID_VIDEO_SEEK_START:
		case ID_VIDEO_SEEK_END:
		case ID_VIDEO_SEEK_PREV:
		case ID_VIDEO_SEEK_NEXT:
		case ID_VIDEO_SEEK_PREVONESEC:
		case ID_VIDEO_SEEK_NEXTONESEC:
		case ID_VIDEO_SEEK_KEYPREV:
		case ID_VIDEO_SEEK_KEYNEXT:
		case ID_VIDEO_SEEK_SELSTART:
		case ID_VIDEO_SEEK_SELEND:
		case ID_VIDEO_SEEK_PREVDROP:
		case ID_VIDEO_SEEK_NEXTDROP:
		case ID_EDIT_JUMPTO:
		case ID_VIDEO_COPYSOURCEFRAME:
		case ID_VIDEO_COPYOUTPUTFRAME:
			break;
		default:
			filters.DeinitFilters();
			filters.DeallocateBuffers();
			break;
		}
	}

	SetAudioSource();
	JobLockDubber();
	DragAcceptFiles(hWnd, FALSE);

	try {
		switch(id) {
		case ID_FILE_QUIT:
			DestroyWindow(hWnd);
			break;
		case ID_FILE_OPENAVI:
			OpenAVI(-1, false);
			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
			SetTitleByFile(hWnd);
			RecalcFrameSizes();
			break;
		case ID_FILE_APPENDSEGMENT:
			AppendAVI();
			break;
		case ID_FILE_PREVIEWAVI:
			PreviewAVI(hWnd, NULL, g_prefs.main.iPreviewPriority);
			break;
		case ID_FILE_SAVEAVI:
			SaveAVI(hWnd, false);
			JobUnlockDubber();
			break;
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		case ID_FILE_SAVEOGM:
			SaveOGM(hWnd, false);
			break;
		case ID_OGM_OGMINFO:
			//DialogBox(g_hInst, MAKEINTRESOURCE(IDD_OGMINFO), hWnd, OGMInfoDlgProc);
			DialogBox(g_hInst, MAKEINTRESOURCE(IDD_SHOWTEXT), hWnd, OGMInfoDlgProc);
			break;
		case ID_OGM_SHOWINPUTS:
			DialogBox(g_hInst, MAKEINTRESOURCE(IDD_OGMINPUTS), hWnd, OGMInputsDlgProc);
			break;
		case ID_OGM_VIDEOCOMMENTS:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_OGMCOMMENTS), hWnd, OGMCommentsDlgProc, (LPARAM)(void *)&video_comments);
			break;
		case ID_OGM_AUDIOCOMMENTS:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_OGMCOMMENTS), hWnd, OGMCommentsDlgProc, (LPARAM)(void *)&audio_comments);
			break;
		case ID_OGM_AUDIO2COMMENTS:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_OGMCOMMENTS), hWnd, OGMCommentsDlgProc, (LPARAM)(void *)&audio2_comments);
			break;
//END ==========================//
		case ID_FILE_SAVECOMPATIBLEAVI:
			SaveAVI(hWnd, true);
			break;
		case ID_FILE_SAVESTRIPEDAVI:
			SaveStripedAVI(hWnd);
			break;
		case ID_FILE_SAVESTRIPEMASTER:
			SaveStripeMaster(hWnd);
			break;
		case ID_FILE_SAVEIMAGESEQ:
			SaveImageSeq(hWnd);
			break;
		case ID_FILE_SAVESEGMENTEDAVI:
			SaveSegmentedAVI(hWnd);
			break;
		case ID_FILE_SAVEWAV:
			SaveWAV(hWnd, FALSE);
			break;
		// 22/11/202, Cyrius
		case ID_FILE_DEMUXAUDIO:
			SaveWAV(hWnd, TRUE);
			break;
		case ID_FILE_CLOSEAVI:
			CloseAVI();
			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
			SetTitleByFile(hWnd);
			break;
		case ID_FILE_STARTSERVER:
			SetAudioSource();
			ActivateFrameServerDialog(hWnd);
			MenuMRUListUpdate(hWnd);
			break;
		case ID_FILE_CAPTUREAVI:
			CPUTest();
			Capture(hWnd);
			MenuMRUListUpdate(hWnd);
			RecalcFrameSizes();		// necessary because filters can be changed in capture mode
			SetTitleByFile(hWnd);
			break;
		case ID_FILE_SAVECONFIGURATION:
			SaveConfiguration(hWnd);
			break;
		case ID_FILE_LOADCONFIGURATION:
		case ID_FILE_RUNSCRIPT:
			RunScript(NULL, (void *)hWnd);
			SetTitleByFile(hWnd);
			RecalcFrameSizes();
			break;
		case ID_FILE_JOBCONTROL:
			OpenJobWindow();
			break;
		case ID_FILE_AVIINFO:
	//		if (inputAVI) ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_AVI_INFO), hWnd, AVIInfoDlgProc);
			if (inputAVI)
				inputAVI->InfoDialog(hWnd);
			break;
		case ID_VIDEO_FILTERS:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_FILTERS), hWnd, FilterDlgProc);
			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
			RecalcFrameSizes();
			break;
		case ID_VIDEO_FRAMERATE:
			SetAudioSource();
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_VIDEO_FRAMERATE), hWnd, VideoDecimationDlgProc);
			RecalcPositionTimeConstant();
			break;
		case ID_VIDEO_COLORDEPTH:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_VIDEO_DEPTH), hWnd, VideoDepthDlgProc);
			break;
		case ID_VIDEO_CLIPPING:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_VIDEO_CLIPPING), hWnd, VideoClippingDlgProc);
			break;
		case ID_VIDEO_COMPRESSION:
			if (!(g_Vcompression.dwFlags & ICMF_COMPVARS_VALID)) {
				memset(&g_Vcompression, 0, sizeof g_Vcompression);
				g_Vcompression.dwFlags |= ICMF_COMPVARS_VALID;
				g_Vcompression.lQ = 10000;
			}

			g_Vcompression.cbSize = sizeof(COMPVARS);

			ChooseCompressor(hWnd, &g_Vcompression, NULL);

			break;
		case ID_VIDEO_MODE_DIRECT:
			g_dubOpts.video.mode = DubVideoOptions::M_NONE;
			break;
		case ID_VIDEO_MODE_FASTRECOMPRESS:
			g_dubOpts.video.mode = DubVideoOptions::M_FASTREPACK;
			break;
		case ID_VIDEO_MODE_NORMALRECOMPRESS:
			g_dubOpts.video.mode = DubVideoOptions::M_SLOWREPACK;
			break;
		case ID_VIDEO_MODE_FULL:
			g_dubOpts.video.mode = DubVideoOptions::M_FULL;
			break;
		case ID_VIDEO_COPYSOURCEFRAME:
			if (!inputVideoAVI || !inputVideoAVI->isFrameBufferValid())
				break;

			if (OpenClipboard(hWnd)) {
				if (EmptyClipboard()) {
					BITMAPINFOHEADER *pbih = inputVideoAVI->getDecompressedFormat();
					long lFormatSize = pbih->biSize + (pbih->biBitCount<=16 ? pbih->biClrUsed*sizeof(RGBQUAD) : 0);
					HANDLE hMem;
					void *lpvMem;

					if (hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, pbih->biSizeImage + lFormatSize)) {
						if (lpvMem = GlobalLock(hMem)) {
							memcpy(lpvMem, pbih, lFormatSize);
							memcpy((char *)lpvMem + lFormatSize, inputVideoAVI->getFrameBuffer(), pbih->biSizeImage);

							GlobalUnlock(lpvMem);
							SetClipboardData(CF_DIB, hMem);
							CloseClipboard();
							break;
						}
						GlobalFree(hMem);
					}
				}
				CloseClipboard();
			}
			break;
		case ID_VIDEO_COPYOUTPUTFRAME:
			if (!filters.isRunning())
				break;
			if (OpenClipboard(hWnd)) {
				if (EmptyClipboard()) {
					BITMAPINFOHEADER bih;
					long lFormatSize;
					HANDLE hMem;
					void *lpvMem;

					filters.LastBitmap()->MakeBitmapHeaderNoPadding(&bih);
					lFormatSize = bih.biSize;

					if (hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, bih.biSizeImage + lFormatSize)) {
						if (lpvMem = GlobalLock(hMem)) {
							memcpy(lpvMem, &bih, lFormatSize);

							VBitmap((char *)lpvMem + lFormatSize, &bih).BitBlt(0, 0, filters.LastBitmap(), 0, 0, -1, -1); 

							GlobalUnlock(lpvMem);
							SetClipboardData(CF_DIB, hMem);
							CloseClipboard();
							break;
						}
						GlobalFree(hMem);
					}
				}
				CloseClipboard();
			}
			break;

		case ID_EDIT_DELETE:
			DoDelete();
			break;

		case ID_EDIT_MASK:
			DoMaskChange(true);
			break;

		case ID_EDIT_UNMASK:
			DoMaskChange(false);
			break;

		case ID_EDIT_SETSELSTART:
			if (inputAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);

				SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETSELSTART, (WPARAM)TRUE, lSample);

				guiSetStatus("Start offset set to %ld ms", 255,
						g_dubOpts.video.lStartOffsetMS = inputVideoAVI->samplesToMs(lSample));
			}
			break;
		case ID_EDIT_SETSELEND:
			if (inputAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);

				SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETSELEND, (WPARAM)TRUE, lSample);

				guiSetStatus("End offset set to %ld ms", 255,
					g_dubOpts.video.lEndOffsetMS = inputVideoAVI->samplesToMs((inputSubset ? inputSubset->getTotalFrames() : (inputVideoAVI->lSampleLast - inputVideoAVI->lSampleFirst)) - lSample));
			}
			break;

		case ID_AUDIO_CONVERSION:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_AUDIO_CONVERSION), hWnd, AudioConversionDlgProc);
			break;
		case ID_AUDIO_INTERLEAVE:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_INTERLEAVE), hWnd, AudioInterleaveDlgProc);
			break;
		case ID_AUDIO_COMPRESSION:
			SetAudioSource();

			if (!inputAudio)
				g_ACompressionFormat = AudioChooseCompressor(hWnd, g_ACompressionFormat, NULL);
			else {
		
				PCMWAVEFORMAT wfex;

				memcpy(&wfex, inputAudio->getWaveFormat(), sizeof(PCMWAVEFORMAT));
				// Say 16-bit if the source was compressed.

				if (wfex.wf.wFormatTag != WAVE_FORMAT_PCM)
					wfex.wBitsPerSample = 16;

				wfex.wf.wFormatTag = WAVE_FORMAT_PCM;

				switch(g_dubOpts.audio.newPrecision) {
				case DubAudioOptions::P_8BIT:	wfex.wBitsPerSample = 8; break;
				case DubAudioOptions::P_16BIT:	wfex.wBitsPerSample = 16; break;
				}

				switch(g_dubOpts.audio.newPrecision) {
				case DubAudioOptions::C_MONO:	wfex.wf.nChannels = 1; break;
				case DubAudioOptions::C_STEREO:	wfex.wf.nChannels = 2; break;
				}

				if (g_dubOpts.audio.new_rate) {
					long samp_frac;

					if (g_dubOpts.audio.integral_rate)
						if (g_dubOpts.audio.new_rate > wfex.wf.nSamplesPerSec)
							samp_frac = 0x10000 / ((g_dubOpts.audio.new_rate + wfex.wf.nSamplesPerSec/2) / wfex.wf.nSamplesPerSec); 
						else
							samp_frac = 0x10000 * ((wfex.wf.nSamplesPerSec + g_dubOpts.audio.new_rate/2) / g_dubOpts.audio.new_rate);
					else
						samp_frac = MulDiv(wfex.wf.nSamplesPerSec, 0x10000L, g_dubOpts.audio.new_rate);

					wfex.wf.nSamplesPerSec = MulDiv(wfex.wf.nSamplesPerSec, 0x10000L, samp_frac);
				}

				wfex.wf.nBlockAlign = (wfex.wBitsPerSample+7)/8 * wfex.wf.nChannels;
				wfex.wf.nAvgBytesPerSec = wfex.wf.nSamplesPerSec * wfex.wf.nBlockAlign;

				g_ACompressionFormat = AudioChooseCompressor(hWnd, g_ACompressionFormat, (WAVEFORMATEX *)&wfex);

			}

			if (g_ACompressionFormat) {
				g_ACompressionFormatSize = sizeof(WAVEFORMATEX) + g_ACompressionFormat->cbSize;
			}
			break;

		case ID_AUDIO_VOLUME:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_AUDIO_VOLUME), hWnd, AudioVolumeDlgProc);
			break;

		case ID_AUDIO_SOURCE_NONE:
			audioInputMode = AUDIOIN_NONE;
			audio2InputMode = AUDIOIN_NONE;
			CloseWAV(); CloseWAV(true);
			CloseMP3(); CloseMP3(true);
			CloseAC3(); CloseAC3(true);
			CloseOGG(); CloseOGG(true);
			//SetAudioSource();
			//RecalcPositionTimeConstant();
			break;
		/*case ID_AUDIO_SOURCE_AVI:
			audioInputMode = AUDIOIN_AVI;
			CloseWAV();
			SetAudioSource();
			RecalcPositionTimeConstant();
			break;
		case ID_AUDIO_SOURCE_WAV:
			OpenWAV();
			SetAudioSource();
			RecalcPositionTimeConstant();
			break;
*/
		case ID_AUDIO_SOURCE_AVI:
			audioInputMode = AUDIOIN_AVI;
			CloseWAV();
			CloseMP3();
			CloseAC3();
			CloseOGG();
			break;
		case ID_AUDIO_SOURCE_WAV:
			CloseMP3();
			CloseAC3();
			CloseOGG();
			OpenWAV();
			break;
		case ID_AUDIO_SOURCE_MP3:
			CloseWAV();
			CloseAC3();
			CloseOGG();
			OpenMP3();
			break;
		case ID_AUDIO_SOURCE_AC3:
			CloseWAV();
			CloseMP3();
			CloseOGG();
			OpenAC3();
			break;
		case ID_AUDIO_SOURCE_OGG:
			CloseWAV();
			CloseMP3();
			CloseAC3();
			OpenOGG();
			break;

		case ID_AUDIO_MODE_DIRECT:
			g_dubOpts.audio.mode = DubAudioOptions::M_NONE;
			break;
		case ID_AUDIO_MODE_FULL:
			g_dubOpts.audio.mode = DubAudioOptions::M_FULL;
			break;


// second audiostream

		case ID_AUDIO2_CONVERSION:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_AUDIO_CONVERSION), hWnd, Audio2ConversionDlgProc);
			break;
		case ID_AUDIO2_INTERLEAVE:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_INTERLEAVE), hWnd, Audio2InterleaveDlgProc);
			break;
		case ID_AUDIO2_COMPRESSION:
			SetAudioSource();

			if (!inputAudio2)
				g_A2CompressionFormat = AudioChooseCompressor(hWnd, g_A2CompressionFormat, NULL);
			else {
		
				PCMWAVEFORMAT wfex;

				memcpy(&wfex, inputAudio2->getWaveFormat(), sizeof(PCMWAVEFORMAT));
				wfex.wf.wFormatTag = WAVE_FORMAT_PCM;

				switch(g_dubOpts.audio2.newPrecision) {
				case DubAudioOptions::P_8BIT:	wfex.wBitsPerSample = 8; break;
				case DubAudioOptions::P_16BIT:	wfex.wBitsPerSample = 16; break;
				}

				switch(g_dubOpts.audio2.newPrecision) {
				case DubAudioOptions::C_MONO:	wfex.wf.nChannels = 1; break;
				case DubAudioOptions::C_STEREO:	wfex.wf.nChannels = 2; break;
				}

				if (g_dubOpts.audio2.new_rate) {
					long samp_frac;

					if (g_dubOpts.audio2.integral_rate)
						if (g_dubOpts.audio2.new_rate > wfex.wf.nSamplesPerSec)
							samp_frac = 0x10000 / ((g_dubOpts.audio2.new_rate + wfex.wf.nSamplesPerSec/2) / wfex.wf.nSamplesPerSec); 
						else
							samp_frac = 0x10000 * ((wfex.wf.nSamplesPerSec + g_dubOpts.audio2.new_rate/2) / g_dubOpts.audio2.new_rate);
					else
						samp_frac = MulDiv(wfex.wf.nSamplesPerSec, 0x10000L, g_dubOpts.audio2.new_rate);

					wfex.wf.nSamplesPerSec = MulDiv(wfex.wf.nSamplesPerSec, 0x10000L, samp_frac);
				}

				wfex.wf.nBlockAlign = (wfex.wBitsPerSample+7)/8 * wfex.wf.nChannels;
				wfex.wf.nAvgBytesPerSec = wfex.wf.nSamplesPerSec * wfex.wf.nBlockAlign;

				g_A2CompressionFormat = AudioChooseCompressor(hWnd, g_A2CompressionFormat, (WAVEFORMATEX *)&wfex);

			}

			if (g_A2CompressionFormat) {
				g_A2CompressionFormatSize = sizeof(WAVEFORMATEX) + g_A2CompressionFormat->cbSize;
			}

			break;

		case ID_AUDIO2_VOLUME:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_AUDIO_VOLUME), hWnd, Audio2VolumeDlgProc);
			break;

		case ID_AUDIO2_SOURCE_NONE:
			audio2InputMode = AUDIOIN_NONE;
			CloseWAV(true);
			CloseMP3(true);
			CloseAC3(true);
			CloseOGG(true);
			break;
		case ID_AUDIO2_SOURCE_AVI:
			audio2InputMode = AUDIOIN_AVI;
			CloseWAV(true);
			CloseMP3(true);
			CloseAC3(true);
			CloseOGG(true);
			break;
		case ID_AUDIO2_SOURCE_WAV:
			CloseMP3(true);
			CloseAC3(true);
			CloseOGG(true);
			OpenWAV(true);
			break;
		case ID_AUDIO2_SOURCE_MP3:
			CloseWAV(true);
			CloseAC3(true);
			CloseOGG(true);
			OpenMP3(true);
			break;
		case ID_AUDIO2_SOURCE_AC3:
			CloseWAV(true);
			CloseMP3(true);
			CloseOGG(true);
			OpenAC3(true);
			break;
		case ID_AUDIO2_SOURCE_OGG:
			CloseWAV(true);
			CloseMP3(true);
			CloseAC3(true);
			OpenOGG(true);
			break;

		case ID_AUDIO2_MODE_DIRECT:
			g_dubOpts.audio2.mode = DubAudioOptions::M_NONE;
			break;
		case ID_AUDIO2_MODE_FULL:
			g_dubOpts.audio2.mode = DubAudioOptions::M_FULL;
			break;



		case ID_OPTIONS_PERFORMANCE:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_PERFORMANCE), hWnd, PerformanceOptionsDlgProc);
			break;
		case ID_OPTIONS_DYNAMICCOMPILATION:
			ActivateDubDialog(g_hInst, MAKEINTRESOURCE(IDD_PERF_DYNAMIC), hWnd, DynamicCompileOptionsDlgProc);
			break;
		case ID_OPTIONS_PREFERENCES:
			DialogBox(g_hInst, MAKEINTRESOURCE(IDD_PREFERENCES), hWnd, PreferencesDlgProc);
			break;
		case ID_OPTIONS_DISPLAYINPUTVIDEO:
			if (g_dubStatus)
				g_dubStatus->ToggleFrame(false);
			else
				g_dubOpts.video.fShowInputFrame = !g_dubOpts.video.fShowInputFrame;
			break;
		case ID_OPTIONS_DISPLAYOUTPUTVIDEO:
			if (g_dubStatus)
				g_dubStatus->ToggleFrame(true);
			else
				g_dubOpts.video.fShowOutputFrame = !g_dubOpts.video.fShowOutputFrame;
			break;
		case ID_OPTIONS_DISPLAYDECOMPRESSEDOUTPUT:
			g_drawDecompressedFrame = !g_drawDecompressedFrame;
			break;
		case ID_OPTIONS_SHOWSTATUSWINDOW:
			if (g_dubStatus)
				g_dubStatus->ToggleStatus();
			else
				g_showStatusWindow = !g_showStatusWindow;
			break;
		case ID_OPTIONS_SYNCHRONOUSBLIT:
			g_syncroBlit = !g_syncroBlit;
			break;
		case ID_OPTIONS_VERTICALDISPLAY:
			g_vertical = !g_vertical;
			RecalcFrameSizes();
			InvalidateRect(hWnd, NULL, TRUE);
			break;
		case ID_OPTIONS_DRAWHISTOGRAMS:
			g_dubOpts.video.fHistogram = !g_dubOpts.video.fHistogram;
			break;
		case ID_OPTIONS_SYNCTOAUDIO:
			g_dubOpts.video.fSyncToAudio = !g_dubOpts.video.fSyncToAudio;
			break;
		case ID_OPTIONS_ENABLEDIRECTDRAW:
			g_dubOpts.perf.useDirectDraw = !g_dubOpts.perf.useDirectDraw;
			break;
		case ID_OPTIONS_DROPFRAMES:
			g_fDropFrames = !g_fDropFrames;
			break;
		case ID_OPTIONS_SWAPPANES:
			g_fSwapPanes = !g_fSwapPanes;
			RecalcFrameSizes();
			InvalidateRect(hWnd, NULL, TRUE);
			break;

		case ID_OPTIONS_PREVIEWPROGRESSIVE:	g_dubOpts.video.nPreviewFieldMode = 0; break;
		case ID_OPTIONS_PREVIEWFIELDA:		g_dubOpts.video.nPreviewFieldMode = 1; break;
		case ID_OPTIONS_PREVIEWFIELDB:		g_dubOpts.video.nPreviewFieldMode = 2; break;


		case ID_TOOLS_HEXVIEWER:
			HexEdit(NULL);
			break;

		case ID_TOOLS_CREATESPARSEAVI:
			CreateExtractSparseAVI(hWnd, false);
			break;

		case ID_TOOLS_EXPANDSPARSEAVI:
			CreateExtractSparseAVI(hWnd, true);
			break;

		case ID_HELP_LICENSE:
			DisplayLicense(hWnd);
			break;

		case ID_HELP_CONTENTS:
			HelpShowHelp(hWnd);
			break;
		case ID_HELP_CHANGELOG:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_SHOWTEXT), hWnd, ShowTextDlgProc, (LPARAM)MAKEINTRESOURCE(IDR_CHANGES));
			break;
		case ID_HELP_RELEASENOTES:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_SHOWTEXT), hWnd, ShowTextDlgProc, (LPARAM)MAKEINTRESOURCE(IDR_RELEASE_NOTES));
			break;
// *******************************************************************
// *** Tobias Minich, 04/11/2002								   ***
// BEGIN *************************************************************
		case ID_HELP_RELEASENOTES_MOD:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_SHOWTEXT), hWnd, ShowTextDlgProc, (LPARAM)MAKEINTRESOURCE(IDR_RELEASE_NOTES_MOD));
			break;
// END ***************************************************************
		case ID_HELP_ABOUT:
// *******************************************************************
// *** Redraw+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
//			DialogBox(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutDlgProc);
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_ABOUT), hWnd, AboutDlgProc, IDR_CREDITS);
// END ***************************************************************
			break;

//==============================//
//=====  MODIFICATION PNG  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		case ID_HELP_PNGSUPPORT:
			MessageBox(hWnd, png_get_copyright(NULL), "About PNG support", MB_OK | MB_ICONINFORMATION);
			break;
//END ==========================//

		case ID_HELP_ONLINE_HOME:	LaunchURL("http://www.virtualdub.org/index"); break;
// *******************************************************************
// *** Tobias Minich, 04/11/2002								   ***
// BEGIN *************************************************************
		case ID_HELP_ONLINE_MOD:	LaunchURL("http://virtualdubmod.sourceforge.net"); break;
// END ***************************************************************
		case ID_HELP_ONLINE_FAQ:	LaunchURL("http://www.virtualdub.org/virtualdub_faq"); break;
		case ID_HELP_ONLINE_NEWS:	LaunchURL("http://www.virtualdub.org/virtualdub_news"); break;
		case ID_HELP_ONLINE_KB:		LaunchURL("http://www.virtualdub.org/virtualdub_kb"); break;

		case ID_DUBINPROGRESS_ABORT:
			if (g_dubber) g_dubber->Abort();
			break;


		case ID_VIDEO_SEEK_START:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_START), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_END:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_END), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_PREV:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_BACKWARD), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_NEXT:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_FORWARD), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_PREVONESEC:
			if (inputVideoAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0) - 50;

				if (lSample < 0)
					lSample = 0;

				SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
				DisplayFrame(hWnd, lSample);
			}
			break;
		case ID_VIDEO_SEEK_NEXTONESEC:
			if (inputVideoAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0) + 50;
				LONG lMax = (inputSubset ? inputSubset->getTotalFrames() : inputVideoAVI->lSampleLast);

				if (lSample > lMax)
					lSample = lMax;

				SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
				DisplayFrame(hWnd, lSample);
			}
			break;
		case ID_VIDEO_SEEK_KEYPREV:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_KEYPREV), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_KEYNEXT:
			PostMessage(hWnd, WM_COMMAND, MAKEWPARAM(IDC_POSITION, PCN_KEYNEXT), (LPARAM)GetDlgItem(hWnd, IDC_POSITION));
			break;
		case ID_VIDEO_SEEK_SELSTART:
			if (inputVideoAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETSELSTART, 0, 0);

				if (lSample >= 0) {
					SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
					DisplayFrame(hWnd, lSample);
				}
			}
			break;
		case ID_VIDEO_SEEK_SELEND:
			if (inputVideoAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETSELEND, 0, 0);

				if (lSample >= 0) {
					SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
					DisplayFrame(hWnd, lSample);
				}
			}
			break;
		case ID_VIDEO_SEEK_PREVDROP:
			if (inputAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);

				while(--lSample >= (inputSubset ? 0 : inputVideoAVI->lSampleFirst)) {
					int err;
					long lBytes, lSamples;

					err = inputVideoAVI->read(inputSubset ? inputSubset->lookupFrame(lSample) : lSample, 1, NULL, 0, &lBytes, &lSamples);
					if (err != AVIERR_OK)
						break;

					if (!lBytes) {
						SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
						DisplayFrame(hWnd, lSample);
						break;
					}
				}

				if (lSample < (inputSubset ? 0 : inputVideoAVI->lSampleFirst))
					guiSetStatus("No previous dropped frame found.", 255);
			}
			break;

		case ID_VIDEO_SEEK_NEXTDROP:
			if (inputAVI) {
				LONG lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);

				while(++lSample < (inputSubset ? inputSubset->getTotalFrames() : inputVideoAVI->lSampleLast)) {
					int err;
					long lBytes, lSamples;

					err = inputVideoAVI->read(inputSubset ? inputSubset->lookupFrame(lSample) : lSample, 1, NULL, 0, &lBytes, &lSamples);
					if (err != AVIERR_OK)
						break;

					if (!lBytes) {
						SendDlgItemMessage(hWnd, IDC_POSITION, PCM_SETPOS, (WPARAM)TRUE, lSample);
						DisplayFrame(hWnd, lSample);
						break;
					}
				}

				if (lSample >= (inputSubset ? inputSubset->getTotalFrames() : inputVideoAVI->lSampleLast))
					guiSetStatus("No next dropped frame found.", 255);
			}
			break;

// *******************************************************************
// *** Redraw+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
		case ID_FILE_OPENAVS:
			OpenAVS();
			RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
			SetTitleByFile(hWnd);
			RecalcFrameSizes();
			break;

		case ID_TOOLS_AVS:
			if (g_hScintilla) {
				AVSEdit(NULL, hWnd);
			} else {
				MessageBox(g_hWnd, "SciLexer.dll missing.\rScript editor not available.", "VirtualDubMod Error", MB_OK | MB_ICONERROR);
			}
			break;

		case ID_HELP_ABOUT_MOD:
			DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_ABOUT_MOD), hWnd, AboutDlgProc, IDR_CREDITS_MOD);
			break;

		case ID_REFRESH:

			if (inputAVI || (strcmp(g_szInputAVIFileLastWorking,"") != 0)) {
				long lFrame;
				char lpszFileTemp[MAX_PATH];
				char *lpszName;

				lFrame = SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);
				strcpy(lpszFileTemp, g_szInputAVIFile);

				CloseAVI();
				//RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
				//SetTitleByFile(hWnd);
				strcpy(g_szInputAVIFile, lpszFileTemp);
				strcpy(g_szInputAVIFileTitle, (char *) (strrchr(lpszFileTemp, '\\')+1));

				try	{
					OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false);
					strcpy(g_szInputAVIFileLastWorking, g_szInputAVIFile);
				} catch(const MyError& e) {
					//e.post(hWnd, szError);
					if (g_szInputAVIFileLastWorking != "") {
						strcpy(g_szInputAVIFile, g_szInputAVIFileLastWorking);
						strcpy(g_szInputAVIFileTitle, (char *) (strrchr(g_szInputAVIFileLastWorking, '\\')+1));
						OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false);
					}
					throw;
				}
				

				lpszName = AutodetectFile(inputVideoAVI);

				if (lpszName)
					strcpy(g_szFileDescription, lpszName);
				else
					g_szFileDescription[0]=0;

				RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
				//SetTitleByFile(hWnd);
				RecalcFrameSizes();
			
				SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_SETPOS, TRUE, lFrame);
				DisplayFrame(g_hWnd, SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0));
			}
			break;

// END ***************************************************************

		case ID_VIDEO_SCANFORERRORS:
			if (inputVideoAVI) {
				EnsureSubset();
				ScanForUnreadableFrames(inputSubset, inputVideoAVI);
			}
			break;

		case ID_EDIT_JUMPTO:
			if (inputAVI) {
				long lFrame;

				lFrame = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_JUMPTOFRAME), g_hWnd, VideoJumpDlgProc, SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0));

				if (lFrame >= 0) {
					SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_SETPOS, TRUE, lFrame);
					DisplayFrame(g_hWnd, SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0));
				}
			}
			break;

			// 13/11/2002, Cyrius : Nandub feature (jump on keyframe preceding position in file)
		case ID_EDIT_JUMP_LAST_KEY:
			if (inputAVI) {
				long lMbytes;

				lMbytes = DialogBox(g_hInst, MAKEINTRESOURCE(IDD_JUMPLASTKEY), g_hWnd, VideoJumpLastKeyDlgProc);

				long lFrame = JumpToLastKeyBeforePos( (__int64)lMbytes*(1024*1024) );

				if (lFrame >= 0) {
					SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_SETPOS, TRUE, lFrame);
					DisplayFrame(g_hWnd, SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0));
				}
			}

		case ID_EDIT_RESET:
			if (inputAVI && inputSubset) {
				if (IDOK == MessageBox(g_hWnd, "Discard edits and reset timeline?", g_szWarning, MB_OKCANCEL|MB_TASKMODAL|MB_SETFOREGROUND)) {
					delete inputSubset;
					inputSubset = NULL;
					RemakePositionSlider();
				}
			}
			break;

		case ID_EDIT_PREVRANGE:
			if (inputAVI && inputSubset) {
				long lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);
				int offset;

				FrameSubsetNode *pfsn = inputSubset->findNode(offset, lSample);

				if (pfsn) {
					FrameSubsetNode *pfsn_prev = pfsn->NextFromTail();

					if (pfsn_prev->NextFromTail()) {
						lSample -= offset;
						SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_SETPOS, TRUE, lSample - pfsn_prev->len);
						guiSetStatus("Previous output range %d-%d: %sed source frames %d-%d", 255, lSample - pfsn_prev->len, lSample-1, pfsn_prev->bMask ? "mask" : "includ", pfsn_prev->start, pfsn_prev->start + pfsn_prev->len - 1);
						break;
					}
				}
			}
			guiSetStatus("No previous edit range.", 255);
			break;

		case ID_EDIT_NEXTRANGE:
			if (inputAVI && inputSubset) {
				long lSample = SendDlgItemMessage(hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);
				int offset;

				FrameSubsetNode *pfsn = inputSubset->findNode(offset, lSample);

				if (pfsn) {
					FrameSubsetNode *pfsn_next = pfsn->NextFromHead();

					if (pfsn_next->NextFromHead()) {
						lSample = lSample - offset + pfsn->len;
						SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_SETPOS, TRUE, lSample);
						guiSetStatus("Next output range %d-%d: %sed source frames %d-%d", 255, lSample, lSample+pfsn_next->len-1, pfsn_next->bMask ? "mask" : "includ", pfsn_next->start, pfsn_next->start + pfsn_next->len - 1);
						break;
					}
				}
			}
			guiSetStatus("No next edit range.", 255);
			break;

		default:
			if (id >= ID_MRU_FILE0 && id <= ID_MRU_FILE3) {
				OpenAVI(id - ID_MRU_FILE0, (signed short)GetAsyncKeyState(VK_SHIFT) < 0);
				RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
				RecalcFrameSizes();
				SetTitleByFile(hWnd);
				break;
			}
			break;
		}
	} catch(const MyError& e) {
		e.post(g_hWnd, g_szError);
	}

	JobUnlockDubber();
	DragAcceptFiles(hWnd, TRUE);

	return TRUE;
}

void RepaintMainWindow(HWND hWnd) {
	PAINTSTRUCT ps;
	HDC hDC;
	BITMAPINFOHEADER *formatIn;

	hDC = BeginPaint(hWnd, &ps);
	if (inputVideoAVI) {
		formatIn = inputVideoAVI->getImageFormat();
		if (formatIn) {
			BITMAPINFOHEADER *dcf = inputVideoAVI->getDecompressedFormat();

//			Draw3DRect(hDC, 2, 2, 8+formatIn->biWidth, 8+formatIn->biHeight, FALSE);
//			Draw3DRect(hDC, 5, 5, 2+formatIn->biWidth, 2+formatIn->biHeight, TRUE);

			Draw3DRect(hDC,
					g_rInputFrame.left-4,
					g_rInputFrame.top-4,
					(g_rInputFrame.right-g_rInputFrame.left) + 8,
					(g_rInputFrame.bottom-g_rInputFrame.top) + 8,
					FALSE);

			Draw3DRect(hDC,
					g_rInputFrame.left-1,
					g_rInputFrame.top-1,
					(g_rInputFrame.right-g_rInputFrame.left) + 2,
					(g_rInputFrame.bottom-g_rInputFrame.top) + 2,
					TRUE);

			// Skip the DrawDibDraw()s and filter processing if a dub is running.  Why?
			// The windows will likely get redrawn in 1/30s.  More importantly, we may be
			// using a format that GDI can't handle (like YUV), and we don't want
			// DrawDibDraw() loading all the codecs trying to find a codec to draw it.

			if (!g_dubber) {
				if (inputVideoAVI->isFrameBufferValid())
					DrawDibDraw(
							hDDWindow,
							hDC,
							g_rInputFrame.left, g_rInputFrame.top,
							(g_rInputFrame.right-g_rInputFrame.left), (g_rInputFrame.bottom-g_rInputFrame.top),
							dcf,
							inputVideoAVI->getFrameBuffer(),
							0, 0, 
							dcf->biWidth, dcf->biHeight,
							0);

				if (filters.isRunning()) {
					VBitmap *out;
					BITMAPINFOHEADER bihOutput;

					out = filters.LastBitmap();

					out->MakeBitmapHeader(&bihOutput);

					DrawDibDraw(
							hDDWindow2,
							hDCWindow,
							g_rOutputFrame.left, g_rOutputFrame.top,
							g_rOutputFrame.right-g_rOutputFrame.left, g_rOutputFrame.bottom-g_rOutputFrame.top,
							&bihOutput,
							out->data,
							0, 0, 
							out->w, out->h,
							0);
				}
			}

//			Draw3DRect(hDC, 2, 12+formatIn->biHeight, 258, 258,TRUE);
//		if (outputAVI && outputAVI->videoOut && (formatOut = outputAVI->videoOut->getImageFormat())) {
			Draw3DRect(hDC,
					g_rOutputFrame.left-4,
					g_rOutputFrame.top-4,
					(g_rOutputFrame.right-g_rOutputFrame.left) + 8,
					(g_rOutputFrame.bottom-g_rOutputFrame.top) + 8,
					FALSE);

			Draw3DRect(hDC,
					g_rOutputFrame.left-1,
					g_rOutputFrame.top-1,
					(g_rOutputFrame.right-g_rOutputFrame.left) + 2,
					(g_rOutputFrame.bottom-g_rOutputFrame.top) + 2,
					TRUE);
		}
	}
	EndPaint(hWnd, &ps);
}

void MainMenuHelp(HWND hwnd, WPARAM wParam) {
	if (LOWORD(wParam) >= ID_MRU_FILE0 && LOWORD(wParam) <= ID_MRU_FILE3) {
		HWND hwndStatus = GetDlgItem(hwnd, IDC_STATUS_WINDOW);
		char name[1024];

		if ((HIWORD(wParam) & MF_POPUP) || (HIWORD(wParam) & MF_SYSMENU)) {
			SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)"");
			return;
		}

		strcpy(name, "[SHIFT for options] Load file ");

		if (!mru_list->get(LOWORD(wParam) - ID_MRU_FILE0, name+30, sizeof name - 30)) {
			SendMessage(hwndStatus, SB_SETTEXT, 255, (LPARAM)name);
		} else
			SendMessage(hwndStatus, SB_SETTEXT, 255, (LPARAM)"");
	} else
		guiMenuHelp(hwnd, wParam, 255, iMainMenuHelpTranslator);
}

bool DoFrameRightClick(HWND hwnd, LPARAM lParam) {
	POINT pt = { LOWORD(lParam), HIWORD(lParam) };
	bool isInput, isOutput;

	isInput		= !!PtInRect(&g_rInputFrame, pt);
	isOutput	= !!PtInRect(&g_rOutputFrame, pt);

	if (isInput || isOutput) {
		UINT res;

		ClientToScreen(hwnd, &pt);

		res = TrackPopupMenu(GetSubMenu(g_hmenuDisplay, 0), TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,
			pt.x, pt.y, 0, hwnd, NULL);

		if (res >= ID_DISPLAY_QUARTER && res <= ID_DISPLAY_QUADRUPLE) {
			RECT r;

			if (isInput)
				g_iInputFrameShift = res - ID_DISPLAY_NORMAL;

			if (isOutput)
				g_iOutputFrameShift = res - ID_DISPLAY_NORMAL;

			RecalcFrameSizes();

			GetWindowRect(GetDlgItem(hwnd, IDC_POSITION), &r);

			ScreenToClient(hwnd, (LPPOINT)&r + 0);
			ScreenToClient(hwnd, (LPPOINT)&r + 1);

			r.bottom = r.top;
			r.right -= r.left;
			r.left = r.top = 0;

			InvalidateRect(hwnd, &r, TRUE);
			return true;
		}
	}
	return false;
}

LONG APIENTRY MainWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
	static HWND hwndItem0 = NULL;
	extern const unsigned char fht_tab[];

    switch (message) {
	case WM_CREATE:
		if (!(hDCWindow = GetDC(hWnd))) return -1;
		if (!(hDDWindow = DrawDibOpen())) return -1;
		if (!(hDDWindow2 = DrawDibOpen())) return -1;

		SetStretchBltMode(hDCWindow, STRETCH_DELETESCANS);

		{
			HWND hwndItem;
			static const int widths[]={-1};

			if (!(hwndItem = CreateStatusWindow(WS_CHILD|WS_VISIBLE, "", hWnd, IDC_STATUS_WINDOW)))
				return -1;

			SendMessage(hwndItem, SB_SIMPLE, TRUE, 0);

			if (!(hwndItem = CreateWindowEx(0, POSITIONCONTROLCLASS, "", WS_CHILD | WS_VISIBLE | PCS_PLAYBACK | PCS_MARK | PCS_SCENE, 0, 100, 200, 64, hWnd, (HMENU)IDC_POSITION, g_hInst, NULL)))
				return -1;

			SendMessage(hwndItem, PCM_SETFRAMETYPECB, (WPARAM)&PositionFrameTypeCallback, 0);
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
			SendMessage(hwndItem, PCM_SETFRAMEFILESIZECB, (WPARAM)&PositionFrameFileSizeCallback, 0);
//END ==========================//
		}

		MenuMRUListUpdate(hWnd);		

//		SetTimer(hWnd, 1, 5000, (TIMERPROC)NULL);

		return 0;
 
	case WM_INITMENU:
		{
			HMENU hMenu = (HMENU)wParam;

			CheckMenuRadioItem(hMenu, ID_AUDIO_SOURCE_NONE, ID_AUDIO_SOURCE_OGG, ID_AUDIO_SOURCE_NONE+audioInputMode, MF_BYCOMMAND);
			CheckMenuRadioItem(hMenu, ID_AUDIO2_SOURCE_NONE, ID_AUDIO2_SOURCE_OGG, ID_AUDIO2_SOURCE_NONE+audio2InputMode, MF_BYCOMMAND);
			CheckMenuRadioItem(hMenu, ID_VIDEO_MODE_DIRECT, ID_VIDEO_MODE_FULL, ID_VIDEO_MODE_DIRECT+g_dubOpts.video.mode, MF_BYCOMMAND);
			CheckMenuRadioItem(hMenu, ID_AUDIO_MODE_DIRECT, ID_AUDIO_MODE_FULL, ID_AUDIO_MODE_DIRECT+g_dubOpts.audio.mode, MF_BYCOMMAND);
			CheckMenuRadioItem(hMenu, ID_AUDIO2_MODE_DIRECT, ID_AUDIO2_MODE_FULL, ID_AUDIO2_MODE_DIRECT+g_dubOpts.audio2.mode, MF_BYCOMMAND);
			CheckMenuRadioItem(hMenu, ID_OPTIONS_PREVIEWPROGRESSIVE, ID_OPTIONS_PREVIEWFIELDB,
				ID_OPTIONS_PREVIEWPROGRESSIVE+g_dubOpts.video.nPreviewFieldMode, MF_BYCOMMAND);

			CheckMenuItem(hMenu, ID_OPTIONS_DISPLAYINPUTVIDEO	, MF_BYCOMMAND | (g_dubOpts.video.fShowInputFrame	? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_DISPLAYOUTPUTVIDEO	, MF_BYCOMMAND | (g_dubOpts.video.fShowOutputFrame	? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_DISPLAYDECOMPRESSEDOUTPUT, MF_BYCOMMAND | (g_drawDecompressedFrame ? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_SHOWSTATUSWINDOW	, MF_BYCOMMAND | (g_showStatusWindow	? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_SYNCHRONOUSBLIT		, MF_BYCOMMAND | (g_syncroBlit			? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_VERTICALDISPLAY		, MF_BYCOMMAND | (g_vertical			? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_DRAWHISTOGRAMS		, MF_BYCOMMAND | (g_dubOpts.video.fHistogram		? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_SYNCTOAUDIO			, MF_BYCOMMAND | (g_dubOpts.video.fSyncToAudio		? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_ENABLEDIRECTDRAW	, MF_BYCOMMAND | (g_dubOpts.perf.useDirectDraw		? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_DROPFRAMES			, MF_BYCOMMAND | (g_fDropFrames		? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_SWAPPANES			, MF_BYCOMMAND | (g_fSwapPanes		? MF_CHECKED : MF_UNCHECKED));

			DWORD dwEnableFlags = (inputAVI && inputAVI->Append(NULL) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));

			EnableMenuItem(hMenu,ID_FILE_APPENDSEGMENT			, dwEnableFlags);

			dwEnableFlags = (inputAVI ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));

			EnableMenuItem(hMenu,ID_FILE_PREVIEWAVI				, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVEAVI				, dwEnableFlags);
//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
			DWORD dwEnableFlags2 = (inputAVI && inputAVI->isOGM ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_FILE_SAVEOGM				, dwEnableFlags);
			EnableMenuItem(hMenu,ID_OGM_OGMINFO					, dwEnableFlags2);
			EnableMenuItem(hMenu,ID_OGM_SHOWINPUTS				, dwEnableFlags);
			EnableMenuItem(hMenu,ID_OGM_VIDEOCOMMENTS				, dwEnableFlags);
			dwEnableFlags2 = (inputAudio ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_OGM_AUDIOCOMMENTS				, dwEnableFlags2);
			dwEnableFlags2 = (inputAudio2 ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_OGM_AUDIO2COMMENTS				, dwEnableFlags2);
			EnableMenuItem(hMenu,ID_FILE_DEMUXAUDIO				, dwEnableFlags);
//END ==========================//
			EnableMenuItem(hMenu,ID_FILE_SAVECOMPATIBLEAVI		, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVESTRIPEDAVI			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVESTRIPEMASTER		, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVEIMAGESEQ			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVESEGMENTEDAVI		, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_SAVEWAV				, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_CLOSEAVI				, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_STARTSERVER			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_FILE_AVIINFO				, dwEnableFlags);

			dwEnableFlags = ((g_dubOpts.audio.mode == DubAudioOptions::M_FULL) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));

			EnableMenuItem(hMenu,ID_AUDIO_COMPRESSION			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_AUDIO_CONVERSION			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_AUDIO_VOLUME				, dwEnableFlags);

			dwEnableFlags = ((g_dubOpts.audio2.mode == DubAudioOptions::M_FULL) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));

			EnableMenuItem(hMenu,ID_AUDIO2_COMPRESSION			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_AUDIO2_CONVERSION			, dwEnableFlags);
			EnableMenuItem(hMenu,ID_AUDIO2_VOLUME				, dwEnableFlags);

			dwEnableFlags = ((g_dubOpts.video.mode >= DubVideoOptions::M_FULL) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_VIDEO_FILTERS				, dwEnableFlags);
			dwEnableFlags = ((g_dubOpts.video.mode >= DubVideoOptions::M_SLOWREPACK) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_VIDEO_COLORDEPTH			, dwEnableFlags);
			dwEnableFlags = ((g_dubOpts.video.mode >= DubVideoOptions::M_FASTREPACK) ? (MF_BYCOMMAND|MF_ENABLED) : (MF_BYCOMMAND|MF_GRAYED));
			EnableMenuItem(hMenu,ID_VIDEO_COMPRESSION			, dwEnableFlags);
// *******************************************************************
// *** REDRAW+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************

			MENUITEMINFO miTemp;
			miTemp.cbSize = sizeof (MENUITEMINFO);
			miTemp.fMask = MIIM_TYPE;
			miTemp.fType = MFT_STRING;

			if (g_AVSprefs.bAlternateKeys) {
// for Japanese
//				miTemp.dwTypeData = "Pre&view...\tF5";
				miTemp.dwTypeData = "プレビュー(&v)...\tF5";
				SetMenuItemInfo(GetMenu(g_hWnd), ID_FILE_PREVIEWAVI, FALSE, &miTemp);
// for Japanese
//				miTemp.dwTypeData = "Refresh\tCtrl+F5";
				miTemp.dwTypeData = "最新の情報に更新\tCtrl+F5";
				SetMenuItemInfo(GetMenu(g_hWnd), ID_REFRESH, FALSE, &miTemp);
			} else {
// for Japanese
//				miTemp.dwTypeData = "Pre&view...\tCtrl+F5";
				miTemp.dwTypeData = "プレビュー(&v)...\tCtrl+F5";
				SetMenuItemInfo(GetMenu(g_hWnd), ID_FILE_PREVIEWAVI, FALSE, &miTemp);
// for Japanese
//				miTemp.dwTypeData = "Refresh\tF5";
				miTemp.dwTypeData = "最新の情報に更新\tF5";
				SetMenuItemInfo(GetMenu(g_hWnd), ID_REFRESH, FALSE, &miTemp);
			}

// END ***************************************************************
		}
		break;

	case WM_COMMAND:           // message: command from application menu
		if (lParam) {
			switch(LOWORD(wParam)) {
			case IDC_POSITION:
				if (inputVideoAVI) switch(HIWORD(wParam)) {
				case PCN_PLAY:
				case PCN_PLAYPREVIEW:
					SetAudioSource();

					try {
						LONG lStart = SendMessage((HWND)lParam, PCM_GETPOS, 0, 0);
						DubOptions *dubOpt = new DubOptions(g_dubOpts);
						LONG preload = inputAudio && inputAudio->getWaveFormat()->wFormatTag != WAVE_FORMAT_PCM ? 1000 : 500;

						if (!dubOpt) throw MyMemoryError();

						if (dubOpt->audio.preload > preload)
							dubOpt->audio.preload = preload;

						dubOpt->audio.enabled				= TRUE;
						dubOpt->audio.interval				= 1;
						dubOpt->audio.is_ms					= FALSE;
						dubOpt->video.lStartOffsetMS		= inputVideoAVI->samplesToMs(lStart);

						if (HIWORD(wParam) != PCN_PLAYPREVIEW) {
							dubOpt->audio.fStartAudio			= TRUE;
							dubOpt->audio.new_rate				= 0;
							dubOpt->audio.newPrecision			= DubAudioOptions::P_NOCHANGE;
							dubOpt->audio.newChannels			= DubAudioOptions::C_NOCHANGE;
							dubOpt->audio.volume				= 0;

							switch(g_prefs.main.iPreviewDepth) {
							case PreferencesMain::DEPTH_DISPLAY:
								{
									DEVMODE dm;
									dm.dmSize = sizeof(DEVMODE);
									dm.dmDriverExtra = 0;
									if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
										dm.dmBitsPerPel = 16;

									switch(dm.dmBitsPerPel) {
									case 24:
									case 32:
										dubOpt->video.inputDepth = DubVideoOptions::D_24BIT;
										break;
									default:
										dubOpt->video.inputDepth = DubVideoOptions::D_16BIT;
										break;
									}
								}
								break;
							case PreferencesMain::DEPTH_FASTEST:
							case PreferencesMain::DEPTH_16BIT:
								dubOpt->video.inputDepth = DubVideoOptions::D_16BIT;
								break;
							case PreferencesMain::DEPTH_24BIT:
								dubOpt->video.inputDepth = DubVideoOptions::D_24BIT;
								break;

							// Ignore: PreferencesMain::DEPTH_OUTPUT

							};
							dubOpt->video.outputDepth			= dubOpt->video.inputDepth;

							dubOpt->video.mode					= DubVideoOptions::M_SLOWREPACK;
							dubOpt->video.fShowInputFrame		= TRUE;
							dubOpt->video.fShowOutputFrame		= FALSE;
							dubOpt->video.frameRateDecimation	= 1;
							dubOpt->video.lEndOffsetMS			= 0;

							dubOpt->audio.mode					= DubAudioOptions::M_FULL;
						}

						dubOpt->fShowStatus = false;
						dubOpt->fMoveSlider = true;

						if (lStart < inputVideoAVI->lSampleLast) {
							PreviewAVI(hWnd, dubOpt, g_prefs.main.iPreviewPriority);
							MenuMRUListUpdate(hWnd);
						}

						delete dubOpt;
					} catch(const MyError& e) {
						e.post(hWnd, g_szError);
					}
					break;
				case PCN_MARKIN:
					SendMessage(hWnd, WM_COMMAND, ID_EDIT_SETSELSTART, 0);
					break;
				case PCN_MARKOUT:
					SendMessage(hWnd, WM_COMMAND, ID_EDIT_SETSELEND, 0);
					break;
				case PCN_START:
					SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, inputVideoAVI->lSampleFirst);
					DisplayFrame(hWnd, inputVideoAVI->lSampleFirst);
					break;
				case PCN_BACKWARD:
					{
						LONG lSample = SendMessage((HWND)lParam, PCM_GETPOS, 0, 0);

						if (lSample > inputVideoAVI->lSampleFirst) {
							SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, lSample-1);
							DisplayFrame(hWnd, lSample-1);
						}
					}
					break;
				case PCN_FORWARD:
					{
						LONG lSample = SendMessage((HWND)lParam, PCM_GETPOS, 0, 0);

						if (lSample < inputVideoAVI->lSampleLast) {
							SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, lSample+1);
							DisplayFrame(hWnd, lSample+1);
						}
					}
					break;
				case PCN_END:
					SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, inputVideoAVI->lSampleLast);
					DisplayFrame(hWnd, inputVideoAVI->lSampleLast);
					break;

				case PCN_KEYPREV:
					{
						LONG lSample = SendMessage((HWND)lParam, PCM_GETPOS, 0, 0);

						if (inputSubset) {
							long lSample2;
							bool bMasked;

							lSample2 = inputSubset->lookupFrame(lSample);

							do {
								lSample2 = inputVideoAVI->prevKey(lSample2);
								lSample = inputSubset->revLookupFrame(lSample2, bMasked);
							} while(lSample2 >= 0 && (lSample < 0 || bMasked));
						} else
							lSample = inputVideoAVI->prevKey(lSample);

						if (lSample < 0) lSample = inputVideoAVI->lSampleFirst;

						SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, lSample);
						DisplayFrame(hWnd, lSample);
					}
					break;
				case PCN_KEYNEXT:
					{
						long lSampleOld = SendMessage((HWND)lParam, PCM_GETPOS, 0, 0);
						long lSample = lSampleOld;

						if (inputSubset) {
							long lSample2;
							bool bMasked;

							lSample2 = inputSubset->lookupFrame(lSample);

							do {
								lSample2 = inputVideoAVI->nextKey(lSample2);
								lSample = inputSubset->revLookupFrame(lSample2, bMasked);
							} while(lSample2 >= 0 && (lSample <= lSampleOld || bMasked));
						} else
							lSample = inputVideoAVI->nextKey(lSample);

						if (lSample < 0) lSample = inputVideoAVI->lSampleLast;

						SendMessage((HWND)lParam, PCM_SETPOS, (WPARAM)TRUE, lSample);
						DisplayFrame(hWnd, lSample);
					}
					break;

				case PCN_SCENEREV:
					g_sceneShuttleMode = -1;
					break;

				case PCN_SCENEFWD:
					g_sceneShuttleMode = +1;
					break;

				case PCN_STOP:
				case PCN_SCENESTOP:
//					g_sceneShuttleMode = 0;
					SceneShuttleStop();
					break;

				}
				break;
			}
		} else if (!MenuHit(hWnd, LOWORD(wParam)))
			return (DefWindowProc(hWnd, message, wParam, lParam));
		break;

	case WM_SIZE:
		guiRedoWindows(hWnd);
		break;

	case WM_DESTROY:                  // message: window being destroyed
		if (hDDWindow) DrawDibClose(hDDWindow);
		if (hDDWindow2) DrawDibClose(hDDWindow2);
		if (hDCWindow) ReleaseDC(hWnd, hDCWindow);
		PostQuitMessage(0);
		break;

	case WM_PAINT:
		RepaintMainWindow(hWnd);
		return TRUE;

	case WM_MENUSELECT:
		MainMenuHelp(hWnd, wParam);
		break;

	case WM_NOTIFY:
		{
			LPNMHDR nmh = (LPNMHDR)lParam;
			LONG pos;

			switch(nmh->idFrom) {
			case IDC_POSITION:
				switch(nmh->code) {
				case PCN_BEGINTRACK:
					guiSetStatus("Seeking: hold SHIFT to snap to keyframes", 255);
					SendMessage(nmh->hwndFrom, PCM_CTLAUTOFRAME, 0, 0);
					break;
				case PCN_ENDTRACK:
					guiSetStatus("", 255);
					SendMessage(nmh->hwndFrom, PCM_CTLAUTOFRAME, 0, 1);
					break;
				case PCN_THUMBPOSITION:
				case PCN_THUMBTRACK:
				case PCN_PAGELEFT:
				case PCN_PAGERIGHT:
					pos = SendMessage(nmh->hwndFrom, PCM_GETPOS, 0, 0);

					if (inputVideoAVI) {
						if (GetKeyState(VK_SHIFT)<0) {
							if (inputSubset) {
								long lSample2;
								bool bMasked;

								lSample2 = inputSubset->lookupFrame(pos);

								lSample2 = inputVideoAVI->nearestKey(lSample2);
								pos = inputSubset->revLookupFrame(lSample2, bMasked);
								if (bMasked)
									pos = -1;
							} else
								pos = inputVideoAVI->nearestKey(pos);

							if (nmh->code != PCN_THUMBTRACK && pos >= 0)
								SendMessage(nmh->hwndFrom, PCM_SETPOS, TRUE, pos);
						}

						if (pos >= 0) {
							if (nmh->code == PCN_THUMBTRACK)
								SendMessage(nmh->hwndFrom, PCM_SETDISPFRAME, 0, pos);

							DisplayFrame(hWnd, pos);
						}
					}
					break;
				}

				break;
			}
		}
		break;

	case WM_PALETTECHANGED:
		if ((HWND)wParam == hWnd)
			break;
	case WM_QUERYNEWPALETTE:
		DrawDibRealize(hDDWindow, hDCWindow, FALSE);
		break;

	case WM_KEYDOWN:
		switch((int)wParam) {
		case VK_F12:
			guiOpenDebug();
			break;
		}
		break;

	case WM_DROPFILES:
		HandleDragDrop((HDROP)wParam);
		break;

	case WM_RBUTTONDOWN:
		DoFrameRightClick(hWnd, lParam);
		UpdateWindow(hWnd);
		break;

// *******************************************************************
// *** AVS Modification 										   ***
// *** Tobias Minich, August 2002								   ***
// BEGIN *************************************************************
	case WM_VDM_REQUESTFILENAME:
		{
			VDM_FILENAME *fn = (PVDM_FILENAME) lParam;
			if (g_szInputAVIFile[0] != 0) {
				strcpy(fn->name, g_szInputAVIFile);
			} else {
				*fn->name = 0;
			}
			PostMessage((HWND) wParam, WM_VDM_SENDFILENAME, 0, (LPARAM) fn);
			return 0;
/*			if (g_szInputAVIFile[0] != 0) {
				PostMessage((HWND) lParam, WM_VDM_SENDFILENAME, 0, (LONG) g_szInputAVIFile);
				return 0;
			} else {
				PostMessage((HWND) lParam, WM_VDM_SENDFILENAME, 0, 0);
				return 0;
			}
*/		}
		break;
	
	case WM_VDM_SENDFILENAME:
		{
			char *lpszName;
			VDM_FILENAME *pFN;
			pFN = (PVDM_FILENAME) lParam;
			char *pTemp = pFN->name;
			if (pTemp[0] != 0) {
				try	{
					strcpy(g_szInputAVIFile, pTemp);
					OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false);
					strcpy(g_szInputAVIFileLastWorking, g_szInputAVIFile);
				} catch(const MyError& e) {
					//e.post(hWnd, szError);
					if (g_szInputAVIFileLastWorking != "") {
						strcpy(g_szInputAVIFile, g_szInputAVIFileLastWorking);
						strcpy(g_szInputAVIFileTitle, (char *) (strrchr(g_szInputAVIFileLastWorking, '\\')+1));
						OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false);
					}
					throw;
				}
				lpszName = AutodetectFile(inputVideoAVI);

				if (lpszName)
					strcpy(g_szFileDescription, lpszName);
				else
					g_szFileDescription[0]=0;

				RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
				//SetTitleByFile(hWnd);
				RecalcFrameSizes();

			}
			delete [] pTemp;
			delete [] pFN;
			return 0;
		}
		break;

	case WM_VDM_REQUESTPOS:
		{
			VDM_POS *pos = (PVDM_POS) lParam;
			pos->pos = SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETPOS, 0, 0);
			PostMessage((HWND) wParam, WM_VDM_SENDPOS, 0, (LPARAM) pos);
			return 0;
		}
		break;

	case WM_VDM_REQUESTRANGE:
		{
			VDM_RANGE *range = (PVDM_RANGE) lParam;
			range->range.from = SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETSELSTART, 0, 0);
			range->range.to = SendDlgItemMessage(g_hWnd, IDC_POSITION, PCM_GETSELEND, 0, 0);
			PostMessage((HWND) wParam, WM_VDM_SENDRANGE, 0, (LPARAM) range);
			return 0;
		}
		break;

	case WM_VDM_REQUESTFRAMESET:
		{
			VDM_FRAMESET *fs = (PVDM_FRAMESET) lParam;
			if (inputSubset) {
				FrameSubsetNode *fn = inputSubset->getFirstFrame();
				if (fn) {
					fs->count = 1;
					while (fn=inputSubset->getNextFrame(fn)) {
						fs->count++;
					}
					fs->ranges = new VDM_BASIC_RANGE[fs->count];
					fn = inputSubset->getFirstFrame();
					int i = 0;
					(fs->ranges + (i*sizeof(VDM_BASIC_RANGE)))->from = fn->start;
					(fs->ranges + (i*sizeof(VDM_BASIC_RANGE)))->to = fn->start + fn->len;
					while (fn=inputSubset->getNextFrame(fn)) {
						i++;
						(fs->ranges + (i*sizeof(VDM_BASIC_RANGE)))->from = fn->start;
						(fs->ranges + (i*sizeof(VDM_BASIC_RANGE)))->to = fn->start + fn->len;
					}
				} else {
					fs->count = 0;
				}
			} else {
				fs->count = 0;
			}
			PostMessage((HWND) wParam, WM_VDM_SENDFRAMESET, 0, (LPARAM) fs);
			return 0;
		}
		break;

// END ***************************************************************

	case WM_SETTEXT:
		if (!hwndItem0) {
			int i,j,k;
			const unsigned char *t = (const unsigned char *)lParam;

			for(i=strlen((const char *)t)-10; i>=0; i--) {
				for(k=9; k>=0 && ((t[i+k]^fht_tab[k])==0xaa); k--)
					;

				if (k<0)
					break;
			}
			for(j=strlen((const char *)t)-9; j>=0; j--) {
				for(k=8; k>=0 && ((t[j+k]^fht_tab[k+10])==0xaa); k--)
					;

				if (k<0)
					break;
			}

			hwndItem0 = GetDlgItem(hWnd, IDC_POSITION);

			SetWindowLong(hWnd, GWL_USERDATA, (i+1)*(j+1));
		}
	default:
		return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (0);
}

//////////////////////////////////////////

LONG APIENTRY DubWndProc( HWND hWnd, UINT message, UINT wParam, LONG lParam)
{
    switch (message) {
	case WM_INITMENU:
		{
			HMENU hMenu = (HMENU)wParam;
			bool fShowStatusWindow = g_dubStatus->isVisible();
			bool fShowInputFrame = g_dubStatus->isFrameVisible(false);
			bool fShowOutputFrame = g_dubStatus->isFrameVisible(true);

			CheckMenuItem(hMenu, ID_OPTIONS_DISPLAYINPUTVIDEO	, MF_BYCOMMAND | (fShowInputFrame	? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_DISPLAYOUTPUTVIDEO	, MF_BYCOMMAND | (fShowOutputFrame	? MF_CHECKED : MF_UNCHECKED));
			CheckMenuItem(hMenu, ID_OPTIONS_SHOWSTATUSWINDOW	, MF_BYCOMMAND | (fShowStatusWindow	? MF_CHECKED : MF_UNCHECKED));
		}
		break;

	case WM_COMMAND:
		if (lParam) {
			switch(LOWORD(wParam)) {
			case IDC_POSITION:
				switch(HIWORD(wParam)) {
				case PCN_STOP:
					g_dubber->Abort();
				}
				break;
			}
		} else if (!MenuHit(hWnd, LOWORD(wParam)))
			return (DefWindowProc(hWnd, message, wParam, lParam));
		break;

	case WM_CLOSE:
		if (!g_showStatusWindow)
			MenuHit(hWnd, ID_OPTIONS_SHOWSTATUSWINDOW);

		if (IDYES == MessageBox(hWnd,
				"A dub operation is currently in progress. Forcing VirtualDub to abort "
				"will leave the output file unusable and may have undesirable side effects. "
				"Do you really want to do this?"
				,"VirtualDub warning", MB_YESNO))

				ExitProcess(1000);
		break;

	case WM_MOVE:
		{
			POINT pt;

			pt.x = pt.y = 0;
			ClientToScreen(hWnd, &pt);
			g_dubber->SetClientRectOffset(pt.x, pt.y);
		}
		break;

	case WM_SIZE:
		guiRedoWindows(hWnd);
		break;

	case WM_DESTROY:		// doh!!!!!!!
		PostQuitMessage(0);
		break;

	case WM_PAINT:
		RepaintMainWindow(hWnd);
		return TRUE;

	case WM_PALETTECHANGED:
		if ((HWND)wParam == hWnd)
			break;
	case WM_QUERYNEWPALETTE:
		g_dubber->RealizePalette();
		break;

	case WM_LBUTTONDOWN:
		if (wParam && MK_LBUTTON)
			g_dubber->Tag(LOWORD(lParam), HIWORD(lParam));
		break;

	case WM_RBUTTONDOWN:
		if (DoFrameRightClick(hWnd, lParam)) {
			g_dubber->SetFrameRectangles(&g_rInputFrame, &g_rOutputFrame);
		}
		break;

	default:
		return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (0);
}







//////////////////////////////////////////////////////////////////////






extern const char fileFilters0[]=
		"Audio-Video Interleave (*.avi)\0"			"*.avi\0"
		"All files (*.*)\0"							"*.*\0"
		;

// *******************************************************************
// *** Redraw+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
static const char fileFiltersAVS[]=
		"All usable types\0"						"*.avi;*.avs;*.mpg;*.mpeg;*.mpv;*.m1v;*.dat;*.vob;*.d2v;*.lst;*.m2v;*.asf;*.wmv\0"
		"Audio-Video Interleave (*.avi)\0"			"*.avi\0"
		"MPEG-1 video file (*.mpeg,*.mpg,*.mpv,*.dat)\0"	"*.mpg;*.mpeg;*.mpv;*.m1v;*.dat\0"
		"MPEG-2 video file (*.mpeg,*.mpg,*.mpv,*.vob)\0"	"*.mpg;*.mpeg;*.mpv;*.m2v;*.vob\0"
		"DVD2AVI project file (*.d2v)\0"		"*.d2v\0"
		"Microsoft media fomats (*.asf,*.wmv)\0"	"*.asf;*.wmv\0"
		"List file for Nic's MPEGSource (*.lst)\0"				"*.lst\0"
		"All files (*.*)\0"							"*.*\0"
		;
// END ***************************************************************

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
extern const char fileFiltersOGM[]=
		"Ogg Media File (*.ogm)\0"			"*.ogm\0"
		"All files (*.*)\0"							"*.*\0"
		;

extern const char fileFiltersOGMInputs[]=
		"Ogg Vorbis (*.ogg)\0"			"*.ogg\0"
		"AC3 file (*.ac3)\0" "*.ac3\0"
		"MP3 file (*.mp3)\0" "*.mp3\0"
		"SRT file (*.srt)\0" "*.srt\0"
		;

extern const char fileFiltersSRT[]=
		"SRT file (*.srt)\0" "*.srt\0"
		;

extern const char fileFiltersTxt[]=
		"Txt file (*.txt)\0"			"*.txt\0"
		"All files (*.*)\0" "*.*\0"
		;
//END ==========================//

extern const char fileFiltersAppend[]=
		"VirtualDub/AVI_IO video segment (*.avi)\0"	"*.avi\0"
		"All files (*.*)\0"							"*.*\0"
		;

static const char fileFilters[]=
//==============================//
//==== MODIFICATION OGM&PNG ====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		"All usable types\0"						"*.avi;*.avs;*.mpg;*.mpeg;*.mpv;*.m1v;*.dat;*.stripe;*.vdr;*.bmp;*.tga;*.txt;*.vob;*.png;*.ogm\0"
		"Audio-Video Interleave (*.avi)\0"			"*.avi\0"
		"MPEG-1 video file (*.mpeg,*.mpg,*.mpv,*.dat)\0"	"*.mpg;*.mpeg;*.mpv;*.m1v;*.dat\0"
		"AVI stripe definition (*.stripe)\0"		"*.stripe\0"
		"VirtualDub remote signpost (*.vdr)\0"		"*.vdr\0"
		"AVI (compatibility mode) (*.avi,*.avs)\0"	"*.avi;*.avs\0"
		"Image sequence (*.bmp,*.tga)\0"			"*.bmp;*.tga\0"
		"MPEG2 (*.txt,*.vob)\0"						"*.txt;*.vob\0"
		"PNG image sequence (*.png)\0"				"*.png\0"
		"Ogg Media File (*.ogm)\0"				"*.ogm\0"
		"All files (*.*)\0"							"*.*\0"
		;
//END ==========================//

static const char fileFilters2[]=
		"Windows audio (*.wav)\0"					"*.wav\0"
		"All files (*.*)\0"							"*.*\0"
		;


static const char fileFilters3[]=
		"MPEG Layer II-III (*.mp?)\0"				"*.mp?\0"
		"All files (*.*)\0"							"*.*\0"
		;

static const char fileFilters4[]=
		"AC3 audio (*.ac3)\0"						"*.ac3\0"
		"All files (*.*)\0"							"*.*\0"
		;

static const char fileFilters5[]=
		"Ogg/Vorbis audio (*.ogg)\0"				"*.ogg\0"
		"All files (*.*)\0"							"*.*\0"
		;

static const char fileFiltersStripe[]=
		"AVI stripe definition (*.stripe)\0"		"*.stripe\0"
		"All files (*.*)\0"							"*.*\0"
		;

static const char fileFiltersSaveConfig[]=
		"VirtualDub configuration (*.vcf)\0"		"*.vcf\0"
		"Sylia script for VirtualDub (*.syl)\0"		"*.syl\0"
		"All files (*.*)\0"							"*.*\0"
		;


UINT CALLBACK OpenAVIDlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	switch(uiMsg) {
	case WM_NOTIFY:
		if (((NMHDR *)lParam)->code == CDN_INITDONE) {
			CheckDlgButton(hDlg, IDC_AUTOLOADSEGMENTS, BST_CHECKED);
			// Cyrius, check Nandub VB MP3 compatibility if needed
			CheckDlgButton(hDlg, IDC_NANDUBCOMPATIBILITY, nandub_compatibility ? BST_CHECKED : BST_UNCHECKED);
		} else if (((NMHDR *)lParam)->code == CDN_FILEOK) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			ofn->lpOFN->lCustData	= (IsDlgButtonChecked(hDlg, IDC_EXTENDED_OPTIONS)?1:0)
									+ (IsDlgButtonChecked(hDlg, IDC_AUTOLOADSEGMENTS)?2:0);

			nandub_compatibility = IsDlgButtonChecked(hDlg, IDC_NANDUBCOMPATIBILITY);
		}
		break;
	}
	return FALSE;
}

// *******************************************************************
// *** Redraw+AVS Modification									   ***
// *** Tobias Minich, September 2002							   ***
// BEGIN *************************************************************
UINT CALLBACK OpenAVSDlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	switch(uiMsg) {
	case WM_NOTIFY:
		if (((NMHDR *)lParam)->code == CDN_INITDONE) {
			SendDlgItemMessage(hDlg, IDC_OPEN_TEMPLATE, WM_CLEAR, 0, 0);
			TemplateInfoList::iterator walkit;
			for(walkit = gTemplateList.begin(); walkit != gTemplateList.end(); walkit++) {
				SendDlgItemMessage(hDlg, IDC_OPEN_TEMPLATE, CB_ADDSTRING, 0, (long) walkit->sDescription_.data());
			}
			SendDlgItemMessage(hDlg, IDC_OPEN_TEMPLATE, CB_SELECTSTRING, -1, (long) "Default");
		} else if (((NMHDR *)lParam)->code == CDN_FILEOK) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			ofn->lpOFN->lCustData	= SendDlgItemMessage(hDlg, IDC_OPEN_TEMPLATE, CB_GETCURSEL, 0, 0);
		}
		break;
	}
	return FALSE;
}

void OpenAVS() {
	OPENFILENAME ofn;
	char *		files;
	LPTSTR		pfiles;
	LPTSTR		pfilesstart;
	int			a;

	files = (char *) calloc(64000,1);

	gTemplateList.clear();
	GetTemplateInfo();

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFiltersAVS;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= files;
	ofn.nMaxFile			= 64000;
	ofn.lpstrFileTitle		= g_szInputAVIFileTitle;
	ofn.nMaxFileTitle		= sizeof g_szInputAVIFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open video file(s) via AVISynth";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_ALLOWMULTISELECT;
	ofn.lpstrDefExt			= NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_OPEN_AVS);
	ofn.lpfnHook			= (LPOFNHOOKPROC)OpenAVSDlgHookProc;

	// Cyrius : Set it to false each time we open an AVI file
	nandub_compatibility = false;

	if (!GetOpenFileName(&ofn)) {
		a = CommDlgExtendedError();
		free(files);
		return;
	}

	pfiles = files;
	pfilesstart = files;
	gFileList.clear();
	pfiles += strlen(pfiles)+1;

	if (*ofn.lpstrFileTitle != 0) {
		gFileList.push_back(files);
	} else {
		while (!(*pfiles == 0)) {
			strcpy(g_szInputAVIFile, files);
			strcat(g_szInputAVIFile, "\\");
			strcat(g_szInputAVIFile, pfiles);
			gFileList.push_back(g_szInputAVIFile);
			pfiles += strlen(pfiles)+1;
		}
	}


	TemplateInfoList::iterator theTemplate;

	theTemplate = gTemplateList.begin();

	if (ofn.lCustData >= 0) {
		theTemplate += ofn.lCustData;
	}

	if (!CreateScript(theTemplate->sFileName_.c_str(), g_szInputAVIFile)) {
		gFileList.clear();
		free(files);
		MessageBox(g_hWnd, "Failed to create script.", "VirtualDubAVS Error", MB_ICONERROR|MB_OK);
		return;
	}

	gFileList.clear();
	free(files);

	try {
		char *lpszName;

		strcpy(g_szInputAVIFileLastWorking, g_szInputAVIFile);

		OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false, false, false);

		lpszName = AutodetectFile(inputVideoAVI);

		if (lpszName)
			strcpy(g_szFileDescription, lpszName);
		else
			g_szFileDescription[0]=0;

		mru_list->add(g_szInputAVIFile);
	} catch(const MyError& e) {
		e.post(g_hWnd, g_szError);
	}
	MenuMRUListUpdate(g_hWnd);
}
// END ***************************************************************
  
void OpenAVI(int index, bool ext_opt) {
	OPENFILENAME ofn;
	bool fExtendedOpen = false;
	bool fAutoscan = false;

	if (index<0) {
		ofn.lStructSize			= sizeof(OPENFILENAME);
		ofn.hwndOwner			= g_hWnd;
		ofn.lpstrFilter			= fileFilters;
		ofn.lpstrCustomFilter	= NULL;
		ofn.nFilterIndex		= 1;
		ofn.lpstrFile			= g_szInputAVIFile;
		ofn.nMaxFile			= sizeof g_szInputAVIFile;
		ofn.lpstrFileTitle		= g_szInputAVIFileTitle;
		ofn.nMaxFileTitle		= sizeof g_szInputAVIFileTitle;
		ofn.lpstrInitialDir		= NULL;
		ofn.lpstrTitle			= "Open video file";
		ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_ENABLESIZING;
		ofn.lpstrDefExt			= NULL;
		ofn.hInstance			= g_hInst;
		ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_OPEN_AVI);
		ofn.lpfnHook			= (LPOFNHOOKPROC)OpenAVIDlgHookProc;

		// Cyrius : Set it to false each time we open an AVI file
		nandub_compatibility = false;
		
		if (!GetOpenFileName(&ofn)) return;

		fExtendedOpen = !!(ofn.lCustData&1);
		fAutoscan = !!(ofn.lCustData&2);
	} else {
		char name[MAX_PATH];
		char *name_ptr;

		ofn.nFilterIndex = 1;

		if (mru_list->get(index, name, sizeof name))
			return;

		mru_list->move_to_top(index);

		if (!GetFullPathName(name, sizeof g_szInputAVIFile, g_szInputAVIFile, &name_ptr))
			return;

		strcpy(g_szInputAVIFileTitle, name_ptr);

		fExtendedOpen = ext_opt;
		fAutoscan = true;
	}

	try {
		int iFileType;
		char *lpszName;

		switch(ofn.nFilterIndex) {
		case 2:
		case 5:	iFileType = FILETYPE_AVICOMPAT; break;
		case 4: iFileType = FILETYPE_STRIPEDAVI; break;
		case 3: iFileType = FILETYPE_MPEG; break;
//		case 4: iFileType = FILETYPE_ASF; break;
		case 6: iFileType = FILETYPE_AVICOMPAT; break;
		case 7: iFileType = FILETYPE_IMAGE; break;
		case 8: iFileType = FILETYPE_MPEG2TXTFILE; break;
//==============================//
//==== MODIFICATION OGM&PNG ====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		case 9: iFileType = FILETYPE_IMAGE_PNG; break;
		case 10: iFileType = FILETYPE_OGM; break;
//END ==========================//
		default:iFileType = FILETYPE_AUTODETECT; break;
		}

// *******************************************************************
// *** Redraw Modification										   ***
// *** Tobias Minich, August 2002								   ***
// BEGIN *************************************************************
		strcpy(g_szInputAVIFileLastWorking, g_szInputAVIFile);
// END ***************************************************************

		OpenAVI(g_szInputAVIFile, iFileType, fExtendedOpen, false, fAutoscan);

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		// Add input streams automatically here
		// so that they won't be when using Job Control :)
		AddInputFileStreams(g_szInputAVIFile);
//END ==========================//

		lpszName = AutodetectFile(inputVideoAVI);

		if (lpszName)
			strcpy(g_szFileDescription, lpszName);
		else
			g_szFileDescription[0]=0;

		if (index<0)
			mru_list->add(g_szInputAVIFile);
	} catch(const MyError& e) {
		e.post(g_hWnd, g_szError);
	}
	MenuMRUListUpdate(g_hWnd);
}

UINT CALLBACK AppendAVIDlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	switch(uiMsg) {
	case WM_NOTIFY:
		if (((NMHDR *)lParam)->code == CDN_FILEOK) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			ofn->lpOFN->lCustData	= (IsDlgButtonChecked(hDlg, IDC_AUTOAPPEND)?1:0);
		}
		break;
	}
	return FALSE;
}

void AppendAVI() {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	if (!inputAVI)
		return;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFiltersAppend;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Append AVI segment";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "avi" : NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_APPEND_AVI);
	ofn.lpfnHook			= (LPOFNHOOKPROC)AppendAVIDlgHookProc;

	if (!GetOpenFileName(&ofn)) return;

	try {
		if (ofn.lCustData)
			AppendAVIAutoscan(g_szFile);
		else
			AppendAVI(g_szFile);
	} catch(const MyError& e) {
		e.post(NULL, g_szError);
	}
}

void HandleDragDrop(HDROP hdrop) {
	char szName[MAX_PATH];

	if (DragQueryFile(hdrop, -1, NULL, 0) < 1)
		return;

	DragQueryFile(hdrop, 0, szName, sizeof szName);

	try {
		char *s;

		if (!GetFullPathName(szName, sizeof g_szInputAVIFile, g_szInputAVIFile, &s))
			return;

		strcpy(g_szInputAVIFileTitle, s);

		OpenAVI(g_szInputAVIFile, FILETYPE_AUTODETECT, false);

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		// Add input streams automatically here
		// so that they won't be when using Job Control :)
		AddInputFileStreams(g_szInputAVIFile);
//END ==========================//

		mru_list->add(g_szInputAVIFile);
		MenuMRUListUpdate(g_hWnd);
		RecalcFrameSizes();
		SetTitleByFile(g_hWnd);
		RedrawWindow(g_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
	} catch(const MyError& e) {
		e.post(NULL, g_szError);
	}
}

////////////////////////////////////

UINT CALLBACK SaveAVIDlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	switch(uiMsg) {
	case WM_NOTIFY:
		if (((NMHDR *)lParam)->code == CDN_FILEOK) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			ofn->lpOFN->lCustData = IsDlgButtonChecked(hDlg, IDC_ADD_AS_JOB);
		}
		break;
	}
	return FALSE;
}
  
void SaveAVI(HWND hWnd, bool fUseCompatibility) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	///////////////

	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= hWnd;
	ofn.lpstrFilter			= fileFilters0;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= fUseCompatibility ? "Save AVI 1.0 File" : "Save AVI 2.0 File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "avi" : NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_SAVE_OUTPUT);
	ofn.lpfnHook			= (LPOFNHOOKPROC)SaveAVIDlgHookProc;

	if (GetSaveFileName(&ofn)) {
		BOOL fAddAsJob = !!ofn.lCustData;

		if (!CoachCheckSaveOp(hWnd, &g_dubOpts, &g_Vcompression, g_ACompressionFormat, &g_listFA, g_szInputAVIFile))
			return;

		if (fAddAsJob) {
			try {
				JobAddConfiguration(&g_dubOpts, g_szInputAVIFile, FILETYPE_AUTODETECT, g_szFile, fUseCompatibility, &inputAVI->listFiles, 0, 0);
			} catch(const MyError& e) {
				e.post(g_hWnd, g_szError);
			}
		} else {
			if (!(outputAVI = new AVIOutputFile()))
				MessageBox(NULL,g_szOutOfMemory,g_szError,MB_OK);
			else {
				((AVIOutputFile *)outputAVI)->disable_os_caching();

				if (g_prefs.fAVIRestrict1Gb)
					((AVIOutputFile *)outputAVI)->set_1Gb_limit();

				if (fUseCompatibility)
					((AVIOutputFile *)outputAVI)->disable_extended_avi();

				InitDubAVI(g_szFile, FALSE, NULL, g_prefs.main.iDubPriority, false, 0, 0);
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////

struct SegmentValues {
	long lThreshMB;
	long lThreshFrames;
	bool fDefer;
};

UINT CALLBACK SaveSegmentedAVIDlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	OFNOTIFY *ofn;
	BOOL fOk;
	SegmentValues *psv;

	switch(uiMsg) {
	case WM_COMMAND:
		switch(LOWORD(wParam)) {
		case IDC_FRAMELIMIT:
			if (HIWORD(wParam) == BN_CLICKED) {
				EnableWindow(GetDlgItem(hDlg, IDC_EDIT_FRAMELIMIT), SendMessage((HWND)lParam, BM_GETSTATE, 0, 0));
			}
			break;
		}
		return 0;

	case WM_NOTIFY:
		ofn = (OFNOTIFY *)lParam;

		switch(ofn->hdr.code) {
		case CDN_INITDONE:
			SetDlgItemInt(hDlg, IDC_LIMIT, 2000, FALSE);
			break;
		case CDN_FILEOK:
			psv = (SegmentValues *)ofn->lpOFN->lCustData;

			psv->lThreshMB = GetDlgItemInt(hDlg, IDC_LIMIT, &fOk, FALSE);

			if (!fOk || psv->lThreshMB<50 || psv->lThreshMB>2048) {
				MessageBox(hDlg, "The AVI segment size cannot be less than 50 or greater than 2048 megabytes.", g_szError, MB_OK);
				SetFocus(GetDlgItem(hDlg, IDC_LIMIT));

				SetWindowLong(hDlg, DWL_MSGRESULT, 1);
				return 1;
			}

			if (IsDlgButtonChecked(hDlg, IDC_FRAMELIMIT)) {
				psv->lThreshFrames = GetDlgItemInt(hDlg, IDC_EDIT_FRAMELIMIT, &fOk, FALSE);

				if (!fOk || psv->lThreshFrames<1) {
					MessageBox(hDlg, "AVI segments must have at least one frame.", g_szError, MB_OK);
					SetFocus(GetDlgItem(hDlg, IDC_EDIT_FRAMELIMIT));

					SetWindowLong(hDlg, DWL_MSGRESULT, 1);
					return 1;
				}
			} else
				psv->lThreshFrames = 0;

			psv->fDefer = !!IsDlgButtonChecked(hDlg, IDC_ADD_AS_JOB);

			return 0;
		}
		break;
	}
	return FALSE;
}
  
void SaveSegmentedAVI(HWND hWnd) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];
	SegmentValues sv;

	///////////////

	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= hWnd;
	ofn.lpstrFilter			= fileFiltersAppend;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Save segmented AVI";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "avi" : NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_SAVE_SEGMENTED);
	ofn.lpfnHook			= (LPOFNHOOKPROC)SaveSegmentedAVIDlgHookProc;
	ofn.lCustData			= (LONG)&sv;

	if (GetSaveFileName(&ofn)) {
		{
			char szPrefixBuffer[MAX_PATH], szPattern[MAX_PATH*2], *t, *t2, c;
			const char *s;
			int nMatchCount = 0;

			t = SplitPathName(g_szFile);
			t2 = SplitPathExt(t);

			if (!stricmp(t2, ".avi")) {
				while(t2>t && isdigit((unsigned)t2[-1]))
					--t2;

				if (t2>t && t2[-1]=='.')
					strcpy(t2, "avi");
			}

			strcpy(szPrefixBuffer, g_szFile);
			SplitPathExt(szPrefixBuffer)[0] = 0;

			s = SplitPathName(szPrefixBuffer);
			t = szPattern;

			while(*t++ = *s++)
				if (s[-1]=='%')
					*t++ = '%';

			t = szPrefixBuffer;
			while(*t)
				++t;

			strcpy(t, ".*.avi");

			WIN32_FIND_DATA wfd;
			HANDLE h;

			h = FindFirstFile(szPrefixBuffer, &wfd);
			if (h != INVALID_HANDLE_VALUE) {
				strcat(szPattern, ".%d.av%c");

				do {
					int n;

					if (2 == sscanf(wfd.cFileName, szPattern, &n, &c) && tolower(c)=='i')
						++nMatchCount;
					
				} while(FindNextFile(h, &wfd));
				FindClose(h);
			}

			if (nMatchCount) {
				if (IDOK != guiMessageBoxF(g_hWnd, g_szWarning, MB_OKCANCEL|MB_ICONEXCLAMATION,
					"There %s %d existing file%s which match%s the filename pattern \"%s\". These files "
					"will be erased if you continue, to prevent confusion with the new files."
					,nMatchCount==1 ? "is" : "are"
					,nMatchCount
					,nMatchCount==1 ? "" : "s"
					,nMatchCount==1 ? "es" : ""
					,SplitPathName(szPrefixBuffer)))
					return;

				h = FindFirstFile(szPrefixBuffer, &wfd);
				if (h != INVALID_HANDLE_VALUE) {
					strcat(szPattern, ".%d.av%c");

					t = SplitPathName(szPrefixBuffer);

					do {
						int n;

						if (2 == sscanf(wfd.cFileName, szPattern, &n, &c) && tolower(c)=='i') {
							strcpy(t, wfd.cFileName);
							DeleteFile(t);
						}
							
						
					} while(FindNextFile(h, &wfd));
					FindClose(h);
				}
			}
		}

		if (!CoachCheckSaveOp(hWnd, &g_dubOpts, &g_Vcompression, g_ACompressionFormat, &g_listFA, g_szInputAVIFile))
			return;

		if (sv.fDefer) {
			try {
				JobAddConfiguration(&g_dubOpts, g_szInputAVIFile, FILETYPE_AUTODETECT, g_szFile, true, &inputAVI->listFiles, sv.lThreshMB, sv.lThreshFrames);
			} catch(const MyError& e) {
				e.post(g_hWnd, g_szError);
			}
		} else {
			if (!(outputAVI = new AVIOutputFile()))
				MessageBox(NULL,g_szOutOfMemory,g_szError,MB_OK);
			else {
				((AVIOutputFile *)outputAVI)->disable_os_caching();
				((AVIOutputFile *)outputAVI)->disable_extended_avi();
				((AVIOutputFile *)outputAVI)->setSegmentHintBlock(true, NULL, 1);

				InitDubAVI(g_szFile, FALSE, NULL, g_prefs.main.iDubPriority, false, sv.lThreshMB, sv.lThreshFrames);
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////

void SaveStripedAVI(HWND hWnd) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];
	AVIStripeSystem *stripe_def = NULL;

	///////////////

	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= hWnd;
	ofn.lpstrFilter			= fileFiltersStripe;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Select AVI stripe definition file";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "stripe" : NULL;

	try {
		if (GetOpenFileName(&ofn))
			SaveStripedAVI(g_szFile);
	} catch(const MyError& e) {
		e.post(NULL, g_szError);
	}

	delete stripe_def;
}

void SaveStripeMaster(HWND hWnd) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];
	AVIStripeSystem *stripe_def = NULL;

	///////////////

	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= hWnd;
	ofn.lpstrFilter			= fileFiltersStripe;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Select AVI stripe definition file";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "stripe" : NULL;

	try {
		if (GetOpenFileName(&ofn))
			SaveStripeMaster(g_szFile);
	} catch(const MyError& e) {
		e.post(NULL, g_szError);
	}

	delete stripe_def;
}

void PreviewAVI(HWND hWnd, DubOptions *quick_options, int iPriority, bool fProp) {
	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	if (!(outputAVI = new AVIOutputPreview()))
		MessageBox(NULL, g_szOutOfMemory, g_szError,MB_OK);
	else {
		BOOL bPreview = g_dubOpts.audio.enabled;

		g_dubOpts.audio.enabled = TRUE;

		InitDubAVI(NULL, FALSE, quick_options, iPriority, fProp, 0, 0);

		g_dubOpts.audio.enabled = bPreview;
	}
}

void PositionCallback(LONG start, LONG cur, LONG end, int progress) {
	SendMessage(GetDlgItem(g_hWnd, IDC_POSITION), PCM_SETPOS, 0, cur);
}

void CPUTest() {
	long lEnableFlags;

	lEnableFlags = g_prefs.main.fOptimizations;

	if (!(g_prefs.main.fOptimizations & PreferencesMain::OPTF_FORCE)) {
		SYSTEM_INFO si;

		lEnableFlags = CPUCheckForExtensions();

		GetSystemInfo(&si);

		if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
			if (si.wProcessorLevel < 4)
				lEnableFlags &= ~CPUF_SUPPORTS_FPU;		// Not strictly true, but very slow anyway
	}

	// Enable FPU support...

	long lActualEnabled = CPUEnableExtensions(lEnableFlags);
}

void InitDubAVI(char *szFile, BOOL fAudioOnly, DubOptions *quick_options, int iPriority, bool fPropagateErrors, long lSpillThreshold, long lSpillFrameThreshold) {
	char szSpillPrefix[MAX_PATH];
	bool fError = false;
	MyError prop_err;
	DubOptions *opts;
	POINT pt;

	try {

		filters.DeinitFilters();
		filters.DeallocateBuffers();

		SetAudioSource();
		RecalcFrameSizes();

		CPUTest();

		// Create a dubber.

		if (!quick_options) {
			g_dubOpts.video.fShowDecompressedFrame = g_drawDecompressedFrame;
			g_dubOpts.fShowStatus = !!g_showStatusWindow;
		}

		opts = quick_options ? quick_options : &g_dubOpts;
		opts->perf.fDropFrames = g_fDropFrames;

		if (!(g_dubber = CreateDubber(opts)))
			throw MyMemoryError();

		// Create dub status window

		g_dubStatus = CreateDubStatusHandler();

		if (opts->fMoveSlider)
			g_dubStatus->SetPositionCallback(g_fJobMode ? JobPositionCallback : PositionCallback);

		// Initialize the dubber.

		g_dubber->SetStatusHandler(g_dubStatus);
		g_dubber->SetInputFile(inputAVI);
		g_dubber->SetFrameRectangles(&g_rInputFrame, &g_rOutputFrame);
		pt.x = pt.y = 0;
		ClientToScreen(g_hWnd, &pt);
		g_dubber->SetClientRectOffset(pt.x, pt.y);

		if (!outputAVI->isPreview() && g_ACompressionFormat)
			g_dubber->SetAudioCompression(g_ACompressionFormat, g_ACompressionFormatSize);

		// As soon as we call Init(), this value is no longer ours to free.

		AVIOutput *pOutput = outputAVI;

		if (lSpillThreshold) {
			char szFile2[MAX_PATH];

			strcpy(szSpillPrefix, szFile);
			const_cast<char *>(SplitPathExt(szSpillPrefix))[0] = 0;

			strcpy(szFile2, szSpillPrefix);
			strcat(szFile2, ".00.avi");

			g_dubber->EnableSpill(szSpillPrefix, (__int64)(lSpillThreshold-1) << 20, lSpillFrameThreshold);
			outputAVI = NULL;
			g_dubber->Init(inputVideoAVI, inputAudio, inputAudio2, pOutput, szFile2, hDCWindow, &g_Vcompression);
		} else if (fAudioOnly == 2) {
			g_dubber->SetPhantomVideoMode();
			outputAVI = NULL;
			g_dubber->Init(inputVideoAVI, inputAudio, inputAudio2, pOutput, szFile, hDCWindow, &g_Vcompression);
		} else {
			outputAVI = NULL;
			g_dubber->Init(inputVideoAVI, inputAudio, inputAudio2, pOutput, szFile, hDCWindow, &g_Vcompression);
		}

		_RPT0(0,"Starting dub.\n");

		if (!quick_options) RedrawWindow(g_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);

		SetMenu(g_hWnd, hMenuDub);
		SetWindowLong(g_hWnd, GWL_WNDPROC, (DWORD)DubWndProc);

		g_dubber->Go(iPriority);

		if (g_dubber->isAbortedByUser())
			g_fJobAborted = true;

	} catch(char *s) {
		if (fPropagateErrors) {
			prop_err.setf(s);
			fError = true;
		} else
			MyError(s).post(g_hWnd,g_szError);
	} catch( MyError& err) {
		if (fPropagateErrors) {
			prop_err.TransferFrom(err);
			fError = true;
		} else
			err.post(g_hWnd,g_szError);
	}

	g_dubber->SetStatusHandler(NULL);
	delete g_dubStatus; g_dubStatus = NULL;

	_CrtCheckMemory();

	SetMenu(g_hWnd, hMenuNormal);
	MenuMRUListUpdate(g_hWnd);
	SetWindowLong(g_hWnd, GWL_WNDPROC, (DWORD)MainWndProc);

	if (inputAVI)
		guiSetTitle(g_hWnd, IDS_TITLE_IDLE, g_szInputAVIFileTitle);
	else
		guiSetTitle(g_hWnd, IDS_TITLE_NOFILE);

	_RPT0(0,"Ending dub.\n");

	delete g_dubber;		g_dubber = NULL;

	CloseNewAVI();

	if (!inputVideoAVI->setDecompressedFormat(24))
		if (!inputVideoAVI->setDecompressedFormat(32))
			if (!inputVideoAVI->setDecompressedFormat(16))
				inputVideoAVI->setDecompressedFormat(8);

	if (!quick_options) RedrawWindow(g_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);

	if (fError && fPropagateErrors)
		throw prop_err;
}

/////////////////////////////

typedef struct SaveImageSeqDlgData {
	char szPrefix[MAX_PATH];
	char szPostfix[MAX_PATH];
	char szDirectory[MAX_PATH];
	char szFormat[MAX_PATH];
	int digits;
	long lFirstFrame, lLastFrame;
	//bool bSaveAsTGA;//	pulco 04/11/2002 : merging with 1.4.11
	AVIOutputImages::ImageType mType;
	bool bRunAsJob;
} SaveImageSeqDlgData;

static void SaveImageSeqShowFilenames(HWND hDlg) {
	SaveImageSeqDlgData *sisdd = (SaveImageSeqDlgData *)GetWindowLong(hDlg, DWL_USER);
	char buf[512], *s;

	if (!sisdd) return;

	strcpy(sisdd->szFormat, sisdd->szDirectory);

	s = sisdd->szFormat;
	while(*s) ++s;
	if (s>sisdd->szFormat && s[-1]!=':' && s[-1]!='\\')
		*s++ = '\\';

	strcpy(s, sisdd->szPrefix);
	while(*s) ++s;

	char *pCombinedPrefixPt = s;

	*s++ = '%';
	*s++ = '0';
	*s++ = '*';
	*s++ = 'l';
	*s++ = 'd';

	strcpy(s, sisdd->szPostfix);

	sprintf(buf, sisdd->szFormat, sisdd->digits, sisdd->lFirstFrame);
	SetDlgItemText(hDlg, IDC_STATIC_FIRSTFRAMENAME, buf);
	sprintf(buf, sisdd->szFormat, sisdd->digits, sisdd->lLastFrame);
	SetDlgItemText(hDlg, IDC_STATIC_LASTFRAMENAME, buf);
	
	*pCombinedPrefixPt = 0;
}

static BOOL CALLBACK SaveImageSeqDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
	SaveImageSeqDlgData *sisdd = (SaveImageSeqDlgData *)GetWindowLong(hDlg, DWL_USER);
	char *lpszFileName;
	BROWSEINFO bi;
	LPITEMIDLIST pidlBrowse;
	LPMALLOC pMalloc;
	UINT uiTemp;
	BOOL fSuccess;

	switch(message) {
	case WM_INITDIALOG:
		SetWindowLong(hDlg, DWL_USER, NULL);
		sisdd = (SaveImageSeqDlgData *)lParam;
		SetDlgItemText(hDlg, IDC_FILENAME_PREFIX, sisdd->szPrefix);
		SetDlgItemText(hDlg, IDC_FILENAME_SUFFIX, sisdd->szPostfix);
		SetDlgItemInt(hDlg, IDC_FILENAME_DIGITS, sisdd->digits, FALSE);
		SetDlgItemText(hDlg, IDC_DIRECTORY, sisdd->szDirectory);
		CheckDlgButton(hDlg, (sisdd->mType == AVIOutputImages::TARGA) ? IDC_FORMAT_TGA : ((sisdd->mType == AVIOutputImages::PNG) ? IDC_FORMAT_PNG : IDC_FORMAT_BMP), BST_CHECKED);
		SetWindowLong(hDlg, DWL_USER, lParam);
		SaveImageSeqShowFilenames(hDlg);

		return TRUE;

	case WM_COMMAND:
		if (!sisdd) break;

		switch(LOWORD(wParam)) {

		case IDC_FILENAME_PREFIX:
			if (HIWORD(wParam) != EN_CHANGE) break;
			SendMessage((HWND)lParam, WM_GETTEXT, sizeof sisdd->szPrefix, (LPARAM)sisdd->szPrefix);
			SaveImageSeqShowFilenames(hDlg);
			return TRUE;

		case IDC_FILENAME_SUFFIX:
			if (HIWORD(wParam) != EN_CHANGE) break;
			SendMessage((HWND)lParam, WM_GETTEXT, sizeof sisdd->szPostfix, (LPARAM)sisdd->szPostfix);
			SaveImageSeqShowFilenames(hDlg);
			return TRUE;

		case IDC_FILENAME_DIGITS:
			if (HIWORD(wParam) != EN_CHANGE) break;
			uiTemp = GetDlgItemInt(hDlg, IDC_FILENAME_DIGITS, &fSuccess, FALSE);
			if (fSuccess) {
				sisdd->digits = uiTemp;

				if (sisdd->digits > 15)
					sisdd->digits = 15;

				SaveImageSeqShowFilenames(hDlg);
			}
			return TRUE;

		case IDC_DIRECTORY:
			if (HIWORD(wParam) != EN_CHANGE) break;
			SendMessage((HWND)lParam, WM_GETTEXT, sizeof sisdd->szDirectory, (LPARAM)sisdd->szDirectory);
			SaveImageSeqShowFilenames(hDlg);
			return TRUE;

		case IDC_SELECT_DIR:
			if (SUCCEEDED(SHGetMalloc(&pMalloc))) {
				if (lpszFileName = (char *)pMalloc->Alloc(MAX_PATH)) {
					bi.hwndOwner		= hDlg;
					bi.pidlRoot			= NULL;
					bi.pszDisplayName	= lpszFileName;
					bi.lpszTitle		= "Select a directory to save images to";
					bi.ulFlags			= BIF_RETURNONLYFSDIRS;
					bi.lpfn				= NULL;

					if (pidlBrowse = SHBrowseForFolder(&bi)) {
						if (SHGetPathFromIDList(pidlBrowse, lpszFileName))
							SetDlgItemText(hDlg, IDC_DIRECTORY, lpszFileName);

						pMalloc->Free(pidlBrowse);
					}
					pMalloc->Free(lpszFileName);
				}
			}
			return TRUE;

//	pulco 4/11/2002 : merging with 1.4.11
//BEGIN ========================//
		case IDC_FORMAT_TGA:
			if (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)) {
				sisdd->mType = AVIOutputImages::TARGA;
				/*if (!stricmp(sisdd->szPostfix, ".bmp"))*/ {
					SetDlgItemText(hDlg, IDC_FILENAME_SUFFIX, ".tga");
				}
			}
			return TRUE;

		case IDC_FORMAT_PNG:
			if (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)) {
				sisdd->mType = AVIOutputImages::PNG;
				/*if (!stricmp(sisdd->szPostfix, ".bmp"))*/ {
					SetDlgItemText(hDlg, IDC_FILENAME_SUFFIX, ".png");
				}
			}
			return TRUE;

		case IDC_FORMAT_BMP:
			if (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)) {
				sisdd->mType = AVIOutputImages::BITMAP;
				/*if (!stricmp(sisdd->szPostfix, ".tga"))*/ {
					SetDlgItemText(hDlg, IDC_FILENAME_SUFFIX, ".bmp");
				}
			}
			return TRUE;

		case IDC_ADD_AS_JOB:
			sisdd->bRunAsJob = !!SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
			return TRUE;
//END ==========================//

		case IDOK:
			EndDialog(hDlg, TRUE);
			return TRUE;

		case IDCANCEL:
			EndDialog(hDlg, FALSE);
			return TRUE;
		}
		break;
	}

	return FALSE;
}

void SaveImageSeq(HWND hwnd) {
	SaveImageSeqDlgData sisdd;

	if (!inputVideoAVI) {
		MessageBox(hwnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	memset(&sisdd, 0, sizeof sisdd);
	//	pulco 4/11/2002 : merging with 1.4.11
	sisdd.mType = AVIOutputImages::TARGA;
	strcpy(sisdd.szPostfix,".tga");
	//strcpy(sisdd.szPostfix,".bmp");
	sisdd.lFirstFrame	= inputVideoAVI->lSampleFirst;
	sisdd.lLastFrame	= inputVideoAVI->lSampleLast-1;

	if (DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_AVIOUTPUTIMAGES_FORMAT), hwnd, SaveImageSeqDlgProc, (LPARAM)&sisdd)) {
		SetAudioSource();

	//	pulco 4/11/2002 : merging with 1.4.11
		try {
			if (sisdd.bRunAsJob)
				JobAddConfigurationImages(&g_dubOpts, g_szInputAVIFile, FILETYPE_AUTODETECT, sisdd.szFormat, sisdd.szPostfix, sisdd.digits, sisdd.mType, &inputAVI->listFiles);
			else
				SaveImageSequence(sisdd.szFormat, sisdd.szPostfix, sisdd.digits, false, NULL, sisdd.mType);
		} catch(const MyError& e) {
			e.post(NULL, g_szError);
//==============================//
//=====  MODIFICATION PNG  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//
		//if (!(outputAVI = new AVIOutputImages(sisdd.szFormat, sisdd.digits)))
//		if (!(outputAVI = new AVIOutputImages(sisdd.szFormat, sisdd.digits, save_to_png ? AVIOutputImages::PNG : AVIOutputImages::BITMAP)))
//END ==========================//
//			MessageBox(NULL,g_szOutOfMemory,g_szError,MB_OK);
//		else {
//			InitDubAVI(NULL, FALSE, NULL, g_prefs.main.iDubPriority);
		}
	}
}

/////////////////////////////

// 22/11/2002, Cyrius : allow demuxing without WAV header
void SaveWAV(HWND hWnd, BOOL skipWAVHeader) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	//////////

	SetAudioSource();

	// 22/11/2002, Cyrius : 2nd audio stream handling
	// warn the user that he/she can't save the 2 audio streams in one shot
	if(inputAudio && inputAudio2) {
		MessageBox(g_hWnd, "Please select only one audio stream to extract.", g_szError, MB_OK);
		return;
	}

	if (!inputAudio && !inputAudio2) {
		MessageBox(g_hWnd, "No input audio stream to extract.", g_szError, MB_OK);
		return;
	}
	AudioSource *audioSelected = (inputAudio ? inputAudio : inputAudio2);

	szFileTitle[0]=0;
	char szFile[MAX_PATH];
	szFile[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	if(skipWAVHeader) {
		switch(audioSelected->getWaveFormat()->wFormatTag) {
			case 0x0050:  // MPEG3-Layer1/2
			case 0x0055:  // MPEG3-Layer3
				ofn.lpstrFilter			= fileFilters3;
				ofn.lpstrTitle			= "Save MP? File";
				ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "mp3" : NULL;
				break;

			case 0x2000:  // AC3
				ofn.lpstrFilter			= fileFilters4;
				ofn.lpstrTitle			= "Save AC3 File";
				ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ac3" : NULL;
				break;

			case 0x0000:  // Vorbis ?
				// Let's see if this a Vorbis stream
				// First the Format should be WAVEFORMATEXTENSIBLE
				// Then the SubFormat field should be set to MEDIASUBTYPE_Vorbis
				if( (audioSelected->getFormatLen() >= sizeof(WAVEFORMATEXTENSIBLE))
					&& (((WAVEFORMATEXTENSIBLE *)audioSelected->getFormat())->SubFormat == MEDIASUBTYPE_Vorbis) ) {
					ofn.lpstrFilter			= fileFilters5;
					ofn.lpstrTitle			= "Save Ogg (Vorbis) File";
					ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ogg" : NULL;
					break;
				}

			default:
				ofn.lpstrFilter			= fileFilters2;
				ofn.lpstrTitle			= "Save Audio File";
				ofn.lpstrDefExt			= NULL;
				break;
		}
	} else {
		ofn.lpstrFilter			= fileFilters2;
		ofn.lpstrTitle			= "Save WAV File";
		ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "wav" : NULL;
	}
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= szFile;
	ofn.nMaxFile			= sizeof szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT;

	if (GetSaveFileName(&ofn))
		try {
		SaveWAV(szFile, false, NULL, skipWAVHeader ? true : false);
		} catch(const MyError& e) {
			e.post(NULL, g_szError);
	}
}

///////////////

void OpenWAV() {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFilters2;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szInputWAVFile;
	ofn.nMaxFile			= sizeof g_szInputWAVFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open WAV File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= NULL;

	if (GetOpenFileName(&ofn))
		OpenWAV(g_szInputWAVFile);
}

/////////////////////////////////////////////////////////////////////////////////

void SaveConfiguration(HWND hWnd) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	//////////

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFiltersSaveConfig;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Save Configuration";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "vcf" : NULL;

	if (GetSaveFileName(&ofn)) {
		FILE *f = NULL;
		try {
			f = fopen(g_szFile, "w");

			if (!f)
				throw MyError("Cannot open output file: %s.", strerror(errno));

			JobWriteConfiguration(f, &g_dubOpts);

			fclose(f);
			f = NULL;
		} catch(const MyError& e) {
			e.post(NULL, g_szError);
		}

		if (f)
			fclose(f);
	}
}

/////////////////////////////////////////////////////////////////////////////////

void DoDelete() {
	if (!inputVideoAVI)
		return;

	try {
		HWND hwndPosition = GetDlgItem(g_hWnd, IDC_POSITION);
		LONG lSample = SendMessage(hwndPosition, PCM_GETPOS, 0, 0);
		LONG lStart = SendMessage(hwndPosition, PCM_GETSELSTART, 0, 0);
		LONG lEnd = SendMessage(hwndPosition, PCM_GETSELEND, 0, 0);

		EnsureSubset();

//		_RPT0(0,"Deleting 1 frame\n");

		if (lStart>=0 && lEnd>=0 && lEnd>=lStart)
			inputSubset->deleteRange(lStart, lEnd+1-lStart);
		else {
			lStart = lSample;
			inputSubset->deleteRange(lSample, 1);
		}

		SendMessage(hwndPosition, PCM_SETRANGEMAX, (BOOL)TRUE, inputSubset->getTotalFrames());
		SendMessage(hwndPosition, PCM_CLEARSEL, (BOOL)TRUE, 0);
		SendMessage(hwndPosition, PCM_SETPOS, (BOOL)TRUE, lStart);
		g_dubOpts.video.lStartOffsetMS = g_dubOpts.video.lEndOffsetMS = 0;

		DisplayFrame(g_hWnd, SendMessage(hwndPosition, PCM_GETPOS, 0, 0));
	} catch(const MyError& e) {
		e.post(g_hWnd, g_szError);
	}
}

void DoMaskChange(bool bNewMode) {
	if (!inputVideoAVI)
		return;

	try {
		HWND hwndPosition = GetDlgItem(g_hWnd, IDC_POSITION);
		LONG lSample = SendMessage(hwndPosition, PCM_GETPOS, 0, 0);
		LONG lStart = SendMessage(hwndPosition, PCM_GETSELSTART, 0, 0);
		LONG lEnd = SendMessage(hwndPosition, PCM_GETSELEND, 0, 0);

		if (!inputSubset)
			if (!(inputSubset = new FrameSubset(inputVideoAVI->lSampleLast - inputVideoAVI->lSampleFirst)))
				throw MyMemoryError();

		if (lStart>=0 && lEnd>=0 && lEnd>=lStart)
			inputSubset->setRange(lStart, lEnd+1-lStart, bNewMode);
		else {
			lStart = lSample;
			inputSubset->setRange(lSample, 1, bNewMode);
		}

		g_dubOpts.video.lStartOffsetMS = g_dubOpts.video.lEndOffsetMS = 0;
		SendMessage(hwndPosition, PCM_CLEARSEL, (BOOL)TRUE, 0);
		DisplayFrame(g_hWnd, SendMessage(hwndPosition, PCM_GETPOS, 0, 0));
	} catch(const MyError& e) {
		e.post(g_hWnd, g_szError);
	}
}




///////////////

void OpenWAV(bool secondary) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFilters2;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	// 13/11/2002, Cyrius : use the good char* for storing file names !
	ofn.lpstrFile			= (secondary ? g_szInput2WAVFile : g_szInputWAVFile);
	ofn.nMaxFile			= (secondary ? sizeof g_szInput2WAVFile : sizeof g_szInputWAVFile);
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open WAV File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= NULL;

	if(GetOpenFileName(&ofn))
		OpenWAV((secondary ? g_szInput2WAVFile : g_szInputWAVFile), secondary);
}

void OpenMP3(bool secondary) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFilters3;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	// 13/11/2002, Cyrius : use the good char* for storing file names !
	ofn.lpstrFile			= (secondary ? g_szInput2MP3File : g_szInputMP3File);
	ofn.nMaxFile			= (secondary ? sizeof g_szInput2MP3File : sizeof g_szInputMP3File);
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open MP3 File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= NULL;

	if (GetOpenFileName(&ofn))
		OpenMP3((secondary ? g_szInput2MP3File : g_szInputMP3File), secondary);
}

// 12/11/2002, Cyrius : feature added when opening AC3 file
UINT CALLBACK OpenAC3DlgHookProc(  HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	switch(uiMsg) {
	case WM_NOTIFY:
		if (((NMHDR *)lParam)->code == CDN_INITDONE) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			CheckDlgButton(hDlg, IDC_AC3FRAMEMODE, ofn->lpOFN->lCustData ? BST_CHECKED : BST_UNCHECKED);
		} else if (((NMHDR *)lParam)->code == CDN_FILEOK) {
			OFNOTIFY *ofn = (OFNOTIFY *)lParam;

			ofn->lpOFN->lCustData	= (IsDlgButtonChecked(hDlg, IDC_AC3FRAMEMODE)?1:0);
		}
		break;
	}
	return FALSE;
}

void OpenAC3(bool secondary) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFilters4;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	// 13/11/2002, Cyrius : use the good char* for storing file names !
	ofn.lpstrFile			= (secondary ? g_szInput2AC3File : g_szInputAC3File);
	ofn.nMaxFile			= (secondary ? sizeof g_szInput2AC3File : sizeof g_szInputAC3File);
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open AC3 File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_OPEN_AC3);
	ofn.lpfnHook			= (LPOFNHOOKPROC)OpenAC3DlgHookProc;

	ofn.lCustData = 0;
	if (GetOpenFileName(&ofn)) {
		// 12/11/2002, Cyrius : boolean to know if we must set nBlockAlign to frame boundaries
		bool frame_boundary = !!(ofn.lCustData);
		OpenAC3((secondary ? g_szInput2AC3File : g_szInputAC3File), secondary, frame_boundary);
	}
}

void OpenOGG(bool secondary) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= g_hWnd;
	ofn.lpstrFilter			= fileFilters5;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	// 13/11/2002, Cyrius : use the good char* for storing file names !
	ofn.lpstrFile			= (secondary ? g_szInput2OGGFile : g_szInputOGGFile);
	ofn.nMaxFile			= (secondary ? sizeof g_szInput2OGGFile : sizeof g_szInputOGGFile);
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Open OGG File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING;
	ofn.lpstrDefExt			= NULL;

	if (GetOpenFileName(&ofn))
		OpenOGG((secondary ? g_szInput2OGGFile : g_szInputOGGFile), secondary);
}





///////////////////////////////////////////

//==============================//
//=====  MODIFICATION OGM  =====//
//=====    -= Cyrius =-    =====//
//BEGIN ========================//

// 13/11/2002, Cyrius : Nandub feature (jump on keyframe preceding position in file)
// adapted to OGM files too
long JumpToLastKeyBeforePos( __int64 pos ) {
	if(!inputAVI || !inputVideoAVI) return -1;
	LONG lSample = 0;
	while( true ) {
		lSample = inputVideoAVI->nextKey( lSample );
		if( lSample<0 ) {
			lSample = inputVideoAVI->lSampleLast;
			break;
		}
		if(!inputAVI->isOGM && (((VideoSourceAVI*)inputVideoAVI)->sampleOffset(lSample) > pos) ) {
			lSample = inputVideoAVI->prevKey( lSample );
			break;
		} else if(inputAVI->isOGM && (((VideoSourceAVI*)inputVideoAVI)->sampleOffset(lSample) > pos) ) {
			lSample = inputVideoAVI->prevKey( lSample );
			break;
		}
	}
	return lSample;
}


void SaveOGM(HWND hWnd, bool fUseCompatibility) {
	OPENFILENAME ofn;
	char szFileTitle[MAX_PATH];

	///////////////

	SetAudioSource();

	if (!inputVideoAVI) {
		MessageBox(hWnd, "No input video stream to process.", g_szError, MB_OK);
		return;
	}

	szFileTitle[0]=0;

	ofn.lStructSize			= sizeof(OPENFILENAME);
	ofn.hwndOwner			= hWnd;
	ofn.lpstrFilter			= fileFiltersOGM;
	ofn.lpstrCustomFilter	= NULL;
	ofn.nFilterIndex		= 1;
	ofn.lpstrFile			= g_szFile;
	ofn.nMaxFile			= sizeof g_szFile;
	ofn.lpstrFileTitle		= szFileTitle;
	ofn.nMaxFileTitle		= sizeof szFileTitle;
	ofn.lpstrInitialDir		= NULL;
	ofn.lpstrTitle			= "Save OGM File";
	ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
	ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ogm" : NULL;
	ofn.hInstance			= g_hInst;
	ofn.lpTemplateName		= MAKEINTRESOURCE(IDD_SAVE_OUTPUT);
	ofn.lpfnHook			= (LPOFNHOOKPROC)SaveAVIDlgHookProc;

	if (GetSaveFileName(&ofn)) {
		BOOL fAddAsJob = !!ofn.lCustData;

		if (!CoachCheckSaveOp(hWnd, &g_dubOpts, &g_Vcompression, g_ACompressionFormat, &g_listFA, g_szInputAVIFile))
			return;

		if (fAddAsJob) {
			try {
				JobAddConfiguration(&g_dubOpts, g_szInputAVIFile, FILETYPE_AUTODETECT, g_szFile, fUseCompatibility, &inputAVI->listFiles, 0, 0, "OGM");
			} catch(const MyError& e) {
				e.post(g_hWnd, g_szError);
			}
		} else {
			if(!(outputAVI = new OGMOutputFile(video_comments, audio_comments, audio2_comments)))
				MessageBox(NULL,g_szOutOfMemory,g_szError,MB_OK);
			else {
				((OGMOutputFile *)outputAVI)->disable_os_caching();

				InitDubOGM(g_szFile, FALSE, NULL, g_prefs.main.iDubPriority, false, 0, 0);
			}
		}
	}
}

extern struct ReposItem ShowTextCtlPosData[]/*={
	{ IDC_CHANGES		, REPOS_SIZERIGHT | REPOS_SIZEDOWN },
	{ IDOK				, REPOS_MOVERIGHT | REPOS_MOVEDOWN },
	{ 0 }
}*/;

extern POINT ShowTextCtlPos[2];

BOOL APIENTRY OGMInfoDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {

	static RECT rInitial;

	switch(uiMsg) {
	case WM_INITDIALOG:
		{
			SendMessage(hdlg, WM_SETTEXT, 0, (LPARAM)"OGM Information");
			SendMessage(GetDlgItem(hdlg, IDC_CHANGES), WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), MAKELPARAM(TRUE, 0));
			char report[64*1024];
			memset(report, 0, sizeof(report));
			char *dest = report;
			dest += sprintf(dest, "OGM Information :\r\n\r\n");
			if(inputAVI && inputAVI->isOGM) {
				OGMReadHandler *ogm_h = (OGMReadHandler *)((InputFileAVI *)inputAVI)->getHandler();
				if(ogm_h) {
					dest += sprintf(dest, "File structure : ");
					if(ogm_h->skippedBytes())
						dest += sprintf(dest, "errors found in structure (%I64d bytes skipped in invalid sections).\r\n", ogm_h->skippedBytes());
					else
						dest += sprintf(dest, "no errors found.\r\n");
					streamNode *stream, *streamNext;
					stream = ogm_h->getStreams().AtHead();
					dest += sprintf(dest, "Streams : ");
					if(stream) {
						int n_vorbis = 1;
						int n_audio = 1;
						int n_video = 1;
						int n_text = 1;
						int n_invalid = 1;
						char *s_type = "Unknown";
						int n_stream = 1;
						__int64 total_bytes_packets = 0;
						__int64 total_bytes_pages = 0;
						while(streamNext = stream->NextFromHead()) {
							switch(stream->type) {
							case S_VIDEO:
								s_type = "Video";
								n_stream = n_video++;
								break;

							case S_VORBIS:
								s_type = "Vorbis";
								n_stream = n_vorbis++;
								break;

							case S_AUDIO:
								s_type = "Audio";
								n_stream = n_audio++;
								break;

							case S_TEXT:
								s_type = "Text";
								n_stream = n_text++;
								break;

							default:
								s_type = "Invalid";
								n_stream = n_invalid++;
							}
							dest += sprintf(dest, "\r\n--=== %s %d (%d) ===--\r\nHeader :\r\n", s_type, n_stream, stream->serial);
							dest += sprintf(dest, "  Type : ");
							strncpy(dest, stream->sh.streamtype, 8);
							dest = report + strlen(report);
							dest += sprintf(dest, "\r\n  SubType : ");
							strncpy(dest, stream->sh.subtype, 4);
							dest = report + strlen(report);
							if(stream->type == S_AUDIO) {
								dest += sprintf(dest, " (%s)"
									, !strncmp(stream->sh.subtype, "0001", 4) ? "PCM"
									: !strncmp(stream->sh.subtype, "0050", 4) ? "MPEG1-Layer1/2"
									: !strncmp(stream->sh.subtype, "0055", 4) ? "MPEG1-Layer3"
									: !strncmp(stream->sh.subtype, "0161", 4) ? "WMA / DivX Audio"
									: !strncmp(stream->sh.subtype, "2000", 4) ? "AC3"
									: "Unknown");
							}
							dest += sprintf(dest, "\r\n  Size : %ld\r\n", stream->sh.size);
							dest += sprintf(dest, "  Time Unit : %I64d\r\n", stream->sh.time_unit);
							dest += sprintf(dest, "  Samples per Unit : %I64d\r\n", stream->sh.samples_per_unit);
							dest += sprintf(dest, "  SampleRate : %.3f\r\n", stream->sample_rate);
							dest += sprintf(dest, "  Default Len : %ld\r\n", stream->sh.default_len);
							dest += sprintf(dest, "  Buffer Size : %ld\r\n", stream->sh.buffersize);
							dest += sprintf(dest, "  Bits per Sample : %d\r\n", stream->sh.bits_per_sample);
							switch(stream->type) {
							case S_VIDEO:
								dest += sprintf(dest, "  Width : %ld\r\n", stream->sh.sh.video.width);
								dest += sprintf(dest, "  Height : %ld\r\n", stream->sh.sh.video.height);
								break;

							case S_VORBIS:
								dest += sprintf(dest, "  Bitrate Lower : %ld\r\n", stream->vi.bitrate_lower);
								dest += sprintf(dest, "  Bitrate Nominal : %ld\r\n", stream->vi.bitrate_nominal);
								dest += sprintf(dest, "  Bitrate Upper : %ld\r\n", stream->vi.bitrate_upper);
							case S_AUDIO:
								dest += sprintf(dest, "  Channels : %d\r\n", stream->sh.sh.audio.channels);
								dest += sprintf(dest, "  BlockAlign : %d\r\n", stream->sh.sh.audio.blockalign);
								dest += sprintf(dest, "  Average Byes per Second : %ld\r\n", stream->sh.sh.audio.avgbytespersec);
								break;
							}
							if((stream->vc.comments > 0) || (stream->vc.vendor)) {
								int n_comment = 1;
								dest += sprintf(dest, "***** COMMENTS *****\r\n");
								dest += sprintf(dest, "Vendor     : [%s]\r\n", stream->vc.vendor);
								for(n_comment = 0 ; n_comment < stream->vc.comments ; n_comment++)
									dest += sprintf(dest, "Comment %2d : [%s]\r\n"
									, n_comment+1, stream->vc.user_comments[n_comment]);
								dest += sprintf(dest, "***** COMMENTS *****\r\n");
							}
							__int64 n_pages = 0;
							__int64 n_packets = 0;
							__int64 n_bytes_packets = 0;
							__int64 n_bytes_pages = 0;
							pageNode *page, *pageNext;
							page = stream->pages.AtHead();
							if(page)
							while(pageNext = page->NextFromHead()) {
								n_pages++;
								n_packets += page->nb_packets;
								n_bytes_pages += page->size;
								for(int i=0 ; i<page->nb_packets ; i++)
									n_bytes_packets += page->n_bytes[i];
								page = pageNext;
							}
							total_bytes_pages += n_bytes_pages;
							total_bytes_packets += n_bytes_packets;
							dest += sprintf(dest, "Number of Pages : %I64d\r\n", n_pages);
							dest += sprintf(dest, "Number of Packets : %I64d (%d headers)\r\n", n_packets, (int)stream->num_headers);
							dest += sprintf(dest, "Number of Samples : %I64d\r\n", stream->n_samples);
							dest += sprintf(dest, "Bytes in Packets: %I64d (%.2fMB)\r\n"
								, n_bytes_packets, (double)n_bytes_packets / 1024. / 1024.);
							dest += sprintf(dest, "Bytes in Pages : %I64d (%.2fMB)\r\n"
								, n_bytes_pages, (double)n_bytes_pages / 1024. / 1024.);
							if(n_packets)
								dest += sprintf(dest, "Samples per Packet : %.1f\r\n"
									, (double)stream->n_samples / (double)n_packets);
							if(n_pages)
								dest += sprintf(dest, "Packets per Page : %.1f\r\n"
									, (double)n_packets / (double)n_pages);
							__int64 duration = 0;
							if(stream->sample_rate)
								duration = stream->n_samples * (__int64)1000 / stream->sample_rate;
							int h, m, s, ms;
							ms = duration % 1000;
							s = (duration / 1000) % 60;
							m = (duration / 1000 / 60) % 60;
							h = (duration / 1000 / 60 / 60);
							dest += sprintf(dest, "Duration : %02d:%02d:%02d.%03d\r\n"
								, h, m, s, ms);
							if(duration)
								dest += sprintf(dest, "Estimated Bitrate : %.1fkbps\r\n"
									, (double)n_bytes_packets * 8. / ((double)duration / 1000.) / 1000.);

							stream = streamNext;
						}
						dest += sprintf(dest, "\r\n\r\nTotal bytes in Packets : %I64d (%.2fMB)"
							, total_bytes_packets, (double)total_bytes_packets / 1024. / 1024.);
						dest += sprintf(dest, "\r\nTotal bytes in Pages : %I64d (%.2fMB)"
							, total_bytes_pages, (double)total_bytes_pages / 1024. / 1024.);
					} else {
						dest += sprintf(dest, "no streams found.");
					}
				} else {
					dest += sprintf(dest, "Error : no valid IAVIReadHandler provided.");
				}
			} else {
				dest += sprintf(dest, "The file is not an OGM file.");
			}
			SetDlgItemText(hdlg, IDC_CHANGES, report);
			SendMessage(hdlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_VIRTUALDUB)));
			guiReposInit(hdlg, ShowTextCtlPosData, ShowTextCtlPos);
		}
		return FALSE;
		break;

	case WM_CLOSE:
		EndDialog(hdlg, TRUE);
		return TRUE;
		break;

	case WM_COMMAND:
		if(HIWORD(wParam) == BN_CLICKED) {
			switch(LOWORD(wParam)) {

			case IDOK:
				EndDialog(hdlg, TRUE);
				return TRUE;
				break;
			}
		}
		return TRUE;
		break;

	case WM_GETMINMAXINFO:
		{
			LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;

			lpmmi->ptMinTrackSize.x = rInitial.right - rInitial.left;
			lpmmi->ptMinTrackSize.y = rInitial.bottom - rInitial.top;
		}
		return TRUE;

	case WM_SIZE:
		guiReposResize(hdlg, ShowTextCtlPosData, ShowTextCtlPos);
		return TRUE;
	}
	return FALSE;
}

ogm_stream *GetOGMStream(int index) {
	if(index < 0)
		return NULL;
	ogm_stream *stream = ogm_streams;
	while(stream) {
		if(!index--)
			break;
		stream = stream->next;
	}

	return stream;
}


static struct ReposItem OGMInputsCtlPosData[]={
	{ IDC_INPUTS		, REPOS_SIZERIGHT | REPOS_SIZEDOWN },
	{ IDC_ADD			, REPOS_MOVERIGHT },
	{ IDC_MOVEUP		, REPOS_MOVERIGHT },
	{ IDC_MOVEDOWN		, REPOS_MOVERIGHT },
	{ IDC_SHOWCOMMENTS	, REPOS_MOVERIGHT },
	{ IDC_OGMDELAY_STATIC, REPOS_MOVERIGHT },
	{ IDC_OGMDELAY		, REPOS_MOVERIGHT },
	{ IDC_REMOVE		, REPOS_MOVERIGHT },
	{ IDC_DEMUX			, REPOS_MOVERIGHT },
	{ IDC_SAVEWAV		, REPOS_MOVERIGHT },
	{ IDOK				, REPOS_MOVERIGHT },
	{ 0 }
};

POINT OGMInputsCtlPos[11];

BOOL APIENTRY OGMInputsDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	static char *szColumnNames[]={ "Name", "Use", NULL };
	static int iColumnWidths[]={ 425, 40 };
	static RECT rInitial;

	HWND hwndItem;
	HWND hDelay;

	switch(uiMsg) {
	case WM_INITDIALOG:
		{
			int i;

			hwndItem = GetDlgItem(hdlg, IDC_INPUTS);

			ListView_SetExtendedListViewStyleEx(hwndItem, LVS_EX_FULLROWSELECT , LVS_EX_FULLROWSELECT);

			LV_COLUMN lvc;

			GetWindowRect(hdlg, &rInitial);

			i = 0;
			while(szColumnNames[i]) {
				lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
				lvc.fmt = LVCFMT_LEFT;
				lvc.cx = iColumnWidths[i];
				lvc.pszText = szColumnNames[i];

				ListView_InsertColumn(hwndItem, i, &lvc);
				i++;
			}

			SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);

			SendMessage(hdlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_VIRTUALDUB)));

			guiReposInit(hdlg, OGMInputsCtlPosData, OGMInputsCtlPos);
		}
		return FALSE;

	case WM_CLOSE:
		EndDialog(hdlg, TRUE);
		return TRUE;

	case WM_NOTIFY:
		{
			hwndItem = GetDlgItem(hdlg, IDC_INPUTS);
			NMHDR *nm = (NMHDR *)lParam;

			if(nm->idFrom == IDC_INPUTS) {
				NMLVDISPINFO *nldi = (NMLVDISPINFO *)nm;
				NMLISTVIEW *nmlv;

				switch(nm->code) {
				case LVN_GETDISPINFO:
					Input_GetDispInfo(nldi);
					return TRUE;

				case NM_CLICK:
					{
						int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
						ogm_stream *stream = GetOGMStream(index);
						EnableWindow(GetDlgItem(hdlg, IDC_DEMUX), stream && (stream->type == AUDIO_TYPE_INPUTFILE));
						EnableWindow(GetDlgItem(hdlg, IDC_SAVEWAV), stream && (stream->type == AUDIO_TYPE_INPUTFILE));
						if(stream)
							SetDlgItemInt(hdlg, IDC_OGMDELAY, stream->offset, TRUE);
						else
							SetDlgItemText(hdlg, IDC_OGMDELAY, "");
					}
					return TRUE;

				case LVN_KEYDOWN:
					switch(((LPNMLVKEYDOWN)lParam)->wVKey) {
					case VK_DELETE:
						SendMessage(hdlg, WM_COMMAND, IDC_REMOVE, (LPARAM)GetDlgItem(hdlg, IDC_REMOVE));
					}
					return TRUE;

				case NM_DBLCLK:
					int index = ListView_GetNextItem(GetDlgItem(hdlg, IDC_INPUTS), -1, LVNI_ALL | LVNI_SELECTED);
					if(index>=0) {
						ogm_stream *stream = GetOGMStream(index);
						if(stream)
							stream->use = !stream->use;
						SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
					return TRUE;
				}
			}
		}
		break;

	case WM_COMMAND:
		hwndItem = GetDlgItem(hdlg, IDC_INPUTS);
		if (!lParam) {
			// menu hit

			return TRUE;

		} else if(LOWORD(wParam) == IDC_REFRESH_LIST) {
			// Update the list
			ListView_DeleteAllItems(hwndItem);

			ogm_stream *stream = ogm_streams;
			int i = 0;
			while(stream) {
				LVITEM li;

				li.mask	= LVIF_TEXT;
				li.iSubItem	= 0;
				li.iItem = i;
				//li.pszText = stream->audio->getName();
				li.pszText = LPSTR_TEXTCALLBACK;

				ListView_InsertItem(hwndItem, &li);

				stream = stream->next;
				i++;
			}
			return TRUE;
		} else if(LOWORD(wParam) == IDC_OGMDELAY) {

			hDelay = GetDlgItem(hdlg, IDC_OGMDELAY);
			int index = ListView_GetNextItem(GetDlgItem(hdlg, IDC_INPUTS), -1, LVNI_ALL | LVNI_SELECTED);
			if(index>=0) {
				ogm_stream *stream = GetOGMStream(index);
				if(stream && SendMessage(hDelay, EM_GETMODIFY, 0, 0)) {
					BOOL success = FALSE;
					long offset = GetDlgItemInt(hdlg, IDC_OGMDELAY, &success, TRUE);
					if(success) {
						stream->offset = offset;
					}
					SendMessage(hDelay, EM_SETMODIFY, (WPARAM)FALSE, 0);
				}
			}
			return TRUE;
		} else if(HIWORD(wParam) == BN_CLICKED) {
			hwndItem = GetDlgItem(hdlg, IDC_INPUTS);
			bool bAddWAVHeader = false;

			switch(LOWORD(wParam)) {

			case IDOK:
				EndDialog(hdlg, TRUE);
				return TRUE;

			case IDC_SHOWCOMMENTS:
				{
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					ogm_stream *stream = GetOGMStream(index);
					if(stream)
						DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_OGMCOMMENTS), hdlg, OGMCommentsDlgProc, (LPARAM)(void *)&stream->comments);
				}
				return TRUE;

			case IDC_REMOVE:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					ogm_stream *stream = GetOGMStream(index);
					ogm_stream *previous = GetOGMStream(index-1);
					// Is this a stream in our list ? (should be !!!)
					if(stream && (stream->type != AUDIO_TYPE_INPUTFILE)) {
						ListView_DeleteItem(hwndItem, index);
						// Update our list
						if(!previous)
							ogm_streams = stream->next;
						else
							previous->next = stream->next;
						// If this is an Ogg file, release its handler
						if(stream->type == AUDIO_TYPE_OGM)
							((OGMAudioSourceOGM *)stream->audio)->getHandler()->Release();
						// Delete the stream
						delete stream;
						// Redraw the list
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						if(ListView_GetItemCount(hwndItem) <= index)
							index = ListView_GetItemCount(hwndItem) - 1;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
				}
				return TRUE;

			case IDC_MOVEUP:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					ogm_stream *stream = GetOGMStream(index);
					ogm_stream *previous_1 = GetOGMStream(index-1);
					ogm_stream *previous_2 = GetOGMStream(index-2);
					// Is this a stream in our list ? (should be !!!)
					if(stream && previous_1) {
						if(previous_2) {
							previous_2->next = stream;
							previous_1->next = stream->next;
							stream->next = previous_1;
						} else {
							ogm_streams = stream;
							previous_1->next = stream->next;
							stream->next = previous_1;
						}
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						index--;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
				}
				return TRUE;

			case IDC_MOVEDOWN:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					ogm_stream *stream = GetOGMStream(index);
					ogm_stream *previous = GetOGMStream(index-1);
					ogm_stream *next = GetOGMStream(index+1);
					// Is this a stream in our list ? (should be !!!)
					if(stream && next) {
						if(previous) {
							previous->next = next;
							stream->next = next->next;
							next->next = stream;
						} else {
							ogm_streams = next;
							stream->next = next->next;
							next->next = stream;
						}
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						index++;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
				}
				return TRUE;

			case IDC_ADD:
				{
					OPENFILENAME ofn;
					char szFileTitle[MAX_PATH];
					szFileTitle[0]=0;
					char szFile[MAX_PATH];
					szFile[0]=0;

					ofn.lStructSize			= sizeof(OPENFILENAME);
					ofn.hwndOwner			= hdlg;
					ofn.lpstrFilter			= fileFiltersOGMInputs;
					ofn.lpstrCustomFilter	= NULL;
					ofn.nFilterIndex		= 1;
					ofn.lpstrFile			= szFile;
					ofn.nMaxFile			= sizeof szFile;
					ofn.lpstrFileTitle		= szFileTitle;
					ofn.nMaxFileTitle		= sizeof szFileTitle;
					ofn.lpstrInitialDir		= NULL;
					ofn.lpstrTitle			= "Open";
					ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING;
					ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ogg" : NULL;
					ofn.hInstance			= g_hInst;

					if(GetOpenFileName(&ofn)) {
						int type = AUDIO_TYPE_INPUTFILE;
						switch(ofn.nFilterIndex) {
						case 1: //Audio stream in Ogg file (likely to be a Vorbis stream)
							type = AUDIO_TYPE_OGM;
							if(!OpenOGMInputFile(szFile, type, hdlg))
								MessageBox(hdlg, "This doesn't seem to be a valid Ogg file", "Warning", MB_ICONWARNING | MB_OK);
							break;
						case 2: //AC3
							type = AUDIO_TYPE_AC3;
							if(!OpenOGMInputFile(szFile, type, hdlg))
								MessageBox(hdlg, "This doesn't seem to be a valid AC3 file", "Warning", MB_ICONWARNING | MB_OK);
							break;
						case 3: //MP3
							type = AUDIO_TYPE_MP3;
							if(!OpenOGMInputFile(szFile, type, hdlg))
								MessageBox(hdlg, "This doesn't seem to be a valid MP3 file", "Warning", MB_ICONWARNING | MB_OK);
							break;
						case 4: //SRT
							type = TEXT_TYPE_SRT;
							if(!OpenOGMInputFile(szFile, type, hdlg))
								MessageBox(hdlg, "This doesn't seem to be a valid SRT file", "Warning", MB_ICONWARNING | MB_OK);
							break;
						}
					}
				}
				return TRUE;

			case IDC_SAVEWAV:
				bAddWAVHeader = true;
			case IDC_DEMUX:
				{
					// What is the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					ogm_stream *stream = GetOGMStream(index);
					if(!stream) {
						MessageBox(hdlg, "No stream selected for demuxing", "Warning", MB_ICONWARNING | MB_OK);
						return TRUE;
					}
					if(stream->type != AUDIO_TYPE_INPUTFILE) {
						MessageBox(hdlg, "Only streams of the input file can be demuxed", "Warning", MB_ICONWARNING | MB_OK);
						return TRUE;
					}
					try {
						OPENFILENAME ofn;
						char szFileTitle[MAX_PATH];
						szFileTitle[0]=0;
						char szFile[MAX_PATH];
						szFile[0]=0;

						ofn.lStructSize			= sizeof(OPENFILENAME);
						ofn.hwndOwner			= g_hWnd;
						if(stream->audio->isText) {
							ofn.lpstrFilter			= fileFiltersSRT;
							ofn.lpstrTitle			= "Save SRT File";
							ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "srt" : NULL;
							if(bAddWAVHeader)
								MessageBox(hdlg, "This is an SRT stream. No WAV header will be added", "Warning", MB_ICONWARNING | MB_OK);
							if(!(outputAVI = new SRTOutputFile())) throw MyMemoryError();
						} else switch (stream->audio->getWaveFormat()->wFormatTag) {
							case 0x0050:  // MPEG3-Layer1/2
							case 0x0055:  // MPEG3-Layer3
								ofn.lpstrFilter			= fileFilters3;
								if(bAddWAVHeader)
									ofn.lpstrTitle			= "Save Waved-MP? File";
								else
									ofn.lpstrTitle			= "Save MP? File";
								ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "mp3" : NULL;
								if(!(outputAVI = new AudioOutputFile(bAddWAVHeader))) throw MyMemoryError();
								break;

							case 0x2000:  // AC3
								ofn.lpstrFilter			= fileFilters4;
								if(bAddWAVHeader)
									ofn.lpstrTitle			= "Save Waved-AC3 File";
								else
									ofn.lpstrTitle			= "Save AC3 File";
								ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ac3" : NULL;
								if(!(outputAVI = new AudioOutputFile(bAddWAVHeader))) throw MyMemoryError();
								break;

							case 0x0000:  // Vorbis ?
								// Let's see if this a Vorbis stream
								// First the Format should be WAVEFORMATEXTENSIBLE
								// Then the SubFormat field should be set to MEDIASUBTYPE_Vorbis
								if( (stream->audio->getFormatLen() >= sizeof(WAVEFORMATEXTENSIBLE))
									&& (((WAVEFORMATEXTENSIBLE *)stream->audio->getFormat())->SubFormat == MEDIASUBTYPE_Vorbis) ) {
									ofn.lpstrFilter			= fileFilters5;
									ofn.lpstrTitle			= "Save Ogg (Vorbis) File";
									ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ogg" : NULL;
									if(bAddWAVHeader)
										MessageBox(hdlg, "This is a Vorbis stream. No WAV header will be added", "Warning", MB_ICONWARNING | MB_OK);
									if(!(outputAVI = new OGMOutputFile(video_comments, audio_comments, audio2_comments, true)))
										throw MyMemoryError();
									else
										((OGMOutputFile *)outputAVI)->disable_os_caching();
									break;
								}

							default:
								ofn.lpstrFilter			= fileFilters2;
								if(bAddWAVHeader) {
									ofn.lpstrTitle			= "Save WAV File";
									ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "wav" : NULL;
								} else {
									ofn.lpstrTitle			= "Save Audio File";
									ofn.lpstrDefExt			= NULL;
								}
								if(!(outputAVI = new AudioOutputFile(bAddWAVHeader))) throw MyMemoryError();
								break;
						}
						ofn.lpstrCustomFilter	= NULL;
						ofn.nFilterIndex		= 1;
						ofn.lpstrFile			= szFile;
						ofn.nMaxFile			= sizeof szFile;
						ofn.lpstrFileTitle		= szFileTitle;
						ofn.nMaxFileTitle		= sizeof szFileTitle;
						ofn.lpstrInitialDir		= NULL;
						ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT;

						if(GetSaveFileName(&ofn)) {
							try {
								ogm_stream *streams = ogm_streams;
								while(streams) {
									if(streams == stream)
										streams->to_demux = true;
									else
										streams->to_demux = false;

									streams = streams->next;
								}

								InitDubOGM(szFile, TRUE, NULL, 0, false, 0, 0);

								CloseNewAVI();
							} catch(const MyError& e) {
								CloseNewAVI();
								e.post(NULL, g_szError);
							}
						}
					} catch(...) {
						CloseNewAVI();
						throw;
					}
				}
				return TRUE;
			}
		}
		break;

	case WM_GETMINMAXINFO:
		{
			LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;

			lpmmi->ptMinTrackSize.x = rInitial.right - rInitial.left;
			lpmmi->ptMinTrackSize.y = rInitial.bottom - rInitial.top;
		}
		return TRUE;

	case WM_SIZE:
		guiReposResize(hdlg, OGMInputsCtlPosData, OGMInputsCtlPos);
		return TRUE;

	}
	return FALSE;
}

static void Input_GetDispInfo(NMLVDISPINFO *nldi) {
	ogm_stream *stream = ogm_streams;
	int index = nldi->item.iItem;
	while(stream) {
		if(!index--)
			break;
		stream = stream->next;
	}

	nldi->item.mask			= LVIF_TEXT;
	nldi->item.pszText[0]	= 0;

	if(!stream) {
		nldi->item.pszText = "Error! Invalid entry";
		return;
	}

	switch(nldi->item.iSubItem) {
	case 0: // input name
		nldi->item.pszText = stream->audio->getDesc();
		break;
	case 1: // input enabled ?
		nldi->item.pszText = stream->use ? "Yes" : "No";
		break;
	}
}


BOOL OpenOGMInputFile(char *szFile, int type, HWND hdlg, long offset) {
	OGMAudioSource *source = NULL;
	comment_list *comments = NULL;
	BOOL valid = FALSE;
	switch(type) {
	case AUDIO_TYPE_OGM: //Audio stream in Ogg file (likely to be a Vorbis stream)
		{
			OGMReadHandler *readHandler = (OGMReadHandler *)CreateOGMReadHandler(szFile);
			if(!readHandler)
				throw MyMemoryError();
			source = new OGMAudioSourceOGM(szFile, readHandler, 0);
			if(!source->init()) {
				valid = FALSE;
			} else {
				valid = TRUE;
				comments = ((OGMReadStream *)((OGMAudioSourceOGM *)source)->getStream())->getComments();
			}
		}
	break;
	case AUDIO_TYPE_AC3: //AC3
		source = new OGMAudioSourceAC3(szFile, 1*1024*1024);
		if(!source)
			throw MyMemoryError();
		if(!source->init()) {
			valid = FALSE;
		} else {
			valid = TRUE;
		}
		break;
	case AUDIO_TYPE_MP3: //MP3
		source = new OGMAudioSourceMP3(szFile, 1*1024*1024);
		if(!source)
			throw MyMemoryError();
		if(!source->init()) {
			valid = FALSE;
		} else {
			valid = TRUE;
		}
		break;
	case TEXT_TYPE_SRT: //SRT
		source = new OGMTextSourceSRT(szFile);
		if(!source)
			throw MyMemoryError();
		if(!source->init()) {
			valid = FALSE;
		} else {
			valid = TRUE;
		}
		break;
	}
	if(valid) {
		AddOGMStream(source, type, comments, offset);
		if(hdlg)
			SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
		/*LVITEM li;

		li.mask	= LVIF_TEXT;
		li.iSubItem	= 0;
		li.pszText= source->getName();

		li.iItem = AddOGMStream(source, type, comments);

		if(hwndItem) {
			ListView_InsertItem(hwndItem, &li);
			ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
		}*/
	}
	return valid;
}

int AddOGMStream(OGMAudioSource *source, int type, comment_list *comments, long offset) {
	ogm_stream *stream = NULL;
	int ind=0;
	if(ogm_streams) {
		stream = ogm_streams;
		while(stream->next) {
			stream = stream->next;
			ind++;
		}
		stream->next = new ogm_stream;
		if(!stream->next)
			throw MyMemoryError();
		stream = stream->next;
		ind++;
	} else {
		stream = ogm_streams = new ogm_stream;
		if(!stream)
			throw MyMemoryError();
	}
	stream->audio = source;
	stream->type = type;
	stream->comments = comments;
	stream->offset = offset;
	return ind;
}

void AddInputFileStreams(char *szFile) {
	if(!inputAVI)
		return;
	if(!inputAVI->isOGM)
		return;
	GetInputComments();
	MoveMainAudio();
	OGMReadHandler *handler = (OGMReadHandler *)((InputFileAVI *)inputAVI)->getHandler();
	int stream = 0;
	while(AddInputAudioStream(szFile, stream++));
	stream = 0;
	while(AddInputTextStream(szFile, stream++));
}

void RemoveInputStreams(void) {
	// Delete stream informations
	ogm_stream *stream = ogm_streams;
	while(ogm_streams) {
		stream = ogm_streams->next;
		// If this is an Ogg file, release its handler
		if(ogm_streams->type == AUDIO_TYPE_OGM)
			((OGMAudioSourceOGM *)ogm_streams->audio)->getHandler()->Release();
		delete ogm_streams;
		ogm_streams = stream;
	}
}

void GetInputComments(void) {
	if(!inputAVI)
		return;
	if(!inputAVI->isOGM)
		return;
	if(inputVideoAVI)
		video_comments = ((OGMReadStream *)((VideoSourceAVI *)inputVideoAVI)->getStream())->getComments();
}

void MoveMainAudio(void) {
	if(!inputAVI)
		return;
	if(!inputAVI->isOGM)
		return;
	if(inputAudioAVI) {
		// For OGM file, we will treat all streams in "Show inputs", even the main audio
		//audio_comments = ((OGMReadStream *)((AudioSourceAVI *)inputAudioAVI)->getStream())->getComments();
		// Normally inputAudioAVI == inputAVI->audioSrc
		if(inputAVI->audioSrc != inputAudioAVI)
			delete inputAVI->audioSrc;
		// If the inputAudio selected is the one from the input OGM file
		// reset it
		if(inputAudio == inputAudioAVI)
			inputAudio = NULL;
		// Delete the source since not needed anymore here
		delete inputAudioAVI;
		// Reset inputAudioAVI and inputAVI->audioSrc for
		// preventing VirtualDub from recreating it (when selecting "AVI audio")
		inputAudioAVI = NULL;
		inputAVI->audioSrc = NULL;
		// 2nd stream
		if(inputAVI->audio2Src != inputAudio2AVI)
			delete inputAVI->audio2Src;
		// If the inputAudio selected is the one from the input OGM file
		// reset it
		if(inputAudio2 == inputAudio2AVI)
			inputAudio2 = NULL;
		// Delete the source since not needed anymore here
		delete inputAudio2AVI;
		// Reset inputAudioAVI and inputAVI->audioSrc for
		// preventing VirtualDub from recreating it (when selecting "AVI audio")
		inputAudio2AVI = NULL;
		inputAVI->audio2Src = NULL;
	}
}

BOOL AddInputAudioStream(char *szFile, int stream, long offset) {
	if(!inputAVI)
		return FALSE;
	if(!inputAVI->isOGM)
		return FALSE;
	OGMReadHandler *handler = (OGMReadHandler *)((InputFileAVI *)inputAVI)->getHandler();
	OGMAudioSourceOGM *source = new OGMAudioSourceOGM(szFile, handler, stream, true);
	if(!source->init()) {
		delete source;
		return FALSE;
	}
	AddOGMStream(source, AUDIO_TYPE_INPUTFILE, ((OGMReadStream *)source->getStream())->getComments(), offset);
	return TRUE;
}

BOOL AddInputTextStream(char *szFile, int stream, long offset) {
	if(!inputAVI)
		return FALSE;
	if(!inputAVI->isOGM)
		return FALSE;
	OGMReadHandler *handler = (OGMReadHandler *)((InputFileAVI *)inputAVI)->getHandler();
	OGMTextSourceOGM *source = new OGMTextSourceOGM(szFile, handler, stream, true);
	if(!source->init()) {
		delete source;
		return FALSE;
	}
	AddOGMStream(source, AUDIO_TYPE_INPUTFILE, ((OGMReadStream *)source->getStream())->getComments(), offset);
	return TRUE;
}

comment_list *GetOGMComments(comment_list *comments, int index) {
	if(!comments || (index<0) )
		return NULL;
	while(comments) {
		if(!index--)
			break;
		comments = comments->next;
	}
	return comments;
}


static struct ReposItem OGMCommentsCtlPosData[]={
	{ IDC_COMMENTS		, REPOS_SIZERIGHT | REPOS_SIZEDOWN },
	{ IDC_MOVEUP		, REPOS_MOVERIGHT },
	{ IDC_MOVEDOWN		, REPOS_MOVERIGHT },
	{ IDC_COMMENTCHAPTERS, REPOS_MOVERIGHT },
	{ IDC_REMOVE		, REPOS_MOVERIGHT },
	{ IDC_REPLACE		, REPOS_MOVERIGHT | REPOS_MOVEDOWN },
	{ IDC_ADD			, REPOS_MOVERIGHT | REPOS_MOVEDOWN },
	{ IDC_COMMENTTAG	, REPOS_MOVEDOWN },
	{ IDC_EQUAL_STATIC	, REPOS_MOVEDOWN },
	{ IDC_COMMENTCOMMENT, REPOS_MOVEDOWN | REPOS_SIZERIGHT },
	{ IDC_COMMENTTAGLIST, REPOS_MOVEDOWN },
	{ IDC_COMMENTLANGLIST, REPOS_MOVEDOWN },
	{ IDOK				, REPOS_MOVERIGHT },
	{ 0 }
};

POINT OGMCommentsCtlPos[13];

BOOL APIENTRY OGMCommentsDlgProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
	static char *szColumnNames[]={ "Comment" };
	static int iColumnWidths[]={ 500 };
	static int nb_columns = 1;
	static RECT rInitial;

	static char *szTags[] = { "TITLE", "LANGUAGE", "LWING_GAIN"
		, "DESCRIPTION", "GENRE", "DATE", "VERSION"
		, "COPYRIGHT", "LICENSE", "ARTIST", "PERFORMER"
	  , "ALBUM", "TRACKNUMBER", "ORGANIZATION"
	  , "LOCATION", "CONTACT", "ISRC"
	  , NULL};
	static int index_language_tag = 1;
	static char *szLangs[] = {"Director's Comments"
		,"Afrikaans","Albanian","Arabic","Basque","Belarusian","Bulgarian"
		,"Catalan","Chinese","Croatian","Czech","Danish","Dutch","English"
		,"Estonian","Faeroese","Farsi","Finnish","French","German","Greek"
		,"Hebrew","Hungarian","Icelandic","Indonesian","Italian","Japanese"
		,"Korean","Latvian","Lithuanian","Malay","Norwegian","Polish"
		,"Portuguese","Romanian","Russian","Serbian","Slovak","Slovenian"
		,"Spanish","Swahili","Swedish","Thai","Turkish","Ukrainian", NULL };

	HWND hwndItem;
	HWND hTag;
	HWND hComment;

	static comment_list **p_comments;

	switch(uiMsg) {
	case WM_INITDIALOG:
		{
			HWND hwndItem;
			int i;

			hwndItem = GetDlgItem(hdlg, IDC_COMMENTS);

			ListView_SetExtendedListViewStyle(hwndItem, LVS_EX_FULLROWSELECT);

			LV_COLUMN lvc;

			GetWindowRect(hwndItem, &rInitial);

			i = 0;
			//for(i=0; i<nb_columns; i++) {
				lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
				lvc.fmt = LVCFMT_LEFT;
				//lvc.cx = iColumnWidths[i];
				lvc.cx = rInitial.right - rInitial.left - 5;
				lvc.pszText = szColumnNames[i];

				ListView_InsertColumn(hwndItem, i, &lvc);
			//}

			GetWindowRect(hdlg, &rInitial);

			p_comments = (comment_list **)lParam;
			//Update the list
			SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);

			hwndItem = GetDlgItem(hdlg, IDC_COMMENTTAGLIST);
			i = 0;
			while(szTags[i]) {
				SendMessage(hwndItem, CB_ADDSTRING, 0, (LPARAM)szTags[i]);
				i++;
			}
			hwndItem = GetDlgItem(hdlg, IDC_COMMENTLANGLIST);
			i = 0;
			while(szLangs[i]) {
				SendMessage(hwndItem, CB_ADDSTRING, 0, (LPARAM)szLangs[i]);
				i++;
			}

			SendMessage(hdlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_VIRTUALDUB)));

			guiReposInit(hdlg, OGMCommentsCtlPosData, OGMCommentsCtlPos);
		}
		return FALSE;

	case WM_CLOSE:
		EndDialog(hdlg, TRUE);
		return TRUE;

	case WM_NOTIFY:
		{
			NMHDR *nm = (NMHDR *)lParam;

			if (nm->idFrom == IDC_COMMENTS) {
				NMLVDISPINFO *nldi = (NMLVDISPINFO *)nm;
				NMLISTVIEW *nmlv;

				switch(nm->code) {
				case LVN_GETDISPINFO:
					Comments_GetDispInfo(nldi, p_comments);
					return TRUE;

				case NM_CLICK:
					{
						hwndItem = GetDlgItem(hdlg, IDC_COMMENTS);
						int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
						comment_list *comment = NULL;
						if(p_comments)
							comment = GetOGMComments(*p_comments, index);
						if(comment) {
							hTag = GetDlgItem(hdlg, IDC_COMMENTTAG);
							hComment = GetDlgItem(hdlg, IDC_COMMENTCOMMENT);
							SetWindowText(hTag, comment->tag);
							SetWindowText(hComment, comment->comment);
							if(comment->tag && !strncmp(comment->tag, "LANGUAGE", 9)) {
								HWND hLangList = GetDlgItem(hdlg, IDC_COMMENTLANGLIST);
								ShowWindow(hLangList, SW_SHOW);
								SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_SETCURSEL, -1, 0);
								bool found_index = false;
								// Try to find the corresponding line in the combo box
								int n_lang = SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETCOUNT, 0, 0);
								if((n_lang != CB_ERR) && (n_lang>0)) {
									for(int index=0 ; index<n_lang ; index++) {
									  int length = SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETLBTEXTLEN, index, 0) + 1;
										if( (length != CB_ERR) && (length > 0) ) {
											char *lang = (char *)malloc(length);
											if(!lang)
												throw MyMemoryError();
											SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETLBTEXT, index, (LPARAM)lang);
											if(!strncmp(lang, comment->comment, length)) {
												SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_SETCURSEL, index, 0);
												free(lang);
												found_index = true;
												break;
											}
											free(lang);
										}
									}
								}
								// If no exact matching, try a partial matching
								if(!found_index) {
									SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_SELECTSTRING, -1, (LPARAM)comment->comment);
								}
							} else {
								HWND hLangList = GetDlgItem(hdlg, IDC_COMMENTLANGLIST);
								ShowWindow(hLangList, SW_HIDE);
							}
						}
					}
					return TRUE;

				case LVN_KEYDOWN:
					switch(((LPNMLVKEYDOWN)lParam)->wVKey) {
					case VK_DELETE:
						SendMessage(hdlg, WM_COMMAND, IDC_REMOVE, (LPARAM)GetDlgItem(hdlg, IDC_REMOVE));
					}
					return TRUE;
				}
			}
		}
		break;

	case WM_COMMAND:
		{
		// If no list to add or remove comments, no need to do anything :(
		if(!p_comments)
			return TRUE;
		hwndItem = GetDlgItem(hdlg, IDC_COMMENTS);
		hTag = GetDlgItem(hdlg, IDC_COMMENTTAG);
		hComment = GetDlgItem(hdlg, IDC_COMMENTCOMMENT);
		HWND hLangList = GetDlgItem(hdlg, IDC_COMMENTLANGLIST);
		if(!lParam) {
			// menu hit

			return TRUE;

		} else if(LOWORD(wParam) == IDC_REFRESH_LIST) {
			// Update the list
			ListView_DeleteAllItems(hwndItem);

			int i = 0;
			comment_list *comment = *p_comments;
			while(comment) {
				LVITEM li;

				li.mask	= LVIF_TEXT;
				li.iSubItem	= 0;
				li.iItem = i;
				/*int length = 0;
				if(comment->tag)
					length += strlen(comment->tag)+1;
				if(comment->comment)
					length += strlen(comment->comment)+1;
				if(length) {
					length += 10;
					char *text = (char *)malloc(length);
					if(!text)
						throw MyMemoryError();
					if(comment->tag)
						sprintf(text, "%s=%s", comment->tag, comment->comment);
					else
						sprintf(text, "%s", comment->comment);
					li.pszText = text;

					ListView_InsertItem(hwndItem, &li);

					free(text);
				}*/
				li.pszText = LPSTR_TEXTCALLBACK;
				ListView_InsertItem(hwndItem, &li);
				comment = comment->next;
				i++;
			}
			return TRUE;
		} else if(LOWORD(wParam) == IDC_COMMENTLANGLIST) {
			if(HIWORD(wParam) == CBN_SELCHANGE) {
				int index = SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETCURSEL, 0, 0);
			  int length = SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETLBTEXTLEN, index, 0) + 1;
				if( (length != CB_ERR) && (length > 0) ) {
					char *value = (char *)malloc(length);
					if(!value)
						throw MyMemoryError();
					SendDlgItemMessage(hdlg, IDC_COMMENTLANGLIST, CB_GETLBTEXT, index, (LPARAM)value);
					SetWindowText(hComment, value);
					free(value);
				}
			}
		} else if(LOWORD(wParam) == IDC_COMMENTTAGLIST) {
			if(HIWORD(wParam) == CBN_SELCHANGE) {
				int index = SendDlgItemMessage(hdlg, IDC_COMMENTTAGLIST, CB_GETCURSEL, 0, 0);
			  int length = SendDlgItemMessage(hdlg, IDC_COMMENTTAGLIST, CB_GETLBTEXTLEN, index, 0) + 1;
				if( (length != CB_ERR) && (length > 0) ) {
					char *tag = (char *)malloc(length);
					if(!tag)
						throw MyMemoryError();
					SendDlgItemMessage(hdlg, IDC_COMMENTTAGLIST, CB_GETLBTEXT, index, (LPARAM)tag);
					SetWindowText(hTag, tag);
					if(!strncmp(tag, "LANGUAGE", 9)) {
						ShowWindow(hLangList, SW_SHOW);
					} else {
						ShowWindow(hLangList, SW_HIDE);
					}
					free(tag);
				}
			}
		} else if(HIWORD(wParam) == BN_CLICKED) {

			switch(LOWORD(wParam)) {

			case IDOK:
				EndDialog(hdlg, TRUE);
				return TRUE;

			case IDC_COMMENTCHAPTERS:
				{
					OPENFILENAME ofn;
					char szFileTitle[MAX_PATH];
					szFileTitle[0]=0;
					char szFile[MAX_PATH];
					szFile[0]=0;

					ofn.lStructSize			= sizeof(OPENFILENAME);
					ofn.hwndOwner			= hdlg;
					ofn.lpstrFilter			= fileFiltersTxt;
					ofn.lpstrCustomFilter	= NULL;
					ofn.nFilterIndex		= 1;
					ofn.lpstrFile			= szFile;
					ofn.nMaxFile			= sizeof szFile;
					ofn.lpstrFileTitle		= szFileTitle;
					ofn.nMaxFileTitle		= sizeof szFileTitle;
					ofn.lpstrInitialDir		= NULL;
					ofn.lpstrTitle			= "Open";
					ofn.Flags				= OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_ENABLESIZING;
					ofn.lpstrDefExt			= g_prefs.main.fAttachExtension ? "ogg" : NULL;
					ofn.hInstance			= g_hInst;

					if(GetOpenFileName(&ofn)) {
						chapter_mesh *chapter_names = NULL;
						chapter_mesh *chapter_times = NULL;
						if(import_chapters(szFile, &chapter_names, &chapter_times)) {
							chapter_mesh *name = chapter_names;
							chapter_mesh *time = chapter_times;
							int num_chapter = 1;
							char *c_tag = NULL;
							c_tag = (char *)malloc(20);
							if(!c_tag)
								throw MyMemoryError();
							while(name && time) {
								sprintf(c_tag, "CHAPTER%02d", num_chapter);
								AddComment(p_comments, c_tag, time->info);
								sprintf(c_tag, "CHAPTER%02dNAME", num_chapter);
								AddComment(p_comments, c_tag, name->info);
								num_chapter++;
								name = name->next;
								time = time->next;
							}
							delete c_tag;
						} else {
							MessageBox(hdlg, "This doesn't seem to be a valid chapter file.\n"
								"Supported formats are :\n"
								" - SmartRipper\n"
								" - chapterXtractor"
								, "Warning", MB_ICONWARNING | MB_OK);
						}
						delete_chapters(&chapter_names);
						delete_chapters(&chapter_times);
						//Update the list
						SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
					}
				}
				return TRUE;

			case IDC_REMOVE:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					comment_list *comment = GetOGMComments(*p_comments, index);
					comment_list *previous = GetOGMComments(*p_comments, index-1);
					// Is this comment in our list ? (should be !!!)
					if(comment) {
						ListView_DeleteItem(hwndItem, index);
						// Update our list
						if(!previous)
							*p_comments = comment->next;
						else
							previous->next = comment->next;
						delete comment;
						// Redraw the list
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						if(ListView_GetItemCount(hwndItem) <= index)
							index = ListView_GetItemCount(hwndItem) - 1;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
						//Don't delete Tag and Comment in Boxes so that the user
						//can insert them again if they were deleted by error
						/*SetWindowText(hTag, NULL);
						SetWindowText(hComment, NULL);*/
					}
					ShowWindow(hLangList, SW_HIDE);
				}
				return TRUE;

			case IDC_MOVEUP:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					comment_list *comment = GetOGMComments(*p_comments, index);
					comment_list *previous_1 = GetOGMComments(*p_comments, index-1);
					comment_list *previous_2 = GetOGMComments(*p_comments, index-2);
					// Is this a stream in our list ? (should be !!!)
					if(comment && previous_1) {
						if(previous_2) {
							previous_2->next = comment;
							previous_1->next = comment->next;
							comment->next = previous_1;
						} else {
							*p_comments = comment;
							previous_1->next = comment->next;
							comment->next = previous_1;
						}
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						index--;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
				}
				return TRUE;

			case IDC_MOVEDOWN:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					comment_list *comment = GetOGMComments(*p_comments, index);
					comment_list *previous = GetOGMComments(*p_comments, index-1);
					comment_list *next = GetOGMComments(*p_comments, index+1);
					// Is this a stream in our list ? (should be !!!)
					if(comment && next) {
						if(previous) {
							previous->next = next;
							comment->next = next->next;
							next->next = comment;
						} else {
							*p_comments = next;
							comment->next = next->next;
							next->next = comment;
						}
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						index++;
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, -1);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					}
				}
				return TRUE;

			case IDC_ADD:
				{
					int tag_l = GetWindowTextLength(hTag);
					int comment_l = GetWindowTextLength(hComment);
					if( (tag_l<=0) && (comment_l<=0) )
						return TRUE;
					// Is there a selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					if(index < 0)
						index = ListView_GetItemCount(hwndItem);
					comment_list *comment = GetOGMComments(*p_comments, index);
					comment_list *previous = GetOGMComments(*p_comments, index-1);
					if(comment) {
						if(!previous) {
							*p_comments = new comment_list;
							if(!*p_comments)
								throw MyMemoryError();
							(*p_comments)->next = comment;
							comment = *p_comments;
						} else {
							comment = new comment_list;
							if(!comment)
								throw MyMemoryError();
							comment->next = previous->next;
							previous->next = comment;
						}
					} else {
						if(!previous) {
							*p_comments = new comment_list;
							if(!*p_comments)
								throw MyMemoryError();
							comment = *p_comments;
						} else {
							comment = previous->next = new comment_list;
							if(!comment)
								throw MyMemoryError();
						}
					}
					if(tag_l > 0) {
						comment->tag = (char *)malloc(tag_l+1);
						memset(comment->tag, 0, tag_l+1);
						GetWindowText(hTag, comment->tag, tag_l+1);
					}
					if(comment_l > 0) {
						comment->comment = (char *)malloc(comment_l+1);
						memset(comment->comment, 0, comment_l+1);
						GetWindowText(hComment, comment->comment, comment_l+1);
					}
					// Redraw the list
					//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
					LVITEM li;
					li.mask	= LVIF_TEXT;
					li.iSubItem	= 0;
					li.iItem = index;
					li.pszText = LPSTR_TEXTCALLBACK;
					ListView_InsertItem(hwndItem, &li);
					ListView_SetItemState(hwndItem, index+1, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
					ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
					SetWindowText(hTag, NULL);
					SetWindowText(hComment, NULL);
					ShowWindow(hLangList, SW_HIDE);
				}
				return TRUE;

			case IDC_REPLACE:
				{
					// What it the selected item ?
					int index = ListView_GetNextItem(hwndItem, -1, LVNI_ALL | LVNI_SELECTED);
					comment_list *comment = GetOGMComments(*p_comments, index);
					// Is this comment in our list ? (should be !!!)
					if(comment) {
						int tag_l = GetWindowTextLength(hTag);
						int comment_l = GetWindowTextLength(hComment);

						if( (tag_l <= 0) && (comment_l <= 0) )
							return TRUE;

						if(comment->tag)
							delete comment->tag;
						comment->tag = NULL;

						if(comment->comment)
							delete comment->comment;
						comment->comment = NULL;

						if(tag_l > 0) {
							comment->tag = (char *)malloc(tag_l+1);
							memset(comment->tag, 0, tag_l+1);
							GetWindowText(hTag, comment->tag, tag_l+1);
						}
						if(comment_l > 0) {
							comment->comment = (char *)malloc(comment_l+1);
							memset(comment->comment, 0, comment_l+1);
							GetWindowText(hComment, comment->comment, comment_l+1);
						}

						// Redraw the list
						//SendMessage(hdlg, WM_COMMAND, IDC_REFRESH_LIST, (LPARAM)hdlg);
						ListView_SetItemState(hwndItem, index, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
						ListView_RedrawItems(hwndItem, 0, ListView_GetItemCount(hwndItem));
						/*SetWindowText(hTag, NULL);
						SetWindowText(hComment, NULL);*/
					}
				}
				return TRUE;
			}
		}
		}
		break;

	case WM_GETMINMAXINFO:
		{
			LPMINMAXINFO lpmmi = (LPMINMAXINFO)lParam;

			lpmmi->ptMinTrackSize.x = rInitial.right - rInitial.left;
			lpmmi->ptMinTrackSize.y = rInitial.bottom - rInitial.top;
		}
		return TRUE;

	case WM_SIZE:
		guiReposResize(hdlg, OGMCommentsCtlPosData, OGMCommentsCtlPos);
		return TRUE;

	}
	return FALSE;
}

static void Comments_GetDispInfo(NMLVDISPINFO *nldi, comment_list **p_comments) {
	comment_list *comment = NULL;
	if(p_comments)
		comment = *p_comments;
	int index = nldi->item.iItem;
	while(comment) {
		if(!index--)
			break;
		comment = comment->next;
	}

	nldi->item.mask			= LVIF_TEXT;
	nldi->item.pszText[0]	= 0;

	if(!comment) {
		nldi->item.pszText = "Error! Invalid entry";
		return;
	}

	switch(nldi->item.iSubItem) {
	case 0: // Comment
		{
			if(comment->tag || comment->comment) {
				if(comment->tag)
					_snprintf(nldi->item.pszText, nldi->item.cchTextMax
					, "%s=%s", comment->tag, comment->comment);
				else
					_snprintf(nldi->item.pszText, nldi->item.cchTextMax
					, "%s", comment->comment);
			}
		}
		break;
	}
}

void add_chapter(chapter_mesh **pmesh, char *info) {
	if(!pmesh || !info)
		return;

	chapter_mesh *chapters = *pmesh;

	if(chapters) {
		chapter_mesh *mesh = chapters;
		while(mesh->next) {
			mesh = mesh->next;
		}
		mesh->next = new chapter_mesh;
		if(!mesh->next)
			throw MyMemoryError();

		mesh->next->info = (char *)malloc(strlen(info)+1);
		if(!mesh->next->info) {
			_RPT1(_CRT_WARN, "Could not malloc %d bytes for a chapter info", strlen(info)+1);
			delete mesh->next;
			mesh->next = NULL;
			return;
		}
		strcpy(mesh->next->info, info);
	} else {
		chapters = new chapter_mesh;
		if(!chapters)
			throw MyMemoryError();

		chapters->info = (char *)malloc(strlen(info)+1);
		if(!chapters->info) {
			_RPT1(_CRT_WARN, "Could not malloc %d bytes for a chapter info", strlen(info)+1);
			delete chapters;
			chapters = NULL;
			return;
		}
		strcpy(chapters->info, info);
		*pmesh = chapters;
	}
}

void delete_chapters(chapter_mesh **pmesh) {
	chapter_mesh *next = NULL;
	while(*pmesh) {
		next = (*pmesh)->next;
		delete *pmesh;
		*pmesh = next;
	}
}

// Some code from OggMux (also put in OGMuxer)
BOOL import_chapters(char *file, chapter_mesh **chapter_names, chapter_mesh **chapter_times) {
	char buffer[256], temp[256] = "", parse;
	ifstream chapterfile;
	char *chapterlist;

	BOOL success = FALSE;

	if(file)
	{
		chapterfile.open(file, ios::in);
// Find format to parse
		chapterfile.getline(buffer,255);
		chapterlist=buffer;
		if(!strncmp(chapterlist, "*********", 9))  // SmartRipper
			parse='a';
		if(!strncmp(chapterlist, "CHAPTER01", 9))  // chapterXtractor
			parse='b';

		switch (parse)
		{
		case 'a':  // SmartRipper Format
			{
// Find the chapter names in file
				while (strncmp(chapterlist, "Chap#=Frame# Name", 17) && (!chapterfile.eof()))
				{
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
				}
// Chapternames found, now read them
				while (strlen(chapterlist) && (!chapterfile.eof()))
				{
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
					if(strlen(chapterlist))
					{
						while(*chapterlist != ' ')
							chapterlist++;
						strcpy(temp, chapterlist+1);
						chapterlist=temp;
						add_chapter(chapter_names, chapterlist);
					}
				}
// Find the chapter times in file
				while (strncmp(chapterlist, "Chap#=Time", 10) && (!chapterfile.eof()))
				{
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
				}
// Chaptertimes found, now read them
				while (strlen(chapterlist) && (!chapterfile.eof()))
				{
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
					if(strlen(chapterlist))
					{
						while(*chapterlist != '=')
							chapterlist++;
						strcpy(temp, chapterlist+1);
						strcat(temp, "0");
						chapterlist=temp;
						add_chapter(chapter_times, chapterlist);
					}
				}
			}
			success = TRUE;
			break;  // end of SmartRipper Parser

		case 'b':  // ChapterXtractor-Format
			{
				chapterlist=buffer;
				while(*chapterlist != '=')
					chapterlist++;
				strcpy(temp, chapterlist+1);
				chapterlist=temp;
				if(strlen(chapterlist) < 9)  // old format
					strcat(temp, ".000");
				chapterlist=temp;
				add_chapter(chapter_times, chapterlist);
				chapterfile.getline(buffer,255);
				chapterlist=buffer;
				while(*chapterlist != '=')
					chapterlist++;
				strcpy(temp, chapterlist+1);
				chapterlist=temp;
				add_chapter(chapter_names, chapterlist);
				while(strlen(chapterlist) && (!chapterfile.eof()))
				{
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
					if(strlen(chapterlist))
					{
						while(*chapterlist != '=')
							chapterlist++;
						strcpy(temp, chapterlist+1);
						chapterlist=temp;
						if (strlen(chapterlist) < 9)  // old format
							strcat(temp, ".000");
						chapterlist=temp;
						add_chapter(chapter_times, chapterlist);
					}
					chapterfile.getline(buffer,255);
					chapterlist=buffer;
					if(strlen(chapterlist))
					{
						while(*chapterlist != '=')
							chapterlist++;
						strcpy(temp, chapterlist+1);
						chapterlist=temp;
						add_chapter(chapter_names, chapterlist);
					}
				}
			}
			success = TRUE;
			break;  // end of ChapterXtractor-Format

		default:
			{
				fprintf(stderr, "Chapter file [%s] is not of a known format\n", file);
			}
			success = FALSE;
			break;  // end of default (unknown file format)
		} // of switch
		chapterfile.close();
	}
	return success;
}

void DeleteComments(void) {
	comment_list *comment = NULL;
	while(video_comments) {
		comment = video_comments->next;
		delete video_comments;
		video_comments = comment;
	}
	while(audio_comments) {
		comment = audio_comments->next;
		delete audio_comments;
		audio_comments = comment;
	}
	ogm_stream *stream = ogm_streams;
	while(stream) {
		while(stream->comments) {
			comment = stream->comments->next;
			delete stream->comments;
			stream->comments = comment;
		}
		stream = stream->next;
	}
}

void AddVideoComment(char *tag, char *comment) {
	AddComment(&video_comments, tag, comment);
}

void AddAudioComment(char *tag, char *comment) {
	AddComment(&audio_comments, tag, comment);
}

void AddAudio2Comment(char *tag, char *comment) {
	AddComment(&audio2_comments, tag, comment);
}

void AddComment(int index, int type, char *tag, char *comment) {
	ogm_stream *stream = ogm_streams;
	while(stream) {
		if(!index--)
			break;
		stream = stream->next;
	}
	if(stream && (stream->type==type))
		AddComment(&stream->comments, tag, comment);
}

void AddComment(comment_list **comments, char *tag, char *comment) {
	if(!comments || (!tag && !comment))
		return;

	int tag_l = 0;
	if(tag)
		tag_l = strlen(tag);
	int comment_l = 0;
	if(comment)
		comment_l = strlen(comment);

	if( (tag_l<=0) && (comment_l<=0) )
		return;

	comment_list *list = NULL;
	if(*comments) {
		list = *comments;
		while(list->next)
			list = list->next;
		list->next = new comment_list;
		if(!list->next)
			throw MyMemoryError();
		list = list->next;
	} else {
		list = *comments = new comment_list;
		if(!list)
			throw MyMemoryError();
	}
	if(tag_l)
		list->tag = strdup(tag);
	if(comment_l)
		list->comment = strdup(comment);
}

////////////////////////////////////////////////////////

void InitDubOGM(char *szFile, BOOL fAudioOnly, DubOptions *quick_options, int iPriority, bool fPropagateErrors, long lSpillThreshold, long lSpillFrameThreshold) {
	char szSpillPrefix[MAX_PATH];
	bool fError = false;
	MyError prop_err;
	DubOptions *opts;
	POINT pt;

	try {

		filters.DeinitFilters();
		filters.DeallocateBuffers();

		SetAudioSource();
		RecalcFrameSizes();

		CPUTest();

		// Create a dubber.

		if (!quick_options) {
			g_dubOpts.video.fShowDecompressedFrame = g_drawDecompressedFrame;
			g_dubOpts.fShowStatus = !!g_showStatusWindow;
		}

		opts = quick_options ? quick_options : &g_dubOpts;
		opts->perf.fDropFrames = g_fDropFrames;

		ogm_stream *streams = ogm_streams;
		// We want an OGMDubber :p
		if (!(g_dubber = CreateOGMDubber(opts, streams)))
			throw MyMemoryError();

		// Create dub status window

		g_dubStatus = CreateOGMDubStatusHandler(streams);

		if (opts->fMoveSlider)
			g_dubStatus->SetPositionCallback(g_fJobMode ? JobPositionCallback : PositionCallback);

		// Initialize the dubber.

		g_dubber->SetStatusHandler(g_dubStatus);
		g_dubber->SetInputFile(inputAVI);
		g_dubber->SetFrameRectangles(&g_rInputFrame, &g_rOutputFrame);
		pt.x = pt.y = 0;
		ClientToScreen(g_hWnd, &pt);
		g_dubber->SetClientRectOffset(pt.x, pt.y);

		if (!outputAVI->isPreview() && g_ACompressionFormat)
			g_dubber->SetAudioCompression(g_ACompressionFormat, g_ACompressionFormatSize);

		// As soon as we call Init(), this value is no longer ours to free.

		AVIOutput *pOutput = outputAVI;

		if (lSpillThreshold) {
			char szFile2[MAX_PATH];

			strcpy(szSpillPrefix, szFile);
			const_cast<char *>(SplitPathExt(szSpillPrefix))[0] = 0;

			strcpy(szFile2, szSpillPrefix);
			strcat(szFile2, ".00.avi");

			g_dubber->EnableSpill(szSpillPrefix, (__int64)(lSpillThreshold-1) << 20, lSpillFrameThreshold);
			outputAVI = NULL;
			g_dubber->Init(inputVideoAVI, inputAudio, inputAudio2, pOutput, szFile2, hDCWindow, &g_Vcompression);
		} else {
			outputAVI = NULL;
			g_dubber->Init(inputVideoAVI, inputAudio, inputAudio2, pOutput, szFile, hDCWindow, &g_Vcompression);
		}

		_RPT0(0,"Starting dub.\n");

		if (!quick_options) RedrawWindow(g_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);

		SetMenu(g_hWnd, hMenuDub);
		SetWindowLong(g_hWnd, GWL_WNDPROC, (DWORD)DubWndProc);

		g_dubber->Go(iPriority);

		if (g_dubber->isAbortedByUser())
			g_fJobAborted = true;

	} catch(char *s) {
		if (fPropagateErrors) {
			prop_err.setf(s);
			fError = true;
		} else
			MyError(s).post(g_hWnd,g_szError);
	} catch( MyError& err) {
		if (fPropagateErrors) {
			prop_err.TransferFrom(err);
			fError = true;
		} else
			err.post(g_hWnd,g_szError);
	}

	g_dubber->SetStatusHandler(NULL);
	delete g_dubStatus; g_dubStatus = NULL;

	_CrtCheckMemory();

	SetMenu(g_hWnd, hMenuNormal);
	MenuMRUListUpdate(g_hWnd);
	SetWindowLong(g_hWnd, GWL_WNDPROC, (DWORD)MainWndProc);

	if (inputAVI)
		guiSetTitle(g_hWnd, IDS_TITLE_IDLE, g_szInputAVIFileTitle);
	else
		guiSetTitle(g_hWnd, IDS_TITLE_NOFILE);

	_RPT0(0,"Ending dub.\n");

	delete g_dubber;		g_dubber = NULL;

	CloseNewAVI();

	if (!inputVideoAVI->setDecompressedFormat(24))
		if (!inputVideoAVI->setDecompressedFormat(32))
			if (!inputVideoAVI->setDecompressedFormat(16))
				inputVideoAVI->setDecompressedFormat(8);

	if (!quick_options) RedrawWindow(g_hWnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);

	if (fError && fPropagateErrors)
		throw prop_err;
}

//END ==========================//
