Files
tim2view/umain.pas
2015-08-11 15:52:38 +03:00

1267 lines
34 KiB
ObjectPascal

unit umain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LazUTF8, LazFileUtils, Forms, Dialogs, ActnList,
Menus, StdCtrls, ExtCtrls, ComCtrls, Grids, ExtDlgs,
uscanresult, uscanthread, usettings, utim, udrawtim, types, Controls;
{$INCLUDE todos.inc}
type
{ TfrmMain }
TfrmMain = class(TForm)
actAbout: TAction;
actAddToSendto: TAction;
actChangeClutIdx: TAction;
actCloseFile: TAction;
actCloseFiles: TAction;
actExit: TAction;
actExtractPngs: TAction;
actExtractTim: TAction;
actExtractTims: TAction;
actChangeFile: TAction;
actChangeBackColor: TAction;
actExtractTimsAll: TAction;
actExtractPngsAll: TAction;
actShowFileInfo: TAction;
actPngImport: TAction;
actStopScan: TAction;
actOpenLab: TAction;
actOpenRepo: TAction;
actReplaceTim: TAction;
actReturnFocus: TAction;
actScanDir: TAction;
actScanFile: TAction;
actList: TActionList;
actStretch: TAction;
actPngExport: TAction;
btnShowClut: TButton;
btnStopScan: TButton;
cbbBitMode: TComboBox;
cbbCLUT: TComboBox;
cbbFiles: TComboBox;
cbbTranspMode: TComboBox;
dlgColor: TColorDialog;
dlgOpenFile: TOpenDialog;
dlgSavePNG: TSavePictureDialog;
dlgSaveFile: TSaveDialog;
mnExport: TMenuItem;
mnExtractAllTimsAll: TMenuItem;
mnExtractAllPngsAll: TMenuItem;
N22: TMenuItem;
mnExtractAllPngs3: TMenuItem;
mnExtractAllTims3: TMenuItem;
N20: TMenuItem;
mnSaveTIM1: TMenuItem;
grdClut: TDrawGrid;
imgTim: TImage;
lblClutHint: TLabel;
lblStatus: TLabel;
MenuItem1: TMenuItem;
mnImportPng1: TMenuItem;
mnShowTimInfo: TMenuItem;
mnImportPng: TMenuItem;
mnChangeBackColor2: TMenuItem;
N8: TMenuItem;
mnChangeBackColor: TMenuItem;
mnSaveAsTim: TMenuItem;
mnExtractAllPngs2: TMenuItem;
mnExtractAllTims2: TMenuItem;
N7: TMenuItem;
mnStretchImage: TMenuItem;
mnSaveAsPng: TMenuItem;
mnAbout: TMenuItem;
mnAssociate: TMenuItem;
mnCloseAllFiles: TMenuItem;
mnCloseFile: TMenuItem;
mnExit: TMenuItem;
mnFile: TMenuItem;
mmMain: TMainMenu;
mnHelp: TMenuItem;
mnOptions: TMenuItem;
mnReplaceIn: TMenuItem;
mnSaveTIM: TMenuItem;
mnSaveToPNG: TMenuItem;
mnScanDir: TMenuItem;
mnScanFile: TMenuItem;
mnSite: TMenuItem;
mnStretch: TMenuItem;
mnSVN: TMenuItem;
mnImage: TMenuItem;
N1: TMenuItem;
N3: TMenuItem;
N5: TMenuItem;
dlgOpenPNG: TOpenPictureDialog;
pnlClut: TPanel;
pbProgress: TProgressBar;
pnlImage: TPanel;
pnlImageOptions: TPanel;
pnlList: TPanel;
pnlStatus: TPanel;
pnlMain: TPanel;
pmImage: TPopupMenu;
pmList: TPopupMenu;
mnReplaceIn1: TMenuItem;
mnSaveToPNG1: TMenuItem;
dlgSelectDir: TSelectDirectoryDialog;
splMain: TSplitter;
grdTimsList: TStringGrid;
tblTimInfo: TStringGrid;
procedure actAboutExecute(Sender: TObject);
procedure actAddToSendtoExecute(Sender: TObject);
procedure actChangeBackColorExecute(Sender: TObject);
procedure actChangeClutIdxExecute(Sender: TObject);
procedure actChangeFileExecute(Sender: TObject);
procedure actCloseFileExecute(Sender: TObject);
procedure actCloseFilesExecute(Sender: TObject);
procedure actExitExecute(Sender: TObject);
procedure actExtractPngsAllExecute(Sender: TObject);
procedure actExtractPngsExecute(Sender: TObject);
procedure actExtractTimExecute(Sender: TObject);
procedure actExtractTimsAllExecute(Sender: TObject);
procedure actExtractTimsExecute(Sender: TObject);
procedure actOpenLabExecute(Sender: TObject);
procedure actOpenRepoExecute(Sender: TObject);
procedure actPngImportExecute(Sender: TObject);
procedure actReplaceTimExecute(Sender: TObject);
procedure actReturnFocusExecute(Sender: TObject);
procedure actScanDirExecute(Sender: TObject);
procedure actScanFileExecute(Sender: TObject);
procedure actShowFileInfoExecute(Sender: TObject);
procedure actStopScanExecute(Sender: TObject);
procedure actStretchExecute(Sender: TObject);
procedure actPngExportExecute(Sender: TObject);
procedure btnShowClutClick(Sender: TObject);
procedure cbbBitModeChange(Sender: TObject);
procedure cbbTranspModeChange(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormDropFiles(Sender: TObject; const FileNames: array of string);
procedure grdClutDblClick(Sender: TObject);
procedure grdClutDrawCell(Sender: TObject; aCol, aRow: Integer; aRect: TRect; aState: TGridDrawState);
procedure grdClutKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure grdTimsListCompareCells(Sender: TObject; ACol, ARow, BCol,
BRow: Integer; var Result: integer);
procedure grdTimsListHeaderClick(Sender: TObject; IsColumn: Boolean;
Index: Integer);
procedure grdTimsListResize(Sender: TObject);
procedure grdTimsListSelection(Sender: TObject; aCol, aRow: Integer);
private
{ private declarations }
StartedScans: Integer; //Count of currently started scans
LastDir: string; //var to store last selected dir
Settings: TSettings; //var to work with program settings
Surf: PDrawSurf;
function FGetSelectedScanResult: TScanResult;
property SelectedScanResult: TScanResult read FGetSelectedScanResult; //Selected scan result
function FGetSelectedTimIdx: Integer;
property SelectedTimIdx: Integer read FGetSelectedTimIdx; //Index of tim, selected in list
function FGetSelectedTimInfo: TTimInfo;
property SelectedTimInfo: TTimInfo read FGetSelectedTimInfo; //Info about tim, selected in list
function FGetTimInfoByIdx(Index: Integer): TTimInfo;
property TimInfoByIdx[Index: Integer]: TTimInfo read FGetTimInfoByIdx; //Info about tim by index
function FGetSelectedTim: PTIM;
property SelectedTimInMode: PTIM read FGetSelectedTim; //Tim, selected in list
procedure GetFilesList(const FileList: TStringList; const Directory: string);
procedure ScanList(const FileList: TStringList);
procedure ScanPath(const Path: string);
procedure ScanFile(const FileName: string);
procedure CheckButtonsAndMainMenu;
procedure ScanFinished(Sender: TObject);
procedure BeforeScan(MaxProgress: Integer);
procedure SetTimsListCount(Count: Integer);
procedure UpdateCLUTInfo;
procedure DrawSelTim;
procedure SetupSelClut;
procedure SetCLUTListToNoCLUT;
function FormatFileName(const FileName: string; ListIdx_, BitMode: Integer; Magic: Byte): string;
function FormatPngName(const FileName: string; ListIdx_, BitMode, Clut: Integer): string;
procedure ShowTim;
procedure ShowTimInfo(ShowInfo: Boolean);
procedure RemoveGridSelection;
procedure UpdateTim(Tim, Clut, ClutInfo: Boolean);
public
{ public declarations }
ScanResults: TScanResultList; //List of finished scan results
ScanThreads: TScanThreadList; //List of currently started scans
end;
var
frmMain: TfrmMain;
implementation
uses ucdimage, ucpucount, lcltype, ucommon, LCLIntf, uexportimport,
FPimage
{$IFDEF Linux}
,BaseUnix
{$IFEND}
;
{$R *.lfm}
{ TfrmMain }
procedure TfrmMain.actScanFileExecute(Sender: TObject);
var
I: Integer;
List: TStringList;
begin
if not dlgOpenFile.Execute then Exit;
List := TStringList.Create;
for I := 1 to dlgOpenFile.Files.Count do
List.Add(dlgOpenFile.Files.Strings[I - 1]);
ScanList(List);
List.Free;
end;
procedure TfrmMain.actShowFileInfoExecute(Sender: TObject);
begin
Settings.InfoVisible := (Sender as TAction).Checked;
tblTimInfo.Visible := (Sender as TAction).Checked;
end;
procedure TfrmMain.actStopScanExecute(Sender: TObject);
var
I: Integer;
begin
for I := 1 to ScanThreads.Count do
ScanThreads[I - 1].StopScan := True;
while (StartedScans > 0) do
Application.ProcessMessages;
ScanThreads.Clear;
actStopScan.Tag := NativeInt(True);
actReturnFocus.Execute;
end;
procedure TfrmMain.actStretchExecute(Sender: TObject);
begin
Settings.StretchMode := (Sender as TAction).Checked;
UpdateTim(True, False, False);
end;
procedure TfrmMain.actPngExportExecute(Sender: TObject);
var
TIM: PTIM;
Image: PDrawSurf;
begin
TIM := SelectedTimInMode;
if TIM = nil then Exit;
dlgSavePNG.FileName := FormatPngName(SelectedScanResult.ScanFile, TIM^.dwTimNumber, SelectedTimInfo.BitMode, cbbCLUT.ItemIndex);
if not dlgSavePNG.Execute then Exit;
New(Image);
Image^ := nil;
Tim2Png(TIM, cbbCLUT.ItemIndex, Image, cbbTranspMode.ItemIndex);
SaveImage(dlgSavePNG.FileName, Image, TIMisIndexed(TIM));
FreeTIM(TIM);
Image^.Free;
Dispose(Image);
{$IFDEF Linux}FpChmod(dlgSavePNG.FileName, &777);{$IFEND}
end;
procedure TfrmMain.btnShowClutClick(Sender: TObject);
begin
pnlClut.Visible := not pnlClut.Visible;
SetupSelClut;
actReturnFocus.Execute;
end;
procedure TfrmMain.cbbBitModeChange(Sender: TObject);
begin
case cbbBitMode.ItemIndex of
1: cbbBitMode.Tag := NativeInt(cTIM4C);
2: cbbBitMode.Tag := NativeInt(cTIM4NC);
3: cbbBitMode.Tag := NativeInt(cTIM8C);
4: cbbBitMode.Tag := NativeInt(cTIM8NC);
5: cbbBitMode.Tag := NativeInt(cTIM16NC);
6: cbbBitMode.Tag := NativeInt(cTIM24NC);
else
cbbBitMode.Tag := NativeInt(-1);
end;
UpdateTim(True, False, False);
end;
procedure TfrmMain.cbbTranspModeChange(Sender: TObject);
begin
Settings.TranspMode := cbbTranspMode.ItemIndex;
UpdateTim(True, False, False);
end;
procedure TfrmMain.actChangeFileExecute(Sender: TObject);
var
I: Integer;
W, H: Word;
begin
SetTimsListCount(SelectedScanResult.Count);
actReturnFocus.Execute;
if grdTimsList.RowCount = 1 then Exit;
grdTimsList.BeginUpdate;
for I := 1 to SelectedScanResult.Count do
begin
W := TimInfoByIdx[I - 1].Width;
H := TimInfoByIdx[I - 1].Height;
grdTimsList.Cells[0, I] := Format('%.6d', [I]);
grdTimsList.Cells[1, I] := Format('%.2d', [TimInfoByIdx[I - 1].BitMode]);
grdTimsList.Cells[2, I] := Format('%.2d', [TimInfoByIdx[I - 1].Cluts]);
grdTimsList.Cells[3, I] := Format('%.2d', [TimInfoByIdx[I - 1].Colors]);
grdTimsList.Cells[4, I] := AnsiUpperCase(TIMTypeStr(TimInfoByIdx[I - 1].Magic));
grdTimsList.Cells[5, I] := Format('%.3dx%.3d', [W, H]);
end;
grdTimsList.EndUpdate;
grdTimsList.Row := 1;
grdTimsListSelection(Self, 0, 1);
grdTimsList.SetFocus;
CheckButtonsAndMainMenu;
end;
procedure TfrmMain.actAboutExecute(Sender: TObject);
begin
Application.MessageBox(cProgramName + #13#10#13#10 + 'Some "about strings" should be here!:)', 'About', MB_OK + MB_ICONINFORMATION);
end;
procedure TfrmMain.actAddToSendtoExecute(Sender: TObject);
begin
{$IFDEF windows}
Settings.AddToSendTo(not (Sender as TAction).Checked);
{$IFEND}
end;
procedure TfrmMain.actChangeBackColorExecute(Sender: TObject);
begin
if not dlgColor.Execute then Exit;
pnlImage.Color := dlgColor.Color;
Settings.BackColor := dlgColor.Color;
end;
procedure TfrmMain.actChangeClutIdxExecute(Sender: TObject);
begin
UpdateTim(True, True, False);
end;
procedure TfrmMain.actCloseFileExecute(Sender: TObject);
begin
SelectedScanResult.Free;
ScanResults.Delete(cbbFiles.ItemIndex);
lblStatus.Caption := '';
ShowTimInfo(False);
SetTimsListCount(0);
UpdateTim(True, True, True);
cbbFiles.Items.Delete(cbbFiles.ItemIndex);
CheckButtonsAndMainMenu;
if cbbFiles.Enabled then
begin
cbbFiles.ItemIndex := cbbFiles.Items.Count - 1;
actChangeFile.Execute;
end;
end;
procedure TfrmMain.actCloseFilesExecute(Sender: TObject);
begin
cbbFiles.Items.BeginUpdate;
while cbbFiles.Items.Count > 0 do
actCloseFile.Execute;
cbbFiles.Items.EndUpdate;
end;
procedure TfrmMain.actExitExecute(Sender: TObject);
begin
Close;
end;
procedure TfrmMain.actExtractPngsAllExecute(Sender: TObject);
var
I, J, OFFSET, BIT_MODE, SIZE: Integer;
FName, Path: string;
IsImage: Boolean;
ScanTim: TTimInfo;
TIM: PTIM;
Image: PDrawSurf;
begin
lblStatus.Caption := sStatusBarPngsExtracting;
for J := 1 to ScanResults.Count do begin
cbbFiles.ItemIndex := J - 1;
actChangeFile.Execute;
FName := ScanResults[J - 1].ScanFile;
IsImage := ScanResults[J - 1].IsImage;
New(Image);
Image^ := nil;
pbProgress.Position := 0;
pbProgress.Max := ScanResults[J - 1].Count;
for I := 1 to ScanResults[J - 1].Count do
begin
ScanTim := TimInfoByIdx[I - 1];
OFFSET := ScanTim.Position;
SIZE := ScanTim.Size;
BIT_MODE := ScanTim.BitMode;
Path := SysToUTF8(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStrUTF8(0)) + cExtractedPngsDir));
CreateDirUTF8(Path);
Path := IncludeTrailingPathDelimiter(Path + ExtractFileName(FName));
CreateDirUTF8(Path);
TIM := LoadTimFromFile(FName, OFFSET, IsImage, SIZE);
Tim2Png(TIM, cbbCLUT.ItemIndex, Image, cbbTranspMode.ItemIndex);
Path := Path + FormatPngName(FName, I - 1, BIT_MODE, 0);
SaveImage(Path, Image, TIMisIndexed(TIM));
{$IFDEF Linux}FpChmod(Path, &777);{$IFEND}
Image^.Free;
Image^ := nil;
FreeTIM(TIM);
pbProgress.Position := I;
Application.ProcessMessages;
end;
Dispose(Image);
pbProgress.Position := 0;
end;
lblStatus.Caption := sStatusBarExtracted;
end;
procedure TfrmMain.actExtractPngsExecute(Sender: TObject);
var
I, OFFSET, BIT_MODE, SIZE: Integer;
FName, Path: string;
IsImage: Boolean;
ScanTim: TTimInfo;
TIM: PTIM;
Image: PDrawSurf;
begin
lblStatus.Caption := sStatusBarPngsExtracting;
FName := SelectedScanResult.ScanFile;
IsImage := SelectedScanResult.IsImage;
New(Image);
Image^ := nil;
pbProgress.Position := 0;
pbProgress.Max := SelectedScanResult.Count;
for I := 1 to SelectedScanResult.Count do
begin
ScanTim := TimInfoByIdx[I - 1];
OFFSET := ScanTim.Position;
SIZE := ScanTim.Size;
BIT_MODE := ScanTim.BitMode;
Path := SysToUTF8(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStrUTF8(0)) + cExtractedPngsDir));
CreateDirUTF8(Path);
Path := IncludeTrailingPathDelimiter(Path + ExtractFileName(FName));
CreateDirUTF8(Path);
TIM := LoadTimFromFile(FName, OFFSET, IsImage, SIZE);
Tim2Png(TIM, cbbCLUT.ItemIndex, Image, cbbTranspMode.ItemIndex);
Path := Path + FormatPngName(FName, I - 1, BIT_MODE, 0);
SaveImage(Path, Image, TIMisIndexed(TIM));
{$IFDEF Linux}FpChmod(Path, &777);{$IFEND}
Image^.Free;
Image^ := nil;
FreeTIM(TIM);
pbProgress.Position := I;
Application.ProcessMessages;
end;
Dispose(Image);
lblStatus.Caption := sStatusBarExtracted;
pbProgress.Position := 0;
end;
procedure TfrmMain.actExtractTimExecute(Sender: TObject);
var
TIM: PTIM;
begin
dlgSaveFile.FileName := FormatFileName(SelectedScanResult.ScanFile, SelectedTimIdx, SelectedTimInfo.BitMode, SelectedTimInfo.Magic);
dlgSaveFile.FilterIndex := SelectedTimInfo.Magic - cTIMMagic + 1;
if not dlgSaveFile.Execute then Exit;
TIM := SelectedTimInMode;
SaveTimToFile(dlgSaveFile.FileName, TIM);
{$IFDEF Linux}FpChmod(dlgSaveFile.FileName, &777);{$IFEND}
FreeTIM(TIM);
end;
procedure TfrmMain.actExtractTimsAllExecute(Sender: TObject);
var
I, J, OFFSET, BIT_MODE, SIZE, MAGIC: Integer;
FName, Path: string;
IsImage: Boolean;
TIM: PTIM;
ScanTim: TTimInfo;
begin
lblStatus.Caption := sStatusBarFilesExtracting;
for J := 1 to ScanResults.Count do begin
cbbFiles.ItemIndex := J - 1;
actChangeFile.Execute;
FName := ScanResults[J - 1].ScanFile;
IsImage := ScanResults[J - 1].IsImage;
pbProgress.Position := 0;
pbProgress.Max := ScanResults[J - 1].Count;
for I := 1 to ScanResults[J - 1].Count do
begin
ScanTim := TimInfoByIdx[I - 1];
OFFSET := ScanTim.Position;
SIZE := ScanTim.Size;
BIT_MODE := ScanTim.BitMode;
MAGIC := ScanTim.Magic;
Path := SysToUTF8(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStrUTF8(0)) + cExtractedFilesDir));
CreateDirUTF8(Path);
Path := IncludeTrailingPathDelimiter(Path + ExtractFileName(FName));
CreateDirUTF8(Path);
TIM := LoadTimFromFile(FName, OFFSET, IsImage, SIZE);
Path := Path + FormatFileName(FName, I - 1, BIT_MODE, MAGIC);
SaveTimToFile(Path, TIM);
{$IFDEF Linux}FpChmod(Path, &777);{$IFEND}
FreeTIM(TIM);
pbProgress.Position := I;
Application.ProcessMessages;
end;
pbProgress.Position := 0;
end;
lblStatus.Caption := sStatusBarExtracted;
end;
procedure TfrmMain.actExtractTimsExecute(Sender: TObject);
var
I, OFFSET, BIT_MODE, SIZE, MAGIC: Integer;
FName, Path: string;
IsImage: Boolean;
TIM: PTIM;
ScanTim: TTimInfo;
begin
lblStatus.Caption := sStatusBarFilesExtracting;
FName := SelectedScanResult.ScanFile;
IsImage := SelectedScanResult.IsImage;
pbProgress.Position := 0;
pbProgress.Max := SelectedScanResult.Count;
for I := 1 to SelectedScanResult.Count do
begin
ScanTim := TimInfoByIdx[I - 1];
OFFSET := ScanTim.Position;
SIZE := ScanTim.Size;
BIT_MODE := ScanTim.BitMode;
MAGIC := ScanTim.Magic;
Path := SysToUTF8(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStrUTF8(0)) + cExtractedFilesDir));
CreateDirUTF8(Path);
Path := IncludeTrailingPathDelimiter(Path + ExtractFileName(FName));
CreateDirUTF8(Path);
TIM := LoadTimFromFile(FName, OFFSET, IsImage, SIZE);
Path := Path + FormatFileName(FName, I - 1, BIT_MODE, MAGIC);
SaveTimToFile(Path, TIM);
{$IFDEF Linux}FpChmod(Path, &777);{$IFEND}
FreeTIM(TIM);
pbProgress.Position := I;
Application.ProcessMessages;
end;
lblStatus.Caption := sStatusBarExtracted;
pbProgress.Position := 0;
end;
procedure TfrmMain.actOpenLabExecute(Sender: TObject);
begin
OpenUrl('http://lab313.ru');
end;
procedure TfrmMain.actOpenRepoExecute(Sender: TObject);
begin
OpenUrl('https://github.com/DrMefistO/tim2view/');
end;
procedure TfrmMain.actPngImportExecute(Sender: TObject);
var
Image: PDrawSurf;
TIM: PTIM;
ScanRes: TScanResult;
begin
if not dlgOpenPNG.Execute then Exit;
TIM := SelectedTimInMode;
if TIM = nil then Exit;
Image := LoadImage(dlgOpenPNG.FileName);
Png2Tim(Image, TIM);
ScanRes := SelectedScanResult;
ReplaceTimInFileFromMemory(ScanRes.ScanFile, TIM, SelectedTimInfo.Position, ScanRes.IsImage);
ShowTim;
FreeTIM(TIM);
Image^.Free;
Dispose(Image);
end;
procedure TfrmMain.actReplaceTimExecute(Sender: TObject);
var
ScanRes: TScanResult;
begin
if not dlgOpenFile.Execute then Exit;
if FileSizeUtf8(dlgOpenFile.FileName) > cTIMMaxSize then Exit;
ScanRes := SelectedScanResult;
ReplaceTimInFile(ScanRes.ScanFile, dlgOpenFile.FileName, SelectedTimInfo.Position, ScanRes.IsImage);
ShowTim;
end;
procedure TfrmMain.actReturnFocusExecute(Sender: TObject);
begin
if pnlList.Enabled then grdTimsList.SetFocus;
end;
procedure TfrmMain.actScanDirExecute(Sender: TObject);
var
SelectedDir: string;
begin
dlgSelectDir.Title := sSelectDirCaption;
dlgSelectDir.FileName := LastDir;
if not dlgSelectDir.Execute then Exit;
SelectedDir := dlgSelectDir.FileName;
if DirectoryExistsUTF8(SelectedDir) then
begin
ScanPath(SelectedDir);
LastDir := SelectedDir;
Settings.LastDir := LastDir;
end;
end;
procedure TfrmMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
actCloseFiles.Execute;
ScanThreads.Free;
ScanResults.Free;
Surf^.Free;
Dispose(Surf);
Settings.Free;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Settings := TSettings.Create(ExtractFilePath(ParamStr(0)));
cbbBitMode.Tag := NativeInt(-1);
{$IFDEF windows}actAddToSendto.Checked := Settings.SendToShortcutExists;{$ENDIF}
actStretch.Checked := Settings.StretchMode;
cbbTranspMode.ItemIndex := Settings.TranspMode;
LastDir := Settings.LastDir;
cbbBitMode.ItemIndex := Settings.BitMode;
pnlImage.Color := Settings.BackColor;
actShowFileInfo.Checked := Settings.InfoVisible;
ScanThreads := TScanThreadList.Create(False); //False - to able scan thread remove itself from this list
ScanResults := TScanResultList.Create(False);
StartedScans := 0;
New(Surf);
Surf^ := nil;
SetCLUTListToNoCLUT;
Caption := cProgramName;
CheckButtonsAndMainMenu;
if ParamCount > 0 then ScanPath(ParamStrUTF8(1));
end;
procedure TfrmMain.FormDropFiles(Sender: TObject;
const FileNames: array of string);
var
I: Integer;
begin
for I := 1 to Length(FileNames) do
ScanPath(FileNames[I - 1]);
end;
procedure TfrmMain.grdClutDblClick(Sender: TObject);
var
TIM: PTIM;
I, SELECTED_CELL, W, DIALOG_COLOR, CLUT_NUM: Integer;
R, G, B: Byte;
CLUT_COLOR: TCLUT_COLOR;
begin
TIM := SelectedTimInMode;
if TIM = nil then Exit;
SELECTED_CELL := grdClut.Row * grdClut.ColCount + grdClut.Col;
W := GetTimColorsCount(TIM);
if SELECTED_CELL >= W then
begin
FreeTIM(TIM);
Exit;
end;
CLUT_NUM := cbbCLUT.ItemIndex;
dlgColor.CustomColors.Clear;
for I := 1 to 16 do
begin
CLUT_COLOR := GetCLUTColor(TIM, CLUT_NUM, I - 1);
R := CLUT_COLOR.R;
G := CLUT_COLOR.G;
B := CLUT_COLOR.B;
dlgColor.CustomColors.Add(Format('Color%s=%.2x%.2x%.2x', [Chr(Ord('A') + (I - 1)), R, G, B]));
end;
CLUT_COLOR := GetCLUTColor(TIM, CLUT_NUM, SELECTED_CELL);
R := CLUT_COLOR.R;
G := CLUT_COLOR.G;
B := CLUT_COLOR.B;
dlgColor.Color := RGB(R, G, B);
if not dlgColor.Execute then
begin
FreeTIM(TIM);
Exit;
end;
DIALOG_COLOR := dlgColor.Color;
CLUT_COLOR.R := ((GetRValue(DIALOG_COLOR) div 8) and $1F) * 8;
CLUT_COLOR.G := ((GetGValue(DIALOG_COLOR) div 8) and $1F) * 8;
CLUT_COLOR.B := ((GetBValue(DIALOG_COLOR) div 8) and $1F) * 8;
WriteCLUTColor(TIM, CLUT_NUM, SELECTED_CELL, CLUT_COLOR);
ReplaceTimInFileFromMemory(SelectedScanResult.ScanFile, TIM, SelectedTimInfo.Position, SelectedScanResult.IsImage);
FreeTIM(TIM);
UpdateTim(True, True, False);
end;
procedure TfrmMain.grdClutDrawCell(Sender: TObject; aCol, aRow: Integer;
aRect: TRect; aState: TGridDrawState);
var
TIM: PTIM;
begin
TIM := SelectedTimInMode;
if TIM = nil then Exit;
if not TIMHasCLUT(TIM) then
begin
FreeTIM(TIM);
Exit;
end;
DrawClutCell(TIM, cbbCLUT.ItemIndex, @grdClut, ACol, ARow);
FreeTIM(TIM);
end;
procedure TfrmMain.grdClutKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
TIM: PTIM;
SELECTED_CELL, CLUT_NUM: Integer;
CLUT_COLOR: TCLUT_COLOR;
begin
if Key <> VK_SPACE then Exit;
TIM := SelectedTimInMode;
if TIM = nil then Exit;
SELECTED_CELL := grdClut.Row * grdClut.ColCount + grdClut.Col;
if SELECTED_CELL >= GetTimColorsCount(TIM) then
begin
FreeTIM(TIM);
Exit;
end;
CLUT_NUM := cbbCLUT.ItemIndex;
CLUT_COLOR := GetCLUTColor(TIM, CLUT_NUM, SELECTED_CELL);
CLUT_COLOR.STP := CLUT_COLOR.STP xor 1;
WriteCLUTColor(TIM, CLUT_NUM, SELECTED_CELL, CLUT_COLOR);
ReplaceTimInFileFromMemory(SelectedScanResult.ScanFile, TIM, SelectedTimInfo.Position, SelectedScanResult.IsImage);
FreeTIM(TIM);
UpdateTim(True, True, False);
end;
procedure TfrmMain.grdTimsListCompareCells(Sender: TObject; ACol, ARow, BCol,
BRow: Integer; var Result: integer);
var
AIdx, BIdx: Integer;
AW, AH, BW, BH, AP, BP: Integer;
AWxH, BWxH: string;
begin
case ACol of
0, 1, 2, 3:
begin
AIdx := StrToIntDef(grdTimsList.Cells[ACol, ARow], 0);
BIdx := StrToIntDef(grdTimsList.Cells[BCol, BRow], 0);
// Result will be either <0, =0, or >0 for normal order.
Result := AIdx - BIdx;
end;
5:
begin
AWxH := grdTimsList.Cells[ACol, ARow];
BWxH := grdTimsList.Cells[BCol, BRow];
AP := Pos('x', AWxH);
BP := Pos('x', BWxH);
AW := StrToIntDef(Copy(AWxH, 1, AP - 1), 0);
BW := StrToIntDef(Copy(BWxH, 1, BP - 1), 0);
AH := StrToIntDef(Copy(AWxH, AP + 1, Length(AWxH) - AP), 0);
BH := StrToIntDef(Copy(BWxH, BP + 1, Length(BWxH) - BP), 0);
if (AW = BW) then
Result := BH - AH
else
Result := BW - AW;
end;
else
begin
Result := AnsiCompareStr(grdTimsList.Cells[ACol, ARow], grdTimsList.Cells[BCol, BRow]);
end;
end;
// For inverse order, just negate the result (eg. based on grid's SortOrder).
if grdTimsList.SortOrder = soAscending then
Result := -Result;
end;
procedure TfrmMain.grdTimsListHeaderClick(Sender: TObject; IsColumn: Boolean;
Index: Integer);
begin
if (grdTimsList.Row < grdTimsList.VisibleRowCount) then
grdTimsList.TopRow := 1
else
grdTimsList.TopRow := grdTimsList.Row - grdTimsList.VisibleRowCount + 1;
end;
procedure TfrmMain.grdTimsListResize(Sender: TObject);
begin
grdTimsList.Columns[5].Width := grdTimsList.ClientWidth -
grdTimsList.Columns[4].Width -
grdTimsList.Columns[3].Width -
grdTimsList.Columns[2].Width -
grdTimsList.Columns[1].Width -
grdTimsList.Columns[0].Width;
end;
procedure TfrmMain.grdTimsListSelection(Sender: TObject; aCol, aRow: Integer);
begin
if (aRow < 1) then Exit;
ShowTim;
end;
function TfrmMain.FGetSelectedScanResult: TScanResult;
begin
Result := ScanResults[cbbFiles.ItemIndex];
end;
function TfrmMain.FGetSelectedTimIdx: Integer;
begin
Result := StrToIntDef(grdTimsList.Cells[0, grdTimsList.Row], 1) - 1;
end;
function TfrmMain.FGetSelectedTimInfo: TTimInfo;
begin
Result := SelectedScanResult.ScanTim[SelectedTimIdx];
end;
function TfrmMain.FGetTimInfoByIdx(Index: Integer): TTimInfo;
begin
Result := SelectedScanResult.ScanTim[Index];
end;
function TfrmMain.FGetSelectedTim: PTIM;
var
P: Integer;
begin
Result := nil;
if (grdTimsList.RowCount = 1) then Exit;
P := SelectedTimInfo.Position;
Result := LoadTimFromFile(SelectedScanResult.ScanFile, P, SelectedScanResult.IsImage, SelectedTimInfo.Size);
if Integer(cbbBitMode.Tag) = -1 then
Result^.OverBpp := Result^.HEAD^.bBPP
else
Result^.OverBpp := Integer(cbbBitMode.Tag);
end;
procedure TfrmMain.GetFilesList(const FileList: TStringList; const Directory: string);
var
sRec: TSearchRec;
isFound: boolean;
Dir: string;
begin
Dir := IncludeTrailingPathDelimiter(Directory);
isFound := FindFirst(UTF8ToSys(Dir + '*'), faAnyFile, sRec) = 0;
while isFound do
begin
if (sRec.Name <> '.') and (sRec.Name <> '..') then
begin
if (sRec.Attr and faDirectory) = faDirectory then
GetFilesList(FileList, Dir + SysToUTF8(sRec.Name))
else
FileList.Add(Dir + SysToUTF8(sRec.Name));
end;
Application.ProcessMessages;
isFound := FindNext(sRec) = 0;
end;
FindClose(sRec);
end;
procedure TfrmMain.ScanList(const FileList: TStringList);
var
I: Integer;
begin
actStopScan.Enabled := True;
actStopScan.Tag := NativeInt(False);
for I := 1 to FileList.Count do
ScanFile(FileList[I - 1]);
while (StartedScans > 0) do
Application.ProcessMessages;
actStopScan.Enabled := False;
actStopScan.Tag := NativeInt(True);
CheckButtonsAndMainMenu;
if cbbFiles.Enabled then
begin
cbbFiles.ItemIndex := cbbFiles.Items.Count - 1;
actChangeFile.Execute;
end;
end;
procedure TfrmMain.ScanPath(const Path: string);
var
FileList: TStringList;
begin
if Path = '' then Exit;
FileList := TStringList.Create;
if FileExistsUTF8(Path) then
FileList.Add(Path)
else
GetFilesList(FileList, Path);
ScanList(FileList);
FileList.Free;
end;
procedure TfrmMain.ScanFile(const FileName: string);
begin
LastDir := ExtractFilePath(FileName);
Settings.LastDir := LastDir;
if CheckForFileOpened(@ScanResults, FileName) then
begin
cbbFiles.ItemIndex := cbbFiles.Items.IndexOf(FileName);
actChangeFile.Execute;
Exit;
end;
if not Boolean(actStopScan.Tag) then
begin
ScanThreads.Add(TScanThread.Create(FileName, GetImageScan(FileName), @ScanResults));
ScanThreads.Last.FreeOnTerminate := True;
ScanThreads.Last.Priority := tpNormal;
ScanThreads.Last.OnTerminate := @ScanFinished;
if StartedScans < GetLogicalCpuCount then
begin
BeforeScan(ScanThreads.Last.FileLength);
ScanThreads.Last.Start;
Inc(StartedScans);
end
else
while (StartedScans > 0) do
Application.ProcessMessages;
end;
end;
procedure TfrmMain.CheckButtonsAndMainMenu;
var
Enable: Boolean;
begin
RemoveGridSelection;
Enable := (cbbFiles.Items.Count <> 0);
cbbFiles.Enabled := Enable;
actExtractTimsAll.Enabled := Enable;
actExtractPngsAll.Enabled := Enable;
pnlList.Enabled := Enable;
actCloseFile.Enabled := Enable;
actCloseFiles.Enabled := Enable;
Enable := (StartedScans = 0);
actScanFile.Enabled := Enable;
actScanDir.Enabled := Enable;
Enable := not (grdTimsList.Row < 1);
actReplaceTim.Enabled := Enable;
actPngExport.Enabled := (Surf^ <> nil) and Enable;
actPngImport.Enabled := Enable;
actExtractTim.Enabled := Enable;
actExtractTims.Enabled := Enable;
actExtractPngs.Enabled := Enable;
pnlImageOptions.Enabled := Enable;
end;
procedure TfrmMain.ScanFinished(Sender: TObject);
var
I: Integer;
begin
ScanThreads.Remove(Sender as TScanThread);
Dec(StartedScans);
for I := 1 to ScanThreads.Count do
if ScanThreads[I - 1].Suspended and (not ScanThreads[I - 1].StopScan) then
begin
BeforeScan(ScanThreads[I - 1].FileLength);
ScanThreads[I - 1].Start;
Inc(StartedScans);
Exit;
end;
end;
procedure TfrmMain.BeforeScan(MaxProgress: Integer);
begin
cbbFiles.Enabled := False;
pnlList.Enabled := False;
actScanFile.Enabled := False;
actScanDir.Enabled := False;
pbProgress.Max := MaxProgress;
pbProgress.Position := 0;
end;
procedure TfrmMain.SetTimsListCount(Count: Integer);
begin
grdTimsList.BeginUpdate;
grdTimsList.RowCount := Count + 1;
grdTimsList.Columns[0].Title.Caption := Format('# / %d', [Count]);
grdTimsList.EndUpdate;
end;
procedure TfrmMain.UpdateCLUTInfo;
var
TIM: PTIM;
I, CLUTS: Word;
begin
TIM := SelectedTimInMode;
if TIM = nil then Exit;
CLUTS := GetTIMClutsCount(TIM);
cbbCLUT.Items.BeginUpdate;
cbbCLUT.Items.Clear;
for I := 1 to CLUTS do
cbbCLUT.Items.Add(Format('CLUT [%.2d/%.2d]', [I, CLUTS]));
cbbCLUT.Items.EndUpdate;
if CLUTS = 0 then SetCLUTListToNoCLUT;
cbbCLUT.ItemIndex := 0;
FreeTIM(TIM);
end;
procedure TfrmMain.DrawSelTim;
var
TIM: PTIM;
begin
imgTim.Picture.Bitmap.FreeImage;
imgTim.Picture.Bitmap := nil;
TIM := SelectedTimInMode;
if TIM = nil then Exit;
Tim2Png(TIM, cbbCLUT.ItemIndex, Surf, cbbTranspMode.ItemIndex);
imgTim.Picture.Bitmap.Assign(Surf^.Bitmap);
FreeTIM(TIM);
imgTim.Stretch := actStretch.Checked;
end;
procedure TfrmMain.SetupSelClut;
var
TIM: PTIM;
begin
if not pnlClut.Visible then Exit;
//ClearGrid(@grdClut);
TIM := SelectedTimInMode;
if TIM = nil then
begin
grdClut.ColCount := 1;
grdClut.RowCount := 1;
Exit;
end;
grdClut.Enabled := TIMHasCLUT(TIM);
if TIMHasCLUT(TIM) then
SetupCLUT(TIM, cbbCLUT.ItemIndex, @grdClut)
else
begin
grdClut.ColCount := 1;
grdClut.RowCount := 1;
end;
FreeTIM(TIM);
end;
procedure TfrmMain.SetCLUTListToNoCLUT;
begin
cbbCLUT.Items.Clear;
cbbCLUT.Items.Add(sThisFileHasNoClut);
cbbCLUT.ItemIndex := 0;
end;
function TfrmMain.FormatFileName(const FileName: string; ListIdx_,
BitMode: Integer; Magic: Byte): string;
begin
Result := Format(cAutoExtractionFileFormat, [ExtractJustName(FileName), ListIdx_ + 1, BitMode, TIMTypeStr(Magic)]);
end;
function TfrmMain.FormatPngName(const FileName: string; ListIdx_, BitMode,
Clut: Integer): string;
begin
Result := Format(cAutoExtractionPngFormat, [ExtractJustName(FileName), ListIdx_ + 1, BitMode, Clut + 1]);
end;
procedure TfrmMain.ShowTim;
begin
if (grdTimsList.Row < 1) then Exit;
{ TODO : Reset bitmode or not? }
//cbbBitMode.ItemIndex := 0;
UpdateTim(True, True, True);
ShowTimInfo(True);
CheckButtonsAndMainMenu;
end;
procedure TfrmMain.ShowTimInfo(ShowInfo: Boolean);
var
TIM: PTIM;
TimInfo: TTimInfo;
begin
tblTimInfo.Cells[1, 1] := '';
tblTimInfo.Cells[1, 2] := '';
tblTimInfo.Cells[1, 3] := '';
tblTimInfo.Cells[1, 5] := '';
tblTimInfo.Cells[1, 6] := '';
tblTimInfo.Cells[1, 8] := '';
if not ShowInfo then Exit;
TimInfo := SelectedTimInfo;
TIM := SelectedTimInMode;
tblTimInfo.Cells[1, 1] := Format('0x%x', [TimInfo.Position]);
tblTimInfo.Cells[1, 2] := Format('0x%.2x', [GetTimVersion(TIM)]);
tblTimInfo.Cells[1, 3] := Format('%s', [TIMIsGoodStr(TIM)]);
if TIMHasCLUT(TIM) then
begin
tblTimInfo.Cells[1, 5] := Format('%dx%d', [GetTimClutVRAMX(TIM), GetTimClutVRAMY(TIM)]);
tblTimInfo.Cells[1, 6] := Format('%d', [GetTimColorsCount(TIM)]);
end;
tblTimInfo.Cells[1, 8] := Format('%dx%d', [GetTimImageVRAMX(TIM), GetTimImageVRAMY(TIM)]);
FreeTIM(TIM);
end;
procedure TfrmMain.RemoveGridSelection;
var
hGrid: TGridRect;
begin
hGrid.Top := -1;
hGrid.Left := -1;
hGrid.Right := -1;
hGrid.Bottom := -1;
grdClut.Selection := hGrid;
end;
procedure TfrmMain.UpdateTim(Tim, Clut, ClutInfo: Boolean);
begin
if ClutInfo then UpdateCLUTInfo;
if Tim then DrawSelTim;
if Clut then
begin
SetupSelClut;
grdClut.Repaint;
end;
end;
end.