Archiwa tagu: Stunt GP

Stunt GP #2: Ponadczasowe problemy

Kiedy zacząłem grać w Stunt GP na Windowsie 10 natychmiast zauważyłem denerwujące półsekundowe przycinki co 7 minut i 9.5 sekundy, problem który praktycznie nie występował na starszych komputerach. W końcu po półtora roku znalazłem powód dlaczego tak się dzieje i naprawiłem ten błąd 🙂

Problem

Gra używa funkcji QueryPerformanceFrequency() oraz QueryPer-
formanceCounter()
do pomiaru czasu. Pierwsza funkcja zwraca częstotliwość z jaką licznik czasu jest aktualizowany. Ta częstotliwość jest różna dla każdego komputera, ale o tym za chwilę. Licznik wydajności jest 64 bitowy, nawet na najszybszych komputerach jego przepełnienie i zresetowanie do 0 nastąpiło by dopiero po 24 TYSIĄCACH lat (zakładając częstotliwość 24MHz). Problem polega na tym, że gra używa tylko 32 bitów z tego licznika, co oznacza, że w najgorszym razie ten licznik przepełni się już po 3 minutach.

Na starszych komputerach częstotliwość tego licznika oscylowała wokół 2-3 MHz, więc licznik przepełnił by się po 23-36 minutach. W Windowsie 10 Microsoft zdecydował ustawić tę częstotliwość na 10MHz, niezależnie od sprzętu, więc licznik przepełnia się dokładnie co 7 minut i 9.5 sekundy. Użytkownik może wymusić używanie sprzętowego zegara przez Windowsa, ale zamiast częstotliwości rzedu 2-3 MHz, w nowszych komputerach wyposażonych w High Precision Event Timer (HPET) ten zegar może mieć częstotliwość 14 lub nawet 24MHz, przepełniając licznik w 5 lub nawet 3 minuty!

Gra źle znosi przepełnienia tego licznika, po prostu się zawieszając. Jedynie to, ze gra wykrywa gdy zacięła się na pół sekundy i resetuje wewnętrzne liczniki pozwala grze działać, ale samo przepełnienie licznika nie jest traktowane w jakiś szczególny sposób.

Rozwiązanie

Stworzyłem nowy moduł StuntKit, który podczas startu gry podmienia funkcję odpowiedzialną za pomiar czasu na taką, która wykrywa przepełnienie licznika i resetuje wewnętrzne liczniki gry, eliminując półsekundowe przycinki.

Stunt GP #1: rozpakowywanie plików

Co jest w folderze gry?

W folderze gry znajdowały się pliki .wad, .pc oraz .pmd. Pliki .pmd znajdowały się w folderze meshdata, więc zapewne były modelami 3D, pliki .pc zaś mieściły się w graphics24, co wskazywałoby na tekstury. Pozostały więc pliki .wad, które podejrzewałem o bycie archiwami, być może takimi samymi jak pliki .wad z gry Doom.

Magia i czary

Wiele plików binarnych posiada magię, kilka pierwszych bajtów identyfikujących typ pliku, warto zacząć sprawdzanie od tego. Plik .pmd zaczynał się od „PMD V1.83”, niestety nie znalazłem informacji w internecie o takich plikach. Pliki PC zaczynały się od „TM!”, a pliki .wad od „DIR”.

Lewa część okna pokazuje bajty w pliku,a prawa ich reprezentację tekstową, jeżeli istnieje

Pliki .wad

Okazuje się, ze po pierwszych 4 bajtach znajduje się rozmiar pliku zapisany jako liczba 32bitowa little-endian. Little endian oznacza, że kolejność bajtów jest zamieniona, od najmniejszego do największego,czyli poniższe „1C 64 02 00” to tak naprawdę liczba szesnastkowa 0x0002641c, co w przeliczeniu wynosi 156700, dokładnie tyle ile bajtów ma boot.wad! Następnie znajdują się 4 bajty których przeznaczenia nie znam. Potem od razu widać znane już „TM!”, czyli plik boot.wad prawdopodobnie jest archiwum zawierającym inne pliki.

Nieznana wartość jest mniejsza od rozmiaru pliku, więc może wskazywać na coś w środku pliku, więc przeniosłem się pod adres 0x252B4:

„0A” i mnóstwo bajtów o wartości 00, poprzeplatanych sporadycznie losowymi danymi. Około 4096 bajtów potem widać nazwy plików w archiwum:

Tak jak na początku pliku tak i tutaj większość wartości wyrównana jest to 4 bajtów, więc po nazwie pliku znajdują się 3 bajty „00”. Za nimi znajduje się 12 bajtów, prawdopodobnie ułożonych w 3 4-bajtowe wartości, więc i pierwszy plik będzie posiadał te wartości.

Dla pierwszego pliku pierwsza wartość wynosi 0, druga zaś jest 0x0C, a trzecia 0x448A (17 546). Druga z nich wskazuje na początek pliku w archiwum,a trzecia rozmiar pliku. Te informacje powinny wystarczyć do rozpakowania wszystkich plików z archiwum.

Pliki .wad: więcej informacji

Pominę niestety większą część opisu jak udało mi się ustalić konkretne wartości, ten post i tak będzie długi, a skupię się na tym o czym trzeba pamiętać, aby móc spakować własne archiwum do tej gry.

Po dłuższej analizie pliku udało mi się również ustalić, że po każdym pliku znajduje się bajt „1A” oraz bajty „00” tak, aby kolejny plik zaczynał się na offsecie (przemieszczeniu) wyrównanym do 4 bajtów (podzielnym przez 4). Okazuje się również, że pliki .dir są znane od czasów Worms Armageddon, o czym dowiedziałem się niestety dopiero po stworzeniu programu rozpakowywującego 🙁 Ale informacje o tablicy 1024 4-bajtowych hashów nazw plików pozwoliło mi pakować własne pliki .wad i podmieniać istniejące tekstury.

Pliki .pc

W tym pliku wartości są wyrównane do 2 bajtów: na początku pliku znajduje się 4-bajtowa magia „TM! 0x1A”, 0x0003, które zawsze jest takie samo i nie wydaje się być używane, szerokość i wysokość obrazu zapisane za pomocą 2 bajtów i skompresowane dane obrazu.

Obraz 256×256 pikseli (0x100 = 256)

Na szczęście istnieje program rozpakowywujący pliki .pc do .bmp: https://forum.xentax.com/viewtopic.php?f=16&t=16944&p=160362#p146133,

Niestety był on dostępny tylko w formie instrukcji assemblera, co czyniło go bardzo nieczytelnym i nie pozwalało pakować innych tekstur. Udało mi się jednak odczytać i zrozumieć jak działa rozpakowywanie danych z plików .pc, napisać program rozpakowujący w języku Python, oraz napisać program pakujący dostępny pod adresem https://github.com/Halamix2/stunt_gp_formats

Podmiana działa. Wybrałem najgorszy możliwy obrazek do podmiany, ponieważ logo zespołu to 3 osobne pliki, kolor, przezroczystość i cień