Jak fungují DTA soubory Mafie

Šifrování, struktura a obsah herních archivů Mafie: The City of Lost Heaven

Zpět na hlavní stránku
Jak fungují DTA soubory Mafie

Celá hra se vejde do dvanácti souborů

Pokud se podíváte do instalačního adresáře Mafie, najdete tam dvanáct souborů pojmenovaných A0.dtaac.dta. V nich je doslova vše — každý výstřel, každá textura dlažby, každá nota z hudby Vladimíra Šimůnka i scripty všech 21 misí. Celá Lost Heaven je zabalená do těchto archivů.

Fungují podobně jako ZIP soubory, ale s jedním háčkem: jsou zašifrované. Nestačí je jen rozbalit — bez správných klíčů dostanete jen změť nesmyslných dat. Samotná hra si klíče nese ukryté v GAME.EXE a při spuštění archivy tiše „připojí" přes vlastní knihovnu rw_data.dll. Hráč nic nevidí, nic netuší — soubory prostě fungují, jako by ležely volně na disku.

Co je v každém archivu?

Každý DTA soubor má na starost jiný typ dat. Takhle je to rozděleno:

SouborObsah
A0.dtaZvukové efekty — výstřely, kroky, motory, okolní hluk
A1.dtaMise — scripty, scény, logika všech 21 misí
A2.dta3D modely — auta, postavy, rekvizity
A3.dtaAnimace postav I
A4.dtaAnimace postav II
A5.dtaRozdílová data — opravy a patche aplikované přes původní obsah
A6.dtaTextury — povrchy, materiály, vizuální vzhled celého světa
A7.dtaZáznamy a interní data
A9.dtaSystémová data enginu
aa.dtaHerní tabulky — statistiky, vlastnosti objektů, parametry
ab.dtaHudba — Citymusic, pozadí a hudba 
ac.dtaAnimace postav III

A6.dta s texturami a A1.dta s daty misí bývají zdaleka největší. Jen data misí zahrnují pohyb všech NPC, rozmístění aut, dialogy a stovky scriptových příkazů pro každou ze 21 misí.

Šifrování: dva klíče a trocha magie s nulami

Každý DTA archiv má svůj vlastní pár šifrovacích klíčů — dvě 32-bitová čísla (tj. čísla velká až přes čtyři miliardy) ukrytá v GAME.EXE. Bez nich jde obsah přečíst jen jako šum.

Samotné šifrování je elegantně jednoduché. Hra prochází data po osmi bajtech najednou a na každý blok aplikuje dvě operace přímé z procesoru: NOT (překlopí každý bit — jedničky se stanou nulami a naopak) a XOR s klíčem (porovná bity a tam, kde se liší, dá jedničku). Geniální na tom je, že stejný postup data zašifruje i dešifruje — nejsou potřeba dva různé algoritmy.

Navíc jsou klíče před použitím ještě jednou XORovány s pevnými hodnotami (0x34985762 a 0x39475694), takže i kdyby někdo klíče v EXE souboru našel, nevidí rovnou jejich „pravou" podobu.

Níže je dešifrovací funkce přesně tak, jak ji MassaSnygga v roce 2002 zkopíroval z disassembleru rw_data.dll. Funkce dostane buffer se zašifrovanými daty, jejich délku a oba klíče. Na výstupu jsou data čitelná:
C / C++
void Decrypt(void* Buffer, DWORD BufferSize, DWORD Key1, DWORD Key2)
// DWORD = 32-bitové celé číslo (4 bajty), hodnota 0 až ~4,3 miliardy
{
    __asm
    {
        mov    ecx, BufferSize        ; délka dat do registru ECX
        push   esi                    ; uložit registry (konvence volání)
        mov    eax, ecx
        push   edi
        shr    eax, 3                 ; vydělit 8 → počet celých 8-bajtových bloků
        lea    esi, ds:0[eax*8]       ; velikost zarovnaná na 8 bajtů
        sub    ecx, esi               ; zbytek po zarovnání (0–7 bajtů)
        mov    edx, ecx               ; uložit zbytek
        mov    ecx, Buffer            ; ukazatel na data
        test   eax, eax
        jz     short loc_0_100046F5  ; žádné celé bloky → přejít na zbytek
        mov    edi, Key1              ; první klíč do EDI
        push   Key2                   ; druhý klíč na zásobník
        push   ebx
        push   ebp
    loc_0_100046B4:                   ; --- smyčka přes 8-bajtové bloky ---
        mov    ebx, [ecx+eax*8-8]    ; načíst první 4 bajty bloku
        mov    ebp, [ecx+eax*8-4]    ; načíst druhé 4 bajty bloku
        dec    eax
        not    ebx                    ; NOT: překlopit všechny bity
        not    ebp
        mov    [ecx+eax*8], ebx
        mov    ebx, [esp + 8h]        ; druhý klíč ze zásobníku
        mov    [ecx+eax*8+4], ebp
        mov    ebp, [ecx+eax*8]
        xor    ebp, ebx               ; XOR s druhým klíčem
        mov    ebx, [ecx+eax*8+4]
        xor    ebx, edi               ; XOR s prvním klíčem
        mov    [ecx+eax*8], ebp
        mov    [ecx+eax*8+4], ebx
        mov    ebx, ebp
        mov    ebp, [ecx+eax*8+4]
        not    ebx                    ; druhý průchod NOT
        not    ebp
        test   eax, eax
        mov    [ecx+eax*8], ebx
        mov    [ecx+eax*8+4], ebp
        jnz    short loc_0_100046B4  ; opakovat dokud jsou bloky
        pop    ebp
        pop    ebx
    loc_0_100046F5:                   ; --- zbytek (méně než 8 bajtů) ---
        add    ecx, esi
        test   edx, edx
        jz     short loc_0_1000471A  ; žádný zbytek → konec
        lea    esi, Key2
        lea    eax, [ecx+edx]
        sub    esi, ecx
        mov    edi, edx
    loc_0_10004706:
        mov    cl, [eax-1]            ; bajt po bajtu
        dec    eax
        not    cl                     ; NOT na jeden bajt
        mov    [eax], cl
        mov    dl, [esi+eax]
        xor    dl, cl                 ; XOR s klíčem
        dec    edi
        not    dl                     ; druhý NOT
        mov    [eax], dl
        jnz    short loc_0_10004706
    loc_0_1000471A:
        pop    edi
        pop    edi
        pop    esi
    }
}

Klíče ke všem dvanácti souborům MassaSnygga vyreverseoval trpělivým debugováním a analýzou GAME.EXE. Jakmile byly klíče známé, mohly vzniknout první nástroje pro extrakci obsahu — a s nimi i základ celé moddovací komunity.

Jak hra archivy čte — rw_data.dll

Mafia nevyřešila čtení DTA archivů přímo v GAME.EXE, ale přesunula tu práci do samostatné knihovny rw_data.dll v instalačním adresáři. Tato DLL je vlastně minimalista čtecí vrstva — umí připojit archiv, otevřít soubor uvnitř, přečíst ho a zavřít:

FunkceCo dělá
dtaCreate()„Připojí" DTA archiv a zpřístupní jeho obsah. Navzdory názvu nic nevytváří — spíš jde o „mount".
dtaOpen()Otevře konkrétní soubor uvnitř archivu a vrátí handle (číselný identifikátor) pro práci s ním.
dtaRead()Přečte požadovaný počet bajtů z otevřeného souboru — stejně jako běžné čtení ze souboru na disku.
dtaClose()Uzavře soubor otevřený přes dtaOpen(). Jako zavření okna po dočtení.
dtaSetDtaFirstForce()Přepne prioritu: DTA archiv má přednost před soubory volně na disku. Klíčová funkce pro moddování — viz níže.
dtaSeek()Přesune pozici čtení na jiné místo v souboru (jako přetočení u přehrávače).

Zvláštní pozornost si zaslouží dtaSetDtaFirstForce(). Ve výchozím stavu má čtení z disku přednost před archivem — hra se nejdřív podívá na disk, a teprve když tam soubor nenajde, sáhne do DTA. To znamená, že modder může jednoduše položit upravený soubor vedle GAME.EXE a hra ho automaticky načte místo originálu. Žádné přepisování archivů, žádné pájení — prostě zkopírovat soubor.

Jenže v průběhu startu hra zavolá dtaSetDtaFirstForce() a priorita se obrátí — od té chvíle DTA vyhrává nad diskem. Proto nástroj MafiaDataXtractor zvolil mazané řešení: v DLL přepsal tuto funkci tak, aby nedělala vůbec nic. Hra si myslí, že přepnutí provedla, ale ve skutečnosti zůstalo vše při starém — disk má pořád přednost, a moddeři mohli načítat soubory přímo.

Takhle vypadá kód, který hra spouští po každém dtaCreate() — připojí archiv a vzápětí nastaví dešifrovací klíče přes interní tabulku funkcí v DLL:
C / C++
DWORD Result;  // DWORD = 32-bitové číslo, zde slouží jako pointer (adresa v paměti)

if (Result = dtaCreate("a9.dta"))  // připoj archiv; Result = adresa interní tabulky
{
    __asm  // inline assembler — přímé instrukce procesoru
    {
        mov eax, 06FEE6324h     ; první klíč (pro A9.dta) do registru EAX
        xor eax, 034985762h     ; zamaskuj ho XOR operací (pevná hodnota pro všechny DTA)
        push eax                ; ulož připravený klíč na zásobník

        mov eax, 0ACDA4783h     ; druhý klíč (pro A9.dta) do EAX
        xor eax, 039475694h     ; zamaskuj druhým pevným XOR faktorem
        push eax                ; ulož druhý klíč na zásobník

        mov ecx, Result         ; do ECX dej pointer vrácený z dtaCreate()
        mov eax, [ecx]          ; přečti adresu tabulky funkcí z tohoto pointeru
        call [eax + 0ch]        ; zavolej čtvrtou funkci v tabulce (+0x0C = offset 12)
                                ; → ta interně nastaví klíče pro dešifrování archivu
    }
}

Struktura souboru — co přesně hledáme v bajtech

Kdo chce s DTA pracovat bez rw_data.dll — třeba napsat vlastní extraktor — musí znát fyzický formát souboru. Každý DTA začíná čtyřmi bajty s textem ISD0. Je to tzv. „magické číslo" — identifikátor formátu, který programu řekne: „toto je opravdový DTA archiv".

Hned za ním leží malá hlavička (16 bajtů), zašifrovaná — nutno nejprve dešifrovat. Po dešifrování odhalí základní mapu archivu:

PoziceTypHodnota
0DWORDPočet souborů uložených v archivu
4DWORDKde v souboru začíná seznam obsahu (offset v bajtech)
8DWORDJak dlouhý je seznam obsahu
12DWORDNeznámé (pravděpodobně interní příznak)

Poznámka k typům: DWORD (Double Word) = 32-bitové celé číslo — 4 bajty, hodnota 0 až ~4,3 miliardy. UCHAR = 1 bajt bez znaménka (0–255). char[] = textový řetězec.

Z hlavičky se dostaneme k seznamu obsahu — sérii 28-bajtových záznamů, jeden za každý soubor v archivu. Každý záznam obsahuje zkrácený název (posledních 16 znaků) a odkaz na podrobnější informační blok. Zkrácený název slouží k rychlému porovnání — pokud odpovídá, teprve pak se čte celý blok.

Teprve informační blok souboru prozradí úplný název a adresu dat:

PoziceTypHodnota
0–15DWORDInterní identifikátor (neznámý účel, pravděpodobně hash)
16DWORDVelikost souboru v bajtech
20DWORDNeznámé
24UCHARDélka názvu souboru (kolik znaků následuje)
25–31char[7]Neznámé (výplň nebo interní data)
32+char[]Název souboru — celá cesta, např. maps\freeride\city.bin

Jakmile máme celý název, stačí ho předat do dtaOpen() — nebo si podle offsetu v archivu soubor manuálně najít a přečíst. Takhle fungují všechny extraktory DTA archivů.

Co se s extrahovanými soubory dá dělat?

Jakmile MassaSnygga v roce 2002 publikoval klíče a formát, ostatní vývojáři rychle napsali nástroje. Nejznámější byl MafiaDataXtractor — program, který z DTA archivů jedním kliknutím vytáhl veškerý obsah.

A co z toho vzešlo? Extrahované soubory otevřely dveře celé moddovací komunitě:

Typ souboruCo lze dělat
Textury (.bmp)Reskin aut, budov, postav — nové barvy, vzory, historické verze vozidel
3D modely (.4ds)Náhrada modelů aut nebo postav za vlastní tvorbu
Scripty misí (scene2.bin, cache.bin)Úpravy misí, nové mise, změny chování NPC
Zvuky (.wav)Nový dabing, hudební mody, změna zvuků zbraní
Herní tabulkyVlastnosti aut, zbraní, fyziky — výkonové mody

Právě díky těmto souborům vznikly desítky modů — od přeskinovaných historických aut přes české dabingové mody až po celé nové mise. A vše začalo tím, že jeden programátor trpělivě prozkoumával binární data a vysledoval, kde jsou ukryté klíče.

Zdroj

Technické informace vycházejí z dokumentu „Accessing Mafias DTA files" od MassaSnygga (kamalook.de), původně publikovaného v roce 2002. Dokument vznikl zpětnou analýzou (reverse engineering) — rozebrání hotového programu a postupné pochopení toho, jak funguje. Část detailů (pole označená jako „neznámé") nebyla nikdy plně objasněna, jak autor sám přiznával.

Komentáře 0

Zatím žádné komentáře. Buď první!

Přidat komentář