Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.
Zero Tolerance for Disrespect

Harry Potter and the Deathly Hallows Parts 1-2 .bin.gz [PC]

Featured Replies

  • Localization

The assets in both Harry Potter and the Deathly Hallows games on PC are stored in .bin.gz files, which are headerless and compressed with ZLIB. After decompressing, I can see the following structure in the first part of each file (this is in ImHex Pattern Language). I can also see DDS files in the archive whose sizes correspond with an entry in the chunk table. I am not sure what the significance of the 44-byte sections are (they are often either padded or slightly larger than that) or where exactly the data starts yet. I'm guessing the first two ints in each chunk entry point to paths/labels in the table at the start, and that the chunks are aligned/padded somehow.

struct LabelEntry {
    u32 id;
    char path[];
};

struct ChunkEntry {
    u32 id;
    s32;
    u32 size;
    s32 unknown[7];
};

struct Section1 {
    u32 int_count;
    u32 ints[int_count];
    
    u32 chunk_count;
    ChunkEntry chunk_entries[chunk_count];
};

struct File {
    u32 next_section_off;
    u32 entry_count;
    LabelEntry header_labels[entry_count];
    Section1 section_1 @ next_section_off + 4;
};

File file_at_0x00 @ 0x00;

Here are a few sample files: https://drive.google.com/drive/folders/1OGEMB1gwo8Lz2Sjiqb8kI8j_FCyEe2pp?usp=sharing

  • Localization

OK, took a while, it was quite unusual, but I think I have it (it seems to work for the 4 files you posted)...

 

+--------------------------------------------------------+
| Harry Potter and the Deathly Hallows - Part 1 *.bin.gz |
+--------------------------------------------------------+

// The whole archive is compressed using ZLib

// ARCHIVE
  X - Compressed Archive
  
  // when uncompressed {
  
    // HEADER
      4 - Filename Directory Length (including padding)
      
    // NAMES DIRECTORY
      4 - Number of Names
      
      // for each name
        4 - Name ID
        X - Name
        1 - null Name Terminator
        
      0-3 - null Padding to a multiple of 4 bytes
        
    // PROPERTIES DIRECTORY
      4 - Number of Properties
      
      // for each property
        4 - Property Name ID
        
    // DETAILS DIRECTORY
      4 - Number of Files
      
      // for each file (40 bytes per entry)
        4 - Name ID
        4 - Reference Name ID (eg TEX files point to a DDS image) (-1 = no reference image)
        4 - File Length
        4 - Flags (16/128)
        4 - File Header Length (can be 0)
        4 - External File? (0=file in archive, 1=file is external / not in archive)
        4 - Number of Entries in the Unknown Directory for this File
        12 - null
        
    // UNKNOWN DIRECTORY
      // for each entry
        4 - Unknown
        
    // FILE DATA
      // for each file
        X - File Header
        X - File Data
        X - null Padding to a multiple of 16 bytes
  
    }

 

The Names directory contains the names for the files, as well as for something I'm calling Properties, which just seemed to match up based on the text strings

The Properties directory just points to the names of the properties, so nothing special there

 

The Details directory has the information to be able to find where the first file starts, and whether it has any padding before it....

  • The size of the Unknown Directory is the total of the field "Number of Entries in the Unknown Directory for this File" for all the entries, x4 . If you calculate that out, you can find where the file data starts.
  • Some files have a File Header, which looks to be just blank padding. If there is a value in the File Header Length field, you need to skip that number of bytes first, then read the file data.
  • File Lengths are padded to a multiple of 16 bytes. This needs to be calculated from the length of the file, not the offset to the file data. Don't include the File Header length in the padding calculation, it's only padding of the File Data itself.
  • There is also a field that indicates whether the file is in the archive or not (I'm calling it the External File field). If the value = 1, the file isn't in the archive at all, so ignore the length entirely.

 

Some untidy code that might help understanding:

      // 4 - Filename Directory Length
      int filenameDirLength = fm.readInt();

      // 4 - Number of Names
      int numNames = fm.readInt();

      String[] names = new String[numNames];

      for (int i = 0; i < numNames; i++) {
        // 4 - Name ID
        fm.skip(4);

        // X - Name
        // 1 - null Name Terminator
        String name = fm.readNullString();
        names[i] = name;
      }

      fm.relativeSeek(filenameDirLength + 4); // should already be here, but just in case...

      // 4 - Number of Properties
      int numProperties = fm.readInt();

      // for each property
      //   4 - Property Name ID
      fm.skip(numProperties * 4);

      // 4 - Number Of Files
      int numFiles = fm.readInt();


      // Loop through directory

      int unknownEntryCount = 0;
      String[] filenames = new String[numFiles];
      int[] lengths = new int[numFiles];
      int[] headerLengths = new int[numFiles];
      int[] externalFiles = new int[numFiles];
      for (int i = 0; i < numFiles; i++) {

        // 4 - Name ID
        int nameID = fm.readInt();

        String filename = names[nameID];

        // 4 - Reference Name ID (eg TEX files point to a DDS image) (-1 = no reference image)
        int referenceNameID = fm.readInt();

        // 4 - File Length
        int length = fm.readInt();

        // 4 - Flags (16/128)
        int flags = fm.readInt();

        // 4 - File Header Length (can be 0)
        int headerLength = fm.readInt();

        // 4 - External File? (0=file in archive, 1=file is external / not in archive)
        int externalFile = fm.readInt();

        // 4 - Number of Entries in the Unknown Directory for this File
        int unknownEntries = fm.readInt();

        unknownEntryCount += unknownEntries;

        // 12 - null
        fm.skip(12);

        filenames[i] = filename;
        lengths[i] = length;
        headerLengths[i] = headerLength;
        externalFiles[i] = externalFile;

      }

      // UNKNOWN DIRECTORY
      //   for each entry
      //     4 - Unknown
      fm.skip(unknownEntryCount * 4);

      long offset = fm.getOffset();
      for (int i = 0; i < numFiles; i++) {
        String filename = filenames[i];

        if (externalFiles[i] == 1) {
          // an external file, not in this archive
          int length = 0;

          //path,name,offset,length,decompLength,exporter
          resources[i] = new Resource(path, filename, offset, length);
        }
        else {
          // a file in this archive

          int length = lengths[i];

          int headerLength = headerLengths[i];
          offset += headerLength;

          //path,name,offset,length,decompLength,exporter
          resources[i] = new Resource(path, filename, offset, length);

          offset += length;
          offset += calculatePadding(length, 16);
        }
      }

 

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.