unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus, ComCtrls, ToolWin, ExtCtrls, GraphicEx, GR32, GR32_Image,
  IniFiles, Buttons, StdCtrls, FileCtrl, ImgList, Themes, ZipForge, ShellApi,
  BarMenus, SBPro, VirtualTrees, madExceptVcl, ExtDlgs;

type
  pGameInfo = ^TGameInfo;
  TGameInfo = record
    eImageIndex: Integer;
    eConsoleType: ShortInt;
    eDescription,
    eGameSize,
    eGameFileName,
    eMediaDescription,
    eSystemDescription: String;
  end;
  TGameInfoArray = array of TGameInfo;

type
  TGameSortCompare = function (Item1, Item2: pGameInfo; ColumnID: Integer): Integer;

const
  MaxSystems = 27; // always update this value when adding new systems!!!!!
  SmoothType: packed array [0..6] of TStretchFilter = (sfNearest, sfLinear, sfLanczos, sfMitchell, sfSpline, sfCosine, sfDraft);
  

type
  TFormMain = class(TForm)
    MainMenu: TBcBarMainMenu;
    MenuFile: TMenuItem;
    MenuExit: TMenuItem;
    CoolBar: TCoolBar;
    OpenFileDialog: TOpenDialog;
    ButtonSuperScopeSNES: TToolButton;
    ButtonMouseSNES_PSX: TToolButton;
    Images: TImageList;
    MenuGames: TMenuItem;
    MenuCreateGamesList: TMenuItem;
    MenuCreateGamesListSNES: TMenuItem;
    MenuCreateGamesListGBA: TMenuItem;
    MenuCreateGamesListNES: TMenuItem;
    MenuCreateGamesListSEGA: TMenuItem;
    MenuSnapshots: TMenuItem;
    MenuStretchPicture: TMenuItem;
    MenuSmoothPicture: TMenuItem;
    FontDialog: TFontDialog;
    ZipForge: TZipForge;
    PanelGames: TPanel;
    PanelPictures: TPanel;
    Splitter: TSplitter;
    PopupGamesFilters: TBcBarPopupMenu;
    GameFilterSNES: TMenuItem;
    GameFilterGBA: TMenuItem;
    GameFilterNES: TMenuItem;
    GameFilterGenesis: TMenuItem;
    GameFilterAtari2600: TMenuItem;
    N2: TMenuItem;
    GameFilterAllConsoles: TMenuItem;
    ImageListGameIconSmall: TImageList;
    ImageListGameIconBig: TImageList;
    MenuCreateGamesListAtari2600: TMenuItem;
    MenuPreferences: TMenuItem;
    N3: TMenuItem;
    MenuView: TMenuItem;
    Picture2: TImage32;
    Picture1: TImage32;
    MenuHideFirstPicture: TMenuItem;
    N4: TMenuItem;
    MenuAlwaysCreateNewList: TMenuItem;
    MenuHelp: TMenuItem;
    MenuVisitHomepage: TMenuItem;
    SendEMail: TMenuItem;
    N1: TMenuItem;
    MenuAbout: TMenuItem;
    GameFilterIntellivision: TMenuItem;
    GameFilterMasterSystem: TMenuItem;
    GameFilterOdyssey2: TMenuItem;
    MenuCreateGamesListIntellivision: TMenuItem;
    MenuCreateGamesListMasterSystem: TMenuItem;
    MenuCreateGamesListOdyssey2: TMenuItem;
    MenuCreateGamesListGameGear: TMenuItem;
    GameFilterGameGear: TMenuItem;
    MenuShowSnapshots: TMenuItem;
    N5: TMenuItem;
    GameFilterColecoVision: TMenuItem;
    MenuCreateGamesListColecoVision: TMenuItem;
    GameFilterGP32: TMenuItem;
    MenuCreateGamesListGP32: TMenuItem;
    MenuMiscellaneous: TMenuItem;
    MenuShowFullCommandLine: TMenuItem;
    MenuCreateGamesListPCEngine: TMenuItem;
    MenuCreateGamesListAtariLynx: TMenuItem;
    GameFilterPCEngine: TMenuItem;
    GameFilterAtariLynx: TMenuItem;
    ButtonsToolBar: TToolBar;
    MenuCreateGamesListAtari5200: TMenuItem;
    GameFilterAtari5200: TMenuItem;
    GameFilterAtariJaguar: TMenuItem;
    MenuCreateGamesListAtariJaguar: TMenuItem;
    MenuViewDocumentation: TMenuItem;
    N6: TMenuItem;
    MenuCreateGamesListSega32X: TMenuItem;
    GameFilterSega32X: TMenuItem;
    GameFilterNeoGeoPocket: TMenuItem;
    MenuCreateGamesListNeoGeoPocket: TMenuItem;
    PopupLatestPlayedGames: TBcBarPopupMenu;
    PopupSNESLatestPlayedGames: TMenuItem;
    PopupGBALatestPlayedGames: TMenuItem;
    PopupNESLatestPlayedGames: TMenuItem;
    PopupGenesisLatestPlayedGames: TMenuItem;
    PopupAtari2600LatestPlayedGames: TMenuItem;
    PopupIntellivisionLatestPlayedGames: TMenuItem;
    PopupMasterSystemLatestPlayedGames: TMenuItem;
    PopupOdyssey2LatestPlayedGames: TMenuItem;
    PopupGameGearLatestPlayedGames: TMenuItem;
    PopupColecoVisionLatestPlayedGames: TMenuItem;
    PopupGP32LatestPlayedGames: TMenuItem;
    PopupPCEngineLatestPlayedGames: TMenuItem;
    PopupAtariLynxLatestPlayedGames: TMenuItem;
    PopupAtari5200LatestPlayedGames: TMenuItem;
    PopupAtariJaguarLatestPlayedGames: TMenuItem;
    PopupSega32XLatestPlayedGames: TMenuItem;
    PopupNeoGeoPocketLatestPlayedGames: TMenuItem;
    PopupNintendo64LatestPlayedGames: TMenuItem;
    PopupSegaCDLatestPlayedGames: TMenuItem;
    MenuCreateGamesListSegaCD: TMenuItem;
    MenuCreateGamesListNintendo64: TMenuItem;
    GameFilterSegaCD: TMenuItem;
    GameFilterNintendo64: TMenuItem;
    PopupEmulators: TBcBarPopupMenu;
    MenuSuperNintendo: TMenuItem;
    MenuGameboyAdvance: TMenuItem;
    MenuNintendo: TMenuItem;
    MenuGenesis: TMenuItem;
    MenuAtari2600: TMenuItem;
    MenuIntellivision: TMenuItem;
    MenuMasterSystem: TMenuItem;
    MenuOdyssey2: TMenuItem;
    MenuGameGear: TMenuItem;
    MenuColecoVision: TMenuItem;
    MenuGP32: TMenuItem;
    MenuPCEngine: TMenuItem;
    MenuAtariLynx: TMenuItem;
    MenuAtari5200SuperSytem: TMenuItem;
    MenuAtariJaguar: TMenuItem;
    MenuSega32X: TMenuItem;
    MenuNeoGeoPocket: TMenuItem;
    MenuNintendo64: TMenuItem;
    MenuSegaCD: TMenuItem;
    PopupSuperNintendo_1: TMenuItem;
    PopupSuperNintendo_2: TMenuItem;
    PopupGameboyAdvance_1: TMenuItem;
    PopupGameboyAdvance_2: TMenuItem;
    PopupNintendo_1: TMenuItem;
    PopupNintendo_2: TMenuItem;
    PopupGenesis_1: TMenuItem;
    PopupGenesis_2: TMenuItem;
    PopupAtari2600_1: TMenuItem;
    PopupAtari2600_2: TMenuItem;
    PopupIntellivision_1: TMenuItem;
    PopupIntellivision_2: TMenuItem;
    PopupMasterSystem_1: TMenuItem;
    PopupMasterSystem_2: TMenuItem;
    PopupOdyssey2_1: TMenuItem;
    PopupOdyssey2_2: TMenuItem;
    PopupGameGear_1: TMenuItem;
    PopupGameGear_2: TMenuItem;
    PopupColecoVision_1: TMenuItem;
    PopupColecoVision_2: TMenuItem;
    PopupGP32_1: TMenuItem;
    PopupGP32_2: TMenuItem;
    PopupPCEngine_1: TMenuItem;
    PopupPCEngine_2: TMenuItem;
    PopupAtariLynx_1: TMenuItem;
    PopupAtariLynx_2: TMenuItem;
    PopupAtari5200_1: TMenuItem;
    PopupAtari5200_2: TMenuItem;
    PopupAtariJaguar_1: TMenuItem;
    PopupAtariJaguar_2: TMenuItem;
    PopupSega32X_1: TMenuItem;
    PopupSega32X_2: TMenuItem;
    PopupNeoGeoPocket_1: TMenuItem;
    PopupNeoGeoPocket_2: TMenuItem;
    PopupNintendo64_1: TMenuItem;
    PopupNintendo64_2: TMenuItem;
    PopupSegaCD_1: TMenuItem;
    PopupSegaCD_2: TMenuItem;
    N7: TMenuItem;
    MenuGameboy: TMenuItem;
    PopupGameboy_1: TMenuItem;
    PopupGameboy_2: TMenuItem;
    PopupGameboyLatestPlayedGames: TMenuItem;
    GameFilterGameboy: TMenuItem;
    MenuCreateGamesListGameboy: TMenuItem;
    ButtonGamesFilters: TToolButton;
    ButtonEmulators: TToolButton;
    ToolButton4: TToolButton;
    LatestPlayedGamesHeader: TMenuItem;
    GamesFiltersHeader: TMenuItem;
    N8: TMenuItem;
    EmulatorsHeader: TMenuItem;
    N9: TMenuItem;
    ToolButton1: TToolButton;
    ButtonHugoParameters: TToolButton;
    MenuCreateGamesListPlayStation: TMenuItem;
    GameFilterPlayStation: TMenuItem;
    PopupPlayStationLatestPlayedGames: TMenuItem;
    MenuPlayStation: TMenuItem;
    PopupPlayStation_1: TMenuItem;
    PopupPlayStation_2: TMenuItem;
    ButtonGunPSX: TToolButton;
    N10: TMenuItem;
    MenuAddPlayStationCDGames: TMenuItem;
    ImageListEmulatorsIcons: TImageList;
    MenuSmoothPictureLanczos: TMenuItem;
    MenuSmoothPictureLinear: TMenuItem;
    N11: TMenuItem;
    MenuSmoothPictureOff: TMenuItem;
    GameFilterGameCube: TMenuItem;
    PopupGameCubeLatestPlayedGames: TMenuItem;
    MenuGameCube: TMenuItem;
    PopupGameCube_1: TMenuItem;
    PopupGameCube_2: TMenuItem;
    MenuCreateGamesListGameCube: TMenuItem;
    MenuCreateGamesListAtari7800: TMenuItem;
    GameFilterAtari7800: TMenuItem;
    PopupAtari7800LatestPlayedGames: TMenuItem;
    MenuAtari7800: TMenuItem;
    PopupAtari7800_1: TMenuItem;
    PopupAtari7800_2: TMenuItem;
    MenuSmoothPictureMitchell: TMenuItem;
    MenuSmoothPictureSpline: TMenuItem;
    MenuSmoothPictureCosine: TMenuItem;
    PanelStatusBar: TPanel;
    StatusBar: TStatusBarPro;
    MenuCreateGamesListDreamcast: TMenuItem;
    GameFilterDreamcast: TMenuItem;
    PopupDreamcastLatestPlayedGames: TMenuItem;
    MenuDreamcast: TMenuItem;
    PopupDreamcast_1: TMenuItem;
    PopupDreamcast_2: TMenuItem;
    GamesTree: TVirtualStringTree;
    MenuSmoothPictureDraft: TMenuItem;
    MenuEmulators: TMenuItem;
    MenuEmulatorSetup: TMenuItem;
    OpenImageDialog: TOpenPictureDialog;
    MenuCreateGamesListWataraSupervision: TMenuItem;
    MenuCreateGamesListWonderswan: TMenuItem;
    GameFilterWataraSupervision: TMenuItem;
    GameFilterWonderswan: TMenuItem;
    PopupWataraSupervisionLatestPlayedGames: TMenuItem;
    PopupWonderswanLatestPlayedGames: TMenuItem;
    MenuWataraSupervision: TMenuItem;
    PopupWataraSupervision_1: TMenuItem;
    PopupWataraSupervision_2: TMenuItem;
    MenuWonderswan: TMenuItem;
    PopupWonderswan_1: TMenuItem;
    PopupWonderswan_2: TMenuItem;
    MenuViewLargeGameIcons: TMenuItem;
    procedure MenuExitClick(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure MenuStretchPictureClick(Sender: TObject);
    procedure MenuCreateGamesListSNESClick(Sender: TObject);
    procedure ZipForgeProcessFileFailure(Sender: TObject; FileName: String;
      Operation: TZFProcessOperation; NativeError, ErrorCode: Integer;
      ErrorMessage: String; var Action: TZFAction);
    procedure MenuPreferencesClick(Sender: TObject);
    procedure MenuHideFirstPictureClick(Sender: TObject);
    procedure MenuVisitHomepageClick(Sender: TObject);
    procedure SendEMailClick(Sender: TObject);
    procedure MenuShowSnapshotsClick(Sender: TObject);
    procedure MenuViewDocumentationClick(Sender: TObject);
    procedure MenuAddPlayStationCDGamesClick(Sender: TObject);
    procedure MenuAboutClick(Sender: TObject);
    procedure MenuSmoothPictureOffClick(Sender: TObject);
    procedure StatusBarPanels1Click(Sender: TObject);
    procedure GamesTreeChange(Sender: TBaseVirtualTree;
      Node: PVirtualNode);
    procedure GamesTreeCompareNodes(Sender: TBaseVirtualTree; Node1,
      Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
    procedure GamesTreeFreeNode(Sender: TBaseVirtualTree;
      Node: PVirtualNode);
    procedure GamesTreeGetHint(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex;
      var LineBreakStyle: TVTTooltipLineBreakStyle;
      var HintText: WideString);
    procedure GamesTreeGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure GamesTreeGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure GamesTreeHeaderClick(Sender: TVTHeader; Column: TColumnIndex;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure GamesTreeIncrementalSearch(Sender: TBaseVirtualTree;
      Node: PVirtualNode; const SearchText: WideString;
      var Result: Integer);
    procedure MenuEmulatorSetupClick(Sender: TObject);
    procedure GamesTreeDblClick(Sender: TObject);
    procedure GamesTreeKeyPress(Sender: TObject; var Key: Char);
    procedure GameFilterAllConsolesClick(Sender: TObject);
    procedure MenuViewLargeGameIconsClick(Sender: TObject);
  private
    { Private declarations }
    ColumnsSort: packed array[0..4] of ShortInt;
    ProcessingConsole: Boolean;
    ColumnSorted: ShortInt;
    FileDebugger: THashedStringList;

    procedure ExecuteGame; // new

    procedure ClearGamesList;
    procedure InitializeColumnsSort;
    procedure InitializeEmulatorVariables;
    procedure ClearMemoryColumnsVariables;
    function  FindGame(GameDescription: String; SystemID: ShortInt): PVirtualNode;
    function  FindGameName(GameFileName: String; SystemID: ShortInt): PVirtualNode;
    function  PopulateROMLine: String;
    procedure UpdateStatusBar;

    procedure LatestPlayedGamesClick(Sender: TObject);
    procedure GetSystemIcon;
    procedure CheckLatestPlayedGames(PopupHolder: TMenuItem; GameDescription: String; SystemID: ShortInt);
    procedure RandomGame_Click(Sender: TObject);

  public
    AppPath: String;
    CompleteGamesList: THashedStringList;
    SnapshotFolder: packed array[1..MaxSystems] of String;

    // Games variables & procedures
    ListGames, ListGameName, ListGameSize: THashedStringList;
    SelectedNode: PVirtualNode;
    SelectedGameName: String;
    SelectedGameID: ShortInt;

    ColumnsProfile: String;

    EmulatorFile,
    EmulatorVersion: packed array[1..MaxSystems] of packed array[1..2] of String;
    EmulatorIconIndex: packed array[1..MaxSystems] of packed array[1..2] of Integer;

    emuROMsFolders: packed array[1..MaxSystems] of THashedStringList;
    
    mImageIndex: Integer;
    mConsoleType: ShortInt;
    mDescription: String;
    mGameSize: String;
    mGameFileName: String;
    mMediaDescription: String;

    SystemDescription, SystemFileName: String;

    procedure ExtractBinaryIcon(SystemID, EmulatorNumber: ShortInt);
    procedure LoadButtonIcon(ButtonHolder: TSpeedButton; IconType: ShortInt; ClearCaption: Boolean);

    procedure DialogOpenFile(FilterType: ShortInt; const DialogTitle: String; EditHolder: TEdit; MultipleFiles, GraphicImage: Boolean);
    procedure DialogSelectFolder(EditHolder: TEdit; MultipleFolders: Boolean);
    procedure SortColumn(ColumnID: ShortInt; ForceAscending: Boolean);

    function  CheckWinXPThemes: Boolean;
    function  CheckReadOnly(const FileString: String): Boolean;
    function  CheckSelected: Boolean;
    function  CheckTotal: Boolean;
    function  CheckTotalVisible: Boolean;

    procedure ProcessPressedKey(Key: Word);
    procedure SelectNode(ItemIndex: Integer);

    procedure GetSystemFileDescription(SystemID: ShortInt);
    procedure GetGames(SystemID: ShortInt; FoldersHolder, GamesHolder: THashedStringList);
    function  VerifyFiles(SystemID: ShortInt; ListFileName: String): Boolean;
    procedure ShowZipFileErrors;
    procedure AddBadZip(ZipFileName: String);
    procedure CreateGamesList(SystemID: ShortInt);
    function  LoadGames(SelectItem: Boolean): Boolean;
    procedure ApplyFilters(SystemTypeIndex: SmallInt; SelectItem: Boolean);
    function  ShowPicture: Boolean;
    function  RunProcess(AppPath: String; Visibility: Word=SW_SHOWNORMAL): DWORD;
    procedure GetGameFields(ROMLine: String);

    procedure AddPopupItem(PopupHolder: TMenuItem; Description: String; Duplicate: Boolean; SystemID: ShortInt);
    procedure AddPopupItem2(PopupHolder: TPopupMenu; Description: String; Duplicate: Boolean; SystemID: ShortInt);
    function  SelectedLatestPlayedGame(GameDescription: String; SystemID: ShortInt): Integer;

    function  ReadIniFile: Boolean;
    function  UpdateIniFile: Boolean;

    // ZIP functions
    function  GetContents(ZipFileName: String; DeleteList: Boolean): Boolean;
    function  UnZipFile(ZipFileName, ROMFileName: String): Boolean;

    function  SearchZIP(GameName: String; EmulatorFolders: TListBox): Boolean;
    function  SearchZIPFolder(GameName: String; EmulatorFolders: TListBox): String;
    function  GetWinTempDir: String;
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

uses uCommon, uFilesUtil, uStatus, uPreferences, uPlayStationCDGameEditor,
  uDebugger, uAbout, uEmulatorSetup;

{$R *.dfm}

procedure TFormMain.ExtractBinaryIcon(SystemID, EmulatorNumber: ShortInt);
var
  IcoHandle: THandle;
  MyIcon: TIcon;

  procedure GetCustomIcon(IconName: String);
  begin
    case FileExists(AppPath+'icons\'+IconName) of
      True : MyIcon.LoadFromFile(AppPath+'icons\'+IconName);
      False: ImageListEmulatorsIcons.GetIcon(0, MyIcon) // no icon in this file...
    end;
  end;
begin
  MyIcon:= TIcon.Create;
  MyIcon.Width:= 32;
  MyIcon.Height:= 32;

  // the last parameter is the index of the icon
  IcoHandle:= ExtractIcon(Application.Handle, PChar(EmulatorFile[SystemID, EmulatorNumber]), Word(0));
  if IcoHandle = 0 then
     begin
       case EmulatorIconIndex[SystemID, EmulatorNumber] of
         9: GetCustomIcon('z26.ico'); // z26 - An Atari 2600 Emulator
       else
          ImageListEmulatorsIcons.GetIcon(0, MyIcon); // no icon in this file...
       end;
     end
  else
     MyIcon.Handle:= IcoHandle;

  if EmulatorIconIndex[SystemID, EmulatorNumber] <= ImageListEmulatorsIcons.Count then
     ImageListEmulatorsIcons.ReplaceIcon(EmulatorIconIndex[SystemID, EmulatorNumber], MyIcon)
  else
     GenerateMessage('Error', Format('Error when trying to replace/add icon '+
        'number %s! The frontend might give errors.', [EmulatorIconIndex[SystemID, EmulatorNumber]]), 2);
  FreeAndNil(MyIcon);
end;

procedure TFormMain.LoadButtonIcon(ButtonHolder: TSpeedButton; IconType: ShortInt; ClearCaption: Boolean);
var
  iconFileName, tempFolder: String;
  Icon32: TIcon;
begin
  tempFolder:= AppPath+'buttons\';
  case IconType of
     0: iconFileName:= tempFolder+'folder.ico'; // folder icon
     1: iconFileName:= tempFolder+'zip.ico'; // zip icon
     2: iconFileName:= tempFolder+'file.ico'; // file icon
     3: iconFileName:= tempFolder+'update.ico'; // update icon
     4: iconFileName:= tempFolder+'default.ico'; // default icon
     5: iconFileName:= tempFolder+'font.ico'; // font icon
     6: iconFileName:= tempFolder+'yes.ico'; // ok icon
     7: iconFileName:= tempFolder+'no.ico'; // no icon
     8: iconFileName:= tempFolder+'clear.ico'; // clear icon
     9: iconFileName:= tempFolder+'hide.ico'; // hide icon
    10: iconFileName:= tempFolder+'arrow_up.ico'; // up arrow
    11: iconFileName:= tempFolder+'arrow_down.ico'; // down arrow
    12: iconFileName:= tempFolder+'load.ico'; // load arrow
    13: iconFileName:= tempFolder+'save.ico'; // save arrow
    14: iconFileName:= tempFolder+'setting.ico'; // setting icon
    15: iconFileName:= tempFolder+'password.ico'; // password icon
    16: iconFileName:= tempFolder+'delete.ico'; // delete icon
    17: iconFileName:= tempFolder+'add.ico'; // add icon
    18: iconFileName:= tempFolder+'search.ico'; // search icon
  end;

  if ClearCaption then
     ButtonHolder.Caption:= '';
  case FileExists(iconFileName) of
    True:
      begin
        try
          Icon32:= TIcon.Create;
          Icon32.LoadFromFile(iconFileName);
          Icon32.Transparent:= True;
          ButtonHolder.Glyph.Width:= 14;
          ButtonHolder.Glyph.Height:= 14;
          ButtonHolder.Glyph.Canvas.Draw(0, 0, Icon32);
        finally
          FreeAndNil(Icon32);
        end;
      end;
    False: GenerateMessage('Error', 'File "'+iconFileName+'" not found! Make sure the file '+
                           'exists and restart the frontend', 2);
  end;
end;

procedure TFormMain.ClearMemoryColumnsVariables;
begin
  mImageIndex:= -1;
  mConsoleType:= -1;
  mDescription:= '';
  mGameSize:= '';
  mGameFileName:= '';
  mMediaDescription:= '';
end;

function TFormMain.PopulateROMLine: String;
begin
  Result:= IntToStr(mConsoleType)+'¬'+mDescription+'¬'+mGameSize+'¬'+mGameFileName+'¬'+mMediaDescription+';'
end;

procedure TFormMain.ClearGamesList;
begin
  GamesTree.BeginUpdate;
  GamesTree.Clear;
  GamesTree.NodeDataSize:= -1;
  GamesTree.EndUpdate;
end;

procedure TFormMain.InitializeColumnsSort;
var
  Loop: ShortInt;
begin
  // set 0 for Ascending
  // set 1 for Descending
  for Loop:=0 to Length(ColumnsSort) do
    ColumnsSort[Loop]:= 0;
end;

procedure TFormMain.InitializeEmulatorVariables;
var
  Loop, Loop2, EmuCounter: Integer;
begin
  // define values for all arrays or exception error will occur....
  try
    for Loop:= 1 to MaxSystems-1 do
    begin
      for Loop2:= 1 to Length(EmulatorFile[Loop]) do
      begin
        EmulatorFile[Loop, Loop2]:= '';
        EmulatorVersion[Loop, Loop2]:= '';
      end;
      emuROMsFolders[Loop]:= nil;
    end;

    // define icon index for each emulator (2 emulators each system!)
    // max index = 52 
    EmuCounter:= 1;
    for Loop:= 1 to MaxSystems-1 do
    begin
      for Loop2:= 1 to Length(EmulatorIconIndex[Loop]) do
      begin
        EmulatorIconIndex[Loop, Loop2]:= EmuCounter;
        Inc(EmuCounter);
      end;
    end;
  except
    GenerateMessage('Error', 'Could not define initial values to emulator variables', 2);
  end;
end;

procedure TFormMain.DialogOpenFile(FilterType: ShortInt; const DialogTitle: String; EditHolder: TEdit; MultipleFiles, GraphicImage: Boolean);
var
  Continue: Boolean;
  SelectedFile: String;
begin
  case GraphicImage of
    True:
      begin
        OpenImageDialog.FilterIndex:= FilterType;
        OpenImageDialog.Title:= DialogTitle;
      end;
    False:
      begin
        case FilterType of
           0: OpenFileDialog.Filter:= 'Executable files (*.exe)|*.exe'; // .exe
           1: OpenFileDialog.Filter:= 'Executable files (*.exe)|*.exe|Batch files (*.bat)|*.bat|All files (*.*)|*.*'; // .exe ; .bat ; *.*
           2: OpenFileDialog.Filter:= 'Data files (*.dat)|*.dat'; // .dat  files
           3: OpenFileDialog.Filter:= 'JPEG image file (*.jpg; *.jpeg)|*.jpg; *.jpeg'; // .jpeg image files
           4: OpenFileDialog.Filter:= 'PNG image file (*.png)|*.png'; // .png image files
           5: OpenFileDialog.Filter:= 'Initialization files (*.ini)|*.ini'; // .ini
           6: OpenFileDialog.Filter:= 'Zip files (*.zip)|*.zip'; // .zip
           7: OpenFileDialog.Filter:= 'All image formats (*.png;*.gif;*.jpg;*.bmp)|*.png;*.gif;*.jpg;*.bmp';
           8: OpenFileDialog.Filter:= 'Config files (*.cfg)|*.cfg'; // .cfg
           9: OpenFileDialog.FilteR:= 'All files (*.*)|*.*'; // all files
        end;
        OpenFileDialog.Title:= DialogTitle;
      end;
  end;

  case GraphicImage of
    True : Continue:= OpenImageDialog.Execute;
    False: Continue:= OpenFileDialog.Execute;
  end;

  if Continue then
     begin
       case GraphicImage of
         True : SelectedFile:= OpenImageDialog.FileName;
         False: SelectedFile:= OpenFileDialog.FileName;
       end;

       if EditHolder <> nil then
          begin
            case MultipleFiles of
              True:
                begin
                  if EditHolder.Text <> '' then
                     EditHolder.Text:= EditHolder.Text+';'+SelectedFile
                  else
                     EditHolder.Text:= SelectedFile;
                end;
              False:
                begin
                  EditHolder.Text:= SelectedFile;
                end;
            end;
          end;
     end;
  SetCurrentDir(AppPath);
end;

procedure TFormMain.DialogSelectFolder(EditHolder: TEdit; MultipleFolders: Boolean);
var
  DialogText, Value: String;
begin
  if EditHolder.Name = 'FolderROMs' then
     DialogText:= 'Select a folder for ROMs'
  else
  if EditHolder.Name = 'FolderSamples' then
     DialogText:= 'Select a folder for Samples'
  else
  if EditHolder.Name = 'FolderINIFiles' then
     DialogText:= 'Select a folder for games .ini files'
  else
  if EditHolder.Name = 'FolderGamesConfiguration' then
     DialogText:= 'Select a folder for Games Configuration'
  else
  if EditHolder.Name = 'FolderSaveStates' then
     DialogText:= 'Select a folder for Save States'
  else
  if EditHolder.Name = 'FolderHighScores' then
     DialogText:= 'Select a folder for High Scores'
  else
  if EditHolder.Name = 'FolderNVRAM' then
     DialogText:= 'Select a folder for NVRAM'
  else
  if EditHolder.Name = 'FolderMemoryCards' then
     DialogText:= 'Select a folder for Memory Cards'
  else
  if EditHolder.Name = 'FolderInputsRecording' then
     DialogText:= 'Select a folder for Recording Games'
  else
  if EditHolder.Name = 'FolderArtworks' then
     DialogText:= 'Select a folder for Artworks'
  else
  if EditHolder.Name = 'FolderDiff' then
     DialogText:= 'Select a folder for HD Diff files'
  else
  if EditHolder.Name = 'FolderSnapshots' then
     DialogText:= 'Select a folder for Snapshots'
  else
  if EditHolder.Name = 'FolderCheats' then
     DialogText:= 'Select a folder for Cheats'
  else
  if EditHolder.Name = 'FolderKeysMapping' then
     DialogText:= 'Select a folder for controllers keys .ini files'
  else
  if EditHolder.Name = 'FolderIPS' then
     DialogText:= 'Select a folder for IPS patch files'
  else
  if EditHolder.Name = 'ZipTitleSnapshots' then
     DialogText:= 'Select a Folder for Title Snapshots'
  else
  if EditHolder.Name = 'ZipGameSnapshots' then
     DialogText:= 'Select a Folder for Game Snapshots'
  else
  if EditHolder.Name = 'ZipMarquees' then
     DialogText:= 'Select a Folder for Marquees'
  else
  if EditHolder.Name = 'ZipFlyers' then
     DialogText:= 'Select a Folder for Flyers'
  else
  if EditHolder.Name = 'ZipCabinets' then
     DialogText:= 'Select a Folder for Cabinets'
  else
  if EditHolder.Name = 'ZipControlPanels' then
     DialogText:= 'Select a Folder for Control Panels'
  else
  if EditHolder.Name = 'ZipControlPanelLayouts' then
     DialogText:= 'Select a Folder for Control Panel Layouts'
  else
  if EditHolder.Name = 'ZipIcons' then
     DialogText:= 'Select a Folder for Games Icons'
  else
  if EditHolder.Name = 'FolderGamesFAQ' then
     DialogText:= 'Select a Folder for Games F.A.Q.'
  else
  if EditHolder.Name = 'DefaultGameIconsFolder' then
     DialogText:= 'Select a Folder for Default Icons'
  else
  if EditHolder.Name = 'FrontendIconsFolder' then
     DialogText:= 'Select a Folder for Frontend Icons Folder'
  {else
  if EditHolder.Name = 'SoundClipFolder' then
     DialogText:= 'Select a Folder for '+
                  GetEmuFullDescription(FormPreferences.SoundClipsSystemSelector.Tag, 0, False)+
                  ' Sound Clips'};

  if SelectDirectory(DialogText, '', Value) then
     begin
       Value:= ExcludeTrailingPathDelimiter(Value);
       case MultipleFolders of
         True:
           begin
             if EditHolder.Text <> '' then
                EditHolder.Text:= EditHolder.Text+';' + Value
             else
                EditHolder.Text:= Value;
           end;
         False: EditHolder.Text:= Value;
       end;
     end;
end;

function CompareColumnsValue(var GameItem1, GameItem2: TGameInfo; var ColumnID: Integer): Integer;
begin
  case ColumnID of
    0: Result:= CompareText(GameItem1.eDescription, GameItem2.eDescription);
    1: Result:= CompareText(GameItem1.eGameSize, GameItem2.eGameSize);
    2: Result:= CompareText(GameItem1.eGameFileName, GameItem2.eGameFileName);
    3: Result:= CompareText(GameItem1.eMediaDescription, GameItem2.eMediaDescription);
  end;
end;

procedure TFormMain.SortColumn(ColumnID: ShortInt; ForceAscending: Boolean);
begin
  with GamesTree.Header, GamesTree do
  begin
    if SortColumn > NoColumn then
      Columns[SortColumn].Options:= Columns[SortColumn].Options + [coParentColor];
    if (SortColumn = NoColumn) or (SortColumn <> ColumnID) then
       SortColumn:= ColumnID;
    case ForceAscending of
      True: SortDirection:= sdAscending;
      False:
        begin
          case ColumnsSort[ColumnID] of
            0: SortDirection:= sdAscending;
            1: SortDirection:= sdDescending;
          end;
        end;
    end;
    Columns[SortColumn].Color:= $F7F7F7;
    //if not FormPreferences.GamesBackgroundImageEnable.Checked then
    //   Columns[SortColumn].Color:= FormPreferences.SortedColumnColor.Selected; // $F7F7F7;
    ColumnSorted:= SortColumn;
    ColumnsSort[ColumnSorted]:= Ord(SortDirection = sdDescending);
    SortTree(SortColumn, SortDirection, False);
    if SelectedNode <> nil then
       ScrollIntoView(SelectedNode, True);
  end;
end;

function TFormMain.CheckWinXPThemes: Boolean;
begin
  Result:= False;
  with ThemeServices do
  begin
    case ThemesAvailable of
      True : Result:= ThemesEnabled;
      False: Result:= False;
    end;
  end;
  if Result then
     Result:= FileExists(AppPath+ShortToLongFileName(Application.ExeName)+'.manifest');
end;

function TFormMain.CheckReadOnly(const FileString: String): Boolean;
var
  FileAttributes: Word;
begin
  Result:= FileExists(FileString);
  if Result then
     begin
       FileAttributes:= FileGetAttr(FileString);
       Result:= (FileAttributes and faReadOnly) = faReadOnly;
       //Result:= (FileAttributes and faArchive) = faArchive;
       //Result:= (FileAttributes and faSysFile) = faSysFile;
       //Result:= (FileAttributes and faHidden) = faHidden;
     end;
  if Result then
     begin
       if FormPreferences.ReadOnlyMessage.Checked then
          GenerateMessage('Warning', 'Could not save "'+FileString+'". '+
                          'File is marked as read-only!', 2);
     end;
end;

function TFormMain.CheckSelected: Boolean;
begin
  Result:= (GamesTree.VisibleCount > 0) and
           (GamesTree.SelectedCount > 0);
end;

function TFormMain.CheckTotal: Boolean;
begin
  Result:= GamesTree.TotalCount > 0;
end;

function TFormMain.CheckTotalVisible: Boolean;
begin
  Result:= GamesTree.VisibleCount > 0;
end;

// ZIP functions ------------------------------------------
function TFormMain.GetContents(ZipFileName: String; DeleteList: Boolean): Boolean;
var
  ArchiveItem: TZFArchiveItem;
begin
  Result:= True;
  if DeleteList then
     begin
       if Assigned(ListGameName) then
          ListGameName.Clear;
       if Assigned(ListGameSize) then
          ListGameSize.Clear;
     end;

  with ZipForge do
  begin
    FileName:= ZipFileName;

    // Open existing archive file
    try
      OpenArchive(fmOpenRead);

      Result:= FileCount > 0;
      if Result then
         begin
           // Search text files stored inside the archive
           if (FindFirst('*.*', ArchiveItem, faAnyFile-faDirectory)) then
              begin
                repeat
                  if Assigned(ListGameName) then
                     ListGameName.Add(AnsiLowerCase(ArchiveItem.FileName));
                  if Assigned(ListGameSize) then
                     ListGameSize.Add(IntToStr(ArchiveItem.UncompressedSize));
                until (not FindNext(ArchiveItem));
              end
           else
              Result:= False;
         end;
      CloseArchive;
      ZipForge.FileName:= '';
    except
      Result:= False;
      CloseArchive;
      ZipForge.FileName:= '';
    end;
  end;
  if (not Result) and (FormPreferences.DebuggerWindow.Checked) then
     AddBadZip(ZipFileName);
end;

function TFormMain.UnZipFile(ZipFileName, ROMFileName: String): Boolean;
begin
  Result:= False;
  with ZipForge do
  begin
    FileName:= ZipFileName;
    try
      OpenArchive(fmOpenRead);

      Result:= FileCount > 0;
      if Result then
         begin
           BaseDir:= GetWinTempDir;
           ExtractFiles(ROMFileName);
         end;
      CloseArchive;
      FileName:= '';
    except
      Result:= False;
      CloseArchive;
      FileName:= '';
    end;
  end;
  if (not Result) and (FormPreferences.DebuggerWindow.Checked) then
     GenerateMessage('Error', Format('Could not open file "%s"! File can be damaged or incomplete. Please verify.', [ZipFileName]), 2);
end;

function TFormMain.SearchZIP(GameName: String; EmulatorFolders: TListBox): Boolean;
var
  Loop: Integer;
begin
  Result:= False;
  for Loop:=0 to EmulatorFolders.Count -1 do
  begin
    if FileExists(EmulatorFolders.Items[Loop]+'\'+GameName+'.zip') then
       begin
         Result:= True;
         Break;
       end;
  end;
end;

function TFormMain.GetWinTempDir: String;
var
  Buffer: array[0..1023] of Char;
begin
  SetString(Result, Buffer, GetTempPath(SizeOf(Buffer), Buffer));
  Delete(Result, Length(Result), 1);
end;

procedure TFormMain.GetGameFields(ROMLine: String);
var
  TempField: String;
  Loop, FieldNumber: Integer;
begin
  TempField:= '';
  FieldNumber:= 1;

  for Loop:=1 to Length(ROMLine) do
  begin
    if (ROMLine[Loop] = '¬') or (ROMLine[Loop] = ';') then
       begin
         case FieldNumber of
           1: mConsoleType:= StrToInt(TempField);
           2: mDescription:= TempField;
           3: mGameSize:= TempField;
           4: mGameFileName:= TempField;
           5: mMediaDescription:= TempField;
         end;
         TempField:= '';
         Inc(FieldNumber);
       end
    else
       TempField:= TempField+ROMLine[Loop];
  end;
end;

function TFormMain.SearchZIPFolder(GameName: String; EmulatorFolders: TListBox): String;
var
  Loop: Integer;
begin
  Result:= 'Not Found';
  for Loop:=0 to EmulatorFolders.Count -1 do
  begin
    if FileExists(EmulatorFolders.Items[Loop]+'\'+GameName+'.zip') then
       begin
         Result:= EmulatorFolders.Items[Loop]+'\'+GameName+'.zip';
         Break;
       end;
  end;
end;

function TFormMain.RunProcess(AppPath: String; Visibility: Word=SW_SHOWNORMAL): DWORD;
var
  SI: TStartupInfo;
  PI: TProcessInformation;
  Proc: THandle;
  zFileName: array[0..255] of Char;
begin
  FillChar(SI, SizeOf(SI), 0);
  SI.cb:= SizeOf(SI);
  SI.dwFlags:= StartF_UseShowWindow;
  SI.wShowWindow:= Visibility;
  StrPCopy(zFileName, AppPath);

  if Length(AppPath) > 254 then
     begin
       GenerateMessage('Error', Format('The command line has more than 254 characters - [%u]',
                                [Length(zFileName)]), 2);
       Exit;
     end;

  Application.Minimize;

  if not CreateProcess(nil, zFileName,
     nil, nil, False, Create_New_Console+Normal_Priority_Class, nil, nil, SI, PI) then
     GenerateMessage('Execution Failed', Format('Failed to excecute program "%s". Error Code %d',
                                        [AppPath, GetLastError]), 2);

  Proc:= PI.hProcess;
  CloseHandle(PI.hThread);

  if WaitForSingleObject(Proc, 10000) <> Wait_Failed then
     begin
       GetExitCodeProcess(Proc, Result);
       while Result = STILL_ACTIVE do
       begin
         Application.ProcessMessages;
         if WaitForSingleObject(Proc, 10000) <> Wait_Failed then
            GetExitCodeProcess(Proc, Result);
       end;
     end;

  CloseHandle(Proc);
  SetCurrentDir(AppPath);
  Application.Restore;
end;

procedure TFormMain.ProcessPressedKey(Key: Word);
begin
  case Key of
    VK_RETURN: GamesTree.OnDblClick(Self);
  end;
end;

procedure TFormMain.SelectNode(ItemIndex: Integer);
var
  Node: PVirtualNode;
begin
  if GamesTree.TotalCount = 0 then
     Exit;
  Node:= GamesTree.GetFirst;
  repeat
    if Node.Index <> ItemIndex then
       Node:= GamesTree.GetNext(Node);
  until (not Assigned(Node)) or (Node.Index = ItemIndex);
  if Node <> nil then
     begin
       GamesTree.Selected[Node]:= True;
       GamesTree.FocusedNode:= Node;
       GamesTree.ScrollIntoView(Node, True);
     end;
end;

function TFormMain.FindGame(GameDescription: String; SystemID: ShortInt): PVirtualNode;
var
  Data: pGameInfo;
  tempNode: PVirtualNode;
begin
  Result:= nil;
  tempNode:= GamesTree.GetFirstVisible;
  if (GameDescription = '') or (not CheckTotal) or (tempNode = nil) then
     Exit;
  while (Result = nil) and (Assigned(tempNode)) do
  begin
    if Assigned(tempNode) then
       begin
         Data:= GamesTree.GetNodeData(tempNode);
         if (Data.eDescription = GameDescription) then
            begin
              if (SystemID = -1) or (Data.eConsoleType = SystemID) then
                 Result:= tempNode;
            end;
         tempNode:= GamesTree.GetNextVisible(tempNode);
       end;
  end;
  if Result = nil then
     Result:= GamesTree.GetFirstVisible;
end;

function TFormMain.FindGameName(GameFileName: String; SystemID: ShortInt): PVirtualNode;
var
  Data: pGameInfo;
  tempNode: PVirtualNode;
begin
  Result:= nil;
  tempNode:= GamesTree.GetFirstVisible;
  if (GameFileName = '') or (not CheckTotal) or (tempNode = nil) then
     Exit;
  while (Result = nil) and (Assigned(tempNode)) do
  begin
    if Assigned(tempNode) then
       begin
         Data:= GamesTree.GetNodeData(tempNode);
         if (Data.eGameFileName = GameFileName) then
            begin
              if (SystemID = -1) or (Data.eConsoleType = SystemID) then
                 Result:= tempNode;
            end;
         tempNode:= GamesTree.GetNextVisible(tempNode);
       end;
  end;
  if Result = nil then
     Result:= GamesTree.GetFirstVisible;
end;

procedure TFormMain.UpdateStatusBar;
begin
  StatusBar.Panels[0].Text:= Format('%u Games', [GamesTree.VisibleCount]);
end;

procedure TFormMain.GetSystemFileDescription(SystemID: ShortInt);
begin
  case SystemID of
    1:
      begin
        SystemDescription:= 'Super Nintendo';
        SystemFileName:= AppPath+'games\supernintendo.dat';
      end;
    2:
      begin
        SystemDescription:= 'Gameboy Advance';
        SystemFileName:= AppPath+'games\gameboyadvance.dat';
      end;
    3:
      begin
        SystemDescription:= 'Nintendo 8-bit';
        SystemFileName:= AppPath+'games\nintendo.dat';
      end;
    4:
      begin
        SystemDescription:= 'Genesis';
        SystemFileName:= AppPath+'games\genesis.dat';
      end;
    5:
      begin
        SystemDescription:= 'Atari 2600';
        SystemFileName:= AppPath+'games\atari2600.dat';
      end;
    6:
      begin
        SystemDescription:= 'Intellivision';
        SystemFileName:= AppPath+'games\intellivision.dat';
      end;
    7:
      begin
        SystemDescription:= 'Master System';
        SystemFileName:= AppPath+'games\mastersystem.dat';
      end;
    8:
      begin
        SystemDescription:= 'Odyssey2';
        SystemFileName:= AppPath+'games\odyssey2.dat';
      end;
    9:
      begin
        SystemDescription:= 'Game Gear';
        SystemFileName:= AppPath+'games\gamegear.dat';
      end;
    10:
      begin
        SystemDescription:= 'ColecoVision';
        SystemFileName:= AppPath+'games\colecovision.dat';
      end;
    11:
      begin
        SystemDescription:= 'GP32';
        SystemFileName:= AppPath+'games\gp32.dat';
      end;
    12:
      begin
        SystemDescription:= 'PC Engine';
        SystemFileName:= AppPath+'games\pcengine.dat';
      end;
    13:
      begin
        SystemDescription:= 'Atari Lynx';
        SystemFileName:= AppPath+'games\lynx.dat';
      end;
    14:
      begin
        SystemDescription:= 'Atari 5200 SuperSystem';
        SystemFileName:= AppPath+'games\atari5200.dat';
      end;
    15:
      begin
        SystemDescription:= 'Atari Jaguar';
        SystemFileName:= AppPath+'games\jaguar.dat';
      end;
    16:
      begin
       SystemDescription:= 'Sega 32X';
       SystemFileName:= AppPath+'games\sega32x.dat';
      end;
    17:
      begin
       SystemDescription:= 'Neo Geo Pocket';
       SystemFileName:= AppPath+'games\neogeopocket.dat';
      end;
    18:
      begin
       SystemDescription:= 'Nintendo 64';
       SystemFileName:= AppPath+'games\nintendo64.dat';
      end;
    19:
      begin
       SystemDescription:= 'Sega CD';
       SystemFileName:= AppPath+'games\segacd.dat';
      end;
    20:
      begin
       SystemDescription:= 'Gameboy / Gameboy Color / Super Gameboy';
       SystemFileName:= AppPath+'games\gameboy.dat';
      end;
    21:
      begin
       SystemDescription:= 'PlayStation';
       SystemFileName:= AppPath+'games\playstation.dat';
      end;
    22:
      begin
        SystemDescription:= 'Nintendo GameCube';
        SystemFileName:= AppPath+'games\gamecube.dat';
      end;
    23:
      begin
        SystemDescription:= 'Atari 7800';
        SystemFileName:= AppPath+'games\atari7800.dat';
      end;
    24:
      begin
        SystemDescription:= 'Dreamcast';
        SystemFileName:= AppPath+'games\dreamcast.dat';
      end;
    25:
      begin
        SystemDescription:= 'Watara Supervision';
        SystemFileName:= AppPath+'games\supervision.dat';
      end;
    26:
      begin
        SystemDescription:= 'Wonderswan';
        SystemFileName:= AppPath+'games\wonderswan.dat';
      end;
  end;
end;

procedure TFormMain.GetGames(SystemID: ShortInt; FoldersHolder, GamesHolder: THashedStringList);
var
  Counter: Integer;
begin
  // Get Files List
  if (not Assigned(FoldersHolder)) or (not Assigned(GamesHolder)) then
     Exit;
  if FoldersHolder.Count > 0 then
     begin
       for Counter:=0 to FoldersHolder.Count - 1 do
       begin
         case SystemID of
           1: // Super Nintendo
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.smc', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.sfc', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.swc', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.fig', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.mgd', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.ufo', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.eur', GamesHolder, False);
             end;
           2: // Gameboy Advance
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gba', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.agb', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.elf', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.mb', GamesHolder, False);
             end;
           3: // Nintendo 8-bit
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.rar', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.tar', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.cab', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.lha', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.lzh', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.jar', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.tgz', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.tbz', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gz', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bz2', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gca', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gza', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bza', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.nes', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.fds', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.fam', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.nsf', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.unf', GamesHolder, False);
             end;
           4: // Sega Genesis
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.smd', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gen', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.zsg', GamesHolder, False);
             end;
           5: // Atari 2600
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.a26', GamesHolder, False);
             end;
           6: // Intellivision
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.int', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.itv', GamesHolder, False);
             end;
           7: // Sega Master System
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.sms', GamesHolder, False);
             end;
           8: // Odyssey2
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.int', GamesHolder, False);
             end;
           9: // Sega Game Gear
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gg', GamesHolder, False);
             end;
           10: // ColecoVision
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.rom', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.col', GamesHolder, False);
             end;
           11: // GP32
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.gxb', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.fxe', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.elf', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.axf', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.smc', GamesHolder, False);
             end;
           12: // PC Engine
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.rom', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.iso', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.isq', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.hcd', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.pce', GamesHolder, False);
             end;
           13: // Atari Lynx
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.lnx', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.com', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.o', GamesHolder, False);
             end;
           14: // Atari 5200
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.a52', GamesHolder, False);
             end;
           15: // Atari Jaguar
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.jag', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.rom', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
             end;
           16: // Sega 32X
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.32x', GamesHolder, False);
             end;
           17: // Neo Geo Pocket
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.ngp', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.ngc', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.npc', GamesHolder, False);
             end;
           18: // Nintendo 64
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.mov', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.n64', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.rom', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.u64', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.v64', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.z64', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.usa', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.jap', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.eur', GamesHolder, False);
             end;
           19: // Sega CD
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.iso', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.raw', GamesHolder, False);
             end;
           20: // Gameboy / Gameboy Color / Super Gameboy
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gbc', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.gb', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.cgb', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.sgb', GamesHolder, False);
             end;
           21: // PlayStation
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.iso', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.img', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.nrg', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.exe', GamesHolder, False);
             end;
           22: // Nintendo GameCube
             GetGamesFilesList(FoldersHolder[Counter], '.gcm', GamesHolder, False);
           23: // Atari 7800
             begin
               //GetGamesFilesList(FoldersHolder[Counter], '.zip', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.a78', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.bin', GamesHolder, False);
             end;
           24: // Dreamcast
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.iso', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.cdi', GamesHolder, False);
             end;
           25: // Watara Supervision
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.sv', GamesHolder, False);
             end;
           26: // Wonderswan
             begin
               GetGamesFilesList(FoldersHolder[Counter], '.ws', GamesHolder, False);
               GetGamesFilesList(FoldersHolder[Counter], '.wsc', GamesHolder, False);
             end;
         end;
       end;
     end;
end;

function TFormMain.VerifyFiles(SystemID: ShortInt; ListFileName: String): Boolean;
begin
  Result:= False;
  case SystemID of
    1: // Super Nintendo
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.smc') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.sfc') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.swc') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.fig') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.mgd') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.ufo') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.eur');
     end;
    2: // Gameboy Advance
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.gba') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.agb') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.elf') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.mb');
     end;
    3: // Nintendo 8-bit
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.nes') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.fds') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.fam') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.nsf') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.unf');
     end;
    4: // Sega Genesis
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.smd') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.gen') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.zsg');
     end;
    5: // Atari 2600
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.a26');
     end;
    6: // Intellivision
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.int') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.itv');
     end;
    7: // Sega Master System
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.sms');
     end;
    8: // Odyssey2
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.int');
     end;
    9: // Sega Game Gear
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.gg');
     end;
    10: // ColecoVision
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.rom') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.col');
     end;
    11: // GP32
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.gxb') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.fxe') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.elf') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.axf') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.smc');
     end;
    12: // PC Engine
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.rom') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.iso') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.isq') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.hcd') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.pce');
     end;
    13: // Atari Lynx
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.lnx') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.com') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.o');
     end;
    14: // Atari 5200
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.a52');
     end;
    15: // Atari Jaguar
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.jag') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.rom') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin');
     end;
    16: // Sega 32X
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.32x');
     end;
    17: // Neo Geo Pocket
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.ngp') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.ngc') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.npc');
     end;
    18: // Nintendo 64
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.mov') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.n64') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.rom') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.u64') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.v64') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.z64') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.usa') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.jap') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.eur');
     end;
    19: // Sega CD
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.iso') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.raw');
     end;
    20: // Gameboy / Gameboy Color / Super Gameboy
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.gbc') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.gb') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.cgb') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.sgb');
     end;
    21: // PlayStation
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.iso') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.img') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.ngr') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.exe');
     end;
    22: // Nintendo GameCube
       Result:= LowerCase(ExtractFileExt(ListFileName)) = '.gcm';
    23: // Atari 7800
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.a78') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.bin');
     end;
    24: // Dreamcast
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.iso') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.cdi');
     end;
    25: // Watara Supervision
     begin
       Result:= LowerCase(ExtractFileExt(ListFileName)) = '.sv';
     end;
    26: // Wonderswan
     begin
       Result:= (LowerCase(ExtractFileExt(ListFileName)) = '.ws') or
                (LowerCase(ExtractFileExt(ListFileName)) = '.wsc');
     end;
  end;
end;

procedure TFormMain.ShowZipFileErrors;
begin
  if (Assigned(FileDebugger)) and (FileDebugger.Count > 0) then
     begin
       if GenerateMessage('Found Zip File Errors', 'Not all .zip files were process correctly. Do '+
                          'you wish to view the files list ?', 1) = mrYes then
          begin
            if not Assigned(FormDebugger) then
               FormDebugger:= TFormDebugger.Create(Self);
            FormDebugger.ErrorsHolder.Lines.AddStrings(FileDebugger);
            FormDebugger.ErrorsHolder.Lines.Insert(0, 'Error handling .zip files:'+#13#10+#13#10);
            FormDebugger.ShowModal;
            FreeAndNil(FormDebugger);
          end;
     end;
  FreeAndNil(FileDebugger);
end;

procedure TFormMain.AddBadZip(ZipFileName: String);
var
  AddFile: Boolean;
begin
  if ZipFileName <> '' then
     begin
       case Assigned(FileDebugger) of
         True: AddFile:= (FileDebugger.IndexOf(ZipFileName) = -1);
         False:
           begin
             FileDebugger:= THashedStringList.Create;
             AddFile:= True;
           end;
       end;
       if AddFile then
          FileDebugger.Add(ZipFileName);
     end;
end;

procedure TFormMain.CreateGamesList(SystemID: ShortInt);
var
  Loop, CounterGame: Integer;
  AddGame: Boolean;
  FileExtension: String;
  FilesList, ExistingGames: THashedStringList;

  function GetActualGamesList: Boolean;
  var
    InternalLoop: Integer;
  begin
    Result:= CheckFile(SystemFileName);
    if Result then
       begin
         ExistingGames:= THashedStringList.Create;
         ExistingGames.LoadFromFile(SystemFileName);
         for InternalLoop:=0 to ExistingGames.Count-1 do
         begin
           GetGameFields(ExistingGames[InternalLoop]);
           ExistingGames[InternalLoop]:= LowerCase(mGameFileName);
         end;
       end;
  end;

begin
  GetSystemFileDescription(SystemID);
  if (not Assigned(emuROMsFolders[SystemID])) or
     (Assigned(emuROMsFolders[SystemID]) and (emuROMsFolders[SystemID].Count = 0)) then
     begin
       GenerateMessage('Warning', 'There are no folders defined for "'+SystemDescription+
                       '". Please select at least one folder in emulators setup screen. '+
                       'Aborting...', 2);
       Exit;
     end;

  if (MenuAlwaysCreateNewList.Checked) and CheckFile(SystemFileName) then
     begin
       if GenerateMessage('Warning', Format('The option "Always Create New List" is enabled. '+
                                 'The current list for the %s console will be totally lost! '+
                                 'To add new games into an existing list, make sure the option '+
                                 'is unchecked... Continue anyway ?', [SystemDescription]), 1) = mrNo then
          Exit;
     end;
  FormStatus.Show;
  FormStatus.LabelTitleMessage.Caption:= 'Create Games List';
  FormStatus.LabelMessage.Text:= Format('Searching %s games. Please, wait a moment...', [SystemDescription]);
  FormStatus.Refresh;

  case MenuAlwaysCreateNewList.Checked of
    True:
      begin
        if FileExists(SystemFileName) then
           DeleteFile(SystemFileName);
      end;
    False: GetActualGamesList;
  end;

  FilesList:= THashedStringList.Create;

  GetGames(SystemID, emuROMsFolders[SystemID], FilesList);
  if FilesList.Count > 0 then
     begin
       ListGames:= THashedStringList.Create;
       if (not MenuAlwaysCreateNewList.Checked) and FileExists(SystemFileName) then
          ListGames.LoadFromFile(SystemFileName);
       ListGames.BeginUpdate;
       FormStatus.ProgressBar.Max:= FilesList.Count-1;
       FormStatus.ProgressBar.Position:= 0;

       mMediaDescription:= '';
       mMediaDescription:= InputBoxEL('Media Description - ['+SystemDescription+']', 'ID', '');
       if mMediaDescription = '' then
          if GenerateMessage('Warning', 'Media description is empty. Would you like to '+
                             'leave it blank ?', 1) = mrNo then
             mMediaDescription:= InputBoxEL('Media Description - ['+SystemDescription+']', 'ID', '');

       Application.ProcessMessages;
       FormStatus.Refresh;
       ListGameName:= THashedStringList.Create;
       ListGameSize:= THashedStringList.Create;
       for Loop:=0 to FilesList.Count -1 do
       begin
         FormStatus.ProgressBar.Position:= Loop;
         FileExtension:= ExtractFileExt(FilesList[Loop]);
         //AddGame:= False;
         if LowerCase(FileExtension) = '.zip' then
            begin
              AddGame:= GetContents(FilesList[Loop], True);
              if AddGame then
                 begin
                   for CounterGame:=0 to ListGameName.Count -1 do
                   begin
                     AddGame:= VerifyFiles(SystemID, ListGameName[CounterGame]);
                     if AddGame then
                        begin
                          mConsoleType:= SystemID;
                          mDescription:= ExtractFileName(FilesList[Loop]);
                          mDescription:= ChangeFileExt(mDescription, '');
                          mGameSize:= ListGameSize[CounterGame];
                          mGameFileName:= ExtractFileName(FilesList[Loop]);
                          Break;
                        end;
                   end;
                 end;
            end
         else
            begin
              AddGame:= VerifyFiles(SystemID, FilesList[Loop]);
              if AddGame then
                 begin
                   mConsoleType:= SystemID;
                   mDescription:= ExtractFileName(FilesList[Loop]);
                   mDescription:= ChangeFileExt(mDescription, '');
                   mGameSize:= IntToStr(GetFileSize(FilesList[Loop]));
                   mGameFileName:= ExtractFileName(FilesList[Loop]);
                 end;
            end;

         if not MenuAlwaysCreateNewList.Checked then
            begin
              if (Assigned(ExistingGames)) and (ExistingGames.Count > 0) and (AddGame) then
                 AddGame:= (ExistingGames.IndexOf(LowerCase(mGameFileName)) = -1);
            end;

         if AddGame then
            ListGames.Add(PopulateROMLine);
       end;
       FreeAndNil(ExistingGames);
       FreeAndNil(ListGameName);
       FreeAndNil(ListGameSize);

       if ListGames.Count > 0 then
          ListGames.SaveToFile(SystemFileName);
       FreeAndNil(ListGames);
       FormStatus.Close;
       ShowZipFileErrors;
       LoadGames(True);
     end
  else
     begin
       FormStatus.Close;
       GenerateMessage('Games Not Found', 'No valid games were found or there are no folders to scan '+
                       'for files!', 2);
     end;
  FreeAndNil(FilesList);
end;

function TFormMain.LoadGames(SelectItem: Boolean): Boolean;
var
  Loop, AddGameIndex: Integer;
  CompleteGamesList, TempList: THashedStringList;
  Node: PVirtualNode;
  Data: pGameInfo;

  function AddGames(SystemID: ShortInt): Boolean;
  begin
    GetSystemFileDescription(SystemID);
    Result:= CheckFile(SystemFileName);
    if Result then
       begin
         TempList.BeginUpdate;
         TempList.Clear;
         TempList.LoadFromFile(SystemFileName);
         TempList.EndUpdate;
         CompleteGamesList.AddStrings(TempList);
       end;
  end;

begin
  CompleteGamesList:= THashedStringList.Create;
  CompleteGamesList.BeginUpdate;
  TempList:= THashedStringList.Create;
  for Loop:= 1 to MaxSystems-1 do
      AddGames(Loop);
  FreeAndNil(TempList);
  CompleteGamesList.EndUpdate;
  AddGameIndex:= 0;
  Result:= (CompleteGamesList.Count > 0);
  ClearGamesList;
  if not Result then
     begin
       FreeAndNil(CompleteGamesList);
       Exit;
      end;

  if Result then
     begin
       GamesTree.NodeDataSize:= SizeOf(TGameInfo);
       GamesTree.RootNodeCount:= 0;
       GamesTree.BeginUpdate;
       for Loop:=0 to CompleteGamesList.Count-1 do
       begin
         ClearMemoryColumnsVariables;
         GetGameFields(CompleteGamesList[Loop]);
         if (mGameSize = '') or (mGameSize = '-1') then
            mGameSize:= '0';

         if mGameSize = '0' then
            mGameSize:= 'Unknown'
         else
            begin
              if LowerCase(mMediaDescription) <> 'cd' then
                 mGameSize:= GetGameSize(StrToInt(mGameSize))
              else
                 begin
                   mGameSize:= mGameSize+' CD';
                   if StrToInt(mGameSize[1]) > 1 then
                      mGameSize:= mGameSize+'s';
                 end;
            end;
         Node:= GamesTree.AddChild(nil);
         Data:= GamesTree.GetNodeData(Node);
         GetSystemFileDescription(mConsoleType);
         //GroupedList.Expanded[ParentNode]:= ButtonGroupedToggleExpandedClosed.Checked;
         Data.eImageIndex:= mConsoleType;
         Data.eConsoleType:= mConsoleType;
         Data.eDescription:= mDescription;
         Data.eGameSize:= mGameSize;
         Data.eGameFileName:= mGameFileName;
         Data.eMediaDescription:= mMediaDescription;
         Data.eSystemDescription:= SystemDescription;
         Inc(AddGameIndex);
       end;
       GamesTree.EndUpdate;
       ApplyFilters(ButtonGamesFilters.Tag, SelectItem);
     end;
  FreeAndNil(CompleteGamesList);
end;

procedure TFormMain.ApplyFilters(SystemTypeIndex: SmallInt; SelectItem: Boolean);
var
  Loop: Integer;
  KeepGame: Boolean;
  NodeIndex: PVirtualNode;
  Data: pGameInfo;

  function ReselectItem: Boolean;
  begin
    //ShowingImage:= True;
    if GamesTree.SelectedCount > 1 then
       GamesTree.ClearSelection;
    if SelectItem then
       SelectedNode:= FindGame(SelectedGameName, SelectedGameID);
    GamesTree.Selected[SelectedNode]:= True;
    GamesTree.FocusedNode:= SelectedNode;
    SortColumn(ColumnSorted, False);
  end;

begin
  if FormStatus.Visible then
     begin
       FormStatus.LabelMessage.Text:= 'Applying filters. Please wait a moment...';
       FormStatus.Refresh;
     end;

  GamesTree.BeginUpdate;
  NodeIndex:= GamesTree.GetFirst;
  repeat
    Data:= GamesTree.GetNodeData(NodeIndex);

    mConsoleType:= Data.eConsoleType;
    mDescription:= Data.eDescription;
    mGameSize:= Data.eGameSize;
    mGameFileName:= Data.eGameFileName;
    mMediaDescription:= Data.eMediaDescription;
    if SystemTypeIndex = 0 then
       KeepGame:= True
    else
       KeepGame:= mConsoleType = SystemTypeIndex;
    GamesTree.IsVisible[NodeIndex]:= KeepGame;
    NodeIndex:= GamesTree.GetNext(NodeIndex);
  until NodeIndex = nil;
  GamesTree.EndUpdate;
  ReselectItem;
  UpdateStatusBar;
end;

function TFormMain.ShowPicture: Boolean;
var
  Folder: String;
  PictureName, PictureExt, NumberExtension: String;
  PictureNotFound, ImageChanged: Boolean;
  Data: pGameInfo;

const
  NoPictureFileName = 'images\nosnap.png';

  function ClearImage(ImageHolder: TImage32): Boolean;
  begin
    ImageHolder.Bitmap.BeginUpdate;
    ImageHolder.Bitmap.Clear;
    ImageHolder.Bitmap.EndUpdate;
  end;

  procedure LoadImage(ImageIndex: Integer; ImageHolder: TImage32);
  begin
    PictureNotFound:= False;
    ImageChanged:= False;
    NumberExtension:= '_0'+IntToStr(ImageIndex);
    case FileExists(Folder+PictureName+NumberExtension+PictureExt) of
      True : ImageChanged:= True;
      False: PictureNotFound:= True;
    end;

    if PictureNotFound then
       ImageChanged:= True;

    if ImageChanged then
       begin
         case PictureNotFound of
           True:
             begin
               ImageHolder.Bitmap.BeginUpdate;
               ImageHolder.Bitmap.LoadFromFile(AppPath+NoPictureFileName);
               ImageHolder.Bitmap.EndUpdate;
             end;
           False:
             begin
               case FileExists(Folder+PictureName+NumberExtension+PictureExt) of
                 True:
                   begin
                     ImageHolder.Bitmap.BeginUpdate;
                     ImageHolder.Bitmap.LoadFromFile(Folder+PictureName+NumberExtension+PictureExt);
                     ImageHolder.Bitmap.EndUpdate;
                   end;
                 False:
                   begin
                     if PictureName = 'nosnap' then
                        ClearImage(ImageHolder);
                   end;
               end;
             end;
         end;
         ImageHolder.Bitmap.Changed;
       end;
  end;

begin
  ProcessingConsole:= True;
  Result:= True;
  Data:= GamesTree.GetNodeData(SelectedNode);
  Folder:= IncludeTrailingPathDelimiter(SnapshotFolder[Data.eConsoleType]);
  if Pos(':', Folder) = 0 then
     Folder:= AppPath+Folder;
  PictureName:= Data.eDescription;
  PictureExt:= '.png';
  if not MenuHideFirstPicture.Checked then
     LoadImage(1, Picture1);
  LoadImage(2, Picture2);
  ProcessingConsole:= False;
end;

procedure TFormMain.LatestPlayedGamesClick(Sender: TObject);
begin
  SelectedLatestPlayedGame(TMenuItem(Sender).Caption, TMenuItem(Sender).Tag);
end;

procedure TFormMain.GetSystemIcon;
var
  SystemIcon: TIcon;
begin
  SystemIcon:= TIcon.Create;
  SystemIcon.Width:= 16;
  SystemIcon.Height:= 16;
  ImageListGameIconSmall.GetIcon(ButtonGamesFilters.Tag, SystemIcon);
  Images.ReplaceIcon(16, SystemIcon);
  FreeAndNil(SystemIcon);
end;

procedure TFormMain.CheckLatestPlayedGames(PopupHolder: TMenuItem; GameDescription: String; SystemID: ShortInt);
begin
  if PopupHolder.Find(GameDescription) = nil then
     begin
       if PopupHolder.Count = 24 then
          PopupHolder.Delete(0);
       AddPopupItem(PopupHolder, GameDescription, False, SystemID);
     end;
end;

procedure TFormMain.AddPopupItem(PopupHolder: TMenuItem; Description: String; Duplicate: Boolean; SystemID: ShortInt);
var
  NewMenuItem: TMenuItem;
  Loop: Integer;
begin
  if not Duplicate then
     begin
       if PopupHolder.Count > 0 then
          begin
            for Loop:=0 to PopupHolder.Count-1 do
            begin
              if PopupHolder.Items[Loop].Caption = Description then
                 Exit;
            end;
          end;
     end;

  NewMenuItem:= TMenuItem.Create(PopupHolder);
  NewMenuItem.Caption:= Description;
  NewMenuItem.Tag:= SystemID;
  NewMenuItem.OnClick:= LatestPlayedGamesClick;
  PopupHolder.Add(NewMenuItem);
end;

procedure TFormMain.AddPopupItem2(PopupHolder: TPopupMenu; Description: String; Duplicate: Boolean; SystemID: ShortInt);
var
  NewMenuItem: TMenuItem;
  Loop: Integer;
begin
  if not Duplicate then
     begin
       if PopupHolder.Items.Count > 0 then
          begin
            for Loop:=0 to PopupHolder.Items.Count-1 do
            begin
              if PopupHolder.Items[Loop].Caption = Description then
                 Exit;
            end;
          end;
     end;

  NewMenuItem:= TMenuItem.Create(PopupHolder);
  NewMenuItem.Caption:= Description;
  NewMenuItem.Tag:= SystemID;
  NewMenuItem.OnClick:= LatestPlayedGamesClick;
  PopupHolder.Items.Add(NewMenuItem);
end;

function TFormMain.SelectedLatestPlayedGame(GameDescription: String; SystemID: ShortInt): Integer;
var
  //GameIndex: Integer;
  Found: Boolean;
  Node: PVirtualNode;
  Data: pGameInfo;
begin
  Result:= -1;
  if not CheckTotalVisible then
     Exit;
  Found:= False;
  Node:= GamesTree.GetFirst;
  repeat
    Data:= GamesTree.GetNodeData(Node);
    Found:= ((Data.eDescription = GameDescription) and (Data.eConsoleType = SystemID));
    if Found then
       begin
         Result:= Node.Index;
         Break;
       end;
    Node:= GamesTree.GetNext(Node);
  until (not Assigned(Node)) or (Found);

  if Found then
     SelectNode(Node.Index);
  GamesTree.SetFocus;
end;

function TFormMain.ReadIniFile: Boolean;
var
  IniFileName: TIniFile;
  LineString, ValueString: String;
  LoopFilter: Integer;

  function ReadEmulatorFolders(Section: String; SystemID: ShortInt): Boolean;
  var
    LoopFolders: Integer;
  begin
    LineString:= IniFileName.ReadString(Section, 'GamesFolders', '');
    Result:= LineString <> '';
    if Result then
       begin
         if not Assigned(emuROMsFolders[SystemID]) then
            emuROMsFolders[SystemID]:= THashedStringList.Create;
         emuROMsFolders[SystemID].Clear;
         ValueString:= '';
         emuROMsFolders[SystemID].BeginUpdate;
         for LoopFolders:=1 to Length(LineString) do
         begin
           if LineString[LoopFolders] <> ';' then
              ValueString:= ValueString+LineString[LoopFolders]
           else
              begin
                emuROMsFolders[SystemID].Add(ValueString);
                ValueString:= '';
              end;
         end;
         emuROMsFolders[SystemID].EndUpdate;
       end;
  end;

  function ReadLatestPlayedGamesList(Section: String; PopupHolder: TMenuItem; SystemID: ShortInt): Boolean;
  var
    LoopPopup, LoopPopup2: Integer;
  begin
    Result:= False;
    for LoopPopup:=1 to 5 do
    begin
      LineString:= IniFileName.ReadString(Section, 'MRU List '+IntToStr(LoopPopup), '');
      if not Result then
         Result:= (LineString <> '');
      if LineString <> '' then
         begin
           ValueString:= '';
           for LoopPopup2:=1 to Length(LineString) do
           begin
             if LineString[LoopPopup2] <> ';' then
                ValueString:= ValueString+LineString[LoopPopup2]
             else
                begin
                  AddPopupItem(PopupHolder, ValueString, False, SystemID);
                  ValueString:= '';
                end;
           end;
         end;
    end;
  end;

  function SetEmulatorToUse(Section: String; MenuEmulator: TMenuItem): Boolean;
  begin
    if IniFileName.ReadInteger(Section, 'UseEmulator', 1) = 2 then
       MenuEmulator.Checked:= True;
  end;

  procedure ReadSystem(SystemID: ShortInt; iniSection: String; EmuPopup_1, EmuPopup_2: TMenuItem; LastPlayed: TMenuItem; SnapDir: String);
  begin
    GetSystemFileDescription(SystemID);
    SnapshotFolder[SystemID]:= IniFileName.ReadString('Snapshots Folder', iniSection, SnapDir);

    EmulatorFile[SystemID, 1]:= IniFileName.ReadString(iniSection, 'Emulator', '');
    EmulatorVersion[SystemID, 1]:= IniFileName.ReadString(iniSection, 'EmuDescription', '');

    if EmulatorFile[SystemID, 1] <> '' then
       begin
         if EmulatorVersion[SystemID, 1] = '' then
            EmulatorVersion[SystemID, 1]:= Format('%s - (%u)', [SystemDescription, 1]);
       end
    else
       begin
         if EmulatorVersion[SystemID, 1] <> '' then
            EmulatorVersion[SystemID, 1]:= '';
       end;
    if EmulatorVersion[SystemID, 1] <> '' then
       EmuPopup_1.Caption:= EmulatorVersion[SystemID, 1]
    else
       EmuPopup_1.Caption:= '(none)';

    EmulatorFile[SystemID, 2]:= IniFileName.ReadString(iniSection, 'Emulator2', '');
    EmulatorVersion[SystemID, 2]:= IniFileName.ReadString(iniSection, 'EmuDescription2', '');

    if EmulatorFile[SystemID, 2] <> '' then
       begin
         if EmulatorVersion[SystemID, 2] = '' then
            EmulatorVersion[SystemID, 2]:= Format('%s - (%u)', [SystemDescription, 2]);
       end
    else
       begin
         if EmulatorVersion[SystemID, 2] <> '' then
            EmulatorVersion[SystemID, 2]:= '';
       end;
    if EmulatorVersion[SystemID, 2] <> '' then
       EmuPopup_2.Caption:= EmulatorVersion[SystemID, 2]
    else
       EmuPopup_2.Caption:= '(none)';

    if EmulatorFile[SystemID, 2] <> '' then
       SetEmulatorToUse(iniSection, EmuPopup_2);
    ReadEmulatorFolders(iniSection, SystemID);
    ReadLatestPlayedGamesList(iniSection, LastPlayed, SystemID);

    if SystemID = 21 then // needed for PlayStation
       begin
         EmuPopup_1.Hint:= IniFileName.ReadString(iniSection, 'Emulator1Type', '0');
         EmuPopup_2.Hint:= IniFileName.ReadString(iniSection, 'Emulator2Type', '0');
       end;

    ExtractBinaryIcon(SystemID, 1);
    ExtractBinaryIcon(SystemID, 2);
  end;

begin
  Result:= FileExists(AppPath+'EmuCon.ini');
  if not Result then
     Exit;

  IniFileName:= TIniFile.Create(AppPath+'EmuCon.ini');
  with IniFileName do
  begin
    FormStatus.LabelTitleMessage.Caption:= 'Initializing';
    FormStatus.LabelMessage.Text:= 'Loading settings.';
    FormStatus.Refresh;

    FormMain.Tag:= ReadInteger('General', 'Maximized', 0);
    FormMain.Left:= ReadInteger('General', 'WindowPosLeft', (Screen.Width shr 1)-(Width shr 1)-1);
    FormMain.Top:=  ReadInteger('General', 'WindowPosTop',  (Screen.Height shr 1)-(Height shr 1)-1);
    FormMain.Width:= ReadInteger('General', 'ScreenWidthSize', 635);
    FormMain.Height:= ReadInteger('General', 'ScreenHeightSize', 640);


    if Boolean(ReadInteger('General', 'StretchPicture', 0)) then
       MenuStretchPicture.Click;
    MenuSmoothPicture.Tag:= ReadInteger('General', 'SmoothPicture', 0);

    case MenuSmoothPicture.Tag of
      1: MenuSmoothPictureLinear.Click;
      2: MenuSmoothPictureLanczos.Click;
      3: MenuSmoothPictureMitchell.Click;
      4: MenuSmoothPictureSpline.Click;
      5: MenuSmoothPictureCosine.Click;
    end;
    PanelPictures.Width:= ReadInteger('General', 'PicturesSize', 328);
    if not Boolean(ReadInteger('General', 'ShowSnapshots', 1)) then
       MenuShowSnapshots.Click;
    MenuHideFirstPicture.Checked:= Boolean(ReadInteger('General', 'Hide1stPicture', 0));

    SelectedGameName:= ReadString('General', 'LastGame', '');
    SelectedGameID:= ReadInteger('General', 'LatGameID', -1);
    ColumnSorted:= ReadInteger('General', 'ColumnSorted', 0);

    MenuAlwaysCreateNewList.Checked:= Boolean(ReadInteger('General', 'AlwaysNewList', 1));
    MenuShowFullCommandLine.Checked:= Boolean(ReadInteger('General', 'ShowCommandLine', 0));
    ButtonHugoParameters.Down:= Boolean(ReadInteger('General', 'ParametersHUGO!', 1));
    FormPreferences.DebuggerWindow.Checked:= Boolean(ReadInteger('General', 'DebuggerWindow', 1));
    ButtonGamesFilters.Tag:= ReadInteger('General', 'SystemFilter', 0);
    if ButtonGamesFilters.Tag > 0 then
       begin
         for LoopFilter:= 1 to PopupGamesFilters.Items.Count-1 do
         begin
           if PopupGamesFilters.Items[LoopFilter].Tag = ButtonGamesFilters.Tag then
              begin
                PopupGamesFilters.Items[LoopFilter].Checked:= True;
                Break;
              end;
         end;
         GetSystemIcon;
       end;
    MenuViewLargeGameIcons.Checked:= Boolean(ReadInteger('General', 'ViewLargeGameIcons', 0));
    MenuViewLargeGameIcons.OnClick(Self);
    //FormPreferences.SnapshotsPrefix.Text:= ReadString('Snapshots', 'Prefix', '_

    // Super Nintendo
    ReadSystem(1, 'Super Nintendo', PopupSuperNintendo_1, PopupSuperNintendo_2,
               PopupSNESLatestPlayedGames, 'images\supernintendo');

    // Gameboy Advance
    ReadSystem(2, 'Gameboy Advance', PopupGameboyAdvance_1, PopupGameboyAdvance_2,
               PopupGBALatestPlayedGames, 'images\gameboyadvance');

    // Nintendo 8-bit
    ReadSystem(3, 'Nintendo 8-bit', PopupNintendo_1, PopupNintendo_2,
               PopupNESLatestPlayedGames, 'images\nintendo');

    // Sega Genesis
    ReadSystem(4, 'Genesis', PopupGenesis_1, PopupGenesis_2,
               PopupGenesisLatestPlayedGames, 'images\genesis');

    // Atari 2600
    ReadSystem(5, 'Atari 2600', PopupAtari2600_1, PopupAtari2600_2,
               PopupAtari2600LatestPlayedGames, 'images\atari2600');

    // Intellivision
    ReadSystem(6, 'Intellivision', PopupIntellivision_1, PopupIntellivision_2,
               PopupIntellivisionLatestPlayedGames, 'images\intellivision');

    // Master System
    ReadSystem(7, 'Master System', PopupMasterSystem_1, PopupMasterSystem_2,
               PopupMasterSystemLatestPlayedGames, 'images\mastersystem');

    // Odyssey2
    ReadSystem(8, 'Odyssey2', PopupOdyssey2_1, PopupOdyssey2_2,
               PopupOdyssey2LatestPlayedGames, 'images\odyssey2');

    // Game Gear
    ReadSystem(9, 'Game Gear', PopupGameGear_1, PopupGameGear_2,
               PopupGameGearLatestPlayedGames, 'images\gamegear');

    // ColecoVision
    ReadSystem(10, 'ColecoVision', PopupColecoVision_1, PopupColecoVision_2,
               PopupColecoVisionLatestPlayedGames, 'images\colecovision');

    // GP32
    ReadSystem(11, 'GP32', PopupGP32_1, PopupGP32_2,
               PopupGP32LatestPlayedGames, 'images\gp32');

    // PC Engine
    ReadSystem(12, 'PC Engine', PopupPCEngine_1, PopupPCEngine_2,
               PopupPCEngineLatestPlayedGames, 'images\pcengine');

    // Atari Lynx
    ReadSystem(13, 'Atari Lynx', PopupAtariLynx_1, PopupAtariLynx_2,
               PopupAtariLynxLatestPlayedGames, 'images\lynx');

    // Atari 5200
    ReadSystem(14, 'Atari 5200', PopupAtari5200_1, PopupAtari5200_2,
               PopupAtari5200LatestPlayedGames, 'images\atari5200');

    // Atari Jaguar
    ReadSystem(15, 'Atari Jaguar', PopupAtariJaguar_1, PopupAtariJaguar_2,
               PopupAtariJaguarLatestPlayedGames, 'images\jaguar');

    // Sega 32X
    ReadSystem(16, 'Sega 32X', PopupSega32X_1, PopupSega32X_2,
               PopupSega32XLatestPlayedGames, 'images\sega32x');

    // Neo Geo Pocket
    ReadSystem(17, 'Neo Geo Pocket', PopupNeoGeoPocket_1, PopupNeoGeoPocket_2,
               PopupNeoGeoPocketLatestPlayedGames, 'images\neogeopocket');

    // Nintendo 64
    ReadSystem(18, 'Nintendo 64', PopupNintendo64_1, PopupNintendo64_2,
               PopupNintendo64LatestPlayedGames, 'images\nintendo64');

    // Sega CD
    ReadSystem(19, 'Sega CD', PopupSegaCD_1, PopupSegaCD_2,
               PopupSegaCDLatestPlayedGames, 'images\segacd');

    // Gameboy / Gameboy Color / Super Gameboy
    ReadSystem(20, 'Gameboy', PopupGameboy_1, PopupGameboy_2,
               PopupGameboyLatestPlayedGames, 'images\gameboy');

    // PlayStation
    ReadSystem(21, 'PlayStation', PopupPlayStation_1, PopupPlayStation_2,
               PopupPlayStationLatestPlayedGames, 'images\playstation');

    // Nintendo GameCube
    ReadSystem(22, 'Nintendo GameCube', PopupGameCube_1, PopupGameCube_2,
               PopupGameCubeLatestPlayedGames, 'images\gamecube');

    // Atari 7800
    ReadSystem(23, 'Atari 7800', PopupAtari7800_1, PopupAtari7800_2,
               PopupAtari7800LatestPlayedGames, 'images\atari7800');

    // Sega Dreamcast
    ReadSystem(24, 'Dreamcast', PopupDreamcast_1, PopupDreamcast_2,
               PopupDreamcastLatestPlayedGames, 'images\dreamcast');

    // Watara Supervision
    ReadSystem(25, 'Watara Supervision', PopupWataraSupervision_1, PopupWataraSupervision_2,
               PopupWataraSupervisionLatestPlayedGames, 'images\supervision');

    // Wonderswan
    ReadSystem(26, 'Wonderswan', PopupWonderswan_1, PopupWonderswan_2,
               PopupWonderswanLatestPlayedGames, 'images\wonderswan');
  end;

  FreeAndNil(IniFileName);
  if FormPreferences.SnapshotsFolder.ItemIndex = -1 then
     begin
       FormPreferences.SnapshotsFolder.ItemIndex:= 0;
       FormPreferences.SnapshotsFolder.OnChange(Self);
     end;
end;

function  TFormMain.UpdateIniFile: Boolean;
var
  IniFileName: TMemIniFile;
  LineString, MRU1, MRU2, MRU3, MRU4, MRU5: String;
  Data: pGameInfo;
  //Counter: Integer;

  function UpdateEmulatorFolders(Section: String; SystemID: ShortInt): Boolean;
  var
    LoopFolders: Integer;
  begin
    LineString:= '';
    Result:= Assigned(emuROMsFolders[SystemID]);
    if Result then
       Result:= (emuROMsFolders[SystemID].Count > 0);
    case Result of
      True:
        begin
          for LoopFolders:=0 to emuROMsFolders[SystemID].Count -1 do
              LineString:= LineString+ emuROMsFolders[SystemID].Strings[LoopFolders]+';';
          IniFileName.WriteString(Section, 'GamesFolders', LineString);
        end;
      False: IniFileName.DeleteKey(Section, 'GamesFolders');
    end;
  end;

  function UpdateLatestPlayedGamesList(Section: String; PopupHolder: TMenuItem): Boolean;
  var
    LoopPopup: Integer;
  begin
    MRU1:= '';
    MRU2:= '';
    MRU3:= '';
    MRU4:= '';
    MRU5:= '';
    Result:= (PopupHolder.Count > 0);
    if Result then
       begin
         LineString:= '';
         for LoopPopup:=0 to PopupHolder.Count - 1 do
         begin
           case LoopPopup of
               0..4: MRU1:= MRU1+PopupHolder.Items[LoopPopup].Caption+';';
               5..9: MRU2:= MRU2+PopupHolder.Items[LoopPopup].Caption+';';
             10..14: MRU3:= MRU3+PopupHolder.Items[LoopPopup].Caption+';';
             15..19: MRU4:= MRU4+PopupHolder.Items[LoopPopup].Caption+';';
             20..24: MRU5:= MRU5+PopupHolder.Items[LoopPopup].Caption+';';
           end;
         end;
         if MRU1 <> '' then
            IniFileName.WriteString(Section, 'MRU List 1', MRU1)
         else
            IniFileName.DeleteKey(Section, 'MRU List 1');
         if MRU2 <> '' then
            IniFileName.WriteString(Section, 'MRU List 2', MRU2)
         else
            IniFileName.DeleteKey(Section, 'MRU List 2');
         if MRU3 <> '' then
            IniFileName.WriteString(Section, 'MRU List 3', MRU3)
         else
            IniFileName.DeleteKey(Section, 'MRU List 3');
         if MRU4 <> '' then
            IniFileName.WriteString(Section, 'MRU List 4', MRU4)
         else
            IniFileName.DeleteKey(Section, 'MRU List 4');
         if MRU5 <> '' then
            IniFileName.WriteString(Section, 'MRU List 5', MRU5)
         else
            IniFileName.DeleteKey(Section, 'MRU List 5');
       end;
  end;

  function UpdateEmulator(Section: String; SystemID, EmulatorNumber: ShortInt): Boolean;
  begin
    Result:= (EmulatorFile[SystemID, EmulatorNumber] <> '');
    case Result of
      True:
        begin
          case EmulatorNumber of
            1: begin
                 IniFileName.WriteString(Section, 'Emulator', EmulatorFile[SystemID, 1]);
                 IniFileName.WriteString(Section, 'EmuDescription', EmulatorVersion[SystemID, 1]);
                 if SystemID = 21 then
                    IniFileName.WriteString(Section, 'Emulator1Type', PopupPlayStation_1.Hint);
               end;
            2: begin
                 IniFileName.WriteString(Section, 'Emulator2', EmulatorFile[SystemID, 2]);
                 IniFileName.WriteString(Section, 'EmuDescription2', EmulatorVersion[SystemID, 2]);
                 if SystemID = 21 then
                    IniFileName.WriteString(Section, 'Emulator2Type', PopupPlayStation_2.Hint);
               end;
          end;
        end;
      False:
        begin
          case EmulatorNumber of
            1: begin
                 IniFileName.DeleteKey(Section, 'Emulator');
                 IniFileName.DeleteKey(Section, 'EmuDescription');
                 if SystemID = 21 then
                    IniFileName.DeleteKey(Section, 'Emulator1Type');
               end;
            2: begin
                 IniFileName.DeleteKey(Section, 'Emulator2');
                 IniFileName.DeleteKey(Section, 'EmuDescription2');
                 if SystemID = 21 then
                    IniFileName.DeleteKey(Section, 'Emulator2Type');
               end;
          end;
        end;
    end;
  end;

  function WriteEmulatorToUse(Section: String; Emulator1Status: TMenuItem): Boolean;
  begin
    case Emulator1Status.Checked of
      True : IniFileName.WriteInteger(Section, 'UseEmulator', 1);
      False: IniFileName.WriteInteger(Section, 'UseEmulator', 2);
    end;
  end;

  procedure WriteSystem(SystemID: ShortInt; Section: String; UseEmulatorPopup: TMenuItem; LastPlayed: TMenuItem);
  begin
    IniFileName.WriteString('Snapshots Folder', Section, SnapshotFolder[SystemID]);

    UpdateEmulator(Section, SystemID, 1);
    UpdateEmulator(Section, SystemID, 2);

    WriteEmulatorToUse(Section, UseEmulatorPopup);
    UpdateEmulatorFolders(Section, SystemID);
    UpdateLatestPlayedGamesList(Section, LastPlayed);
  end;

begin
  if CheckReadOnly(AppPath+'EmuCon.ini') then
     Exit;
  Result:= True;
  if FileExists(AppPath+'EmuCon.ini') then
     DeleteFile(AppPath+'EmuCon.ini');
  IniFileName:= TMemIniFile.Create(AppPath+'EmuCon.ini');
  with IniFileName do
  begin
    WriteInteger('General', 'Maximized', Ord(FormMain.WindowState = wsMaximized));
    if FormMain.WindowState = wsNormal then
       begin
         WriteInteger('General', 'WindowPosLeft', FormMain.Left);
         WriteInteger('General', 'WindowPosTop',  FormMain.Top);
         WriteInteger('General', 'ScreenWidthSize', FormMain.Width);
         WriteInteger('General', 'ScreenHeightSize', FormMain.Height);
       end;

    WriteInteger('General', 'StretchPicture', Ord(MenuStretchPicture.Checked));
    WriteInteger('General', 'SmoothPicture', MenuSmoothPicture.Tag);
    WriteInteger('General', 'ShowSnapshots', Ord(MenuShowSnapshots.Checked));
    if PanelPictures.Visible then
       WriteInteger('General', 'PicturesSize', PanelPictures.Width);
    WriteInteger('General', 'Hide1stPicture', Ord(MenuHideFirstPicture.Checked));
    if CheckSelected then
       begin
         Data:= GamesTree.GetNodeData(SelectedNode);
         WriteString('General', 'LastGame', Data.eDescription);
         WriteInteger('General', 'LatGameID', Data.eConsoleType);
       end;
    WriteInteger('General', 'ColumnSorted', ColumnSorted);
    WriteInteger('General', 'AlwaysNewList', Ord(MenuAlwaysCreateNewList.Checked));
    WriteInteger('General', 'ShowCommandLine', Ord(MenuShowFullCommandLine.Checked));
    WriteInteger('General', 'ParametersHUGO!', Ord(ButtonHugoParameters.Down));

    WriteInteger('General', 'DebuggerWindow', Ord(FormPreferences.DebuggerWindow.Checked));
    WriteInteger('General', 'SystemFilter', ButtonGamesFilters.Tag);
    WriteInteger('General', 'ViewLargeGameIcons', Ord(MenuViewLargeGameIcons.Checked));

    // Super Nintendo
    WriteSystem(1, 'Super Nintendo',
                PopupSuperNintendo_1, PopupSNESLatestPlayedGames);

    // Gameboy Advance
    WriteSystem(2, 'Gameboy Advance',
                PopupGameboyAdvance_1, PopupGBALatestPlayedGames);

    // Nintendo 8-bit
    WriteSystem(3, 'Nintendo 8-bit',
                PopupNintendo_1, PopupNESLatestPlayedGames);

    // Genesis
    WriteSystem(4, 'Genesis',
                PopupGenesis_1, PopupGenesisLatestPlayedGames);

    // Atari 2600
    WriteSystem(5, 'Atari 2600',
                PopupAtari2600_1, PopupAtari2600LatestPlayedGames);

    // Intellivision
    WriteSystem(6, 'Intellivision',
                PopupIntellivision_1, PopupIntellivisionLatestPlayedGames);

    // Master System
    WriteSystem(7, 'Master System',
                PopupMasterSystem_1, PopupMasterSystemLatestPlayedGames);

    // Odyssey2
    WriteSystem(8, 'Odyssey2',
                PopupOdyssey2_1, PopupOdyssey2LatestPlayedGames);

    // Sega Game Gear
    WriteSystem(9, 'Game Gear',
                PopupGameGear_1, PopupGameGearLatestPlayedGames);

    // ColecoVision
    WriteSystem(10, 'ColecoVision',
                PopupColecoVision_1, PopupColecoVisionLatestPlayedGames);

    // GP32
    WriteSystem(11, 'GP32',
                PopupGP32_1, PopupGP32LatestPlayedGames);

    // PC Engine
    WriteSystem(12, 'PC Engine',
                PopupPCEngine_1, PopupPCEngineLatestPlayedGames);

    // Atari Lynx
    WriteSystem(13, 'Atari Lynx',
                PopupAtariLynx_1, PopupAtariLynxLatestPlayedGames);

    // Atari 5200
    WriteSystem(14, 'Atari 5200',
                PopupAtari5200_1, PopupAtari5200LatestPlayedGames);

    // Atari Jaguar
    WriteSystem(15, 'Atari Jaguar',
                PopupAtariJaguar_1, PopupAtariJaguarLatestPlayedGames);

    // Sega 32X
    WriteSystem(16, 'Sega 32X',
                PopupSega32X_1, PopupSega32XLatestPlayedGames);

    // Neo Geo Pocket
    WriteSystem(17, 'Neo Geo Pocket',
                PopupNeoGeoPocket_1, PopupNeoGeoPocketLatestPlayedGames);

    // Nintendo 64
    WriteSystem(18, 'Nintendo 64',
                PopupNintendo64_1, PopupNintendo64LatestPlayedGames);

    // Sega CD
    WriteSystem(19, 'Sega CD',
                PopupSegaCD_1, PopupSegaCDLatestPlayedGames);

    // Gameboy / Gameboy Color / Super Gameboy
    WriteSystem(20, 'Gameboy',
                PopupGameboy_1, PopupGameboyLatestPlayedGames);

    // PlayStation
    WriteSystem(21, 'PlayStation',
                PopupPlayStation_1, PopupPlayStationLatestPlayedGames);

    // Nintendo GameCube
    WriteSystem(22, 'Nintendo GameCube',
                PopupGameCube_1, PopupGameCubeLatestPlayedGames);

    // Atari 7800
    WriteSystem(23, 'Atari 7800',
                PopupAtari7800_1, PopupAtari7800LatestPlayedGames);

    // Sega Dreamcast
    WriteSystem(24, 'Dreamcast',
                PopupDreamcast_1, PopupDreamcastLatestPlayedGames);

    // Watara Supervision
    WriteSystem(25, 'Watara Supervision',
                PopupWataraSupervision_1, PopupWataraSupervisionLatestPlayedGames);

    // Wonderswan
    WriteSystem(26, 'Wonderswan',
                PopupWonderswan_1, PopupWonderswanLatestPlayedGames);
  end;
  IniFileName.UpdateFile;
  FreeAndNil(IniFileName);
end;

procedure TFormMain.MenuExitClick(Sender: TObject);
begin
  Close;
end;

procedure TFormMain.FormActivate(Sender: TObject);
begin
  if FormMain.Tag = 1 then
     begin
       FormMain.Tag:= 0;
       FormMain.WindowState:= wsMaximized;
     end;
end;

procedure TFormMain.FormShow(Sender: TObject);
var
  romID: ShortInt;

  procedure FillEmulatorBinaryIcons;
  var
    Index, MaxIndex: ShortInt;
    DefaultIcon: TIcon;
  begin
    DefaultIcon:= TIcon.Create;
    DefaultIcon.Width:= 32;
    DefaultIcon.Height:= 32;
    MaxIndex:= (PopupEmulators.Items.Count-2)*2;
    ImageListEmulatorsIcons.GetIcon(0, DefaultIcon);
    repeat
      Index:= ImageListEmulatorsIcons.AddIcon(DefaultIcon);
      if Index = -1 then
         GenerateMessage('Error', Format('Error when trying to add ghost icon number: %u to the '+
                         'emulators icons list! The frontend might give errors.', [Index]), 2);
    until Index = MaxIndex;
    FreeAndNil(DefaultIcon);
  end;

begin
  FormStatus.Show;
  FormStatus.LabelTitleMessage.Caption:= 'Initializing';
  FormStatus.LabelMessage.Text:= 'Reading initial options. Please, wait a moment...';
  FormStatus.Refresh;
  ProcessingConsole:= False;

  FillEmulatorBinaryIcons;
  if ReadIniFile then
     begin
       FormStatus.LabelTitleMessage.Caption:= 'Loading Games';
       FormStatus.LabelMessage.Text:= 'Loading games list based on the games filter. Please, wait a moment...';
       FormStatus.Refresh;
       romID:= mConsoleType;
       LoadGames(True);
     end
  else
     GenerateMessage('Welcome', 'Please, configure the frontend before using.', 2);

  FormStatus.Close;
end;

procedure TFormMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  Loop: Integer;
begin
  if CanClose then
     begin
       UpdateIniFile;
       FreeAndNil(PopupSNESLatestPlayedGames);
       FreeAndNil(PopupGBALatestPlayedGames);
       FreeAndNil(PopupNESLatestPlayedGames);
       FreeAndNil(PopupGenesisLatestPlayedGames);
       FreeAndNil(PopupAtari2600LatestPlayedGames);
       FreeAndNil(PopupIntellivisionLatestPlayedGames);
       FreeAndNil(PopupMasterSystemLatestPlayedGames);
       FreeAndNil(PopupOdyssey2LatestPlayedGames);
       FreeAndNil(PopupGameGearLatestPlayedGames);
       FreeAndNil(PopupColecoVisionLatestPlayedGames);
       FreeAndNil(PopupGP32LatestPlayedGames);
       FreeAndNil(PopupPCEngineLatestPlayedGames);
       FreeAndNil(PopupAtariLynxLatestPlayedGames);
       FreeAndNil(PopupAtari5200LatestPlayedGames);
       FreeAndNil(PopupAtariJaguarLatestPlayedGames);
       FreeAndNil(PopupSega32XLatestPlayedGames);
       FreeAndNil(PopupNeoGeoPocketLatestPlayedGames);
       FreeAndNil(PopupNintendo64LatestPlayedGames);
       FreeAndNil(PopupSegaCDLatestPlayedGames);
       FreeAndNil(PopupGameboyLatestPlayedGames);
       FreeAndNil(PopupPlayStationLatestPlayedGames);
       FreeAndNil(PopupGameCubeLatestPlayedGames);
       FreeAndNil(PopupAtari7800LatestPlayedGames);
       FreeAndNil(PopupDreamcastLatestPlayedGames);
       FreeAndNil(PopupWataraSupervisionLatestPlayedGames);
       FreeAndNil(PopupWonderswanLatestPlayedGames);

       for Loop:=1 to MaxSystems-1 do
           FreeAndNil(emuROMsFolders[Loop]);

       // hack to make sure main form disappears from Windows task bar
       if FormMain.WindowState <> wsNormal then
          FormMain.WindowState:= wsNormal;
       FormMain.Width:= FormMain.Width-2;
     end;
end;

procedure TFormMain.RandomGame_Click(Sender: TObject);
var
  RandomValue: Integer;
  Continue: Boolean;
  NodeIndex: PVirtualNode;
begin
  if not CheckTotalVisible then
     Exit;
  //ShowingImage:= True;
  Continue:= False;
  repeat
    Randomize;
    RandomValue:= Random(GamesTree.TotalCount-1);
    if RandomValue <> SelectedNode.Index then
       begin
         NodeIndex:= GamesTree.GetFirstVisible;
         repeat
           if NodeIndex.Index <> RandomValue then
              NodeIndex:= GamesTree.GetNextVisible(NodeIndex)
           else
              Continue:= True;
         until (not Assigned(NodeIndex)) or Continue;
       end;
  until (Continue);
  SelectNode(RandomValue);
end;

procedure TFormMain.FormCreate(Sender: TObject);
begin
  AppPath:= ShortToLongPath(ExtractFilePath(Application.ExeName));
  FormMain.Caption:= Format(FormMain.Caption+ ' - [v%s]', [GetFileInfo(Application.ExeName, VersionInfo[3])]);
  ThousandSeparator:= Char(',');
  DecimalSeparator:= Char('.');
  ProcessingConsole:= True;
  InitializeColumnsSort;
  InitializeEmulatorVariables;
end;

procedure TFormMain.ExecuteGame;
var
  Parameters, GameFileName, CommandLine: String;
  Quote: String[1];
  Data: pGameInfo;

  function GetAtari2600CliFile: String;
  var
    CliFile: THashedStringList;
  begin
    Result:= '';
    if CheckFile(ExtractFilePath(EmulatorFile[5, 1])+'z26.cli') then
       begin
         CliFile:= THashedStringList.Create;
         CliFile.LoadFromFile(ExtractFilePath(EmulatorFile[5, 1])+'z26.cli');
         if CliFile.Count > 0 then
            Result:= CliFile[0];
         FreeAndNil(CliFile);
       end;
  end;

  function SearchGameFile(GameName: String): Boolean;
  var
    LoopSearch: Integer;
  begin
    Result:= False;
    if emuROMsFolders[Data.eConsoleType].Count = 0 then
       Exit;
    for LoopSearch:=0 to emuROMsFolders[Data.eConsoleType].Count-1 do
       begin
         if FileExists(emuROMsFolders[Data.eConsoleType].Strings[LoopSearch]+'\'+GameName) then
            begin
              mGameFileName:= emuROMsFolders[Data.eConsoleType].Strings[LoopSearch]+'\'+GameName;
              Result:= True;
              Break;
            end;
       end;
  end;

  function SetMediaDescription(PopupHolder: TMenuItem): Boolean;
  begin
    Result:= PopupHolder.Checked;
    case Result of
      True : mMediaDescription:= EmulatorFile[Data.eConsoleType, 1];
      False: mMediaDescription:= EmulatorFile[Data.eConsoleType, 2];
    end;
  end;

  {function UpdateGameFileName(GameName: String): Boolean;
  var
    LoopUpdate: Integer;
    GameFile: String;
    FileList: THashedStringList;
  begin
    case Data.eConsoleType of
      1: GameFile:= AppPath+'games\supernintendo.dat';
      2: GameFile:= AppPath+'games\gameboyadvance.dat';
      3: GameFile:= AppPath+'games\nintendo.dat';
      4: GameFile:= AppPath+'games\genesis.dat';
      5: GameFile:= AppPath+'games\atari2600.dat';
      6: GameFile:= AppPath+'games\intellivision.dat';
      7: GameFile:= AppPath+'games\mastersystem.dat';
      8: GameFile:= AppPath+'games\odyssey2.dat';
      9: GameFile:= AppPath+'games\gamegear.dat';
     10: GameFile:= AppPath+'games\colecovision.dat';
     11: GameFile:= AppPath+'games\gp32.dat';
     12: GameFile:= AppPath+'games\pcengine.dat';
     13: GameFile:= AppPath+'games\lynx.dat';
     14: GameFile:= AppPath+'games\atari5200.dat';
     15: GameFile:= AppPath+'games\jaguar.dat';
     16: GameFile:= AppPath+'games\sega32x.dat';
     17: GameFile:= AppPath+'games\neogeopocket.dat';
     18: GameFile:= AppPath+'games\nintendo64.dat';
     19: GameFile:= AppPath+'games\segacd.dat';
     20: GameFile:= AppPath+'games\gameboy.dat';
     21: GameFile:= AppPath+'games\playstation.dat';
     22: GameFile:= AppPath+'games\gamecube.dat';
     23: GameFile:= AppPath+'games\atari7800.dat';
     24: GameFile:= AppPath+'games\dreamcast.dat';
    end;
    Result:= FileExists(GameFile);
    if Result then
       begin
         FileList:= THashedStringList.Create;
         FileList.LoadFromFile(GameFile);
         for LoopUpdate:= 0 to FileList.Count-1 do
         begin
           ClearMemoryColumnsVariables;
           GetGameFields(FileList[LoopUpdate]);
           if (mConsoleType = Data.eConsoleType) and
              (mDescription = Data.eDescription) and
              (mMediaDescription = Data.eMediaDescription) then
              begin
                Result:= mGameFileName <> GameName;
                if Result then
                   begin
                     mGameFileName:= GameName;
                     FileList[LoopUpdate]:= PopulateROMLine;
                     FileList.SaveToFile(GameFile);
                     Break;
                   end;
              end;
         end;
         FreeAndNil(FileList);
       end;
  end;}
  
begin
  if not CheckSelected then
     Exit;

  Parameters:= '';
  Data:= GamesTree.GetNodeData(SelectedNode);

  mGameFileName:= Data.eGameFileName;
  mMediaDescription:= '';
  ProcessingConsole:= SearchGameFile(mGameFileName);
  if not ProcessingConsole then
     begin
       if LowerCase(Data.eMediaDescription) <> 'cd' then
          begin
            GenerateMessage('File Not Found', Format('File "%s" was not found! Aborting...',
                            [Data.eGameFileName]), 2);
            ClearMemoryColumnsVariables;
            ProcessingConsole:= False;
            Exit;
          end;
     end;

  case Data.eConsoleType of
    1: begin // Super Nintendo
         CheckLatestPlayedGames(PopupSNESLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupSuperNintendo_1);
         // Check for Super Scope and Mouse
         if ButtonSuperScopeSNES.Down then
            Parameters:= ' -superscope'
         else
         if ButtonMouseSNES_PSX.Down then
            Parameters:= ' -mouse';
       end;
    2: begin // Gameboy Advance
         CheckLatestPlayedGames(PopupGBALatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGameboyAdvance_1);
       end;
    3: begin // Nintendo 8-bit
         CheckLatestPlayedGames(PopupNESLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupNintendo_1);
       end;
    4: begin // Genesis
         CheckLatestPlayedGames(PopupGenesisLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGenesis_1);
       end;
    5: begin // Atari 2600
         CheckLatestPlayedGames(PopupAtari2600LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         if SetMediaDescription(PopupAtari2600_1) then
            Parameters:= Parameters+GetAtari2600CliFile;

       end;
    6: begin // Intellivision
         CheckLatestPlayedGames(PopupIntellivisionLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupIntellivision_1);
       end;
    7: begin // Master System
         CheckLatestPlayedGames(PopupMasterSystemLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupMasterSystem_1);
       end;
    8: begin // Odyssey2
         CheckLatestPlayedGames(PopupOdyssey2LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupOdyssey2_1);
       end;
    9: begin // Game Gear
         CheckLatestPlayedGames(PopupGameGearLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGameGear_1);
       end;
   10: begin // ColecoVision
         CheckLatestPlayedGames(PopupColecoVisionLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupColecoVision_1);
       end;
   11: begin // GP32
         CheckLatestPlayedGames(PopupGP32LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGP32_1);

         // GP32 needs special treatment to load games
         if LowerCase(ExtractFileExt(mGameFileName)) = '.gxb' then
            mGameFileName:= '/GXB='+mGameFileName
         else
         if LowerCase(ExtractFileExt(mGameFileName)) = '.fxe' then
            mGameFileName:= '/FXE='+mGameFileName
         else
         if LowerCase(ExtractFileExt(mGameFileName)) = '.smc' then
            mGameFileName:= '/SMC='+mGameFileName;

         Parameters:= ' /RUN';
       end;
   12: begin // PC Engine
         CheckLatestPlayedGames(PopupPCEngineLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupPCEngine_1);

         if ButtonHugoParameters.Down then // These parameters are only used in HUGO! (PC Engine)
            Parameters:= ' -f 1 -s 1'; // Full screen and Stereo ON
       end;
   13: begin // Atari Lynx
         CheckLatestPlayedGames(PopupAtariLynxLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupAtariLynx_1);
       end;
   14: begin // Atari 5200
         CheckLatestPlayedGames(PopupAtari5200LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupAtari5200_1);
       end;
   15: begin // Atari Jaguar
         CheckLatestPlayedGames(PopupAtariJaguarLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupAtariJaguar_1);
       end;
   16: begin // Sega 32X
         CheckLatestPlayedGames(PopupSega32XLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupSega32X_1);
       end;
   17: begin // Neo Geo Pocket
         CheckLatestPlayedGames(PopupNeoGeoPocketLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupNeoGeoPocket_1);
       end;
   18: begin // Nintendo 64
         CheckLatestPlayedGames(PopupNintendo64LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupNintendo64_1);
       end;
   19: begin // Sega CD
         CheckLatestPlayedGames(PopupSegaCDLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupSegaCD_1);
       end;
   20: begin // Gameboy / Gameboy Color / Super Gameboy
         CheckLatestPlayedGames(PopupGameboyLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGameboy_1);
       end;
   21: begin // PlayStation
         CheckLatestPlayedGames(PopupPlayStationLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupPlayStation_1);
         if ((PopupPlayStation_1.Checked) and (PopupPlayStation_1.Hint = '0')) or
            ((PopupPlayStation_2.Checked) and (PopupPlayStation_2.Hint = '0')) then
            begin
              // for ePSXe only!
              if ButtonMouseSNES_PSX.Down then
                 Parameters:= ' -mouse'
              else
              if ButtonGunPSX.Down then
                 Parameters:= ' -gun';
            end;
         Parameters:= Parameters+' -nogui';
       end;
   22: begin // Nintendo GameCube
         CheckLatestPlayedGames(PopupGameCubeLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupGameCube_1);
       end;
   23: begin // Atari 7800
         CheckLatestPlayedGames(PopupAtari7800LatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupAtari7800_1);
       end;
   24: begin // Sega Dreamcast
         CheckLatestPlayedGames(PopupDreamcastLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         if SetMediaDescription(PopupDreamcast_1) then
            begin
               mGameFileName:= ' -file "'+mGameFileName+'"';
               Parameters:= Parameters+' -nogui';
            end;
       end;
   25: begin // Watara Supervision
         CheckLatestPlayedGames(PopupWataraSupervisionLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupWataraSupervision_1);
       end;
   26: begin // Wonderswan
         CheckLatestPlayedGames(PopupWonderswanLatestPlayedGames, Data.eDescription, Data.eConsoleType);
         SetMediaDescription(PopupWonderswan_1);
       end;
  end;

  if GetExeType(mMediaDescription) = etDOS then
     Quote:= '"' else Quote:= '';

  case Data.eConsoleType of
    1,6: CommandLine:= Quote+mMediaDescription+Quote+Parameters+' "'+mGameFileName+'"'; // Super Nintendo (Snes9x) and Intellivision working command line
    18: CommandLine:= Quote+mMediaDescription+Quote+' '+mGameFileName+Parameters; // Nintendo 64 can't have quotes in game filename
    21:
      begin
        // ePSXe needs special treatment
        if ((PopupPlayStation_1.Checked) and (PopupPlayStation_1.Hint = '0')) or
           ((PopupPlayStation_2.Checked) and (PopupPlayStation_2.Hint = '0')) then
           begin
             if LowerCase(Data.eMediaDescription) <> 'cd' then
                mGameFileName:= ' -loadiso "'+mGameFileName+'"'
             else
                begin
                  GenerateMessage('Aborting', '"ePSXe" does not support booting a CD from the command line!', 2);
                  ClearMemoryColumnsVariables;
                  ProcessingConsole:= False;
                  Exit;
                end;
           end
        else

        // PCSX also needs special treatment
        if ((PopupPlayStation_1.Checked) and (PopupPlayStation_1.Hint = '2')) or
           ((PopupPlayStation_2.Checked) and (PopupPlayStation_2.Hint = '2')) then
           begin
             if LowerCase(Data.eMediaDescription) = 'cd' then
                Parameters:= Parameters+ ' -runcd'
             else
                mGameFileName:= ' -load "'+mGameFileName+'"';
           end
        else

        // AdriPSX... don't know if it needs special treatment
           begin
             if LowerCase(Data.eMediaDescription) <> 'cd' then
                mGameFileName:= ' "'+mGameFileName+'"'
             else
                begin
                  GenerateMessage('Aborting', '"AdriPSX" does not support booting a CD from the command line!', 2);
                  ClearMemoryColumnsVariables;
                  ProcessingConsole:= False;
                  Exit;
                end;
           end;
        CommandLine:= Quote+mMediaDescription+Quote+mGameFileName+Parameters;
      end
    else
       CommandLine:= Quote+mMediaDescription+Quote+' "'+mGameFileName+'"'+Parameters; // all other emulators
  end;

  if MenuShowFullCommandLine.Checked and (mMediaDescription <> '') then
     begin
       if GenerateMessage('Emulator Command Line', CommandLine+#13#10+#13#10+'Continue ?', 1) = mrNo then
          begin
            ClearMemoryColumnsVariables;
            SetCurrentDir(AppPath);
            ProcessingConsole:= False;
            Exit;
          end;
     end;

  if mMediaDescription <> '' then
     begin
       SetCurrentDir(ExtractFilePath(mMediaDescription));
       if Quote <> '' then
          begin
            case Win32Platform of
              0,1: RunProcess('command.com /c '+CommandLine, SW_SHOWNORMAL); // Win9x
              2  : RunProcess('cmd.exe /c '+'"'+CommandLine+'"', SW_SHOWNORMAL); // WinXP
            end;
          end
       else
          RunProcess(CommandLine, SW_SHOWNORMAL);
     end
  else
     begin
       GenerateMessage('File Not Found', 'There is no emulator to execute. '+#13+'Please make sure to select an '+
                              'emulator binary before running a game, or select the alternate emulator.', 2);
     end;
  ClearMemoryColumnsVariables;
  SetCurrentDir(AppPath);
  ProcessingConsole:= False;
end;

procedure TFormMain.MenuStretchPictureClick(Sender: TObject);
begin
  case MenuStretchPicture.Checked of
    True : Picture2.ScaleMode:= smResize;
    False: Picture2.ScaleMode:= smNormal;
  end;
end;

procedure TFormMain.MenuCreateGamesListSNESClick(Sender: TObject);
begin
  CreateGamesList(TMenuItem(Sender).Tag);
end;

procedure TFormMain.ZipForgeProcessFileFailure(Sender: TObject;
  FileName: String; Operation: TZFProcessOperation; NativeError,
  ErrorCode: Integer; ErrorMessage: String; var Action: TZFAction);
begin
  if FormPreferences.DebuggerWindow.Checked then
     begin
       case Operation of
         poExtract: GenerateMessage('Error', Format('An error has occurred while extracting from file "%s".'+#13#10+#13#10+'%s', [FileName, ErrorMessage]), 2);
         poDelete: GenerateMessage('Error', Format('An error has occurred while deleting from file "%s".'+#13#10+#13#10+'%s', [FileName, ErrorMessage]), 2);
       end;
     end;
end;

procedure TFormMain.MenuPreferencesClick(Sender: TObject);
begin
  FormPreferences.ShowModal;
end;

procedure TFormMain.MenuHideFirstPictureClick(Sender: TObject);
begin
  Picture1.Visible:= not MenuHideFirstPicture.Checked;
  case MenuHideFirstPicture.Checked of
    True:
      begin
        Picture1.Bitmap.BeginUpdate;
        Picture1.Bitmap.Clear;
        Picture1.Bitmap.EndUpdate;
        Picture1.Changed;
      end;
    False: ShowPicture;
  end;
end;

procedure TFormMain.MenuVisitHomepageClick(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'http://www.mameworld.net/emuloader/', nil, nil, SW_SHOWNORMAL);
end;

procedure TFormMain.SendEMailClick(Sender: TObject);
begin
  ShellExecute(Handle, 'open', 'mailto:emuloader@mameworld.net?subject=Bugs Report / Questions', nil, nil, SW_SHOWNORMAL);
end;

procedure TFormMain.MenuShowSnapshotsClick(Sender: TObject);
begin
  PanelPictures.Visible:= MenuShowSnapshots.Checked;
  Splitter.Visible:= MenuShowSnapshots.Checked;
  if MenuShowSnapshots.Checked then
     ShowPicture;
end;

procedure TFormMain.MenuViewDocumentationClick(Sender: TObject);
begin
  if FileExists(AppPath+'docs\readme.htm') then
     ShellExecute(Handle, 'open', 'docs\readme.htm', nil, nil, SW_SHOWNORMAL);
end;

procedure TFormMain.MenuAddPlayStationCDGamesClick(Sender: TObject);
begin
  if not Assigned(FormPlayStationCDGameEditor) then
     FormPlayStationCDGameEditor:= TFormPlayStationCDGameEditor.Create(Self);
  FormPlayStationCDGameEditor.ShowModal;
  FreeAndNil(FormPlayStationCDGameEditor);
end;

procedure TFormMain.MenuAboutClick(Sender: TObject);
begin
  if not Assigned(FormAbout) then
     FormAbout:= TFormAbout.Create(Self);
  FormAbout.LabelFrontendVersion.Caption:= 'v'+GetFileInfo(Application.ExeName, VersionInfo[3]);
  FormAbout.ShowModal;
  FreeAndNil(FormAbout);
end;

procedure TFormMain.MenuSmoothPictureOffClick(Sender: TObject);
begin
  MenuSmoothPicture.Tag:= TMenuItem(Sender).Tag;
  Picture2.Bitmap.StretchFilter:= SmoothType[MenuSmoothPicture.Tag];
end;

procedure TFormMain.StatusBarPanels1Click(Sender: TObject);
begin
  RandomGame_Click(Self);
end;

procedure TFormMain.GamesTreeChange(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  Data: pGameInfo;
begin
  if (Sender.SelectedCount = 1) and (Sender.Visible) then
     begin
       SelectedNode:= Node;
       Data:= Sender.GetNodeData(Node);
       SelectedGameName:= Data.eDescription;
       SelectedGameID:= Data.eConsoleType;
       if not ProcessingConsole then
          begin
            if MenuShowSnapshots.Checked then
               ShowPicture;
          end;
     end;
end;

procedure TFormMain.GamesTreeCompareNodes(Sender: TBaseVirtualTree; Node1,
  Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
var
  Data1, Data2: pGameInfo;
begin
  Data1:= Sender.GetNodeData(Node1);
  Data2:= Sender.GetNodeData(Node2);
  case Column of
     0: Result:= CompareText(Data1.eDescription, Data2.eDescription);
     1: Result:= CompareText(Data1.eGameSize, Data2.eGameSize);
     2: Result:= CompareText(Data1.eGameFileName, Data2.eGameFileName);
     3: Result:= CompareText(Data1.eMediaDescription, Data2.eMediaDescription);
     4: Result:= CompareText(Data1.eSystemDescription, Data2.eSystemDescription);
  end;
end;

procedure TFormMain.GamesTreeFreeNode(Sender: TBaseVirtualTree;
  Node: PVirtualNode);
var
  Data: pGameInfo;
begin
  Data:= GamesTree.GetNodeData(Node);
  Finalize(Data^);
end;

procedure TFormMain.GamesTreeGetHint(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex;
  var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: WideString);
var
  Data: pGameInfo;
begin
  Data:= GamesTree.GetNodeData(Node);
  case Column of
    0: HintText:= Data.eDescription;
    1: HintText:= Data.eGameSize;
    2: HintText:= Data.eGameFileName;
    3: HintText:= Data.eMediaDescription;
    4: HintTexT:= Data.eSystemDescription;
  end;
end;

procedure TFormMain.GamesTreeGetImageIndex(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
  var Ghosted: Boolean; var ImageIndex: Integer);
var
  Data: pGameInfo;
begin
  if Kind in [ikNormal, ikSelected] then
     begin
       if Column = 0 then
          begin
            Data:= Sender.GetNodeData(Node);
            Data.eImageIndex:= Data.eConsoleType;
            ImageIndex:= Data.eImageIndex;
          end;
     end;
end;

procedure TFormMain.GamesTreeGetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: WideString);
var
  Data: pGameInfo;
begin
  Data:= Sender.GetNodeData(Node);
  case Column of
    0: CellText:= Data.eDescription;
    1: CellText:= Data.eGameSize;
    2: CellText:= Data.eGameFileName;
    3: CellText:= Data.eMediaDescription;
    4: CellTexT:= Data.eSystemDescription;
  end;
end;

procedure TFormMain.GamesTreeHeaderClick(Sender: TVTHeader;
  Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
  case Button of
    mbLeft:
      begin
        with Sender, Treeview do
        begin
          if SortColumn > NoColumn then
             Columns[SortColumn].Options:= Columns[SortColumn].Options + [coParentColor];

          if (SortColumn = NoColumn) or (SortColumn <> Column) then
          begin
            SortColumn:= Column;
            SortDirection:= sdAscending;
          end
          else
            if SortDirection = sdAscending then
              SortDirection:= sdDescending
            else
              SortDirection:= sdAscending;

          Columns[SortColumn].Color:= $F7F7F7;
          //if not FormPreferences.GamesBackgroundImageEnable.Checked then
          //   Columns[SortColumn].Color:= FormPreferences.SortedColumnColor.Selected; // }$F7F7F7;
          ColumnSorted:= SortColumn;
          ColumnsSort[ColumnSorted]:= Ord(SortDirection = sdDescending);
          SortTree(SortColumn, SortDirection, False);
          ScrollIntoView(SelectedNode, True);
        end;
      end;
    //mbRight: MenuColumnsEditor.Click;
  end;
end;

procedure TFormMain.GamesTreeIncrementalSearch(Sender: TBaseVirtualTree;
  Node: PVirtualNode; const SearchText: WideString; var Result: Integer);
var
  StringsText,
  EntryText: String;
  Data: pGameInfo;
begin
  StringsText:= SearchText;
  Data:= Sender.GetNodeData(Node);
  EntryText:= Data.eDescription;
  Result := AnsiStrLIComp(PChar(StringsText), PChar(EntryText), Length(StringsText));
end;

procedure TFormMain.MenuEmulatorSetupClick(Sender: TObject);
begin
  if not Assigned(FormEmulatorSetup) then
     FormEmulatorSetup:= TFormEmulatorSetup.Create(Self);
  FormEmulatorSetup.ShowModal;
  FreeAndNil(FormEmulatorSetup);
end;

procedure TFormMain.GamesTreeDblClick(Sender: TObject);
begin
  ExecuteGame;
end;

procedure TFormMain.GamesTreeKeyPress(Sender: TObject; var Key: Char);
begin
  ProcessPressedKey(Word(Key));
  //Key:= Word(0);
end;

procedure TFormMain.GameFilterAllConsolesClick(Sender: TObject);
begin
  if TMenuItem(Sender).Tag = ButtonGamesFilters.Tag then
     begin
       TMenuItem(Sender).Checked:= True;
       Exit;
     end;
  ButtonGamesFilters.Tag:= TMenuItem(Sender).Tag;
  ApplyFilters(ButtonGamesFilters.Tag, False);
end;

procedure TFormMain.MenuViewLargeGameIconsClick(Sender: TObject);
var
  Node: PVirtualNode;
begin
  GamesTree.BeginUpdate;
  case MenuViewLargeGameIcons.Checked of
    True:
      begin
        GamesTree.DefaultNodeHeight:= 34;
        GamesTree.Images:= ImageListGameIconBig;
      end;
    False:
      begin
        GamesTree.DefaultNodeHeight:= 18;
        GamesTree.Images:= ImageListGameIconSmall;
      end;
  end;
  if GamesTree.TotalCount > 0 then
     begin
       Node:= GamesTree.GetFirst;
       repeat
         GamesTree.NodeHeight[Node]:= GamesTree.DefaultNodeHeight;
         Node:= GamesTree.GetNext(Node);
       until (not Assigned(Node));
       GamesTree.EndUpdate;
       if GamesTree.SelectedCount > 0 then
          GamesTree.ScrollIntoView(SelectedNode, True);
     end;
  GamesTree.EndUpdate;
end;

end.

