Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: scmark15 on 2010-05-08 02:39:52
-
Here is some notes I made using a hex editor and playing around...
Everything below is from this 4x4 pixel image with 4 colors (Red, Green, Blue & White)...
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
--------- 4-Bit TIM (Paletted)-------------
-- HEADER ---------------------------------
Format: 10 00 00 00 TIM Magic
BPP: 08 00 00 00 4bpp
-- CLUT DATA HEADER------------------------
Length: 2C 00 00 00 44 bytes (32 bytes/palette + 12 bytes for this header)
palX: 00 00 0px
palY: 00 00 0px
colr#: 10 00 16 (Always 16)
#CLUT: 01 00 1 CLUT
-- CLUT - 2 byte (ABGR[1555] Color) little endian-------
HEX -> HEX -> BINARY -> COLOR -> COLOR-REF
LE -> BE -> A BBBBB GGGGG RRRRR -> - -> -
-----------------------------------------------------------
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED -> 0000
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN -> 0001
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE -> 0010
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE -> 0100
There is only 4 Colors in palette rest is "Black" padding -> 00 00
00 00 -> 0 00000 00000 00000 -> BLACK -> 1000
00 00 -> 0 00000 00000 00000 -> BLACK -> 1001
00 00 -> 0 00000 00000 00000 -> BLACK -> 1010
...repeat until this CLUT is a total of 32 bytes/16 color entries (last index: 00 00 -> 0 00000 00000 00000 -> BLACK -> 1111)
(Example: We only have 4 colors so there will be 24 bytes of padding or
"00 00" 12 times (PADDING: 00 00 -> 0 00000 00000 00000 -> BLACK --> XXXX)
-- IMAGE HEADER ---------------------------
Length: 14 00 00 00 20 bytes (1 byte for each 2 pixel set + 12 bytes for this header)
Hres: C0 03 960
Vres: 00 00 0
Width: 01 00 4px (multiplied by 4 to get actual)
Height: 04 00 4px
-- IMAGE DATA (Like 8-bit TIM, REFERENCED, only 4 bits instead of 8 bits)----------------
HEX -> BINARY -> PX2Cr | PX1Cr
-> px2 px1 -> - | -
------------------------------------------------
00 -> 0000 0000 -> RED | RED
11 -> 0001 0001 -> GREEN | GREEN
00 -> 0000 0000 -> RED | RED
11 -> 0001 0001 -> GREEN | GREEN
22 -> 0010 0010 -> BLUE | BLUE
33 -> 0100 0100 -> WHITE | WHITE
22 -> 0010 0010 -> BLUE | BLUE
33 -> 0100 0100 -> WHITE | WHITE
Read 2 pixels at a time but second pixel is output'd first (ie right to left)
1st byte...
1st 4-bit set -> 2nd pixel
2nd 4-bit set -> 1st pixel
2nd byte...
3rd 4-bit set -> 4th pixel
4th 4-bit set -> 3rd pixel
3rd byte...
5th 4-bit set -> 6th pixel
6th 4-bit set -> 5th pixel
4th byte...
7th 4-bit set -> 8th pixel
8th 4-bit set -> 7th pixel
Visual Example (4 x 2 px image):
+-------+-------+-------+-------+
| 2nd | 1st | 4th | 3rd |
+-------+-------+-------+-------+
| 6th | 5th | 8th | 7th |
+-------+-------+-------+-------+
So... 00 -> Output is: RED | RED
11 -> Output is: GREEN | GREEN
01 -> Output is: GREEN | RED
10 -> Output is: RED | GREEN
^-- RED is 0 and GREEN is 1
IMAGE (1 CLUT):
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
====================================================================
--------- 8-Bit TIM (Paletted)-------------
-- HEADER ---------------------------------
Format: 10 00 00 00 TIM Magic
BPP: 09 00 00 00 8bpp
-- CLUT DATA HEADER------------------------
Length: 0C 02 00 00 524 bytes (512 bytes/palette + 12 bytes for this header)
palX: 00 00 0px
palY: 00 00 0px
colr#: 00 01 256 (Always 256)
#CLUT: 01 00 1 CLUT
-- CLUT - 2 byte (ABGR[1555] Color) little endian -------
*Each CLUT or Color LookUp Table is 256 colors or 512 bytes
HEX -> HEX -> BINARY -> COLOR -> COLOR-REF
LE -> BE -> A BBBBB GGGGG RRRRR -> - -> -
------------------------------------------------------------
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED -> color00
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN -> color01
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE -> color02
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE -> color03
There is only 4 Colors in palette rest is "Black" padding -> 00 00 ...
00 00 -> 0 00000 00000 00000 -> BLACK -> color04
00 00 -> 0 00000 00000 00000 -> BLACK -> color05
...repeat until this CLUT is a total of 512 bytes/256 color entries
(Example: We only have 4 colors so there will be 504 bytes of padding or
"00 00" 252 times (PADDING: 00 00 -> 0 00000 00000 00000 -> BLACK -> colorXX)
-- IMAGE HEADER ---------------------------
Length: 1C 00 00 00 28 bytes (1 byte for each pixel + 12 bytes for this header)
Hres: C0 03 960
Vres: 00 00 0
Width: 02 00 4px (multiplied by 2 to get actual)
Height: 04 00 4px
-- IMAGE DATA (Just like paletted BMP, REFERENCE each color from CLUT) --
HEX -> REF -> COLOR
-----------------------
00 -> color00 -> RED
00 -> color00 -> RED
01 -> color01 -> GREEN
01 -> color01 -> GREEN
00 -> color00 -> RED
00 -> color00 -> RED
01 -> color01 -> GREEN
01 -> color01 -> GREEN
02 -> color02 -> BLUE
02 -> color02 -> BLUE
03 -> color03 -> WHITE
03 -> color03 -> WHITE
02 -> color02 -> BLUE
02 -> color02 -> BLUE
03 -> color03 -> WHITE
03 -> color03 -> WHITE
IMAGE (1 CLUT):
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
====================================================================
------- 16-Bit TIM (NO CLUT DATA)----------
-- HEADER ---------------------------------
Format: 10 00 00 00 TIM Magic
BPP: 02 00 00 00 16bpp
-- IMAGE DATA HEADER-----------------------
Length: 2C 00 00 00 44 bytes (2 bytes for each pixel + 12 bytes for this header)
Hres: C0 03 960
Vres: 00 00 0
Width: 04 00 4px
Height: 04 00 4px
-- RAW IMAGE DATA - 2 byte (ABGR[1555] Color) little endian-------
HEX -> HEX -> BINARY -> COLOR
LE -> BE -> A BBBBB GGGGG RRRRR -> -
-----------------------------------------------
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE
IMAGE:
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
====================================================================
-------- 24-Bit TIM (NO CLUT DATA)---------
NOTE: Some TIM programs don't support 24-bit TIMs
Example...Yu_Ri doesn't but TIM viewer does
-- HEADER ---------------------------------
Format: 10 00 00 00 TIM Magic
BPP: 03 00 00 00 24bpp
-- IMAGE DATA HEADER ----------------------
Length: 3C 00 00 00 60 bytes (3 bytes for each pixel + 12 bytes for this header)
Hres: C0 03 960
Vres: 00 00 0
Width: 06 00 4px (Divided by 1.5 to get actual)
Height: 04 00 4px
-- RAW IMAGE DATA - 3 byte RGB[888] big endian ----
HEX -> COLOR
RR GG BB -> -
-----------------
FF 00 00 -> Red
FF 00 00 -> Red
00 FF 00 -> Green
00 FF 00 -> Green
FF 00 00 -> Red
FF 00 00 -> Red
00 FF 00 -> Green
00 FF 00 -> Green
00 00 FF -> Blue
00 00 FF -> Blue
FF FF FF -> White
FF FF FF -> White
00 00 FF -> Blue
00 00 FF -> Blue
FF FF FF -> White
FF FF FF -> White
IMAGE:
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Red | Red | Green | Green |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
| Blue | Blue | White | White |
+-------+-------+-------+-------+
Updated: for those who just wanna copy and paste to have a copy (see my next post to see what was fixed)
-
Thanks for this. Perhaps some of it can be merged with the documentation on the wiki (http://wiki.qhimm.com/PSX/TIM_file)? Also, I don't notice anything about TIMs with an odd width. I included some handling for that case in my (currently unused) library (http://kenai.com/projects/jpsxdec/sources/svn/content/trunk/src/jpsxdec/modules/psx/tim/Tim.java?rev=37), but haven't ever tested it.
-
:o I forgot to note that you have to change the hex in the CLUT/Raw Image Data (4, 8 & 16 bit palettes) to Big Endian before converting it to binary to get A BBBBB GGGGG RRRRR
because its stored in Little Endian as GGG RRRRR A BBBBB GG. :-D
For example... Little Endian 1F 00 (Binary: 000 11111 0 00000 00) -> Big Endian: 00 1F (Binary: 0 00000 00000 11111)
this will output pure red.
HEX -> HEX -> BINARY -> COLOR -> COLOR-REF
LE -> BE -> A BBBBB GGGGG RRRRR -> - -> -
-----------------------------------------------------------
1F 00 -> 00 1F -> 0 00000 00000 11111 -> RED -> 0000
E0 03 -> 03 E0 -> 0 00000 11111 00000 -> GREEN -> 0001
00 7C -> 7C 00 -> 0 11111 00000 00000 -> BLUE -> 0010
FF 7F -> 7F FF -> 0 11111 11111 11111 -> WHITE -> 0011
I just thought people who didn't know anything or not enough about TIM files might enjoy this so I thought I'd share.
I only used a 4x4 image with 4 colors because it was easier to figure out then a smaller image and a bigger image had
too much to sort through. As for odd pixeled images I think I tried once and I couldn't get it to display, maybe because of
the CLUT data, or BPP I tried... I can't remember, I'll have to look into that ^_^
Please note: that these are my notes and may not be 100% correct...so if anyone could or wants to add/update
these notes please do so and share with the community, especially me... could always use some more info & feedback.
Oh, and if they want to add some of these notes to the wiki I have no problem with that at all.
Peace,
scmark15
<[email protected]>
-
I'm always forgetting to mention something ^_^
If anyone wants a copy of Yu_Ri 0.99e in english drop me an email at <[email protected]>
just make sure to add "NEED YU_RI" as the Subject or I won't read it.
Yu_Ri is a TIM, BMP extractor... it searches through big and small files for images.
It will take a multi CLUT TIM and open an image for each CLUT to view as well as save/multi-save
to BMP or TIFF. Version 0.99e includes GIF and JPEG ripping plugins.
Yu_Ri 0.99e is the last version of yu_ri released and is hard to find most websites only
have version 0.99c. I translated 90% of it to english for personal use but I'm willing to share ^_^
-later
-
I wanted to see if this could be done....
I made a TIM reader using PHP. It only works for 8bpp images with only 1 CLUT...for now anyway. I got it to work with the FF7 character faces in the psx versions "MENU" folder.
Test Requirements:
-PHP 5 (this is what I used)
-IIS or Apache Server running PHP
-A copy of FF7 for PSX
-A 1x1 GIF image thats completely transparent (must be named "spacer.gif")
- place it in the same folder as the php script.
- (its for drawing a table to display the TIM)
-A copy of the script below
- Copy it to notepad and save it as TIMReader.php
<html>
<head>
<title></title>
<style>
<!--
body {
margin: 10px;
padding: 0px;
font-family: courier new;
font-size: 11px;
}
-->
</style>
</head>
<body>
<?php
$file = "test.tim";
// Dummy out file
$fileHex = '';
$handle = fopen($file, "rb") or die("Error: Check your permissions");
while(!feof($handle)){
foreach(unpack("C*", fgets($handle)) as $dec){
$tmp = dechex($dec);
$fileHex .= strtoupper(str_repeat("0", 2-strlen($tmp)) . $tmp);
}
}
fclose($handle);
function hexbin($str_hex) {
$str_bin = FALSE;
for ($i=0; $i < strlen($str_hex); $i++) {
$str_bin .= sprintf("%04s", decbin(hexdec($str_hex[$i])));
}
return $str_bin;
}
// FILE HEADER
// file, offset, numchar
$timMagic = wordwrap(substr($fileHex, 0, 8), 2, " ", true);
$timType = wordwrap(substr($fileHex, 8, 8), 2, " ", true);
// CLUT HEADER
$CLUTLength = wordwrap(substr($fileHex, 16, 8), 2, " ", true);
$PalX = wordwrap(substr($fileHex, 24, 4), 2, " ", true);
$PalY = wordwrap(substr($fileHex, 28, 4), 2, " ", true);
$NumColors = wordwrap(substr($fileHex, 32, 4), 2, " ", true);
$NumCLUTs = wordwrap(substr($fileHex, 36, 4), 2, " ", true);
// CLUT DATA ~~~~ offsex starts at 40
$offset = 40;
//for($i = 0; $i < $NumCLUTs; $i++){
for($i = 0; $i < 256; $i++){
$colorArray[] = wordwrap(substr($fileHex, $offset, 4), 2, " ", true);
$offset += 4;
}
//}
// IMAGE HEADER
$IMGLength = wordwrap(substr($fileHex, $offset, 8), 2, " ", true);
$IMGX = wordwrap(substr($fileHex, $offset + 8, 4), 2, " ", true);
$IMGY = wordwrap(substr($fileHex, $offset + 12, 4), 2, " ", true);
$Pitch = wordwrap(substr($fileHex, $offset + 16, 4), 2, " ", true);
$Height = wordwrap(substr($fileHex, $offset + 20, 4), 2, " ", true);
// Fix Offset for the next block;
$offset += 24;
// OUTPUT
echo "TIM Header: " . $timMagic . "<br />" . "\n";
echo "TIM Type: " . $timType . "<br />" . "\n";
echo "<br />" ."\n";
echo "CLUT Length: " . $CLUTLength . "<br />" . "\n";
echo "PalX: " . $PalX . "<br />" . "\n";
echo "PalY: " . $PalY . "<br />" . "\n";
echo "# of Colors: " . $NumColors . "<br />" . "\n";
echo "# of CLUTs: " . $NumCLUTs . "<br />" . "\n";
echo "<br />" ."\n";
$x = 0;
// BGR[1555] to RGB[888]
function getRGB($colorBin, $int){
global $Palette;
$a1 = substr($colorBin, 0, 1);
$b5 = substr($colorBin, 1, 5);
$g5 = substr($colorBin, 6, 5);
$r5 = substr($colorBin, 11, 5);
$alphadec = bindec($a1);
$bluedec = bindec($b5);
$greendec = bindec($g5);
$reddec = bindec($r5);
$a8 = $alphadec * 8;
// Older BGR[1555] to RGB[888] Conversion
// (Output is slightly darker)
// $r8 = $reddec * 8;
// $g8 = $greendec * 8;
// $b8 = $bluedec * 8;
// New BGR[1555] to RGB[888] Conversion
// (Output is slightly brighter)
$r8 = round(($reddec * 8) + ($reddec / 31 * 7));
$g8 = round(($greendec * 8) + ($greendec / 31 * 7));
$b8 = round(($bluedec * 8) + ($bluedec / 31 * 7));
// Alpha is ignored
// $RGBAlpha = strtoupper(str_repeat("0", 2-strlen(dechex($a8))) . dechex($a8));
$RGBRed = strtoupper(str_repeat("0", 2-strlen(dechex($r8))) . dechex($r8));
$RGBGreen = strtoupper(str_repeat("0", 2-strlen(dechex($g8))) . dechex($g8));
$RGBBlue = strtoupper(str_repeat("0", 2-strlen(dechex($b8))) . dechex($b8));
$RGB = '#' . $RGBRed . $RGBGreen . $RGBBlue;
$Palette[$int] = $RGB;
return $RGB;
}
foreach($colorArray as $color){
$tmp = explode(" ", $color);
$colorLE = $tmp[1] . $tmp[0];
unset($tmp);
echo "Color " . strtoupper(str_repeat("0", 2-strlen(dechex($x))) . dechex($x)) . ": " . $color . "\n";
echo ' -> ' . "\n";
$colorBin = hexbin($colorLE);
echo $colorBin;
echo ' -> ' . "\n";
echo getRGB($colorBin, $x);
echo '<br />' . "\n";
$x++;
}
echo "<br />" ."\n";
echo "IMG Length: " . $IMGLength . "<br />" . "\n";
echo "IMGX: " . $IMGX . "<br />" . "\n";
echo "IMGY: " . $IMGY . "<br />" . "\n";
echo "Pitch: " . $Pitch . "<br />" . "\n";
echo "Height: " . $Height . "<br />" . "\n";
echo "<br />" ."\n";
$tmp = explode(" ", $Pitch);
$Pitch = $tmp[1] . $tmp[0];
$Width = hexdec($Pitch) * 2;
unset($tmp);
$tmp = explode(" ", $Height);
$tHeight = $tmp[1] . $tmp[0];
$High = hexdec($tHeight);
$RawLength = $High * $Width;
echo '<table border="0" cellpadding="0" cellspacing="0"><tr>' ."\n";
$count = 0;
for($i = 0; $i < $RawLength; $i++){
$IMGRaw = substr($fileHex, $offset, 2);
$offset += 2;
$picked = hexdec($IMGRaw);
echo '<td style="background-color: ' . $Palette[$picked] . ';><img src="spacer.gif" border="0" height="1" width="1" /></td>' . "\n";
$count++;
if($count == $Width){
echo '</tr></tr>' ."\n";
$count = 0;
}
}
echo '</tr></table>';
?>
</body>
</html>
To use: copy the TIM file to the same folder as the script and rename it to "test.tim"
Run the script to see if it worked.
This is only a work in progress, so I appologize for my messy code (I wasn't trying to impress ^_^)
I hope to add functionality to work with Multi-CLUT tims as well as 4, 16 & 24 bpp also.
SCMark15
P.S.
Any feedback or comments are welcome anytime. I need to know if this worked for you, or what
other files you tried it out on. I'll update this when I have more time to play around with it.
-later