Привет всем возникла необходимость програмно без помощи редакторов ресурсов создавать Res файлы. Покопавшись в сети нашел следующие страницы: 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 Откуда узнал что файл ресурсов состоит из каскада записей(HEADER) за которыми идут сами данные. По структуре заголовка тоже дана информация typedef struct { DWORD DataSize; DWORD HeaderSize; DWORD TYPE; DWORD NAME; DWORD DataVersion; WORD MemoryFlags; WORD LanguageId; DWORD Version; DWORD Characteristics; } RESOURCEHEADER; но также есть оговорка, в которой говорится что эта структура представлена только для ознакомления и она не представлена в каком либо стандартном файле ресурсов. Откуда возникает вопрос какова всетаки структура RES файла потому что если использовать эту структуру и предерживаться понятий о структуре записей в res файле описаных выше получается что если взять файл и откомпелировать его в res файл программой основанной на этом принципе и компилятором ресурсов то у компилятора ресурсов файл получается на 18 байт больше.
формат рес файла, как я помню, - просто дерево ресурсов подряд один за другим, заголовок-данные, заголовокданные. Открой хексредактором рес файл из одного ресурса и сравни его содержимое и содержимое ресурса. Найдешь хидер. Сравни со структурой.
Посмотрел в WinHex, совпадение началось res bmp 64 14 биты Размер хидера получился 64 бита для res 14 бит для bmp Размер исходного файла bmp 938 бит Значит размер данных равен 924 байта В хидере res файла удалось покачто установить смещение для DataSize оказалось равным 32
Покачто удалось установить что величина хидера для res файла всетаки вещь не постоянная и меняется в зависимости от таких величин как NAME которая на самом деле не является параметром типа DWORD в языках высокого уровня ее обычно инициируют как PCHAR. Удалось установить смещение для следующих полей Смещение Поле 32 DataSize 42 TYPE 44 NAME Смещение для поля входящего в минимальное число полей для работы с файлом ресурсов HeaderSize установить не удается.
Там идет чисто по размеру полей как в ms ссылках что я давал выше и скорее всего взято оттуда же. По форматам полей размер хидера не должен превышать 32, но в реальности размер хидера больше чем в два раза превышает 32 в моем случае был размер 64 при имени ресурса 1 и 80 при имени ресурса BITMAP_0.
Удалось определить поле HeaderSize. Оно рассчитывается по двум смещениям 1 равно 4, значение всегда равно 32 видимо отражает что ресурс предназначен для 32 битных приложений, а также используется как база при вычислении поля. 2 равно 36 по этому смещению располагается число равное разнице между размером заголовка и 32.
Сообщение для модераторов Как можно разместить статью по формату res файлов на вашем сайте. Вариант Wasm@wasm.ru не катит mail bot пишет что такого сервера нет.
Подведем итог по res файлам(в данном случае применительно к ресурсу типа BITMAP): res файл представляет собой файл в котором записаны поверх друг друга заголовки ресурсов и соответственно сами данные ресурсов. Код (Text): Header (Заголовок) DATA (Данные) Header (Заголовок) DATA (Даные) Что из себя представляет заголовок некую структуру с полями Код (Text): typedef struct { DWORD DataSize; DWORD HeaderSize; DWORD TYPE; DWORD NAME; DWORD DataVersion; WORD MemoryFlags; WORD LanguageId; DWORD Version; DWORD Characteristics; } RESOURCEHEADER; но эта структура лишь является эталонной моделью как в сетях является эталонной моделью модель ISO/OSI. На практике же чаще используется менее громоздкая конструкция Код (Text): typedef struct { DWORD DataSize; DWORD HeaderSize; DWORD TYPE; DWORD NAME; } RESOURCEHEADER; Разберемся что значат поля в этой структуре DataSize размер данных ресурса, т.е. данных копируемых из файла bmp он всегда меньше реального размера файла на 14 бит ввиду того что отбрасываются несколько полей заголовка файла bmp. HeaderSize размер заголовка ресурса т.е. размер занимаемый всей структурой RESOURCEHEADER в байтах. Равен разнице между размером заголовка и 32. TYPE тип ресурса в нашем случае это значение равно FFFF0200 где ключевым числом является 2 т.к. именно она является числовым эквивалентом типа BITMAP. Значения для других типов можно посмотреть здесь http://msdn.microsoft.com/en-us/library/ms648009(VS.85).aspx Записываем конечно в шестнадцатиричном виде. NAME официально это поле имеет тип DWORD. ms пишет что на самом деле это поле является строкой с нулевым окончанием. На практике было выяснено что поле NAME записывается по одному коду символа в ASCII кодировке с отделением символов друг от друга нулевым битом, после чего по неизвестному автору алгоритму строка добивается нулевыми битами после чего ставится ASCII код нуля. Пример поля name. Также стоит заметить что в начале заголовка ставится значение типа DWORD равное нулю и в конце заголовка ставится два значения типа DWORD равные нулю Рассмотрим как на практике пишется файл ресурса по смещениям. Код (Text): ┌────────────┬────────────────┐ │ Смещение │ Поле │ ├────────────┼────────────────┤ │ 32 │ DataSize │ │ 36 │ HeaderSize │ │ 40 │ TYPE │ │ 44 │ NAME │ └────────────┴────────────────┘ Далее после заголовка записываются данные из bmp файла начиная с 14 бита потому что как я уже писал часть заголовка bmp файла отбрасывается.
Также хотелось бы привести пример программы формирующей res файл из bmp файла. Программа написана на delphi, но никто не мешает переписать ее на asm. Также следует отметить тот факт что имя ресурса не может превышать двух символов, ввиду отсутствия информации по алгоритму выравнивания имени нулями. Код (Text): 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.