TOMBA2.IMG: Difference between revisions
(Created page with "Compressed image files for Tomba 2") |
No edit summary |
||
Line 1: | Line 1: | ||
Compressed | 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