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

Collapse (2008) t000 unpack

Featured Replies

  • Localization
import struct
import os
import sys
from PIL import Image

# Constants for offsets
FILE_COUNT_OFFSET = 0x28
FILENAME_TABLE_OFFSET = 0x2C
DATA_INDEX_OFFSET = 0x178C

# A SINGLE, CORRECT LOOKUP TABLE FOR IMAGE DIMENSIONS BASED ON FILE SIZE.
# All files are grayscale (1 byte/pixel).
SIZE_TO_DIMENSIONS = {
    262144:   (512, 512),
    524288:   (1024, 512),
    1048576:  (1024, 1024)
    # Add more entries here if new sizes appear
}

def extract_grayscale_images(file_path, output_dir):
    """
    Extracts all image files from a given .dat archive, assuming they are
    raw grayscale pixel data.
    """
    with open(file_path, 'rb') as f:
        # Read the total number of files in the archive
        f.seek(FILE_COUNT_OFFSET)
        file_count = struct.unpack('<I', f.read(4))[0]
        
        # Read the filename table
        f.seek(FILENAME_TABLE_OFFSET)
        filenames = []
        for _ in range(file_count):
            try:
                name_len = struct.unpack('<I', f.read(4))[0]
                filenames.append(f.read(name_len).decode('ascii', errors='replace'))
            except (struct.error, IndexError):
                # Stop if the file ends unexpectedly
                break

        # Move to the data index table
        f.seek(DATA_INDEX_OFFSET)
        print(f"Starting extraction of {len(filenames)} files (all as grayscale)...\n")
        
        success_count, skipped_count = 0, 0
        for i, filename in enumerate(filenames):
            try:
                # Read the offset and size for the current file
                entry_data = f.read(8)
                if len(entry_data) < 8:
                    break # Reached end of index
                
                offset, size = struct.unpack('<II', entry_data)
                
                # Check if we know the dimensions for this file size
                if size not in SIZE_TO_DIMENSIONS:
                    print(f"[{i+1}/{file_count}] {filename} -> UNKNOWN SIZE ({size} B). Skipping.")
                    skipped_count += 1
                    continue

                width, height = SIZE_TO_DIMENSIONS[size]
                
                # Read the pixel data
                current_pos = f.tell() # Save current position in the index
                f.seek(offset)
                pixel_data = f.read(size)
                f.seek(current_pos) # Return to the index

                # Prepare the output path, preserving directory structure
                safe_name = filename.replace('.dds', '.png').replace('\\', os.path.sep).strip(os.path.sep)
                out_path = os.path.join(output_dir, safe_name)
                os.makedirs(os.path.dirname(out_path), exist_ok=True)
                
                # ALWAYS convert as 'L' (grayscale)
                img = Image.frombytes('L', (width, height), pixel_data)
                img.save(out_path, 'PNG')
                
                print(f"[{i+1}/{file_count}] {filename} -> {out_path} ({width}x{height}, Grayscale)")
                success_count += 1

            except Exception as e:
                print(f"[{i+1}/{file_count}] {filename} -> CRITICAL ERROR: {e}")
                skipped_count += 1

    print(f"\nFinished! Converted: {success_count}, Skipped: {skipped_count}.")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: python {os.path.basename(__file__)} <file.dat>")
        sys.exit(1)
        
    input_file = sys.argv[1]
    # Create a more standard output directory name
    output_dir = os.path.splitext(input_file)[0] + "_extracted_images"
    
    print(f"Input file: {input_file}")
    print(f"Output directory: {output_dir}\n")
    
    extract_grayscale_images(input_file, output_dir)

This script unpack texture.
ย 

The file names are given with the .dds extension, but these are not DDS files. There are two types of files: those with [e] in the name build correctly, and those without [e] are strange. That's all I can help with.

Script usage: python <scriptname>.py <path to file>
e.g., python unpack.py sky.t000 - if it's in the same directory as the script.
ย 

ย 

box2[e].png

  • Localization
15 hours ago, Serge25 said:

Thank you very much! But the textures are monochrome...

Yes, they're monochromatic. This is the original texture. There's probably a color palette under the name index, but I don't know anything about it.

  • Author

The size of the files in the folder I unpacked using your script is about three times smaller than the archive itself.
I think this indicates that only one channel (e.g., Red) has been unpacked.

  • Author

Yeah. Now the size of the unpacked raw files matches, but they don't have headers; it's a data set.

  • 3 weeks later...
  • Localization

Hello, everyone. Is it possible to export and import textures to archives? I really want to replace the textures of the environment and materials with high-quality ones.

Edited by InKviZ

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.