http://sulfurzona.ru/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Wing-Thunder Game (fly simulator)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
Скачать игру Крыло-Гром (Wing-Thunder) бесплатно
 
 

Паскаль для новичков (часть 32)

 
Спрашивали? Отвечаю…
 

Ресурсные файлы

 
Автор: Владислав Демьянишин
 
Паскаль для новичковПриходилось ли вам создавать программы, которым для нормальной работы требовалось присутствие нескольких десятков или сотен мелких файлов, содержащих всевозможные данные, как то шрифты, изображения интерфейсных элементов, звуковых эффектов, и прочего?
  
Вот и мне приходилось. При этом такое количество файлов могло занимать на жестком диске гораздо больше места, чем следовало.
При архивации даже в единый архив такие мелкие файлы сжимались плохо. Одновременно могло быть доступно всего лишь немного файлов, и следовало их закрывать, чтобы иметь возможность получить доступ к другим.
  
К тому же, данными из этих файлов могли воспользоваться другие программисты, что нарушило бы мои авторские права. Например, я старался, рисовал оригинальные шрифты, а кто-то мог взять и, извиняюсь, спионерить мой шрифт.
  
В совокупности всех этих недостатков вырисовывалась невеселая перспектива. Тогда мне пришла в голову уже не новая, но мысль ;O)
сделать модуль, который бы позволял группировать массу файлов в один ресурсный файл таким образом, чтобы затем программа могла получить доступ к любому из вложенных файлов, при этом “на горизонте” сразу нарисовались следующие преимущества:
   а) позволяет использовать лишь одну файловую переменную, хотя предоставляет доступ к содержимому большого количества файлов;
   б) обеспечивает привычный интерфейс работы с файлами;
   в) при загрузке ресурсного файла в XMS-память обеспечивает сверхбыстрый доступ к содержимому файлов, без износа дисковых накопителей (при этом нет задержек из-за медлительности оных);
   г) обеспечивает эффективное использование дискового пространства при хранении ресурсного файла на диске, за счет объединения группы файлов в одну файловую структуру – ресурсный файл;
   д) обеспечивает высокое сжатие информации при архивировании ресурсного файла обычными архиваторами, такими как RAR, ZIP и другие;
  
Теперь чувствуете, чем запахло? ;O) Правильно, снова будем составлять модуль, на этот раз назовем его ResFiles. Так как для работы модуля нам понадобится extended-память, то в интерфейсной части ссылаемся на модуль XMS. Для ясности, следует разработать структуру хранения вложенных файлов в ресурсном файле так, чтобы можно было легко найти что-либо. Такую структуру назовем “заголовком вложенного файла” и объявим тип-запись TResFileRec, которая будет озаглавливать каждый вложенный файл, причем содержимое самого вложенного файла будет следовать сразу за его заголовком. За последним байтом вложенного файла может следовать заголовок и содержимое следующего вложения, и так сколько угодно раз. Итак, поля заголовка: FileName – должен содержать имя вложенного файла; NoUse – не используется, нужен исключительно в целях выравнивания размера структуры;
ID – идентификатор, показывающий, что это вложенный файл, а не что-то другое и всегда равен константе ResSignature, позволяет выявить повреждение заголовка и предотвратить ошибочное чтение данных; FOfs – смещение содержимого вложенного файла в байтах относительно начала ресурсного файла, обычно указывает на байт, следующий за заголовком; FSize – размер вложенного файла в байтах без учета размера заголовка, по сути, это размер файла до его добавления в ресурсный файл. Константа ResSignature являет собой идентификатор вложенного файла.
  
Далее следует объявить структуру, которая бы содержала информацию о вложенном файле, к которому в данный момент осуществляется доступ. Опишем что-то вроде стандартного файлового типа под названием TFile и полями: Name – имя файла,
Index – индекс заголовка файла в списке заголовков, начиная с нуля:
FOfs и FSize – аналогичны одноименным полям из заголовка; FPos – текущая позиция для операции чтения из вложенного файла.
  
Для операций чтения объявим тип TArrByte для временного буфера.
  
Для работы с ресурсным файлом создадим объектный тип TResource, причем следует предусмотреть возможность загрузки ресурсного файла в XMS-память, а в случае экономии дополнительной памяти или ее нехватки возможность получать доступ к вложениям прямо из ресурсного файла на диске. Для этого объявим тип TResourceType с двумя возможными значениями на такой случай.
  
Последним в интерфейсной части идет объявление экспортируемой функции AddFileToResource, рассмотрение задачи которой еще впереди.
 
Unit ResFiles;
 
interface
 
Uses XMS;
 
const ResSignature = 'RF';
type
     TArrByte = array [0..64000] of byte;
     { структура заголовка вложенного файла }
     TResFileRec = record
       FileName : string[12];
       NoUse : byte;
       ID : array [0..1] of char;
       FOfs,
       FSize : longint;
     end;
     { структура читаемого файла }
     TFile = record
       Name : string[12];
       Index : word;
       FOfs, FSize, FPos : longint;
     end;
     TResourceType = ( rtXMS, rtDisk );
     TResource = object
      constructor Create( FileName : string; rType : TResourceType );
      destructor Free;
      function SearchFile( FileName : string; var f : TFile ) : integer;
      procedure GetFile( Index : word; var f : TFile );
      function rIoResult : word;
      procedure rAssign( var f; FileName : string );
      procedure rReset( var f );
      function rFileSize( var f ) : longint;
      procedure rSeek( var f; Pos : longint );
      procedure rBlockread( var f, DosBuf; ACount : word; var result : word );
      procedure rClose( var f );
      function Count : longint;
     private
       FName : string[12];
       FResBuf : TXMSPtr;
       FResOfs : longint;
       FCount,
       FIoResult : word;
       FResType : TResourceType;
     end;
 
function AddFileToResource( ResFileName, AddFileName : string ) : word;
 
   В блоке реализации объявляем константу tsize просто, чтобы сократить размер листинга.
 
implementation
 
const tsize = sizeof( TResFileRec );
 
Конструктор объекта должен получить два параметра: имя ресурсного файла и способ доступа к его вложениям. В начале конструктор проверяет наличие ресурсного файла, и в случае неудачи создание экземпляра объекта завершается аварийно.
  
Далее следует repeat-цикл контроля и подсчета вложений. Если не будет найдено ни единого вложения, либо хотя бы в одном заголовке будет обнаружена ошибка, конструктор будет завершен аварийно.
  
Потом выполняется определение размера таблицы заголовков в килобайтах плюс 1 килобайт про запас с тем, чтобы поместить таблицу в XMS-памяти. В случае FResType=rtXMS в XMS-памяти следует разместить не только таблицу заголовков, но и сам ресурсный файл. Поэтому в поле FResOfs будет записано смещение содержимого ресурсного файла в байтах относительно начала XMS-буфера, а к значению локальной переменной XMSBufSize будет добавлен размер ресурсного файла в килобайтах плюс один, чтобы потом выделить память этого размера под XMS-буфер. В случае же FResType=rtDisk доступ к вложениям будет осуществляться прямо с диска, и, значит, поле FResOfs устанавливается в ноль.
  
Затем следует операция получения XMS-памяти и загрузка заголовков вложений в таблицу в XMS-буфере.
  
Если FResType=rtDisk, то на этом с конструктором все, иначе следует выделить память под временный буфер Buf размером MaxBuf и выполнить загрузку ресурсного файла целиком в XMS-буфер сразу за таблицей заголовков по смещению FResOfs. Файл закрываем. Возможно, кто-то из читателей про себя скажет: “Ну об этом мог бы и не напоминать” ;O)
 
constructor TResource.Create( FileName : string; rType : TResourceType );
const MaxBuf = 1000;
var d : word;
       Size, boffs, XMSBufSize : longint;
       Rec : TResFileRec;
       Buf : ^TArrByte;
       f : file;
begin
FName := FileName;
Assign( f, FName ); {$I-}
Reset( f, 1 );      {$I+}
if IoResult <> 0 then Fail;
Size := FileSize( f );
FResType := rType;
FIoResult := 0;
{ подсчет вложений }
FCount := 0;
repeat
 blockread( f, Rec, tsize, d );
 if Rec.ID <> ResSignature then begin
     close( f ); Fail; end;
 Seek( f, Rec.FOfs + Rec.FSize );
 inc( FCount );
until Size <= FilePos( f );
 
if FCount <= 0 then begin Close( f ); Fail; end;
{ определение размера таблицы заголовков }
XMSBufSize := FCount;
XMSBufSize := ((XMSBufSize * tsize) div 1024) + 1;
if FResType = rtXMS then begin
    FResOfs := XMSBufSize * 1024;
    XMSBufSize := XMSBufSize + (Size div 1024) + 1;
    end
    else FResOfs := 0;
{ получение XMS-памяти }
if GetXMS( FResBuf, XMSBufSize ) <> 0 then Fail;
{ загрузка заголовков в таблицу }
boffs := 0;
Seek( f, 0 );
repeat
 blockread( f, Rec, tsize, d );
 Seek( f, Rec.FOfs + Rec.FSize );
 moveMemToXMS( FResBuf, Rec, boffs, tsize );
 boffs := boffs + tsize;
until Size <= FilePos( f );
{ загрузка ресурсного файла в XMS-память }
if FResType = rtXMS then begin
   if MaxAvail < MaxBuf then begin
      Close( f );
      FreeXMS( FResBuf );
      Fail;
      end;
   GetMem( Buf, MaxBuf );
 
   Seek( f, 0 );
   boffs := FResOfs;
   repeat
    blockread( f, Buf^, MaxBuf, d );
    moveMemToXMS( FResBuf, Buf^, boffs, MaxBuf );
    boffs := boffs + MaxBuf;
   until Size <= FilePos( f );
   FreeMem( Buf, MaxBuf );
   end;
Close( f );
end;
 
Уничтожать экземпляр объекта следует вызовом следующего деструктора, в функцию которого входит лишь освобождение XMS-памяти.
 
destructor TResource.Free;
begin
FreeXMS( FResBuf );
end;
 
Следующий метод позволяет найти вложенный файл с именем FileName в таблице заголовков и, в случае успеха, в переменную F получить полную информацию о вложении и в качестве результата вернет нуль. Ненулевой результат будет означать, что заданный файл не найден.
 
function TResource.SearchFile( FileName : string; var f : TFile ):integer;
var offs : longint;
       i : word;
       Rec : TResFileRec;
begin
SearchFile := -1;
offs := 0;
i := 0;
repeat
 moveXMSToMem( FResBuf, Rec, offs, tsize );
 if Rec.FileName = FileName then begin
    f.Name := FileName;
    f.Index := i;
    f.FOfs := Rec.FOfs;
    f.FSize := Rec.FSize;
    f.FPos := 0;
    SearchFile := 0;
    exit;
    end;
 offs := offs + tsize;
 inc( i );
until i > FCount – 1;
end;
 
Предусмотрена возможность получения информации о вложении с помощью метода GetFile при указании индекса вложения и файловой переменной, в которую будет получена вся информация. В случае ошибочного значения индекса Index в поле Name переменной F будет возвращена пустая строка.
 
procedure TResource.GetFile( Index : word; var f : TFile );
var offs : longint;
       Rec : TResFileRec;
begin
f.Name := '';
if Index > FCount – 1 then exit;
offs := Index*SizeOf( TResFileRec );
moveXMSToMem( FResBuf, Rec, offs, tsize );
f.Name := Rec.FileName;
f.Index := Index;
f.FOfs := Rec.FOfs;
f.FSize := Rec.FSize;
f.FPos := 0;
end;
 
Предусмотрим получение кода ошибки функцией rIoResult, которая возвратит значение поля FIoResult объекта, и обнулит его.
 
function TResource.rIoResult : word;
begin
rIoResult := FIoResult;
FIoResult := 0;
end;
 
Для получения количества вложений служит метод Count, возвращающий значение поля FCount объекта.
 
function TResource.Count : longint;
begin
Count := FCount;
end;
 
С информационными методами разобрались. Теперь предстоит составить методы работы непосредственно с вложениями.
  
Для аналогии со стандартными процедурами объявим метод rAssign с таким же списком входных параметров. Задача оного заключается в поиске затребованного файла, и в случае успеха возвращении файловой переменной F со всей необходимой информацией для доступа к вложению. При неудачном поиске в поле FIoResult заносится код ошибки “Файл не найден”.
 
procedure TResource.rAssign( var f; FileName : string );
var af : TFile absolute f;
begin
if SearchFile(FileName,af)<0 then FIoResult := 2;
end;
 
Следующий метод на столько простой, что проще и быть не может. Он позиционирует указатель вложенного файла на его начало, чтобы следующий доступ к файлу происходил именно с этой позиции.
 
procedure TResource.rReset( var f );
var af : TFile absolute f;
begin
af.FPos := 0;
end;
 
Продолжение следует…
 
© Владислав Демьянишин
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ.
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 32): Ресурсные файлы
 
 
 
 
 
 
На главную страницу На предыдущую страницу На начало страницы