BSOne.SFC/Tocsg.Lib/VCL/Tocsg.Prefetch.pas

366 lines
8.6 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.Prefetch }
{ }
{ Copyright (C) 2022 kku }
{ }
{*******************************************************}
unit Tocsg.Prefetch;
interface
uses
Tocsg.Obj, System.SysUtils, System.Classes, Winapi.Windows,
System.Generics.Collections;
const
PF_VER_1 = 17;
PF_VER_2 = 23;
PF_VER_3 = 26;
PF_VER_4 = 30;
type
TPfHeader = record
dwVer: DWORD;
arrSig: array[0..3] of AnsiChar;
dwUnknown1,
dwFSize: DWORD;
arrFName: array[0..29] of Char; // array[0..59] of AnsiChar;
dwPfHash,
dwUnknown2: DWORD;
end;
TPfFileInfoVer1 = record
dwOffset,
dwEntCnt,
dwOffsetTrace,
dwEntCntTrace,
dwOffsetFName,
dwFNameSize,
dwOffsetVolInfo,
dwVolNum,
dwVolSize: DWORD;
ftLastExeTime: TFileTime;
arrUnknown1: array [0..15] of AnsiChar;
dwExeCounter: DWORD;
dwUnknown2: DWORD;
end;
TPfFileInfoVer2 = record
dwOffset,
dwEntCnt,
dwOffsetTrace,
dwEntCntTrace,
dwOffsetFName,
dwFNameSize,
dwOffsetVolInfo,
dwVolNum,
dwVolSize: DWORD;
arrUnknown1: array [0..7] of AnsiChar;
ftLastExeTime: TFileTime;
arrUnknown2: array [0..15] of AnsiChar;
dwExeCounter: DWORD;
arrUnknown3: array [0..83] of AnsiChar;
end;
TPfFileInfoVer34 = record
dwOffset,
dwEntCnt,
dwOffsetTrace,
dwEntCntTrace,
dwOffsetFName,
dwFNameSize,
dwOffsetVolInfo,
dwVolNum,
dwVolSize: DWORD;
arrUnknown1: array [0..7] of AnsiChar;
arrLastExeTime: array [0..7] of TFileTime;
arrUnknown2: array [0..15] of AnsiChar;
dwExeCounter: DWORD;
arrUnknown3: array [0..95] of AnsiChar;
end;
TAssocFileEntVer1 = record
dwStartTime,
dwDuration,
dwFNameOffset,
dwFNameLen,
dwUnknown: DWORD;
end;
TAssocFileEntVer234 = record
dwStartTime,
dwDuration,
dwAverageDuration,
dwFNameOffset,
dwFNameLen,
dwUnknown: DWORD;
arrRefNTFS: array [0..7] of AnsiChar;
end;
TTgPrefetchAnal = class(TTgObject)
private
ms_: TMemoryStream;
PfHeader_: TPfHeader;
nRunCnt_: Integer;
ExeDtList_: TList<TDateTime>;
AssocFList_: TStringList;
sPfPath_,
sPath_,
sFName_: String;
function GetPfHeader(aStream: TStream): Boolean;
procedure ExtrPfInfoVer1;
procedure ExtrPfInfoVer2;
procedure ExtrPfInfoVer34;
function GetRunCount: Integer;
public
Constructor Create;
Destructor Destroy; override;
function LoadFromStream(aStream: TStream): Boolean;
function LoadFromFile(sPath: String): Boolean;
procedure Clear;
function GetExeDateTimeToText(sDm: String = ''; sDtFormat: String = ''): String;
function GetExeDateEnum: TEnumerator<TDateTime>;
property RunCount: Integer read GetRunCount;
property ExeDtList: TList<TDateTime> read ExeDtList_;
property FilePath: String read sPath_;
property FileName: String read sFName_;
end;
implementation
uses
Tocsg.Safe, Tocsg.Exception, Tocsg.NTDLL.Decompress, Tocsg.DateTime,
Tocsg.Strings, System.Math, Tocsg.Path;
{ TTgPrefetchAnal }
Constructor TTgPrefetchAnal.Create;
begin
Inherited Create;
sPfPath_ := '';
sPath_ := '';
sFName_ := '';
ms_ := nil;
ExeDtList_ := TList<TDateTime>.Create;
AssocFList_ := TStringList.Create;
end;
Destructor TTgPrefetchAnal.Destroy;
begin
FreeAndNil(AssocFList_);
FreeAndNil(ExeDtList_);
if ms_ <> nil then
FreeAndNil(ms_);
Inherited;
end;
function TTgPrefetchAnal.GetPfHeader(aStream: TStream): Boolean;
var
RtlHeader: TRtlHeader;
pSrc, pBuf: TBytes;
dwDecomLen: DWORD;
begin
Result := false;
if aStream.Size < SizeOf(PfHeader_) then
exit;
ZeroMemory(@PfHeader_, SizeOf(PfHeader_));
aStream.Position := 0;
ZeroMemory(@RtlHeader, SizeOf(RtlHeader));
if aStream.Read(RtlHeader, SizeOf(RtlHeader)) <> SizeOf(RtlHeader) then
exit;
if not CompareMem(@RtlHeader.arrSig[0], @AnsiString('MAM')[1], 3) then
exit;
SetLength(pSrc, aStream.Size - aStream.Position);
aStream.Read(pSrc[0], Length(pSrc));
SetLength(pBuf, RtlHeader.dwSize);
dwDecomLen := RtlDecompress(@pSrc[0], @pBuf[0], Length(pSrc), RtlHeader.dwSize, COMPRESSION_FORMAT_XPRESS_HUFF);
if dwDecomLen > 0 then
begin
ms_ := TMemoryStream.Create;
ms_.Write(pBuf[0], dwDecomLen);
ms_.Position := 0;
ms_.Read(PfHeader_, SizeOf(PfHeader_));
if PfHeader_.arrSig = 'SCCA' then
Result := true;
end;
end;
procedure TTgPrefetchAnal.ExtrPfInfoVer1;
var
FInfo: TPfFileInfoVer1;
begin
if ms_ = nil then
exit;
ms_.Read(FInfo, SizeOf(FInfo));
nRunCnt_ := FInfo.dwExeCounter;
ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.ftLastExeTime));
end;
procedure TTgPrefetchAnal.ExtrPfInfoVer2;
var
FInfo: TPfFileInfoVer2;
begin
if ms_ = nil then
exit;
ms_.Read(FInfo, SizeOf(FInfo));
nRunCnt_ := FInfo.dwExeCounter;
ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.ftLastExeTime));
end;
procedure TTgPrefetchAnal.ExtrPfInfoVer34;
var
FInfo: TPfFileInfoVer34;
i: Integer;
dwPos, dwEntSize,
dwNameOffset: DWORD;
AssocFileEnt: TAssocFileEntVer234;
pBuf: TBytes;
sPath: String;
begin
if ms_ = nil then
exit;
ms_.Read(FInfo, SizeOf(FInfo));
nRunCnt_ := FInfo.dwExeCounter;
for i := Low(FInfo.arrLastExeTime) to High(FInfo.arrLastExeTime) do
if (FInfo.arrLastExeTime[i].dwLowDateTime > 0) and (FInfo.arrLastExeTime[i].dwHighDateTime > 0) then
ExeDtList_.Add(ConvFileTimeToDateTime_Local(FInfo.arrLastExeTime[i])); // 로컬 타임으로 가져옴 22_0905 15:54:59 kku
// ExeDtList_.Add(ConvFileTimeToDateTime(FInfo.arrLastExeTime[i]));
SetLength(pBuf, 1024);
dwPos := FInfo.dwOffset;
dwNameOffset := FInfo.dwOffsetFName;
dwEntSize := SizeOf(AssocFileEnt);
for i := 0 to FInfo.dwEntCnt - 1 do
if dwPos < ms_.Size then
begin
ms_.Position := dwPos;
if ms_.Read(AssocFileEnt, dwEntSize) <> dwEntSize then
exit;
dwPos := ms_.Position;
with AssocFileEnt do
begin
if (dwNameOffset + dwFNameOffset + (dwFNameLen * 2)) < ms_.Size then
begin
ms_.Position := dwNameOffset + dwFNameOffset;
ZeroMemory(pBuf, 1024);
if ms_.Read(pBuf[0], dwFNameLen * 2) <> (dwFNameLen * 2) then
exit;
sPath := UpperCase(String(PChar(@pBuf[0])));
if GetFileExt(sPath) = 'EXE' then
begin
sPath_ := ExtractFilePath(sPath);
sFName_ := ExtractFileName(sPath);
end;
AssocFList_.Add(sPath);
end;
end;
end;
end;
function TTgPrefetchAnal.LoadFromStream(aStream: TStream): Boolean;
begin
Clear;
Result := false;
try
if (aStream = nil) or (aStream.Size = 0) then
exit;
Result := GetPfHeader(aStream);
if not Result then
exit;
case PfHeader_.dwVer of
PF_VER_1 : ExtrPfInfoVer1;
PF_VER_2 : ExtrPfInfoVer2;
PF_VER_3,
PF_VER_4 : ExtrPfInfoVer34;
end;
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. LoadFromStream()');
end;
end;
function TTgPrefetchAnal.LoadFromFile(sPath: String): Boolean;
var
fs: TFileStream;
begin
Result := false;
if not FileExists(sPath) then
exit;
try
sPfPath_ := sPath;
Guard(fs, TFileStream.Create(sPath, fmOpenRead));
Result := LoadFromStream(fs);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. LoadFromFile()');
end;
end;
procedure TTgPrefetchAnal.Clear;
begin
if ms_ <> nil then
FreeAndNil(ms_);
Finalize(PfHeader_);
ZeroMemory(@PfHeader_, SizeOf(PfHeader_));
nRunCnt_ := 0;
ExeDtList_.Clear;
AssocFList_.Clear;
sPfPath_ := '';
sFName_ := '';
sPath_ := '';
end;
function TTgPrefetchAnal.GetRunCount: Integer;
begin
Result := Max(nRunCnt_, ExeDtList_.Count);
end;
function TTgPrefetchAnal.GetExeDateTimeToText(sDm: String = ''; sDtFormat: String = ''): String;
var
i: Integer;
begin
Result := '';
try
if sDm = '' then
sDm := ',';
for i := 0 to ExeDtList_.Count - 1 do
if sDtFormat <> '' then
SumString(Result, FormatDateTime(sDtFormat, ExeDtList_[i]), sDm)
else
SumString(Result, DateTimeToStr(ExeDtList_[i]), sDm);
except
on E: Exception do
ETgException.TraceException(Self, E, 'Fail .. GetExeDateTimeToCommaText()');
end;
end;
function TTgPrefetchAnal.GetExeDateEnum: TEnumerator<TDateTime>;
begin
Result := ExeDtList_.GetEnumerator;
end;
end.