TOMBA2.IMG: Difference between revisions

From Tomba! Wiki
Jump to navigation Jump to search
(Created page with "Compressed image files for Tomba 2")
 
No edit summary
Line 1: Line 1:
Compressed image files for Tomba 2
In ''Tomba! 2: The Evil Swine Return'', the '''IMG''' file format is used to store '''compressed VRAM (Video RAM) data''' used for textures and graphical elements of the game. This format is specific to the game and plays an essential role in managing the game's graphical assets, such as background images, UI elements, and textures for in-game models.
 
The IMG file contains multiple chunks of compressed graphical data, each representing a set of textures that are used in different parts of the game world. These chunks are organized in a way that they can be loaded into the PlayStation's VRAM during gameplay.
----
 
=== '''Structure of the IMG File''' ===
While the specific structure of the IMG file may vary based on the game's content, the general layout is as follows:
 
* '''Headers''': The IMG file contains headers that provide information about the data stored in the file. These headers define how many texture chunks there are, their locations, and the sizes of each chunk.
* '''Compressed Data''': The textures within the IMG file are compressed to save space and improve performance. The compression format is proprietary to the game and requires special handling to extract and decompress.
* '''Shards''': The IMG file may store textures in a shard format, where a texture is broken down into smaller "shards" for more efficient processing or loading into the PlayStation's VRAM.
 
== unIMG.py ==
The IMG file can be extracted using unIMG.py script, which can be found [https://drive.google.com/file/d/10eUAb-TFogzYsKyHgHZJzY4y5rBcuXKd/view?usp=drive_link here]. Here is how it works:
 
== '''Overview''' ==
The <code>unIMG.py</code> script is used to extract and convert compressed VRAM (Video RAM) data from ''Tomba! 2: The Evil Swine Return''’s <code>.IMG</code> file format. It breaks down the <code>.IMG</code> file, extracts the compressed VRAM data into separate chunks, and converts those chunks into individual texture "shards" which are then recombined into usable VRAM pages. The script processes the VRAM data to extract the textures into <code>.cvram</code>, <code>.shard</code>, and <code>.vram</code> files for further use, such as modding or archival purposes.
----
 
== '''Prerequisites''' ==
 
* Python installed on your system.
* A copy of the ''Tomba! 2'' <code>.IMG</code> file.
* Basic understanding of binary data structures, particularly video memory management.
 
----
 
== '''Output Structure''' ==
The script creates the following directory structure for the extracted and converted VRAM data:
outputfolder/
├── cvrams/
│  ├── 0-0000.cvram
│  └── ...
├── shards/
│  ├── 0-0000-0.shard
│  ├── 0-0000-1.shard
│  └── ...
└── vrams/
    ├── 0-0000.vram
    └── ...
 
=== '''1. Directory Setup''' ===
The script begins by ensuring that the required directories for output files are created:
def dir(name):
     mypath = name
     if not os.path.isdir(mypath):
         os.makedirs(mypath)
This ensures that the directories for storing extracted VRAM, shards, and compressed VRAM files exist before proceeding with the extraction.
----
 
=== '''2. File Setup and Initialization''' ===
The script opens the <code>.IMG</code> file (in this case, <code>TOMBA2.IMG</code>) and seeks to the end to determine the size of the file. The file pointer is then reset to the beginning:
with open(name, "rb") as rom:
     rom.seek(0, 2)  # Seek to the end to get file size
     imgsize = rom.tell()
     rom.seek(0)  # Reset to the start of the file
The script initializes several variables:
 
* '''vc''': A counter for VRAM sections.
* '''filelist''': A list to store the output file paths for each extracted VRAM chunk.
 
=== '''3. Extracting VRAM Data''' ===
The script processes the <code>.IMG</code> file in chunks, extracting the compressed VRAM data based on header information. It reads the number of VRAM headers and their sizes to determine how much data to extract:
header_amount = struct.unpack("<I", rom.read(4))[0]
header_size = header_amount * 0xC + 4  # Header size calculation
For each VRAM chunk, the script reads a header containing the VRAM's dimensions and data size, then stores the data in <code>.cvram</code> files. The extracted VRAM is written to the directory <code>./cvrams/</code>:
with open("./cvrams/" + outpath + ".cvram", "wb") as out:
    out.write(rom.read(0x800 + total))
 
=== '''4. Converting Compressed VRAM to Shards''' ===
Once all the VRAM chunks are extracted, the script moves on to converting them into individual texture "shards". It reads the <code>.cvram</code> files, processes the headers to determine the positions of the texture data, and extracts the texture data into smaller shard files:
with open("./cvrams/" + filelist[filename] + ".cvram", "rb") as cvram:
     c_header_amount = struct.unpack("<I", cvram.read(4))[0]
     c_header_size = c_header_amount * 0xC + 4
     skip = 0x800 - c_header_size  # Padding to align the header
Each shard is then written to a separate file, saved in the <code>./shards/</code> directory:
with open("./shards/" + filelist[filename] + "-{:d}.shard".format(i), "wb+") as shard:
     shard.write(cvram.read(amount))  # Write texture data to shard
The script handles the special data format of each shard, including the decompression logic using offsets and extra data handling (e.g., negative offsets):
if extra != 0:
     for i in range(amount):
         shard.seek(extras[extra], 2)  # Seek with the specific offset
         b = shard.read(1)  # Read the byte at the offset
         shard.seek(0, 2)  # Seek back to the end of the file
         shard.write(b)  # Write the byte to the shard
 
=== '''5. Reconstructing Full VRAM Pages''' ===
After all shards are created, the script reconstructs complete VRAM pages by combining the shards back into their appropriate locations within a VRAM page. It writes the full VRAM data into <code>.vram</code> files stored in the <code>./vrams/</code> directory:
with open("./vrams/" + filelist[i] + ".vram", "w+b") as vram:
     vram.seek(0x100000 - 1)
     vram.write(b"\0")  # Initialize VRAM with zero padding
The script then places each shard in the correct position within the VRAM page based on the coordinates (x, y), width (w), and height (h) stored in the shard headers:
vram.seek((x * 2) + (y * 0x800))  # Seek to the correct VRAM position
for k in range(h):
     vram.write(inshard.read(w * 2))  # Write shard data to VRAM
     vram.read(0x800 - w * 2)  # Read padding between rows

Revision as of 15:52, 10 January 2025

In Tomba! 2: The Evil Swine Return, the IMG file format is used to store compressed VRAM (Video RAM) data used for textures and graphical elements of the game. This format is specific to the game and plays an essential role in managing the game's graphical assets, such as background images, UI elements, and textures for in-game models.

The IMG file contains multiple chunks of compressed graphical data, each representing a set of textures that are used in different parts of the game world. These chunks are organized in a way that they can be loaded into the PlayStation's VRAM during gameplay.


Structure of the IMG File

While the specific structure of the IMG file may vary based on the game's content, the general layout is as follows:

  • Headers: The IMG file contains headers that provide information about the data stored in the file. These headers define how many texture chunks there are, their locations, and the sizes of each chunk.
  • Compressed Data: The textures within the IMG file are compressed to save space and improve performance. The compression format is proprietary to the game and requires special handling to extract and decompress.
  • Shards: The IMG file may store textures in a shard format, where a texture is broken down into smaller "shards" for more efficient processing or loading into the PlayStation's VRAM.

unIMG.py

The IMG file can be extracted using unIMG.py script, which can be found here. Here is how it works:

Overview

The unIMG.py script is used to extract and convert compressed VRAM (Video RAM) data from Tomba! 2: The Evil Swine Return’s .IMG file format. It breaks down the .IMG file, extracts the compressed VRAM data into separate chunks, and converts those chunks into individual texture "shards" which are then recombined into usable VRAM pages. The script processes the VRAM data to extract the textures into .cvram, .shard, and .vram files for further use, such as modding or archival purposes.


Prerequisites

  • Python installed on your system.
  • A copy of the Tomba! 2 .IMG file.
  • Basic understanding of binary data structures, particularly video memory management.

Output Structure

The script creates the following directory structure for the extracted and converted VRAM data:

outputfolder/ 
├── cvrams/ 
│   ├── 0-0000.cvram 
│   └── ... 
├── shards/ 
│   ├── 0-0000-0.shard 
│   ├── 0-0000-1.shard 
│   └── ... 
└── vrams/
    ├── 0-0000.vram
    └── ...

1. Directory Setup

The script begins by ensuring that the required directories for output files are created:

def dir(name):
    mypath = name
    if not os.path.isdir(mypath):
        os.makedirs(mypath)

This ensures that the directories for storing extracted VRAM, shards, and compressed VRAM files exist before proceeding with the extraction.


2. File Setup and Initialization

The script opens the .IMG file (in this case, TOMBA2.IMG) and seeks to the end to determine the size of the file. The file pointer is then reset to the beginning:

with open(name, "rb") as rom:
    rom.seek(0, 2)  # Seek to the end to get file size
    imgsize = rom.tell()
    rom.seek(0)  # Reset to the start of the file

The script initializes several variables:

  • vc: A counter for VRAM sections.
  • filelist: A list to store the output file paths for each extracted VRAM chunk.

3. Extracting VRAM Data

The script processes the .IMG file in chunks, extracting the compressed VRAM data based on header information. It reads the number of VRAM headers and their sizes to determine how much data to extract:

header_amount = struct.unpack("<I", rom.read(4))[0]
header_size = header_amount * 0xC + 4  # Header size calculation

For each VRAM chunk, the script reads a header containing the VRAM's dimensions and data size, then stores the data in .cvram files. The extracted VRAM is written to the directory ./cvrams/:

with open("./cvrams/" + outpath + ".cvram", "wb") as out:
    out.write(rom.read(0x800 + total))

4. Converting Compressed VRAM to Shards

Once all the VRAM chunks are extracted, the script moves on to converting them into individual texture "shards". It reads the .cvram files, processes the headers to determine the positions of the texture data, and extracts the texture data into smaller shard files:

with open("./cvrams/" + filelist[filename] + ".cvram", "rb") as cvram:
    c_header_amount = struct.unpack("<I", cvram.read(4))[0]
    c_header_size = c_header_amount * 0xC + 4
    skip = 0x800 - c_header_size  # Padding to align the header

Each shard is then written to a separate file, saved in the ./shards/ directory:

with open("./shards/" + filelist[filename] + "-{:d}.shard".format(i), "wb+") as shard:
    shard.write(cvram.read(amount))  # Write texture data to shard

The script handles the special data format of each shard, including the decompression logic using offsets and extra data handling (e.g., negative offsets):

if extra != 0:
    for i in range(amount):
        shard.seek(extras[extra], 2)  # Seek with the specific offset
        b = shard.read(1)  # Read the byte at the offset
        shard.seek(0, 2)  # Seek back to the end of the file
        shard.write(b)  # Write the byte to the shard

5. Reconstructing Full VRAM Pages

After all shards are created, the script reconstructs complete VRAM pages by combining the shards back into their appropriate locations within a VRAM page. It writes the full VRAM data into .vram files stored in the ./vrams/ directory:

with open("./vrams/" + filelist[i] + ".vram", "w+b") as vram:
    vram.seek(0x100000 - 1)
    vram.write(b"\0")  # Initialize VRAM with zero padding

The script then places each shard in the correct position within the VRAM page based on the coordinates (x, y), width (w), and height (h) stored in the shard headers:

vram.seek((x * 2) + (y * 0x800))  # Seek to the correct VRAM position
for k in range(h):
    vram.write(inshard.read(w * 2))  # Write shard data to VRAM
    vram.read(0x800 - w * 2)  # Read padding between rows