📄 diskfs.pas
字号:
{ FAT File System Module, Copyright (C) 2001, 2002 by Alexander Grau }
{ FAT file system structure:
|----|-------|-------|-------|------------|----------------------------|
| BS | FAT 1 | ... | FAT n | root 12/16 | data area |
|----|-------|-------|-------|------------|----------------------------|
example for FAT12:
1. boot sector sector 0 (in partition)
2. first FAT (4085 Cluster)
3. second FAT
4. root directory (32 Byte/entry)
5. data area (data cluster)
Remark: Long filename support has nothing to do with FAT32! FAT32 is only used for big volumes. }
unit diskfs;
interface
uses classes, devices, comctrls, statusdlg;
const
{ user defined FAT Type constants }
FATnone = 0 ; { not detected }
FAT12 = 1 ;
FAT16 = 2 ;
FAT32 = 3 ;
{ user defined cluster types }
clusUsed = 1;
clusFree = 2;
clusBad = 4;
clusEOF = 8;
attrReadOnly = 1; { File Attribute }
attrHidden = 2;
attrSystem = 4;
attrVolume = 8;
attrSubDir = 16;
attrArchive = 32;
attrLongName = attrReadOnly + attrHidden + attrSystem + attrVolume;
type
PBootSec = ^TBootSec;
{: Boot Sector and BPB Structure }
TBootSec = packed record
BS_jmpBoot : array[0..2] of byte;
BS_OEMName : array[0..7] of char;
{ ;-----now the BPB (BIOS Parameter Block) begins ---------------------------------- }
BPB_BytesPSec : word;
BPB_SecPerClus : byte;
BPB_RsvdSecCnt : word;
BPB_NumFATs : byte;
BPB_RootEntCnt : word;
BPB_TotSec16 : word;
BPB_Media : byte;
BPB_FATsz16 : word;
BPB_SecPerTrk : word;
BPB_NumHeads : word;
BPB_HiddSec : longword;
BPB_TotSec32 : longword;
{;------now the BPB/boot sector for FAT12/16 differs from the BPB/boot sector for FAT32...}
BS_DrvNum : byte;
BS_Reserved1 : byte;
BS_BootSig : byte;
BS_VolID : longword;
BS_VolLab : array[0..10] of char;
BS_FilSysType : array[0..7] of char;
{; ... Loader Routine follows ...}
end;
PBootSec32 = ^TBootSec32;
{: boot sector structure for FAT32 }
TBootSec32 = packed record
BS_jmpBoot : array[0..2] of byte;
BS_OEMName : array[0..7] of char;
{ ;-----now the BPB (BIOS Parameter Block) begins ---------------------------------- }
BPB_BytesPSec : word;
BPB_SecPerClus : byte;
BPB_RsvdSecCnt : word;
BPB_NumFATs : byte;
BPB_RootEntCnt : word;
BPB_TotSec16 : word;
BPB_Media : byte;
BPB_FATsz16 : word;
BPB_SecPerTrk : word;
BPB_NumHeads : word;
BPB_HiddSec : longword;
BPB_TotSec32 : longword;
{ ;------now the BPB/boot sector for FAT32 differs from the BPB/boot sector for FAT12/16... }
BPB_FATSz32 : longword;
BPB_ExtFlags : word;
BPB_FSVer : word;
BPB_RootClus : longword;
BPB_FSInfo : word;
BPB_BkBootSec : word;
BPB_Reserved : array[0..11] of byte;
BS32_DrvNum : byte;
BS32_Reserved1_: byte;
BS32_BootSig : byte;
BS32_VolID : array[0..3] of byte;
BS32_VolLab : array[0..10] of char;
BS32_FilSysType: array[0..7] of char;
end;
{: FAT32 FSInfo Sector Structure }
TFSInfo = packed record
FSI_LeadSig : longword;
FSI_Reserved1 : array[0..479] of byte;
FSI_StrucSig : longword;
FSI_FreeCount : longword;
FSI_NxtFree : longword;
FSI_Reserved2 : array[0..11] of byte;
FSI_TrailSig : longword;
end;
{: 32 Byte Directory Entry Structure }
PDirEntry = ^TDirEntry;
TDirEntry = packed record
DIR_Name : array[0..10] of byte;
DIR_Attr : byte;
DIR_NTRes : byte;
DIR_CrtTimeTenth : byte;
DIR_CrtTime : word;
DIR_CrtDate : word;
DIR_LstAccDate : word;
DIR_FstClusHI : word;
DIR_WrtTime : word;
DIR_WrtDate : word;
DIR_FstClusLO : word;
DIR_FileSize : longword;
end;
PLnDirEntry = ^TLnDirEntry;
{: Long Name Directory Entry Structure }
TLnDirEntry = packed record
DIR_Sig : byte;
DIR_LName1 : array[0..4] of word;
DIR_Attr : byte;
DIR_Flags : byte;
DIR_ChkSum : byte;
DIR_LName2 : array[0..5] of word;
DIR_First : word;
DIR_LName3 : array[0..1] of word;
end;
{: Information structure for Reset, Blockread }
TFATFileInfo = record
FI_FirstClus: longword; { erster Cluster }
FI_Cluster : longword; { aktueller Cluster }
FI_recpos : longword; { current record position (0 to filesize-1) }
FI_size : longword; { file size }
FI_LastClus : longword; { last used cluster (for blockwrite) }
end;
{ File }
TFATFile = class (TCustomFile)
public
attr: byte;
cluster: longword;
time, date: word;
procedure duplicate(dest: TCustomFile); override;
function Rename(aname: string): boolean; override;
function GetPath(RelativeToDir: TCustomDirectory): string; override;
procedure SaveTo(dest: string; FATno: byte; dlg: TStatusDialog); virtual;
procedure ChangeListViewItem(listitem: TListItem); override;
end;
{ Directory }
TFATDirectory = class (TCustomDirectory)
public
cluster: longword;
sector: longword; // if FAT12/16 root directory, this is the sector (and cluster = 0)
time, date: word;
procedure duplicate(dest: TCustomDirectory); override;
function Rename(aname: string): boolean; override;
function GetPath(RelativeToDir: TCustomDirectory): string; override;
procedure AddDirToTree(TreeView: TTreeView; node: TTreeNode; deleted: boolean); override;
procedure ChangeListViewItem(listitem: TListItem); override;
procedure AddChildrenToListView(listview: TListView; deleted: boolean); override;
function CompareChildren(item1, item2: TListItem; useIdx: integer): integer; override;
procedure ForEachChild(proc: TProcessDirProc; deleted, exclusive, recursive: boolean;
UserParams: integer); virtual;
function ChildIsSubDir: boolean; override;
private
function SubDirFound(finddeleted: boolean): boolean;
//function IsValid: boolean;
procedure AddChildren(deleted, exclusive: boolean);
end;
TFATAnalyser = class;
{: The FAT drive object }
TFATdrive = class(TCustomDrive)
public
{ current options }
useFAT : byte; // use: 0=no FAT, 1=first, 2=second FAT
skipBadMarkedClus : boolean; // skip bad marked cluster?
BootSec : TBootSec32; { Boot sector of device (accessed via BootSec/BootSec32 structure) }
FSInfo : TFSInfo; { FSInfo Structure }
{ optional parameters calculated from the boot sector... }
FATtype : byte; { determined FAT type (FAT12, FAT16 or FAT32) }
TotSec : longword; { Total count of sectors }
BytePerClus : word; { Bytes per Cluster }
RootClus : longword;
RootDirSectors : word; { Sectors occupied by the root directory }
FirstRootDirSecNum: longword;
FATsz : longword; { Count of sectors occupied by ONE FAT }
FATSectors : longword; { Sectors occupied by all FATs }
FirstDataSector : longword; { Start of the data region, the first sector of cluster 2 }
TotDataSec : longword; { Data sector count }
CountOfClusters : longword; { Total count of Clusters }
constructor Create; override;
destructor Destroy; override;
function ReadSec(LBA: longint; blocks: word; buf: pointer; ErrorDlg: boolean): boolean; override;
function MountDrive(quiet: boolean): boolean; override;
procedure FindLostData(dlg: TStatusDialog); override;
procedure AddDriveToTree(TreeView: TTreeView); override;
procedure AddListViewColumns(ListView: TListView); override;
function FindFiles: boolean; override;
procedure SaveListViewItems(ListView: TListView); override;
function SaveFile(afile: TfatFile; dest: string; FATno: byte;
dlg: TStatusDialog; var overwrite: boolean): integer;
function SaveDirectory(dir: TfatDirectory; dest: string;
deleted, exclusive, recursive: boolean; FATno: byte;
dlg: TStatusDialog; var overwrite: boolean; var fileoverwrite: boolean): integer;
function CalcDriveInfo(pbs: PBootSec; quiet: boolean): boolean;
function GetFAT(N: longword; FATno: byte): longword;
function GetClusterMask(clustype: byte): longword;
function IsEOF(value: longword): boolean;
function Cluster2Sec(cluster: longword): longword;
function Sec2Cluster(sec: longword): longword;
function ReadDataSec(var cluster: longword; var sector: longword; var clussec: word; xferbuf: pointer): boolean;
function GetNextRecord(var cluster: longword; var sector: longword; var recno: word; xferbuf: pointer): boolean;
function GetLongName(var cluster: longword; var recsec: longword; var recno: word;
entry: PDirEntry; var longname: string): boolean;
function FindNextEntry(var cluster: longword; var recsec: longword; var recno: word;
var entry: TDirentry; var name: string; findDeleted: boolean): boolean;
function ResetByCluster(cluster, size: longword; var handle: TFATFileInfo): boolean;
function BlockRead(var handle: TFATFileInfo; count: longword; buf: pointer; FATno: byte;
var bytesread: longword): boolean;
function GetRecPosCluster(var N: longword; recpos: longword; FATno: byte): boolean;
function Seek(var handle: TFATFileInfo; N: longword; FATno: byte): boolean;
function filepos(handle: TFATFileInfo): longword;
function filesize(handle: TFATFileInfo): longword;
procedure Close(var handle: TFATFileInfo);
private
{ sector buffers }
FFATSecBuf : array[0..512*2 -1] of byte; { 2 Sectors FAT Buffer (2 because FAT12 can bound a sector!) }
FFATSec : longword; { current FAT sector in buffer }
FFATchg : byte; { 0=FAT not changed / 1=first sector changed, 2=both sectors changed (FAT12 only) }
FWorkSecBuf : array[0..511] of byte; { Working Sector Buffer for getnextrecord }
FWorkSec : longword; { current working sector in buffer }
FDataSecBuf : array[0..511] of byte; { Data Sector Buffer for Blockread }
FDataSec : longword; { current data sector in buffer }
FFATAnalyser : TFATAnalyser;
procedure CreateStartItems;
end;
{: The FAT drive analyser }
TFATAnalyser = class
public
AnalyseDev: TDevice; // device to analyse
LastFATsec: longword; // last FAT sector found
MaxFATclus: longword; // last maximum FAT cluster number
FirstFATsecbuf: pointer; // first FAT sector buffer
FATstartsec: longword; // FAT start sector (of all FATs)
FATendsec: longword; // FAT end sector (of all FATs)
NumberOfFATsFound: byte; // number of FATs found
OneFATsecsize: longword; // sector size of one FAT
fattype: byte; // FAT type found
FATAreafound: boolean; // FAT area found?
DataAreaFound: boolean; // data area found?
RootAreaFound: boolean; // Root area found?
SecPerClus : word; // sectors per cluster
ClusterCount: longword; // count of clusters
FirstDirClus: longword; // cluster of first directory found
FirstDirSec: longword; // sector of first directory found
FirstDataSec: longword; // sector of first data cluster (cluster 2)
succSecValidEntries: byte; // number of succesive sectors with valid entries
RootSec: longword; // root directory sector
RootClus: longword; // root directory cluster (=0 if not found)
RootEntCnt: word; // number of root directory entries
succSecValidEntriesRoot: byte; // number of successive sectors with valid entries in the root area
function CnvName(buf: pointer): shortstring;
function ClusterType(FATtype: byte; value: longword): byte;
function IsBlankEntry(entry: TDirEntry): boolean;
function IsBootSecB(p: pointer): boolean;
function AreBootSecEqualB(p1, p2: pointer): boolean;
function DirEntriesValidB(buf: pointer; bufsize: word;
ValidClusterCount, ValidTotalDataSec: longword): boolean;
function GetNextRecordB(buf: pointer; bufsize: longword; var recno: word; xferbuf: pointer): boolean;
function IsFATbeginB(buf: pointer; bufsize: longword; FATtype: byte): boolean;
function IsFATB(buf: pointer; bufsize: longword; var IsFATbegin: boolean; var FATtype: byte;
var maxclus: longword): boolean;
function IsDirB(buf: pointer; bufsize: longword; var cluster: longword): boolean;
function IsRootB(buf: pointer; bufsize: longword): boolean;
function GetFATB(buf: pointer; bufsize: longword; FATtype: byte; N: longword): longword;
function IsRootC(drv: TfatDrive; clus, sec: longword): boolean;
function IsDirC(drv: TfatDrive; clus, sec: longword): boolean;
function DirEntriesValidC(drv: TfatDrive; clus, sec: longword): boolean;
function IsDirEmptyC(drv: TfatDrive; clus, sec: longword): boolean;
function IsFileC(drv: TfatDrive; scanclus: longword; var fileext: string; var datatype: byte): boolean;
procedure AnalyseSecStart(dev: TDevice);
procedure AnalyseSecStop;
function AnalyseSec(dev: TDevice; physsec: longword; buf: pointer; bufsize: longword): boolean;
procedure AnalyseRestart;
procedure RebuildBootSec(dev: TDevice; pbs: pbootsec32; FATtype, SecPerclus: word;
RootEntCnt: word; RootClus: longword;
OneFATsecSize: longword; NumFATs: byte;
ClusterCount: longword);
end;
{: FAT file stream object, for easy use with text/hex viewer etc. }
TFATStream = class(TStream)
private
FPosition: longint;
FSize: longint;
Fcluster: longint;
Fdrv: TfatDrive;
FFATno: byte;
Fhandle: TfatFileInfo;
procedure SetPosition(offset: longint);
public
property position: longint read FPosition write SetPosition;
property size: longint read Fsize;
constructor create(drv: TfatDrive; clus, size: longint; FATno: byte);
destructor destroy; override;
function read(var buffer; count: longint): longint; override;
function seek(offset: longint; origin: word): longint; override;
end;
function ReplaceDeletedChar(const s: string): string;
implementation
uses common, windows, sysutils, main, clusdlg, helpers, dialogs, finddlg, dirseldlg, controls, filedet;
function ReplaceDeletedChar(const s: string): string;
begin
if MainForm.options.DeletedUseMultiByte then
result:=StringReplace(s, char($e5), MainForm.options.DeletedCharMultibyte, [])
else
result:=StringReplace(s, char($e5), MainForm.options.DeletedCharAnsi, []);
end;
// ---------------------------------------------------------------------------
// TfatAnalyser
// ---------------------------------------------------------------------------
function TFATAnalyser.CnvName(buf: pointer): shortstring;
var
i: byte;
ext, name, res: shortstring;
c: ^char;
begin
name:=''; ext:='';
i:=0; c:=buf;
while (i < 11) do
begin
if (c^ <> ' ') then
begin
if (i < 8) then name:=name+c^
else ext:=ext+c^;
end;
inc(i);
inc(c);
end;
res:=name;
if ext <> '' then res:=res+'.'+ext;
CnvName:=res;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -