📄 devices.pas
字号:
{: Devices, Drives, Files & Directories }
unit devices;
interface
uses classes, comctrls, statusdlg;
const
{: partition types }
pid_None = $00; { not used }
pid_FAT12 = $01; { 12-bit FAT primary partition or logical drive. The number of sectors in the volume is fewer than 32680 }
pid_FAT16 = $04; { 16-bit FAT primary partition or logical drive. The number of sectors is between 32680 and 65535 }
pid_Extended = $05; { Extended partition }
pid_BigDOS = $06; { BIGDOS FAT primary partition or logical drive }
pid_NTFS = $07; { NTFS primary partition or logical drive }
pid_FAT32_LBA = $0B; { Primary Fat32 partition, using interrupt 13 (INT 13) extensions }
pid_FAT32_EXT_LBA = $0C; { Extended Fat32 partition, using INT 13 extensions }
pid_FAT16_LBA = $0E; { Primary Fat16 partition, using INT 13 extensions }
pid_Extended_LBA = $0F; { Extended partition, using INT 13 extensions }
type
{: structure of one partition entry }
PartEntry = packed record
PE_status : byte;
PE_StartSectHead : byte;
PE_StartSectSecCyl : word;
PE_OSID : byte;
PE_EndSecHead : byte;
PE_EndSectSecCyl : word;
PE_SectOfs : longword;
PE_SectCnt : longword;
end;
PPartSec = ^PartSec;
{: structure of partition sector }
PartSec = packed record
BootCode : array[0..$1bd] of byte;
PartTable: array[1..4] of PartEntry;
BootID: word;
end;
const
{ Driver }
DRIVER_TYPE_PHYS = 0; // physical device (using PDISKIO driver)
DRIVER_TYPE_LOG = 1; // logical device (using LDISKIO driver)
{ Device Type }
DEVICE_TYPE_UNKNOWN = 0;
DEVICE_TYPE_FLOPPY = 1;
DEVICE_TYPE_REMOVABLE = 2;
DEVICE_TYPE_FIXED = 3;
DEVICE_TYPE_REMOTE = 4;
DEVICE_TYPE_CDROM = 5;
DEVICE_TYPE_RAMDISK = 6;
{ Info Flags }
DEVICE_FLAG_PARTITION_TABLE = 1; // device has partition table
{ Attributes }
DEVICE_ATTR_REMOVABLE = 1; // device is removable
{: TCustomDirectory/TCustomFile Flags }
item_deleted = 1;
item_lost = 2;
// file (undelete) recovery conditions
rec_cond_good = 1;
rec_cond_poor = 2;
{: TCustomDrive condition flags }
drv_cond_virtual = 1; // is it lost?
drv_cond_BootSecRebuild = 2; // boot sector rebuild?
drv_cond_quickFormatted = 4; // has it been quick-formatted?
type
//: Device List
TDeviceList = class;
//: Device object
TDevice = class
protected
FUseCache : boolean;
procedure TestCache;
public
name : string; // device name
driver : byte; { driver: physical (INT13 driver) or logical (MS-DOS/Windows drive) }
DevType : byte; { device type: floppy, removable, fixed, remote, CDROM, RAMDISK... }
Attr : word; { device attributes }
TotalSec : longword; { number of sectors }
BytesPerSec: word; { bytes per sector }
InfoFlags : word;
drv : byte; { drive number: INT13 drive number (0=FDD0, 1=FDD1, ... 80h=HDD0, 81h=HDD1, ...)
or : MS-DOS/Windows drive letter (1=A:, 2=B: 3=C:, 4=D: ...) }
cachesec : longword; { number of sectors to cache (=0 if no caching at all) }
constructor Create; virtual;
destructor Destroy; override;
function ReadSec(LBA: longint; blocks: word; buf: pointer; ErrorDlg: boolean): boolean;
function WriteSec(LBA: longint; blocks: word; buf: pointer; verify, ErrorDlg: boolean): boolean;
function FindLostDrives(devList: TDeviceList; StartSec, EndSec: longword): boolean;
procedure DetectCacheLineSize;
procedure UseCache(enable: boolean);
procedure InvalidateCache;
end;
//: Device List
TDeviceList = class
public
devices: TList;
constructor Create; virtual;
destructor Destroy; override;
function count: integer;
procedure clear;
function GetDevice(i: integer): TDevice;
procedure DetectDevices(dlg: TStatusDialog);
function GetDeviceText(devno: integer; sizetext: boolean): string;
function IndexOf(dev: TDevice): integer;
end;
TCustomDirectory = class;
//: abstract Drive object
TCustomDrive = class
public
dev : TDevice; // device
name : string; // the drive's name (e.g. volume name)
condition : byte; // condition flags (see above)
PosBootSec : longword; // Position of boot sector (normally start of partition)
PartOfs : longword; // Offset to this drive on the device
PartSectors : longword; // Total count of sectors
RootDir : TCustomDirectory; // root directory
RootDirDeleted : TCustomDirectory; // root directory (contains deleted files/directories)
RootDirLost : TCustomDirectory; // root directory (contains lost files/directories)
RootDirSearched : TCustomDirectory; // root directory (contains searched files/directories)
constructor Create; virtual; abstract;
destructor Destroy; override; abstract;
function ReadSec(LBA: longint; blocks: word; buf: pointer; ErrorDlg: boolean): boolean; virtual; abstract;
function MountDrive(quiet: boolean): boolean; virtual; abstract;
procedure FindLostData(dlg: TStatusDialog); virtual; abstract;
procedure AddDriveToTree(TreeView: TTreeView); virtual; abstract;
procedure AddListViewColumns(ListView: TListView); virtual; abstract;
function FindFiles: boolean; virtual; abstract;
procedure SaveListViewItems(ListView: TListView); virtual; abstract;
end;
//: Drive List
TDriveList = class
public
drives: TList;
constructor Create; virtual;
destructor Destroy; override;
function count: integer;
procedure clear;
function GetDrive(i: integer): TCustomDrive;
procedure AddVirtualDrive(dev: TDevice; posBootSec, physsec, seccount: longword; quiet: boolean);
procedure DetectDrives(dev: TDevice);
procedure DetectPartitions(dev: TDevice; physsec, firstExtended: longword);
end;
PCustomFile = ^TCustomFile;
//: abstract File object
TCustomFile = class
public
name: string;
size: longword;
drive: TCustomDrive;
flags: byte; // item flags => item_XXX
condition: byte; // recovery condition => rec_cond_XXX
parent: TCustomDirectory;
procedure duplicate(dest: TCustomFile); virtual;
function Rename(aname: string): boolean; virtual; abstract;
function GetPath(RelativeToDir: TCustomDirectory): string; virtual; abstract;
procedure ChangeListViewItem(listitem: TListItem); virtual; abstract;
end;
//: function prototype that is called for each directory/file item by the ForEachChild method
TProcessDirProc = function(item: TObject; UserParams: integer): boolean; stdcall;
PCustomDirectory = ^TCustomDirectory;
//: abstract Directory object
TCustomDirectory = class
public
name: string;
drive: TCustomDrive;
expanded: boolean;
flags: byte; // item flags => item_XXX
condition: byte; // recovery condition => rec_cond_XXX
Children: TList; // children directories (list of directory/file objects)
parent: TCustomDirectory;
constructor Create; virtual;
destructor destroy; override;
procedure duplicate(dest: TCustomDirectory); virtual;
procedure DeleteChildren; virtual;
function GetPath(RelativeToDir: TCustomDirectory): string; virtual; abstract;
function Rename(aname: string): boolean; virtual; abstract;
procedure AddDirToTree(TreeView: TTreeView; node: TTreeNode; deleted: boolean); virtual; abstract;
procedure ChangeListViewItem(listitem: TListItem); virtual; abstract;
procedure AddChildrenToListView(listview: TListView; deleted: boolean); virtual; abstract;
function CompareChildren(item1, item2: TListItem; useIdx: integer): integer; virtual; abstract;
function ChildIsSubDir: boolean; virtual; abstract;
end;
var
optCacheEnabled: boolean;
implementation
uses main, pdiskio, ldiskio, sysutils, windows, diskfs, helpers;
const
CACHELINES = 4;
CACHELINEBUFSZ = 2048*1024;
type
TCacheLine = record
dev : TDevice; // cached device
LRU : byte; // last recently used (0=no hits, 15=always hits)
SecStart: longword; // cached sectors start
SecEnd : longword; // cached sectors end
buf: array[0..CACHELINEBUFSZ-1] of byte;
end;
var
Cache: array[0..CACHELINES-1] of TCacheLine;
ExitSave: Pointer;
//-----------------------------------------------------------------------------
// TDevice
//-----------------------------------------------------------------------------
constructor TDevice.create;
begin
FUseCache:=FALSE;
cachesec:=0;
end;
destructor TDevice.Destroy;
begin
InvalidateCache;
end;
procedure TDevice.InvalidateCache;
var
i: integer;
begin
for i:=0 to CACHELINES-1 do
if cache[i].dev = self then
begin
cache[i].dev:=NIL;
cache[i].LRU:=0;
end;
end;
{: detects optimal cache line size for the device }
procedure TDevice.DetectCacheLineSize;
var
sectors: longword;
sec: longword;
starttime: longword;
res: boolean;
dummybuf: pointer;
begin
res:=FALSE;
if TotalSec > 2047 then
begin
try
getmem(dummybuf, 2048);
UseCache(FALSE);
sec:=0;
res:=ReadSec(0, 1, dummybuf, FALSE); // first access to spin-up drive...
if res then
begin
starttime:=GetTickCount;
// test sector reading speed for time of 10ms...
while ((GetTickCount < starttime + 100) AND (res)) do
begin
res:=ReadSec(sec, 1, dummybuf, FALSE);
inc(sec);
end;
if res then
begin
// compute cache line sectors per second
if sec > 1 then cachesec:=sec * 10
else cachesec:=1;
if cachesec * BytesPerSec > CACHELINEBUFSZ then
cachesec:=CACHELINEBUFSZ div BytesPerSec;
end;
end;
finally
freemem(dummybuf, 2048);
end;
end;
if NOT res then cachesec:=0; // don't cache device...
end;
{: Reads sector from device }
function TDevice.ReadSec(LBA: longint; blocks: word; buf: pointer; ErrorDlg: boolean): boolean;
var
res: boolean;
readblocks: longword;
LRUline: byte;
LRUvalue: byte;
i: integer;
{: Tries to read sector from cache - returns FALSE if not (fully) cached }
function ReadSecCached(var LBA: longint; var blocks: word; var buf: pointer): boolean;
var
cacheblocks: longword;
load: boolean;
hit: boolean;
i: integer;
test: byte;
begin
load:=TRUE;
hit:=FALSE;
for i:=0 to CACHELINES-1 do // if and where is the sector cached ?
begin
if (NOT hit) AND (Cache[i].dev = self) AND (LBA >= Cache[i].SecStart) AND (LBA <= Cache[i].SecEnd) then
begin
hit:=TRUE; // cache hit!
if Cache[i].LRU < 15 then inc (Cache[i].LRU);
load:=FALSE;
cacheblocks:=blocks;
if LBA + blocks-1 > Cache[i].SecEnd then
begin
// not ALL blocks are cached...
cacheblocks:=Cache[i].SecEnd - LBA +1;
load:=TRUE;
end;
(*hexdump(Cache[i].buf[ (LBA - Cache[i].SecStart) *BytesPerSec], 512);
messagebox(0, 'readsecached', 'info', mb_ok);*)
move(Cache[i].buf[ (LBA - Cache[i].SecStart) *BytesPerSec], buf^, cacheblocks * BytesPerSec);
if load then
begin
// adjust paramters for rest blocks...
LBA:=LBA+cacheblocks;
blocks:=blocks-cacheblocks;
inc(longint(buf), cacheblocks * BytesPerSec);
end;
end else if Cache[i].LRU > 0 then dec(Cache[i].LRU);
end;
result:=(NOT load);
end;
begin
res:=FALSE;
if (optCacheEnabled) AND (FUseCache) AND (cachesec > 0) then // cache enabled and use it?
begin
res:=ReadSecCached(LBA, blocks, buf);
if NOT res then
begin
// do read-ahead caching...
// first determine LRU cache line...
LRUline:=0; LRUvalue:=15;
for i:=0 to CACHELINES-1 do
if Cache[i].LRU < LRUvalue then
begin
LRUline:=i; LRUvalue:=Cache[i].LRU;
end;
//debug(inttostr(LRUline), DebugHigh);
readblocks:=cachesec; //CACHELINEBUFSZ div BytesPerSec;
if LBA + readblocks > TotalSec then readblocks:=TotalSec-LBA;
if driver = DRIVER_TYPE_PHYS then
begin
res:=ReadPhysicalSectors(drv, LBA, readblocks, @cache[LRUline].buf[0], ErrorDlg);
end
else if driver = DRIVER_TYPE_LOG then
begin
res:=ReadLogicalSectors(drv, LBA, readblocks, @cache[LRUline].buf[0]);
end;
if res then
begin
cache[LRUline].dev:=self;
cache[LRUline].LRU:=15;
cache[LRUline].SecStart:=LBA;
cache[LRUline].SecEnd:=LBA + readblocks-1;
ReadSecCached(LBA, blocks, buf);
end;
end
end;
if NOT res then // read without cache if caching disabled or read error during cache read...
begin
if driver = DRIVER_TYPE_PHYS then
begin
res:=ReadPhysicalSectors(drv, LBA, blocks, buf, ErrorDlg);
end else if driver = DRIVER_TYPE_LOG then
begin
res:=ReadLogicalSectors(drv, LBA, blocks, buf);
end;
end;
result:=res;
end;
procedure TDevice.UseCache(enable: boolean);
begin
FUseCache:=enable;
end;
{: Writes sector to device }
function TDevice.WriteSec(LBA: longint; blocks: word; buf: pointer; verify, ErrorDlg: boolean): boolean;
var
res: boolean;
begin
if driver = DRIVER_TYPE_PHYS then
begin
res:=WritePhysicalSectors(drv, LBA, blocks, buf, verify, ErrorDlg);
end else if driver = DRIVER_TYPE_LOG then
begin
res:=WriteLogicalSectors(drv, LBA, blocks, buf);
end;
result:=res;
end;
{:Test cache algorithms }
procedure TDevice.TestCache;
var
bufon: array[0..2047] of byte;
bufoff: array[0..2047] of byte;
sec: longword;
res: boolean;
diff: longword;
i: integer;
begin
(* debug('testing readsec...', debughigh);
UseCache(FALSE);
ReadSec(0, 2048, @bufoff[0], true);
for i:=0 to 2047 do
begin
ReadSec(i, 1, @bufon[0], true);
if res then
begin
diff:=BytesEqual(@bufoff[i*512], @bufon, 512);
if diff > 0 then debug(format('not equal: %d bytes, sec: %d', [diff, i]), debughigh);
end;
end;
debug('...ready', debughigh);
exit;*)
randomize;
debug('testing cache...', debughigh);
for i:=0 to 1000 do
begin
sec:=i; //random(TotalSec);
UseCache(TRUE);
res:=ReadSec(sec, 1, @bufon, true);
if res then
begin
UseCache(FALSE);
res:=ReadSec(sec, 1, @bufoff, true);
if res then
begin
diff:=BytesEqual(@bufon, @bufoff, BytesPerSec);
if diff > 0 then debug(format('not equal: %d bytes, sec: %d', [diff, sec]), debughigh);
end else debug(format('error reading sector (cache on): %d', [sec]), debughigh);
end else debug(format('error reading sector (cache on): %d', [sec]), debughigh);
if i mod 100=0 then debug('...', debughigh);
end;
UseCache(FALSE);
debug('...ready', debughigh);
end;
function TDevice.FindLostDrives(devlist: TDeviceList; StartSec, EndSec: longword): boolean;
var
buf: pointer;
bufextra: pointer;
physsec: longword;
res: boolean;
bsfound: boolean;
lostcount: integer;
flostfound: boolean;
FATanalyser: TFATanalyser;
i: integer;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -