/*
 CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
 Copyright (C) 2014-2018 by Frédéric Coste

 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 3 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, see <http://www.gnu.org/licenses/>.
 */


#include <vcl.h>
#include <Clipbrd.hpp>
#include <tchar.h>
#pragma hdrstop
#include "TFormGraphicsExplorer.h"
#include "TFormEditValue.h"
#include "TFormMain.h"
#include "TFormMemoryEditor.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


//=================================
// Options

//=================================


static const AnsiString cTitle = "Graphics explorer";
static const AnsiString cTitlePaused  = " - [PAUSED]";
static const AnsiString cTitleMonitoring  = " - [Monitoring]";
static const AnsiString cTitlePreview  = " - [PREVIEW]";


TFormGraphicsExplorer *FormGraphicsExplorer;
//---------------------------------------------------------------------------
__fastcall TFormGraphicsExplorer::TFormGraphicsExplorer(TComponent* Owner)
  : TForm(Owner)
{
  mEmulatorP = NULL;
  mSettingsP = NULL;

  mZoom = 1;
  mTileX = 2;
  mTileY = 8;
  mMapX = 32;
  mMapY = 8;
  mTopLeftAddress = 0;
  mTileAddress = 0;
  mFirstTileAddress = 0;
  mLastTileAddress = 0;
  mNbTiles = 1;
  mMapStartAddress = 0;
	mCursorAddressBeforeLeaving = 0;

	mMapPreviewTiles = new TGraphicsExplorerTilePreview[mMapX * mMapY];
  mDrawCells = DrawStringGridTiles;
	mDrawMapPreview = UpdateMapPreviewDataLinearBytes;

  GraphicsEditorP = new TFormGraphicsEditor(this);

  ComboBoxMemory->ItemIndex = 0;
  ComboBoxMode->ItemIndex = 0;
  ComboBoxAddressing->ItemIndex = 0;
  ComboBoxIndexing->ItemIndex = 0;

  // Apply Theme
  Font->Name = "Amstrad CPC464 Regular";
  Font->Size = 6;
  for (int Loop=0; Loop < ComponentCount; Loop++)
  {
    if (Components[Loop]->Name.Pos("Panel") == 1)
		{
      ((TPanel*)Components[Loop])->Font->Color = clBlack;
    }
    else if (Components[Loop]->Name.Pos("Edit") == 1)
    {
      ((TEdit*)Components[Loop])->Font->Color = clBlack;
      ((TEdit*)Components[Loop])->Font->Size = 12;
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::FormDestroy(TObject *Sender)
{
  delete GraphicsEditorP;
  delete [] mMapPreviewTiles;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::FormShow(TObject *Sender)
{
  if (FormMain == NULL)
  {
    Close();
    return;
  }

	FormMain->OnGraphicsExplorerFormShow();

	ShowHint = FormMain->ShowHint;

  mMonitoring = false;
  mMapPreview = false;
  UpdateControlAccess();
	mPaused = FormMain->NativeCPC->paused;
	Caption = Title;

	ComboBoxModeChange(Sender);
	ComboBoxAddressingChange(Sender);
	ComboBoxMemoryChange(Sender);
	ComboBoxIndexingChange(Sender);
	UpDownZoom->Position = mZoom;
	UpDownTileX->Position = mTileX;
	UpDownTileY->Position = mTileY;
	PanelFormat->Caption = "&&";
	PanelAddress->Caption = AnsiString::IntToHex(mTopLeftAddress, 6);
	PanelTileAddress->Caption = AnsiString::IntToHex(mTileAddress, 6);
	PanelCursorAddress->Caption = "";
	PanelCursorData->Caption = "";
	PanelFirstTile->Caption = AnsiString::IntToHex(mFirstTileAddress, 6);
	PanelLastTile->Caption = AnsiString::IntToHex(mLastTileAddress, 6);
	PanelMapStart->Caption = AnsiString::IntToHex(mMapStartAddress, 6);
	PageControlExplorer->ActivePage = TabSheetTiles;
	FormMain->SubscribeToCPCEvents(OnStepByStepEvent);

	// Initialise Grid
	SetAddressingMode(ComboBoxAddressing->ItemIndex);
	SetMemory(ComboBoxMemory->ItemIndex);
	SetMode(ComboBoxMode->ItemIndex);

	// Grid Auto Size
	PanelButtonGridAutoSizeClick(Sender);

	if (mSettingsP != NULL)
  {
    for (int Loop=0; Loop < ComponentCount; Loop++)
    {
			if (Components[Loop]->Name.Pos("PanelButton") == 1)
			{
				((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
				((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
			}
			else if (Components[Loop]->Name.Pos("PanelCheck") == 1)
			{
				((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
				((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
			}
		}
	}

  // Refresh display
  for (int Loop=0; Loop < ComponentCount; Loop++)
  {
		if (((TControl*)Components[Loop])->Visible == true)
		{
      ((TControl*)Components[Loop])->Invalidate();
    }
  }

  mMapPreview = false;
  mDrawCells = DrawStringGridTiles;

  ResizeTiles();
  UpdatePalette();
  UpdateMapPreview();

  if (mSettingsP)
  {
    mSettingsP->GraphicsExplorerFormVisible = true;
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::FormHide(TObject *Sender)
{
	// Unsubscribe to step by step event
	if (FormMain != NULL)
	{
		FormMain->UnsubscribeToCPCEvents(OnStepByStepEvent);
		FormMain->OnGraphicsExplorerFormHide();
	}

	if (mSettingsP)
	{
		mSettingsP->GraphicsExplorerFormVisible = false;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::FormResize(TObject *Sender)
{
	if ( (Visible == true) && (mSettingsP) && (WindowState != wsMaximized) )
	{
		mSettingsP->GraphicsExplorerFormWidth = Width;
		mSettingsP->GraphicsExplorerFormHeight = Height;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


AnsiString __fastcall TFormGraphicsExplorer::GetTitle(void)
{
  AnsiString Title = cTitle;

  if (mMonitoring == true) Title += cTitleMonitoring;
  if (mPaused == true) Title += cTitlePaused;
  if (mMapPreview == true) Title += cTitlePreview;

  return Title;
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetSettings(TSettings* Value)
{
	mSettingsP = Value;

	if (    (mSettingsP->GraphicsExplorerFormLeft != 0)
			 || (mSettingsP->GraphicsExplorerFormTop != 0) )
	{
		Position = poDesigned;
		Left = mSettingsP->GraphicsExplorerFormLeft;
		Top = mSettingsP->GraphicsExplorerFormTop;
		Width = mSettingsP->GraphicsExplorerFormWidth;
		Height = mSettingsP->GraphicsExplorerFormHeight;
	}

	// Apply theme
	Color = mSettingsP->ThemeBackgroundColor;
	Font->Color = mSettingsP->ThemeText1Color;
	LabelGridX->Font->Color = mSettingsP->ThemeText2Color;
	LabelGridY->Font->Color = mSettingsP->ThemeText2Color;
	LabelTileAddress->Font->Color = mSettingsP->ThemeText2Color;
	LabelCursorAddress->Font->Color = mSettingsP->ThemeText2Color;
	for (int Loop=0; Loop < ComponentCount; Loop++)
	{
		if (Components[Loop]->Name.Pos("PanelButton") == 1)
		{
			((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
		}
		else if (Components[Loop]->Name.Pos("PanelTile") == 1)
		{
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeText1Color;
		}
		else if (Components[Loop]->Name.Pos("PanelCursor") == 1)
		{
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeText1Color;
		}

		// Refresh display
		if ( (Visible == true) && (((TControl*)Components[Loop])->Visible == true) )
		{
			((TControl*)Components[Loop])->Invalidate();
		}
	}

	if (mSettingsP->HideAll == false)
	{
		Visible = mSettingsP->GraphicsExplorerFormVisible;
	}
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::WMMove(TMessage &Message)
{
	if ( (Visible == true) && (mSettingsP) && (WindowState != wsMaximized) )
	{
		mSettingsP->GraphicsExplorerFormLeft = Left;
		mSettingsP->GraphicsExplorerFormTop = Top;
	}

	NOT_USED(Message);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ComboBoxDrawItem(TWinControl *Control, int Index,
          TRect &Rect, TOwnerDrawState State)
{
  TComboBox* ComboBox = (TComboBox*)Control;
  TCanvas* Canvas = ComboBox->Canvas;

  if (State.Contains(odSelected))
  {
    Canvas->Brush->Color = clHighlight;
    Canvas->Font->Color = clWhite;
  }
  else
  {
    Canvas->Brush->Color = clWhite;
    Canvas->Font->Color = clBlack;
  }

  // Draw cell
  Canvas->FillRect(Rect);
  AnsiString Text = ComboBox->Items->Strings[Index];
  int TextHeight = Canvas->TextHeight(Text);
  int TextPos = (ComboBox->ItemHeight - TextHeight) / 2;
  Canvas->TextOut(Rect.Left+5, Rect.Top+TextPos, Text);
  if (State.Contains(odFocused))
  {
    Canvas->DrawFocusRect(Rect);
  }

  NOT_USED(Control);
  NOT_USED(Index);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::EditZoomChange(TObject *Sender)
{
  if (EditZoom->Text.IsEmpty() == false)
  {
    mZoom = EditZoom->Text.ToInt();
    if (mZoom < 1) mZoom = 1;

		ResizeTiles();
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::EditTileXChange(TObject *Sender)
{
	if (EditTileX->Text.IsEmpty() == false)
	{
		mTileX = EditTileX->Text.ToInt();
		if (mTileX < 1) mTileX = 1;

		ResizeTiles();
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::EditTileYChange(TObject *Sender)
{
	if (EditTileY->Text.IsEmpty() == false)
	{
		mTileY = EditTileY->Text.ToInt();
		if (mTileY < 1) mTileY = 1;

		ResizeTiles();
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::EditGridXChange(TObject *Sender)
{
	ResizeTiles();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::EditGridYChange(TObject *Sender)
{
	ResizeTiles();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ComboBoxModeChange(TObject *Sender)
{
	SetMode(ComboBoxMode->ItemIndex);
	RefreshGrid();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetTopLeftAddress(tULong Value)
{
  mTopLeftAddress = Value;

  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelAddress->Caption = mTopLeftAddress;
  }
  else
  {
    PanelAddress->Caption = AnsiString::IntToHex(mTopLeftAddress, 6);
  }

	UpdateTileAddress();
	RefreshGrid();
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetCursorAddress(int Value)
{
  if (Value != -1)
  {
    mCursorAddress = Value;

    // Get Byte Address
    if (PanelFormat->Caption.IsEmpty() == true)
    {
      PanelCursorAddress->Caption = mCursorAddress;
    }
    else
    {
      PanelCursorAddress->Caption = AnsiString::IntToHex(mCursorAddress, 6);
    }
  }
  else
  {
    PanelCursorAddress->Caption = "";
    PanelCursorData->Caption = "";
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::SetCursorData(tUChar Value)
{
  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelCursorData->Caption = Value;
  }
  else
  {
    PanelCursorData->Caption = AnsiString::IntToHex(Value, 2);
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetTileAddress(tULong Value)
{
  mTileAddress = Value;

  bool bHexa;
  if (mMapPreview == false)
  {
    bHexa = (PanelFormat->Caption.IsEmpty() == true) ? false : true;
  }
  else
  {
    bHexa = (PanelFormat->Caption.IsEmpty() == true) ? false : true;
  }

  if (bHexa == false)
  {
    PanelTileAddress->Caption = mTileAddress;
  }
  else
  {
    PanelTileAddress->Caption = AnsiString::IntToHex(mTileAddress, 6);
  }

  // Update Tile Index
  int TileIndex = (int)(mTileAddress - mFirstTileAddress) / (mTileX * mTileY);
  if (mMapPreview == false)
  {
    if (TileIndex >= 0)
    {
      mTileIndex = (tULong)TileIndex;
      PanelTileIndex->Visible = true;

      if (PanelFormat->Caption.IsEmpty() == true)
      {
        PanelTileIndex->Caption = mTileIndex;
      }
      else
      {
        PanelTileIndex->Caption = AnsiString::IntToHex(mTileIndex, 2);
      }
    }
    else
    {
      PanelTileIndex->Visible = false;
    }
  }
  else
  {
    PanelTileIndex->Visible = false;
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::SetFirstTileAddress(tULong Value)
{
  mFirstTileAddress = Value;

  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelFirstTile->Caption = mFirstTileAddress;
  }
  else
  {
    PanelFirstTile->Caption = AnsiString::IntToHex(mFirstTileAddress, 6);
  }

  if (mLastTileAddress >= mFirstTileAddress)
  {
    mNbTiles = 1 + ((mLastTileAddress - mFirstTileAddress) / (mTileX * mTileY));
    UpDownMapperTiles->Position = mNbTiles;

    // Update Scrollbar position
    if (mMapPreview == true)
		{
			RefreshGrid();
    }
  }
  else
  {
    mNbTiles = 1;
    UpDownMapperTiles->Position = mNbTiles;
  }

  // Update Tile Index
  int TileIndex = (int)(mTileAddress - mFirstTileAddress) / (mTileX * mTileY);
  if (mMapPreview == false)
  {
    if (TileIndex >= 0)
    {
      mTileIndex = (tULong)TileIndex;
      PanelTileIndex->Visible = true;

      if (PanelFormat->Caption.IsEmpty() == true)
      {
        PanelTileIndex->Caption = mTileIndex;
      }
      else
      {
        PanelTileIndex->Caption = AnsiString::IntToHex(mTileIndex, 2);
      }
    }
    else
    {
      PanelTileIndex->Visible = false;
    }
  }
  else
  {
    PanelTileIndex->Visible = false;
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::SetLastTileAddress(tULong Value)
{
  mLastTileAddress = Value;

  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelLastTile->Caption = mLastTileAddress;
  }
  else
  {
    PanelLastTile->Caption = AnsiString::IntToHex(mLastTileAddress, 6);
  }

  if (mLastTileAddress >= mFirstTileAddress)
  {
    mNbTiles = 1 + ((mLastTileAddress - mFirstTileAddress) / (mTileX * mTileY));
    UpDownMapperTiles->Position = mNbTiles;

    // Update Scrollbar position
    if (mMapPreview == true)
		{
			RefreshGrid();
    }
  }
  else
  {
    mNbTiles = 1;
    UpDownMapperTiles->Position = mNbTiles;
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetMapStartAddress(tULong Value)
{
  mMapStartAddress = Value;

  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelMapStart->Caption = mMapStartAddress;
  }
  else
  {
    PanelMapStart->Caption = AnsiString::IntToHex(mMapStartAddress, 6);
  }

	UpdateTileAddress();

  // Update Scrollbar position
  if (mMapPreview == true)
	{
		RefreshGrid();
  }
}
//---------------------------------------------------------------------------


bool __fastcall TFormGraphicsExplorer::GetMapXReverse(void)
{
  return PanelMapXReverse->Caption.IsEmpty();
}
//---------------------------------------------------------------------------
bool __fastcall TFormGraphicsExplorer::GetMapYReverse(void)
{
  return PanelMapYReverse->Caption.IsEmpty();
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelFormatClick(TObject *Sender)
{
  if (PanelFormat->Caption.IsEmpty() == true)
  {
    PanelFormat->Caption = "&&";
    PanelAddress->Caption = AnsiString::IntToHex(mTopLeftAddress, 6);
    PanelTileAddress->Caption = AnsiString::IntToHex(mTileAddress, 6);
    PanelTileIndex->Caption = AnsiString::IntToHex(mTileIndex, 2);
    PanelFirstTile->Caption = AnsiString::IntToHex(mFirstTileAddress, 6);
    PanelLastTile->Caption = AnsiString::IntToHex(mLastTileAddress, 6);
    PanelMapStart->Caption = AnsiString::IntToHex(mMapStartAddress, 6);
  }
  else
  {
    PanelFormat->Caption = "";
    PanelAddress->Caption = mTopLeftAddress;
    PanelTileAddress->Caption = mTileAddress;
    PanelTileIndex->Caption = mTileIndex;
    PanelFirstTile->Caption = mFirstTileAddress;
    PanelLastTile->Caption = mLastTileAddress;
    PanelMapStart->Caption = mMapStartAddress;
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelAddressClick(TObject *Sender)
{
  TFormEditValue* EditValue = new TFormEditValue(this);
  EditValue->Settings = mSettingsP;
  EditValue->CurrentValue = mTopLeftAddress;
  EditValue->Hexadecimal = true;
  EditValue->MinValue = 0;
  EditValue->MaxValue = mRAMMax;
  if (EditValue->ShowModal() == mrOk)
  {
    TopLeftAddress = EditValue->NewValue;
  }
  delete EditValue;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonSetTopLeftClick(TObject *Sender)
{
  TopLeftAddress = TileAddress;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonRefreshClick(TObject *Sender)
{
  ComboBoxModeChange(Sender);

	RefreshGrid();
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonEditTileClick(TObject *Sender)
{
	EditTile();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelButtonMapPreviewClick(TObject *Sender)
{
  // Invert Map Preview
  mMapPreview = mMapPreview == true ? false : true;

  Caption = Title;
  UpdateControlAccess();

	SetMapPreview(mMapPreview);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ComboBoxAddressingChange(TObject *Sender)
{
	SetAddressingMode(ComboBoxAddressing->ItemIndex);
	RefreshGrid();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ComboBoxMemoryChange(TObject *Sender)
{
  switch (ComboBoxMemory->ItemIndex)
  {
    case 0:
    case 1:
    {
      mRAMMax = 65535;
    }
    break;

    default:
    {
      mRAMMax = (FormMain->NativeCPC->ram_size * 1024) - 1;
    }
    break;
  }

	SetMemory(ComboBoxMemory->ItemIndex);
	SetRAMMax(mRAMMax);
	RefreshGrid();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelCheckMonitoringClick(TObject *Sender)
{
  mMonitoring = mMonitoring == true ? false : true;
  Caption = Title;

	if (mMonitoring == true)
	{
		PanelCheckMonitoring->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckMonitoring->Font->Color = mSettingsP->ThemeButtonColor;
  }
  else
  {
		PanelCheckMonitoring->Color = mSettingsP->ThemeButtonColor;
		PanelCheckMonitoring->Font->Color = mSettingsP->ThemeButtonTextColor;
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


bool __fastcall TFormGraphicsExplorer::OnStepByStepEvent(tULong Condition, tNativeCPC* NativeCPC)
{
  bool Pause = NativeCPC->paused;

  if (mPaused != Pause)
  {
    mPaused = Pause;
    Caption = Title;
  }

  if ( (mMonitoring == true)
       && (Condition & (EC_FRAME_COMPLETE | EC_END_INSTRUCTION)) )
	{
		RefreshGrid();
  }

  if (Condition & (EC_RESET | EC_REFRESH))
  {
    // Refresh RAM content display
    ComboBoxMemoryChange(NULL);
  }


  return Pause;
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PageControlExplorerDrawTab(TCustomTabControl *Control,
          int TabIndex, const TRect &Rect, bool Active)
{
  TCanvas* Canvas = PageControlExplorer->Canvas;

  if (Active == true)
  {
    Canvas->Font->Color = mSettingsP->ThemeText2Color;
  }

  // Get sheet corresponding to TabIndex
  int TabCount=0;
  TTabSheet* Sheet = NULL;
  for (int Loop=0; Loop < PageControlExplorer->PageCount; Loop++)
  {
    if (PageControlExplorer->Pages[Loop]->TabVisible == true)
    {
      if (TabCount == TabIndex)
      {
        Sheet = PageControlExplorer->Pages[Loop];
        break;
      }

      TabCount++;
    }
  }

  // Draw tab
  if (Sheet != NULL)
  {
    Canvas->FillRect(Rect);
    AnsiString Text = Sheet->Caption;
    int TextWidth = Canvas->TextWidth(Text);
    int TextHeight = Canvas->TextHeight(Text);
    int TextPosX = (Rect.Width() - TextWidth) / 2;
    int TextPosY = (PageControlExplorer->TabHeight - TextHeight) / 2;
    Canvas->TextOut(Rect.Left+TextPosX, Rect.Top+TextPosY, Text);
  }

  NOT_USED(Control);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelFirstTileClick(TObject *Sender)
{
  if (mMapPreview == true) return;

  TFormEditValue* EditValue = new TFormEditValue(this);
  EditValue->Settings = mSettingsP;
  EditValue->CurrentValue = mFirstTileAddress;
  EditValue->Hexadecimal = true;
  EditValue->MinValue = 0;
  EditValue->MaxValue = mRAMMax;
  EditValue->Increment = mTileX * mTileY;
  if (EditValue->ShowModal() == mrOk)
  {
    FirstTileAddress = EditValue->NewValue;
  }
  delete EditValue;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelButtonSetFirstTileClick(TObject *Sender)
{
  FirstTileAddress = TileAddress;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelLastTileClick(TObject *Sender)
{
  if (mMapPreview == true) return;

  TFormEditValue* EditValue = new TFormEditValue(this);
  EditValue->Settings = mSettingsP;
  EditValue->CurrentValue = mLastTileAddress;
  EditValue->Hexadecimal = true;
  EditValue->MinValue = 0;
  EditValue->MaxValue = mRAMMax;
  EditValue->Increment = mTileX * mTileY;
  if (EditValue->ShowModal() == mrOk)
  {
    LastTileAddress = EditValue->NewValue;
  }
  delete EditValue;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonSetLastTileClick(TObject *Sender)
{
  LastTileAddress = TileAddress;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::EditMapperTilesChange(TObject *Sender)
{
  if (EditMapperTiles->Text.IsEmpty() == false)
  {
    mNbTiles = EditMapperTiles->Text.ToInt();
    if (mNbTiles < 1) mNbTiles = 1;

    // Update Last Tile Address
    LastTileAddress = mFirstTileAddress + ((mNbTiles - 1) * (mTileX * mTileY));
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------



void __fastcall TFormGraphicsExplorer::PanelMapStartClick(TObject *Sender)
{
  TFormEditValue* EditValue = new TFormEditValue(this);
  EditValue->Settings = mSettingsP;
  EditValue->CurrentValue = mMapStartAddress;
  EditValue->Hexadecimal = true;
  EditValue->MinValue = 0;
  EditValue->MaxValue = mRAMMax;
  if (EditValue->ShowModal() == mrOk)
  {
    MapStartAddress = EditValue->NewValue;
  }
  delete EditValue;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelButtonSetMapStartClick(TObject *Sender)
{
  MapStartAddress = TileAddress;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::EditMapXChange(TObject *Sender)
{
  if (EditMapX->Text.IsEmpty() == false)
  {
    mMapX = EditMapX->Text.ToInt();
    if (mMapX < 1) mMapX = 1;

    if (mMapPreview == true)
		{
			RefreshGrid();
		}

		SetMapX(mMapX);
	}

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelMapXReverseClick(TObject *Sender)
{
  if (PanelMapXReverse->Caption.IsEmpty() == true)
  {
    PanelMapXReverse->Caption = "R";
  }
  else
  {
    PanelMapXReverse->Caption = "";
  }

  if (mMapPreview == true)
	{
		UpdateTileAddress();
		RefreshGrid();
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::EditMapYChange(TObject *Sender)
{
  if (EditMapY->Text.IsEmpty() == false)
  {
    mMapY = EditMapY->Text.ToInt();
    if (mMapY < 1) mMapY = 1;

    if (mMapPreview == true)
		{
			RefreshGrid();
		}

		SetMapY(mMapY);
	}

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelMapYReverseClick(TObject *Sender)
{
  if (PanelMapYReverse->Caption.IsEmpty() == true)
  {
    PanelMapYReverse->Caption = "R";
  }
  else
  {
    PanelMapYReverse->Caption = "";
  }

  if (mMapPreview == true)
	{
		UpdateTileAddress();
		RefreshGrid();
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PageControlExplorerChanging(TObject *Sender,
          bool &AllowChange)
{
  if (mMapPreview == true)
  {
    AllowChange = false;
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonMapperCopyClick(TObject *Sender)
{
	MapperCopy();

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ComboBoxIndexingChange(TObject *Sender)
{
	SetMapperIndexing(ComboBoxIndexing->ItemIndex);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonMouseDown(TObject *Sender,
          TMouseButton Button, TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonTextColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelButtonMouseUp(TObject *Sender,
          TMouseButton Button, TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonTextColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::UpdateControlAccess(void)
{
  // Update controls accesses
  bool ShowWhenEdit = (mMapPreview == true) ? false : true;
	//LabelGrid->Visible = ShowWhenEdit;
  //PanelGrid->Visible = ShowWhenEdit;
  LabelTopLeftAddress->Visible = ShowWhenEdit;
  PanelFormat->Visible = ShowWhenEdit;
  PanelAddress->Visible = ShowWhenEdit;
  LabelCursorAddress->Visible = ShowWhenEdit;
  PanelCursorAddress->Visible = ShowWhenEdit;
  PanelCursorData->Visible = ShowWhenEdit;
  PanelButtonSetTopLeft->Visible = ShowWhenEdit;
  PanelButtonSetFirstTile->Visible = ShowWhenEdit;
  PanelButtonSetLastTile->Visible = ShowWhenEdit;

  if (mMapPreview == false)
  {
    LabelTileAddress->Visible = true;
    PanelTileAddress->Visible = true;
    PanelButtonMapPreview->Color = mSettingsP->ThemeButtonColor;
    PanelButtonMapPreview->Font->Color = mSettingsP->ThemeButtonTextColor;
    EditMapperTiles->Enabled = true;
  }
  else
  {
    PanelButtonMapPreview->Color = mSettingsP->ThemeButtonTextColor;
    PanelButtonMapPreview->Font->Color = mSettingsP->ThemeButtonColor;
    EditMapperTiles->Enabled = false;
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ResizeTiles(void)
{
	int SizeX = (8 * mZoom * mTileX) + 2; // Add 1 pixel margin
	int SizeY = (2 * mZoom * mTileY) + 2; // Add 1 pixel margin

  // Update tile size
  StringGridTiles->DefaultColWidth = SizeX;
  StringGridTiles->DefaultRowHeight = SizeY;

	// Update number of tiles shown
	if (mMapPreview == false)
	{
		StringGridTiles->ColCount = EditGridX->Text.ToInt();
		StringGridTiles->RowCount = EditGridY->Text.ToInt();
	}
	else
	{
		StringGridTiles->ColCount = mMapX;
		StringGridTiles->RowCount = mMapY;
	}

	if (mMapPreview == false)
	{
		// Linear addressing ?
		if (ComboBoxAddressing->ItemIndex == 0)
		{
			ScrollBarAddress->PageSize =  StringGridTiles->ColCount * StringGridTiles->RowCount *
																		mTileX * mTileY;
			ScrollBarAddress->SmallChange = StringGridTiles->RowCount * mTileX * mTileY;
		}
		else
		{
			ScrollBarAddress->PageSize =  StringGridTiles->ColCount * StringGridTiles->RowCount *
																		mTileX * mTileY;
			ScrollBarAddress->SmallChange = StringGridTiles->ColCount * mTileX;
		}
	}

	UpdateTileAddress();
	StringGridTiles->Refresh();

	// Prevent scrollbar update display bug
	if (ScrollBarAddress->Focused() == true)
	{
		StringGridTiles->SetFocus();
	}
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::StringGridTilesDrawCell(TObject *Sender, int ACol,
					int ARow, TRect &Rect, TGridDrawState State)
{
	mDrawCells(ACol, ARow, Rect, State);
	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::DrawStringGridTiles(int ACol,
          int ARow, TRect &Rect, TGridDrawState State)
{
  TCanvas* Canvas = StringGridTiles->Canvas;

  int BaseAddress = mGetTileAddress(TopLeftAddress, ACol, ARow);
  if (BaseAddress <= (int)mRAMMax)
  {
    // Draw frame
    if (State.Contains(gdSelected))
    {
			// Selected frame
      Canvas->Brush->Color = mSettingsP->ThemeText1Color;
      Canvas->FrameRect(Rect);
      mColorsP = mGrayscaleColor;
    }
    else
    {
      Canvas->Pen->Color = clLtGray;
      Canvas->MoveTo(Rect.left, Rect.top);
      Canvas->LineTo(Rect.right-1, Rect.top);
      Canvas->LineTo(Rect.right-1, Rect.bottom-1);
      Canvas->Pen->Color = clDkGray;
      Canvas->LineTo(Rect.left, Rect.bottom-1);
      Canvas->LineTo(Rect.left, Rect.top);
      mColorsP = mColor;
    }

    int Y = Rect.Top + 1;
    for (int LoopY = 0; LoopY < mTileY; LoopY++)
		{
      int X = Rect.Left + 1;
      for (int LoopX = 0; LoopX < mTileX; LoopX++)
      {
        tULong Address = mGetByteAddress(BaseAddress, LoopX, LoopY);
        if (Address <= mRAMMax)
        {
          // Read Byte
          tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, Address);
          // Draw Byte
          X = mDrawByte(Data, X, Y);
        }
        else
        {
          // Empty Byte
          Canvas->Brush->Color = mSettingsP->ThemeBackgroundColor;
          Canvas->FillRect(TRect(X, Y, X+(mZoom*8), Y+mZoom*2));
          X += 8*mZoom;
        }
      }

      Y += 2*mZoom;
    }
  }
  else
  {
    // Empty tile
    Canvas->Brush->Color = mSettingsP->ThemeBackgroundColor;
    Canvas->FillRect(Rect);
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::DrawStringGridMapPreview(int ACol,
          int ARow, TRect &Rect, TGridDrawState State)
{
  TCanvas* Canvas = StringGridTiles->Canvas;

  int Index = ACol + (ARow * StringGridTiles->ColCount);
  int Address = MapStartAddress + Index;
  if (Address <= (int)mRAMMax)
	{
    tUShort Data = mMapPreviewTiles[Index].Index;
    if (Data < (int)NbTiles)
    {
      tULong TileAddress = FirstTileAddress + (Data * mTileX * mTileY);

      // Draw frame
      if (State.Contains(gdSelected))
      {
        Canvas->Brush->Color = mSettingsP->ThemeText1Color;
        Canvas->FrameRect(Rect);
      }
      else
      {
        Canvas->Pen->Color = clLtGray;
        Canvas->MoveTo(Rect.left, Rect.top);
        Canvas->LineTo(Rect.right-1, Rect.top);
        Canvas->LineTo(Rect.right-1, Rect.bottom-1);
        Canvas->Pen->Color = clDkGray;
        Canvas->LineTo(Rect.left, Rect.bottom-1);
        Canvas->LineTo(Rect.left, Rect.top);
      }

      mColorsP = mColor;

      // Draw Tile
      int Y = Rect.Top + 1;
      for (int LoopY = 0; LoopY < mTileY; LoopY++)
      {
        int X = Rect.Left + 1;
        for (int LoopX = 0; LoopX < mTileX; LoopX++)
        {
          tULong Address = mGetByteAddress(TileAddress, LoopX, LoopY);
          if (Address <= mRAMMax)
          {
            // Read Byte
            tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, Address);
            // Draw Byte
            X = mDrawByte(Data, X, Y);
          }
          else
          {
						// Empty Byte
            Canvas->Brush->Color = mSettingsP->ThemeBackgroundColor;
            Canvas->FillRect(TRect(X, Y, X+(mZoom*8), Y+mZoom*2));
            X += 8*mZoom;
          }
        }

        Y += 2*mZoom;
      }

    }
    else
    {
      // Empty tile
      Canvas->Brush->Color = mSettingsP->ThemeBackgroundColor;
      Canvas->FillRect(Rect);

      if (State.Contains(gdSelected))
      {
        Canvas->Brush->Color = mSettingsP->ThemeText1Color;
        Canvas->FrameRect(Rect);
        mColorsP = mGrayscaleColor;
      }
    }
  }
  else
  {
    // Empty tile
    Canvas->Brush->Color = mSettingsP->ThemeBackgroundColor;
    Canvas->FillRect(Rect);

    if (State.Contains(gdSelected))
    {
      Canvas->Brush->Color = mSettingsP->ThemeText1Color;
      Canvas->FrameRect(Rect);
      mColorsP = mGrayscaleColor;
    }
  }

  NOT_USED(State);
}
//---------------------------------------------------------------------------


tULong __fastcall TFormGraphicsExplorer::GetTileAddressLinear(tULong Address, int X, int Y)
{
  return (Address
          + (X * mTileY * mTileX * StringGridTiles->RowCount)
          + (Y * mTileY * mTileX));
}
//---------------------------------------------------------------------------
tULong __fastcall TFormGraphicsExplorer::GetByteAddressLinear(tULong Address, int X, int Y)
{
  return (Address + (Y * mTileX) + X);
}
//---------------------------------------------------------------------------

tULong __fastcall TFormGraphicsExplorer::GetTileAddressScreen(tULong Address, int X, int Y)
{
  return (Address
          + (X * mTileX)
          + (Y * mTileX * StringGridTiles->ColCount));
}
//---------------------------------------------------------------------------
tULong __fastcall TFormGraphicsExplorer::GetByteAddressScreen(tULong Address, int X, int Y)
{
  return (Address + (0x800 * Y) + X);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::UpdatePalette(void)
{
  tNativeCPC* NativeCPC = FormMain->NativeCPC;

  for (int Loop=0; Loop < GA_INK_VALUES_NBELEMENT; Loop++)
  {
    colours_rgb_entry Color = NativeCPC->colours_rgb[NativeCPC->GateArray->ink_values[Loop]];
    mColor[Loop] = (TColor)RGB(Color.colour_r, Color.colour_g, Color.colour_b);

    // Negative grayscale
    int GrayScale = 255 - ((Color.colour_r + Color.colour_g + Color.colour_b) / 3);
    mGrayscaleColor[Loop] = (TColor)RGB(GrayScale, GrayScale, GrayScale);
  }
}
//---------------------------------------------------------------------------


TColor __fastcall TFormGraphicsExplorer::GetPixelColorMode0(tUChar Data, int X)
{
  tUChar Color;
  switch (X % 2)
  {
    case 0:
    {
      Color =   ((Data & 0x02) << 2) | ((Data & 0x20) >> 3)
              | ((Data & 0x08) >> 2) | ((Data & 0x80) >> 7);
    }
    break;
    case 1:
    {
      Color =   ((Data & 0x01) << 3) | ((Data & 0x10) >> 2)
              | ((Data & 0x04) >> 1) | ((Data & 0x40) >> 6);
    }
    break;
  }

  return (mColor[Color]);
}
//---------------------------------------------------------------------------
TColor __fastcall TFormGraphicsExplorer::GetPixelColorMode1(tUChar Data, int X)
{
  tUChar Color;
  switch (X % 4)
  {
    case 0: Color = ((Data & 0x08) >> 2) + ((Data & 0x80) >> 7); break;
    case 1: Color = ((Data & 0x04) >> 1) + ((Data & 0x40) >> 6); break;
    case 2: Color = ((Data & 0x02) >> 0) + ((Data & 0x20) >> 5); break;
    case 3: Color = ((Data & 0x01) << 1) + ((Data & 0x10) >> 4); break;
  }

  return (mColor[Color]);
}
//---------------------------------------------------------------------------
TColor __fastcall TFormGraphicsExplorer::GetPixelColorMode2(tUChar Data, int X)
{
  return (mColor[(Data >> (7 - (X % 8))) & 0x01]);
}
//---------------------------------------------------------------------------


int __fastcall TFormGraphicsExplorer::DrawByteMode0(tUChar Data, int X, int Y)
{
  tUChar Pixel0 =   ((Data & 0x02) << 2)
                  | ((Data & 0x20) >> 3)
                  | ((Data & 0x08) >> 2)
                  | ((Data & 0x80) >> 7);
  tUChar Pixel1 =   ((Data & 0x01) << 3)
                  | ((Data & 0x10) >> 2)
                  | ((Data & 0x04) >> 1)
                  | ((Data & 0x40) >> 6);

  // Draw 2 pixels
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel0];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*4), Y+mZoom*2));
  X += 4*mZoom;
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel1];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*4), Y+mZoom*2));
  X += 4*mZoom;

  return X;
}
//---------------------------------------------------------------------------
int __fastcall TFormGraphicsExplorer::DrawByteMode1(tUChar Data, int X, int Y)
{
  tUChar Pixel1 = ((Data & 0x08) >> 2) + ((Data & 0x80) >> 7);
  tUChar Pixel2 = ((Data & 0x04) >> 1) + ((Data & 0x40) >> 6);
  tUChar Pixel3 = ((Data & 0x02) >> 0) + ((Data & 0x20) >> 5);
  tUChar Pixel4 = ((Data & 0x01) << 1) + ((Data & 0x10) >> 4);

  // Draw 4 pixels
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel1];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*2), Y+mZoom*2));
  X += 2*mZoom;
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel2];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*2), Y+mZoom*2));
  X += 2*mZoom;
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel3];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*2), Y+mZoom*2));
  X += 2*mZoom;
  StringGridTiles->Canvas->Brush->Color = mColorsP[Pixel4];
  StringGridTiles->Canvas->FillRect(TRect(X, Y, X+(mZoom*2), Y+mZoom*2));
  X += 2*mZoom;

  return X;
}
//---------------------------------------------------------------------------
int __fastcall TFormGraphicsExplorer::DrawByteMode2(tUChar Data, int X, int Y)
{
  // Draw 8 pixels
  for (int Loop=0; Loop < 8; Loop++)
  {
    StringGridTiles->Canvas->Brush->Color = mColorsP[Data & 0x80 ? 1 : 0];
    StringGridTiles->Canvas->FillRect(TRect(X, Y, X+mZoom, Y+mZoom*2));
    X += mZoom;
    Data = Data << 1;
  }

  return X;
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::UpdateMapPreview(void)
{
  if (FormMain->NativeCPC == NULL) return;
  if (mReadData == NULL) return;

  delete [] mMapPreviewTiles;
  mMapPreviewTiles = new TGraphicsExplorerTilePreview[mMapX * mMapY];

  Screen->Cursor = crHourGlass;

  mPreviewAddress = MapStartAddress;
  mPreviewOccurences = 0;
  mPreviewIncrements = 0;

  // Forward X
  if (MapXReverse == true)
  {
    // Forward Y
    if (MapYReverse == true)
    {
      for (int LoopY=0; LoopY < (int)mMapY; LoopY++)
      {
        for (int LoopX=0; LoopX < (int)mMapX; LoopX++)
        {
          if (mPreviewAddress <= mRAMMax)
          {
            mDrawMapPreview((LoopY * mMapX) + LoopX);
					}
        }
      }
    }
    // Reverse Y
    else
    {
      for (int LoopY=(int)mMapY; LoopY; LoopY--)
      {
        for (int LoopX=0; LoopX < (int)mMapX; LoopX++)
        {
          if (mPreviewAddress <= mRAMMax)
          {
            mDrawMapPreview(((LoopY-1) * mMapX) + LoopX);
          }
        }
      }
    }
  }
  // Reverse X
  else
  {
    // Forward Y
    if (MapYReverse == true)
    {
      for (int LoopY=0; LoopY < (int)mMapY; LoopY++)
      {
        for (int LoopX=(int)mMapX; LoopX; LoopX--)
        {
          if (mPreviewAddress <= mRAMMax)
          {
            mDrawMapPreview((LoopY * mMapX) + (LoopX-1));
          }
        }
      }
    }
    // Reverse Y
    else
    {
      for (int LoopY=(int)mMapY; LoopY; LoopY--)
      {
        for (int LoopX=(int)mMapX; LoopX; LoopX--)
				{
          if (mPreviewAddress <= mRAMMax)
          {
            mDrawMapPreview(((LoopY-1) * mMapX) + (LoopX-1));
          }
        }
      }
    }
  }

  Screen->Cursor = crDefault;
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::UpdateMapPreviewDataLinearBytes(int Index)
{
  mMapPreviewTiles[Index].Index = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress) % NbTiles;
  mMapPreviewTiles[Index].Address = mPreviewAddress;
  mPreviewAddress++;
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::UpdateMapPreviewDataCompressedRL0(int Index)
{
  if (mPreviewOccurences > 0)
  {
    mMapPreviewTiles[Index].Index = 0U;
    mMapPreviewTiles[Index].Address = mPreviewAddress;
    mPreviewOccurences--;
    if (mPreviewOccurences == 0U)
    {
      mPreviewAddress += 2;
    }
  }
  else
  {
    if (mPreviewAddress <= mRAMMax)
    {
      tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress);
      mMapPreviewTiles[Index].Index = Data % NbTiles;
      mMapPreviewTiles[Index].Address = mPreviewAddress;

      if (Data == 0U)
      {
        if ((mPreviewAddress+1) <= mRAMMax)
        {
          mPreviewOccurences = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress+1) - 1;
          if (mPreviewOccurences == 0U)
          {
            mPreviewAddress += 2;
          }
        }
      }
      else
      {
        mPreviewAddress++;
      }
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::UpdateMapPreviewDataLinearWordsBigEndian(int Index)
{
  tUShort TileIndex = (tUShort)mReadData(FormMain->NativeCPC, mPreviewAddress);
  TileIndex = TileIndex << 8;
  TileIndex = TileIndex + (tUShort)mReadData(FormMain->NativeCPC, mPreviewAddress+1);

  mMapPreviewTiles[Index].Index = TileIndex % NbTiles;
  mMapPreviewTiles[Index].Address = mPreviewAddress;
  mPreviewAddress += 2;
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::UpdateMapPreviewDataCompressedLinear(int Index)
{
  if (mPreviewOccurences > 0)
  {
    tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress);
    mMapPreviewTiles[Index].Index = Data % NbTiles;
    mMapPreviewTiles[Index].Address = mPreviewAddress;
    mPreviewOccurences--;
    if (mPreviewOccurences == 0U)
		{
      mPreviewAddress++;
    }
  }
  else if (mPreviewIncrements > 0)
  {
    tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress);
    mMapPreviewTiles[Index].Index = Data % NbTiles;
    mMapPreviewTiles[Index].Address = mPreviewAddress++;
    mPreviewIncrements--;
  }
  else
  {
    if (mPreviewAddress <= mRAMMax)
    {
      tUChar Increment = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress++);
      if (mPreviewAddress <= mRAMMax)
      {
        tUChar Data = (tUChar)mReadData(FormMain->NativeCPC, mPreviewAddress);

        mMapPreviewTiles[Index].Index = Data % NbTiles;
        mMapPreviewTiles[Index].Address = mPreviewAddress;

        // Linear ?
        if (Increment & 0x80)
        {
          mPreviewIncrements = (Increment & 0x7f) - 1;
          mPreviewAddress++;
        }
        else
        {
          mPreviewOccurences = Increment - 1;
          if (mPreviewOccurences == 0U)
          {
            mPreviewAddress++;
          }
        }
      }
    }
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::SetAddressingMode(int Value)
{
  mAddressingMode = Value;

  switch (mAddressingMode)
  {
		case 0:
    {
      mGetByteAddress = GetByteAddressLinear;
      mGetTileAddress = GetTileAddressLinear;
    }
    break;

    case 1:
    {
      mGetByteAddress = GetByteAddressScreen;
      mGetTileAddress = GetTileAddressScreen;
			EditGridX->Text = 40;
			EditGridY->Text = 25;
			TopLeftAddress = 0xC000;
		}
		break;
  }

  // Update Scrollbar position
  if (mMapPreview == false)
  {
    ScrollBarAddress->Position = TopLeftAddress;
  }
  else
  {
    ScrollBarAddress->Position = MapStartAddress;
  }
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMemory(int Value)
{
  switch (Value)
  {
    case 0:
    {
      mReadData = Engine_ReadBankedRead;
      mWriteData = Engine_WriteBankedRead;
    }
    break;

    case 1:
    {
      mReadData = Engine_ReadBankedWrite;
      mWriteData = Engine_WriteBankedWrite;
		}
    break;

    default:
    {
      mReadData = Engine_ReadRAM;
      mWriteData = Engine_WriteRAM;
    }
    break;
  }

  UpdateMapPreview();
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMapperIndexing(int Value)
{
  switch (Value)
  {
    // Compresses RL0
    case 1:
    {
      mDrawMapPreview = UpdateMapPreviewDataCompressedRL0;
    }
    break;

    // Linear words Big Endian
    case 2:
    {
      mDrawMapPreview = UpdateMapPreviewDataLinearWordsBigEndian;
    }
    break;

    // Compressed & Linear
    case 3:
    {
      mDrawMapPreview = UpdateMapPreviewDataCompressedLinear;
    }
    break;

    // Linear bytes
    default:
    {
			mDrawMapPreview = UpdateMapPreviewDataLinearBytes;
    }
    break;
  }

  UpdateMapPreview();

  if (mMapPreview == true)
  {
    StringGridTiles->Refresh();
  }
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMapPreview(bool Value)
{
  mMapPreview=Value;

  StringGridTiles->ColCount=1;
  StringGridTiles->RowCount=1;
  StringGridTiles->Col = 0;
  StringGridTiles->Row = 0;
	//StringGridTiles->ScrollBars = (mMapPreview == true) ? ssBoth : ssNone;

  if (mMapPreview == true)
  {
    mDrawCells = DrawStringGridMapPreview;
    ScrollBarAddress->Position = MapStartAddress;
  }
  else
  {
    mDrawCells = DrawStringGridTiles;
    ScrollBarAddress->Position = TopLeftAddress;
  }

  UpdateMapPreview();
  UpdateTileAddress();
  ResizeTiles();
}
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMapX(tULong Value)
{
  mMapX = Value;

  delete [] mMapPreviewTiles;
	mMapPreviewTiles = new TGraphicsExplorerTilePreview[mMapX * mMapY];

  UpdateMapPreview();
  ResizeTiles();
};
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMapY(tULong Value)
{
  mMapY = Value;

  delete [] mMapPreviewTiles;
  mMapPreviewTiles = new TGraphicsExplorerTilePreview[mMapX * mMapY];

  UpdateMapPreview();
  ResizeTiles();
};
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetMode(int Value)
{
  if (Value == 0)
  {
    // Read current mode
		mMode = (tUChar)FormMain->NativeCPC->GateArray->scr_mode;
  }
  else
  {
    mMode = (tUChar)(Value - 1);
  }

  switch (mMode)
  {
    case 0:
    {
      mDrawByte = DrawByteMode0;
      mGetPixelColor = GetPixelColorMode0;
    }
    break;

    case 1:
    {
      mDrawByte = DrawByteMode1;
      mGetPixelColor = GetPixelColorMode1;
    }
    break;

    case 2:
    {
      mDrawByte = DrawByteMode2;
      mGetPixelColor = GetPixelColorMode2;
    }
    break;
  }
};
//---------------------------------------------------------------------------
void __fastcall TFormGraphicsExplorer::SetRAMMax(tULong Value)
{
  mRAMMax = Value;

  if (ScrollBarAddress->Position > (int)mRAMMax) ScrollBarAddress->Position = (int)mRAMMax;
  ScrollBarAddress->Max = mRAMMax;

  UpdateMapPreview();
};
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::RefreshGrid(void)
{
  UpdatePalette();
	UpdateMapPreview();

	StringGridTiles->Refresh();
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::UpdateTileAddress(void)
{
  if (mMapPreview == true)
  {
    // Update Tile address
    int Index = StringGridTiles->Col + (StringGridTiles->Row * mMapX);
    TileAddress = mMapPreviewTiles[Index].Address;
  }
  else
  {
    // Update tile address
    TileAddress = TopLeftAddress
                  + (StringGridTiles->Col * mTileY * mTileX * StringGridTiles->RowCount)
                  + (StringGridTiles->Row * mTileY * mTileX);
  }

  // Update Scrollbar position
  if (mMapPreview == false)
  {
    ScrollBarAddress->Position = TopLeftAddress;
  }
  else
  {
    ScrollBarAddress->Position = MapStartAddress;
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::EditTile(void)
{
  // Prepare tile edition
  GraphicsEditorP->ShowHint = ShowHint;
	GraphicsEditorP->Emulator = mEmulatorP;
  GraphicsEditorP->Settings = mSettingsP;
  GraphicsEditorP->TileX = mTileX;
  GraphicsEditorP->TileY = mTileY;
	GraphicsEditorP->Mode = mMode;
  int Pixels=0;
  for (int LoopY=0; LoopY < mTileY; LoopY++)
  {
    for (int LoopX=0; LoopX < mTileX; LoopX++)
    {
      tULong Address = mGetByteAddress(TileAddress, LoopX, LoopY);
      if (Address <= mRAMMax)
      {
        GraphicsEditorP->Data[Pixels++] = (tUChar)mReadData(FormMain->NativeCPC, Address);
      }
      else
      {
        GraphicsEditorP->Data[Pixels++] = 0;
      }
    }
  }

  if (GraphicsEditorP->ShowModal() == mrOk)
  {
    // Apply tile updates
    Pixels=0;
    for (int LoopY=0; LoopY < mTileY; LoopY++)
    {
      for (int LoopX=0; LoopX < mTileX; LoopX++)
      {
        tULong Address = mGetByteAddress(TileAddress, LoopX, LoopY);
        if ((Address <= mRAMMax) && (mWriteData) )
        {
          mWriteData(FormMain->NativeCPC, Address, GraphicsEditorP->Data[Pixels++]);
        }
      }
    }

    RefreshGrid();
		FormMain->Refresh();
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::MapperCopy(void)
{
	// Create bitmap
  TBitmap* BitmapP = new TBitmap();

  if (BitmapP)
  {
    int SizeX;
    switch (mMode)
    {
      case 0: SizeX = 4; break;
      case 1: SizeX = 2; break;
      case 2: SizeX = 1; break;
    }

    // Populate bitmap
    if (mMapPreview == false)
    {
      BitmapP->PixelFormat = pf32bit;
      BitmapP->Width = mTileX * 8 * StringGridTiles->ColCount;
      BitmapP->Height = mTileY * 2 * StringGridTiles->RowCount;

      //
      // Copy Current Tiles Display
      //
      for (int TileY = 0; TileY < StringGridTiles->RowCount; TileY++)
      {
        for (int TileX = 0; TileX < StringGridTiles->ColCount; TileX++)
        {
          int TileAddress = mGetTileAddress(TopLeftAddress, TileX, TileY);

          int BaseMapX = TileX * mTileX * 8;
          int BaseMapY = TileY * mTileY * 2;

          for (int ByteY = 0; ByteY < mTileY; ByteY++)
          {
            for (int ByteX = 0; ByteX < mTileX; ByteX++)
            {
              tULong Address = mGetByteAddress(TileAddress, ByteX, ByteY);
              tUChar Data = 0;
              if (Address <= mRAMMax)
              {
                // Read data
                Data = (tUChar)mReadData(FormMain->NativeCPC, Address);
							}

              int PixelX = BaseMapX + (ByteX * 8);
              int PixelY = BaseMapY + (ByteY * 2);
              for (int X=0; X < 8; X++)
              {
                //BitmapP->Canvas->Pixels[X+(ByteX * 8)][Y+(ByteY * 2)] = mGetPixelColor(Data, X/SizeX);
                TColor Color = mGetPixelColor(Data, X/SizeX);
                BitmapP->Canvas->Pixels[PixelX + X][PixelY+0] = Color;
                BitmapP->Canvas->Pixels[PixelX + X][PixelY+1] = Color;
              }
            }
          }
        }
      }

      // Display message
			Application->MessageBox(_T("All shown tiles have been copied to clipboard."), _T("Clipboard"), MB_OK);
    }
    else
    {
      BitmapP->PixelFormat = pf32bit;
      BitmapP->Width = mTileX * 8 * mMapX;
      BitmapP->Height = mTileY * 2 * mMapY;

      Screen->Cursor = crHourGlass;

      //
      // Copy Map
      //
      for (int TileY = 0; TileY < (int)mMapY; TileY++)
      {
        for (int TileX = 0; TileX < (int)mMapX; TileX++)
        {
          tUShort TileIndex = mMapPreviewTiles[TileX + (TileY * mMapX)].Index;
          tULong TileAddress = FirstTileAddress + (TileIndex * mTileX * mTileY);

          int BaseMapX = TileX * mTileX * 8;
          int BaseMapY = TileY * mTileY * 2;

          for (int ByteY = 0; ByteY < mTileY; ByteY++)
          {
						for (int ByteX = 0; ByteX < mTileX; ByteX++)
            {
              tULong Address = mGetByteAddress(TileAddress, ByteX, ByteY);
              tUChar Data = 0;
              if ( (Address <= mRAMMax) && (TileIndex < (tUShort)NbTiles) )
              {
                // Read data
                Data = (tUChar)mReadData(FormMain->NativeCPC, Address);
              }

              int PixelX = BaseMapX + (ByteX * 8);
              int PixelY = BaseMapY + (ByteY * 2);
              for (int X=0; X < 8; X++)
              {
                TColor Color = mGetPixelColor(Data, X/SizeX);
                BitmapP->Canvas->Pixels[PixelX + X][PixelY+0] = Color;
                BitmapP->Canvas->Pixels[PixelX + X][PixelY+1] = Color;
              }
            }
          }
        }
      }

      Screen->Cursor = crDefault;

      // Display message
			Application->MessageBox(_T("Entire map preview has been copied to clipboard."), _T("Clipboard"), MB_OK);
    }

    // Copy Bitmap to clipboard
    WORD AFormat;
    NativeUInt AData;
    HPALETTE APalette;
    BitmapP->SaveToClipboardFormat(AFormat, AData, APalette);
    TClipboard *pCB = Clipboard();
    pCB->Clear();
    pCB->SetAsHandle(AFormat, AData);

    delete BitmapP;
  }
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::ButtonByteDecrementClick(TObject *Sender)
{
  if (mMapPreview == false)
  {
    if (TopLeftAddress > 0)
    {
      TopLeftAddress--;
    }
  }
  else
  {
    if (MapStartAddress > 0)
    {
      MapStartAddress--;
    }
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::ButtonByteIncrementClick(TObject *Sender)
{
  if (mMapPreview == false)
  {
    if (TopLeftAddress < mRAMMax)
    {
      TopLeftAddress++;
    }
  }
  else
  {
    if (MapStartAddress < mRAMMax)
    {
      MapStartAddress++;
    }
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::ScrollBarAddressChange(TObject *Sender)
{
  if (ScrollBarAddress->Focused() == true)
  {
    if (mMapPreview == false)
    {
      TopLeftAddress = ScrollBarAddress->Position;
    }
    else
    {
      MapStartAddress = ScrollBarAddress->Position;
    }
		StringGridTiles->Refresh();
		UpdateTileAddress();
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::StringGridTilesDblClick(TObject *Sender)
{
  if (mMapPreview == false)
  {
    EditTile();
  }

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------



void __fastcall TFormGraphicsExplorer::StringGridTilesSelectCell(TObject *Sender,
          int ACol, int ARow, bool &CanSelect)
{
  if (mMapPreview == false)
  {
		TileAddress = mGetTileAddress(TopLeftAddress, ACol, ARow);
	}
	else
	{
		int Index = ACol + (ARow * mMapX);
		TileAddress = mMapPreviewTiles[Index].Address;
	}

	NOT_USED(Sender);
	NOT_USED(CanSelect);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::StringGridTilesMouseMove(TObject *Sender, TShiftState Shift,
          int X, int Y)
{
  int ACol = X / StringGridTiles->DefaultColWidth;
  int ARow = Y / StringGridTiles->DefaultRowHeight;

  if ( (ACol < StringGridTiles->ColCount) && (ARow < StringGridTiles->RowCount) )
  {
    // Update cursor address
    int PixelX = X - (ACol * StringGridTiles->DefaultColWidth);
    PixelX = PixelX / ((StringGridTiles->DefaultColWidth-2) / mTileX);
    if (PixelX > (mTileX-1))
    {
      PixelX = mTileX-1;
    }
    int PixelY = Y - (ARow * StringGridTiles->DefaultRowHeight);
    PixelY = PixelY / ((StringGridTiles->DefaultRowHeight-2) / mTileY);
    if (PixelY > (mTileY-1))
    {
      PixelY = mTileY-1;
    }

		CursorAddress = mGetByteAddress(mGetTileAddress(TopLeftAddress, ACol, ARow), PixelX, PixelY);
		CursorData = (tUChar)mReadData(FormMain->NativeCPC, CursorAddress);
	}
	else
	{
		CursorAddress = -1;
	}

	NOT_USED(Sender);
	NOT_USED(Shift);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::StringGridTilesMouseLeave(TObject *Sender)
{
  mCursorAddressBeforeLeaving = CursorAddress;
	CursorAddress = -1;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::PanelButtonGridAutoSizeClick(TObject *Sender)
{
	int SizeX = (8 * mZoom * mTileX) + 2; // Add 1 pixel margin
	int SizeY = (2 * mZoom * mTileY) + 2; // Add 1 pixel margin
	StringGridTiles->DefaultColWidth = SizeX;
	StringGridTiles->DefaultRowHeight = SizeY;
	int NbCol = StringGridTiles->Width / SizeX;
	if (NbCol == 0) NbCol = 1;
	int NbRow = StringGridTiles->Height / SizeY;
	if (NbRow == 0) NbRow = 1;
	EditGridX->Text = NbCol;
	EditGridY->Text = NbRow;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormGraphicsExplorer::ShowMemoryAtTileMenuClick(TObject *Sender)
{
	FormMemoryEditor->ShowCurrentAddress(mTileAddress);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::ShowMemoryatcursoraddress1Click(TObject *Sender)
{
	FormMemoryEditor->ShowCurrentAddress(mCursorAddressBeforeLeaving);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormGraphicsExplorer::PanelButtonShowTileMemoryClick(TObject *Sender)
{
	FormMemoryEditor->ShowCurrentAddress(mTileAddress);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

