Jump to content

Recommended Posts

  • Members
Posted
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

  • Like 1
  • Members
Posted
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.

Posted

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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...