Формат файлов RES применительно к BITMAP-ресурсам
Автор: SOA
Написать данную статью автора побудило недостаточное количество информации в рунете и за его пределами для написания своего компилятора ресурсов типа BITMAP. Начнём!
RES-файл представляет собой файл, в котором записаны поверх друг друга заголовки ресурсов и соответственно сами данные ресурсов:
Header (Заголовок)
DATA (Данные)
Header (Заголовок)
DATA (Даные)
Заголовок из себя представляет некую структуру с полями:
typedef struct {
DWORD DataSize;
DWORD HeaderSize;
DWORD TYPE;
DWORD NAME;
DWORD DataVersion;
WORD MemoryFlags;
WORD LanguageId;
DWORD Version;
DWORD Characteristics;
} RESOURCEHEADER;
Но эта структура лишь является эталонной моделью, как в сетях является эталонной моделью модель ISO/OSI. На практике же чаще используется менее ромоздкая конструкция:
typedef struct {
DWORD DataSize;
DWORD HeaderSize;
DWORD TYPE;
DWORD NAME;
} RESOURCEHEADER;
Разберемся, что значат поля в этой структуре.
DataSize — размер данных ресурса, т.е. данных, копируемых из файла bmp. Он всегда меньше реального размера файла на 14 бит ввиду того, что отбрасываются несколько полей заголовка файла bmp (да-да, у bmp файлов тоже есть заголовок, как впрочем и у всех остальных типов файлов).
HeaderSize — размер заголовка ресурса, т.е. размер, занимаемый всей структурой RESOURCEHEADER в байтах.
Эта структура разделена на две части: базу ResourceHeaderBase, значение которой всегда 32 и, по-видимому, отражает принадлежность ресурса к 32 битному приложению; вторая часть — ResourceHeaderOffset равна разнице между размером заголовка и базой.
TYPE — тип ресурса, в нашем случае это значение равно FFFF0200, где ключевым числом является 2 т.к. именно оно является числовым эквивалентом типа BITMAP.
Значения для других типов можно посмотреть здесь: http://msdn.microsoft.com/en-us/library/ms648009(VS.85).aspx
Записываем, конечно, в шестнадцатеричном виде.
NAME — официально это поле имеет тип DWORD. MS пишет, что на самом деле это поле является строкой с нулевым окончанием. На практике было выяснено, что в поле NAME записывается по одному коду символа в ASCII кодировке с отделением символов друг от друга нулевым битом, после чего по неизвестному автору алгоритму строка добивается нулевыми битами, после чего ставится ASCII код нуля.
Также стоит заметить, что в начале заголовка ставится значение типа DWORD, равное нулю, и в конце заголовка ставится два значения типа DWORD, равные нулю.
Рассмотрим, как на практике пишется файл ресурса по смещениям:
Смещение | Поле |
---|---|
4 | ResourceHeaderBase |
8 | FFFF |
12 | FFFF |
32 | DataSize |
36 | ResourceHeaderOffset |
40 | TYPE |
44 | NAME |
Далее, после заголовка записываются данные из bmp-файла, начиная с 14 бита, потому что, как я уже писал, часть заголовка bmp-файла отбрасывается.
Ниже приведена программа, записывающая из-bmp файла ресурс. Однако у этой программы есть одно ограничение ввиду того, что, как я уже писал, я не знаю, каким образом высчитывается количество нулевых битов в поле NAME до ASCII значения нуля, поэтому размер имени ресурса не может превышать двух символов.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; OpenDialog1: TOpenDialog; Edit1: TEdit; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; type Header = Record DataSize:DWORD; HeaderSize:DWORD; Base:DWORD; RcType:DWORD; NAME:String; End; const HearedSizeBase = 4; HeaderSizeOffset = 36; DataSizeOffset = 32; TypeOffset = 40; NameOffset = 44; var Form1: TForm1; MyRes,SrcFile: TFileStream; Heder:Header; NULL: array [0..43] of byte; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i:Integer; Buf: BYTE; Count: LongInt; FFFF:DWORD; CHR:WORD; begin for i:=0 to 43 do NULL[i]:=0; i:=FileCreate('Resource.res');//Создаем сам файл ресурса FileClose(i); MyRes:=TFileStream.Create('Resource.res',fmOpenWrite); If OpenDialog1.Execute=true then SrcFile:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead); Heder.NAME:=Edit1.Text;//Имя текущего файла для записи в ресурсы Heder.DataSize:=SrcFile.Size-14;//Размер ресурса (-14 это отбрасываемая часть заголовка файла bmp) Heder.RcType:=$0002FFFF; //ffff0200 тип ресурса //Записываем описание ресурса MyRes.Write(NULL,SizeOf(NULL)); MyRes.Seek(8,soFromBeginning); FFFF:=$0000FFFF; MyRes.Write(FFFF,SizeOf(FFFF));//Не знаю почему но эти биты должны иметь именно такое значение MyRes.Write(FFFF,SizeOf(FFFF)); MyRes.Seek(HearedSizeBase,soFromBeginning); Heder.Base:=32; MyRes.Write(Heder.Base,SizeOf(Heder.Base));//записываем базу заголовка MyRes.Seek(DataSizeOffset,soFromBeginning); MyRes.Write(Heder.DataSize,SizeOf(Heder.DataSize));//записываем размер данных MyRes.Seek(NameOffset-2,soFromBeginning);//Записываем имя ресурса For i:=0 to Length(Heder.Name) do Begin CHR:=Ord(Heder.Name[i]); MyRes.Write(CHR,SizeOf(CHR)); End; buf:=0; //Выравнивание if Length(Heder.Name)=1 Then For i:=1 to 6 do MyRes.Write(buf,SizeOf(buf)) Else Begin FFFF:=$00000000; For i:=1 to 2 do//расчет MyRes.Write(FFFF,SizeOf(FFFF)); End; FFFF:=$00001030; MyRes.Write(FFFF,SizeOf(FFFF)); i:=MyRes.Seek(0,soFromCurrent); MyRes.Seek(TypeOffset,soFromBeginning); MyRes.Write(Heder.RcType,SizeOf(Heder.RcType));//Записываем тип ресурса MyRes.Seek(i,soFromBeginning); FFFF:=$00000000; MyRes.Write(FFFF,SizeOf(FFFF)); MyRes.Write(FFFF,SizeOf(FFFF)); i:=MyRes.Seek(0,soFromCurrent); Heder.HeaderSize:=i-32;//Размер описания ресурса MyRes.Seek(HeaderSizeOffset,soFromBeginning); MyRes.Write(Heder.HeaderSize,SizeOf(Heder.HeaderSize));//записываем разницу между размером и базой заголовка MyRes.Seek(i,soFromBeginning); //пишем сам ресурс Count:=14;//Отбрасываем часть заголовка bmp файла SrcFile.Seek(14,soFromBeginning); While Count<SrcFile.Size do Begin SrcFile.Read(Buf,1); MyRes.Write(Buf,1); Count:=Count+1; End; SrcFile.Free; MyRes.Free; ShowMessage('Готово'); end; end.
В данной статье использованы материалы:
- http://msdn.microsoft.com/en-us/library/ms648027(VS.85).aspx
- http://msdn.microsoft.com/en-us/library/ms648007(VS.85).aspx
- http://msdn.microsoft.com/en-us/library/ms648009(VS.85).aspx
При использовании материалов статьи просьба давать ссылку на ресурс и на автора SOA.
Автор: SOA
Статья добавлена: 28 июля 2010
Следующая статья: Поменять местами элементы в ListView »
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
{{a:55}} (буква a — латинская) — только адрес статьи (URL);
{{статья:55}} — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
Пока нет комментариев к данной статье. Оставьте свой и он будет первым.
Оставлять комментарии к статьям могут только зарегистрированные пользователи.