Serge25 Posted October 16 Posted October 16 Help unpack archive .t000 from Collapse (2008). Here my 'Collapse-Textures-t000.zip' .bt file for "010 Editor" but it don't recognize sky.t000 archive 😞 sky.zip Collapse-Textures-t000.zip
Members zbirow Posted Wednesday at 09:09 PM Members Posted Wednesday at 09:09 PM 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. Â Â 1
Serge25 Posted Wednesday at 09:56 PM Author Posted Wednesday at 09:56 PM Thank you very much! But the textures are monochrome...
Members zbirow Posted Thursday at 01:37 PM Members Posted Thursday at 01:37 PM 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.
Serge25 Posted Thursday at 01:54 PM Author Posted Thursday at 01:54 PM 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.
Serge25 Posted Thursday at 05:23 PM Author Posted Thursday at 05:23 PM Yeah. Now the size of the unpacked raw files matches, but they don't have headers; it's a data set.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now