Загрузка...
 
Печать

Работа с двоичными ресурсами (.rc) в MS Visual C++ 2010 Express


В бесплатной MS Visual C++ 2010 Express редактор ресурсов отсутствует. Но это не беда, ведь ресурсы (и скрипты для них) можно создавать в сторонних редакторах. После чего их останется только импортировать в MSVC++. Хороший пример - редактор ResEdit. О нём мы и расскажем в данной статье.

Содержание

Структура исполняемого PE-модуля (теория)

Рис. 1 Структура PE-файла
Рис. 1 Структура PE-файла

Сокращение PE расшифровывается как Portable Executable (от англ. "переносной исполняемый"). Данный формат пришёл из UNIX, где аналогичный формат назывался COFF (Common Object File Format - Стандартный формат объектных файлов). Microsoft подвергла COFF-формат значительной переработке и в теперешнем виде исполняемые PE-файлы используются в среде Windows повсеместно. В группу PE-файлов входят:

  • Исполняемые файлы приложений Windows (.exe);
  • Динамически подсоединяемые библиотеки (Dynamic Link Library - DLL);
  • Объектные модули (obj);
  • Драйверы режима ядра.

Главной особенностью PE-модуля является простота его загрузки в память, для чего он не требует какой-либо дополнительной настройки. По сути, PE-модуль содержит слепок участка оперативной памяти. По своей структуре PE-файл чем-то напоминает слоёный пирог (см. Рис. 1). На Рис. 1 изображена общая схема PE-формата исполняемого модуля. В самом первом разделе (на рисунке он изображён сверху) разработчики отдали дань совместимости с операционной системой MS-DOS. Данный раздел давно ушёл в прошлое вместе с самой MS-DOS, но именно с него по сей день начинается любой исполняемый файл Win32. Два первых байта (MZ) - это т.н. сигнатура, которая подтверждает, что перед нами исполняемый модуль ОС MS-DOS.

Закрыть
noteЛюбопытный факт

Сокращение MZ - это инициалы сотрудника компании Microsoft Марка Збиковски (Mark Zbikowscki), разработчика структуры исполняемых модулей в ОС MS-DOS.

После сигнатуры MZ в PE-модуле идёт стандартный заголовок MS-DOS, а далее - маленькая программа-заглушка, которая обычно выводит на экран консоли сообщение: "This programm cannot be run in DOS mode." ("Эта программа не может выполняться в MS-DOS."), после чего приложение завершает свою работу. Данный кусок кода часто называют stub (от англ. "заглушка").

Сразу после заголовков MZ и PE располагается таблица секций, состоящая из структур по 40 байтов каждая. Вот наиболее часто встречающиеся секции, генерируемые различными компиляторами1:

Секция Описание
.text Секция содержит исполняемый код (Microsoft).
CODE Секция содержит исполняемый код (Borland).
.data Секция, содержащая инициализированные глобальные переменные (Microsoft).
DATA Секция, содержащая инициализированные глобальные переменные (Borland).
.bss Все неинициализированные данные; размер секции в фале равен нулю.
.CRT Секция, содержащая инициализированные данные (Microsoft).
CRT Секция, содержащая инициализированные данные (Borland).
.rdata Данные доступны только для чтения (константы, отладочная информация).

По таблице секций можно вычислить положение секции в файле, её размер и, тем самым, получить возможность посмотреть, что в ней хранится.

Ресурсы исполняемых PE-файлов (тоже теория)

Форматы исполняемых файлов, используемых в Windows, позволяют хранить внутри них особые типы данных - ресурсы.
К стандартным ресурсам Windows относятся:

  • Диалоговые окна (Dialog)
  • Значки (Icons)
  • Определения меню (Menu)
  • Определения панелей инструментов (Toolbar)
  • Растровые изображения (Bitmap)
  • Курсоры мыши (Cursor)

Кроме стандартных, программист может сам определить свои собственные ресурсы.
Помимо редактора ресурсов, MS Visual C++ также включает в себя встроенный язык описания ресурсов. Его директивы можно увидеть, если открыть в текстовом редакторе, предварительно созданный в MS Visual C++, шаблон ресурсов (фaйл с расширением .rc).
Ресурсы можно создавать двумя способами:

  • Описать ресурсы программы в текстовом ASCII файле с расширением RC. (Этот способ практически не используется).
  • Создать ресурс при помощи специализированного редактора ресурсов в режиме WYSIWYG (What You See Is What You Get – “что видишь, то и получаешь”).

В состав MS Visual C++ всех версий (кроме бесплатных редакций Express) входит редактор ресурсов, позволяющий визуально создавать и редактировать стандартные виды ресурсов. На самом деле это не один, а несколько редакторов (для каждого вида ресурсов), но в литературе их часто рассматривают как один. После компиляции Проекта все задействованные в нём ресурсы включаются в результирующий файл.
Существуют и другие редакторы ресурсов от сторонних производителей (ResEdit, PE-Editor, Resource Workshop и др.). Все они имеют схожий функционал и пользовательский интерфейс, не требующий много времени на освоение.

Создаём базовое оконное приложение Windows

Читай здесь: Создание приложений (Cpp, Win32)

В статье, расположенной по вышеприведённой ссылке, мы создаём пустое Решение/Проект Test01, добавляем в него файл исходного кода Main.cpp, вводим исходный код базового оконного приложения и отправляем Проект на компиляцию.
Далее будем работать с этим Проектом (Test01). Поэтому...

Закрыть
noteОбрати внимание

Напомним, проект изначально должен быть пустым, для чего ставится соответствующая галка в мастере (см Рис. 1).

Рис. 1
Рис. 1

Создаём Main.cpp

  • Добавь в него файл исходного кода Main.cpp, если не сделал этого ранее.

Добавленный файл сразу откроется в MSVC++. Сейчас он пуст.

Main.cpp
// Файл: Main.cpp 
// Описание: Шаблон оконного приложения Windows. Простейшее (базовое) приложение Windows,
// выводящее на экран окно.
// www.igrocoder.ru 2015
// При использовании просьба указывать ссылку на источник.

#define STRICT
#define WIN32_LEAN_AND_MEAN	// Уменьшаем кол-во используемых компонентов в программе.

#include <windows.h>
#include <windowsx.h>

// Объявление оконной процедуры
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

// Реализация главной функции программы
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hWnd;			// Дескриптор окна
	MSG msg;			// Структура сообщения
	WNDCLASSEX wndClass;	// Объявляем экземпляр класса программы на базе WNDCLASSEX
	wndClass.cbSize=sizeof(wndClass);
	wndClass.style=CS_HREDRAW|CS_VREDRAW;	// Перерисовываем при перетаскивании
	wndClass.lpfnWndProc=WindowProc;		// Оконная процедура
	wndClass.cbClsExtra=0;
	wndClass.cbWndExtra=0;
	wndClass.hInstance=hInstance;			// Экземпляр окна
	wndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);		// Пиктограмма приложения
	wndClass.hIconSm=LoadIcon(NULL, IDI_APPLICATION);	// Малая пиктограмма приложения
	wndClass.hCursor=LoadCursor(NULL, IDC_ARROW);		// Курсор при наведении на окно
	wndClass.hbrBackground=GetStockBrush(BLACK_BRUSH);	// Закрашиваем окно чёрным цветом
	wndClass.lpszMenuName=NULL;							// Дескриптор Главного меню окна. Сейчас оно не нужно
	wndClass.lpszClassName="GameClass"; // Обзываем оконный класс.
	
	// Если класс не зарегистрируется, досрочно прерываем выполнение программы
	if(!RegisterClassEx(&wndClass)) return FALSE;

	// Создание окна на основе зарегистрированного класса
	hWnd=CreateWindowEx(
		WS_EX_TOPMOST,		// Дополнительный стиль окна
		"GameClass",		// Класс окна.
		"My game title",			// Текст заголовка (на верхнем тулбаре).
		WS_OVERLAPPEDWINDOW,		// Обычное окно с кнопками в правом верхнем углу.
		0, 0,					// Координаты X и Y
		GetSystemMetrics(SM_CXSCREEN),	// Ширина окна
		GetSystemMetrics(SM_CYSCREEN),	// Высота окна
		NULL,							// Дескриптор родительского окна
		NULL,							// Дескриптор меню
		hInstance,						// Дескриптор экземпляра приложения
		NULL);							// Дополнительные данные

	if(hWnd==NULL) return(FALSE);

	// Собственно, показываем окно
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	// Очищаем структуру сообщения
	ZeroMemory(&msg, sizeof(MSG));

	// Процедура выборки сообщений
	// Продолжаем бесконечно, пока не получим сообщение WM_QUIT (завершение).
	while(msg.message != WM_QUIT)
	{	// Просмотрим очередь: вдруг там ожидает сообщение...
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		// Есть сообщение! Обработаем его как обычно...
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
		else
		{
			// Нет сообщений, ожидающих обработки.
			// Значит приступим к выполнению критичных ко времени операций.
			// Например, займёмся рендерингом.
		}
	}

	//Удаляем регистрацию класса
	UnregisterClass("GameClass", hInstance);
	
	// Выходим из приложения
	return (msg.wParam);
}

// Оконная процедура
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_DESTROY:			// В случае этого сообщения...
		PostQuitMessage(0);		// Говорим Windows закрыть приложение
		break;

		// В случае любых других сообщений...
	default: return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	return 0;
}

Устанавливаем многобайтовую кодировку (multibyte encoding)

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...

  • В Обозревателе решений щёлкаем по названию только что созданного Проекта Test01.
  • Во всплывающем меню выбираем "Свойства"
  • В появившемся окне установки свойств Проекта жмём "Свойства конфигурации", в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
  • Жмём ОК.

Image

Отключаем инкрементную компоновку (incremental linking)

Иначе при копиляции возможны ошибки вроде этой:

Error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства.
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации -> Компоновщик -> Общие (Configuration Properties -> Linker -> General), в правой части в строке "Включить инкрементную компоновку" ставим значение "Нет (/INCREMENTAL:NO)".
  • Жмём ОК.

  • Сохрани Решение/Проект (Файл -> Сохранить все).

Компилируем Проект Test01

Проект готов к компиляции.

  • Скомпилируй Проект/Решение (кнопка с зелёным треугольником на Панели инструментов MSVC++ или <F5> на клавиатуре).

Image
Если весь код был введён без ошибок, после компиляции запустится приложение, отображающее окно с чёрным фоном. Большинство современных игр начинается на чёрном фоне.

Мутим двоичные ресурсы в MSVC++2010 Express

Продолжаем работать с Проектом Test01.

Организуем в нашем приложении оригинальные значки (иконки)

У каждой игры есть стартовый (запускной) .exe файл, который в Проводнике Windows отображается с оригинальным значком с логотипом игры. В каждом таком приложении как правило используется два значка, в исходном виде представляющие собой растровые изображения с расширением .ico:

  • Значок размером 32х32 точек, отображаемый в Проводнике Windows.
  • Уменьшенная копия первого значка размером 16х16 точек, отображаемая на тулбаре (верхней полоске) окна приложения.

Оба значка можно без труда создать и экспортировать в файл формата .ico в любом человеческом графическом редакторе (в идеале Photoshop). Битность может быть любая, но желательно не больше 8-битной палитры (256 цветов). Часто оба значка выглядят одинаково, но отличаются лишь размером.
В моём случае я нашёл готовое ICO-изображение в Интернете по запросу "ico 16x16". Скачанный файл почему-то оказался больше - 128х128 пикселей. Я открыл его в бесплатной программе-просмотрщике XnView(external link) и поочерёдно изменил размер изображения (Изображение -> Изменение размера... ):

  • сначала до размеров 32х32, пересохранив результат как Test01.ico,
  • затем до размеров 16х16, пересохранив результат как Test01_sm.ico.
Закрыть
noteКстати

XNView также без труда открывает и пересохраняет в .ico файлы многих других форматов (jpg, png, bmp и др.).
Кроме того значки из Интернета можно также создавать и редактировать с помощью онлайн-редактора http://www.xiconeditor.com/(external link).

Напомню, для дальнейшей работы нам требуется 2 значка размером 16х16 и 32х32.

  • Подготовь два значка размером 16х16 и 32х32.
  • Переименуй значок 32х32 в Test01.ico, а значок 16х16 в Test01_sm.ico.
  • Скопируй оба значка в каталог с текущим Проектом Test01 (по умолчанию: C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01).

Создаём скрипт ресурсов. Редактор скриптов ресурсов (RC) ResEdit.

Просто добавить значки в Проект Test01 не получится. Сперва необходимо их описать в специальном скрипте ресурсов, представляющем собой обычный текстовый файл с расширением .rc . Напомним, в бесплатной MSVC++2010 Express редактор ресурсов вырезан напрочь. Поэтому для создания скрипта ресурсов воспользуемся сторонним приложением ResEdit. Далее в этом абзаце будем работать только в ней.

Если у тебя 64-разрядная версия ОС Windows, то сильно желательно выбрать 7z-архив с пометкой "x64". Распаковщик 7z-архивов нетрудно найти в Интернете (WinRar также поддерживает распаковку 7z-архивов).

  • Распакуй содержимое архива в любую папку на жёстком диске. В нашем случае это будет C:\ResEdit .
  • Перейди в каталог с программой и запусти ResEdit.exe .

Программа на английском языке. После запуска автоматически запускается мастер (Wizzard) с вопросом, что мы собираемся сделать (What do you want to do?)

  • Выбираем первый пункт Developer mode (режим разработчика).
  • На следующей странице мастера отмечаем пункт Resource script (*.rc) (скрипт ресурсов), в строке Location выбираем папку с Проектом Test01 (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\).

Там же у нас расположены файлы с растровыми изображениями значков (Test01.ico и Test01_sm.ico).

  • В поле Name вводим имя файла скрипта. В нашем случае Resource01.
  • Убери галку с пункта "Создать отдельный подкаталог проекта" (Create project folder).

Пусть скрипт ресурсов и сопутствующие .ico-файлы будут расположены в одной папке. Для наглядности.

  • Жмём Finish
  • В появившемся диалоговом окне о том, что программа не может найти windows.h (windows.h could not be found in any include path. Continue anyway?) жмём "Да" (=всё равно продолжить).
  • В появившемся диалоговом окне о том, что программа не может найти commctrl.h (commctrl.h could not be found in any include path. Continue anyway?) жмём "Да" (=всё равно продолжить).
  • В появившемся диалоговом окне о том, что программа не может найти richedit.h (richedit.h could not be found in any include path. Continue anyway?) жмём "Да" (=всё равно продолжить).

Сейчас нам не нужны эти файлы.

  • На запрос программы сконфигурировать пути к include-файлам жмём "Нет" (отказ).

После этого мы получаем доступ к главному окну программы с пустым проектом скрипта.

  • Открой папку с Проектом Test01 (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\).

При запуске программа ResEdit автоматически создала в этом каталоге файлы:

  • Resource01.rc
  • resource.h

...И даже разместила в них какой-то текст. По старой традиции в resource.h прописываются т.н. IDI (идентификационные номера) ресурсов.

  • Открой оба файла в любом текстовом редакторе. Ознакомься с их содержимым.

Но вернёмся к программе ResEdit. В левой части её главного окна расположена колонка с т.н. "отстыковываемыми" (docked) окнами Resources (Ресурсы) и Properties (Свойства).
Как ни странно, ResEdit не открыла только что созданный файл Enum01.rc для редактирования, из-за чего мы не можем добавлять новые ресурсы. Поэтому...

  • В Главном меню ResEdit жми: File -> Open project... и выбери только что созданный скрипт ресурсов Resource01.rc.

В нашем случае путь до него такой: C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\Resource01.rc. Теперь можно добавлять новые ресурсы.

  • В окне Resources щёлкни правой кнопкой мыши.
  • Во всплывающем меню выбери Add Resource... (Добавить ресурс) -> icon.
  • В появившемся диалоге Insert a new resource отметь пункт Create from an existing file (Создать из существующего файла) и жми Next.
Закрыть
noteКстати

Если выбрать пункт Create a new resource программа откроет вполне толковый редактор значков, где ты сможешь легко пересоздать (и потом пересохранить) созданные ранее значки Test01.ico и Test01_sm.ico. Главное - проследи чтобы они имели размер 32х32 и 16х16 соответственно. Чтобы не уходить от темы (создание скрипта ресурсов) сам встроенный редактор значков (иконок) здесь не рассматриваем. Там всё просто. При желании разберёшься сам.

  • Выбери первый значок Test01.ico, ранее сохранённый в папку с Проектом Test01 (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\).
  • В появившемся диалоге Path designation (Назначение пути) выбери Relative path (относительный путь) жми OK.

Выбранный значок откроется во встроенном редакторе ResEdit, но главное - появится в окне Resources в общем списке ресурсов.

  • Добавь таким же образом второй значок Test01_sm.ico.
  • Сохрани проект скрипта (в главном меню ResEdit выбери File->Save).

Программа ResEdit автоматически добавит сведения о наших значках в скрипт ресурсов Resource01.rc.

  • Снова открой файлы Resource01.rc и resource.h в любом текстовом редакторе. Просмотри их содержимое.

Как видим, нашим значкам автоматически были присвоены символьные идентификаторы IDI_ICON1 и IDI_ICON2. Они-то и нужны для загрузки значков в приложение.
В принципе всё. Скрипт ресурсов Resource01.rc и сопутствующий заголовок resource.h готовы. Осталось добавить оба файла в наш Проект Test01.

Добавляем файл Resource01.rc в Проект Test01

  • Стартуй MSVC++2010 Express, если не сделал этого раньше. Открой Решение/Проект Test01, созданное выше.
  • В Обозревателе решений щёлкни правой кнопкой мыши по "папке" (Напомним, что в терминологии Microsoft это не папки, а фильтры!) "Файлы ресурсов".
  • Во всплывающем контекстном меню выбери Добавить->Существующий элемент.
  • В появившемся окне выбора файла выбери файл скрипта ресурсов Resource01.rc и нажми "Добавить".

Добавляем файл resource.h в Проект Test01

  • В Обозревателе решений MSVC++2010 Express щёлкни правой кнопкой мыши по "папке" "Заголовочные файлы".
  • Во всплывающем контекстном меню выбери Добавить->Существующий элемент.
  • В появившемся окне выбора файла выбери файл скрипта ресурсов resource.h и нажми "Добавить".

Изменения в Main.cpp . Подключаем resource.h к Проекту Test01

В начале листинга Main.cpp найди список подключаемых заголовочных файлов (include-строки):

Фрагмент Main.cpp
...
#define STRICT
#define WIN32_LEAN_AND_MEAN	// Уменьшаем кол-во используемых компонентов в программе.

#include <windows.h>
#include <windowsx.h>
...
  • Добавь следующую строку чуть ниже остальных:
#include "resource.h"
  • Сохрани Решение (Файл->Сохранить всё).

Изменения в Main.cpp . Прописываем новые значки в структуре оконного класса

  • В Main.cpp найди следующий фрагмент:
Фрагмент Main.cpp
...
// Реализация главной функции программы
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	HWND hWnd;			// Дескриптор окна
	MSG msg;			// Структура сообщения
	WNDCLASSEX wndClass;	// Объявляем экземпляр класса программы на базе WNDCLASSEX
	wndClass.cbSize=sizeof(wndClass);
	wndClass.style=CS_HREDRAW|CS_VREDRAW;	// Перерисовываем при перетаскивании
	wndClass.lpfnWndProc=WindowProc;		// Оконная процедура
	wndClass.cbClsExtra=0;
	wndClass.cbWndExtra=0;
	wndClass.hInstance=hInstance;			// Экземпляр окна
	wndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION);		// Пиктограмма приложения
	wndClass.hIconSm=LoadIcon(NULL, IDI_APPLICATION);	// Малая пиктограмма приложения
	wndClass.hCursor=LoadCursor(NULL, IDC_ARROW);		// Курсор при наведении на окно
	wndClass.hbrBackground=GetStockBrush(BLACK_BRUSH);	// Закрашиваем окно чёрным цветом
	wndClass.lpszMenuName=NULL;							// Дескриптор Главного меню окна. Сейчас оно не нужно
	wndClass.lpszClassName="GameClass"; // Обзываем оконный класс.
...
  • Строку wndClass.hIcon=LoadIcon(NULL, IDI_APPLICATION); замени на

wndClass.hIcon=LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));

  • Строку wndClass.hIconSm=LoadIcon(NULL, IDI_APPLICATION); замени на

wndClass.hIconSm=LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON2));

  • Сохрани Решение (Файл->Сохранить всё).
Закрыть
noteОбрати внимание

Если до этого в первом параметре функции LoadIcon стояло NULL (идентификатор экземпляра окна), то теперь мы прописали текущий экземпляр (инстанс) hInstance. Без этого тоже компилируется, но значок в заголовке выводится в виде знака вопроса.

Закрыть
noteОбрати внимание

Макрос MAKEINTRESOURCE конвертирует имя ресурса в строковой указатель (string pointer), который требуют многие функции.

  • Скомпилируй Решение/Проект (F5).

Если всё сделал правильно, на экране появится черное окно приложения. На его тулбаре будет подготовленный нами значок 16х16. Второй значок 32х32 можно увидеть в деле, открыв папку с исполняемым файлом приложения в Проводнике (по умолчанию: C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Debug).

Работа с кастомными (пользовательскими ресурсами)

Как мы уже говорили, что помимо стандартных ресурсов (значки, меню, строки и т.д.), MSVC++ также поддерживает загрузку и использование нестандартных ресурсов, например игровых карт (level maps).

Подготовка кастомного (нестандартного) ресурса

Вновь работаем с ResEdit. Ты же его не закрыл? Если закрыл:
Упёртая прога отказывается открывать ранее созданный Resource01.rc из-за строк, добавленных MSVC++.

  • Создавай новый проект в ResEdit, следуя шагам выше. Назови его так же Resource01, согласившись на перезапись. Добавь в плаваюшее окно Resources ранее подготовленные значки Test01.ico и Test01_sm.ico.
  • Переcохрани Resource01 в том же каталоге Проекта Test01 (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\).

Так вот, продолжаем.
Предположим, что нашим кастомным ресурсом будет игровая карта. Парсить мы её сейчас, конечно, не будем. И вообще, карты не компилируют вместе с exe-шником. Важно усвоить сам принцип.

  • Найди и скачай в Интернете по запросу "Quake 2 maps" любую карту.

В нашем случае это оказался архив с картой 187city_w.zip, взятый отсюда: https://www.lahtela.com/action-quake-2-maps/(external link).

  • Открой архив, например, программой WinRar и распакуй из него 187city_w.bsp из подкаталога maps. Желательно сначала распаковать на Рабочий стол, а затем поместить файл в каталог с Проектом Test01.

В программе ResEdit:

  • В окне Resources щёлкни правой кнопкой мыши.
  • Во всплывающем меню выбери Add Resource... (Добавить ресурс) -> User defined.
  • Поставь галку на пункте "Name Identifier", ниже в строке укажи тип: bsp_maps. Жми OK.

Назвать тип можно как угодно. Главное - чтобы потом можно было легко понять, что внутри.

  • В появившемся диалоговом окне выбери файл 187city_w.bsp, предварительно размещённый в каталоге Проекта Test01 (по умолчанию C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Test01\). Жми Open.
  • Далее отметь Relative path и жми OK.

Кастомный ресурс добавится в список ресурсов. ResEdit по умолчанию поместил его в папку bsp_maps, согласно указанному нами типу.

  • Сохрани ResEdit-проект (File->Save).

В идеале картовый ресурс желательно переименовать.
В окне Resources программы ResEdit:

  • Правой кнопкой мыши по имени нового ресурса и во всплывающем меню выбери Rename (Переименовать).
  • В появившемся диалоге измени имя ресурса с IDR_bsp_maps1 на IDR_187city. Жми OK.
  • Сохрани ResEdit-проект (File->Save).
  • Снова открой файлы Resource01.rc и resource.h в любом текстовом редакторе. Изучи их содержимое.

Видим, что в обоих файлах появились записи о нашем новом картовом ресурсе.

Перекомпилируем Проект Test01

Обновив всего 2 файла (Resource01.rc и resource.h) с помощью ResEdit, мы можем смело отправлять Проект Test01 на перекомпиляцию! При этом ни один из ресурсов даже не был добавлен в MSVC++2010. Всё делалось через однажды указанный скрипт ресурсов Resource.rc .

  • Стартуй MSVC++2010 Express, если не сделал этого раньше. Открой Решение/Проект Test01, созданное выше.
  • Скомпилируй Решение/Проект (F5).

На экране появится чёрное окно. Любопытно, что файл карты 187city.bsp был полностью размещён внутри исполняемого файла Test01.exe (по умолчанию размещён по пути C:\Users\User1\Documents\Visual Studio 2010\Projects\Test01\Debug), а его размер сразу увеличился с 22 до 685 Кб.

Применение кастомного ресурса в коде приложения

После включения (посредством скрипта ресурсов) кастомного ресурса в Проект Test01 (и размещения его внутри исполняемого файла приложения), мы можем получить к нему доступ из исходного кода через его (ресурса) указатель, который получают с помощью одной из трёх специальных функций:2
Таблица 1. Функции для работы с ресурсами

Функция Описание
FindResource Ищет прикреплённый к исполняемому .exe-файлу ресурс и возвращает его дескриптор (handle).
LoadResource Загружает прикреплённый к исполняемому .exe-файлу ресурс в глобальную область памяти и возвращает его дескриптор (handle).
LockResource Блокирует (locks) участок памяти с размещённым в нём ресурсом и возвращает указатель (memory pointer) на него.

Функция FindResource... Вот её прототип:

Прототип функции FindResource
HRSRC FindResource(
 HMODULE hModule,  // Указатель на модуль ресурсов, обозначенный в WinMain.
 LPCSTR lpName,  // Имя ресурса.
 LPCSTR lpType);  // Тип ресурса.

...находит ресурс в блоке ресурсов исполняемого .exe-файла и возвращает его дескриптор (handle), который затем использует функция LoadResource. Параметр hModule представляет собой указатель на экземпляр (instance handle), получаемый от функции WinMain. lpName - имя ресурса (например IDR_MAP1). lpType - имя типа ресурса (например GameMaps). Как мы видели в программе ResEdit ресурсы каждого типа помещаются в отдельную папку-фильтр.

Функция LoadResource загружает ресурс в память и возвращает ещё один дескриптор, в этот раз для использования в функции LockResource.

Прототип функции LoadResource
GLOBAL LoadResource(
 HMODULE hModule,  // Указатель на модуль ресурсов, обозначенный в WinMain.
 HRSRC hResInfo);  // Дескриптор (handle) из вызова функции FindResource.

Вызовы функций FindResource и LoadResource можно комбинировать в один:

HGLOBAL hResource = LoadResource(hInstance, FindResource(hInstance, MAKEINTRESOURCE(IDR_MAP1), "GameMaps"));


Функция LockResource принимает всего 1 параметр - дескриптор (handle), полученный от функции LoadResource:

Прототип функции LockResource
LPVOID LockResource(HGLOBAL hResData);

Дескриптор, возвращаемый функцией LockResource представляет собой void-указатель, который затем можно транслировать (cast) в любой тип данных.

В качестве примера загрузки кастомного ресурса возьмём текстовый файл readme.txt и зададим ему пользовательский тип ресурсов TEXT. Допустим, имя данного ресурса IDR_TEXT1. Данный фрагмент кода загружает ресурс в память и выводит содержимое текстового файла во всплывающем окне сообщений (Message box):

HGLOBAL hResource = LoadResource(hInst, FindResource(hInst, MAKEINTRESOURCE(IDR_TEXT1), "TEXT"));
if(hResource!=NULL)
{
 char *pText = (char*)LockResource(hResource);
 MessageBox(NULL, pText, "Text", MB_OK);
}

Итоги

Урезанность бесплатной MS Visual C++ 2010 - не помеха. Раздобыв дополнительную бесплатную программу по созданию ресурсов ResEdit, мы запросто можем "подсунуть" нашей IDE практически любой готовый ресурс.

Источники:


1. Пирогов В.Ю. Ассемблер и дизассемблирование. - СПб.: БХВ-Петербург, 2006
2. Jim Adams. Programming Role Playing Games with DirectX 8.0. - Premier Press. 2002

Contributors to this page: slymentat .
Последнее изменение страницы Пятница 04 / Декабрь, 2020 08:03:22 MSK автор slymentat.

Помочь проекту

yoomoney.ru (бывший Яндекс-деньги): 410011791055108