|
| |||||||||||||||||||||||
Паскаль для новичков (часть 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;
Продолжение следует…
© Владислав Демьянишин
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте можно бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Среди доступных программ есть мобильная читалка книг, менеджер переноса файлов с фото- и видеокамер на компьютер, текстовый редактор, WYSIWYG редактор, 3D аниматор, GIF аниматор, AVI аниматор, пакетный конвертор изображений, редактор электрических схем, программа для скриншотов, диспетчер тем рабочего стола и другие. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 32): Ресурсные файлы
![]()
| ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|