The rules have been updated, read them now: Rules!

Bloodrayne .TEX (Xbox type 13)

Get your graphics formats figures out here! Got details for others? Post here!
Post Reply
TheFifthHorseman
ultra-n00b
Posts: 3
Joined: Sun Nov 22, 2020 5:24 pm
Has thanked: 1 time
Been thanked: 1 time

Bloodrayne .TEX (Xbox type 13)

Post by TheFifthHorseman » Tue Nov 24, 2020 4:18 pm

I'm looking to decode the textures used in console versions of Bloodrayne to a more readable / editable format. It seems, though, that each of the console versions had its' own (and possibly more than one) variation on the format.
Currently looking at the Xbox format and while I've got the header, palette and image data positions, the texture seems to be encoded in some way that requires translating the pixel coordinates. I have no idea what the algorithm for that should be, any ideas?

What I expect:
Image

What I get:
Image

User avatar
Acewell
VIP member
VIP member
Posts: 1329
Joined: Wed Nov 05, 2008 12:16 pm
Has thanked: 2683 times
Been thanked: 841 times

Re: Bloodrayne .TEX (Xbox type 13)

Post by Acewell » Tue Nov 24, 2020 5:10 pm

likely morton order swizzled, post some samples. :D

TheFifthHorseman
ultra-n00b
Posts: 3
Joined: Sun Nov 22, 2020 5:24 pm
Has thanked: 1 time
Been thanked: 1 time

Re: Bloodrayne .TEX (Xbox type 13)

Post by TheFifthHorseman » Tue Nov 24, 2020 5:47 pm

The raw texture for the above picture: https://drive.google.com/file/d/1Nv6LDl ... sp=sharing
The current conversion script (adding to a Type 1 / Type 2 converter script that goes some 15 years back):

Code: Select all

#!/usr/bin/perl  -0777

use open IO=> ':raw';

$opaque= pack "C", 255;
sub addalpha {
	return pack '(a4)*', map {$_.$opaque} unpack '(a3)*', shift;
}
while (<>) {
	unless (($base= $ARGV) =~s/\.TEX$//i) {
		warn "$ARGV has an unrecognized file extension\n";
		next;
	}

	($version, $texType, $W, $H, $x, $mipLevels, $x, $x)= unpack "I*", substr $_, 0, 32;

	if (3 != $version) {
		if (2 != $version) {
			if (6 != $version) {
				if (13 != $version) {
					warn "$ARGV is not a TEX file\n";
					next;
				}
			}
		}
	}
	#$W=$W+$W;
	#$H=$H/2;
	warn "version $version\n";
	warn "texType $texType\n";
	warn "$W x $H\n";
	warn "mip levels $mipLevels\n";
	$suffix= "";
	@shift= 0 .. $mipLevels;
	@W= map {$W >> $_} (@shift,@shift);	# doubled for offsets into texType 2 with miplevels
	@H= map {$H >> $_} (@shift,@shift);
	$pixW= $texType == 3 ?4 :1;
	@off= ($sum= 0);
	for (0..$#W) {
		push @off, $sum+= $W[$_]*$pixW*$H[$_];
	}

	for $mip (0 .. 0) {	
#	for $mip (0 .. $mipLevels) { # We're only interested in the largest version of the texture
		$W= $W[$mip];
		$H= $H[$mip];
		if ($mip) {
			$suffix= -$mip;
		}
		$out= "$base$suffix.bmp";

		# likely BMP values
		$offset= 54;
		$size= $offset + $W*$H*4;
		$ID2= 40;
		$planes= 1;
		$bpp= 32;
		$compression= 0;
		$SizeImage= $W*$H;
		$XPPM= 2834;
		$YPPM= 2834;
		$ColorsUsed= 0;
		$ColorsImportant= 0;

		$colormap= "";
		# override some of these on a per-tex-type basis
		if (1 == $texType || 2 == $texType) {

			@color= @col= unpack "C*", substr $_, 32, 256*3;
			if (2 == $version) {
				@color= @col= unpack "C*", substr $_, 24, 256*3;
			}
			for $n (0..255) {
				# bmp colors are BGR instead of RGB
				for $m (0..2) {
					$color[($n*3)+$m]= $col[($n*3)+2-$m];
				}
			}
			if (1 == $texType) {
				$offset= 54 + 256*4;    # 256 color palette
				$size= $offset+$W*$H;
				$bpp= 8;
				$ColorsUsed= 256;
				$ColorsImportant= 256;
				$colormap= addalpha pack 'C*', @color;
				$texture= pack "(a$W)*", reverse unpack "(a$W)*", substr $_, 800+$off[$mip], $W*$H;
			} else {
				$wh= $W*$H;
				my @image= unpack "C*", pack "(a$W)*", reverse unpack "(a$W)*", substr $_, 800+$off[$mip], $wh;
				my @opacity= unpack "(a1)*", pack "(a$W)*", reverse unpack "(a$W)*", substr $_, 800+$off[$mip+1+$mipLevels], $wh;
				my @rgb= unpack "(a3)*", pack "C*", @color;
				$texture= pack "(a4)*", map {"$rgb[$image[$_]]$opacity[$_]"} 0..($wh-1);
			}
		} elsif (3 == $texType) {
			$W4= $W*4;	# width of scanline
			$texture= pack "(a$W4)*", reverse unpack "(a$W4)*", substr $_, 32+$off[$mip], $W4*$H;
		} elsif (6 == $texType) {
			#PS2 texture format
			@color= @col= unpack "C*", substr $_, 0x38, 256*4;
			
			$CurrOfs = 1;
			for $i (1 .. 255){
				$SwitchI = ($i / 8)%4;
				if    ( 1 == $SwitchI ) { $vSum = 8;  }
				elsif ( 2 == $SwitchI ) { $vSum = -8; }
				else  				 	{ $vSum = 0; }
				$storeAddr = $i + $vSum ;
				$color[($storeAddr*4)+0] = $col[($CurrOfs*4)+0];
				$color[($storeAddr*4)+1] =  $col[($CurrOfs*4)+1];
				$color[($storeAddr*4)+2] =  $col[($CurrOfs*4)+2];
				$color[($storeAddr*4)+3] =  $col[($CurrOfs*4)+3];
				$CurrOfs += 1;
			}
			for $n (0..256*4-1) {
				$col[$n]=$color[$n];
			}
			for $n (0..255) {
				# bmp colors are BGR instead of RGB
				$color[($n*4)+0]= $col[($n*4)+2];
				$color[($n*4)+1]= $col[($n*4)+1];
				$color[($n*4)+2]= $col[($n*4)+0];
				$color[($n*4)+3]= 255;
			}
			
			$offset= 54 + 256*4;    # 256 color palette
			$size= $offset+$W*$H;
			$bpp= 8;
			$ColorsUsed= 256;
			$ColorsImportant= 256;
			$colormap= pack 'C*', @color;
			@texout = @rawtex = unpack "C*", substr $_, (0x38 + 31 + 0x100*4)+$off[$mip], $W*$H;

			$block_location = 0;
			$byte_num = 0;
			$swap_selector = 0;
			$posY = 0;
			$column_location = 0;
			for $y (0..$H-1) {
				for $x (0..$W-1) {
					$block_location = ($y & -16) * $W + ($x & -16) * 2;
					$swap_selector = (( ($y + 2) >> 2) & 0x1) * 4;
					$posY = ((($y  & -4) >> 1) + ($y & 1)) & 0x7;
					$column_location = $posY * $W * 2 + (($x + $swap_selector) & 0x7) * 4;
					$byte_num = (( $y >> 1) & 1) + ( ( $x >> 2) & 2);
					$texout[($y * $W) + $x] = $rawtex[$block_location + $column_location + $byte_num + 1];
				}
			}

			@texrows = @rawtexrows=  unpack "(a$W)*", pack 'C*', @texout;
			$texture= pack "(a$W)*", reverse @texrows;
		} elsif (13 == $texType) {
			#Xbox (original) texture format
			@color= @col= unpack "C*", substr $_, 0x18, 256*4;
			#The palette is BMP-correct as-is
			$offset= 0x18 + 256*4;    # 256 color palette
			$size= $offset+$W*$H;
			$bpp= 8;
			$ColorsUsed= 256;
			$ColorsImportant= 256;
			$colormap= pack 'C*', @color;
			@texout = @rawtex = unpack "C*", substr $_, (0x18 + 0x100*4)+$off[$mip], $W*$H;

			@texrows = @rawtexrows=  unpack "(a$W)*", pack 'C*', @texout;
			$texture= pack "(a$W)*", reverse @texrows;
		}
		else {
			warn "$ARGV: unsupported tex file type: $texType\n";
			next;
		}

		$header= "BM".pack "IssIIIIssIIIIII", $size, 0, 0, $offset, $ID2, $W, $H, $planes, $bpp, $compression, $SizeImage, $XPPM, $YPPM, $ColorsUsed, $ColorsImportant;

		open OUT, "> $out" or die "can't write to $out";

		print OUT $header;
		print OUT $colormap;
		print OUT $texture;

		close OUT;
	}
}
The type 6 support was based on code described in another thread here ( viewtopic.php?p=161964#p162244 ) and works for that, but the unswizzling algorithm isn't applicable for Type 13. When tried, this is the result I get:
Image

User avatar
DKDave
mega-veteran
mega-veteran
Posts: 270
Joined: Mon May 06, 2019 6:07 pm
Location: On board the USS Callister
Has thanked: 6 times
Been thanked: 121 times

Re: Bloodrayne .TEX (Xbox type 13)

Post by DKDave » Tue Nov 24, 2020 6:37 pm

Have you tried using Turfster's BloodRayne Toolset? It works perfectly with your example:

Image
"Each person is born with their fate written into their own genetic code. It's unchangeable, immutable. But that's not all there is to life." - Dr. Naomi Hunter

TheFifthHorseman
ultra-n00b
Posts: 3
Joined: Sun Nov 22, 2020 5:24 pm
Has thanked: 1 time
Been thanked: 1 time

Re: Bloodrayne .TEX (Xbox type 13)

Post by TheFifthHorseman » Tue Nov 24, 2020 6:57 pm

Which version are you using? I've got v3.5BETA5 build 051205-0240, and that one doesn't handle the Xbox textures.

EDIT: Found 3.7 build 190106-1258, that one SOLVED the problem. Thanks!

Post Reply