RSC
Материал из GTAModding.ru
Формат
Ресурсные файлы GTA IV имеют следующий формат:
Заголовок
4 байта - Сигнатура RSC (52 53 43 05) 4 байта - Версия ресурса 4 байта - Флаги
Далее следует сами данные ресурса в запакованном формате. ПК-версия игры использует в качестве алгоритма упаковки - Zlib.
Ресурс содержит два блока памяти - System Memory Segment (CPU) и Graphics Memory Segment (GPU). Размер каждого блока кратен 256. В одном из них хранится сам объект, в другом - используемые данные Direct3D (тексельные, вертексные или индексные буфера).
Каждый ресурс содержит один (и только один) объект, со всеми включенными в него подобъектами. (Например, файл с расширением .WTD
содержит в себе объект rage::pgDictionary
. При этом, в файле сохраняется не только сама коллекция, но и все включенные в нее объекты текстур, используемые ими хэши, имена и, конечно, пиксельные данные). Все указатели заменяются на смещения в файле. При чтении, смещения заменяются обратно на указатели в памяти.
Любой указатель (число вида 0x5xxxxxxx или 0x6xxxxxxx) преобразуется в 32-битное число. Четыре старших бита в нем - код блока (5 - CPU блок, 6 - GPU блок), младшие 28 - смещение в блоке (для GPU блока возможны случаи, когда структура выравнивается на размер страницы, т.е. младшие 12 бит заменяются нулями, а содержавшееся в них значение используется, например, как идентификатор формата данных).
Таким образом преобразовать любой указатель в значение оффсета можно простой операцией (пример на Delphi и C++):
if (Ptr shr 28 = 5) or (Ptr shr 28 = 6) then Offset:=Ptr and $0FFFFFFF;
if ((Ptr >> 28 == 5) | (Ptr >> 28 == 6)) Offset = Ptr & 0x0FFFFFFF;
Размеры блоков хранятся в RSC-заголовке (параметр "флаги), их можно вычислить следующим образом (пример на Delphi и C++):
CPUSize:= (Flags AND $7FF) shl (((Flags shr 11) AND $F) + 8); //расчет размера System Memory Segment
GPUSize:= ((Flags shr 15) AND $7FF) shl (((Flags shr 26) AND $F)+8); //расчет размера Graphics Memory Segment
CPUSize = (Flags & 0x7FF) << (((Flags >> 11) & 0xF) + 8); //расчет размера System Memory Segment
GPUSize = ((Flags >> 15) & 0x7FF) << (((Flags >> 26) & 0xF)+8); //расчет размера Graphics Memory Segment
Каждый блок памяти представляет собой набор страниц. В поле флагов кодируется размер и количество страниц. Ресурс должен содержать как минимум одну большую страницу и от нуля до четырех страниц меньшего размера. Любой непрерывный блок не должен пересекать границы страницы.
Допустим, соответствующая часть поля flags, содержит значение 0x1045. Получаем базовый размер страницы 1024 байта (1 << ((0x1145 >> 11)+8)) и количество страниц - 0x45. Верно? Не совсем. На самом деле, будет создано шесть страниц: 4 страницы по 16 килобайт, одна - 4 килобайта и одна - размером в килобайт.
На количество страниц отводится всего 11 бит. Из них старшие семь - это действительно количество страниц (больших страниц), а четрые младших - определяют необходимость выделения страниц меньшего размера (каждый бит отвечает за страницу своего размера). Размер страницы всегда является степенью двойки (если это не так, он округляется вверх).
Полное вычисление всех значений параметра флагов (пример на Delphi и C++):
WORD vpageCount = dwRscFlags & 0x7FF;
WORD largeVpageCount = vpageCount >> 4;
DWORD vpageSize = ((dwRscFlags >> 11) & 15)+8;
WORD ppageCount = (dwRscFlags >> 15) & 0x7FF;
WORD largePpageCount = ppageCount >> 4;
DWORD ppageSize = ((dwRscFlags >> 26) & 15)+8;
{WORD} vpageCount := dwRscFlags and $7FF;
{WORD} largeVpageCount := vpageCount shr 4;
{DWORD} vpageSize := ((dwRscFlags shr 11) and 15)+8;
{WORD} ppageCount := (dwRscFlags shr 15) and $7FF;
{WORD} largePpageCount := ppageCount shr 4;
{DWORD} ppageSize := ((dwRscFlags sgr 26) and 15)+8;
Если загрузка ресурса получается очень простой, то с записью не все так гладко. При записи мало просто посчитать размер блока, нужно еще и найти относительно оптимальную раскладку структур по блокам.
#define RSC_PAGED 0x80000000
#define RSC_COMPRESSED 0x40000000
#define RSC_PHYS_PAGE_COUNT_MASK 0x3C000000
#define RSC_PHYS_TOTAL_PAGES_MASK 0x03FF8000
#define RSC_PHYS_LARGE_PAGES_MASK 0x03F80000
#define RSC_PHYS_S2_PAGE_MASK 0x00040000
#define RSC_PHYS_S4_PAGE_MASK 0x00020000
#define RSC_PHYS_S8_PAGE_MASK 0x00010000
#define RSC_PHYS_S16_PAGE_MASK 0x00008000
#define RSC_VIRT_PAGE_COUNT_MASK 0x00007800
#define RSC_VIRT_TOTAL_PAGES_MASK 0x000007FF
#define RSC_VIRT_LARGE_PAGES_MASK 0x000007F0
#define RSC_VIRT_S2_PAGE_MASK 0x00000008
#define RSC_VIRT_S4_PAGE_MASK 0x00000004
#define RSC_VIRT_S8_PAGE_MASK 0x00000002
#define RSC_VIRT_S16_PAGE_MASK 0x00000001
Существующий алгоритм "подбора" значения флагов является некорректным, но он позволяет в некоторых случаях подобрать значение поля флагов, исходя из имеющихся размеров блоков. (пример на Delphi)
function getCompactSize(size: longword): word; //вспомогательная функция
var
i: word;
begin
Assert(size mod 256 = 0); //не забудьте про проверку кратности на 256
size := size shr 8;
i := 0;
while (size mod 2 = 0) and (size >= 32) and (i < 15) do begin
i := i + 1;
size := size shr 1;
end;
result := ((i and $F) shl 11) or (size and $7FF);
end;
function getFlags(sysSegSize, gpuSegSize: longword): longword; //основная функция
begin
result := (getCompactSize(sysSegSize) and $7FFF)
or (getCompactSize(gpuSegSize) and $7FFF) shl 15
or 3 shl 30;
end;
Версии и типы ресурсов
Тип ресурса определяется расширением (и только расширением), для каждого типа, определен объект, который содержится в этом ресурсе и набор обработчиков для загрузки, инициализации, освобождения, поиска и прочих рутинных операций.
Название | Платформа | Расширение файла | Версия объекта | Содержащийся объект | Описание |
Animation Dictionary | Windows | *.wad
|
1 | rage::pgDictionary<rage::crAnimation>
|
Коллекция анимаций, содержит преобразованные в ресурсы файлы типа *.anim
|
Xbox360 | *.xad
| ||||
Texture Dictionary | Windows | *.wtd
|
8 | rage::pgDictionary<rage::grcTexturePC>
|
Коллекция текстур, содержит преобразованные в ресурсы файлы типа *.DDS
|
Xbox360 | *.xtd
|
7 | rage::pgDictionary<rage::grcTextureXenon>
| ||
Physics store ( Bound Dictionary ) | Windows | *.wbd
|
32 | rage::pgDictionary<rage::phBound>
|
Коллекция содержащая несколько границ (Bound), для нескольких разных объектов. |
Xbox360 | *.xbd
| ||||
Drawble | Windows | *.wdr
|
110 | rage::gtaDrawable
|
Ресурс "простой" модели, помимо информации о самой моделе, информации о шейдерах, также может содержать текстуры используемые только в этой моделе. |
Xbox360 | *.xdr
|
109 | rage::gtaDrawable
| ||
Frag Type | Windows | *.wft
|
112 | Ресурс "комплексной" модели, кроме собственно самой модели также содержит информацию о границах (Bounds), анимациях и другое... | |
Xbox360 | *.xft
|
112 | |||
HTML Document | Windows | *.whm
|
1 | rage::CHtmlDocument
|
"HTML" страница внутреннего интернета |
Xbox360 | *.xhm
|
1 | rage::CHtmlDocument
|
"HTML" страница внутреннего интернета | |
Drawable Dictionary | Windows | *.wdd
|
110 | rage::pgDictionary<rage::gtaDrawable>
|
Коллекция содержащая несколько "простых" моделей |
Xbox360 | *.xdd
|
109 | |||
Static bound ( Bound ) | Windows | *.wbn
|
32 | rage::pgBaseWrapper<rage::datOwner<rage::phBound>>
|
Ресурс содержит описание границ (Bound) объекта игрового мира, используется для определения физичиских границ объекта. |
Xbox360 | *.xbn
|