////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia 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.
//
// Nestopia 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 Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include "NstIoLog.hpp"
#include "NstWindowUser.hpp"
#include "NstWindowParam.hpp"
#include "NstResourceString.hpp"
#include "NstManagerPaths.hpp"
#include "NstApplicationInstance.hpp"
#include "NstDialogPaletteEditor.hpp"
#include "NstDialogVideoDecoder.hpp"
#include "NstDialogVideo.hpp"

namespace Nestopia
{
	namespace Window
	{
		NST_COMPILE_ASSERT
		(
			IDC_VIDEO_NTSC_TOP    == IDC_VIDEO_NTSC_LEFT + 1 &&
			IDC_VIDEO_NTSC_RIGHT  == IDC_VIDEO_NTSC_LEFT + 2 &&
			IDC_VIDEO_NTSC_BOTTOM == IDC_VIDEO_NTSC_LEFT + 3 &&
			IDC_VIDEO_PAL_LEFT    == IDC_VIDEO_NTSC_LEFT + 4 &&
			IDC_VIDEO_PAL_TOP     == IDC_VIDEO_NTSC_LEFT + 5 &&
			IDC_VIDEO_PAL_RIGHT   == IDC_VIDEO_NTSC_LEFT + 6 &&
			IDC_VIDEO_PAL_BOTTOM  == IDC_VIDEO_NTSC_LEFT + 7
		);

		NST_COMPILE_ASSERT
		(
			IDC_VIDEO_32_BIT == IDC_VIDEO_16_BIT + 1
		);

		NST_COMPILE_ASSERT
		(
			IDC_VIDEO_PALETTE_YUV      == IDC_VIDEO_PALETTE_AUTO + 1 &&
			IDC_VIDEO_PALETTE_RGB      == IDC_VIDEO_PALETTE_AUTO + 2 &&
			IDC_VIDEO_PALETTE_CUSTOM   == IDC_VIDEO_PALETTE_AUTO + 3 &&
			IDC_VIDEO_PALETTE_PATH     == IDC_VIDEO_PALETTE_AUTO + 4 &&
			IDC_VIDEO_PALETTE_BROWSE   == IDC_VIDEO_PALETTE_AUTO + 5 &&
			IDC_VIDEO_PALETTE_CLEAR    == IDC_VIDEO_PALETTE_AUTO + 6 &&
			IDC_VIDEO_PALETTE_EDITOR   == IDC_VIDEO_PALETTE_AUTO + 7
		);

		NST_COMPILE_ASSERT
		(
			Nes::Video::MIN_BRIGHTNESS == -100 && Nes::Video::DEFAULT_BRIGHTNESS == 0 && Nes::Video::MAX_BRIGHTNESS == +100 &&
			Nes::Video::MIN_SATURATION == -100 && Nes::Video::DEFAULT_SATURATION == 0 && Nes::Video::MAX_SATURATION == +100 &&
			Nes::Video::MIN_CONTRAST   == -100 && Nes::Video::DEFAULT_CONTRAST   == 0 && Nes::Video::MAX_CONTRAST   == +100
		);

		struct Video::Handlers
		{
			static const MsgHandler::Entry<Video> messages[];
			static const MsgHandler::Entry<Video> commands[];
		};

		const MsgHandler::Entry<Video> Video::Handlers::messages[] =
		{
			{ WM_INITDIALOG, &Video::OnInitDialog },
			{ WM_DESTROY,    &Video::OnDestroy    },
			{ WM_HSCROLL,    &Video::OnHScroll    }
		};

		const MsgHandler::Entry<Video> Video::Handlers::commands[] =
		{
			{ IDC_VIDEO_DEVICE,            &Video::OnCmdDevice         },
			{ IDC_VIDEO_MODE,              &Video::OnCmdMode           },
			{ IDC_VIDEO_16_BIT,            &Video::OnCmdBitDepth       },
			{ IDC_VIDEO_32_BIT,            &Video::OnCmdBitDepth       },
			{ IDC_VIDEO_EFFECTS,           &Video::OnCmdFilter         },
			{ IDC_VIDEO_FILTER_SETTINGS,   &Video::OnCmdFilterSettings },
			{ IDC_VIDEO_PALETTE_AUTO,      &Video::OnCmdPalType        },
			{ IDC_VIDEO_PALETTE_YUV,       &Video::OnCmdPalType        },
			{ IDC_VIDEO_PALETTE_RGB,       &Video::OnCmdPalType        },
			{ IDC_VIDEO_PALETTE_CUSTOM,    &Video::OnCmdPalType        },
			{ IDC_VIDEO_COLORS_ADVANCED,   &Video::OnCmdColorsAdvanced },
			{ IDC_VIDEO_COLORS_RESET,      &Video::OnCmdColorsReset    },
			{ IDC_VIDEO_PALETTE_BROWSE,    &Video::OnCmdPalBrowse      },
			{ IDC_VIDEO_PALETTE_CLEAR,     &Video::OnCmdPalClear       },
			{ IDC_VIDEO_PALETTE_EDITOR,    &Video::OnCmdPalEditor      },
			{ IDC_VIDEO_DEFAULT,           &Video::OnCmdDefault        },
			{ IDOK,                        &Video::OnCmdOk             }
		};

		Video::DefaultMode::DefaultMode()
		{
			if (::GetSystemMetrics( SM_CXSCREEN ) == DEFAULT_16_9_WIDTH && ::GetSystemMetrics( SM_CYSCREEN ) == DEFAULT_16_9_HEIGHT)
			{
				width = DEFAULT_16_9_WIDTH;
				height = DEFAULT_16_9_HEIGHT;
			}
			else
			{
				width = DEFAULT_4_3_WIDTH;
				height = DEFAULT_4_3_HEIGHT;
			}
		}

		const Video::DefaultMode Video::defaultMode;

		class Video::Settings::Backup
		{
			const Adapters::const_iterator adapter;
			const Modes::const_iterator mode;
			Filter::Settings* const filter;
			Filter::Settings filters[Filter::NUM_TYPES];
			const short screenCurvature;
			const bool autoPalette;
			const schar brightness;
			const schar saturation;
			const schar contrast;
			const schar hue;
			const schar sharpness;
			const schar colorResolution;
			const schar colorBleed;
			const schar colorArtifacts;
			const schar colorFringing;
			const Nes::Video::Decoder decoder;
			const Path palettePath;
			const Nes::Video::Palette::Mode paletteMode;
			const Nes::Video::Palette::CustomType paletteCustomType;
			uchar paletteData[Nes::Video::Palette::NUM_ENTRIES_EXT][3];

		public:

			Backup(const Settings& settings,Nes::Video nes)
			:
			adapter           (settings.adapter),
			mode              (settings.mode),
			filter            (settings.filter),
			screenCurvature   (settings.screenCurvature),
			autoPalette       (settings.autoPalette),
			brightness        (nes.GetBrightness()),
			saturation        (nes.GetSaturation()),
			contrast          (nes.GetContrast()),
			hue               (nes.GetHue()),
			sharpness         (nes.GetSharpness()),
			colorResolution   (nes.GetColorResolution()),
			colorBleed        (nes.GetColorBleed()),
			colorArtifacts    (nes.GetColorArtifacts()),
			colorFringing     (nes.GetColorFringing()),
			decoder           (nes.GetDecoder()),
			palettePath       (settings.palette),
			paletteMode       (nes.GetPalette().GetMode()),
			paletteCustomType (nes.GetPalette().GetCustomType())
			{
				for (uint i=0; i < Filter::NUM_TYPES; ++i)
					filters[i] = settings.filters[i];

				if (paletteMode == Nes::Video::Palette::MODE_CUSTOM)
					nes.GetPalette().GetCustom( paletteData, paletteCustomType );
			}

			void Restore(Settings& settings,Nes::Video nes) const
			{
				settings.adapter = adapter;
				settings.mode = mode;
				settings.filter = filter;

				for (uint i=0; i < Filter::NUM_TYPES; ++i)
					settings.filters[i] = filters[i];

				settings.screenCurvature = screenCurvature;
				settings.palette = palettePath;
				settings.autoPalette = autoPalette;

				nes.SetBrightness( brightness );
				nes.SetSaturation( saturation );
				nes.SetContrast( contrast );
				nes.SetHue( hue );
				nes.SetSharpness( sharpness );
				nes.SetColorResolution( colorResolution );
				nes.SetColorBleed( colorBleed );
				nes.SetColorArtifacts( colorArtifacts );
				nes.SetColorFringing( colorFringing );
				nes.SetDecoder( decoder );
				nes.GetPalette().SetMode( paletteMode );

				if (paletteMode == Nes::Video::Palette::MODE_CUSTOM)
					nes.GetPalette().SetCustom( paletteData, paletteCustomType );
			}
		};

		Video::Settings::Settings()
		: fullscreenScale(SCREEN_MATCHED), backup(NULL) {}

		Video::Settings::~Settings()
		{
			delete backup;
		}

		Video::Video
		(
			Managers::Emulator& emulator,
			const Adapters& a,
			const Managers::Paths& p,
			const Configuration& cfg
		)
		:
		adapters ( a ),
		nes      ( emulator ),
		dialog   ( IDD_VIDEO, this, Handlers::messages, Handlers::commands ),
		paths    ( p )
		{
			if (cfg["view"]["size"]["fullscreen"].Str() == L"stretched")
				settings.fullscreenScale = SCREEN_STRETCHED;

			Configuration::ConstSection video( cfg["video"] );

			settings.adapter = std::find
			(
				adapters.begin(),
				adapters.end(),
				System::Guid( video["device"].Str() )
			);

			if (settings.adapter == adapters.end())
				settings.adapter = adapters.begin();

			{
				Configuration::ConstSection fullscreen( video["fullscreen"] );

				settings.mode = settings.adapter->modes.find
				(
					Mode
					(
						fullscreen[ "width"  ].Int(),
						fullscreen[ "height" ].Int(),
						fullscreen[ "bpp"    ].Int()
					)
				);
			}

			if (settings.mode == settings.adapter->modes.end())
				settings.mode = GetDefaultMode();

			settings.texMem =
			(
				video["memory-pool"].Str() == L"system" ? Settings::TEXMEM_SYSMEM :
                                                          Settings::TEXMEM_VIDMEM
			);

			{
				Configuration::ConstSection region( video["region"] );

				settings.rects.ntsc.Set
				(
					region[ "ntsc" ][ "left"   ].Int( 0                  ),
					region[ "ntsc" ][ "top"    ].Int( NTSC_CLIP_TOP      ),
					region[ "ntsc" ][ "right"  ].Int( NES_WIDTH-1        ),
					region[ "ntsc" ][ "bottom" ].Int( NTSC_CLIP_BOTTOM-1 )
				);

				settings.rects.pal.Set
				(
					region[ "pal" ][ "left"   ].Int( 0                 ),
					region[ "pal" ][ "top"    ].Int( PAL_CLIP_TOP      ),
					region[ "pal" ][ "right"  ].Int( NES_WIDTH-1       ),
					region[ "pal" ][ "bottom" ].Int( PAL_CLIP_BOTTOM-1 )
				);
			}

			ValidateRects();

			{
				Configuration::ConstSection colors( video["colors"] );

				uint v;

				if (200 >= (v=colors["brightness"].Int( 100 )))
					Nes::Video(nes).SetBrightness( int(v) - 100 );

				if (200 >= (v=colors["saturation"].Int( 100 )))
					Nes::Video(nes).SetSaturation( int(v) - 100 );

				if (200 >= (v=colors["contrast"].Int( 100 )))
					Nes::Video(nes).SetContrast( int(v) - 100 );

				int hue = colors["hue"].Int( 0 );

				if (hue > 180 && hue <= 360)
					hue -= 360;

				if (hue >= Nes::Video::MIN_HUE && hue <= Nes::Video::MAX_HUE)
					Nes::Video(nes).SetHue( hue );
			}

			{
				settings.autoPalette = false;
				settings.palette = video["palette"]["file"].Str();

				Nes::Video::Palette::Mode mode;
				const GenericString type( video["palette"]["type"].Str() );

				if (type == L"yuv")
				{
					mode = Nes::Video::Palette::MODE_YUV;
				}
				else if (type == L"rgb")
				{
					mode = Nes::Video::Palette::MODE_RGB;
				}
				else if (type == L"custom" && settings.palette.Length())
				{
					mode = Nes::Video::Palette::MODE_CUSTOM;
				}
				else
				{
					settings.autoPalette = true;
					mode = Nes::Video::Palette::MODE_YUV;
				}

				Nes::Video(nes).GetPalette().SetMode( mode );
			}

			ImportPalette( settings.palette, Managers::Paths::QUIETLY );

			{
				int v;

				if (MAX_SCREEN_CURVATURE-MIN_SCREEN_CURVATURE >= (v=video["screen-curvature"].Int( MAX_SCREEN_CURVATURE )))
					v -= MAX_SCREEN_CURVATURE;

				if (v >= MIN_SCREEN_CURVATURE && v <= MAX_SCREEN_CURVATURE)
					settings.screenCurvature = v;
				else
					settings.screenCurvature = 0;
			}

			settings.autoHz = !video[ "auto-display-frequency" ].No();
			settings.tvAspect = video[ "tv-aspect-ratio" ].Yes();

			settings.filter = settings.filters + Filter::Load
			(
				cfg,
				settings.filters,
				Nes::Video(nes),
				settings.adapter->maxScreenSize,
				settings.adapter->filters & Adapter::FILTER_BILINEAR,
				settings.adapter->canDoScanlineEffect,
				GetDesiredPaletteMode()
			);

			VideoDecoder::Load( cfg, Nes::Video(nes) );

			UpdateFinalRects();
		}

		Video::~Video()
		{
		}

		void Video::Save(Configuration& cfg) const
		{
			cfg["view" ]["size"]["fullscreen"].Str() =
			(
				settings.fullscreenScale == SCREEN_STRETCHED ? "stretched" :
                                                               "matched"
			);

			Configuration::Section video( cfg["video"] );

			if (settings.adapter != adapters.end())
			{
				video["device"].Str() = settings.adapter->guid.GetString();

				if (settings.mode != settings.adapter->modes.end())
				{
					Configuration::Section fullscreen( video["fullscreen"] );

					fullscreen[ "width"  ].Int() = settings.mode->width;
					fullscreen[ "height" ].Int() = settings.mode->height;
					fullscreen[ "bpp"    ].Int() = settings.mode->bpp;
				}
			}

			video["memory-pool"].Str() =
			(
				settings.texMem == Settings::TEXMEM_SYSMEM ? "system" :
                                                             "video"
			);

			video[ "screen-curvature"       ].Int() = MAX_SCREEN_CURVATURE + settings.screenCurvature;
			video[ "auto-display-frequency" ].YesNo() = settings.autoHz;
			video[ "tv-aspect-ratio"        ].YesNo() = settings.tvAspect;

			{
				Configuration::Section palette( video["palette"] );

				palette["file"].Str() = settings.palette;

				GenericString type;

				if (settings.autoPalette)
				{
					type = L"auto";
				}
				else switch (Nes::Video(nes).GetPalette().GetMode())
				{
					case Nes::Video::Palette::MODE_YUV: type = L"yuv";    break;
					case Nes::Video::Palette::MODE_RGB: type = L"rgb";    break;
					default:                            type = L"custom"; break;
				}

				palette["type"].Str() = type;
			}

			{
				Configuration::Section region( video["region"] );

				region[ "ntsc" ][ "left"   ].Int() = settings.rects.ntsc.left;
				region[ "ntsc" ][ "top"    ].Int() = settings.rects.ntsc.top;
				region[ "ntsc" ][ "right"  ].Int() = settings.rects.ntsc.right - 1;
				region[ "ntsc" ][ "bottom" ].Int() = settings.rects.ntsc.bottom - 1;
				region[ "pal"  ][ "left"   ].Int() = settings.rects.pal.left;
				region[ "pal"  ][ "top"    ].Int() = settings.rects.pal.top;
				region[ "pal"  ][ "right"  ].Int() = settings.rects.pal.right - 1;
				region[ "pal"  ][ "bottom" ].Int() = settings.rects.pal.bottom - 1;
			}

			{
				Configuration::Section colors( video["colors"] );

				colors[ "brightness" ].Int() = 100 + Nes::Video(nes).GetBrightness();
				colors[ "saturation" ].Int() = 100 + Nes::Video(nes).GetSaturation();
				colors[ "contrast"   ].Int() = 100 + Nes::Video(nes).GetContrast();
				colors[ "hue"        ].Int() = Nes::Video(nes).GetHue() + (Nes::Video(nes).GetHue() < 0 ? 360 : 0);
			}

			Filter::Save
			(
				cfg,
				settings.filters,
				Nes::Video(nes),
				static_cast<Filter::Type>(settings.filter - settings.filters)
			);

			VideoDecoder::Save( cfg, Nes::Video(nes) );
		}

		Nes::Video::Palette::Mode Video::GetDesiredPaletteMode() const
		{
			if (Nes::Machine(nes).Is(Nes::Machine::VS) || Nes::Machine(nes).Is(Nes::Machine::PC10))
				return Nes::Video::Palette::MODE_RGB;
			else
				return Nes::Video::Palette::MODE_YUV;
		}

		void Video::UpdateAutoModes() const
		{
			const Nes::Video::Palette::Mode mode = GetDesiredPaletteMode();

			if (settings.autoPalette)
				Nes::Video(nes).GetPalette().SetMode( mode );

			VideoFilters::UpdateAutoModes( settings.filters, nes, mode );
		}

		uint Video::GetScanlines() const
		{
			if (settings.adapter->canDoScanlineEffect)
			{
				switch (settings.filter - settings.filters)
				{
					case Filter::TYPE_STD:

						return settings.filters[Filter::TYPE_STD].attributes[Filter::ATR_SCANLINES];

					case Filter::TYPE_NTSC:

						return settings.filters[Filter::TYPE_NTSC].attributes[Filter::ATR_SCANLINES];
				}
			}

			return 0;
		}

		const Rect Video::GetRenderState(Nes::Video::RenderState& state,const Point screen) const
		{
			typedef Nes::Video::RenderState State;

			Rect rect( Nes::Machine(nes).GetMode() == Nes::Machine::NTSC ? settings.rects.ntsc : settings.rects.pal );

			state.width = NES_WIDTH;
			state.height = NES_HEIGHT;
			state.filter = State::FILTER_NONE;

			uint scale = 1;

			switch (settings.filter - settings.filters)
			{
				case Filter::TYPE_STD:
					break;

				case Filter::TYPE_NTSC:

					if (settings.adapter->maxScreenSize.x >= NTSC_WIDTH)
					{
						rect.left  = (rect.left  * (NTSC_WIDTH - 4) + NES_WIDTH / 2) / NES_WIDTH + 3;
						rect.right = (rect.right * (NTSC_WIDTH - 4) + NES_WIDTH / 2) / NES_WIDTH + 3;

						state.width = NTSC_WIDTH;
						state.filter = State::FILTER_NTSC;
					}
					break;

				case Filter::TYPE_SCALEX:

					if (settings.adapter->maxScreenSize.x >= NES_WIDTH*2 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*2)
					{
						int attribute = settings.filters[Filter::TYPE_SCALEX].attributes[Filter::ATR_TYPE];

						if (attribute == Filter::ATR_SCALEAX)
						{
							const Point nes( rect.Size() );

							if (screen.x >= nes.x*3 && screen.y >= nes.y*3)
							{
								attribute = Filter::ATR_SCALE3X;
							}
							else if (screen.x >= nes.x*2 && screen.y >= nes.y*2)
							{
								attribute = Filter::ATR_SCALE2X;
							}
						}

						switch (attribute)
						{
							case Filter::ATR_SCALE3X:

								if (settings.adapter->maxScreenSize.x >= NES_WIDTH*3 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*3)
								{
									scale = 3;
									state.filter = State::FILTER_SCALE3X;
									break;
								}

							case Filter::ATR_SCALE2X:

								scale = 2;
								state.filter = State::FILTER_SCALE2X;
								break;
						}
					}
					break;

				case Filter::TYPE_HQX:

					if (settings.adapter->maxScreenSize.x >= NES_WIDTH*2 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*2)
					{
						int attribute = settings.filters[Filter::TYPE_HQX].attributes[Filter::ATR_TYPE];

						if (attribute == Filter::ATR_HQAX)
						{
							const Point nes( rect.Size() );

							if (screen.x >= nes.x*4 && screen.y >= nes.y*4)
							{
								attribute = Filter::ATR_HQ4X;
							}
							else if (screen.x >= nes.x*3 && screen.y >= nes.y*3)
							{
								attribute = Filter::ATR_HQ3X;
							}
							else if (screen.x >= nes.x*2 && screen.y >= nes.y*2)
							{
								attribute = Filter::ATR_HQ2X;
							}
						}

						switch (attribute)
						{
							case Filter::ATR_HQ4X:

								if (settings.adapter->maxScreenSize.x >= NES_WIDTH*4 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*4)
								{
									scale = 4;
									state.filter = State::FILTER_HQ4X;
									break;
								}

							case Filter::ATR_HQ3X:

								if (settings.adapter->maxScreenSize.x >= NES_WIDTH*3 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*3)
								{
									scale = 3;
									state.filter = State::FILTER_HQ3X;
									break;
								}

							case Filter::ATR_HQ2X:

								scale = 2;
								state.filter = State::FILTER_HQ2X;
								break;
						}
					}
					break;

				case Filter::TYPE_2XSAI:

					if (settings.adapter->maxScreenSize.x >= NES_WIDTH*2 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*2)
					{
						scale = 2;
						state.filter = State::FILTER_2XSAI;
					}
					break;

					case Filter::TYPE_XBR:
					if (settings.adapter->maxScreenSize.x >= NES_WIDTH*2 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*2)
					{
						int attribute = settings.filters[Filter::TYPE_XBR].attributes[Filter::ATR_TYPE];

						if (attribute == Filter::ATR_AXBR)
						{
							const Point nes( rect.Size() );

							if (screen.x >= nes.x*4 && screen.y >= nes.y*4)
							{
								attribute = Filter::ATR_4XBR;
							}
							else if (screen.x >= nes.x*3 && screen.y >= nes.y*3)
							{
								attribute = Filter::ATR_3XBR;
							}
							else if (screen.x >= nes.x*2 && screen.y >= nes.y*2)
							{
								attribute = Filter::ATR_2XBR;
							}
						}

						switch (attribute)
						{
							case Filter::ATR_4XBR:

								if (settings.adapter->maxScreenSize.x >= NES_WIDTH*4 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*4)
								{
									scale = 4;
									state.filter = State::FILTER_4XBR;
									break;
								}

							case Filter::ATR_HQ3X:

								if (settings.adapter->maxScreenSize.x >= NES_WIDTH*3 && settings.adapter->maxScreenSize.y >= NES_HEIGHT*3)
								{
									scale = 3;
									state.filter = State::FILTER_3XBR;
									break;
								}

							case Filter::ATR_HQ2X:

								scale = 2;
								state.filter = State::FILTER_2XBR;
								break;
						}
					}
					break;
			}

			state.width = state.width * scale;
			state.height = state.height * scale;

			rect.left   *= scale;
			rect.top    *= scale;
			rect.right  *= scale;
			rect.bottom *= scale;

			return rect;
		}

		Video::Modes::const_iterator Video::GetDialogMode() const
		{
			for (Modes::const_iterator it(settings.adapter->modes.begin()), end(settings.adapter->modes.end()); it != end; ++it)
			{
				if (it->bpp == settings.mode->bpp && it->width >= MIN_DIALOG_WIDTH && it->height >= MIN_DIALOG_HEIGHT)
					return it;
			}

			return settings.adapter->modes.begin();
		}

		uint Video::GetFullscreenScaleMethod() const
		{
			return settings.filter == settings.filters+Filter::TYPE_NTSC ? settings.filters[Filter::TYPE_NTSC].attributes[Filter::ATR_RESCALE_PIC] ? 1 : 2 : 0;
		}

		void Video::UpdateFullscreenScaleMethod(uint prev)
		{
			if (settings.fullscreenScale != SCREEN_STRETCHED && prev != GetFullscreenScaleMethod())
				settings.fullscreenScale = SCREEN_MATCHED;
		}

		void Video::UpdateFinalRects()
		{
			for (uint i=0; i < 2; ++i)
			{
				Rect& rect = (i ? settings.rects.outPal : settings.rects.outNtsc);
				rect = (i ? settings.rects.pal : settings.rects.ntsc);

				if (settings.filter == settings.filters + Filter::TYPE_NTSC)
				{
					if (settings.tvAspect || !settings.filters[Filter::TYPE_NTSC].attributes[Filter::ATR_RESCALE_PIC])
					{
						rect.top *= 2;
						rect.bottom *= 2;
						rect.left  = (rect.left  * (NTSC_WIDTH - 4) + NES_WIDTH  / 2) / NES_WIDTH + 3;
						rect.right = (rect.right * (NTSC_WIDTH - 4) + NES_WIDTH  / 2) / NES_WIDTH + 3;
					}
				}
				else if (settings.tvAspect)
				{
					rect.left  = (rect.left  * TV_WIDTH + NES_WIDTH / 2) / NES_WIDTH;
					rect.right = (rect.right * TV_WIDTH + NES_WIDTH / 2) / NES_WIDTH;
				}
			}
		}

		void Video::ValidateRects()
		{
			Settings::Rects& r = settings.rects;

			r.ntsc.left   = NST_CLAMP( r.ntsc.left,   0,           NES_WIDTH  -1 );
			r.ntsc.top    = NST_CLAMP( r.ntsc.top,    0,           NES_HEIGHT -1 );
			r.ntsc.right  = NST_CLAMP( r.ntsc.right,  r.ntsc.left, NES_WIDTH  -1 ) + 1;
			r.ntsc.bottom = NST_CLAMP( r.ntsc.bottom, r.ntsc.top,  NES_HEIGHT -1 ) + 1;
			r.pal.left    = NST_CLAMP( r.pal.left,    0,           NES_WIDTH  -1 );
			r.pal.top     = NST_CLAMP( r.pal.top,     0,           NES_HEIGHT -1 );
			r.pal.right   = NST_CLAMP( r.pal.right,   r.pal.left,  NES_WIDTH  -1 ) + 1;
			r.pal.bottom  = NST_CLAMP( r.pal.bottom,  r.pal.top,   NES_HEIGHT -1 ) + 1;
		}

		void Video::ResetColors()
		{
			Nes::Video(nes).SetBrightness ( Nes::Video::DEFAULT_BRIGHTNESS );
			Nes::Video(nes).SetSaturation ( Nes::Video::DEFAULT_SATURATION );
			Nes::Video(nes).SetContrast   ( Nes::Video::DEFAULT_CONTRAST   );
			Nes::Video(nes).SetHue        ( Nes::Video::DEFAULT_HUE        );
		}

		Video::Modes::const_iterator Video::GetDefaultMode() const
		{
			for (uint bpp=16; bpp <= 32; bpp += 16)
			{
				const Modes::const_iterator it( settings.adapter->modes.find(Mode(defaultMode.width,defaultMode.height,bpp)) );

				if (it != settings.adapter->modes.end())
					return it;
			}

			return settings.adapter->modes.begin();
		}

		ibool Video::OnInitDialog(Param&)
		{
			NST_ASSERT( settings.backup == NULL );
			settings.backup = new Settings::Backup( settings, Nes::Video(nes) );

			{
				const Control::ComboBox comboBox( dialog.ComboBox(IDC_VIDEO_DEVICE) );

				for (Adapters::const_iterator it(adapters.begin()), end(adapters.end()); it != end; ++it)
				{
					comboBox.Add( it->name.Ptr() );

					if (settings.adapter == it)
						comboBox[it - adapters.begin()].Select();
				}
			}

			for (uint i=IDC_VIDEO_NTSC_LEFT; i <= IDC_VIDEO_PAL_BOTTOM; ++i)
				dialog.Edit( i ).Limit( 3 );

			dialog.Slider( IDC_VIDEO_COLORS_BRIGHTNESS ).SetRange( 0, Nes::Video::MAX_BRIGHTNESS-Nes::Video::MIN_BRIGHTNESS );
			dialog.Slider( IDC_VIDEO_COLORS_SATURATION ).SetRange( 0, Nes::Video::MAX_SATURATION-Nes::Video::MIN_SATURATION );
			dialog.Slider( IDC_VIDEO_COLORS_CONTRAST   ).SetRange( 0, Nes::Video::MAX_CONTRAST-Nes::Video::MIN_CONTRAST );
			dialog.Slider( IDC_VIDEO_COLORS_HUE        ).SetRange( 0, Nes::Video::MAX_HUE-Nes::Video::MIN_HUE );

			dialog.Slider( IDC_VIDEO_SC_SLIDER ).SetRange( 0, MAX_SCREEN_CURVATURE-MIN_SCREEN_CURVATURE );

			dialog.CheckBox( IDC_VIDEO_AUTO_HZ ).Check( settings.autoHz );

			dialog.RadioButton( settings.texMem == Settings::TEXMEM_VIDMEM ? IDC_VIDEO_NESTEXTURE_VIDMEM : IDC_VIDEO_NESTEXTURE_SYSMEM ).Check();

			UpdateDevice( *settings.mode );
			UpdateRects( settings.rects.ntsc, settings.rects.pal );
			UpdateColors();
			UpdateScreenCurvature();
			UpdatePalette();

			return true;
		}

		ibool Video::OnDestroy(Param&)
		{
			if (settings.backup)
			{
				settings.backup->Restore( settings, Nes::Video(nes) );

				delete settings.backup;
				settings.backup = NULL;
			}
			else
			{
				settings.autoHz = dialog.CheckBox(IDC_VIDEO_AUTO_HZ).Checked();

				if (dialog.RadioButton(IDC_VIDEO_NESTEXTURE_VIDMEM).Checked())
					settings.texMem = Settings::TEXMEM_VIDMEM;
				else
					settings.texMem = Settings::TEXMEM_SYSMEM;

				dialog.Control( IDC_VIDEO_NTSC_LEFT   ).Text() >> settings.rects.ntsc.left;
				dialog.Control( IDC_VIDEO_NTSC_TOP    ).Text() >> settings.rects.ntsc.top;
				dialog.Control( IDC_VIDEO_NTSC_RIGHT  ).Text() >> settings.rects.ntsc.right;
				dialog.Control( IDC_VIDEO_NTSC_BOTTOM ).Text() >> settings.rects.ntsc.bottom;
				dialog.Control( IDC_VIDEO_PAL_LEFT    ).Text() >> settings.rects.pal.left;
				dialog.Control( IDC_VIDEO_PAL_TOP     ).Text() >> settings.rects.pal.top;
				dialog.Control( IDC_VIDEO_PAL_RIGHT   ).Text() >> settings.rects.pal.right;
				dialog.Control( IDC_VIDEO_PAL_BOTTOM  ).Text() >> settings.rects.pal.bottom;

				ValidateRects();
			}

			UpdateFinalRects();

			return true;
		}

		ibool Video::OnHScroll(Param& param)
		{
			const int value = param.Slider().Scroll();

			switch (param.Slider().GetId())
			{
				case IDC_VIDEO_COLORS_BRIGHTNESS:

					Nes::Video(nes).SetBrightness( value + Nes::Video::MIN_BRIGHTNESS );
					break;

				case IDC_VIDEO_COLORS_SATURATION:

					Nes::Video(nes).SetSaturation( value + Nes::Video::MIN_SATURATION );
					break;

				case IDC_VIDEO_COLORS_CONTRAST:

					Nes::Video(nes).SetContrast( value + Nes::Video::MIN_CONTRAST );
					break;

				case IDC_VIDEO_COLORS_HUE:

					Nes::Video(nes).SetHue( value + Nes::Video::MIN_HUE );
					break;

				case IDC_VIDEO_SC_SLIDER:

					settings.screenCurvature = value + MIN_SCREEN_CURVATURE;
					UpdateScreenCurvature();
					Application::Instance::GetMainWindow().Redraw();

				default:

					return true;
			}

			UpdateColors();
			Application::Instance::GetMainWindow().Redraw();

			return true;
		}

		ibool Video::OnCmdDevice(Param& param)
		{
			if (param.ComboBox().SelectionChanged())
			{
				settings.adapter = adapters.begin();

				for (uint i=dialog.ComboBox( IDC_VIDEO_DEVICE ).Selection().GetIndex(); i; --i)
					++settings.adapter;

				UpdateDevice( *settings.mode );
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdBitDepth(Param& param)
		{
			if (param.Button().Clicked())
				UpdateResolutions( Mode(settings.mode->width,settings.mode->height,param.Button().GetId() == IDC_VIDEO_32_BIT ? 32 : 16) );

			return true;
		}

		ibool Video::OnCmdMode(Param& param)
		{
			if (param.ComboBox().SelectionChanged())
			{
				settings.mode = settings.adapter->modes.begin();

				for (uint i=dialog.ComboBox(IDC_VIDEO_MODE).Selection().Data(); i; --i)
					++settings.mode;
			}

			return true;
		}

		ibool Video::OnCmdFilter(Param& param)
		{
			if (param.ComboBox().SelectionChanged())
			{
				const uint method = GetFullscreenScaleMethod();
				settings.filter = settings.filters + dialog.ComboBox( IDC_VIDEO_EFFECTS ).Selection().Data();
				UpdateFullscreenScaleMethod( method );

				UpdatePalette();
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdFilterSettings(Param& param)
		{
			if (param.Button().Clicked())
			{
				const uint method = GetFullscreenScaleMethod();

				static const ushort idd[] =
				{
					IDD_VIDEO_FILTER_STD,
					IDD_VIDEO_FILTER_NTSC,
					IDD_VIDEO_FILTER_SCALEX,
					IDD_VIDEO_FILTER_HQX,
					IDD_VIDEO_FILTER_2XSAI,
					IDD_VIDEO_FILTER_XBR
				};

				VideoFilters
				(
					Nes::Video(nes),
					idd[settings.filter-settings.filters],
					*settings.filter,
					settings.adapter->maxScreenSize,
					settings.adapter->filters & Adapter::FILTER_BILINEAR,
					settings.adapter->canDoScanlineEffect,
					GetDesiredPaletteMode()
				).Open();

				UpdateFullscreenScaleMethod( method );
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdPalType(Param& param)
		{
			if (param.Button().Clicked())
			{
				const uint cmd = param.Button().GetId();

				settings.autoPalette = (cmd == IDC_VIDEO_PALETTE_AUTO);

				Nes::Video(nes).GetPalette().SetMode
				(
					cmd == IDC_VIDEO_PALETTE_YUV    ? Nes::Video::Palette::MODE_YUV :
					cmd == IDC_VIDEO_PALETTE_RGB    ? Nes::Video::Palette::MODE_RGB :
					cmd == IDC_VIDEO_PALETTE_CUSTOM ? Nes::Video::Palette::MODE_CUSTOM :
                                                      GetDesiredPaletteMode()
				);

				UpdatePalette();
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdColorsReset(Param& param)
		{
			if (param.Button().Clicked())
			{
				ResetColors();
				UpdateColors();
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdColorsAdvanced(Param& param)
		{
			if (param.Button().Clicked())
			{
				VideoDecoder( nes ).Open();
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdPalBrowse(Param& param)
		{
			if (param.Button().Clicked())
			{
				const Path file
				(
					paths.BrowseLoad
					(
						Managers::Paths::File::PALETTE|Managers::Paths::File::ARCHIVE,
						Application::Instance::GetFullPath( settings.palette )
					)
				);

				if (file.Length())
				{
					settings.palette = file;

					ImportPalette( settings.palette, Managers::Paths::NOISY );
					UpdatePalette();
					Application::Instance::GetMainWindow().Redraw();
				}
			}

			return true;
		}

		ibool Video::OnCmdPalClear(Param& param)
		{
			if (param.Button().Clicked())
			{
				if (settings.palette.Length())
				{
					settings.palette.Destroy();

					if (Nes::Video(nes).GetPalette().GetMode() == Nes::Video::Palette::MODE_CUSTOM)
					{
						Nes::Video(nes).GetPalette().SetMode( GetDesiredPaletteMode() );
						settings.autoPalette = true;
					}

					UpdatePalette();
					Application::Instance::GetMainWindow().Redraw();
				}
			}

			return true;
		}

		ibool Video::OnCmdPalEditor(Param& param)
		{
			if (param.Button().Clicked())
			{
				Nes::Video video( nes );

				if (settings.palette.Empty())
					video.GetPalette().ResetCustom();

				{
					const Path path( PaletteEditor( video, paths, settings.palette ).Open() );

					if (settings.palette.Empty() && path.Length())
						settings.palette = path;
				}

				if (settings.palette.Length())
				{
					ImportPalette( settings.palette, Managers::Paths::QUIETLY );
					UpdatePalette();
				}

				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdDefault(Param& param)
		{
			if (param.Button().Clicked())
			{
				const uint method = GetFullscreenScaleMethod();

				settings.adapter = adapters.begin();
				settings.mode = GetDefaultMode();

				for (uint i=0; i < Filter::NUM_TYPES; ++i)
					settings.filters[i].Reset();

				settings.filter = settings.filters + Filter::TYPE_STD;

				UpdateDevice( *settings.mode );

				dialog.RadioButton( IDC_VIDEO_NESTEXTURE_VIDMEM ).Check();
				dialog.RadioButton( IDC_VIDEO_NESTEXTURE_SYSMEM ).Uncheck();

				UpdateRects
				(
					Rect( 0, NTSC_CLIP_TOP, NES_WIDTH, NTSC_CLIP_BOTTOM ),
					Rect( 0, PAL_CLIP_TOP, NES_WIDTH, PAL_CLIP_BOTTOM )
				);

				dialog.CheckBox( IDC_VIDEO_AUTO_HZ ).Check();

				ResetColors();
				UpdateColors();

				settings.autoPalette = true;

				settings.screenCurvature = 0;
				UpdateScreenCurvature();

				const Nes::Video::Palette::Mode mode = GetDesiredPaletteMode();
				Nes::Video video(nes);

				video.GetPalette().SetMode( mode );

				VideoFilters::UpdateAutoModes( settings.filters, video, mode );

				video.SetDecoder( Nes::Video::DECODER_CANONICAL );

				UpdateFullscreenScaleMethod( method );

				UpdatePalette();
				Application::Instance::GetMainWindow().Redraw();
			}

			return true;
		}

		ibool Video::OnCmdOk(Param& param)
		{
			if (param.Button().Clicked())
			{
				delete settings.backup;
				settings.backup = NULL;

				dialog.Close();
			}

			return true;
		}

		void Video::UpdateDevice(Mode mode)
		{
			dialog.RadioButton( IDC_VIDEO_NESTEXTURE_VIDMEM ).Enable( settings.adapter->videoMemScreen );
			dialog.RadioButton( IDC_VIDEO_NESTEXTURE_SYSMEM ).Enable( settings.adapter->videoMemScreen );

			uint available = 0;

			for (Modes::const_iterator it(settings.adapter->modes.begin()), end(settings.adapter->modes.end()); it != end; ++it)
			{
				switch (it->bpp)
				{
					case 16: available |= 16; break;
					case 32: available |= 32; break;
				}

				if (available == (16|32))
					break;
			}

			NST_ASSERT( available & (16|32) );

			dialog.Control( IDC_VIDEO_16_BIT ).Enable( available & 16 );
			dialog.Control( IDC_VIDEO_32_BIT ).Enable( available & 32 );

			switch (mode.bpp)
			{
				case 32: mode.bpp = ((available & 32) ? 32 : 16); break;
				case 16: mode.bpp = ((available & 16) ? 16 : 32); break;
			}

			UpdateResolutions( mode );
			UpdateFilters();
		}

		void Video::UpdateResolutions(Mode mode)
		{
			settings.mode = settings.adapter->modes.find( mode );

			if (settings.mode == settings.adapter->modes.end())
			{
				settings.mode = settings.adapter->modes.find( Mode(defaultMode.width,defaultMode.height,mode.bpp) );

				if (settings.mode == settings.adapter->modes.end())
					settings.mode = settings.adapter->modes.begin();

				mode = *settings.mode;
			}

			dialog.RadioButton( IDC_VIDEO_16_BIT ).Check( mode.bpp == 16 );
			dialog.RadioButton( IDC_VIDEO_32_BIT ).Check( mode.bpp == 32 );

			const Control::ComboBox comboBox( dialog.ComboBox(IDC_VIDEO_MODE) );
			comboBox.Clear();

			uint idx=0;
			HeapString string;

			for (Modes::const_iterator it(settings.adapter->modes.begin()), end(settings.adapter->modes.end()); it != end; ++it, ++idx)
			{
				if (mode.bpp == it->bpp)
				{
					string.Clear();
					comboBox.Add( (string << it->width << 'x' << it->height).Ptr() ).Data() = idx;

					if (mode.width == it->width && mode.height == it->height)
						comboBox[comboBox.Size() - 1].Select();
				}
			}
		}

		void Video::UpdateFilters()
		{
			const Control::ComboBox comboBox( dialog.ComboBox(IDC_VIDEO_EFFECTS) );
			comboBox.Clear();

			comboBox.Add( Resource::String(IDS_VIDEO_FILTER_STD) ).Data() = Filter::TYPE_STD;

			if (settings.adapter->maxScreenSize.x >= NTSC_WIDTH)
				comboBox.Add( Resource::String(IDS_VIDEO_FILTER_NTSC) ).Data() = Filter::TYPE_NTSC;

			if
			(
				settings.adapter->maxScreenSize.x >= NES_WIDTH*2 &&
				settings.adapter->maxScreenSize.y >= NES_HEIGHT*2
			)
			{

				comboBox.Add( Resource::String(IDS_VIDEO_FILTER_SCALEX) ).Data() = Filter::TYPE_SCALEX;
				comboBox.Add( Resource::String(IDS_VIDEO_FILTER_HQX) ).Data() = Filter::TYPE_HQX;
				comboBox.Add( Resource::String(IDS_VIDEO_FILTER_2XSAI) ).Data() = Filter::TYPE_2XSAI;
				comboBox.Add( Resource::String(IDS_VIDEO_FILTER_XBR) ).Data() = Filter::TYPE_XBR;
			}

			for (uint i=0, size=comboBox.Size(); i < size; ++i)
			{
				if (settings.filter-settings.filters == (Filter::Type) (Control::ComboBox::Value) comboBox[i].Data())
				{
					comboBox[i].Select();
					return;
				}
			}

			settings.filter = settings.filters + Filter::TYPE_STD;
			comboBox[0].Select();
		}

		void Video::UpdateColors() const
		{
			const int colors[] =
			{
				Nes::Video(nes).GetBrightness(),
				Nes::Video(nes).GetSaturation(),
				Nes::Video(nes).GetContrast(),
				Nes::Video(nes).GetHue()
			};

			dialog.Control( IDC_VIDEO_COLORS_RESET ).Enable
			(
				colors[0] != Nes::Video::DEFAULT_BRIGHTNESS ||
				colors[1] != Nes::Video::DEFAULT_SATURATION ||
				colors[2] != Nes::Video::DEFAULT_CONTRAST   ||
				colors[3] != Nes::Video::DEFAULT_HUE
			);

			dialog.Slider( IDC_VIDEO_COLORS_BRIGHTNESS ).Position() = colors[0] - Nes::Video::MIN_BRIGHTNESS;
			dialog.Slider( IDC_VIDEO_COLORS_SATURATION ).Position() = colors[1] - Nes::Video::MIN_SATURATION;
			dialog.Slider( IDC_VIDEO_COLORS_CONTRAST   ).Position() = colors[2] - Nes::Video::MIN_CONTRAST;
			dialog.Slider( IDC_VIDEO_COLORS_HUE        ).Position() = colors[3] - Nes::Video::MIN_HUE;

			dialog.Control( IDC_VIDEO_COLORS_BRIGHTNESS_VAL ).Text() << RealString( colors[0] / 100.0, 2 ).Ptr();
			dialog.Control( IDC_VIDEO_COLORS_SATURATION_VAL ).Text() << RealString( (colors[1] + 100) / 100.0, 2 ).Ptr();
			dialog.Control( IDC_VIDEO_COLORS_CONTRAST_VAL   ).Text() << RealString( (colors[2] + 100) / 100.0, 2 ).Ptr();
			dialog.Control( IDC_VIDEO_COLORS_HUE_VAL        ).Text() << colors[3];
		}

		void Video::UpdateScreenCurvature() const
		{
			dialog.Slider( IDC_VIDEO_SC_SLIDER ).Position() = settings.screenCurvature - MIN_SCREEN_CURVATURE;
			dialog.Control( IDC_VIDEO_SC_VALUE ).Text() << RealString( settings.screenCurvature / 10.0 ).Ptr();
		}

		void Video::UpdateRects(const Rect& ntsc,const Rect& pal) const
		{
			dialog.Control( IDC_VIDEO_NTSC_LEFT   ).Text() << uint( ntsc.left       );
			dialog.Control( IDC_VIDEO_NTSC_TOP    ).Text() << uint( ntsc.top        );
			dialog.Control( IDC_VIDEO_NTSC_RIGHT  ).Text() << uint( ntsc.right - 1  );
			dialog.Control( IDC_VIDEO_NTSC_BOTTOM ).Text() << uint( ntsc.bottom - 1 );
			dialog.Control( IDC_VIDEO_PAL_LEFT    ).Text() << uint( pal.left        );
			dialog.Control( IDC_VIDEO_PAL_TOP     ).Text() << uint( pal.top         );
			dialog.Control( IDC_VIDEO_PAL_RIGHT   ).Text() << uint( pal.right - 1   );
			dialog.Control( IDC_VIDEO_PAL_BOTTOM  ).Text() << uint( pal.bottom - 1  );
		}

		void Video::UpdatePalette() const
		{
			const Nes::Video::Palette::Mode mode = Nes::Video(nes).GetPalette().GetMode();

			dialog.Control( IDC_VIDEO_PALETTE_CLEAR  ).Enable( settings.palette.Length() );
			dialog.Control( IDC_VIDEO_PALETTE_CUSTOM ).Enable( settings.palette.Length() );
			dialog.Control( IDC_VIDEO_PALETTE_PATH   ).Enable( mode == Nes::Video::Palette::MODE_CUSTOM );

			dialog.Edit( IDC_VIDEO_PALETTE_PATH ) << settings.palette.Ptr();

			dialog.Control( IDC_VIDEO_COLORS_ADVANCED ).Enable( mode == Nes::Video::Palette::MODE_YUV );

			dialog.RadioButton( IDC_VIDEO_PALETTE_AUTO   ).Check( settings.autoPalette );
			dialog.RadioButton( IDC_VIDEO_PALETTE_YUV    ).Check( !settings.autoPalette && mode == Nes::Video::Palette::MODE_YUV    );
			dialog.RadioButton( IDC_VIDEO_PALETTE_RGB    ).Check( !settings.autoPalette && mode == Nes::Video::Palette::MODE_RGB    );
			dialog.RadioButton( IDC_VIDEO_PALETTE_CUSTOM ).Check( !settings.autoPalette && mode == Nes::Video::Palette::MODE_CUSTOM );
		}

		void Video::ImportPalette(Path& palette,Managers::Paths::Alert alert)
		{
			if (palette.Empty())
				return;

			palette.MakePretty();

			Managers::Paths::File file;

			if (paths.Load( file, Managers::Paths::File::PALETTE|Managers::Paths::File::ARCHIVE, Application::Instance::GetFullPath(palette), alert ))
			{
				if (file.data.Size() >= Nes::Video::Palette::NUM_ENTRIES*3)
				{
					Nes::Video::Palette::CustomType type = Nes::Video::Palette::STD_PALETTE;

					if (file.data.Size() >= Nes::Video::Palette::NUM_ENTRIES_EXT*3)
						type = Nes::Video::Palette::EXT_PALETTE;

					if (NES_SUCCEEDED(Nes::Video(nes).GetPalette().SetCustom( reinterpret_cast<Nes::Video::Palette::Colors>(file.data.Begin()), type )))
						return;
				}
			}

			if (alert == Managers::Paths::QUIETLY)
				Io::Log() << "Video: warning, custom palette file: \"" << palette << "\" invalid or not found!\r\n";
			else
				User::Fail( IDS_DIALOG_VIDEO_INVALID_PALETTE );

			palette.Destroy();
			Nes::Video(nes).GetPalette().SetMode( GetDesiredPaletteMode() );
		}
	}
}
