BSOne.SFC/Tocsg.Module/Bs1Flt/MTPMon/MTPControl/BsoneUtil.pas

379 lines
9.7 KiB
Plaintext

unit BsoneUtil;
interface
uses
System.SysUtils,
System.IOUtils,
System.Classes,
System.StrUtils,
Winapi.Windows,
Winapi.Messages,
Winapi.Winsock2,
Winapi.IPHlpApi,
Winapi.ActiveX,
Winapi.WinSpool,
System.Math,
GlobalDefine,
System.NetEncoding,
BsoneDebug;
const
STATUS_SUCCESS = $00000000;
// FS_INFORMATION_CLASS (enum 값을 상수로 정의)
FileFsVolumeInformation = 1;
FileFsLabelInformation = 2;
FileFsSizeInformation = 3;
FileFsDeviceInformation = 4; // 우리가 필요한 값
FileFsAttributeInformation = 5;
FileFsControlInformation = 6;
FileFsFullSizeInformation = 7;
FileFsObjectIdInformation = 8;
FileFsDriverPathInformation = 9;
FileFsVolumeFlagsInformation = 10;
// GetOffLineType에서 사용할 리턴 타입 상수
OFFTYPE_UNKNOWN = 0;
OFFTYPE_REMOVABLE = 1;
OFFTYPE_FLOPPY = 2;
OFFTYPE_NETWORKDRIVEOUT = 3;
OFFTYPE_CDROM = 4;
OFFTYPE_EXTERNALHDD = 5;
IOCTL_STORAGE_QUERY_PROPERTY = $002D1400;
StorageDeviceProperty = 0; // STORAGE_PROPERTY_ID
PropertyStandardQuery = 0; // STORAGE_QUERY_TYPE
type
// 이전 질문에서 정의한 STORAGE_BUS_TYPE이 있다고 가정합니다.
// 만약 다른 유닛에 있다면 uses에 추가하고 이 정의는 주석 처리하세요.
{$MINENUMSIZE 4}
STORAGE_BUS_TYPE = (
BusTypeUnknown = $00,
BusTypeScsi, BusTypeAtapi, BusTypeAta, BusType1394, BusTypeSsa, BusTypeFibre,
BusTypeUsb, BusTypeRAID, BusTypeiScsi, BusTypeSas, BusTypeSata, BusTypeSd,
BusTypeMmc, BusTypeVirtual, BusTypeFileBackedVirtual, BusTypeSpaces,
BusTypeNvme, BusTypeSCM, BusTypeUfs, BusTypeNvmeof, BusTypeMax,
BusTypeMaxReserved = $7F
);
{$MINENUMSIZE 1}
// STORAGE_PROPERTY_QUERY 구조체
PSTORAGE_PROPERTY_QUERY = ^STORAGE_PROPERTY_QUERY;
STORAGE_PROPERTY_QUERY = record
PropertyId: DWORD; // STORAGE_PROPERTY_ID
QueryType: DWORD; // STORAGE_QUERY_TYPE
AdditionalParameters: array [0..0] of Byte;
end;
// STORAGE_DESCRIPTOR_HEADER 구조체
STORAGE_DESCRIPTOR_HEADER = record
Version: DWORD;
Size: DWORD;
end;
// STORAGE_DEVICE_DESCRIPTOR 구조체 (필요한 부분만 정의)
PSTORAGE_DEVICE_DESCRIPTOR = ^STORAGE_DEVICE_DESCRIPTOR;
STORAGE_DEVICE_DESCRIPTOR = record
Version: DWORD;
Size: DWORD;
DeviceType: Byte;
DeviceTypeModifier: Byte;
RemovableMedia: Boolean;
CommandQueueing: Boolean;
VendorIdOffset: DWORD;
ProductIdOffset: DWORD;
ProductRevisionOffset: DWORD;
SerialNumberOffset: DWORD;
BusType: STORAGE_BUS_TYPE; // 여기에 BusType 위치
RawPropertiesIncluded: DWORD;
// 이후 가변 길이 배열...
end;
NTSTATUS = LongInt;
PIO_STATUS_BLOCK = ^IO_STATUS_BLOCK;
IO_STATUS_BLOCK = record
case Integer of
0: (
Status: NTSTATUS;
Information: NativeUInt;
);
1: (
Pointer: Pointer;
);
end;
PFILE_FS_DEVICE_INFORMATION = ^FILE_FS_DEVICE_INFORMATION;
FILE_FS_DEVICE_INFORMATION = record
DeviceType: DWORD;
Characteristics: ULONG;
end;
TNtQueryVolumeInformationFile = function(
FileHandle: THandle;
IoStatusBlock: PIO_STATUS_BLOCK;
FsInformation: Pointer;
Length: ULONG;
FsInformationClass: Integer
): NTSTATUS; stdcall;
function UtilGetDriveTypeEx(path: string): DWORD; stdcall;
function UtilCompareCode(Src: PByte; Code: PByte; CodeSize: DWORD): BOOL; stdcall;
function UtilFindCode(BaseAddress: PByte; BaseSize: DWORD; Code: PByte;
CodeSize: DWORD; var nPos: Integer): PByte; stdcall;
function UtilGetBufferHex(pData: PByte; ASize, AMaxSize: DWORD): string;
procedure UtilSaveBufferToFile(sPath: string; pData: PByte; ASize: DWORD);
function UtilGetDriveTypeAndCharacteristics(hDevice: THandle; out DeviceType: DWORD; out Characteristics: ULONG): Boolean;
function UtilIsFileSizeOverBlockSize(const path: string; OutBlockSize: DWORD): Boolean;
implementation
function UtilGetDriveTypeEx(path: string): DWORD; stdcall;
var
driver: string;
P3, P4: Integer;
begin
// driver := TPath.GetPathRoot(path); 해당 API 실행 시 explorer가 죽는 현상이 있음.
if path = '' then
begin
Result := DRIVE_UNKNOWN;
Exit;
end;
if (Length(path) >= 2) and (path[1] = '\') and (path[2] = '\') then // 2. UNC 경로 검사 (예: \\Server\Share)
begin
P3 := PosEx('\', path, 3); // 3번째 '\' (e.g., \\Server[\ ]Share)
if P3 = 0 then
begin
driver := path; // '\\Server' (루트가 됨)
end
else
begin // 4번째 '\' (e.g., \\Server\Share[\ ]Folder)
P4 := PosEx('\', path, P3 + 1);
if P4 = 0 then
driver := path
else
// '\\Server\Share\Folder' (루트는 '\\Server\Share\')
driver := Copy(path, 1, P4);
end;
end
else if (Length(path) >= 3) and (path[2] = ':') and (path[3] = '\') then // 3. 드라이브 문자 경로 검사 (예: C:\)
begin
driver := Copy(path, 1, 3); // "C:\"
end
else if (Length(path) = 2) and (path[2] = ':') then // 4. 드라이브 문자만 검사 (예: C:)
begin
driver := path + '\'; // "C:\"
end
else if path[1] = '\' then // 5. 루트 디렉토리 검사 (예: \)
begin
driver := '\';
end
// 6. 위 모든 경우에 해당하지 않으면 상대 경로로 간주
else
begin
// TPath.GetPathRoot는 빈 문자열을 반환 -> DRIVE_UNKNOWN (혹은 NO_ROOT_DIR)
Result := DRIVE_NO_ROOT_DIR;
Exit;
end;
// DVLOG('UtilGetDriveTypeEx, GetPathRoot--(%s)', [PChar(driver)]);
if driver = '' then
begin
// 루트가 없으면(상대 경로 등) C++ 원본의 의도대로 DRIVE_UNKNOWN 반환
Result := DRIVE_UNKNOWN;
end
else
begin
// GetDriveType API는 'C:\' 및 '\\Server\Share\' 같은 UNC 경로를
// PChar로 전달받아 올바르게 처리합니다.
Result := GetDriveType(PChar(driver));
end;
end;
function UtilCompareCode(Src: PByte; Code: PByte; CodeSize: DWORD): BOOL; stdcall;
begin
Result := CompareMem(Src, Code, CodeSize);
end;
function UtilFindCode(BaseAddress: PByte; BaseSize: DWORD; Code: PByte;
CodeSize: DWORD; var nPos: Integer): PByte; stdcall;
var
i: Integer;
p: PByte;
c: PByte;
Bytes: Integer;
sBuff: string;
begin
Result := nil;
nPos := -1;
p := BaseAddress;
c := Code;
Bytes := Integer(BaseSize) - Integer(CodeSize);
if Bytes < 0 then
Exit;
// DVLOG('UtilFindCode, %d : %d %d', [BaseSize, CodeSize, Bytes]);
// for i := 0 to CodeSize - 1 do
// sBuff := sBuff + IntToHex(Code[i], 2);
//
// DVLOG('UtilFindCode: Code(%s)', [sBuff]);
// if Bytes = 0 then
// Bytes := 1
// else
// Bytes := Bytes;
for i := 0 to Bytes do
begin
if CompareMem(p, c, CodeSize) then
begin
nPos := i;
Result := p;
Exit;
end;
Inc(p);
end;
end;
function UtilGetBufferHex(pData: PByte; ASize, AMaxSize: DWORD): string;
var
i, inSize: DWORD;
begin
Result := '';
if (pData = nil) or (ASize = 0) then
Exit;
if ASize > AMaxSize then
inSize := AMaxSize
else
inSize := ASize;
for i := 0 to inSize - 1 do
Result := Result + IntToHex(pData[i], 2);
end;
procedure UtilSaveBufferToFile(sPath: string; pData: PByte; ASize: DWORD);
var
fs: TFileStream;
sFileName: string;
begin
if (pData = nil) or (ASize = 0) then
Exit;
try
sFileName := TPath.Combine(sPath,
'wpd_data_' + FormatDateTime('yyyymmdd_hhnnss_zzz', Now) + '.bin');
fs := TFileStream.Create(sFileName, fmCreate);
try
fs.WriteBuffer(pData^, ASize);
finally
fs.Free;
end;
except
on E: Exception do
DVLOG('_DeviceIoControl: FAILED to save file. Error: %s', [PChar(E.Message)]);
end;
end;
function UtilGetDriveTypeAndCharacteristics(hDevice: THandle; out DeviceType: DWORD; out Characteristics: ULONG): Boolean;
var
hNtDll: HMODULE;
NtQueryVolumeInformationFile: TNtQueryVolumeInformationFile;
status: NTSTATUS;
IoStatusBlock: IO_STATUS_BLOCK;
FileFsDeviceInfo: FILE_FS_DEVICE_INFORMATION;
begin
Result := False;
DeviceType := 0;
Characteristics := 0;
// 1. ntdll.dll 핸들 얻기
hNtDll := GetModuleHandle('ntdll.dll');
if hNtDll = 0 then
Exit;
// 2. 함수 주소 얻기
@NtQueryVolumeInformationFile := GetProcAddress(hNtDll, 'NtQueryVolumeInformationFile');
if not Assigned(NtQueryVolumeInformationFile) then
Exit;
// 3. API 호출
status := NtQueryVolumeInformationFile(
hDevice,
@IoStatusBlock,
@FileFsDeviceInfo,
SizeOf(FileFsDeviceInfo),
FileFsDeviceInformation // = 4
);
// 4. 결과 처리 (STATUS_SUCCESS == 0)
if status = STATUS_SUCCESS then
begin
Result := True;
DeviceType := FileFsDeviceInfo.DeviceType;
Characteristics := FileFsDeviceInfo.Characteristics;
end;
end;
function UtilIsFileSizeOverBlockSize(const path: string; OutBlockSize: DWORD): Boolean;
var
Attribute: TWin32FileAttributeData;
CurrentFileSize: Int64;
LimitSize: Int64;
begin
Result := False;
if OutBlockSize = 0 then
begin
Result := True;
Exit;
end;
// 1. 파일 속성 가져오기
if not GetFileAttributesEx(PChar(path), GetFileExInfoStandard, @Attribute) then
begin
// 실패 시 0으로 간주하거나, 예외 처리 (여기서는 0으로 진행)
Attribute.nFileSizeLow := 0;
Attribute.nFileSizeHigh := 0;
end;
// 2. [중요] 64비트 파일 크기로 변환 (High + Low)
// 4GB 이상의 대용량 파일도 정확하게 비교하기 위함
CurrentFileSize := (Int64(Attribute.nFileSizeHigh) shl 32) or Attribute.nFileSizeLow;
// 3. 로그 출력 (현재 크기 vs 제한 크기)
// OutBlockSize는 MB 단위라고 가정
OutputDebugString(PChar(Format('UtilIsFileSizeOverBlockSize File Size: %d Bytes, Limit: %d MB',
[CurrentFileSize, OutBlockSize])));
// 4. MB 단위를 Byte로 변환하여 비교
// 1 MB = 1024 * 1024 Bytes (또는 1 shl 20)
// Int64로 캐스팅해야 곱셈 과정에서 오버플로우가 발생하지 않음
LimitSize := Int64(OutBlockSize) * 1024 * 1024;
if CurrentFileSize > LimitSize then
begin
Result := True;
end;
end;
end.