Skip to content

Instantly share code, notes, and snippets.

@modeco80
Last active April 25, 2022 17:42
Show Gist options
  • Save modeco80/6d09626ade1727e9d5dcbbc5b3810803 to your computer and use it in GitHub Desktop.
Save modeco80/6d09626ade1727e9d5dcbbc5b3810803 to your computer and use it in GitHub Desktop.
ImHex pattern for (mostly) understanding Namco Museum (PS1) .TIP (TIP0/TIP1) image data.
//
// ImHex pattern for (mostly) understanding Namco Museum
// (PS1) .TIP (TIP0/TIP1) image data.
//
// (C) 2022 modeco80 <lily.modeco80@protonmail.ch>
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// To use this pattern in ImHex:
//
// - Load a .TIP file into ImHex (okay, I hope you managed to figure this out on your own)
//
// - Load this pattern file into ImHex (... again, this seems a tiny bit obvious)
//
// - Use the console output (it does size fudging for you) and the Pattern Data
// window to figure out what you need (start of data/CLUT data, whether or not it's 8bpp, such.)
//
// - Use your favourite graphics analysis program (i.e: TiledGGD will work for this)
// to look at all images (be patient - I'm working on a dumper!)
#include <std/io.pat> // used for debug printing
#pragma endian little
// PS1 `RECT` structure, maybe?
struct TipRect {
u16 x;
u16 y;
u16 w;
u16 h;
} [[static, format("FormatRect")]];
// note that this is the raw rect with no fudging applied to it,
// use the console for the right resolution.
fn FormatRect(TipRect rect) {
return std::format("Pos: {}x{} Size: {}x{}", rect.x, rect.y, rect.w, rect.h);
};
fn FormatImageFlags(u8 flags) {
str total = "";
if(flags & 0x4)
total += "CLUT ";
else
if(flags & 0x1)
total += "8bpp";
else
total += "4bpp";
return total;
};
struct TipImage {
u32 length [[name("Image length")]];
u16 index [[name("Image index")]];
u8 flags [[name("Image flags"), format("FormatImageFlags")]];
// I don't know what this really is.
// However, the game code uses this.
// It may not be of importance to dumpers,
// and set when the game loads this TIP into memory
// (as it loads the WHOLE tip file. jeezums)
u8 unknown__twiddled_by_game [[name("Unknown")]];
// For some reason, the width needs to be fudged by multiplying by 4 (2 for 8bpp?).
// I don't really understand why, unless it's to get a bit more mileage out of a u16?
// If this were a rect of u8's and the game extended it to a u16, I'd get it.
// However, it's not. This should easily go as big as needed for PS1 VRAM.
TipRect rect [[name("VRAM rectangle")]];
// Test flags and print out some debug information
// (mostly for my annoyance/usage)
if(flags & 0x4) {
// std::print("[TipImage] Image {} is a CLUT for image {}", index, index - 1);
} else if(flags & 0x1) {
std::print("[TipImage] Image {} (size: {}) is 8bpp", index / 2, std::format("{}x{}", rect.w * 2, rect.h));
} else {
std::print("[TipImage] Image {} (size: {}) is 4bpp", index / 2, std::format("{}x{}", rect.w * 4, rect.h));
}
u8 data[length - 0x10] [[name("Data")]];
// The CLUT image for this texture.
TipImage clut [[name("CLUT image")]];
};
fn FormatNumImages(u32 NumImages) {
return std::format("{} (incl. CLUT: {})", NumImages/2, NumImages);
};
// The TIP file header.
struct TipFile {
u8 magic[4] [[hidden]]; // Either 'TIP0' or 'TIP1'.
u32 NumImages [[name("Image count"), format("FormatNumImages")]]; // The number of images stored in this file.
// Includes images marked with TipFlags::palette.
if(magic[0] == 'T' && magic[1] == 'I' && magic[2] == 'P') {
// There are only 2 known versions: TIP0 and TIP1.
//
// The format is byte identical, so I don't know why
// there's a separate version.
if(magic[3] == '0' || magic[3] == '1')
TipImage images[NumImages / 2] [[name("Images")]]; // Divided by 2 since TipImage adds a nested CLUT "image"
} else {
std::error("[TipFile] NOT a TIP0/TIP1 file.");
}
} [[static]];
// TIP0/TIP1 file data.
TipFile tip @ 0x0 [[name("TIP file")]];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment