Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Модуль для работы с дисковыми драйверами (на уровне FAT)

01.01.2007

                       FAT/FAT16/FAT32

            Модуль для работы с дисковыми драйверами

 

                     Автор  : NikNet

                     E-MAIL : NikNet@Yandex.ru

                     Сайт   : NikNet.narod.ru [Скоро будет :)]

                            2006г

                   Версия 4.00 (Win9x/NT)

 

                  Файловая система FAT фирмы Microsoft

 

                  FAT12/FAT16                 FAT32

 

              Загрузочный сектор        Загрузочный сектор

 

                    FAT 1                Структура FSInfo

 

                    FAT 2                     FAT 1

                                                                   Системная

               Корнивой каталог               FAT 2                область

 

                Область данных            Область данных

 

unit uFAT;
erface
 
 
 
 
Uses Windows,SysUtils,DISK,CLASSES;
TYPE
{----------- Структура загрузочного сектора для FAT12 и FAT16 ---------------}
 PBoot  = ^TBoot;
 TBoot           {                                                             }= packed record
  bsJmpBoot      { Переход на код загрузки                                     }: array[1..3] of byte;
  bsOemname      { Имя пройзводителя                                           }: array[1..8] of char;
  bsBytePerSec   { Число байт в секторе                                        }: word;
  bsSecPerClus   { Число секторов в сластере                                   }: byte;
  bsRsvdSecCnt   { Начала FAT1 в секторах                                      }: word;
  bsNumFATs      { Число копий FAT                                             }: byte;
  bsRootEntCnt   { Количество элементов в корне                                }: word;
  bsToolSec12    { Общее количество секторов на диске                          }: word;
  bsMedia        { Тип носителя                                                }: byte;
  bsFATSz16      { Количество в одной  FAT                                     }: word;
  bsSecPerTrk    { Число секторов на одной дорожки                             }: word;
  bsNumHeads     { Число головок на одной дорожки                              }: word;
  bsNumHideSec   { Количество "скрытых" секторов                               }: LongInt;
  bsToolSec16    { Общее количество секторов на диске                          }: LongInt;
  bsDrvNum       { Номер дискавода                                             }: byte;
  bsReserved1    { Резервировано для WinNT                                     }: byte;
  bsBootSig      { Признак расширеной загрузочной записи (24h)                 }: byte;
  bsVolID        { Серийны номер диска                                         }: LongInt;
  bsVolLab       { Метка тома диска                                            }: array[1..11] of char;
  bsFSType       { Тип файловой системы                                        }: array[1..8]  of char;
  bsBoot         { Загрузочный код                                             }: array[1..448]of Byte;
  bsTrailSig     { Сигнатура AA55h                                             }: array[1..2] of char;
  end;
 
{--------------- Структура загрузочного сектора для FAT32 -------------------}
 PBoot32 = ^TBoot32;
 TBoot32         {                                                             }=  packed record
  bsJmpBoot      { Переход на код загрузки                                     }: array[1..3] of byte;
  bsOemname      { Имя пройзводителя                                           }: array[1..8] of char;
  bsBytePerSec   { Число байт в секторе                                        }: word;
  bsSecPerClus   { Число секторов в сластере                                   }: byte;
  bsRsvdSecCnt   { Начала FAT1 в секторах                                      }: word;
  bsNumFATs      { Число копий FAT                                             }: byte;
  bsRootEntCnt   { Количество элементов в корне                                }: word;
  bsToolSec16    { Зарезервировано                                             }: word;
  bsMedia        { Тип носителя                                                }: byte;
  bsFATz16       { Зарезервировано                                             }: word;
  bsSecPerTrk    { Число секторов на одной дорожки                             }: word;
  bsNumHeads     { Число головок на одной дорожки                              }: word;
  bsHiddSec      { Число скрытых секторов                                      }: Longint;
  bsTolSec32     { Общее количество секторов на диске                          }: LongInt;
  bsFATSz32      { Количество сектаров для одной FAT                           }: LongInt;
  bsExtFlags     { Номер активой FAT                                           }: word;
  bsFSVer        { Номер версии: старший байт номер версии,младши номер ревизи }: word;
  bsRootClus     { Первый кластер обычно имеет номер 2                         }: LongInt;
  bsFSInfo       { Номер сектора структуры FSINFO                              }: word;
  bsBkBootSec    { Номер BootSector(Копия) обычно имеет номер 2                }: word;
  bsReserved     { Облость резервированная                                     }: array[1..12] of byte;
  bsDrvNum       { Номер дискавода                                             }: byte;
  bsReserved1    { Резервировано для WinNT                                     }: byte;
  bsBootSig      { Признак расширеной загрузочной записи (24h)                 }: byte;
  bsVolID        { Серийны номер диска                                         }: LongInt;
  bsVolLab       { Метка тома диска                                            }: array[1..11] of char;
  bsFSType       { Тип файловой системы                                        }: array[1..8]  of char;
  bsBoot         { Загрузочный код                                             }: array[1..420]of byte;
  bsTrailSig     { Сигнатура AA55h                                             }: array[1..2] of char;
  end;
 
{-------------------------> Типы носителей информации <------------------------}const
 MediaType       {                                                             }:array[1..7] of byte= (
  $F0            { Гибкий диск, 2 стороны, 18 секторов на дорожке              },
  $F8            { Жесткий диск                                                },
  $F9            { Гибкий диск, 2 стороны, 15 секторов на дорожке              },
  $FC            { Гибкий диск, 1 стороны, 09 секторов на дорожке              },
  $FD            { Гибкий диск, 2 стороны, 09 секторов на дорожке              },
  $FE            { Гибкий диск, 1 стороны, 08 секторов на дорожке              },
  $FF            {  Гибкий диск, 2 стороны, 08 секторов на дорожке             }  );
 
{----- Структура сектора FSInfo и резервного загрузочного сектора FAT32 -----}Type
 PFsInfo = ^TFsInfo;
 TFsInfo         {                                                             }= Record
  fsLeadSig      { Сигнатура 41615252h                                         }:LongInt;
  fsReserved1    { Зарезервировано                                             }:array[1..480] of byte;
  fsStrucSig     { Сигнатура 61417272h                                         }:LongInt;
  fsFree_Count   { Количество свободных кластеров                              }:LongInt;
  fsNxt_Free     { Обычно номер 2                                              }:LongInt;
  fsReserved2    { Зарезервировано                                             }:array[1..12] of byte;
  fsTrailSig     { Сигнатура AA550000h                                         }:array[1..4] of byte;
  end;
{------------ Вид начальных фрагментов для FAT различных типов --------------}{
 
  Байт   00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  FS12 - FF 8F FF 00 30 04 00 5F FF 00 7F FF FF F0 0A 00 BF FF 00 D0 0E FF FF FF
  FS16 - FF F8 FF FF 00 03 00 04 00 05 FF FF 00 07 FF FF FF FF 00 0A 00 0B FF FF
  FS32 - 0F FF FF F8 0F FF FF FF 00 00 00 03 00 00 00 04 00 00 00 05 0F FF FF FF
 
          Резервные файлы                                          Конечный
                                                                    кластер
                                                                    файла
 
{---------------- Значения специальных кодов элементов FAT ------------------}{
 
      Значение кода               FAT12      FAT16          FAT32
      Свободный кластер           0          0              0
      Дефектный кластер           $FF7       $FFF7         $FFFFFF7
      Последний кластер в списке  $FF8-$FFF $FFF8-$FFFF $FFFFFF8-$FFFFFFF}
 
const
  FAT_Available    = 0;
  FAT_Reserved_Min = $FFFFFFF0;
  FAT_Reserved_Max = $FFFFFFF6;
  FAT_BAD          = $FFFFFFF7;
  FAT_EOF_Min      = $FFFFFFF8;
  FAT_EOF_Max      = $FFFFFFFF;
 
  FAT_MASK_12      = $FFF;
  FAT_MASK_16      = $FFFF;
  FAT_MASK_32      = $FFFFFFF;
 
const
  ATTR_ARCHIVE     = $20;  // Архивный
  ATTR_DIRECTORY   = $10;  // Директория
  ATTR_VOLUME      = $08;  // Метка тома
  ATTR_SYSTEM      = $04;  // Системный
  ATTR_HIDDEN      = $02;  // Скрытый
  ATTR_READONLY    = $01;  // Только для чтение
 
TYPE
{----------------------- Структура элемента каталога ------------------------}
    PDIRENTRY = ^TDIRENTRY;
    TDIRENTRY = record
      Name         { Имя файла или директори                                     }:array[1..8] of char;
      EXT          { Расширение файла                                            }:array[1..3] of char;
      Attr         { Атрибуты файла                                              }:BYTE;
      NTRes        { Поле зарезервировано для WinNT должно содержать 0           }:BYTE;
      CrtTimeTenth { Поле, уточняющее время создание файла в милисикундах        }:BYTE;
      CrtTime      { Время создание файла                                        }:WORD;
      CrtDate      { Дата создание файла                                         }:WORD;
      LstAccDate   { Дата последнего обращения к файлу для I/O данных            }:WORD;
      FSIClasHi    { Старшее слово номера первого кластера файла                 }:WORD;
      WrtTime      { Время выпонения последней операции записи в файл            }:WORD;
      WrtDate      { Дата выпонения последней операции записи в файл             }:WORD;
      FSIClasLo    { Младшее слово номера первого кластера файла                 }:WORD;
      Size         { Размер файла в байтах (   32-разрядное  число   )           }:LONGINT;
    end;
 
{--- Структура элемента каталога, хранящего фрагмент длинного имени файла ---}
    PLONGDIRENTRY = ^TLONGDIRENTRY;
    TLONGDIRENTRY = record
      Counter      { Номер фрагмента                                             }:Byte;
      LFN1         { Первый участок фрагмента имени                              }:array[1..5]of Wchar;
      Attr         { Атрибуты файла                                              }:BYTE;
      Flags        { Байт флагов                                                 }:BYTE;
      ChkSum       { Контроляная сумма << короткого имени >>                     }:BYTE;
      LFN2         { Второй участок фрагмента имени                              }:array[1..6]of Wchar;
      FirstClus    { Номер первого кластера ( должен быть равен 0 )              }:Word;
      LFN3         { Третий участок фрагмента имени                              }:array[1..2]of Wchar;
    end;
 
 
 
{------------------------------------------------------------------------------}
(******************************************************************************)
{------------------------------------------------------------------------------}
(******************************************************************************)
{------------------------------------------------------------------------------}
 
 TYPE
  TFSType = (fsNone, fsFAT12, fsFAT16, fsFAT32);
  TDIR_Entry = record
      Name            : String;
      LongName        : String;
      Ext             : String;
      Attr            : Byte;
      StartCluster    : Longint;
      CreateTime      : Longint;
      CreateDate      : Longint;
      WriteLastDate   : Longint;
      WriteLastTime   : Longint;
      FileSize        : Longint;
      LastAccessDate  : Longint;
      Erased          : Boolean;
      CurrentSector   : Int64;
      StartByteNamePerSec : Integer;
    end;
  PDIR_Entry = ^TDIR_Entry;
 
 
 VAR
  PhysicalVolume    : word  = 0;              // Номер текущего Физичиского диска
  Volume            : Byte  = 0;              // Текущий логический диск
  VolumeSerial      : DWord = 0;              // Серийный номер тома
  BytesPerSector    : DWORD = 0;              // Количество байт в одном секторе
  LogicalSectors    : Int64 = 0;              // Количество секторов на лог. диске
 
  SectorsPerCluster : DWORD = 0;              // Количество секторов в одном кластере
  RootDirSector     : Int64 = 0;              // Начало корневого каталога
  RootDirCluster    : Int64 = 0;              // Начальный кластер корневого каталога
  RootDirEntries    : Int64 = 0;              // Количество элементов в корневом каталоге
  DataAreaSector    : Int64 = 0;              // Текущий кластер
 
  FATCount          : Byte  = 0;              // Количество копий FAT (Обычно 2)
  SectorsPerFAT     : Int64 = 0;              // Количеств секторов в одной FAT
  FATSize           : Int64 = 0;              // Размер FAT в кластерах
  FATSector         : Pointer = nil;          // Начало FAT
  FAT               : Pointer = nil;          // Буфер для файловых элементов
  ActiveFAT         : word;
  EndingCluster     : Int64 = 0;              // Последний кластер для одной FAT
 
 
  VolumeName        : array[1..11] of char;   // Метка тома
  FSName            : array [1..8] of Char;   // Название файловой системы
  FSType            : TFSType = fsNone;       // Тип файловой системы
 
 
 Function  Init         (Drive:byte):Boolean;
 Function  ReadSector  (Sector: Int64; Count: Word; Var Buffer; nSize:DWORD): Boolean;
 Function  WriteSector (Sector: Int64; Count: Word; Var Buffer; nSize:DWORD): Boolean;
 Function  GetFATCluster(FATIndex: LongInt): LongInt;
 Function  GetFATEntry  (Cluster: Int64): Longint;
 Procedure SetFATEntry  (Cluster: Int64; Value: Longint);
 
 Function  GetCluster(Sector: Int64):Int64;
 Function  ReadCluster  (Cluster: Int64; var Buffer; BufferSize: Longint): Boolean;
 Function  WriteCluster (Cluster: Int64; var Buffer; BufferSize: Longint): Boolean;
 Function  WriteClusterChain(StartCluster: Longint; Buffer: Pointer; BufferSize: Longint): Boolean;
 Function  ReadClusterChain(StartCluster: Int64; var Buffer: Pointer; var BufferSize: Longint): Boolean;
 Function  SeekForChainStart(Cluster: Int64): Longint;
 Function  ValidCluster (Cluster: Int64): Boolean;
 function  ReadDIR(Cluster: Longint; var DIR: PDIR_Entry; var Entries: Longint): Boolean;
 Procedure Done;
   // Дополнение...
 procedure ParseDOSTime (Time: Word; var Hour, Minute, Second: Word);
 procedure ParseDOSDate (Date: Word; var Day, Month, Year: Word);
 function  GetShortName (Name: String): String;
 function  FormatDiskSize (Value: TLargeInteger): string;
 function  DosToWin(St: string): string;
 
implementation
 
 
function ReadDIR(Cluster: Longint; var DIR: PDIR_Entry; var Entries: Longint): Boolean;
label
    NextSector,
    LongNameComponent,
    ElementNotUsed,
    EndDIR;
 
var P: Pointer;
    P1: PDIREntry;
    PL: PLONGDIRENTRY;
    Dir_Entry: TDIR_Entry;
    Size: Longint;
    ADIR: TMemoryStream;
    J: DWORD;
    s,s1,sTmp: String;
    L:DWORD;
    LFNErase:Boolean;
begin
    s1:='';
   LFNErase:=False;
   Entries:=0;
   Result := False;
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   // Читаем ципочку кластеров в FAT пока не встретим $FFF
   Result := ReadClusterChain(Cluster, P, Size);
   // проверим нет ли ошибки с диском
   if not Result then Exit;
   // установим количество каталогов
   Size := Size div 32;
   // создаем поточный объект в памяти
   ADIR := TMemoryStream.Create;
   // P = начало каталога
   P1 := P;
 NextSector:
    s:='';
     FillChar(DIR_Entry, SizeOf(DIR_Entry), 0);
    // Проверить признак конца каталога
    if (Byte(Pointer(Longint(P1)+$00)^) = $00)  then
//      if (Byte(Pointer(Longint(P1)+$0B)^) = $00)  then
      goto EndDir;
 
    // Проверить наличие данных в элементе каталга
     if Byte(Pointer(P1)^) = $e5 then
        DIR_Entry.Erased := True else
        DIR_Entry.Erased := False;
 
     // Обычный элемент или компонента длинного имени?
     if (Byte(Pointer(Longint(P1)+$0b)^) = $0F) then
     Begin
           Inc(Longint(P1), SizeOf(TDIRENTRY));
           Goto NextSector;
      end;
{     if ((Byte(Pointer(P1)^) and $3F) = 37) then
     Begin
           Inc(Longint(P1), SizeOf(TDIRENTRY));
           Goto NextSector;
     end
     else
     Goto LongNameComponent;}
 
     // Проверить признак метки если "True" пропустим его...
     if Byte(Pointer(Longint(P1)+$0b)^) = ATTR_VOLUME then
     Begin
        Inc(Longint(P1), SizeOf(TDIRENTRY));
        Goto NextSector;
     end;
 
     Begin
     // Обрабатываем короткое имя
        if ((Byte(Pointer(Longint(P1)+$0b)^) and ATTR_DIRECTORY) = 0) and
        (P1^.Ext[1] <> chr($20))then
        s:=P1^.Name+'.'+P1^.Ext else
        s:=P1^.Name;
        for j:=1 to Length(s) do
          if (s[j] <> chr($20)) then
          Dir_Entry.Name:=Dir_Entry.Name+s[j];
        for j:=1 to 3 do
        Dir_Entry.Ext:=Dir_Entry.Ext+P1^.Ext[j];
        s:='';
    end;
    Goto ElementNotUsed;
 
 LongNameComponent:
        PL:=PLONGDIRENTRY(P1);
         if (PL.LFN1[1] <> WideChar(0)) and (PL.LFN1[1] <> WideChar($FFFF)) then
           For j:=1 to 5 do if (PL.LFN1[j]  <> #0) then s:=s+PL.LFN1[j];
         if (PL.LFN2[1] <> WideChar(0)) and (PL.LFN2[1] <> WideChar($FFFF)) then
           For j:=1 to 6 do if (PL.LFN2[j] <> #0) then s:=s+PL.LFN2[j];
         if (PL.LFN3[1] <> WideChar(0)) and (PL.LFN3[1] <> WideChar($FFFF)) then
           For j:=1 to 2 do if (PL.LFN3[j] <> #0) then s:=s+PL.LFN3[j];
         s1:=s+s1;
 
 
         if ((Byte(Pointer(P1)^) and $3F) <> 01) then
         Begin
           Inc(Longint(P1), SizeOf(TDIRENTRY));
           Goto NextSector;
         end;
 
       Inc(Longint(P1), SizeOf(TDIRENTRY));
        Dir_Entry.Name:=s1;
        LFNErase:=False;
       s1:='';
       s:='';
 
 ElementNotUsed:
       // Сохраним текущий сектор и смещение текущего элемента
       // Он будет нужен в будущем...
        Dir_Entry.CurrentSector:=(LongInt(P1)-LongInt(P)) div 512;
        l:=(LongInt(P1)-LongInt(P));
        l:=l-(512*Dir_Entry.CurrentSector);
        Dir_Entry.StartByteNamePerSec:=l;
        if Cluster <> 0 then
         Dir_Entry.CurrentSector:=Dir_Entry.CurrentSector+((Cluster-2)*
         SectorsPerCluster)+DataAreaSector else
         Dir_Entry.CurrentSector:=Dir_Entry.CurrentSector+RootDirSector;
        DIR_Entry.Attr := P1^.Attr;
        if  FSType = fsFAT32 then
        begin
         DIR_Entry.StartCluster  := P1^.FSIClasHi;
         DIR_Entry.StartCluster  := DIR_Entry.StartCluster shl 16;
         DIR_Entry.StartCluster  := DIR_Entry.StartCluster+P1^.FSIClasLo;
        end else
        DIR_Entry.StartCluster   := P1^.FSIClasLo;
        DIR_Entry.CreateTime     := P1^.CrtTime;
        DIR_Entry.CreateDate     := P1^.CrtDate;
        DIR_Entry.FileSize       := P1^.Size;
        DIR_Entry.LastAccessDate := P1^.LstAccDate;
        DIR_Entry.WriteLastTime := P1^.WrtTime;
        DIR_Entry.WriteLastDate := P1^.WrtDate;
 
        Inc(Longint(P1), SizeOf(TDIRENTRY));
        ADIR.Write(DIR_Entry, SizeOf(DIR_Entry));
        inc(Entries);
 Goto NextSector;
 
 EndDir:
   FreeMem(P);
   GetMem(DIR, ADIR.Size);
   ADIR.Seek(0, 0);
   ADIR.Read(DIR^, ADIR.Size);
   ADIR.Free;
   Result := True;
end;
 
 
 
  function ReadSector  (Sector: Int64; Count: Word; Var Buffer; nSize:DWORD): Boolean;
  Var
   F:TMemoryStream;
   P:Pointer;
  Begin
   FillChar(Buffer, nSize, 0);
   Result:=False;
   if Volume = 0 Then Exit;
   F := TMemoryStream.Create;
   F.SetSize(Count*BytesPerSector);
   P:=F.Memory;
   Result:=ReadLogicalSector(Volume, Sector, Count,P^);
   F.Seek(0, 0);
   if nSize > F.Size then
   F.Read(Buffer, F.Size) else
   F.Read(Buffer, nSize);
   F.Free;
  end;
 
  function WriteSector (Sector: Int64; Count: Word; Var Buffer; nSize:DWORD): Boolean;
  Var
   F:TMemoryStream;
   P:Pointer;
  Begin
   Result:=False;
   if Volume = 0 Then Exit;
   F := TMemoryStream.Create;
   F.SetSize(Count*BytesPerSector);
   F.Seek(0, 0);
   F.Write(Buffer, F.Size);
   P := F.Memory;
   Result:=WriteLogicalSector(Volume, Sector, Count, P^);
   F.Seek(0, 0);
   if nSize > F.Size then F.Read(Buffer, F.Size)
                     else F.Read(Buffer, nSize);
   F.Free;
  end;
 
  function GetFATCluster(FATIndex: LongInt): LongInt;
  begin
     Result := 0;
     if FATCount=0 then Exit;
     if FATIndex<1 then FATIndex := 1;
     if FATIndex>FATCount then FATIndex := FATCount;
     Result := Longint(Pointer(Longint(FATSector)+(FATIndex-1)*4)^);
  end;
 
 Function Init(Drive:byte):Boolean;
  Var
    NumFreeClusters   : DWORD;   // количество свободных кластеров на диске
    TotalClusters     : DWORD;   // Количество кластеров}
  var
    P, P1, P2: Pointer;
    I, J: Longint;
    B1, B2: Byte;
    W: Word;
    L: Longint;
 Begin
 
  Result:=False;
  Volume := Drive;
  GetDiskFreeSpace(PChar(chr(drive+64)+':\'), SectorsPerCluster,BytesPerSector, NumFreeClusters, TotalClusters);
 
  GetMem(P, BytesPerSector);
  if not ReadLogicalSector(Volume,0,1,P^) then
  begin
    FreeMem(P);
    Exit;
  end;
 
  if PBoot32(P)^.bsFATz16 = 0 Then
  with PBoot32(P)^ do
  Begin
    for I := 1 to 8 do FSName[I] := bsFSType[I];
    for I := 1 to 11 do VolumeName[I] := bsVolLab[I];
    FSType            := fsFAT32;
    VolumeSerial      := bsVolID;
    PhysicalVolume    := bsDrvNum;
    LogicalSectors    := bsTolSec32;
    SectorsPerCluster := bsSecPerClus;
    BytesPerSector    := bsBytePerSec;
    FATCount          := bsNumFATs;
    GetMem(FATSector, FATCount*4);
    SectorsPerFAT     := bsFATSz32;
    I                 := bsRsvdSecCnt;
    If bsExtFlags and (1 shl 7) <> 0 Then
    ActiveFAT         := bsExtFlags and $F;
    RootDirCluster    := bsRootClus;
    DataAreaSector    := bsRsvdSecCnt + FATCount * SectorsPerFAT;
    RootDirSector     := DataAreaSector + (RootDirCluster-2) * SectorsPerCluster;
  end else
  Begin
   with PBoot(P)^ do
    Begin
    for I := 1 to 8 do FSName[I] := bsFSType[I];
    for I := 1 to 11 do VolumeName[I] := bsVolLab[I];
    if (TotalClusters > 4086) or (bsToolSec12 = 0) then
     Begin
      FSType := fsFAT16;
      LogicalSectors    := bsToolSec16;
     end else
     Begin
      FSType := fsFAT12;
      LogicalSectors    := bsToolSec12;
     end;
    VolumeSerial      := bsVolID;
    PhysicalVolume    := bsDrvNum;
    SectorsPerCluster := bsSecPerClus;
    BytesPerSector    := bsBytePerSec;
    FATCount          := bsNumFATs;
    GetMem(FATSector, FATCount*4);
    SectorsPerFAT     := bsFATSz16;
    I                 := bsRsvdSecCnt;
    ActiveFAT         := 0;
    RootDirEntries    := bsRootEntCnt;
    RootDirSector     := bsRsvdSecCnt+SectorsPerFAT*FATCount;
    RootDirCluster    := 0;
    DataAreaSector    := RootDirSector+((RootDirEntries*32+BytesPerSector-1) div BytesPerSector);
    end;
  end;
    // Заполняем адреса файловых структур 1/2
    // в FATSector
    Longint(FATSector^) := I;
    P1 := FATSector;
    Inc(Longint(P1), 4);
    if FATCount>1 then
    for J := 2 to FATCount do
    begin
      I := I+SectorsPerFAT;
      Longint(P1^) := I;
      Inc(Longint(P1), 4);
    end;
 
   dsBytePerSector:=BytesPerSector;
   EndingCluster :=((LogicalSectors-DataAreaSector) div SectorsPerCluster)+1;
   FreeMem(P);
   if FSType = fsNone then Exit;
 
   GetMem(P, SectorsPerFAT*FATCount*BytesPerSector);
   if not ReadSector(GetFATCluster(1), SectorsPerFAT*FATCount,
   P^, SectorsPerFAT*FATCount*BytesPerSector) then
      begin
         FreeMem(P);
         Exit;
      end;
   FATSize := EndingCluster-1;
   GetMem(FAT, FATSize*FATCount*4);
   FillChar(FAT^, FATSize*FATCount*4, 0);
   P2:= FAT;
   if FSType = fsFAT12 then
      begin
         for J := 0 to FATCount-1 do
             begin
                P1 := Pointer(Longint(P)+J*SectorsPerFAT*BytesPerSector+3);
                for I := 1 to FATSize div 2 do
                    begin
                       B1 := Byte(P1^); Inc(Longint(P1));
                       B2 := Byte(P1^) and $0F;
                       W := B2; W := (W shl 8) or B1;
                       L := W;
                       Longint(P2^) := L and FAT_MASK_12;
                       Inc(Longint(P2), 4);
                       B1 := Byte(P1^) and $F0; Inc(Longint(P1));
                       B2 := Byte(P1^); Inc(Longint(P1));
                       W := B2; W := (W shl 4) or (B1 shr 4);
                       L := W;
                       Longint(P2^) := L and FAT_MASK_12;
                       Inc(Longint(P2), 4);
                    end;
                if Odd(FATSize) then
                   begin
                      B1 := Byte(P1^); Inc(Longint(P1));
                      B2 := Byte(P1^) and $0F;
                      W := B2; W := (W shl 8) or B1;
                      L := W;
                      Longint(P2^) := L and FAT_MASK_12;
                   end;
             end;
      end else
   if FSType = fsFAT16 then
      begin
         for J := 0 to FATCount-1 do
             begin
                P1 := Pointer(Longint(P)+J*SectorsPerFAT*BytesPerSector+4);
                for I := 1 to FATSize do
                    begin
                       L := Word(P1^); Inc(Longint(P1), 2);
                       Longint(P2^) := L and FAT_MASK_16;
                       Inc(Longint(P2), 4);
                    end;
             end;
      end else
        if FSType = fsFAT32 then
      begin
         for J := 0 to FATCount-1 do
             begin
                P1 := Pointer(Longint(P)+J*SectorsPerFAT*BytesPerSector+8);
                for I := 1 to FATSize do
                    begin
                       L := Longint(P1^);
                       Inc(Longint(P1), 4);
                       Longint(P2^) := L and FAT_MASK_32;
                       Inc(Longint(P2), 4);
                    end;
             end;
      end;
   FreeMem(P);
end;
 
 
function GetFATEntry(Cluster: Int64): Longint;
Var
 CopyOfFAT:Byte;
begin
   Result := -1;
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   if ActiveFAT = 0 then
   CopyOfFAT := FATCount else
   CopyOfFAT := ActiveFAT;
   Cluster := Cluster-2;
   CopyOfFAT := CopyOfFAT-1;
   Result := Longint(Pointer(Longint(FAT)+CopyOfFAT*FATSize*4+Cluster*4)^);
   if FSType = fsFAT12 then Result := Result and FAT_MASK_12 else
   if FSType = fsFAT16 then Result := Result and FAT_MASK_16 else
      Result := Result and FAT_MASK_32;
end;
 
procedure SetFATEntry(Cluster: Int64; Value: Longint);
Var
 CopyOfFAT:Byte;
begin
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   if ActiveFAT = 0 then CopyOfFAT := FATCount else
   CopyOfFAT := ActiveFAT;
//   if Cluster < 2 then Cluster := 2;
//   if Cluster > EndingCluster then Cluster := EndingCluster;
   Cluster := Cluster-2;
   CopyOfFAT := CopyOfFAT-1;
   if FSType = fsFAT12 then Value := Value and FAT_MASK_12 else
   if FSType = fsFAT16 then Value := Value and FAT_MASK_16 else
      Value := Value and FAT_MASK_32;
   Longint(Pointer(Longint(FAT)+CopyOfFAT*FATSize*4+Cluster*4)^) := Value;
end;
 
 
FUNCTION GetCluster(Sector: Int64):Int64;
BEGIN
      if (Sector - DataAreaSector >= 0) and (LogicalSectors -Sector >= 0) then
      GetCluster :=(Sector-DataAreaSector) div SectorsPerCluster
      else
      Result := 0;
END;
 
function ReadCluster(Cluster: Int64; var Buffer; BufferSize: Longint): Boolean;
var P: Pointer;
    I: Int64;
begin
   Result := False;
   if Cluster < 1 then Cluster := RootDirCluster;
   Cluster := Cluster-2;
   GetMem(P, BytesPerSector*SectorsPerCluster);
   I := DataAreaSector + (SectorsPerCluster*Cluster);
   Result := ReadSector(I, SectorsPerCluster, Buffer,
   BytesPerSector*SectorsPerCluster);
   FreeMem(P);
end;
 
function WriteCluster(Cluster: Int64; var Buffer; BufferSize: Longint): Boolean;
var P: Pointer;
    I: Int64;
begin
   Result := False;
   if FSType = fsNone then Exit;
   if FATSize = 0 then Exit;
   if Cluster < 1 then Cluster := RootDirCluster;
   Cluster := Cluster-2;
   GetMem(P, BytesPerSector*SectorsPerCluster);
   FillChar(P^, BytesPerSector*SectorsPerCluster, 0);
   if BufferSize > BytesPerSector * SectorsPerCluster then
   BufferSize := BytesPerSector*SectorsPerCluster;
   Move(Buffer, P^, BufferSize);
   I := DataAreaSector+SectorsPerCluster*Cluster;
   Result := WriteSector(I, SectorsPerCluster, P^,
   BytesPerSector*SectorsPerCluster);
   FreeMem(P);
end;
 
 
function WriteClusterChain(StartCluster: Longint; Buffer: Pointer; BufferSize: Longint): Boolean;
var ClusterSize: Longint;
    I: Int64;
begin
   Result := False;
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   if StartCluster < 1 then StartCluster := RootDirSector;
   ClusterSize := BytesPerSector*SectorsPerCluster;
   I := StartCluster;
   while ValidCluster(I) do
     begin
        if BufferSize<ClusterSize then
           begin
              Result := WriteCluster(I, Buffer^, BufferSize);
              Break;
           end else Result := WriteCluster(I, Buffer^, ClusterSize);
        if not Result then Break;
        Longint(Buffer) := Longint(Buffer)+ClusterSize;
        BufferSize := BufferSize-ClusterSize;
        I := GetFATEntry(I);
     end;
end;
 
function ReadClusterChain(StartCluster: Int64; var Buffer: Pointer; var BufferSize: Longint): Boolean;
var I, J:Int64;
    P: Pointer;
    F: TMemoryStream;
    B: Boolean;
begin
   Result := False;
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   if StartCluster < 1 then StartCluster := RootDirCluster;
   I := StartCluster;
   J := BytesPerSector*SectorsPerCluster;
   GetMem(P, J);
   F := TMemoryStream.Create;
   repeat
     if not ValidCluster(I) then Break;
     B := ReadCluster(I, P^, J);
     if not B then
        begin
           Result := False;
           Break;
        end;
     Result := True;
     F.Write(P^, J);
     I := GetFATEntry(I);
   until False;
   FreeMem(P);
   Buffer := NIL;
   BufferSize := 0;
   if Result then
      begin
         BufferSize := F.Size;
         GetMem(Buffer, BufferSize);
         F.Seek(0, 0);
         F.Read(Buffer^, BufferSize);
      end;
   F.Free;
end;
 
function SeekForChainStart(Cluster: Int64): Longint;
var I: DWORD;
    J:LongInt;
    B: Boolean;
begin
   Result := -1;
   if FSType = fsNone then Exit;
   if FAT = NIL then Exit;
   if FATSize = 0 then Exit;
   if Cluster < 1 then Cluster := RootDirCluster;
   J := -1;
   repeat
     B := False;
     for I := 2 to EndingCluster do
         if GetFATEntry(I) = Cluster then
            begin
               J := I;
               Cluster := I;
               B := True;
               Break;
            end;
   until not B;
   Result := J;
end;
 
 
function ValidCluster(Cluster: Int64): Boolean;
begin
   Result := (Cluster>=2) and (Cluster<=EndingCluster);
end;
 
 
 
 
 
 
Procedure Done;
Begin
   if FATSector <> NIL then FreeMem(FATSector);
   if FAT <> NIL then FreeMem(FAT);
end;
 
 
(******************************************************************************)
 
procedure ParseDOSTime(Time: Word; var Hour, Minute, Second: Word);
begin
  Second := (Time and $001f)*2;
  Minute := (Time and $07e0) shr 5;
  Hour := (Time and $f800) shr 11;
end;
 
procedure ParseDOSDate(Date: Word; var Day, Month, Year: Word);
begin
  Day := Date and $001f;
  Month := (Date and $01e0) shr 5;
  Year := ((Date and $fe00) shr 9) + 1980;
end;
 
 
function GetShortName(Name: String): String;
var S: String;
    I: Longint;
begin
   SetLength(S, 10000);
   I := GetShortPathName(PChar(Name), @S[1], 10000);
   SetLength(S, I);
   Result := S;
end;
 
 
function FormatDiskSize (Value: TLargeInteger): string;
const
  SizeUnits: array[1..5] of string = (' Bytes', ' KB', ' MB', ' GB', 'TB');
var
  SizeUnit: Integer;
  Temp: TLargeInteger;
  Size: Integer;
begin
  SizeUnit := 1;
  if Value < 1024 then
    Result := IntToStr(Value)
  else begin
    Temp := Value;
    while (Temp >= 1000*1024) and (SizeUnit <= 5) do begin
      Temp := Temp shr 10; //div 1024
      Inc(SizeUnit);
    end;
    Inc(SizeUnit);
    Size := (Temp shr 10); //div 1024
    Temp := Temp - (Size shl 10);
    if Temp > 1000 then
      Temp := 999;
    if Size > 100 then
      Result := IntToStr(Size)
    else if Size > 10 then
      Result := Format('%d%s%.1d', [Size, DecimalSeparator, Temp div 100])
    else
      Result := Format('%d%s%.2d', [Size, DecimalSeparator,
        Temp div 10])
  end;
  Result := Result + SizeUnits[SizeUnit];
end;
 
 
function DosToWin(St: string): string;
var
  Ch: PChar;
begin
  Ch := StrAlloc(Length(St) + 1);
  OemToAnsi(PChar(St), Ch);
  Result := Ch;
  StrDispose(Ch)
end;
 
 
end.