Pizza Tycoon 2 / Fast Food Tycoon 2

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
m00k00
Posts: 2
Joined: Sat Sep 09, 2017 5:42 pm

Pizza Tycoon 2 / Fast Food Tycoon 2

Post by m00k00 »

Hello everybody.

I figured that this is most likely the best place on the web to ask for help in this regard. The Game is loading all it's data from .PAK files. It seems like there is no encryption or packing involved. I could really need a helping Hand in understanding how those files are build and to possibly be able to extract their contents and maybe even pack together new .PAK files. So if anyone if you have a little time to share and wants to help me out I would really appreciate that.

The smallest sample I can provide is actually an Update File (230kb in size), but i suspect it to be structured just like all the other .PAK files.

http://www46.zippyshare.com/v/4R2YDuzq/file.html

Thanks a lot in advance and happy hacking everyone :)
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by aluigi »

The table containing the information about the archived files is obfuscated with some sort of sequential sequence of bytes but with something missing.

I leave here a debugging script for who is interested in checking this stuff, no it's NOT an extraction script so do NOT use it:

Code: Select all

idstring "ResourceFile-ID-"
goto 0x60
get SIZE long
get DUMMY long
get DUMMY long  # obfuscated flag?
savepos OFFSET
encryption xor "\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9"
log MEMORY_FILE OFFSET SIZE
encryption "" ""
get FILES long MEMORY_FILE
for i = 0 < FILES
    get DUMMY1 long MEMORY_FILE
    get DUMMY2 long MEMORY_FILE
    get DUMMY3 long MEMORY_FILE
    get DUMMY4 long MEMORY_FILE
    get DUMMY5 long MEMORY_FILE
next i
m00k00
Posts: 2
Joined: Sat Sep 09, 2017 5:42 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by m00k00 »

Thanks for investing your time, aluigi, it's really appreciated!
Is there something I could do to assist further with this thingy? For example providing more files?

Just let me know, please. Thanks again for your efforts! :)
aluigi
Site Admin
Posts: 12984
Joined: Wed Jul 30, 2014 9:32 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by aluigi »

Without the table containing the information of the archived files you can only "rip" the known file formats using a file ripper:
viewtopic.php?f=17&t=712
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

Hello! Nice to see some people are still interested in this old classic. IMO it's so much better than the new Pizza Connection 3. I have spent many hours playing it and quite some time now digging into the game files as well. I want to share some of my findings (and also ask some questions!):

PAK files are quite easy to extract when you take a look at what is happening with them in the exe file. And all of them are packed and obfuscated the same way as that update file m00k00 shared. Basically a header with filenames and lengths starts at byte 108 and has a length defined at byte 96. It can be deobfuscated with a simple XOR operation (I attached a CPP script with the algorithm). When deobfuscated, it contains this object:

Code: Select all

  size_t numberOfFiles;
  file files[];

Where file object looks like this:

Code: Select all

  FILETIME filetime;
  DWORD fileLength;
  DWORD offsetFromBeginningOfHeader;
  CHAR *filename;

First 8 bytes is still a bit of a mystery to me, but it definitely looks like a FILETIME object containing the date that file was created. They all point to some date in the year 2000, which makes a lot of sense. Filenames are stored after the files array with filename field initially containing offset to the corresponding string from the end of files array. Anyway, it should be clear how this works from the CPP file.

Enjoy!
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

Alright, so extracting resources from PAK was the easy part (in reality not easy at all, I spent a lot of time trying to understand ASM and generated C pseudo-code from HexRays to get it to work). But after extracting all the game files I was both excited (because they contained some cool stuff in simple TGA file format) and prepared for more suffering when I realized most of the game files are in (probably) custom SPR format. I continued investigating disassembled code for loading these sprites into memory, but this time it looks a bit more complicated.
I started with a very simple sprite - head_m_00.dat (normally it would have spr extension, but I changed that to be able to upload it here). It's one of the first files the game is loading, so it was convenient for me to debug it.
From the disassembled code I realized it contains 3 parts:

* Header consisting of the first 33 bytes. First 4 bytes and bytes 25-28 contain (for some reason) the same number (in this case it's 3000), which is length of the next part. Byte 5 is some kind of sprite type indicator (probably there are more different formats than this one). Why it has a value of 8 for this texture? I don't know, but perhaps that's an indicator it's using 8 bit RGB colours?
* First part which looks like a table with references to colour palette. Its length in this case is defined in header (3000), but what are the dimensions of the actual texture? Could it be bytes 19-20 and 21-22? I think it's very likely. In this file they are equal to 0x3C and 0x32, which is 60x50. Exactly 3000 pixels! Also I noticed there are a lot of "07" values in this block. Could they correspond to empty pixels?
* Second part Distinctively in HEX editor looking part of 768 bytes, starting at byte 3033 (header lenght + first part length) and ending at the end of the file. Its length (768 bytes) is hard-coded in the exe file. Looks like a 8 bit RGB colour palette to me? In the disassembled code I can see it's processing 3 consecutive elements from this part in each iteration, which makes sense if it is a colour palette.

Maybe anyone has some ideas about this file? I would be really grateful!
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

Yes! I think I was right with my assumptions in the previous post. At least some of them. I tried visualizing head_m_XX.spr files using dimensions of 60x50 and that color palette I found in the last 768 bytes of the file. Here is a result and corresponding image in the game:

Image

The one that I marked definitely matches that head shape from the game. Others from my form probably also do match other options I can pick, but they are not as distinctive. This one is obvious. Lots of 0x07 values in the texture probably just correspond to the background color (which takes a significant part of the image). However, the colors are not right at all. But I am not really concerned with that for now. Probably it can be fixed by doing some manipulations on the palette (or I just messed something up reading the palette from the file).

What bothers me is that these head_X_XX.spr are pretty much the only ones that can be constructed using this simple algorithm. I tried other SPR files and they seem to use some kind of compression for the block containing palette references. Take for example the one that I attached, called litfass01_0.spr:
The first four bytes indicate that block with palette references is 821 bytes in length. Cool. I jump to that position calculating from the end of the header (which is always 33 bytes in size). I can see that the color palette really starts at this position. This texture is probably not using all the 256 colors, so its palette has several zero values.
But I have a problem trying to construct an actual image from these references... If my assumption is correct and bytes 19-20 and 21-22 from the header indicate image dimensions (which I think indeed is correct), it means this texture is 256x256 pixels in size. If it was uncompressed, like that head_m_XX.spr images, it would take whopping 65536 bytes! Yet it only takes 821 byte. So there must be some smart compression used in this block:

Code: Select all

FF 05 5E 47 47 47 47 B1 47 D1 47 47 FF 05 FF 03
5B D1 D1 47 B1 47 D1 47 B1 47 5B 47 5B 47 FF 03
FF 02 5B 53 59 53 B1 9C 9C 9C 47 47 B1 B1 5B 5B
5B D1 FF 02 FF 01 5B 5B 59 53 DF CF 53 B3 47 47
5B 47 5B D1 5B D1 5B B3 FF 01 FF 01 5B 53 53 CF
53 CF 53 53 EE 5B 5B 5B 5B 5B B1 5B B1 D1 FF 01
48 DE 5B 53 53 59 53 59 53 53 5B 53 5B EE 47 5B
D1 5B 42 45 B6 5B 53 53 53 CF CF 53 53 53 5C 5B
EE 5B 5B 5B 5B B1 8F 3A FF 01 4D EE 5A 53 59 53
53 5B 53 5B 53 5B 53 B1 5B 47 D2 35 FF 01 FF 01
4F D2 D1 58 F3 58 59 AF 53 53 53 CF 53 DE 47 8F
4A 34 FF 01 FF 02 4E 75 B3 EE 59 58 58 FD 58 CD
53 9C 4D 4E 49 33 FF 02 FF 02 BB 50 4D 42 47 D1
B1 47 47 42 75 4D 4E 49 35 2F FF 02 FF 02 BC 4E
49 90 50 8C 8C D2 48 48 4F 49 33 4B 29 23 FF 02
FF 02 79 9E 37 37 4D 61 79 7B 62 4E 43 4F 2C 50
28 2F FF 02 FF 02 7D 61 48 41 4D 92 64 7C 78 68
74 30 40 2E 44 34 FF 02 FF 02 6B 7D 61 B5 F0 F7
F7 D8 91 D0 57 52 3E 2A 2C 35 FF 02 FF 02 AC 80
63 BD A6 E5 F8 F5 E2 E0 57 57 58 47 2B 35 FF 02
FF 02 83 82 AB C4 A7 C3 F1 C0 B8 76 3F 52 3E 40
38 34 FF 02 FF 02 83 6D 6C DB ED E9 EA A4 E6 EF
89 57 3E 40 38 4C FF 02 FF 02 82 6C C7 C9 EB DB
DA DC E6 8D CE 57 1C 2A 38 29 FF 02 FF 02 82 7F
CA CB EB EA A6 A5 E6 8B 3F 3B 3E 40 38 35 FF 02
FF 02 65 7F C9 ED FE C2 C1 C3 D7 E0 DD 57 3D 2A
38 2F FF 02 FF 02 6F DC 98 FA FA FE E9 F2 D7 F4
DD 3B 3E 31 32 3A FF 02 FF 02 6D A9 96 EB FE F2
E8 E8 D5 E0 DD 3C 27 4F 2F 22 FF 02 FF 02 71 AA
C6 FA FA FE F2 F9 F6 E1 53 2C 29 1F 13 22 FF 02
FF 02 71 AD C6 ED FA FA FE FE D7 D4 4F 51 1B 14
15 15 FF 02 FF 02 9B 97 A7 EB FA FE FA FE D7 D4
39 20 16 14 13 1A FF 02 FF 02 6F CA C6 EB FA FE
FE FE E4 B7 39 19 16 1A 1A 25 FF 02 FF 02 66 CA
81 FA FA FE FA FE E7 D4 4F 20 1F 1F 21 1F FF 02
FF 02 9A 99 98 ED FA FA FA FA E4 B7 4F 18 1F 1A
25 1A FF 02 FF 02 73 71 CC FB FB FA FA FE E7 60
4F 19 14 14 1A 22 FF 02 FF 02 72 67 87 CB ED FA
FA FA A2 69 39 23 14 1A 1A 7E FF 02 FF 02 55 70
72 AE 85 C8 EC EB A0 B7 54 29 16 16 15 46 FF 02
FF 01 47 A1 55 56 73 86 84 6E CA BE 69 4F 1E 17
16 14 3A 32 FF 01 FF 01 47 5D 94 6A C5 AB A8 86
CA 7A B7 4F 26 1D 17 24 32 4E FF 01 FF 01 47 47
8E 93 95 BF D9 A5 C4 9F D6 4F 29 2D 36 2C 49 4D
B6 38 53 47 47 58 5A 77 E3 B9 BA D3 9D 5F 5E 5E
B3 B6 4E 75 A3 FF 01 75 DF B1 42 EE 74 FC B2 B2
8A B0 58 5B 4D B6 75 47 49 51 FF 01 4E 42 DF 59
DF 59 5B 47 5B 47 75 8C 5B DE 53 B3 4F 34 FF 01
FF 02 4D 42 5B DF 88 FD F3 58 DF 59 DF 53 9C 9E
B6 45 2F FF 01 FF 03 4D D2 47 D1 53 5B 53 D1 B1
75 B4 48 4F 45 FF 03 FF 05 4E D2 5E B4 5E 5E 4D
48 4F 44 FF 05

To me, it would make sense to just store it as [X, Y, PaletteElement] pairs for sparse textures containing lots of empty space. But is it the case with this one? No, I don't think so. At least I cannot see any pattern like that. Disassembled code also doesn't give any clues about this block. Looks like it just gets loaded into the memory like this and when I put a breakpoint on the memory block in Ida, it only gets triggered when the object is destroyed. Maybe it looks familiar to someone? I noticed that some values appear more often than others, for example 0xFF or 0x02. But does it mean anything? Any help is much appreciated. I attached the sprite I am talking about to this post.
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

I have spent a big part of my free time on this and there are some updates (mostly bad ones):

I decompiled parts of the code that deal with animation files (*.ani). Looks like there is just a variable number of animations packed into that *.ani file. For example, for people moving on the streets (say kids_m_sma.ani) there would be 8 animations that correspond to movement in each direction. For people's movement inside the restaurant (fe. kids_m_big.ani), there would be 8 animations for movement, 4 animations for sitting at the table, and 4 animations for eating. "Animation" itself is just a sequence of 24 sprites. Each of them of course in that stupid compressed SPR format seen in the previous post. So it's impossible to do anything with these animations without understanding the compression algorithm apart from changing movement speed. Which I did, but there is not much sense in that anyway).

And regarding the SPR files: I actually found some pieces of code that process the compressed part I showed in the previous post. But I have no clue what is going on there.
I found out it generates a new array of 512 bytes in size from the last 768 bytes in the SPR file (which I assumed to be a color palette) and uses it somewhere.
Then it iterates through the compressed part. "FF" values from the previous post are some kind of "separators". It would read that array from one "FF" byte to another, doing something with the byte which comes just after "FF" - either of 05/03/02/01 in this case. And then processing the rest of the values till the next "FF" value.

Here is another good example of 3 sprites with small size, same shape but different colors: m_kids.spr, m_teens.spr, m_studies.spr.
Corresponding in-game images for kids, teens and studies (sprites are probably without background):

Image

Both parts of SPR files are different when I compare them. But the first part is somewhat similar. It's only that "separator" that is different.
The game is using ddraw.dll and DirectX 7. Maybe it's some kind of format that this DDRAW thing renders automatically and I'm digging too deep missing something obvious?

To finish the post on a positive note, I managed to relatively easily decompile the code that sets and displays building attendance numbers. They are all hard-coded into an exe file, so binary patch is required. I did that and managed to make a train station "alive". This empty train station has always been super irritating and illogical to me. Now it's full of people!

Image

I am not sure how to share the details on what bytes need to be patched as the offsets probably depend on the game version. But if anyone is interested, just drop me your exe file and I can do it.

I also accidentally found the code which calculates income when pizza is served to the customer (from some hard-coded strings the developers have left for debugging purposes :lol: ). It revealed each served customer is counted as 100 pizzas sold. But what is more interesting, there are hidden tips percentage coefficients for each customer group. Which are added to each of these 100 sold pizzas. I find this both fascinating and illogical. Fascinating because it's not mentioned anywhere I had no clue something like this was happening while I was playing the game for the last 20 years. And illogical because tips is something that waiters get - and not the restaurant owner.

Oh, and I found a way to enable fps indication which was used by developers/testers to check performance back in a day. Cool, but kind of useless thing :P
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

A little bit more effort during the weekend and I think I approximately know how this compression works.
To begin with I decided to ignore colours completely and just try to understand how pixel positions are calculated. This turned out to be a brilliant idea.
For that 3 images from the previous post I found this simple algorithm works for image contours:

Code: Select all

* Read 2 bytes from the first block (which starts at byte 33)

* If first of these bytes is not zero - move X offset by amount defined in the second of the read bytes.
* If first of these bytes is zero - pixel has a colour at current X offset. Mark it and move X offset by 1.

* If current X offset is equal to image width, it means that row is filled. Reset X offset to 0 and increase Y offset by 1.

* Repeat same steps till all rows are filled (Y offset is equal to image height).

Real image dimensions are defined in bytes 8-9 (width which is equal to 12) and in bytes 10-11 (height which is equal to 16).
In Hex Rays this algorithm was barely understandable with 3 while loops and tons of strange bit shift operations. While in reality, it's pretty easy. But I doubt it would work for any sprite in the game as there are 5 or 6 more similar pieces of code in the same method with minor differences.

Nonetheless, running this algorithm on the images from previous post gave me this result:
Image

Which is indeed correct. But there is so much more to find out.. First of all how the colours are constructed. That part looks really complicated in Hex Rays.
picusiaubas
Posts: 6
Joined: Wed Oct 06, 2021 9:01 pm

Re: Pizza Tycoon 2 / Fast Food Tycoon 2

Post by picusiaubas »

OMG! Sometimes you just have to drop decompiled code and trust your intuition..
Today I was trying to understand something from the way Hex-Rays handles colors on this little sprite. With not much of luck. And then decided to try something very obvious.
I played around with the algorithm I posted in the previous post. An obvious thing to do was to replace the second step, which was

Code: Select all

* If first of these bytes is zero - pixel has a color at current X offset. Mark it and move X offset by 1.

with this:

Code: Select all

* If first of these bytes is zero - pixel has a color. Get second of the read bytes and multiply it by 3. That's the offset in the color palette (which is the last 768 bytes of the SPR file).
RGB value of the color is defined in the next 3 color palette bytes. Mark it at current X offset and move X offset by 1.

And it worked like a magic! Here is the result:
Image
Sure, the colors are slightly off. But they do make sense. And that gives me lots of optimism and motivation to keep digging into this.

picusiaubas wrote:I found out it generates a new array of 512 bytes in size from the last 768 bytes in the SPR file (which I assumed to be a color palette) and uses it somewhere.

BTW, I'm absolutely sure this part is what is happening in the game code. It is generating a 512 bytes color palette from the 768 bytes palette. Yet I was almost able to get it to work with 768 bytes color palette. My only explanation for this is that it is reducing bit depth on purpose to increase performance. Well, maybe it made sense back in a day..