Last active
April 25, 2022 17:42
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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