Reverse engineering a simple game archive

Sometimes I like to poke around in the files of the games I bought on Steam, just for fun or maybe to listen to the game music. Most game engines put the game files in their own proprietary archive files, so I started to reverse engineer those formats, at least to the point where I could extract the files. Some game archive file formats proved to be so simple that I was able to easily write the archives, too. So I thought I write down how I did it, maybe someone else finds this useful?

The main tools I use are a hex editor (bless) and Python.

Short detour: I'm not 100% satisfied with bless, though. Let me show you what I mean:

I would like those fields in the status bar to be text fields that I can edit, making it easy to select a certain area of the file when I know the size in bytes. If anyone knows a (Linux) hex editor that does that let me know!

But what I do like about bless is that it displays the bytes at the cursor in all possible number formats and it's dynamically resizable, making table structures in the file much more apparent. See:

So much for that. Now to the actual reverse engineering. Let's start with the simples game archive format that I saw: FEZ .pak files. Looking at the start of FEZ/Content/Essentials.pak we see this:

00000000: d700 0000 186f 7468 6572 2074 6578 7475  .....other textu
00000010: 7265 735c 6675 6c6c 7768 6974 65cb 0000  res\fullwhite...
00000020: 0058 4e42 7705 01cb 0000 0001 9401 4d69  .XNBw.........Mi
00000030: 6372 6f73 6f66 742e 586e 612e 4672 616d  crosoft.Xna.Fram
00000040: 6577 6f72 6b2e 436f 6e74 656e 742e 5465  ework.Content.Te
00000050: 7874 7572 6532 4452 6561 6465 722c 204d  xture2DReader, M
00000060: 6963 726f 736f 6674 2e58 6e61 2e46 7261  icrosoft.Xna.Fra
00000070: 6d65 776f 726b 2e47 7261 7068 6963 732c  mework.Graphics,
00000080: 2056 6572 7369 6f6e 3d34 2e30 2e30 2e30   Version=4.0.0.0
00000090: 2c20 4375 6c74 7572 653d 6e65 7574 7261  , Culture=neutra
000000a0: 6c2c 2050 7562 6c69 634b 6579 546f 6b65  l, PublicKeyToke
000000b0: 6e3d 3834 3263 6638 6265 3164 6535 3035  n=842cf8be1de505
000000c0: 3533 0000 0000 0001 0000 0000 0200 0000  53..............
000000d0: 0200 0000 0100 0000 1000 0000 ffff ffff  ................
000000e0: ffff ffff ffff ffff ffff ffff 1f6f 7468  .............oth
000000f0: 6572 2074 6578 7475 7265 735c 7472 616e  er textures\tran
00000100: 7370 6172 656e 7477 6869 7465 cb00 0000  sparentwhite....
00000110: 584e 4277 0501 cb00 0000 0194 014d 6963  XNBw.........Mic
00000120: 726f 736f 6674 2e58 6e61 2e46 7261 6d65  rosoft.Xna.Frame
00000130: 776f 726b 2e43 6f6e 7465 6e74 2e54 6578  work.Content.Tex
00000140: 7475 7265 3244 5265 6164 6572 2c20 4d69  ture2DReader, Mi
00000150: 6372 6f73 6f66 742e 586e 612e 4672 616d  crosoft.Xna.Fram
00000160: 6577 6f72 6b2e 4772 6170 6869 6373 2c20  ework.Graphics, 
00000170: 5665 7273 696f 6e3d 342e 302e 302e 302c  Version=4.0.0.0,
00000180: 2043 756c 7475 7265 3d6e 6575 7472 616c   Culture=neutral
00000190: 2c20 5075 626c 6963 4b65 7954 6f6b 656e  , PublicKeyToken
000001a0: 3d38 3432 6366 3862 6531 6465 3530 3535  =842cf8be1de5055
000001b0: 3300 0000 0000 0100 0000 0002 0000 0002  3...............
000001c0: 0000 0001 0000 0010 0000 00ff ffff 00ff  ................
000001d0: ffff 00ff ffff 00ff ffff 0018 6f74 6865  ............othe
...

So there are 5 bytes that aren't printable ASCII character and then there is a bit of ASCII. Those first bytes don't look like a file signature/magic, so I guess it's already data. But denoting what and how? For the how: Most CPU architectures today are little-endian, Windows certainly only runs on little-endian systems (ignoring the XBox 360). Little- and big-endian are ways to encode integers in memory. It's the order of the bytes that make up the number. A 32bit integer consists of 4 bytes. For little-endian the least significant byte comes first, for big endian it's the other way around. Almost everyone has strong opinions about which encoding is the "correct" one, but let's not go into that.

As a first assumption (which I never had to overthrow so far) let's assume integers are stored in little-endian encoding. As a further assumption lets say that sizes of embedded files and things like file counts are stored as unsigned 32 bit values. Only if the game files would exceed 4 GB (or 2 GB for signed values) one would need 64 bit integers. So let's interpret the first 4 bytes as an unsigned 32 bit integer, what do we get? 215 That's a low enough number that it might be a file count. Let's run with that. But it's 5 bytes that are non-ASCII, what is the next one? Well, file names usually are quite short, so maybe they are limited to 255 characters in FEZ? Then we get 24 followed by 24 bytes of printable ASCII characters ("other textures\fullwhite") and then more non-ASCII bytes. So that seems right. How many non-ASCII bytes? 4! As 32 bit integer it's the number 203. A file size? Maybe the next 203 bytes are the file data?

Coloring in the bytes of the file count and the first file you get this:

  • number of files
  • file name length
  • file name
  • file data size
  • file data
00000000: d700 0000 186f 7468 6572 2074 6578 7475  .....other textu
00000010: 7265 735c 6675 6c6c 7768 6974 65cb 0000  res\fullwhite...
00000020: 0058 4e42 7705 01cb 0000 0001 9401 4d69  .XNBw.........Mi
00000030: 6372 6f73 6f66 742e 586e 612e 4672 616d  crosoft.Xna.Fram
00000040: 6577 6f72 6b2e 436f 6e74 656e 742e 5465  ework.Content.Te
00000050: 7874 7572 6532 4452 6561 6465 722c 204d  xture2DReader, M
00000060: 6963 726f 736f 6674 2e58 6e61 2e46 7261  icrosoft.Xna.Fra
00000070: 6d65 776f 726b 2e47 7261 7068 6963 732c  mework.Graphics,
00000080: 2056 6572 7369 6f6e 3d34 2e30 2e30 2e30   Version=4.0.0.0
00000090: 2c20 4375 6c74 7572 653d 6e65 7574 7261  , Culture=neutra
000000a0: 6c2c 2050 7562 6c69 634b 6579 546f 6b65  l, PublicKeyToke
000000b0: 6e3d 3834 3263 6638 6265 3164 6535 3035  n=842cf8be1de505
000000c0: 3533 0000 0000 0001 0000 0000 0200 0000  53..............
000000d0: 0200 0000 0100 0000 1000 0000 ffff ffff  ................
000000e0: ffff ffff ffff ffff ffff ffff 1f6f 7468  .............oth
000000f0: 6572 2074 6578 7475 7265 735c 7472 616e  er textures\tran
00000100: 7370 6172 656e 7477 6869 7465 cb00 0000  sparentwhite....
00000110: 584e 4277 0501 cb00 0000 0194 014d 6963  XNBw.........Mic
00000120: 726f 736f 6674 2e58 6e61 2e46 7261 6d65  rosoft.Xna.Frame
00000130: 776f 726b 2e43 6f6e 7465 6e74 2e54 6578  work.Content.Tex
00000140: 7475 7265 3244 5265 6164 6572 2c20 4d69  ture2DReader, Mi
00000150: 6372 6f73 6f66 742e 586e 612e 4672 616d  crosoft.Xna.Fram
00000160: 6577 6f72 6b2e 4772 6170 6869 6373 2c20  ework.Graphics, 
00000170: 5665 7273 696f 6e3d 342e 302e 302e 302c  Version=4.0.0.0,
00000180: 2043 756c 7475 7265 3d6e 6575 7472 616c   Culture=neutral
00000190: 2c20 5075 626c 6963 4b65 7954 6f6b 656e  , PublicKeyToken
000001a0: 3d38 3432 6366 3862 6531 6465 3530 3535  =842cf8be1de5055
000001b0: 3300 0000 0000 0100 0000 0002 0000 0002  3...............
000001c0: 0000 0001 0000 0010 0000 00ff ffff 00ff  ................
000001d0: ffff 00ff ffff 00ff ffff 0018 6f74 6865  ............othe
...

Indeed it works to read the file like that (1 byte file name length, file name, 4 bytes file data length, file data, repeat). That was easy!

Using what we learned we can write a small Python script to list files of a FEZ game archive:

Which produces this output if you pass Essentials.pak to it:

    0         33        203 other textures\fullwhite
    1        272        203 other textures\transparentwhite
    2        504        211 other textures\fullblack
    3        745      10150 other textures\smooth_ray
    4      10928      44506 other textures\rainbow_flare
    5      55465       3980 other textures\flare_alpha
    6      59480        210 background planes\white_square
    7      59726        251 background planes\dust_particle
    8      60005       3016 effects\basicposteffect
    9      63043       3928 effects\doteffect
   10      67012      22804 effects\instancedanimatedplaneeffect
   11      89848      10192 effects\animatedplaneeffect
   12     100075       3680 effects\fakepointspriteseffect
   13     103790       5760 effects\defaulteffect_textured
   14     109590       5148 effects\defaulteffect_vertexcolored
   15     114781       7288 effects\defaulteffect_litvertexcolored
   16     122107       7624 effects\defaulteffect_littextured
   17     129779       5644 effects\defaulteffect_texturedvertexcolored
   18     135474       7672 effects\defaulteffect_littexturedvertexcolored
   19     143183      17316 effects\instancedblackholeeffect
   20     160527     128137 sounds\ui\menu\exitgame
   21     288691      42781 sounds\ui\menu\confirm
   22     331502     151699 sounds\nature\watersplash
   23     483237    1197237 sounds\intro\zoomtofarawayplace
   24    1680502     264705 sounds\zu\blackholebuzz
   25    1945232      21014 resources\statictext
   26    1966280        278 other textures\glyphs\abutton
   27    1966597        278 other textures\glyphs\abutton_sony
   28    1966912        308 other textures\glyphs\backbutton
   29    1967262        334 other textures\glyphs\backbutton_sony
   30    1967630        278 other textures\glyphs\bbutton
   31    1967947        278 other textures\glyphs\bbutton_sony
   32    1968260        260 other textures\glyphs\dpaddown
   33    1968560        280 other textures\glyphs\dpaddown_sony
   34    1968875        262 other textures\glyphs\dpadleft
   35    1969177        284 other textures\glyphs\dpadleft_sony
   36    1969497        260 other textures\glyphs\dpadright
   37    1969798        284 other textures\glyphs\dpadright_sony
   38    1970115        258 other textures\glyphs\dpadup
   39    1970411        280 other textures\glyphs\dpadup_sony
   40    1970727        316 other textures\glyphs\leftarrow
   41    1971080        288 other textures\glyphs\leftbumper
   42    1971409        294 other textures\glyphs\leftbumper_ps3
   43    1971744        308 other textures\glyphs\leftbumper_ps4
   44    1972088        302 other textures\glyphs\leftstick
   45    1972431        264 other textures\glyphs\leftstick_sony
   46    1972733        266 other textures\glyphs\lefttrigger
   47    1973041        276 other textures\glyphs\lefttrigger_ps3
   48    1973359        278 other textures\glyphs\lefttrigger_ps4
   49    1973674        316 other textures\glyphs\rightarrow
   50    1974028        298 other textures\glyphs\rightbumper
   51    1974368        296 other textures\glyphs\rightbumper_ps3
   52    1974706        314 other textures\glyphs\rightbumper_ps4
   53    1975057        310 other textures\glyphs\rightstick
   54    1975409        278 other textures\glyphs\rightstick_sony
   55    1975726        276 other textures\glyphs\righttrigger
   56    1976045        286 other textures\glyphs\righttrigger_ps3
   57    1976374        292 other textures\glyphs\righttrigger_ps4
   58    1976704        322 other textures\glyphs\startbutton
   59    1977069        304 other textures\glyphs\startbutton_sony
   60    1977407        280 other textures\glyphs\xbutton
   61    1977726        278 other textures\glyphs\xbutton_sony
   62    1978038        286 other textures\glyphs\ybutton
   63    1978363        280 other textures\glyphs\ybutton_sony
   64    1978682        276 other textures\keyboard_glyphs\p_0
   65    1978997        266 other textures\keyboard_glyphs\p_1
   66    1979302        284 other textures\keyboard_glyphs\p_2
   67    1979625        278 other textures\keyboard_glyphs\p_3
   68    1979942        278 other textures\keyboard_glyphs\p_4
   69    1980259        282 other textures\keyboard_glyphs\p_5
   70    1980580        282 other textures\keyboard_glyphs\p_6
   71    1980901        278 other textures\keyboard_glyphs\p_7
   72    1981218        274 other textures\keyboard_glyphs\p_8
   73    1981531        280 other textures\keyboard_glyphs\p_9
   74    1981850        280 other textures\keyboard_glyphs\p_a
   75    1982174        352 other textures\keyboard_glyphs\p_arrows
   76    1982574        272 other textures\keyboard_glyphs\p_arrow_down
   77    1982894        280 other textures\keyboard_glyphs\p_arrow_left
   78    1983223        280 other textures\keyboard_glyphs\p_arrow_right
   79    1983549        276 other textures\keyboard_glyphs\p_arrow_up
   80    1983864        270 other textures\keyboard_glyphs\p_b
   81    1984181        366 other textures\keyboard_glyphs\p_backspace
   82    1984586        280 other textures\keyboard_glyphs\p_c
   83    1984908        358 other textures\keyboard_glyphs\p_caps
   84    1985309        268 other textures\keyboard_glyphs\p_comma
   85    1985616        274 other textures\keyboard_glyphs\p_d
   86    1985934        346 other textures\keyboard_glyphs\p_delete
   87    1986324        282 other textures\keyboard_glyphs\p_divide
   88    1986645        278 other textures\keyboard_glyphs\p_e
   89    1986964        312 other textures\keyboard_glyphs\p_end
   90    1987319        358 other textures\keyboard_glyphs\p_enter
   91    1987720        266 other textures\keyboard_glyphs\p_equal
   92    1988027        320 other textures\keyboard_glyphs\p_esc
   93    1988391        356 other textures\keyboard_glyphs\p_escape
   94    1988789        334 other textures\keyboard_glyphs\p_esdf
   95    1989162        278 other textures\keyboard_glyphs\p_f
   96    1989480        290 other textures\keyboard_glyphs\p_f0
   97    1989810        280 other textures\keyboard_glyphs\p_f1
   98    1990131        290 other textures\keyboard_glyphs\p_f10
   99    1990462        278 other textures\keyboard_glyphs\p_f11
  100    1990781        290 other textures\keyboard_glyphs\p_f12
  101    1991111        294 other textures\keyboard_glyphs\p_f2
  102    1991445        288 other textures\keyboard_glyphs\p_f3
  103    1991773        292 other textures\keyboard_glyphs\p_f4
  104    1992105        288 other textures\keyboard_glyphs\p_f5
  105    1992433        292 other textures\keyboard_glyphs\p_f6
  106    1992765        286 other textures\keyboard_glyphs\p_f7
  107    1993091        286 other textures\keyboard_glyphs\p_f8
  108    1993417        290 other textures\keyboard_glyphs\p_f9
  109    1993746        282 other textures\keyboard_glyphs\p_g
  110    1994067        278 other textures\keyboard_glyphs\p_h
  111    1994387        320 other textures\keyboard_glyphs\p_home
  112    1994746        266 other textures\keyboard_glyphs\p_i
  113    1995054        328 other textures\keyboard_glyphs\p_ijkl
  114    1995426        360 other textures\keyboard_glyphs\p_insert
  115    1995825        272 other textures\keyboard_glyphs\p_j
  116    1996136        284 other textures\keyboard_glyphs\p_k
  117    1996459        262 other textures\keyboard_glyphs\p_l
  118    1996764        318 other textures\keyboard_glyphs\p_l_alt
  119    1997129        272 other textures\keyboard_glyphs\p_l_bracket
  120    1997448        352 other textures\keyboard_glyphs\p_l_control
  121    1997845        348 other textures\keyboard_glyphs\p_l_shift
  122    1998240        356 other textures\keyboard_glyphs\p_l_windows
  123    1998635        286 other textures\keyboard_glyphs\p_m
  124    1998964        268 other textures\keyboard_glyphs\p_minus
  125    1999278        286 other textures\keyboard_glyphs\p_multiply
  126    1999603        278 other textures\keyboard_glyphs\p_n
  127    1999923        320 other textures\keyboard_glyphs\p_none
  128    2000286        324 other textures\keyboard_glyphs\p_num_0
  129    2000653        318 other textures\keyboard_glyphs\p_num_1
  130    2001014        330 other textures\keyboard_glyphs\p_num_2
  131    2001387        326 other textures\keyboard_glyphs\p_num_3
  132    2001756        320 other textures\keyboard_glyphs\p_num_4
  133    2002119        328 other textures\keyboard_glyphs\p_num_5
  134    2002490        326 other textures\keyboard_glyphs\p_num_6
  135    2002859        322 other textures\keyboard_glyphs\p_num_7
  136    2003224        324 other textures\keyboard_glyphs\p_num_8
  137    2003591        322 other textures\keyboard_glyphs\p_num_9
  138    2003959        352 other textures\keyboard_glyphs\p_num_lock
  139    2004350        276 other textures\keyboard_glyphs\p_o
  140    2004665        274 other textures\keyboard_glyphs\p_p
  141    2004986        356 other textures\keyboard_glyphs\p_page_down
  142    2005387        312 other textures\keyboard_glyphs\p_page_up
  143    2005743        266 other textures\keyboard_glyphs\p_period
  144    2006051        260 other textures\keyboard_glyphs\p_pipe
  145    2006353        278 other textures\keyboard_glyphs\p_plus
  146    2006670        276 other textures\keyboard_glyphs\p_q
  147    2006992        274 other textures\keyboard_glyphs\p_question
  148    2007310        268 other textures\keyboard_glyphs\p_quotes
  149    2007617        282 other textures\keyboard_glyphs\p_r
  150    2007942        316 other textures\keyboard_glyphs\p_r_alt
  151    2008305        270 other textures\keyboard_glyphs\p_r_bracket
  152    2008622        352 other textures\keyboard_glyphs\p_r_control
  153    2009019        342 other textures\keyboard_glyphs\p_r_shift
  154    2009408        350 other textures\keyboard_glyphs\p_r_windows
  155    2009797        280 other textures\keyboard_glyphs\p_s
  156    2010121        354 other textures\keyboard_glyphs\p_scroll
  157    2010522        268 other textures\keyboard_glyphs\p_semicolon
  158    2010833        354 other textures\keyboard_glyphs\p_space
  159    2011226        274 other textures\keyboard_glyphs\p_t
  160    2011541        308 other textures\keyboard_glyphs\p_tab
  161    2011892        280 other textures\keyboard_glyphs\p_tilde
  162    2012211        272 other textures\keyboard_glyphs\p_u
  163    2012522        282 other textures\keyboard_glyphs\p_v
  164    2012843        276 other textures\keyboard_glyphs\p_w
  165    2013161        340 other textures\keyboard_glyphs\p_wasd
  166    2013540        288 other textures\keyboard_glyphs\p_x
  167    2013867        282 other textures\keyboard_glyphs\p_y
  168    2014188        284 other textures\keyboard_glyphs\p_z
  169    2014514        358 other textures\keyboard_glyphs\p_zqsd
  170    2014914        254 other textures\speech_bubble\b_prompt
  171    2015216        238 other textures\speech_bubble\speechbubblene
  172    2015502        234 other textures\speech_bubble\speechbubblenw
  173    2015784        212 other textures\speech_bubble\speechbubblese
  174    2016044        238 other textures\speech_bubble\speechbubblesw
  175    2016332        230 other textures\speech_bubble\speechbubbletail
  176    2016598        324 other textures\thought_glyphs\b
  177    2016959        386 other textures\thought_glyphs\up
  178    2017380        420 other textures\black_hole\rips
  179    2017836        418 other textures\black_hole\stars
  180    2018289       3460 other textures\splash\polytron
  181    2021789       9470 other textures\splash\polytron_1440
  182    2031294       9124 other textures\splash\trapdoor
  183    2040452       5186 other textures\splash\trixels
  184    2045677       9132 other textures\splash\trixels_1440
  185    2054831     486056 fonts\chinese big
  186    2540911     242918 fonts\chinese small
  187    2783852     335020 fonts\japanese big
  188    3118897     161618 fonts\japanese small
  189    3280536     217780 fonts\korean big
  190    3498339     114350 fonts\korean small
  191    3612709       3860 fonts\latin big
  192    3616591       3866 fonts\latin small
  193    3620473       2572 fonts\zuish
  194    3623075    2326381 sounds\intro\fezlogodrone
  195    5949488      34247 sounds\intro\fezlogoglitch1
  196    5983767      34247 sounds\intro\fezlogoglitch2
  197    6018046      34247 sounds\intro\fezlogoglitch3
  198    6052321    1428457 sounds\intro\hyperspeed
  199    7480810     803029 sounds\intro\lensflaregleam
  200    8283865    1157729 sounds\intro\logozoom
  201    9441619    1411305 sounds\intro\pandown
  202   10852956     482837 sounds\intro\polytronjingle
  203   11335817     881177 sounds\intro\reboot
  204   12217020    1625505 sounds\intro\starzoom
  205   13842555     185953 sounds\intro\trixellogoin
  206   14028539     185953 sounds\intro\trixellogoout
  207   14214528    1197237 sounds\intro\zoomtofarawayplace
  208   15411794    1182929 sounds\intro\zoomtohouse
  209   16594746     103267 sounds\dot\comeout
  210   16698038      78067 sounds\dot\heylisten
  211   16776125     132405 sounds\dot\hide
  212   16908550     112717 sounds\dot\idle
  213   17021287     150125 sounds\dot\move
  214   17171432     604905 sounds\dot\talk

And finally we can also dump files from a FEZ game archive like this:

With this knowledge I wrote a small Python program that can list, unpack, pack and even read-only mount FEZ game archives. See fezpak on GitHub.

Next time I'll talk about a more complicated file format, which I reverse engineered in order to make a simple sprite swap game mod.

Comments

Popular posts from this blog

Save/download data generated in JavaScript

How to write a binary file format

Cross site HTTP Auth via JavaScript