IPB

Добро пожаловать, гость ( Вход | Регистрация )

> Alien Trilogy (PC), разбор ресурсов
LexSafonov
Oct 29 2020, 17:48
Сообщение #1


Member
**

Группа: Authorized
Сообщений: 21
Регистрация: 29-October 20
Пользователь №: 18,034
Спасибо сказали: 1 раз(а)



Всем привет, решил создать тему про разбор форматов ресурсов игры Alien Trilogy, преимущественно её ПК варианта. Некоторые ресурсы лежат довольно открыто, но есть некоторые, весьма нестандартные.
Но обо всём по порядку.

Начну я вот с чего.
Из довольно открытых ресурсов - это звуки, это обычные raw-данные без заголовка. Спокойно съедается через Sound Forge или аналогичные программы, достаточно выставить нужный битрейт.
Так же из открытых ресурсов можно выделить текстуры, о них я напишу ниже, т.к. это напрямую связано с "секциями" BND(B16) файлов.
Кстати говоря о этих файлах. Игра использует BND "архивы", для хранения ресурсов - моделей, текстур, спрайтов, текстурных сеток для моделей. Ещё есть аналог BND - B16, в которых лежат данные от 16 битных вариаций текстур\спрайтов.
Вернёмся к так называемым "секциям". В файлах встречаются вот такие секции:

F000(#1,2,3....) - F? - Frame, кадр, используется такая секция у спрайтов. Может внутри себя содержать несколько изображений. Об этом я напишу далее и расскажу об одной особенности.
T000(#1,2,3....) - T? - Texture, текстура. Тут всё проще, у таких секций есть строгие параметры, как у обычных изображений, длина ширина, нет компресии(что самое главное), изображение содержит идексы цветов. Спокойно дёргается XWE редактором. Может быть несколько штук в одном месте. Вообще эта секция достаточно интересная, т.к. используется у моделей\карт, рядом с этой секцией обычно бывает лежит ещё секция с индексами полигонов.
M000(#1,2,3....) - M? - Model, модель. Тут всё просто, эта секция отвечает за модели. Имет список квадов(не полигонов!)\вершин. Прикол с квадами очевидно связан с тем, что изначально игру лепили для двух приставок - PS и Sega Saturn. Так вот, на сколько помню у сеги вроде бы аппаратная часть лучше работала именно с квадами. И вроде бы(поправьте, если не прав) - сначала игру делали именно для сеги. Но не суть, в файле именно описание квадов. В одном файле может быть несколько штук
таких секций.
BX00(#1,2,3....) - BX? - прямоугольники для текстурирования квадов(полигонов) в модели.Что то типа текстурной сетки.
CX00(#1,2,3....) - CX? - пока не разбирал и не смотрел реакцию игры на эту секцию.

Вот описание формата моделей текстом от моего товарища по думу ZZYZX:

Разбор формата PICKMOD.BND текстом

- сначала заголовок файла:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 46 4F 52 4D FORM
0x04 - размер данных файла в байтах, BIG-ENDIAN (перевёрнутый как на сраном арме)
0x04 - количество моделей в файле текстом. всегда 4 символа (формат %04d)

- дальше идут модели по очереди. у каждой модели есть:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x01 4D первая буква идентификатора M (M000, M001, ...)
0x03 - индекс модели текстом. всегда три символа (формат %03d)
0x04 - размер данных модели в байтах, BIG-ENDIAN
0x04 4F 42 4A 31 OBJ1
0x08 00 00 00 00 00 00 00 00 неизвестное значение
0x04 - количество прямоугольников в модели. LITTLE-ENDIAN
0x04 - неизвестное значение
0x14*N - прямоугольники по очереди (см. формат дальше)
0x08*N - вершины по очереди (см. формат дальше)

- формат прямоугольника:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 - индекс первой точки. LITTLE-ENDIAN
0x04 - вторая точка
0x04 - третья точка
0x04 - четвёртая точка. может быть -1 (0xFFFFFFFF), тогда это треугольник и надо продублировать третью точку.
0x04 - неизвестное значение

- формат вершины:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x02 - координата X (signed, LITTLE-ENDIAN, short)
0x02 - координата Y
0x02 - координата Z
0x02 - неизвестное значение, вроде бы всегда 0

А вот и текстурная сетка для моделек

Разбор формата PICKGFX.BND текстом

- сначала заголовок файла:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 46 4F 52 4D FORM
0x04 - размер данных файла в байтах, BIG-ENDIAN
0x04 50 53 58 54 PSXT (вероятно идентификатор формата)

- дальше идут (в произвольном порядке?) секции INFO, TP00, CL00, BX00. Возможно бывают *01, *02 и так далее, но мне не встречались.
- секция INFO
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 49 4E 46 4F INFO
0x04 - размер данных секции в байтах, BIG-ENDIAN
0x02 - размер текстуры X
0x02 - размер текстуры Y
0x0C - неизвестная информация, 12 байт

- секция TP00
тут тупо лежат WxH пиксели. каждый пиксель = 1 байт. смещение в палитру текущую экрана. найти можно в PALS/WSELECT.PAL (768 байт, 3 байта на каждый цвет, умножить на 4 каждый компонент)

- секция CL00
тут лежит неизвестно что. не кантовать.

- секция BX00
тут лежат прямоугольники текстуры для текстурирования квадов.
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 42 58 30 30 BX00
0x04 - размер данных секции в байтах, BIG-ENDIAN
0x04 - количество прямоугольников
- - прямоугольники по очереди (см. формат дальше)

- формат прямоугольника текстуры BX00
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x01 - размер по X (-1 пиксель, т.е. 31 вместо 32 и так далее)
0x01 - размер по Y (-1 пиксель)
0x01 - неизвестное значение
0x01 - неизвестное значение
0x01 - смещение по X с конца (т.е. надо отнять ширину перед использованием)
0x01 - смещение по Y

C000(#1,2,3....) - C? - Color, цвет, секция, очевидно отвечающая за цвет. У файлов B16, такая секция идёт в самом конце. Формат этой секции такой:

0x04 - С000
0x04 - кол-во байт, отведённых под цвета\палитру.
0x02 - непосредственно цвета, по 2 байта на цвет(обычно)

У этой секции есть одна особенность - в некоторых файлах бывает так, что кол-во байт под палитру меньше 512, не понял с чем это связано. Если я правильно понимаю логику, то 512\2 = 256 ячеек(если отталкиваться от каких то простых форматов тип BMP). Вроде бы во многих мануалах пишут, что 16 бит именно так и работает, поправьте, если я не прав.


Теперь вернёмся с секциям F000, которые отвечают за спрайты. Собрал небольшое описание, в основном из экспериментов с пожатыми данными в Hex-редакторе.
Временное описание формата:


0x04 - Заголовок файла FORM
0x04 - размер данных файла в байтах, BIG-ENDIAN
0x04 - кол-во блоков, видимо текст

-------------------------------------------------------------------------------------------------------------------------
0x04 - индентификатор F000
0x04 - Длина до следующей секции, видимо в бинарном представлении.
0x01 - Непонятный байт, крошит изображение, есть подозрение, что это длина алфавита или какой то цепочки байт. Это не длина\ширина. Ниже объясню.
0x0? - Цвета и повторения.


Формат повторений
0x01 - Код цвета
0х01 - Флаг\префикс повторений(обычно символ P, т.е. в Hex коде 50)
0х01 - промежуток повторений(через какой промежуток надо повторить этот цвет)
0х01 - кол-во раз повторений(сколько пикселей рисовать в ряд
https://www.old-games.ru/forum/attachments/...mat-png.211586/
немного наглядности из примерного описания формата, файл MM9.B16

По поводу формата повторений. У него тоже есть определённые условия, а именно, описание верно только для одного условия - когда первое число после префикса равно 1. Видимо для игры это обозначает рисовать цвет сплошняком. Плюс изначально для сплошного цвета игра прибавляет в ряд толи 8, толи 10 неубираемых пикселей. Я насчитал 10.
В файле присутствуют варианты, когда первый байт после префикса больше единицы. Из своих экспериментов пока только сделал вывод, что что-то двигается(из пикселей), но не понял закономерности. Иногда бывает, что в повторениях походу висят какие-то ссылки на другие места. Кстати об этом тоже по подробнее.

В один момент я решил разбирать вообще сплошняком побайтово, но запутался ещё больше:
https://www.old-games.ru/forum/attachments/...ble-png.215950/
красный - видимо ссылки, при изменении ломают изображение
желтый - меняют цвет в нескольких местах, либо подставляют туда какой то кусок
синий - одиночный пиксель
голубой - нет эффекта, либо эффект незаметен
бардовый - нули, непонятно чё делают, если изменить на значение, отличное от нуля, то в изображении пропадают пиксели(местами).

Из переписки с -=CHE@TER=- я увидел, что стандартных данных, как у любой другой картинки, в файле нет, т.к. он нашёл указатель на этот самый пистолет в TRILOGY.EXE, где благополучно лежит длина и ширина кадра.

Теперь по поводу магического числа, ломающего изображение. В MM9.B16 это 92(Hex вариант).
Я пошёл ещё дальше и решил сделать "чистый" кадр. Забил всё одиночными пикселями, с цветом 01, а само число 92 поставил на нуль. Получил такую картину:
https://www.old-games.ru/forum/attachments/nulls-png.216418/
Откуда то взялась лесенка. Байт, где число 92, каким то образом на неё влияет, как будто сдвигает её. Закономерности сдвигов не понял, двигает всегда на разную длину(если менять значение).
Потом забил серый цвет. Лесенка уменьшилась, а само изображение увеличилось(по идее ничего не должно было подобного произойти):
https://www.old-games.ru/forum/attachments/probe-png.216419/

Вопрос к знатокам - может, кто то видел подобное? Похоже на LZX, но какое то своё, хитро-мудрое.
Я описывал B16 вариант, но на сайте old-games, чувак с ником ak48 видимо пробовал смотреть BND аналог и там какие то вывороты с палитрой жёсткие)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
 
Reply to this topicStart new topic
Ответов
LexSafonov
Nov 22 2020, 12:45
Сообщение #2


Member
**

Группа: Authorized
Сообщений: 21
Регистрация: 29-October 20
Пользователь №: 18,034
Спасибо сказали: 1 раз(а)



И так, сейчас будет хороший такой пост по разбору формата карт.
Для начала, разобранный заголовок на данный момент:




Текущее описание заголовка формата карт




0x04 46 4F 52 4D FORM
0x04 - размер данных файла в байтах, BIG-ENDIAN
0x04 - количество карт в файле текстом.(обычно нуль, т.к. больше
одной карты в файлах не встречал)




- дальше идет информация о геометрии карты и её содержании, а именно:
размер содержимое комментарий
-------------------------------------------------------------------------------------------------------------------------
0x04 - Название карты, текст
0x04 - размер данных карты, BIG-ENDIAN(прямой порядок байт)
0х02 - Кол-во вершин?.LITTLE-ENDIAN(обратный порядок байт)
Формула - значение этих двух байт умножить на 8
(6 байт на 3 точки + 2 байта нули)




0x02 Кол-во квадов(прямоугольников).LITTLE-ENDIAN
(обратный порядок байт). Формула - значение этих 2-х байт
умножить на 20 (16 байт индексы точкек и 4 байта информация)




0x02 ------------ Длина "прямоугольника" мини карты(физ. движка)(Little-Endian)
0х02 ------------ Ширина "прямоугольника" мини карты(физ. движка)(Little-Endian)
Формула для этих байт = умножить длину на ширину и полученое значение умножить на 16
16 байт описывают одну ячейку. Пока что не расшифровал все значения одной ячейки.
Эти поля что то описывают для физического движка, осязаемость, высота.
Попробовал подменить пару блоков данными от "неосязаемых", и получилось - игра
убирает осязаемость и с миникарты убирает характерную "точку".
Исходя из этого можно предположить, что игра с трёхмерной картой практически
не взаимодействует.




0х06 - Непонятные значения




0х02 - Поле описания монстров, которые просто спавнятся.
Формула = кол-во элементов умножить на 20(20 байт на одного монстра)
0х02 - Поле описания пикапов, формула = кол-во элементов умножить на 8
0х02 - Поле описания "коробок" и подобных объектов(свитчей, бочек)
Формула = кол-во элементов умножить на 16




0x16 - Непонятные значения.

Теперь отдельно по форматам данных.




На самом первом скриншоте вершины отмечены.

- формат вершины:

размер содержимое комментарий

-------------------------------------------------------------------------------------------------------------------------

0x02 - координата X (signed, LITTLE-ENDIAN, short)

0x02 - координата Y

0x02 - координата Z

0x02 - неизвестное значение, вроде бы всегда 0



Следующий скриншот показывает "границу" - конец квадов и начала "блоков физ. движка".





- формат прямоугольника:

размер содержимое комментарий

-------------------------------------------------------------------------------------------------------------------------

0x04 - индекс первой точки. LITTLE-ENDIAN

0x04 - вторая точка

0x04 - третья точка

0x04 - четвёртая точка. может быть -1 (0xFFFFFFFF), тогда это треугольник и надо продублировать третью точку.

0x02 - индекс текстуры квада

0x02 - некий параметр. Видимо что то по отображению.




Формат одного блока физ. движка пока не расшифровывал, подозреваю, что там есть что то по осязаемости, высоте, может быть какие то индексы для логики на карте.
Как и писал выше - я пробовал подменять данные от блоков, которые неосязаемые, данными от осязаемых и наоборот - эффекты есть и весьма ожидаемые.(Как и на автокарте)

Пока не ясен ещё один нюанс - какая система применяется в игре, пол-потолок(как в играх типа дум\дюк), или же блоки полностью трёхмерные. Но ясно одно точно - трёхмерная карта это чисто декорация. Очевидно связано с тем, чтобы не делать сложный физический движок и все ресурсы пустить на рендеринг(Если посчитать кол-во квадов на первой карте, то там уже 11 тысяч штук!).



Следующий скриншот показывает поле с описанием монстров


Оранжевым цветом помечены лицехваты, светло-сиреневым помечены монстры, которые не заспавнились на карте, серым помечены взрослые чужие.
Возможно, монстры, отмеченные светло-сиреневым, зависят как то от уровня сложности, но это только предположение.




Формат данных одного монстра:

0х01 - Тип атаки?(Может быть и тип монстра)

0х01 - X координата?(Весьма странно описаны координаты, максимум 255? fixed числа?)

0х01 - Y координата?(Тем не менее монстра они двигают)

0х01 - Z координата?(Видимо FF обозначает отспавнить на полу)

0х02 - Кол-во хелс-поинтов(видимо в фиксед числе)

0x01 - Какой объект спавнит при смерти(индекс)

0х01 - Непонятный байт, если больше нуля, то монстра не спавнит

0х04 - Непонятные байты, не ясен эфффект от изменения.

0х02 - Непонятные байты, изменения не заметны(возможно угол поворота при спавне)

0х02 - Скорость перемещения объекта(монстра).

0х04 - Непонятные байты, при изменении не ясен эффект. Один и тот же монстр может

иметь разные значения


Ну и вдовесок к разбору монстров видос, где я изменил параметры лицехвата, который попадается на старте:
https://www.youtube.com/watch?v=NNl59yII4Ys

По поводу типажа монстра - пробовал подменить значение 02 на 08 - спавнит чужого, однако при выставлении значения 03 спавнит монстра, у которого не рендерятся спрайты, на вид атака похожа на ту, что у грудолома.

Следующий скриншот показывает 2 поля - поле с пикапами и поле с "коробками" - спец. объектами(бочками, коробками, свитчами).





Формат данных одного пикапа(По всей видимости 8 байт):


0х01 - Координата X?
0х01 - Координата Y?
0х01 - Тип пикапа, индекс
Типажи:
0 - пистолет
1 - дробовик
2 - пульсатор
3 - огнемёт
4 - смарт-ган
5 - не используется
6 - сейсмо-заряд
7 - батарея(для свитчей, использующих тип "батарея")
8 - очки ночного видения
9 - пистолетная обойма
10 - патроны для дробовика
11 - обойма для пульсатора
12 - подствольная граната для гранатомёта
13 - топливо для огнемёта
14 - обойма для смарт-гана
15 - идентификационная карта(которая вылетает из колонистов\охранников)
16 - автокарта
17 - гипер-пак
18 - броня (acid west), видимо защищает от кислоты
19 - броня простая
20 - аптечка
21 - бинт(+1 хелс)
22 - защитные ботинки
23 - адреналин
24 - бинт?
25 - солдатская лампа


Типажи выше можно поднять, следующие неподнимаемые(видимо использовали для отладки)
26 - гильза от дробовика
27 - гильза от пистолета
Значения выше 27(28 и далее), стабильно приводят к вылету.


0х01 - кол-во, которое выдаёт пикап, при поднятии
0х01 - какой то множитель для предыдущего значения
0х01 - непонятное значение, везде вроде нуль
0х01 - видимо Z координата
0х01 - тоже какое то кол-во для пикапа, непонятно как работает.




Формат "коробок"(видимо 16 байт), один нюанс, в поле коробок описываются не только коробки.
0х01 - Координата X?
0х01 - Координата Y?
0x01 - Тип "коробки"(Видимо и типаж и модель одновременно)
Есть такие типы:
19 - коробка, которую нельзя взорвать
20 - обычная коробка, которую можно взорвать
21 - пустой объект(на данный момент), может быть свитч для уровней на корабле
22 - ещё один мелкий свитч, отличие внизу у модели(нарисована молния)
23 - бочка, взрывается.
24 - мелкий свитч без молнии
25 - "двойная коробка"(две коробки друг над другом, которые можно взорвать)
26 - широкий свитч с молнией
27 - широкий свитч без молнии
28 - пустой объект, который можно прострелить
29 - пустой объект, который можно прострелить, что то спавнит при смерти
30 - обычная коробка, которую можно взорвать
0х01 - что объект заспавнит при смерти 00 - видимо пикап, 02 - видимо монстр
0х02 - пока не понял, что данные два байта делают
0х01 - видимо индекс объекта, который надо заспавнить
0х01 - видимо индекс второго объекта, который надо заспавнить
0х08 - несовсем понятные значения, два последних байта каким то образом влияют
на скорость, с которой вылетают заспавненые объекты




На последок опишу последний эксперимент. Я решил выяснить - зависит ли как то набор монстрия от номера уровня, т.е. не забито ли это в движке. Для этого я файл первого уровня подменил файлом от шестого. Результат меня приятно удивил - монстры заспавнились как положено, и остальные данные заработали тоже как нужно. Однако не заработали звуки тех монстров, которых на первом уровне не должно быть. Но я уже понял причину - это связано с тем, что ПК вариант игры подгружает набор звуков из текстовых файлов в папке SFX.

Тексты миссий и их скрины остаются от первого уровня.

Пока что на данный момент это всё. Пока буду продолжать дальше разделять границы полей и примерно описывать эффекты, которые они делают.
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Nov 22 2020, 14:27
Сообщение #3


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,355
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



Нифига себе сколько. Кстати, на первом скриншоте опечатка - XYX, вместо XYZ.

Прочитал и, пока что, только две мысли:
QUOTE(LexSafonov @ Nov 22 2020, 12:45) *
0х01 - X координата?(Весьма странно описаны координаты, максимум 255? fixed числа?)
0х01 - Y координата?(Тем не менее монстра они двигают)
Может оно тут как в Minecraft? Когда уровень делится на условные кубы и в данном месте задаются координаты куба, в середине которого будет создаваться монстр или предмет.

QUOTE(LexSafonov @ Nov 22 2020, 12:45) *
0х01 - кол-во, которое выдаёт пикап, при поднятии
0х01 - какой то множитель для предыдущего значения
А это случайно не одно значение 0x02 (word, 2 байта) в LITTLE-ENDIAN?
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
LexSafonov
Nov 22 2020, 15:52
Сообщение #4


Member
**

Группа: Authorized
Сообщений: 21
Регистрация: 29-October 20
Пользователь №: 18,034
Спасибо сказали: 1 раз(а)



По поводу опечатки, да.... Я заметил это в самый последний момент. Ладно, суть то не больно меняет)))

По поводу координат, скорее всего да, система очень похожая, т.к. один элемент - это 1 блок, он же чётко рисуется на автокарте. Скорее всего в координатах указываются X и Y "блока", а остальные координаты "дорассчитываются" в процессе. Но это пока чисто предположения.

Далее по поводу значений пикапов. Нет, это два разных значения, т.к. я пробовал менять их. Не ясна логика работы "множителя". Я смотрел пару пикапов - пикап пистолетной обоймы и пикап патронов для дробовика. У пикапа пистолетных патронов такие значения 0F 01, что значит - 15 1(15 патронов), а у пикапа патронов дробовика там 0A 01 - 10 1(10 патронов). Если читать в по 2 байта, то больно большое значение получается.... Возможно множитель работает как то от уровня сложности.

Ещё одна проблема в том, что я очень туго соображаю по рисованию графики в С++. Даже если найду все поля, то для написания простенького редактора карт, надо будет применять что то из 3д.... У меня на простой декомпрессор спрайтов чуть ли не неделя ушла)
User is offlineProfile CardPM
Go to the top of the page
+Quote Post
-=CHE@TER=-
Nov 22 2020, 16:51
Сообщение #5


Walter Sullivan
***

Группа: Root Admin
Сообщений: 1,355
Регистрация: 4-February 08
Пользователь №: 3
Спасибо сказали: 311 раз(а)



QUOTE(LexSafonov @ Nov 22 2020, 15:52) *
Ещё одна проблема в том, что я очень туго соображаю по рисованию графики в С++. Даже если найду все поля, то для написания простенького редактора карт, надо будет применять что то из 3д.... У меня на простой декомпрессор спрайтов чуть ли не неделя ушла)
Вот, нашёл - слегка поправленные мной исходные коды простенького 3D-лабиринта на OpenGL (если найти выход, то игра начнётся заново). Автор Stan Melax - см. комментарии сверху. Для работы программы необходим в том же каталоге что и программа файл maze.bmp с рисунком 128x128 в 24-х битах - это текстура стен, любой файл туда положи указанного размера и глубины цвета.
Можешь разбираться как работает и переделывать под себя - будет с чего начать.
Обрати внимание, что для сборки исполняемого файла тебе нужно указать линковку библиотек "GLU32.dll" и "OpenGL32.dll" помимо стандартных Windows'ых.

maze.c
CODE
/*
* OpenGL Maze Example
*
* by Stan Melax melax@bioware.com
*
* In this little demo the player navigates through a simple maze
* using the arrow keys. The maze is defined by a 2D array where
* each element in the array indicates solid or empty space. This
* program wraps polygon (quad) walls around the solid space and
* disallows the player to navigate into solid space during the demo.
* Note that all the walls are limited to being 90 degrees to each
* other - there are no "angled" features. The purpose of this
* sample program is to show a beginning 3D game programmer some
* things they can do.
*
* One other cool thing that this program does is that it constucts
* a single quad strip to draw all the walls by doing a recursive
* depth first search on the maze array data.
*
* Permission to execute this program, or look at the code is only
* granted to those who do not like to sue other people :-)
* Some of the window setup code was stolen from a simple Cosmo example.
* OpenGL is a trademark of SGI.
*/

#include <assert.h>
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <math.h>

#define IDM_APPLICATION_EXIT (101)
#define IDM_APPLICATION_TEXTURE (102)
#define IDM_APPLICATION_BANK (103)
HMENU hMENU;
HWND hWND;
HDC hDC;
HGLRC hGLRC;
int enablebank=0;
int enabletexture=0;

#define MAZE_HEIGHT (16)
#define MAZE_WIDTH (16)

// unfortunately due to the way the polygon walls are generated there
// are restrictions on what the wall/maze data can look like. See below.
char mazedata[MAZE_HEIGHT][MAZE_WIDTH + 1] = {
"****************",
"* * *",
"* * *** * * *",
"* ** * ** * * *",
"* * * *",
"********** *** *",
"* * *",
"* ***** *** ****",
"* * * * *",
"* ******* *",
"* * * * * *",
"* ***** **** * *",
"* * * *",
"** ** **** *** *",
"* * * * *",
"************* **",
};

int wall(int x, int y) {
// true if the region at x,y is solid space that
// should be surrounded by other solid space or polygon walls
return((x >= 0) && (y >= 0) && (x < MAZE_WIDTH) && (y < MAZE_HEIGHT) && (mazedata[y][x] != ' '));
}

/*
* The next group of routines implements the depth-first search
* that is used to wrap a quad strip around all the solid regions of the
* maze. Note this enforces certain topological restrictions on the
* maze data itself. There cant be any loops, clusters, or floating pieces
* existing by themselves. The solid nodes must be a tree (graph theory speak).
*
*/
int onopen(int x, int y) {
// returns whether node x,y is on the depth-first search open list
assert(wall(x, y));
return(mazedata[y][x] == '*');
}

void closeit(int x, int y) {
// puts node x,y on the closed list
assert(wall(x, y));
assert(onopen(x, y));
mazedata[y][x] = 'X';
}

int neighbor(int x, int y, int w, int *nx, int *ny) {
// if x,y has a neighbor in direction w then returns true
switch (w) {
case 0: *nx = x-1; *ny=y; break;
case 1: *nx = x; *ny=y+1; break;
case 2: *nx = x+1; *ny=y; break;
case 3: *nx = x; *ny=y-1; break;
default: assert(0);
}
return(wall(*nx, *ny));
}

int diagnol(int x, int y, int w, int *nx, int *ny) {
switch(w) {
case 0: *nx = x-1; *ny=y-1; break;
case 1: *nx = x-1; *ny=y+1; break;
case 2: *nx = x+1; *ny=y+1; break;
case 3: *nx = x+1; *ny=y-1; break;
default: assert(0);
}
return(wall(*nx, *ny));
}

// normal vectors for each wall direction
float nrml[4][3] = {
{ -1.0f, 0.0f,0.0f},
{ 0.0f, 1.0f,0.0f},
{ 1.0f, 0.0f,0.0f},
{ 0.0f,-1.0f,0.0f},
};

// default color for each wall direction
float clr[4][3] = {
{ 1.0f, 0.0f,0.0f},
{ 0.0f, 1.0f,0.0f},
{ 0.0f, 0.0f,1.0f},
{ 1.0f, 1.0f,0.0f},
};

static float texcoordX = 0.0f;

int dw(int x, int y, int p) {
// the recursive draw wall routine that extends the quad strip
int w = p; // w is the current wall direction being considered
closeit(x, y);
do {
int x2, y2;
if (neighbor(x, y, w, &x2, &y2)) {
if (onopen(x2, y2)) {
dw(x2,y2,(w+3)%4);
} else {
assert(((w + 1) % 4) == p); // or a loop or cluster exists
return(1);
}
} else {
float fx;
float fy;
if (diagnol(x, y, w, &x2, &y2) && onopen(x2, y2)) {
dw(x2,y2,(w+2)%4);
}
glNormal3fv(nrml[w]); // useful iff using lighting
glColor3fv(clr[w]);
texcoordX = (texcoordX < 0.5) ? 1.0f : 0.0f;
fx = (float) x + (((w == 1) || (w == 2)) ? 1.0f : 0.0f);
fy = (float) y + (((w == 0) || (w == 1)) ? 1.0f : 0.0f);
glTexCoord2f(texcoordX, 0.0f); // useful iff using textures
glVertex3f(fx, fy, 0.0f);
glTexCoord2f(texcoordX, 1.0f);
glVertex3f(fx, fy, 1.0f);
}
w++;
w %= 4;
} while (w!=p);
return(1);
}

int drawwalls(void) {
int dl;
glNewList(dl = glGenLists(1), GL_COMPILE);
glBegin(GL_QUAD_STRIP);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 1.0f);
dw(0, 0, 0);
glEnd();
glEndList();
return(dl);
}

//-----------------------------------------------

int drawtop(void) {
// draws the top and the bottom of the maze
// which is useful for overhead views
// The display list created here is only used for the intro
// spinning bit. No optimizations such as using quad strips
// or combining adjacent polygons are done here.
int x, y, dl;
glNewList(dl = glGenLists(1), GL_COMPILE);
glPushAttrib(GL_TEXTURE_BIT | GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
for (y = 0; y < MAZE_HEIGHT; y++) {
for (x = 0; x < MAZE_WIDTH; x++) {
if (wall(x, y)) {
// bottomside:
glVertex3f(x+0.0f ,y+0.0f ,0.0f );
glVertex3f(x+0.0f ,y+1.0f ,0.0f );
glVertex3f(x+1.0f ,y+1.0f ,0.0f );
glVertex3f(x+1.0f ,y+0.0f ,0.0f );
// topside:
glVertex3f(x+0.0f ,y+0.0f ,1.0f );
glVertex3f(x+1.0f ,y+0.0f ,1.0f );
glVertex3f(x+1.0f ,y+1.0f ,1.0f );
glVertex3f(x+0.0f ,y+1.0f ,1.0f );
}
}
}
glEnd();
glPopAttrib();
glEndList();
return(dl);
}

#define STARTING_POINT_X (1.5f)
#define STARTING_POINT_Y (1.5f)
#define STARTING_HEADING (90.0f)

float player_x = STARTING_POINT_X;
float player_y = STARTING_POINT_Y;
float player_h = STARTING_HEADING; // player's heading
float player_s = 0.0f; // forward speed of the player
float player_m = 1.0f; // speed multiplier of the player
float player_t = 0.0f; // player's turning (change in heading)
float player_b = 0.0f; // viewpoint bank (roll)
int walllist = 0;
int mazelist = 0;

void spinmaze(HDC hDC);
void entermaze(HDC hDC);
void navmaze(HDC hDC);
void (*idlefunc)(HDC hDC) = NULL;

void spinmaze(HDC hDC) {
// spin the maze around on 2 axis
static float spin = 720.0f;
spin -= 5.0f; // change this line to be time dependant!
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glTranslatef(0.0f, 0.0f, -20.0f);
glRotatef(spin, 0.0f, 1.0f, 1.0f);
glTranslatef(-MAZE_WIDTH / 2.0f, -MAZE_HEIGHT / 2.0f, 0.0f);
glCallList(walllist);
glCallList(mazelist);
glPopMatrix();
SwapBuffers(hDC);
if (spin <= 0.0f) {
spin = 720.0f;
idlefunc = entermaze;
player_x = STARTING_POINT_X;
player_y = STARTING_POINT_Y;
player_h = STARTING_HEADING;
}
}

void entermaze(HDC hDC) {
static float p=0.0f;
p += 0.02f; // hmmm, should be time dependant
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glRotatef(-90.0f*p, 1.0f, 0.0f, 0.0f);
glRotatef(player_h*p, 0.0f, 0.0f, 1.0f);
glTranslatef(
-(player_x*p + MAZE_WIDTH/2.0f*(1 - p)),
-(player_y*p + MAZE_HEIGHT/2.0f*(1 - p)),
-(0.5f*p + 20.0f*(1-p))
);
glCallList(walllist);
glCallList(mazelist);
glPopMatrix();
SwapBuffers(hDC);
if (p >= 1.0f) {
p = 0.0f;
idlefunc = navmaze;
}
}

int forward(float px, float py, float bf) {
// this routine does wall collision detection
// the inputs to this routine are:
// - the desired location
// - the minimum distance to wall allowed
// changes:
// - the player's x and y coordinates
// returns:
// - whether a wall caused change in target position
// This is really easy with these walls that lie only on axes.
// If the player collides into a wall at an angle he/she will
// still slide along the wall - I hate programs where you stick
// to the polygon and cant move until you back away from it.
// This collision detection isn't perfect - if you're precise you
// can jump through at a corner - but its tough.
int x = ((int)player_x);
int y = ((int)player_y);
int h = 0; // number of walls hit
if ((px > x + 1.0f - bf) && wall(x + 1, y)) {
px = (float)(x) + 1.0f - bf;
h++;
}
if ((py > y + 1.0f - bf) && wall(x, y + 1)) {
py = (float)(y) + 1.0f - bf;
h++;
}
if ((px < x + bf) && wall(x - 1, y)) {
px = (float)(x) + bf;
h++;
}
if ((py < y + bf) && wall(x, y - 1)) {
py = (float)(y) + bf;
h++;
}
player_x = px;
player_y = py;
return(h);
}

void navmaze(HDC hDC) {
// navigate through the maze and render it from the
// players point of view.
// Ideally updates to heading and position should be time dependant
forward(
player_x + player_m*player_s*(float)sin(player_h*M_PI/180),
player_y + player_m*player_s*(float)cos(player_h*M_PI/180),
0.2f
);
player_h += player_t;
player_b = 3*player_b/4 + player_t/4;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPushMatrix();
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
if (enablebank) {
glRotatef(-player_b, 0.0f, 1.0f, 0.0f); // add roll to viewpoint
}
glRotatef(player_h, 0.0f, 0.0f, 1.0f);
glTranslatef(-player_x, -player_y, -0.5f);
glCallList(walllist); // no need to draw the top since we're in the maze now
glPopMatrix();
SwapBuffers(hDC);
if ((player_x > MAZE_WIDTH) || (player_y > MAZE_HEIGHT)) {
// start over
idlefunc = spinmaze;
}
}

#define IMG_SIZE 128
static BOOL bTexLoaded = FALSE;
void readtexture(void) {
BYTE *image;
DWORD dw;
HANDLE fl;
if (bTexLoaded) { return; }
// the bitmap must be a 24 bit bmp file thats 128x128
// I think I mixed up red and blue componants - oh well :-)
fl = CreateFile("maze.bmp", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (fl != INVALID_HANDLE_VALUE) {
SetFilePointer(fl, 54, NULL, FILE_BEGIN);
dw = IMG_SIZE*IMG_SIZE*3;
image = (void *) LocalAlloc(LMEM_FIXED, dw);
if (image) {
ReadFile(fl, image, dw, &dw, NULL);
glTexImage2D(GL_TEXTURE_2D, 0, 3, IMG_SIZE, IMG_SIZE, 0, GL_BGR, GL_UNSIGNED_BYTE, image);
// let texture wrap-around
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// use point sampleing (fastest)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glEnable(GL_TEXTURE_2D);
LocalFree(image);
bTexLoaded = TRUE;
}
CloseHandle(fl);
}
}

static void SetupDC(HDC hDC) {
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */
1, /* version num */
PFD_DRAW_TO_WINDOW | /* window types */
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, /* RGBA type */
8, /* 8-bit color depth */
0, 0, 0, 0, 0, 0, /* color bits (ignored) */
0, /* no alpha buffer */
0, /* alpha bits (ignored) */
0, /* no accumulation buffer */
0, 0, 0, 0, /* accum bits (ignored) */
16, /* 16-bit depth buffer */
0, /* no stencil buffer */
0, /* no auxiliary buffers */
PFD_MAIN_PLANE, /* main layer */
0, /* reserved */
0, 0, 0, /* no layer, visible, damage */
/* masks */
};
int SelectedPixelFormat;
BOOL retVal;

// see if the pixel format exists
SelectedPixelFormat = ChoosePixelFormat(hDC, &pfd);
if (SelectedPixelFormat == 0) {
MessageBox(
WindowFromDC(hDC),
"Failed to find acceptable pixel format.",
"OpenGL application error",
MB_ICONERROR | MB_OK
);
ExitProcess(1);
}
// use the pixel format
retVal = SetPixelFormat(hDC, SelectedPixelFormat, &pfd);
if (retVal != TRUE) {
MessageBox(
WindowFromDC(hDC),
"Failed to set pixel format.",
"OpenGL application error",
MB_ICONERROR | MB_OK
);
ExitProcess(1);
}
}

LRESULT APIENTRY WinProc(HWND hWND, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {

case WM_CREATE:
hDC = GetDC(hWND);
SetupDC(hDC);
hGLRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hGLRC);
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE);
glEnable(GL_CULL_FACE);
glShadeModel(GL_FLAT);
walllist = drawwalls();
mazelist = drawtop();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, 1.0, 0.1, 60.0);
glMatrixMode(GL_MODELVIEW);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glLoadIdentity();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
idlefunc = spinmaze;
return(0);
break;

case WM_DESTROY:
if (IsZoomed(hWND)) {
ShowWindow(hWND, SW_RESTORE);
}
if (hGLRC) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hGLRC);
}
ReleaseDC(hWND, hDC);
PostQuitMessage(0);
return(0);
break;

case WM_PAINT:
if (hGLRC) {
// not necessary to do anything here
// since we're constantly drawing to the window anyway
//PAINTSTRUCT ps;
//BeginPaint(hWND, &ps);
//Redraw(hDC);
//EndPaint(hWND, &ps);
return(0);
}
break;

case WM_CONTEXTMENU:
TrackPopupMenu(
hMENU, TPM_LEFTALIGN | TPM_RIGHTBUTTON,
LOWORD(lParam), HIWORD(lParam), 0, hWND, NULL
);
break;

case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
break;

case WM_COMMAND:
switch (LOWORD(wParam)) {

case IDM_APPLICATION_EXIT:
PostQuitMessage(0);
break;

case IDM_APPLICATION_TEXTURE:
enabletexture ^= 1;
((enabletexture) ? glEnable : glDisable)(GL_TEXTURE_2D);
if (enabletexture) {
readtexture();
}
break;

case IDM_APPLICATION_BANK:
enablebank ^= 1;
break;

}
break;

case WM_CHAR:
switch (LOWORD((int)wParam)) {

case VK_ESCAPE:
PostQuitMessage(0);
break;

case 'b':
enablebank ^= 1;
break;

case 't':
enabletexture ^= 1;
((enabletexture) ? glEnable : glDisable)(GL_TEXTURE_2D);
if (enabletexture) {
readtexture();
}
break;
}
break;

case WM_KEYDOWN:
switch (LOWORD((int)wParam)) {

case VK_LEFT:
player_t = -5.0f;
break;

case VK_RIGHT:
player_t = 5.0f;
break;

case VK_UP:
player_s = 0.05f;
break;

case VK_DOWN:
player_s = -0.02f;
break;

case VK_SHIFT:
player_m = 3.0f;
break;

case VK_RETURN:
if (IsZoomed(hWND)) {
ShowWindow(hWND, SW_RESTORE);
} else {
ShowWindow(hWND, SW_MAXIMIZE);
}
break;
}
break;

case WM_KEYUP:
switch (LOWORD((int)wParam)) {

case VK_LEFT:
// the reason for the "if(" is that sometimes
// the player hits the right key down before the
// left key is all the way up.
if (player_t < 0.0f) {
player_t = 0.0f;
}
break;

case VK_RIGHT:
if (player_t > 0.0f) {
player_t = 0.0f;
}
break;

case VK_UP:
player_s = 0.0f;
break;

case VK_DOWN:
player_s = 0.0f;
break;

case VK_SHIFT:
player_m = 1.0f;
break;
}
break;

default:
break;
}
return(DefWindowProc(hWND, message, wParam, lParam));
}

//int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, LPSTR lpszCmdLine, int nCmdShow) {
int main(void) {
char *className = "OpenGLWnd";
char *winName = "Maze Example";
WNDCLASS winClass;
MSG msg;
RECT rc;

// Define and register the window class
ZeroMemory(&winClass, sizeof(winClass));
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WinProc;
winClass.hInstance = GetModuleHandle(NULL);//hCurrentInst;
winClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = GetStockObject(WHITE_BRUSH);
winClass.lpszClassName = className;
RegisterClass(&winClass);

// create window
hWND = CreateWindow(
className, winName,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 320, 240,
NULL, // Parent window's handle
NULL, // Menu handle
winClass.hInstance, // Instance handle
NULL // No additional data
);

// center window
ZeroMemory(&rc, sizeof(rc));
rc.right = 320;
rc.bottom = 240;
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, FALSE);
rc.right -= rc.left;
rc.bottom -= rc.top;
rc.left = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
rc.top = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
MoveWindow(hWND, rc.left, rc.top, rc.right, rc.bottom, FALSE);

// add quit menu item
hMENU = CreatePopupMenu();
AppendMenu(hMENU, MF_STRING, IDM_APPLICATION_EXIT, "Exit");
AppendMenu(hMENU, MF_STRING, IDM_APPLICATION_TEXTURE, "Add/Remove Texture");
AppendMenu(hMENU, MF_STRING, IDM_APPLICATION_BANK, "Add/Remove Banking");

ShowWindow(hWND, SW_SHOWNORMAL);//nCmdShow);
UpdateWindow(hWND);

// DEBUG: use textures by default
SendMessage(hWND, WM_COMMAND, MAKELONG(IDM_APPLICATION_TEXTURE, 0), 0);

msg.wParam = 0;
// process events
while (1) {
// oops, busy wait when no messages or idlefunc - ohwell
if (idlefunc) {
idlefunc(hDC);
// PATCH: do not eat 100% of CPU
Sleep(1);
}
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
if (GetMessage(&msg, NULL, 0, 0) != TRUE) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(msg.wParam);
}
User is offlineProfile CardPM
Go to the top of the page
+Quote Post

Сообщения в этой теме
LexSafonov   Alien Trilogy (PC)   Oct 29 2020, 17:48
-=CHE@TER=-   Ну, я уже писал, что смотрел игру бегло, там реаль...   Oct 29 2020, 19:21
LexSafonov   Так, продолжаю работу по просмотру сырого дизассем...   Oct 30 2020, 19:34
-=CHE@TER=-   По началу при беглом просмотре этих строк я и не д...   Oct 30 2020, 20:24
LexSafonov   Так, сегодня сел со вежей головой и решил прикинут...   Oct 31 2020, 09:46
-=CHE@TER=-   Всё, я нашёл функцию декодирования. Ищешь в исполн...   Oct 31 2020, 17:22
LexSafonov   Блин, бро, да ты мозг! Прям интересно стало, ч...   Nov 1 2020, 08:11
-=CHE@TER=-   Проверка возможно связана с тем, что в BND вариант...   Nov 1 2020, 18:03
LexSafonov   Так, ещё порция глупых вопросов, по поводу того ал...   Nov 11 2020, 08:21
-=CHE@TER=-   В функцию надо передавать указатель на файл? Или ж...   Nov 11 2020, 12:59
LexSafonov   Ух ты. Даже как то не по себе, от того, что ты про...   Nov 11 2020, 14:09
-=CHE@TER=-   Там вроде есть секции, в которых содержится сразу ...   Nov 11 2020, 18:02
LexSafonov   Так, сделал простую прогу, линейную, для декомпрес...   Nov 13 2020, 16:01
-=CHE@TER=-   А блин, понял, я же неправильно скользящее окно сд...   Nov 13 2020, 17:21
LexSafonov   Получилось! Переделал немного, сделал выходной...   Nov 13 2020, 17:51
-=CHE@TER=-   Получилось!Во, молодец! Можно наверное эт...   Nov 13 2020, 20:54
LexSafonov   На счёт исходных кодов - и твои и мои. Если ты не ...   Nov 13 2020, 22:15
-=CHE@TER=-   На счёт исходных кодов - и твои и мои. Если ты не ...   Nov 14 2020, 12:05
LexSafonov   И так, после долгого мучения и раздумий, я наконец...   Nov 16 2020, 19:24
-=CHE@TER=-   Недоработка: все файлы на выходе дозаписываются в ...   Nov 17 2020, 11:15
LexSafonov   Так ты "wb" (w = write, b = binary) дел...   Nov 17 2020, 13:39
-=CHE@TER=-   А функция fwrite() жаловаться не будет? У меня же ...   Nov 17 2020, 16:04
LexSafonov   Какая разница куда у тебя данные записываются, ес...   Nov 17 2020, 16:39
-=CHE@TER=-   Ладно, исправлю, не ругайся только :)Я не ругаюсь,...   Nov 17 2020, 17:18
LexSafonov   Так, решил своим же инструментом попробовать извле...   Nov 19 2020, 09:33
-=CHE@TER=-   Пока извлекал ресурсы, нашёл неиспользуемые спрайт...   Nov 19 2020, 14:43
LexSafonov   Так, продолжаю свои разборы формата карты. Ещё раз...   Nov 19 2020, 20:10
LexSafonov   Обновляю данные по поводу формата карт. http://i....   Nov 20 2020, 15:08
LexSafonov   И так, сейчас будет хороший такой пост по разбору ...   Nov 22 2020, 12:45
-=CHE@TER=-   Нифига себе сколько. Кстати, на первом скриншоте о...   Nov 22 2020, 14:27
LexSafonov   По поводу опечатки, да.... Я заметил это в самый п...   Nov 22 2020, 15:52
-=CHE@TER=-   Ещё одна проблема в том, что я очень туго сообража...   Nov 22 2020, 16:51
LexSafonov   Так ребята, всем привет. Долго меня не было. Вообщ...   May 13 2021, 16:11
-=CHE@TER=-   - Так же я не совсем понял, что за последнее поле ...   May 14 2021, 18:11


Reply to this topicStart new topic
9 чел. читают эту тему (гостей: 9, скрытых пользователей: 0)
Пользователей: 0 -

 



Упрощённая версия Сейчас: 23rd April 2024 - 11:20