unit uParentalLockEditor;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  ExtCtrls, StdCtrls, ComCtrls, ImgList, IniFiles, Menus,
  Buttons, VirtualTrees, ToolWin;

type
  pGameInfo_pl = ^TGameInfo_pl;
  TGameInfo_pl = record
    plImageIndex: Integer;
    plDescription,
    plName,
    plClone,
    plDriverName,
    plCategory,
    plVersionAdded: String;
    plSystem: ShortInt;
  end;

type
  TFormParentalLockEditor = class(TForm)
    PageControlLockOptions: TPageControl;
    TabSheetSearchGames: TTabSheet;
    ButtonBeginSearch: TButton;
    TabSheetProtectedGamesList: TTabSheet;
    LabelTotalGamesLocked: TLabel;
    ButtonDeleteSelectedLockedGames: TButton;
    ParentalLockImageList: TImageList;
    ProtectedList: TVirtualStringTree;
    TabSheetAdvancedUser: TTabSheet;
    ButtonDecryptFile: TButton;
    ButtonEncryptFile: TButton;
    ButtonPassword: TButton;
    ButtonReadFile: TButton;
    ButtonOk: TButton;
    ButtonCancel: TButton;
    LabelSearchKeywordsList: TGroupBox;
    SearchKeywords: TListBox;
    ButtonResetKeywords: TButton;
    ButtonDeleteSelectedKeywords: TButton;
    LabelGamesFound: TGroupBox;
    GamesFoundList: TVirtualStringTree;
    ButtonImportGamesFound: TButton;
    ButtonDeleteSelectedGamesFound: TButton;
    ButtonAddGames: TButton;
    GroupBox1: TGroupBox;
    SearchPatt_Description: TCheckBox;
    SearchPatt_Category: TCheckBox;
    Keyword: TEdit;
    ButtonAddKeyword: TButton;
    SearchPatt_Name: TCheckBox;
    SearchPatt_CloneOf: TCheckBox;
    SearchPatt_DriverName: TCheckBox;
    SearchPatt_Manufacturer: TCheckBox;
    ClearSearchList: TCheckBox;
    ButtonClearKeywords: TButton;
    ButtonRemoveDuplicates: TButton;
    procedure ButtonCancelClick(Sender: TObject);
    procedure ButtonOkClick(Sender: TObject);
    procedure FormKeyPress(Sender: TObject; var Key: Char);
    procedure SearchKeywordsKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure ButtonAddGamesClick(Sender: TObject);
    procedure ButtonAddKeywordClick(Sender: TObject);
    procedure ButtonDeleteSelectedKeywordsClick(Sender: TObject);
    procedure ButtonReadFileClick(Sender: TObject);
    procedure ButtonBeginSearchClick(Sender: TObject);
    procedure KeywordKeyPress(Sender: TObject; var Key: Char);
    procedure ButtonDeleteSelectedGamesFoundClick(Sender: TObject);
    procedure ButtonDeleteSelectedLockedGamesClick(Sender: TObject);
    procedure ButtonPasswordClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure ButtonDecryptFileClick(Sender: TObject);
    procedure GamesFoundListKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure GamesFoundListCompareNodes(Sender: TBaseVirtualTree; Node1,
      Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
    procedure GamesFoundListFreeNode(Sender: TBaseVirtualTree;
      Node: PVirtualNode);
    procedure GamesFoundListGetHint(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex;
      var LineBreakStyle: TVTTooltipLineBreakStyle;
      var HintText: WideString);
    procedure GamesFoundListGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure GamesFoundListGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure GamesFoundListHeaderClick(Sender: TVTHeader;
      Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure GamesFoundListIncrementalSearch(Sender: TBaseVirtualTree;
      Node: PVirtualNode; const SearchText: WideString;
      var Result: Integer);
    procedure ProtectedListKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure ProtectedListCompareNodes(Sender: TBaseVirtualTree; Node1,
      Node2: PVirtualNode; Column: TColumnIndex; var Result: Integer);
    procedure ProtectedListFreeNode(Sender: TBaseVirtualTree;
      Node: PVirtualNode);
    procedure ProtectedListGetHint(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex;
      var LineBreakStyle: TVTTooltipLineBreakStyle;
      var HintText: WideString);
    procedure ProtectedListGetImageIndex(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
      var Ghosted: Boolean; var ImageIndex: Integer);
    procedure ProtectedListGetText(Sender: TBaseVirtualTree;
      Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
      var CellText: WideString);
    procedure ProtectedListHeaderClick(Sender: TVTHeader;
      Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X,
      Y: Integer);
    procedure ProtectedListIncrementalSearch(Sender: TBaseVirtualTree;
      Node: PVirtualNode; const SearchText: WideString;
      var Result: Integer);
    procedure ButtonResetKeywordsClick(Sender: TObject);
    procedure ButtonImportGamesFoundClick(Sender: TObject);
    procedure ButtonEncryptFileClick(Sender: TObject);
    procedure ButtonClearKeywordsClick(Sender: TObject);
    procedure ButtonRemoveDuplicatesClick(Sender: TObject);
  private
    ProtectedListChanged: Boolean;
    procedure ReadLockedGamesDAT;
    procedure AddGamesToProtectedList;
    function UpdateLockGamesList: Boolean;
    procedure GetCategoryVersionInfo(GameName: String);
    { Private declarations }
  public
    HaveProtectedGames: Boolean;
    { Public declarations }
  end;

var
  FormParentalLockEditor: TFormParentalLockEditor;

implementation

uses uMain, uStatus, uParentalLockPasswordLogout, uPreferences, uCommon,
     uFilesUtil;

{$R *.dfm}

procedure TFormParentalLockEditor.GetCategoryVersionInfo(GameName: String);
begin
  case Assigned(FormMain.CategoriesList) of
    True:
      begin
        FormMain.mCategory:= FormMain.CategoriesList.ReadString('Category', Lowercase(GameName), '');
        FormMain.mVersionAdded:= FormMain.CategoriesList.ReadString('VerAdded', Lowercase(GameName), '');
      end;
    False:
      begin
        FormMain.mCategory:= '';
        FormMain.mVersionAdded:= '';
      end;
  end;
end;

procedure TFormParentalLockEditor.ReadLockedGamesDAT;
var
  LockedGamesDAT, GamesListDAT: THashedStringList;
  Loop: Integer;
  Node: PVirtualNode;
  Data: pGameInfo_pl;
begin
  if FileExists(FormMain.FrontendPath+'ini_files\parental_lock.ini') then
     begin
       Screen.Cursor:= crHourGlass;
       if HaveProtectedGames then
          begin
            LockedGamesDAT:= THashedStringList.Create;
            LockedGamesDAT.LoadFromFile(FormMain.FrontendPath+'ini_files\parental_lock.ini');
            if LockedGamesDAT.Count > 1 then
               begin
                 ProtectedList.BeginUpdate;
                 ProtectedList.Clear;
                 ProtectedList.NodeDataSize:= -1;
                 ProtectedList.EndUpdate;
               end;
            GamesListDAT:= THashedStringList.Create;
            GetGamesList(GamesListDAT, 1);
            GetGamesList(GamesListDAT, 2);
            GetGamesList(GamesListDAT, 3);
            GetGamesList(GamesListDAT, 4);
            GetGamesList(GamesListDAT, 5);
            GetGamesList(GamesListDAT, 6);
            ProtectedList.NodeDataSize:= SizeOf(TGameInfo_pl);
            ProtectedList.RootNodeCount:= 0;
            ProtectedList.BeginUpdate;
            for Loop:=0 to LockedGamesDAT.Count -1 do
                LockedGamesDAT[Loop]:= DecryptData(LockedGamesDAT[Loop]);
            FormMain.ParentalLockPasswordString:= LockedGamesDAT.Values['password'];
            for Loop:=0 to GamesListDAT.Count -1 do
            begin
              FormMain.GetROMFields(GamesListDAT, Loop);
              if LockedGamesDAT.IndexOfName(LowerCase(FormMain.mName)) <> -1 then
                 begin
                   Node:= ProtectedList.AddChild(nil);
                   Data:= ProtectedList.GetNodeData(Node);
                   Data.plImageIndex:= FormMain.mROMIdentification;
                   Data.plDescription:= FormMain.mDescription;
                   Data.plName:= FormMain.mName;
                   Data.plClone:= FormMain.mClone;
                   Data.plDriverName:= FormMain.mDriverName;
                   GetCategoryVersionInfo(FormMain.mName);
                   Data.plCategory:= FormMain.mCategory;
                   Data.plVersionAdded:= FormMain.mVersionAdded;
                   Data.plSystem:= FormMain.GetSystemID(FormMain.mROMIdentification);
                 end;
            end;
            ProtectedList.EndUpdate;
            ProtectedList.SortTree(0, sdAscending);
            FreeAndNil(LockedGamesDAT);
            FreeAndNil(GamesListDAT);
          end;
       Screen.Cursor:= crDefault;
     end;
end;

procedure TFormParentalLockEditor.AddGamesToProtectedList;
var
  GameFound: Boolean;
  Node_found, Node_protected: PVirtualNode;
  Data_found, Data_protected: pGameInfo_pl;
begin
  if GamesFoundList.TotalCount > 0 then
     begin
       FormStatus.LabelStatusType.Caption:= 'Add Games To Protected List';
       FormStatus.LabelMessage.Caption:= 'Verifying games to be added.';
       FormStatus.ProgressBar.MaxValue:= GamesFoundList.TotalCount-1;
       FormStatus.Show;
       FormStatus.Refresh;
       Screen.Cursor:= crHourGlass;
       ProtectedList.NodeDataSize:= SizeOf(TGameInfo_pl);
       if ProtectedList.TotalCount = 0 then
          ProtectedList.RootNodeCount:= 0;
       ProtectedList.BeginUpdate;
       Node_found:= GamesFoundList.GetFirst;
       repeat
         Data_found:= GamesFoundList.GetNodeData(Node_found);
         GameFound:= False;
         if ProtectedList.TotalCount > 0 then
            begin
              Node_protected:= ProtectedList.GetFirst;
              repeat
                Data_protected:= ProtectedList.GetNodeData(Node_protected);
                if ((Data_found.plName = Data_protected.plName) and
                    (Data_found.plClone = Data_protected.plClone) and
                    (Data_found.plSystem = Data_protected.plSystem)) then
                   GameFound:= True;
                Node_protected:= ProtectedList.GetNext(Node_protected);
              until (Node_protected = nil) or (GameFound);
            end;

         if not GameFound then
            begin
              Node_protected:= ProtectedList.AddChild(nil);
              Data_protected:= ProtectedList.GetNodeData(Node_protected);
              Data_protected.plImageIndex:= Data_found.plImageIndex;
              Data_protected.plDescription:= Data_found.plDescription;
              Data_protected.plName:= Data_found.plName;
              Data_protected.plClone:= Data_found.plClone;
              Data_protected.plDriverName:= Data_found.plDriverName;
              Data_protected.plCategory:= Data_found.plCategory;
              Data_protected.plVersionAdded:= Data_found.plVersionAdded;
              Data_protected.plSystem:= Data_found.plSystem;
              ProtectedListChanged:= True;
            end;
         FormStatus.ProgressBar.Progress:= Node_found.Index;
         Node_found:= GamesFoundList.GetNext(Node_found);
       until Node_found = nil;
       ProtectedList.SortTree(0, sdAscending);
       ProtectedList.EndUpdate;
       FormStatus.Close;
       Screen.Cursor:= crDefault;
     end;
  LabelTotalGamesLocked.Caption:= Format('%u Games', [ProtectedList.TotalCount]);
end;

function TFormParentalLockEditor.UpdateLockGamesList: Boolean;
var
  LockedGamesDAT: THashedStringList;
  Node: PVirtualNode;
  Data: pGameInfo_pl;
begin
  Result:= False;
  if ProtectedList.TotalCount > 0 then
     begin
        if FormMain.ParentalLockPasswordString = '' then
           begin
             GenerateMessage('Error', 'Password is empty! You need to set a password to continue.', 2);
             Exit;
           end;
        Randomize;
        LockedGamesDAT:= THashedStringList.Create;
        LockedGamesDAT.BeginUpdate;
        LockedGamesDAT.Clear;
        LockedGamesDAT.Add(EncryptData('password='+FormMain.ParentalLockPasswordString));
        Node:= ProtectedList.GetFirst;
        repeat
          Data:= ProtectedList.GetNodeData(Node);
          LockedGamesDAT.Add(EncryptData(Data.plName+'='+FormMain.GetSystemFolderName(Data.plSystem)));
          Node:= ProtectedList.GetNext(Node);
        until Node = nil;
        LockedGamesDAT.EndUpdate;
        if FileExists(FormMain.FrontendPath+'ini_files\parental_lock.ini') then
           DeleteFile(FormMain.FrontendPath+'ini_files\parental_lock.ini');
        LockedGamesDAT.SaveToFile(FormMain.FrontendPath+'ini_files\parental_lock.ini');
        FreeAndNil(LockedGamesDAT);
        Result:= True;
     end
  else
     GenerateMessage('No Games Found', 'There is no games in the protected list! You must select at least one game for this to work.', 2);
end;

procedure TFormParentalLockEditor.ButtonCancelClick(Sender: TObject);
begin
  Close;
end;

procedure TFormParentalLockEditor.ButtonOkClick(Sender: TObject);
begin
  if not ButtonOk.Focused then
     Exit;
  if ProtectedList.TotalCount = 0 then
     begin
       if GenerateMessage('Warning', 'The protected list is empty. The file "'+
                  FormMain.FrontendPath+'ini_files\parental_lock.ini" will be '+
                  'deleted.'+#13#10+'Continue ?', 1) = mrNo then
          Exit
       else
         begin
           DeleteFile(FormMain.FrontendPath+'ini_files\parental_lock.ini');
           ProtectedListChanged:= False;
           Close;
           Exit;
         end;
     end;
  if FormMain.ParentalLockPasswordString = '' then
     ButtonPassword.Click;
  if UpdateLockGamesList then
     begin
       FormMain.SetLockedGamesList(False);
       ProtectedListChanged:= False;
       Close;
     end;
end;

procedure TFormParentalLockEditor.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #27 then
     ButtonCancel.OnClick(Self);
end;

procedure TFormParentalLockEditor.SearchKeywordsKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if Key = VK_DELETE then
     ButtonDeleteSelectedKeywords.OnClick(Self);
end;

procedure TFormParentalLockEditor.ButtonAddGamesClick(Sender: TObject);
begin
  AddGamesToProtectedList;
  TabSheetProtectedGamesList.Show;
end;

procedure TFormParentalLockEditor.ButtonAddKeywordClick(Sender: TObject);
begin
  if Keyword.Text <> '' then
     begin
       if SearchKeywords.Items.IndexOf(LowerCase(Keyword.Text)) = -1 then
          begin
            SearchKeywords.Items.BeginUpdate;
            SearchKeywords.Items.Add(LowerCase(Keyword.Text));
            SearchKeywords.Items.EndUpdate;
          end;
     end;
  Keyword.SetFocus;
end;

procedure TFormParentalLockEditor.ButtonDeleteSelectedKeywordsClick(
  Sender: TObject);
begin
  SearchKeywords.Items.BeginUpdate;
  SearchKeywords.DeleteSelected;
  SearchKeywords.Items.EndUpdate;
end;

procedure TFormParentalLockEditor.ButtonReadFileClick(Sender: TObject);
begin
  ReadLockedGamesDAT;
end;

procedure TFormParentalLockEditor.ButtonBeginSearchClick(Sender: TObject);
var
  GamesListDAT: THashedStringList;
  AddGame, GameFound: Boolean;
  Loop, Loop2: Integer;
  Node: PVirtualNode;
  Data: pGameInfo_pl;

  function CheckGame: Boolean;
  begin
    Result:= False;
    if SearchPatt_Description.Checked then
       Result:= (Pos((LowerCase(SearchKeywords.Items.Strings[Loop2])), (LowerCase(FormMain.mDescription))) > 0);

    if (not Result) and SearchPatt_Manufacturer.Checked then
       Result:= (Pos((LowerCase(SearchKeywords.Items.Strings[Loop2])), (LowerCase(FormMain.mManufacturer))) > 0);

    if (not Result) and SearchPatt_Category.Checked then
       begin
         if FormMain.mCategory <> '' then
            Result:= (Pos((LowerCase(SearchKeywords.Items.Strings[Loop2])), (LowerCase(FormMain.mCategory))) > 0);
       end;

    if (not Result) and SearchPatt_Name.Checked then
       Result:= SameText(SearchKeywords.Items.Strings[Loop2], FormMain.mName);

    if (not Result) and SearchPatt_CloneOf.Checked then
       Result:= SameText(SearchKeywords.Items.Strings[Loop2], FormMain.mClone);

    if (not Result) and SearchPatt_DriverName.Checked then
       Result:= SameText(SearchKeywords.Items.Strings[Loop2]+'.c', FormMain.mDriverName);
  end;

begin
  Screen.Cursor:= crHourGlass;
  FormStatus.LabelStatusType.Caption:= 'Search Games';
  FormStatus.LabelMessage.Caption:= 'Scanning games based on keywords.';
  FormStatus.Show;
  FormStatus.Refresh;
  GamesListDAT:= THashedStringList.Create;
  GetGamesList(GamesListDAT, 1);
  GetGamesList(GamesListDAT, 2);
  GetGamesList(GamesListDAT, 3);
  GetGamesList(GamesListDAT, 4);
  GetGamesList(GamesListDAT, 5);
  GetGamesList(GamesListDAT, 6);
  FormStatus.ProgressBar.MaxValue:= GamesListDAT.Count-1;

  GamesFoundList.NodeDataSize:= SizeOf(TGameInfo_pl);
  if ClearSearchList.Checked then
     GamesFoundList.RootNodeCount:= 0;
  GamesFoundList.BeginUpdate;

  if GamesFoundList.TotalCount = 0 then
     begin
       for Loop:=0 to GamesListDAT.Count -1 do
       begin
         AddGame:= False;
         FormMain.GetROMFields(GamesListDAT, Loop);
         for Loop2:=0 to SearchKeywords.Items.Count -1 do
         begin
           GetCategoryVersionInfo(FormMain.mName);
           if CheckGame then
              begin
                Node:= GamesFoundList.AddChild(nil);
                Data:= GamesFoundList.GetNodeData(Node);
                Data.plImageIndex:= FormMain.mROMIdentification;
                Data.plDescription:= FormMain.mDescription;
                Data.plName:= FormMain.mName;
                Data.plClone:= FormMain.mClone;
                Data.plDriverName:= FormMain.mDriverName;
                Data.plCategory:= FormMain.mCategory;
                Data.plVersionAdded:= FormMain.mVersionAdded;
                Data.plSystem:= FormMain.GetSystemID(FormMain.mROMIdentification);
                Break;
              end;
         end;
         FormStatus.ProgressBar.Progress:= Loop;
       end;
     end
  else
     begin
       for Loop:=0 to GamesListDAT.Count -1 do
       begin
         AddGame:= False;
         GameFound:= False;
         FormMain.GetROMFields(GamesListDAT, Loop);
         for Loop2:=0 to SearchKeywords.Items.Count -1 do
         begin
           GetCategoryVersionInfo(FormMain.mName);
           if CheckGame then
              begin
                AddGame:= True;
                Break;
              end;
         end;

         Node:= GamesFoundList.GetFirst;
         repeat
           Data:= GamesFoundList.GetNodeData(Node);
           GameFound:= ((FormMain.mName = Data.plName) and
                        (FormMain.mClone = Data.plClone) and
                        (FormMain.GetSystemID(FormMain.mROMIdentification) = Data.plSystem));
           Node:= GamesFoundList.GetNext(Node);
         until (Node = nil) or (GameFound);

         if (AddGame) and (not GameFound) then
            begin
              Node:= GamesFoundList.AddChild(nil);
              Data:= GamesFoundList.GetNodeData(Node);
              Data.plImageIndex:= FormMain.mROMIdentification;
              Data.plDescription:= FormMain.mDescription;
              Data.plName:= FormMain.mName;
              Data.plClone:= FormMain.mClone;
              Data.plDriverName:= FormMain.mDriverName;
              Data.plCategory:= FormMain.mCategory;
              Data.plVersionAdded:= FormMain.mVersionAdded;
              Data.plSystem:= FormMain.GetSystemID(FormMain.mROMIdentification);
            end;
         FormStatus.ProgressBar.Progress:= Loop;
       end;
     end;
  LabelGamesFound.Caption:= Format('Games Found [%u]', [GamesFoundList.TotalCount]);
  GamesFoundList.SortTree(0, sdAscending);
  GamesFoundList.EndUpdate;
  FreeAndNil(GamesListDAT);
  FormStatus.Close;
  Screen.Cursor:= crDefault;
end;

procedure TFormParentalLockEditor.KeywordKeyPress(Sender: TObject;
  var Key: Char);
begin
  if Key = #13 then
     ButtonAddKeyword.OnClick(Self);
end;

procedure TFormParentalLockEditor.ButtonDeleteSelectedGamesFoundClick(
  Sender: TObject);
begin
  if GamesFoundList.SelectedCount = 0 then
     begin
       GenerateMessage('Error', 'There are no games selected!', 2);
       Exit;
     end;
  if GenerateMessage('Delete Selected Games', 'Delete selected games from the list. Are you sure ?', 1) = mrYes then
     begin
       GamesFoundList.BeginUpdate;
       GamesFoundList.DeleteSelectedNodes;
       GamesFoundList.EndUpdate;
       LabelGamesFound.Caption:= Format('Games Found [%u]', [GamesFoundList.TotalCount]);
     end;
end;

procedure TFormParentalLockEditor.ButtonDeleteSelectedLockedGamesClick(
  Sender: TObject);
begin
  if ProtectedList.SelectedCount = 0 then
     begin
       GenerateMessage('Error', 'There are no games selected!', 2);
       Exit;
     end;
  if GenerateMessage('Delete Selected Games', 'Delete selected games from the protected list. Are you sure ?', 1) = mrYes then
     begin
       ProtectedList.BeginUpdate;
       ProtectedList.DeleteSelectedNodes;
       ProtectedList.EndUpdate;
       LabelTotalGamesLocked.Caption:= Format('%u Games', [ProtectedList.TotalCount]);
       ProtectedListChanged:= True;
     end;
end;

procedure TFormParentalLockEditor.ButtonPasswordClick(Sender: TObject);
begin
  if not Assigned(FormParentalLockPasswordLogout) then
     FormParentalLockPasswordLogout:=TFormParentalLockPasswordLogout.Create(nil);
  FormParentalLockPasswordLogout.Password.Tag:= 1;
  FormParentalLockPasswordLogout.ShowModal;
  FreeAndNil(FormParentalLockPasswordLogout);
end;

procedure TFormParentalLockEditor.FormShow(Sender: TObject);
begin
  FormMain.SetAlphaBlendTreeView(GamesFoundList);
  FormMain.SetAlphaBlendTreeView(ProtectedList);
  if not Assigned(FormMain.CategoriesList) then
     begin
       if FormMain.ValidateFile(FormMain.FrontendPath+'ini_files\catver.ini') then
          FormMain.CategoriesList:= TMemIniFile.Create(FormMain.FrontendPath+'ini_files\catver.ini');
     end;
  ReadLockedGamesDAT;
  LabelTotalGamesLocked.Caption:= Format('%u Games', [ProtectedList.TotalCount]);
  Keyword.SetFocus;
end;

procedure TFormParentalLockEditor.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  if ProtectedListChanged then
     begin
       if GenerateMessage('Games List Changed', 'The protected list has changed! Cancel the changes ?', 1) = mrNo then
          CanClose:= False;
     end;
  if CanClose then
     begin
       if Assigned(FormMain.CategoriesList) then
          FreeAndNil(FormMain.CategoriesList);
       FormMain.ClearTreeView(GamesFoundList);
       FormMain.ClearTreeView(ProtectedList);
     end;
end;

procedure TFormParentalLockEditor.ButtonDecryptFileClick(Sender: TObject);
var
  LockedGamesDAT: THashedStringList;
  Loop: Integer;
begin
  if FileExists(FormMain.FrontendPath+'ini_files\parental_lock.ini') then
     begin
       LockedGamesDAT:= THashedStringList.Create;
       LockedGamesDAT.LoadFromFile(FormMain.FrontendPath+'ini_files\parental_lock.ini');
       if LockedGamesDAT.Count > 0 then
          begin
            LockedGamesDAT.BeginUpdate;
            for Loop:=0 to LockedGamesDAT.Count -1 do
                LockedGamesDAT[Loop]:= DecryptData(LockedGamesDAT[Loop]);
            LockedGamesDAT.EndUpdate;
            FormMain.GamesListSaveDialog.FilterIndex:= 3;
            if FormMain.GamesListSaveDialog.Execute then
               LockedGamesDAT.SaveToFile(FormMain.GamesListSaveDialog.FileName);
          end;
       FreeAndNil(LockedGamesDAT);
     end
  else
     GenerateMessage('Error', 'File "'+FormMain.FrontendPath+'ini_files\parental_lock.ini" '+
                     'not found.', 2);
end;

procedure TFormParentalLockEditor.GamesFoundListKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if Key = VK_DELETE then
     ButtonDeleteSelectedGamesFound.OnClick(Self);
end;

procedure TFormParentalLockEditor.GamesFoundListCompareNodes(
  Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
  Column: TColumnIndex; var Result: Integer);
var
  Data1, Data2: pGameInfo_pl;
begin
  Data1:= Sender.GetNodeData(Node1);
  Data2:= Sender.GetNodeData(Node2);
  case Column of
    0: Result:= CompareText(Data1.plDescription, Data2.plDescription);
    1: Result:= CompareText(Data1.plCategory, Data2.plCategory);
    2: Result:= CompareText(Data1.plVersionAdded, Data2.plVersionAdded);
    3: Result:= CompareText(Data1.plName, Data2.plName);
    4: Result:= CompareText(Data1.plClone, Data2.plClone);
    5: Result:= CompareText(Data1.plDriverName, Data2.plDriverName);
    6: Result:= CompareIntValue(Data1.plSystem, Data2.plSystem);
  end;
end;

procedure TFormParentalLockEditor.GamesFoundListFreeNode(
  Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Data: pGameInfo_pl;
begin
  Data:= GamesFoundList.GetNodeData(Node);
  Finalize(Data^);
end;

procedure TFormParentalLockEditor.GamesFoundListGetHint(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: WideString);
var
  Data: pGameInfo_pl;
begin
  Data:= GamesFoundList.GetNodeData(Node);
  case Column of
    0: HintText:= Data.plDescription;
    1: HintText:= Data.plCategory;
    2: HintText:= Data.plVersionAdded;
    3: HintText:= Data.plName;
    4: HintText:= Data.plClone;
    5: HintText:= Data.plDriverName;
    6: HintText:= FormMain.GetSystemIniSection(Data.plSystem);
  end;
end;

procedure TFormParentalLockEditor.GamesFoundListGetImageIndex(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
  Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
var
  Data: pGameInfo_pl;
begin
  if Kind in [ikNormal, ikSelected] then
     begin
       if Column = 0 then
          begin
            Data:= Sender.GetNodeData(Node);
            ImageIndex:= Data.plImageIndex;
          end;
     end;
end;

procedure TFormParentalLockEditor.GamesFoundListGetText(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: WideString);
var
  Data: pGameInfo_pl;
begin
  Data:= Sender.GetNodeData(Node);
  case Column of
    0: CellText:= Data.plDescription;
    1: CellText:= Data.plCategory;
    2: CellText:= Data.plVersionAdded;
    3: CellText:= Data.plName;
    4: CellText:= Data.plClone;
    5: CellText:= Data.plDriverName;
    6: CellText:= FormMain.GetSystemIniSection(Data.plSystem);
  end;
end;

procedure TFormParentalLockEditor.GamesFoundListHeaderClick(
  Sender: TVTHeader; Column: TColumnIndex; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
     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:= $00F7F7F7;
         SortTree(SortColumn, SortDirection, False);
       end;
     end;
end;

procedure TFormParentalLockEditor.GamesFoundListIncrementalSearch(
  Sender: TBaseVirtualTree; Node: PVirtualNode;
  const SearchText: WideString; var Result: Integer);
var
  StringsText,
  EntryText: String;
  Data: pGameInfo_pl;
begin
  StringsText:= SearchText;
  Data:= Sender.GetNodeData(Node);
  EntryText:= Data.plDescription;
  Result:= AnsiStrLIComp(PChar(StringsText), PChar(EntryText), Length(StringsText));
end;

procedure TFormParentalLockEditor.ProtectedListKeyUp(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if Key = VK_DELETE then
     ButtonDeleteSelectedLockedGames.OnClick(Self);
end;

procedure TFormParentalLockEditor.ProtectedListCompareNodes(
  Sender: TBaseVirtualTree; Node1, Node2: PVirtualNode;
  Column: TColumnIndex; var Result: Integer);
var
  Data1, Data2: pGameInfo_pl;
begin
  Data1:= Sender.GetNodeData(Node1);
  Data2:= Sender.GetNodeData(Node2);
  case Column of
    0: Result:= CompareText(Data1.plDescription, Data2.plDescription);
    1: Result:= CompareText(Data1.plCategory, Data2.plCategory);
    2: Result:= CompareText(Data1.plVersionAdded, Data2.plVersionAdded);
    3: Result:= CompareText(Data1.plName, Data2.plName);
    4: Result:= CompareText(Data1.plClone, Data2.plClone);
    5: Result:= CompareText(Data1.plDriverName, Data2.plDriverName);
    6: Result:= CompareIntValue(Data1.plSystem, Data2.plSystem);
  end;
end;

procedure TFormParentalLockEditor.ProtectedListFreeNode(
  Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  Data: pGameInfo_pl;
begin
  Data:= ProtectedList.GetNodeData(Node);
  Finalize(Data^);
end;

procedure TFormParentalLockEditor.ProtectedListGetHint(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  var LineBreakStyle: TVTTooltipLineBreakStyle; var HintText: WideString);
var
  Data: pGameInfo_pl;
begin
  Data:= ProtectedList.GetNodeData(Node);
  case Column of
    0: HintText:= Data.plDescription;
    1: HintText:= Data.plCategory;
    2: HintText:= Data.plVersionAdded;
    3: HintText:= Data.plName;
    4: HintText:= Data.plClone;
    5: HintText:= Data.plDriverName;
    6: HintText:= FormMain.GetSystemIniSection(Data.plSystem);
  end;
end;

procedure TFormParentalLockEditor.ProtectedListGetImageIndex(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind;
  Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer);
var
  Data: pGameInfo_pl;
begin
  if Kind in [ikNormal, ikSelected] then
     begin
       if Column = 0 then
          begin
            Data:= Sender.GetNodeData(Node);
            ImageIndex:= Data.plImageIndex;
          end;
     end;
end;

procedure TFormParentalLockEditor.ProtectedListGetText(
  Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: WideString);
var
  Data: pGameInfo_pl;
begin
  Data:= Sender.GetNodeData(Node);
  case Column of
    0: CellText:= Data.plDescription;
    1: CellText:= Data.plCategory;
    2: CellText:= Data.plVersionAdded;
    3: CellText:= Data.plName;
    4: CellText:= Data.plClone;
    5: CellText:= Data.plDriverName;
    6: CellText:= FormMain.GetSystemIniSection(Data.plSystem);
  end;
end;

procedure TFormParentalLockEditor.ProtectedListHeaderClick(
  Sender: TVTHeader; Column: TColumnIndex; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
     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:= $00F7F7F7;
         SortTree(SortColumn, SortDirection, False);
       end;
     end;
end;

procedure TFormParentalLockEditor.ProtectedListIncrementalSearch(
  Sender: TBaseVirtualTree; Node: PVirtualNode;
  const SearchText: WideString; var Result: Integer);
var
  StringsText,
  EntryText: String;
  Data: pGameInfo_pl;
begin
  StringsText:= SearchText;
  Data:= Sender.GetNodeData(Node);
  EntryText:= Data.plDescription;
  Result := AnsiStrLIComp(PChar(StringsText), PChar(EntryText), Length(StringsText));
end;

procedure TFormParentalLockEditor.ButtonResetKeywordsClick(
  Sender: TObject);
begin
  SearchKeywords.Items.BeginUpdate;
  SearchKeywords.Items.Clear;
  SearchKeywords.Items.Add('adult');
  SearchKeywords.Items.Add('mature');
  SearchKeywords.Items.Add('nude');
  SearchKeywords.Items.Add('nudity');
  SearchKeywords.Items.EndUpdate;
end;

procedure TFormParentalLockEditor.ButtonImportGamesFoundClick(
  Sender: TObject);
var
  extFile: THashedStringList;
  ini: TMemIniFile;
  Loop: Integer;
  Node: PVirtualNode;
  Data: pGameInfo_pl;
  AddGame, FoundMatch: Boolean;
  system: ShortInt;
  tmpString: String;
begin
  FormMain.OpenDialog.FilterIndex:= 5;
  FormMain.OpenDialog.Title:= 'Select a games list file to import';
  if FormMain.OpenDialog.Execute then
     begin
       ini:= TMemIniFile.Create(FormMain.OpenDialog.FileName);
       extFile:= THashedStringList.Create;
       ini.ReadSectionValues('games', extFile);
       FreeAndNil(ini);
       if extFile.Count > 0 then
          begin
            FormStatus.LabelStatusType.Caption:= 'Import Games';
            FormStatus.LabelMessage.Caption:= 'Parsing games list.';
            FormStatus.ProgressBar.MaxValue:= extFile.Count-1;
            FormStatus.Show;
            FormStatus.Refresh;
            // fix stringlist
            extFile.BeginUpdate;
            if extFile[0] = '[info_file]' then
               begin
                 extFile.Delete(3);
                 extFile.Delete(2);
                 extFile.Delete(1);
                 extFile.Delete(0);
               end;
            for Loop:=extFile.Count-1 downto 0 do
            begin
              tmpString:= extFile[Loop];
              if (tmpString = '') or (tmpString[1] = '[') then
                 extFile.Delete(Loop);
            end;
            extFile.EndUpdate;
            // end of fix stringlist

            GamesFoundList.NodeDataSize:= SizeOf(TGameInfo_pl);
            if GamesFoundList.TotalCount = 0 then
               GamesFoundList.RootNodeCount:= 0;
            GamesFoundList.BeginUpdate;
            for Loop:=0 to extFile.Count-1 do
            begin
              FormMain.GetROMFields(extFile, Loop);
              system:= FormMain.GetSystemID(FormMain.mROMIdentification);
              if system = -1 then
                 begin
                   GenerateMessage('Error', 'Could not extract the system type. File "'+
                                   FormMain.OpenDialog.FileName+'" might be corrupted!', 2);
                   AddGame:= False;
                 end
              else
                 begin
                   AddGame:= GamesFoundList.TotalCount = 0;
                   if not AddGame then
                      begin
                        Node:= GamesFoundList.GetFirst;
                        FoundMatch:= False;
                        repeat
                          Data:= GamesFoundList.GetNodeData(Node);
                          if ((Data.plImageIndex = FormMain.mROMIdentification) and
                              (Data.plName = FormMain.mName) and
                              (Data.plClone = FormMain.mClone) and
                              (Data.plSystem = system)) then
                              FoundMatch:= True;
                          Node:= GamesFoundList.GetNext(Node);
                        until (Node = nil) or (FoundMatch);
                        AddGame:= (not FoundMatch);
                      end;
                 end;
              if AddGame then
                 begin
                   Node:= GamesFoundList.AddChild(nil);
                   Data:= GamesFoundList.GetNodeData(Node);
                   Data.plImageIndex:= FormMain.mROMIdentification;
                   Data.plDescription:= FormMain.mDescription;
                   Data.plName:= FormMain.mName;
                   Data.plClone:= FormMain.mClone;
                   Data.plDriverName:= FormMain.mDriverName;
                   GetCategoryVersionInfo(FormMain.mName);
                   Data.plCategory:= FormMain.mCategory;
                   Data.plVersionAdded:= FormMain.mVersionAdded;
                   Data.plSystem:= system;
                 end;
              FormStatus.ProgressBar.Progress:= Loop;
            end;
            GamesFoundList.EndUpdate;
            FreeAndNil(extFile);
            FormStatus.Close;
            LabelGamesFound.Caption:= Format('Games Found [%u]', [GamesFoundList.TotalCount]);
            GamesFoundList.SortTree(0, sdAscending);
            GamesFoundList.EndUpdate;
          end
       else
          GenerateMessage('Error', 'Could not find any games in "'+
                          FormMain.OpenDialog.FileName+'". Please make sure that the '+
                          'file is compatible with Emu Loader v4.7 format.', 2);
     end;
end;

procedure TFormParentalLockEditor.ButtonEncryptFileClick(Sender: TObject);
var
  LockedGamesDAT: THashedStringList;
  Loop: Integer;
begin
  if GenerateMessage('Information', 'This function will encrypt a parental lock file '+
                     'that is not encrypted. Password entry must be present and it needs '+
                     'to be in the first line of the file'+#13#10+'The encrypted filename will '+
                     'be the same but with a "_encrypted" extended extension (without quotes)'+
                     #13#10+'Continue ?', 1) = mrNo then
                     Exit;
  FormMain.OpenDialog.FilterIndex:= 5;
  if FormMain.OpenDialog.Execute then
     begin
       LockedGamesDAT:= THashedStringList.Create;
       LockedGamesDAT.LoadFromFile(FormMain.OpenDialog.FileName);
       if LockedGamesDAT.Count > 0 then
          begin
            LockedGamesDAT.BeginUpdate;
            for Loop:=0 to LockedGamesDAT.Count -1 do
                LockedGamesDAT[Loop]:= EncryptData(LockedGamesDAT[Loop]);
            LockedGamesDAT.EndUpdate;
            LockedGamesDAT.SaveToFile(FormMain.OpenDialog.FileName+'_encrypted');
          end
       else
          GenerateMessage('Error', 'File "'+FormMain.OpenDialog.FileName+'" is empty! '+
                          'Operation aborted...', 2);
       FreeAndNil(LockedGamesDAT);
       GenerateMessage('Success', 'Encryption process completed. File "'+FormMain.OpenDialog.FileName+
                       '_encrypted" created.', 2);
     end;
end;

procedure TFormParentalLockEditor.ButtonClearKeywordsClick(
  Sender: TObject);
begin
  SearchKeywords.Clear;
end;

procedure TFormParentalLockEditor.ButtonRemoveDuplicatesClick(
  Sender: TObject);
var
  FoundProtected: Boolean;
  Node_found, Node_protected: PVirtualNode;
  Data_found, Data_protected: pGameInfo_pl;
begin
  if (GamesFoundList.TotalCount > 0) and (ProtectedList.TotalCount > 0) then
     begin
       Screen.Cursor:= crHourGlass;
       GamesFoundList.ClearSelection;
       GamesFoundList.BeginUpdate;
       Node_found:= GamesFoundList.GetFirst;
       repeat
         Data_found:= GamesFoundList.GetNodeData(Node_found);
         FoundProtected:= False;
         Node_protected:= ProtectedList.GetFirst;
         repeat
           Data_protected:= ProtectedList.GetNodeData(Node_protected);
           if ((Data_found.plName = Data_protected.plName) and
               (Data_found.plClone = Data_protected.plClone) and
               (Data_found.plSystem = Data_protected.plSystem)) then
              begin
                GamesFoundList.Selected[Node_found]:= True;
                FoundProtected:= True;
              end;
           Node_protected:= ProtectedList.GetNext(Node_protected);
         until (Node_protected = nil) or (FoundProtected);
         FormStatus.ProgressBar.Progress:= Node_found.Index;
         Node_found:= GamesFoundList.GetNext(Node_found);
       until Node_found = nil;
       if FoundProtected then
          begin
            if GamesFoundList.SelectedCount > 0 then
               GamesFoundList.DeleteSelectedNodes;
          end;
       GamesFoundList.SortTree(0, sdAscending);
       GamesFoundList.EndUpdate;
       Screen.Cursor:= crDefault;
       LabelGamesFound.Caption:= Format('Games Found [%u]', [GamesFoundList.TotalCount]);
     end;
end;

end.
