ФлеймФорумПрограммирование

Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++) (11 стр)

Страницы: 110 11 12 13 14 Следующая »
#150
17:22, 18 авг 2021

}:+()___ [Smile]
> Это плохой совет. Проблемы со штатным memcpy

Это не штатный memcpy. Это memcpy, не следующий стандарту на эту функцию - копировать побайтно, если адрес не выровнен.

}:+()___ [Smile]
> говорят о том, что у тебя осталось UB.
> Лучше исправить ошибку, чем пытаться заменить язык на нестандартный диалект.

Уже давно всё сделано и работает: https://gamedev.ru/flame/forum/?id=262348&page=10&m=5434017#m149

Доступы к невыровненным данным фиксятся через перегрузку операторов и заменяются на побайтное копирование. Нет тут никакого UB уже.

#151
18:10, 18 авг 2021

Gradius

А MMU у тебя настраивается как-нибудь?

#152
4:44, 26 авг 2021

Ghost2
> А MMU у тебя настраивается как-нибудь?

На ARM без MMU кеш данных не работает.
У меня включен MMU и PA = VA

На DSP участки памяти тоже прокешированы MAR-регистрами.
ЕМНИП, трансляции адресов там нет.  Поэтому используются только PA.

Gradius
> Уже пошёл титульник игры, но до самой игры, увы, пока далеко: отсутствуют
> враги, туннель, корабль игрока отрисовывается в виде точек и не
> текстурируется.

Проблема была в падении программы из-за неправильного вызова функций sprintf по вине декомпилятора Hex-rays.
Обычные вызовы sprintf, он зачем-то превратил в наркоманский типкаст:

((void (*)(char *, char *))sprintf)(v3, aCongratulation_1);

Из-за этого имена файлов формировались неверно. Переправил:

sprintf(v3, aCongratulation_1);

Сразу всё заработало.

Работы по портированию игры Tube на DSP C6745 успешно завершены:

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

Хочется поблагодарить всех, кто был неравнодушен к данной теме и особенно тех, кто помог и оказал влияние на развитие проекта в целом!

Напоследок, вынесу пару перлов, которые подняли мне настроение и мотивировали довести начатое до конца:

2 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

3 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)
lol | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++) Жду Нобелевской премии и сертификата, подтверждающего наличие титановой задницы lol | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)


В настоящее время, ведутся попытки адаптации кода игры на 64-битную архитектуру.
Но ничего не обещаю!

Проблемы с которыми я столкнулся и их решение подробно расписал здесь:
https://gamedev.ru/code/forum/?id=263319&m=5437710#m0

P.S.
Обновил исходный код игры (фикс алиасинга указателей и обращения к невыровненным данным):

https://github.com/rep-stosw/tube-game-dos

#153
12:59, 26 авг 2021

Gradius
> Жду Нобелевской премии и сертификата, подтверждающего наличие титановой задницы
А, может это "всё" из-за титановых/золотых/платиновых яиц? :)

@ "Снесла Курочка-ряба яйцо деду - начисто"

P.S. Может, с такими успехами, "восстановить" IDA pro? :)

#154
13:21, 26 авг 2021

Я бы не связывал выхлоп компилятора бородатого года под DOS и фразу "после тяжело оптимизирующего компилятора", так что сертификат выдаём, а Нобелевку пока откладываем.

#155
16:51, 26 авг 2021
haha | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)
#156
6:36, 27 авг 2021

KPG
> А, может это "всё" из-за титановых/золотых/платиновых яиц? :)

Обычно про яйца  вспоминают, когда хотят подчеркнуть, что у их обладателя крепкие нервы. :)

entryway
> Я бы не связывал выхлоп компилятора бородатого года под DOS и фразу "после
> тяжело оптимизирующего компилятора", так что сертификат выдаём, а Нобелевку
> пока откладываем.

Это в Паскале нет и не было оптимизации.  В ватком С она сделана очень хорошо.  Так что вопрос получения нобелевки оставляю открытым! :)

baga
> правда то что получилось как по мне ближе к ретрансляции чем к декомпиляции, я
> надеялся увидеть нечто такое

Есть и такое - звуковая система - писал самостоятельно + подрубил плеер HMP:

https://github.com/rep-stosw/tube-game-dos/tree/main/TubePCWEB/Sound

https://github.com/rep-stosw/tube-game-dos/tree/main/TubePCWEB/Music

P.S.
За сертификат - спасибо! :)

#157
10:05, 27 авг 2021

Очередной "бред наркомана" от Hex-rays.
Выловлено с помощью MinGW64.

unsigned short v77;  //16 bit

if ( v77 - 0x8000 > v77 ) 
{
 //...
}

if ( v77 + 0x8000 < v77 )
{
 //...
}

Основная мысль:  иногда разность может быть больше уменьшаемого.  А сумма меньше слагаемого.  Из-за переполнения беззнакового типа, которое тут используется. 

Я зову это - "кольцевой арифметикой".  Которая основана на переполнениях.

Переписал условия в более человечном виде:

if ( v77 < 0x8000 ){}

if ( v77 >= 0x8000 ){}
#158
14:04, 27 авг 2021

Gradius
> Это в Паскале нет и не было оптимизации. В ватком С она сделана очень хорошо.
Позднее может и стало хорошо, а в бородатые времена на уровне ранних делфи как раз и было, если не хуже. Я это помню по дизасму doom, он простой как три копейки, больше на трансляцию построчную похоже, а не на то что сейчас компиляторы вытворяют.

Если что, я в watcom не спец, но вот только что не поленился, скачал и установил watcom 8.5. Смотрю справку:
WCL386.EXE
/ox maximum optimization (/oil /s)

Окей, пишу код.

#include <stdio.h>
int main(int argc, char *argv[]) {
  int x = 0;
  x = !x;
  if(x) {
    printf("asd\n");
  }else{
    printf("qwe\n");
  }
  return 0;
}

Компилирую: wcl386 /ox a.cpp. Правильно всё делаю? Выхлоп:

+ Показать

На всякий случай попробовал "/os optimize for space" и "/ot optimize for time" - тоже самое.

#159
14:27, 27 авг 2021

entryway
> Если что, я в watcom не спец, но вот только что не поленился, скачал и
> установил watcom 8.5. Смотрю справку:
> WCL386.EXE
> /ox maximum optimization (/oil /s)

Я тоже не поленился и скомпилировал вашу программу на двух опциях.

1) без оптимизации:

+ Показать

2. С оптимизацией:

.387
.386p
.model flat
    PUBLIC  main_
    EXTRN  printf_:BYTE
    EXTRN  __argc:BYTE
    EXTRN  _cstart_:BYTE
DGROUP    GROUP  CONST,CONST2,_DATA
_TEXT    SEGMENT  PARA PUBLIC USE32 'CODE'
_TEXT    ENDS
CONST    SEGMENT  DWORD PUBLIC USE32 'DATA'
L$1:
    DB  61H, 73H, 64H, 0aH, 0, 71H, 77H, 65H
    DB  0aH, 0

CONST    ENDS
CONST2    SEGMENT  DWORD PUBLIC USE32 'DATA'
CONST2    ENDS
_DATA    SEGMENT  DWORD PUBLIC USE32 'DATA'
_DATA    ENDS
_TEXT    SEGMENT  PARA PUBLIC USE32 'CODE'
    ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP
main_:
    push        offset FLAT:L$1 
    call        near ptr FLAT:printf_ 
    add         esp,4 
    xor         eax,eax 
    ret         
_TEXT    ENDS
    END

Список команд с ключами:

cd C:\WATCOM\test
wmake -f C:\WATCOM\test\test.mk -h -e -a C:\WATCOM\test\*.obj
wcc386 test.c -i="C:\WATCOM/h" -w4 -e25 -zq -otexan -s -ob -oc -oi -zm -6r -bt=dos -fo=.obj -mf
Execution complete
#160
14:30, 27 авг 2021

Gradius
Это у тебя какой ватком? Мой такие ключи не умеет. Ну да и без разницы. Версия 8.5 как бы, не 1.0, а просто элементарнейшая оптимизация не работает, ну или я что-то не так сделал, покажи как надо.

Наверное надо было 9 ставить, им же компилили в то время как раз. Но не нашел удобной инсталяхи.

--
Проверил на 9.5. У него, в отличии от 8.5, всё ок конкретно с этим примером.

#161
15:49, 27 авг 2021

entryway
> Проверил на 9.5. У него, в отличии от 8.5, всё ок конкретно с этим примером.

В отладочной инфе игры (оригинал) есть упоминание, что использовался 10-й ватком.

Ну и к тому же, я не думаю, что программисты из Bullfrog писали игры на плохих компиляторах.

1 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)
#162
10:52, 28 авг 2021

Сегодня игра получила жизнь на 64-битных архитектурах!

Проект получил название "Tube64" и является отдельным ответвлением от основной 32-битной версии.

logo | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Такое решение было принято, для того, чтобы не загромождать код предыдущих версий игры.
Оказалось, что 64-битная версия кода обратно совместима с 32-битной: новые исходники собираются и работают и на 32 битах.

В основе адаптации кода для 64 бит, лежит концепция "коротких" указателей (Short Pointers, Broken Pointers, 32-bit Pointers, e t.c.).

Эта концепция была придумана мной. В её основе лежит непоколебимое представление о том, что размер указателя должен быть 4 байта.
При таком подходе, сохраняются все структуры игре и алгоритмы обращения к ним.
А также, арифметика указателей, приведение типов - по правилам 32-битного режима.

Ну и естественно, переход от длинных(64-битных) указателей к коротким осуществляется просто: отсечением старших 32 бит адреса.
Это реализуется наиэлементарнейшим типкастом intptr_t в int. Проще говоря,  короткий указатель - это хвост(остаток) от длинного указателя.
Обратное преобразование короткого указателя в длинный реализуется путём склеивания со старшими 32 битами базы адреса.

Имеено такой подход преобразования оказался наиболее гибок.
Вариант сложения с базой тут не прёт, потому что в структурах игры очень много "хвостов" - тех самых 32-битных огрызков.

Реализованы все необходимые операторы для работы с Short Pointers:
- загрузка указателя: длинным(обычным) указателем или целым числом
- разыменовывание указателя. Поддерживается корректная работа на многократных разыменовываниях.
- преобразование короткого указателя обратно в длинный
- есть согласование с разыменовыванием короткого указателя, если адрес невыровнен (через транзитные типкасты)

64-битная версия игры была протестирована на следующих ОС:
- Win7
- Win10
- Linux (Ubuntu)

В этом посте: https://gamedev.ru/code/forum/?id=263319&m=5437710#m0 озвучены ограничения на использование концепции.
Все ограничения удалось обойти:

1)

указатели должны указывать на один пул данных

Решение: был использован сторонний аллокатор памяти, который выделяет адреса в пределах одного пула.
Отказ от стандартного аллокатора был продиктован тем, что адреса, выделяемые им(например, в Linux) могли быть разнесены более, чем 4 ГБ, что приводило к сегфолтам.
В качестве аллокатора использовался TLSF (https://github.com/mattconte/tlsf) , за что спасибо его автору и участнику этого форума Ghost2, который познакомил меня с ним!

2)

При особо "удачном случае" для пула данных может выделиться адрес, пересекающий границу в 4ГБ. Это ещё зависит от используемого размера памяти. При пересечении границы в 4 ГБ, восстановление утраченной части указателя при разыменовывании сработает неправильно

В процессе отладки, был определён требуемый размер пула под игру. Приблизительно около 2 МБ.
Пул был взят с двукратным запасом и выровнен на границу 4 МБ (размер пула столько же).
Выравнивание на размер пула даёт нам гарантию, что пересечение границы никогда не произойдёт.

3)

должны быть написаны свои malloc, free, memset, memcpy

На практике оказалось достаточно своего аллокатора и замены трёх функций: malloc/calloc/free.
Остальные - штатные. Передача указателей в них происходит с помощью типкаста короткого в длинный.

4)

=A=L=X=: могут быть проблемы с тем, когда такие указатели временно направляются на локальные переменные находящиеся в стеке.

Были выловлены пара таких случаев в игре. Эти аллокации из стека были заменены аллокациями из нашего пула аллокатора.

Таким образом, концепция прошла "боевое крещение" и показала свою живучесть на конкретном примере игры.

---

Исходники проекта лежат в репозитории: https://github.com/rep-stosw/tube64

Собранные билды для Windows и Linux лежат в этой папке:

https://github.com/rep-stosw/tube64/tree/main/Tube64/bin

---

Напоследок, практические примеры внедрения и использования коротких указателей.

1) Загрузка целым значением, которое - 32 битный адрес:

Было:

int startup_files;
CPP_LoadAllData( (int)startup_files );

Стало:

CPP_LoadAllData( (char*)(SP<char>)startup_files );

Здесь переменная startup_files хранит 32-битный адрес, который расширяется до 64-битного указателя серией типкастов: (char*)(SP<char>)

2) Двойное разыменовывание короткого указателя с невыровненными данными:

Было:

char *v2;
CPP_draw_block( **((_WORD **)v2 + 1));

Стало:

CPP_draw_block( *(UA<u16>*)(u32*)(SP<u32>)*(SP<u32>)(v2+4) ); //misaligned data on 2-nd depointer

На двойном разыменовывании - опасная мина - невыровненные данные!
Пришлось применять на втором этапе разыменовывание невыровненных данных: *(UA<u16>*)
Перед этим, конечно, типкастим короткий указатель в длинный: (u32*)(SP<u32>)
А это - первое разыменовывание короткого указателя - оно должно давать КОРОТКИЙ 32-битный адрес для второго разыменовывания: *(SP<u32>)(v2+4)


3) Двойное разыменовывание тройного указателя - ну точно связанные 32-битные списки:

Было:

char *a3;
if ( CPP_LoadFileAt((const char *)a3, **(char ***)(a3 + 28)) != *(_DWORD *)(a3 + 36) )

Стало:

if ( (unsigned)CPP_LoadFileAt(a3, **(SP<char>)((uintptr_t)a3 + 28)) != *(UA<u32> *)(a3 + 36) )

Здесь оказалось проще - данные выровнены, двойное разыменовывание делается короткими указателями: **(SP<char>)((uintptr_t)a3 + 28))

4) Присвоение указателю суммы разыменовываний:

Было:

_DWORD *v7;
if ( v7 ) *v7 = *(_DWORD *)(a3 + 36) + **(_DWORD **)(a3 + 28);

Стало:

SP<u32> v7;
if ( v7 ) *v7 = *(UA<u32> *)(a3 + 36) + **(SP<u32>)(a3 + 28);

Здесь необходимо было введение короткого указателя для результата. Иначе условие не срабатывало без усечения старших бит.
Так как программа работает в логике 32 бит.

5) Инициализация списков 32-битными ссылками на 32-битные адреса:

Было:

*(u32*)&startup_files[(44*0)+28]=(u32)&GlassMap;
*(u32*)&array_list[2* 0]=(u32)&anim_order_1[0];
*(u32*)&array_list[2* 1]=(u32)anim_order_2;

Стало:

*(u32*)&startup_files[(44*0)+28]=(u32)(uintptr_t)&GlassMap;
*(u32*)&array_list[2* 0]=(u32)(uintptr_t)&anim_order_1[0];
*(u32*)&array_list[2* 1]=(u32)(uintptr_t)anim_order_2;

Здесь, практически, ничего не изменилось, кроме разве что добавлен транзитный типкаст в uintptr_t - и то, чтобы не ругался компилятор!
Списки честно хранят 32-битные обрезки адресов структур, поля которых не превышает 4 байта. При разыменовывании коротких указателей, адреса будут восстанавливаться.

11 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

Код движка короткого указателя - этого оказалось достаточно:

template<typename T>
struct SP
{
 u32 sp;                                        //короткий указатель (32-битная реализация)

 inline SP()                                    //конструктор объявления короткого указателя
 {
 }

 inline SP(const u32 p)                         //оператор загрузки значения в указатель
 {
  sp=p;
 }

 inline SP(const void *p)                       //оператор загрузки значения-указателя в указатель
 {
  sp=(uintptr_t)p;
 }

 inline operator T()                            //оператор типкаста SP<T> в T
 {
  return (T)sp;
 }

 inline operator T*()                           //оператор типкаста SP<T> в T*
 {
  return (T*)(BASE64|sp);
 }

 inline SP<T> & operator * ()                   //перегрузка оператора разыменовывания указателя
 {
  return *(SP<T>*)(BASE64|sp);
 }
};

Подытоживая:

1) Я рад, что у меня получилось "сделать невозможное", а именно перевести код игры с 32-бит на 64 бита без изменения алгоритмов работы с данными игры.

2) Пора наверное открывать свои курсы "программирования "не по правилам" lol | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

3) Кандидат на звание "магистр указателей и перегрузок" lol | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)

7789912-1 | Дизассемблер IDA Pro 7.5 для восстановления исходного кода игры (C/C++)
#163
13:59, 28 авг 2021

Gradius
> В основе адаптации кода для 64 бит, лежит концепция "коротких" указателей
x32 abi

#164
14:11, 28 авг 2021

clc
> x32 abi
оно мертво

Страницы: 110 11 12 13 14 Следующая »
ФлеймФорумПрограммирование