Registration Code (Part 1): w%kQ6
Registration Code (Part 2): b<#$1[*(cw~
In order to register on this forum, you must use the codes above. Combine them into one code (copy paste).

Painkiller: Black Edition - PAK File Format

Releases and other info for the Painkiller game series.
Post Reply
User avatar
atom0s
Site Admin
Posts: 391
Joined: Sun Jan 04, 2015 11:23 pm
Location: 127.0.0.1
Contact:

Painkiller: Black Edition - PAK File Format

Post by atom0s » Sun Jun 14, 2015 4:41 pm

Here is the information regarding the file format used for the Painkiller .PAK files.

PAK files are opened/parsed via:
  1. char __thiscall GPack::OpenPack(GPack *this, const char *a2, const char *a3)
  2. {
  3.   GPack *v3; // edi@1
  4.   FILE *v4; // eax@1
  5.   char result; // al@2
  6.   FILE *v6; // ST3C_4@3
  7.   void *v7; // eax@5
  8.   void *v8; // esi@6
  9.   char v9; // cl@8
  10.   int v10; // eax@8
  11.   char v11; // cl@9
  12.   GFileManager *v12; // ecx@10
  13.   char v13; // [sp+Fh] [bp-115h]@3
  14.   int i; // [sp+10h] [bp-114h]@4
  15.   size_t v15; // [sp+14h] [bp-110h]@8
  16.   int v16; // [sp+18h] [bp-10Ch]@3
  17.   __int32 v17; // [sp+1Ch] [bp-108h]@3
  18.   char v18[260]; // [sp+20h] [bp-104h]@9
  19.  
  20.   v3 = this;
  21.   GPack::Release(this);
  22.   strcpy((char *)v3 + 273, a2);
  23.   strcpy((char *)v3 + 13, a3);
  24.   v4 = fopen((const char *)v3 + 273, "rb");
  25.   *((_DWORD *)v3 + 2) = v4;
  26.   if ( v4 )
  27.   {
  28.     v13 = 0;
  29.     fread(&v13, 1u, 1u, v4);
  30.     v6 = (FILE *)*((_DWORD *)v3 + 2);
  31.     *((_BYTE *)v3 + 12) = v13 != 0;
  32.     v17 = 0;
  33.     fread(&v17, 1u, 4u, v6);
  34.     fseek(*((FILE **)v3 + 2), v17, 0);
  35.     fread(&v16, 1u, 4u, *((FILE **)v3 + 2));
  36.     if ( v16 )
  37.     {
  38.       for ( i = 0; i < v16; ++i )
  39.       {
  40.         v7 = operator new(0x120u);
  41.         if ( v7 )
  42.         {
  43.           *((_BYTE *)v7 + 4) = 0;
  44.           *((_DWORD *)v7 + 69) = 0;
  45.           *((_DWORD *)v7 + 68) = 0;
  46.           *((_DWORD *)v7 + 67) = 0;
  47.           *((_DWORD *)v7 + 66) = 0;
  48.           *((_DWORD *)v7 + 65) = 0;
  49.           *(_DWORD *)v7 = 0;
  50.           *((_DWORD *)v7 + 70) = 0;
  51.           *((_DWORD *)v7 + 71) = 0;
  52.           v8 = v7;
  53.         }
  54.         else
  55.         {
  56.           v8 = 0;
  57.         }
  58.         *(_DWORD *)v8 = v3;
  59.         *((_DWORD *)v8 + 69) = 0;
  60.         fread(&v15, 1u, 4u, *((FILE **)v3 + 2));
  61.         fread((char *)v8 + 4, 1u, v15, *((FILE **)v3 + 2));
  62.         v9 = i;
  63.         *((_BYTE *)v8 + v15 + 4) = 0;
  64.         sub_101810C0((int)((char *)v8 + 4), v15, v9);
  65.         fread((char *)v8 + 272, 1u, 4u, *((FILE **)v3 + 2));
  66.         fread((char *)v8 + 264, 1u, 4u, *((FILE **)v3 + 2));
  67.         fread((char *)v8 + 268, 1u, 4u, *((FILE **)v3 + 2));
  68.         *((_DWORD *)v8 + 65) = i;
  69.         v10 = (int)((char *)v8 + 4);
  70.         do
  71.         {
  72.           v11 = *(_BYTE *)v10;
  73.           *(_BYTE *)(v18 - ((_BYTE *)v8 + 4) + v10) = *(_BYTE *)v10;
  74.           ++v10;
  75.         }
  76.         while ( v11 );
  77.         _strlwr(v18);
  78.         v12 = (GFileManager *)*((_DWORD *)v3 + 1);
  79.         if ( v12 )
  80.           GFileManager::RegisterFile(v12, v18, (struct FIdx *)v8);
  81.       }
  82.     }
  83.     result = 1;
  84.   }
  85.   else
  86.   {
  87.     result = 0;
  88.   }
  89.   return result;
  90. }
When the file is first opened, the main 'header' information is read:
  1. v13 = 0;
  2. fread(&v13, 1u, 1u, v4); // Read the packed status. (If non-zero this means the files are compressed.)
  3. v6 = (FILE *)*((_DWORD *)v3 + 2);
  4. *((_BYTE *)v3 + 12) = v13 != 0;
  5. v17 = 0;
  6. fread(&v17, 1u, 4u, v6); // Read the file data offset.
  7. fseek(*((FILE **)v3 + 2), v17, 0); // Set the file pointer to the data offset.
  8. fread(&v16, 1u, 4u, *((FILE **)v3 + 2)); // Read the total number of files.
After the total number of files starts the file list. This list is setup like this:
  1. struct FileEntry
  2. {
  3.     unsigned int NameLength;
  4.     unsigned char Name[NameLength];
  5.     unsigned int FileOffset;
  6.     unsigned int FileSize;
  7.     unsigned int FileSizeCompressed;
  8. };
Each file entry follows the above format until the number of files is met.
If the file size is set to 0, then the file is considered a directory.

Names are encoded in a custom format as follows:
  1. int __cdecl sub_101810C0(int a1, signed int a2, char a3)
  2. {
  3.   signed int v3; // ebx@1
  4.   signed int v4; // ecx@1
  5.   char v5; // al@3
  6.   int result; // eax@4
  7.   int v7; // [sp+Ch] [bp+8h]@2
  8.  
  9.   v3 = a2;
  10.   v4 = 0;
  11.   if ( a2 <= 0 )
  12.   {
  13.     result = a1;
  14.   }
  15.   else
  16.   {
  17.     v7 = a2 % 5;
  18.     do
  19.     {
  20.       v5 = v7 + 2 * (v4++ + v3);
  21.       *(_BYTE *)(v4 + a1 - 1) ^= a3 + v5;
  22.     }
  23.     while ( v4 < v3 );
  24.     result = a1;
  25.   }
  26.   return result;
  27. }
Derp~
Need a great web host? Check out: AnHonestHost.com


Donations can be made via Paypal:
https://www.paypal.com/cgi-bin/webscr?c ... Q2GRT6KUJN
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest