24 ноя 2016
Наконец-то добрался до разбора основных классов, тут о них и расскажу. Кстати, информация ниже в большинстве своем является моими не особо опытными догадками и размышлениями, поэтому дополнения и поправки приветствуются в комментариях.
Так-же у каждого сервера есть свой уникальный id (индекс в глобальном массиве серверов). Используются id, например, при загрузке данных из архивов (каждый файл внутри архива содержит id), чтобы понять, метод Unmarshal какого сервера нужно вызвать чтобы загрузить этот файл.
Привожу список некоторых серверов. Имена у большинства взяты из бинарника:
while(true) { g_pMasterServer->Update(); g_pTimer->AdjustTime(); }
А так же в разборе наследования классов мне помогает компилятор, которым пользовались разработчики. Он вежливо переопределяет таблицу виртуальных методов класса в конструкторе и деструкторе этого экземляра класса, а так как деструкторы/конструкторы класса почти всегда вызывают соответствующие конструкторы/деструкторы своих родителей, тем самым можно извлечь древо наследования (часто мешает inline подстановка).
Кроме того компилятор очень аккуратно расставляет указатель на таблицу виртуальных методов сразу после полезных данных структуры экземпляра класса, из чего мы можем делать вывод о размере этой структуры данных (работает только для самых корневых (не детей, а от которых наследуются) классов).
Так же если он видит вложенную структуру, то перед там как поработать с ней, любезно высчитывает указатель на эту структуру, а потом уже начинает работать с ней (из этого можно судить о наличии этих вложенных структур и их смещение относительно основных).
Конечно у него есть и минусы, но я его прощаю, ведь они присущи всем компиляторам с флагом -O2, а именно сплошь и рядом подстановка функций (inline), что заставляет разбирать похожие места кода лишь чтобы убедиться что они делают то-же самое.
Ссылка | Комментарии [4]
24 окт 2016
Читатель, прости что информация подается разрозненно, я не любитель писать.
Пишу я сюда больше в ожидании помощи. Если кто что знает полезного по теме, прошу, напишите в комментариях. Буду рад если укажите на ошибки, особенно в запятых и терминологии (некоторые названия я натягивал и придумывал).
// установить что мы будем распаковывать данные в память vu1 с шагом (аналог аргумента stride в функциях opengl, только работает наоборот). // например, у нас XYZW|XYZW|RGB|RGB|UV|UV, а в памяти vu1 мы хотим построить XYZW|RGB|UV|XYZW|RGB|UV. Stcycl wl=01 cl=03 // то что это текстурные координаты я определил по target адресу, и да, не бойтесь что адреса такие маленькие, ведь vu1 работает с векторами 4х4float. vif unpack [ uv2]: 65 elements: 51 components: 2 type: 16 target: 001 sign: true addr: true size: 000144 Stmod mode=[pos] (1) // туфта для управления шиной Strow proc command // xyzw, где w - это флаги для программы, исполняющейся на vu1, например, использовать ли вертекс для построение треугольника с предыдущими 2мя, или только запомнить его, и не строить треугольника vif unpack [xyzw]: 6d elements: 51 components: 4 type: 16 target: 003 sign: true addr: true size: 000288 Stmod mode= pos (0) // rgba per vertex vif unpack [rgba]: 6e elements: 51 components: 4 type: 08 target: 002 sign: false addr: true size: 000144 // выключаем strip, теперь данные нам нужны подряд, друг за другом Stcycl wl=01 cl=01 // загружаем разные флаги, например тут могут быть перечислены веса для jointов vif unpack [vmta]: 6c elements: 01 components: 4 type: 32 target: 000 sign: true addr: true size: 000010 vif unpack [meta]: 6c elements: 01 components: 4 type: 32 target: 0f4 sign: true addr: true size: 000010 nop // говорим vu1 процессору, что он может начать обрабатывать наши данные Mscall proc command
Для парсинга таких структур приходится эмулировать обработку пакетов в памяти. По хорошему надо отреверсить код, который исполняется на vu1, но до этого руки не дошли (точнее дошли, но сломались об непонятную магию и 5 одновременно исполняющихся инструкций с иногда работающей синхронизацией), тогда прояснятся магические флаги, которые передаются в пакетах (хотя сейчас ясно что там передается наличие тех или иных данных о вертексах (позиция/цвет/текстура/нормаль) и их джойнты. Таких батчей может быть несколько, и они не могут быть больше, чем есть места у vu1. А в нем, кстати, одновременно должно поместиться 3 таких батча, что позволяет оптимизировать работу в 3 потока (1 - "ee core" считает анимацию и всякую лабуду, 2 - gif шина передает данные в памать vu1, 3 - vu1 обрабатывает данные в памяти и отправляет их в gs через path1).
В нашем батче можно заметить что у xyzw пакета стоит размер 16 и флаг sign - что в сумме дает нам int16, эта практика распространяется на всю игру. Я не могу поверить что хватает 2х байтов на координату для такой более-менее графики.
Насчет per vertex rgba можно сказать что он используется везде. На всех статичных объектах запечены тени, из этого кстати следует что одинаковые объекты на сцене, типа камней/дверей/стен будут сохранены в памяти и переданы по шине очень много раз, при этом отличаться они будут только цветом вертексов.
Батчи пакетов группируются по материалу в массив, потом еще по корневому joint в массив - на выходе получаем меш.
Ах да, названия не было, и я позаимствовал идею у openLara.
Ссылка | Комментарии [11]