Skip to content

Instantly share code, notes, and snippets.

@penguino118
Last active March 25, 2024 20:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save penguino118/afeb198ad6ba5495311bc24fe195548e to your computer and use it in GitHub Desktop.
Save penguino118/afeb198ad6ba5495311bc24fe195548e to your computer and use it in GitHub Desktop.
Phantom Blood PS2 Texture Converter ( P2TX / TEX2 -> TIM2 )
import os
import sys
import struct
import argparse
import subprocess
from pathlib import Path
def ru08(buf, offset):
return struct.unpack("<B", buf[offset:offset+1])[0]
def ru16(buf, offset):
return struct.unpack("<H", buf[offset:offset+2])[0]
def ru32(buf, offset):
return struct.unpack("<I", buf[offset:offset+4])[0]
def wu08(value):
return struct.pack("<B", value)
def wu16(value):
return struct.pack("<H", value)
def wu32(value):
return struct.pack("<I", value)
def find_STR(buf, string):
if string == 'TEX2':
MAGIC = bytearray((int(0x54), int(0x45), int(0x58), int(0x32)))
elif string == 'P2TX':
MAGIC = bytearray((int(0x50), int(0x32), int(0x54), int(0x58)))
offsets = []
off_start = 0x0
off_end = len(buf)
exit = False
while off_start < off_end and exit == False:
offset = buf.find(MAGIC, off_start, off_end)
if offset == -1:
exit = True
else:
offsets.append(offset)
off_start += offset+4
return offsets
def Swizzle8to32(mode, reversed, pInTexels, width, height):
# https://ps2linux.no-ip.info/playstation2-linux.com/docs/howto/display_docef7c.html?docid=75
if reversed: pInTexels.reverse() #For P2TX
pSwizTexels = pInTexels.copy()
for y in range(height):
for x in range(width):
uPen = pInTexels[y*width+x]
block_location = (y&(~0xf))*width + (x&(~0xf))*2 #every 2nd row of 4 bytes is right
swap_selector = (((y+2)>>2)&0x1)*4
posY = (((y&(~3))>>1) + (y&1))&0x7
column_location = posY*width*2 + ((x+(swap_selector))&0x7)*4
byte_num = ((y>>1)&1) + ((x>>2)&2) # 0,1,2,3
if mode == 'unswizzle':
pSwizTexels[y * width + x] = pInTexels[column_location + block_location + byte_num]
elif mode == 'swizzle':
pSwizTexels[column_location + block_location + byte_num] = uPen
if reversed: pSwizTexels.reverse() #For P2TX
# aa = pSwizTexels[:16]
# bb = pSwizTexels[:16]
return pSwizTexels
def Swizzle4to32(mode, reversed, pInTexels, width, height):
# https://ps2linux.no-ip.info/playstation2-linux.com/docs/howto/display_docef7c.html?docid=75
if reversed: pInTexels.reverse() #For P2TX
pSwizTexels = pInTexels.copy()
for y in range(height):
for x in range(width):
index = y*width+x
uPen = (pInTexels[(index>>1)]>>((index&1)*4))&0xf
#uPen = pInTexels[index >> 1] & 0xff
# swizzle
pageX = x & (~0x7f)
pageY = y & (~0x7f)
pages_horz = (width+127)//128
pages_vert = (height+127)//128
page_number = (pageY//128)*pages_horz + (pageX//128)
page32Y = (page_number//pages_vert)*32
page32X = (page_number%pages_vert)*64
page_location = page32Y*height*2 + page32X*4
locX = x & 0x7f
locY = y & 0x7f
block_location = ((locX&(~0x1f))>>1)*height + (locY&(~0xf))*2
swap_selector = (((y+2)>>2)&0x1)*4
posY = (((y&(~3))>>1) + (y&1))&0x7
column_location = posY*height*2 + ((x+swap_selector)&0x7)*4
byte_num = (x>>3)&3 # 0,1,2,3
bits_set = (y>>1)&1 # 0,1 (lower/upper 4 bits)
if mode == 'unswizzle':
setPix = pInTexels[page_location + block_location + column_location + byte_num]
uPen = (setPix >> (bits_set * 4)) & 0xf
pSwizTexels[index>>1] &= ~(0xf<<((index & 1)*4))
pSwizTexels[index>>1] |= uPen<<((index & 1)*4)
elif mode == 'swizzle':
setPix = pSwizTexels[page_location + block_location + column_location + byte_num]
setPix = (setPix & (-bits_set)) | (uPen << (bits_set * 4))
pSwizTexels[page_location + block_location + column_location + byte_num] = setPix
if reversed: pSwizTexels.reverse() #For P2TX
return pSwizTexels
def Swizzle16to32(mode, reversed, pInTexels, width, height):
pSwizTexels = pInTexels.copy()
if reversed: pInTexels.reverse() #For P2TX
pSwizTexels = pInTexels.copy()
#height = height//2
for y in range(height):
for x in range(width):
#print(y, x)
uCol = pInTexels[y*width+x]
pageX = x & (~0x3f)
pageY = y & (~0x3f)
pages_horz = (width+63)//64
pages_vert = (height+63)//64
page_number = (pageY//64)*pages_horz + (pageX//64)
page32Y = (page_number//pages_vert)*32
page32X = (page_number%pages_vert)*64
page_location = (page32Y*height + page32X)*2
locX = x & 0x3f
locY = y & 0x3f
block_location = (locX&(~0xf))*height + (locY&(~0x7))*2
column_location = ((y&0x7)*height + (x&0x7))*2
short_num = (x>>3)&1 #0,1
if mode == 'unswizzle':
pSwizTexels[y*width+x] = pInTexels[page_location + block_location + column_location + short_num]
elif mode == 'swizzle':
pSwizTexels[page_location + block_location + column_location + short_num] = uCol
if reversed: pSwizTexels.reverse() #For P2TX
return pSwizTexels
def TEX2toTIM2(buf, keep_alpha, keep_swizz):
offset = 0
imgcount = ru32(buf, 0x4)
def get_img(buf, img_offset, img_length, resolution, type, keep_swizz):
image = buf[img_offset:img_length]
if keep_swizz == False:
if type == 0x501 or type == 0x502 or type == 0x504 or type == 0x508: return Swizzle4to32('unswizzle', False, image, resolution, resolution) #4bpp, 16color
#elif type == : return Swizzle4to32('unswizzle', image, resolution, resolution) #4bpp, 256color
elif type == 0x4C2 or type == 0x4C4 or type == 0x4C8: return Swizzle8to32('unswizzle', False, image, resolution, resolution) #8bpp, 256color
else: raise Exception(f"Unhandled swizzling ({hex(type)}), ping @penguino118 on discord 300 times")
else:
return image
def get_pal(buf, pal_offset, keep_alpha):
color_count = ru08(buf, pal_offset) * 4
byte_size = ru08(buf, pal_offset+0x1) * 8
pal_offset += 0x10
if keep_alpha == False: #0-128 alpha to 0-255
outpal = buf[pal_offset:pal_offset+color_count*4]#byte_size]
for y in range(color_count):
alpha = outpal[0x3 + (y * 4)]
if alpha == 128: alpha = 255
else: alpha = alpha * 2
outpal[0x3 + (y * 4)] = alpha
return outpal
else:
return buf[pal_offset:pal_offset+byte_size]
tex2_head_unk = ru32(buf, 0xC)
image_meta = []
images = []
palettes = []
offset = 0x10
for x in range(imgcount):
resolution = ru16(buf, offset)
unk0_short = ru16(buf, offset+0x2) #swizzle mode?
unk1_short = ru16(buf, offset+0x4)
unk2_short = ru16(buf, offset+0x6)
image_offset = ru32(buf, offset+0x8)
unk3_int = ru32(buf, offset+0x74)
palette_offset = ru32(buf, offset+0x78)
unk4_int = ru16(buf, offset+0x80)
#print(hex(offset))
color_count = ru08(buf, palette_offset) * 4
image_meta.append([color_count, resolution,
unk0_short, unk1_short, unk2_short, unk3_int, unk4_int])
#print(f"{buf[offset:offset+0x4].hex()}{buf[offset+0x4:offset+0x8].hex()}{buf[offset+0x8:offset+0xC].hex()}{buf[offset+0xC:offset+0x10].hex()}")
images.append( get_img(buf, image_offset+0x10, palette_offset, resolution, unk0_short, keep_swizz) )
palettes.append( get_pal(buf, palette_offset, keep_alpha) )
offset += 0x90
NewTIM2 = []
NewTIM2.append(wu32(843925844)) #TIM2
NewTIM2.append(wu16(4))
NewTIM2.append(wu16(imgcount))
NewTIM2.append(wu32(0))
NewTIM2.append(wu32(0))
for x in range(imgcount):
curr_meta = image_meta[x]
curr_img = images[x]
curr_pal = palettes[x]
img_length = wu32( len(curr_img) + len(curr_pal) + 0x30 )
pal_length = wu32( len(curr_pal) )
img_dat_length = wu32( len(curr_img) )
header_length = wu16( 0x30 )
color_count = wu16( curr_meta[0] )
img_format = wu08( 0x00 )
mipmap_count = wu08( 0x01 )
pal_format = wu08( 0x03 )
if curr_meta[2] == 0x4C8 or curr_meta[2] == 0x4C4 or curr_meta[2] == 0x4C2: csm_register = wu08( 0x05 )
else:csm_register = wu08( 0x04 )
width = wu16( curr_meta[1] )
height = wu16( curr_meta[1] )
GsBs0 = wu32(1668903792)
GsBs1 = wu16(28520)
GsBs2 = wu16(curr_meta[2]) #TEX(0x02)
GsBs3 = wu16(curr_meta[3]) #TEX(0x04)
GsBs4 = wu16(curr_meta[4]) #TEX(0x06)
GsBs5 = wu32(curr_meta[5]) #TEX(0x74)
GsBs6 = wu32(curr_meta[6]) #TEX(0x80)
GsBs7 = wu32(tex2_head_unk)
NewTIM2.append(img_length)
NewTIM2.append(pal_length)
NewTIM2.append(img_dat_length)
NewTIM2.append(header_length)
NewTIM2.append(color_count)
NewTIM2.append(img_format)
NewTIM2.append(mipmap_count)
NewTIM2.append(pal_format)
NewTIM2.append(csm_register)
NewTIM2.append(width)
NewTIM2.append(height)
NewTIM2.append(GsBs0)
NewTIM2.append(GsBs1)
NewTIM2.append(GsBs2)
NewTIM2.append(GsBs3)
NewTIM2.append(GsBs4)
NewTIM2.append(GsBs5)
NewTIM2.append(GsBs6)
NewTIM2.append(GsBs7)
for y in curr_img: NewTIM2.append(wu08(y))
for y in curr_pal: NewTIM2.append(wu08(y))
return NewTIM2
def P2TXtoTIM2(buf, keep_alpha, keep_swizz):
def get_img(buf, img_offset, img_length, resolution, col_count, type, keep_swizz):
image = buf[img_offset:img_length]
if keep_swizz == False:
if col_count == 16 and (type == 0x5DC4 or type == 0x6208 or type == 0xDDC4): return Swizzle4to32('unswizzle', False, image, resolution, resolution) #4bpp, 16color
elif col_count == 256 and (type == 0x6208 or type == 0x5DC4 or type == 0x5982): return Swizzle8to32('unswizzle', True, image, resolution, resolution) #8bpp, 256color
else: return image
#else: raise Exception(f"Unhandled swizzling ({hex(type)}), ping @penguino118 on discord 300 times")
else:
return image
def get_pal(buf, pal_offset, keep_alpha):
color_count = ru08(buf, pal_offset) * 4
byte_size = ru08(buf, pal_offset+0x1) * 8
pal_offset += 0x10
if keep_alpha == False: #0-128 alpha to 0-255
outpal = buf[pal_offset:pal_offset+color_count*4]#byte_size]
for y in range(color_count):
alpha = outpal[0x3 + (y * 4)]
if alpha == 128: alpha = 255
else: alpha = alpha * 2
outpal[0x3 + (y * 4)] = alpha
return outpal
else:
return buf[pal_offset:pal_offset+byte_size]
tex2_head_unk = ru32(buf, 0xC)
image_meta = []
images = []
palettes = []
offset = 0x10
imgcount = ru32(buf, 0x4 )
# hack for when a p2tx has exactly one swizzled iamge and it has to offset every other fucking image
fucking_swizzle_offset = False
for x in range(imgcount):
if ru32(buf, offset+(0x10*(x+1))) == 0:
fucking_swizzle_offset = True
print(fucking_swizzle_offset)
for x in range(imgcount):
image_offset = ru32(buf, offset)
palette_offset = ru32(buf, offset+0x4)
resolution = ru16(buf, offset+0x8)
unk0_short = ru16(buf, offset+0xA) # swizzle mode ?
unk1_short = ru16(buf, offset+0xC)
unk2_short = ru16(buf, offset+0xE) # related to color count // (13 = 256 colors || 14 = 16 colors)
print(f"index={x}, imgoffs={image_offset}, paloffs={palette_offset}")
color_count = ru08(buf, palette_offset) * 4 # thou still think this is more reliable than that ^ lol
#if unk0_short == 0x5DC4 or unk0_short == 0x6208 or unk0_short == 0xDDC4: #swizzled images have one extra line of garbage
if ru32(buf, offset+0x10) == 0:
unk3_int = ru32(buf, offset+0x1A)
image_meta.append([color_count, resolution,
unk0_short, unk1_short, unk2_short, unk3_int])
#image_offset += 0x10
offset += 0x10
else:
image_meta.append([color_count, resolution,
unk0_short, unk1_short, unk2_short])
if not fucking_swizzle_offset:
images.append( get_img(buf, image_offset+0x10, palette_offset, resolution, color_count, unk0_short, keep_swizz) )
else:
images.append( get_img(buf, image_offset+0x20, palette_offset, resolution, color_count, unk0_short, keep_swizz) )
palettes.append( get_pal(buf, palette_offset, keep_alpha) )
offset += 0x10
NewTIM2 = []
NewTIM2.append(wu32(843925844)) #TIM2
NewTIM2.append(wu16(4))
NewTIM2.append(wu16(imgcount))
NewTIM2.append(wu32(0))
NewTIM2.append(wu32(0))
for x in range(imgcount):
curr_meta = image_meta[x]
curr_img = images[x]
curr_pal = palettes[x]
img_length = wu32( len(curr_img) + len(curr_pal) + 0x30 )
pal_length = wu32( len(curr_pal) )
img_dat_length = wu32( len(curr_img) )
header_length = wu16( 0x30 )
color_count = wu16( curr_meta[0] )
img_format = wu08( 0x00 )
mipmap_count = wu08( 0x01 )
pal_format = wu08( 0x03 )
if int.from_bytes(color_count, "little") == 256: csm_register = wu08( 0x05 )
else:csm_register = wu08( 0x04 )
width = wu16( curr_meta[1] )
height = wu16( curr_meta[1] )
GsBs0 = wu16(65535) #ff
GsBs1 = wu32(1481912912) #P2TX
GsBs2 = wu16(65535) #ff
GsBs3 = wu16(curr_meta[2]) #TX (0x0A)
GsBs4 = wu16(curr_meta[3]) #TX (0x0C)
GsBs5 = wu16(curr_meta[4]) #TX (0x0E)
GsBs6 = wu16(65535) #ff
if len(curr_meta) == 6:
GsBs7 = wu32(curr_meta[5])
else:
GsBs7 = wu32(65535) #ff
GsBs8 = wu32(0)
NewTIM2.append(img_length)
NewTIM2.append(pal_length)
NewTIM2.append(img_dat_length)
NewTIM2.append(header_length)
NewTIM2.append(color_count)
NewTIM2.append(img_format)
NewTIM2.append(mipmap_count)
NewTIM2.append(pal_format)
NewTIM2.append(csm_register)
NewTIM2.append(width)
NewTIM2.append(height)
NewTIM2.append(GsBs0)
NewTIM2.append(GsBs1)
NewTIM2.append(GsBs2)
NewTIM2.append(GsBs3)
NewTIM2.append(GsBs4)
NewTIM2.append(GsBs5)
NewTIM2.append(GsBs6)
NewTIM2.append(GsBs7)
NewTIM2.append(GsBs8)
for y in curr_img: NewTIM2.append(wu08(y))
for y in curr_pal: NewTIM2.append(wu08(y))
return NewTIM2
############ TIM2 to... ############
def TIM2toTEX2(buf, keep_alpha, keep_swizz):
minihead = wu32(134217728)
def get_img(buf, img_offset, img_length, type, keep_swizz):
image = []
image_buffer = buf[img_offset:img_offset+img_length]
if keep_swizz == False:
if type == 8: image_buffer = Swizzle8to32('swizzle', False, image_buffer, resolution, resolution)
elif type == 4: image_buffer = Swizzle4to32('swizzle', False, image_buffer, resolution, resolution)
img_minihead = wu32((img_length//16)+32768)
image.append(img_minihead)
image.append(minihead)
image.append(wu32(0))
image.append(wu32(0))
for byte in image_buffer: image.append(wu08(byte))
return image
def get_pal(buf, pal_offset, pal_length, keep_alpha):
palette = []
palette_buffer = buf[pal_offset:pal_offset+pal_length]
color_count = pal_length // 4
if keep_alpha == False:
for y in range(color_count):
alpha = palette_buffer[0x3 + (y * 4)]
if alpha == 255: alpha = 128
else: alpha = alpha // 2
palette_buffer[0x3 + (y * 4)] = alpha
pal_minihead = wu32((color_count//4)+32768)
palette.append(pal_minihead)
palette.append(minihead)
palette.append(wu32(0))
palette.append(wu32(0))
for byte in palette_buffer: palette.append(wu08(byte))
return palette
offset = 0x10
imgcount = ru16(buf, 0x6)
tex2_head_unk = ru32(buf, 0x3C)
header_length = 0x10 + (imgcount*0x90)
images = []
palettes = []
NewTEX2 = []
NewTEX2.append(wu32(844645716)) #TIM2
NewTEX2.append(wu32(imgcount))
NewTEX2.append(wu32(0))
NewTEX2.append(wu32(tex2_head_unk))
prev_img_length = 0
#building header
for x in range(imgcount):
#FROM TIM2
total_img_length = ru32(buf, offset)
palette_length = ru32(buf, offset+0x4)
image_length = ru32(buf, offset+0x8)
color_count = ru16(buf, offset+0xE)
resolution = ru16(buf, offset+0x14)
unk0_short = ru16(buf, offset+0x1E) #swizzle mode?
unk1_short = ru16(buf, offset+0x20)
unk2_short = ru16(buf, offset+0x22)
unk3_int = ru32(buf, offset+0x24)
unk4_int = ru32(buf, offset+0x28)
if color_count == 16: type = 4
else: type = 8
images.append(get_img(buf, offset+0x30, image_length, type, keep_swizz))
palettes.append(get_pal(buf, offset+0x30+image_length, palette_length, keep_alpha))
offset += total_img_length
#TO TEX2
tex2_img_offset = header_length + prev_img_length
tex2_pal_offset = header_length + prev_img_length + (image_length+0x10)
NewTEX2.append(wu16(resolution))
NewTEX2.append(wu16(unk0_short))
NewTEX2.append(wu16(unk1_short))
NewTEX2.append(wu16(unk2_short))
NewTEX2.append(wu32(tex2_img_offset))
for i in range(26): NewTEX2.append(wu32(0x0)) #pad
NewTEX2.append(wu32(unk3_int))
NewTEX2.append(wu32(tex2_pal_offset))
NewTEX2.append(wu32(0)) #pad
NewTEX2.append(wu32(unk4_int))
for i in range(3): NewTEX2.append(wu32(0x0)) #pad
prev_img_length += total_img_length - 0x10
#offset = 0x10
for x in range(imgcount):
curr_img = images[x]
curr_pal = palettes[x]
img_offset = len(NewTEX2)
for n in curr_img: NewTEX2.append(n)
pal_offset = len(NewTEX2)
for n in curr_pal: NewTEX2.append(n)
# NewTEX2[offset+0x08] = wu32(img_offset) #header img offset
# NewTEX2[offset+0x78] = wu32(pal_offset) #header pal offset
#offset += 0x90
return NewTEX2
def TIM2toP2TX(buf, keep_alpha, keep_swizz):
minihead = wu32(134217728)
def get_img(buf, colcount, img_offset, img_length, type, keep_swizz):
image = []
image_buffer = buf[img_offset:img_offset+img_length]
if keep_swizz == False:
if colcount == 16 and (type == 0x5DC4 or type == 0x6208 or type == 0xDDC4): image_buffer = Swizzle4to32('swizzle', True, image_buffer, resolution, resolution)
elif colcount == 256 and (type == 0x6208 or type == 0x5DC4 or type == 0x5982): image_buffer = Swizzle8to32('swizzle', True, image_buffer, resolution, resolution)
#if col_count == 16 and (type == 0x5DC4 or type == 0x6208 or type == 0xDDC4): return Swizzle4to32('unswizzle', False, image, resolution, resolution) #4bpp, 16color
# elif col_count == 256 and (type == 0x6208 or type == 0x5DC4 or type == 0x5982):
img_minihead = wu32((img_length//16)+32768)
image.append(img_minihead)
image.append(minihead)
image.append(wu32(0))
image.append(wu32(0))
for byte in image_buffer: image.append(wu08(byte))
return image
def get_pal(buf, pal_offset, pal_length, keep_alpha):
palette = []
palette_buffer = buf[pal_offset:pal_offset+pal_length]
color_count = pal_length // 4
if keep_alpha == False:
for y in range(color_count):
alpha = palette_buffer[0x3 + (y * 4)]
if alpha == 255: alpha = 128
else: alpha = alpha // 2
palette_buffer[0x3 + (y * 4)] = alpha
pal_minihead = wu32((color_count//4)+32768)
palette.append(pal_minihead)
palette.append(minihead)
palette.append(wu32(0))
palette.append(wu32(0))
for byte in palette_buffer: palette.append(wu08(byte))
return palette
offset = 0x10
imgcount = ru16(buf, 0x6)
header_length = 0x10 + (imgcount*0x10)
images = []
palettes = []
NewP2TX = []
NewP2TX.append(wu32(1481912912)) #P2TX
NewP2TX.append(wu32(imgcount))
NewP2TX.append(wu32(0))
NewP2TX.append(wu32(0))
prev_img_length = 0
#building header
for x in range(imgcount):
#FROM TIM2
total_img_length = ru32(buf, offset)
palette_length = ru32(buf, offset+0x4)
image_length = ru32(buf, offset+0x8)
color_count = ru16(buf, offset+0xE)
resolution = ru16(buf, offset+0x14)
unk0_short = ru16(buf, offset+0x20) #swizzle mode?
unk1_short = ru16(buf, offset+0x22)
unk2_short = ru16(buf, offset+0x24)
unk3_int = ru32(buf, offset+0x28) #swizzled only header val
#if color_count == 16: type = 4
#else: type = 8
images.append(get_img(buf, color_count, offset+0x30, image_length, unk0_short, keep_swizz))
palettes.append(get_pal(buf, offset+0x30+image_length, palette_length, keep_alpha))
offset += total_img_length
#SWIZZLE TRASH!!!!!!!
if unk3_int != 65535: # probably wrong but i cant find any cases of multi image swizzled p2txs
tex2_img_offset = header_length + prev_img_length
tex2_pal_offset = 0x10 + header_length + prev_img_length + (image_length+0x10)
else:
tex2_img_offset = header_length + prev_img_length
tex2_pal_offset = header_length + prev_img_length + (image_length+0x10)
#P2TX header
NewP2TX.append(wu32(tex2_img_offset))
NewP2TX.append(wu32(tex2_pal_offset))
NewP2TX.append(wu16(resolution))
NewP2TX.append(wu16(unk0_short))
NewP2TX.append(wu16(unk1_short))
NewP2TX.append(wu16(unk2_short))
#SWIZZLE TRASH!!!!!!!
if unk3_int != 65535:
for i in range(2): NewP2TX.append(wu32(0x0)) #pad
NewP2TX.append(wu16(resolution//2))
NewP2TX.append(wu32(unk3_int))
NewP2TX.append(wu16(0))
prev_img_length += 0x10
prev_img_length += total_img_length - 0x10
#offset = 0x10
for x in range(imgcount):
curr_img = images[x]
curr_pal = palettes[x]
img_offset = len(NewP2TX)
for n in curr_img: NewP2TX.append(n)
pal_offset = len(NewP2TX)
for n in curr_pal: NewP2TX.append(n)
# NewTEX2[offset+0x08] = wu32(img_offset) #header img offset
# NewTEX2[offset+0x78] = wu32(pal_offset) #header pal offset
#offset += 0x90
return NewP2TX
def Get_Images(directory):
image_array = []
pathlist = Path(directory).glob('*.tm2')
for file_path in pathlist:
#last term separated by '_' (offset) to INT
file_offset = int( str(file_path).split('_')[-1].split('.')[0] ,16)
file_buffer = bytearray( open(str(file_path), "rb").read() )
test_str = ru32(file_buffer, 0x2A)
if test_str == 1481912912: # P2TX
output_tex = TIM2toP2TX(file_buffer, args.keepalpha, args.keepswizz)
elif test_str == 1869112185: # TEX2
output_tex = TIM2toTEX2(file_buffer, args.keepalpha, args.keepswizz)
else:
raise Exception(f"{file_path}: Missing output format from TIM2. ({hex(test_str)})")
image_array.append([file_offset, output_tex])
return image_array
def Inject_TM2s(buf, img_array):
output_buffer = buf.copy()
for image in img_array:
offset = int(image[0])
#print(image[1])
new_buffer = bytearray(b''.join(image[1])) #byte lIST to byte array LOL
#print(f"{offset}:{offset} + {len(new_buffer)}")
#print(f"{type(offset)}:{type(offset)} + {type(len(new_buffer))}")
output_buffer[offset:offset + len(new_buffer)] = new_buffer
return output_buffer
parser = argparse.ArgumentParser(description='TEX2 Converter')
parser.add_argument("-i", "--inpath", required=True, help="File Input (TEX2/TIM2)")
parser.add_argument("-ri", "--repack_inpath", help="Edited Image(s) Folder (TEX2/TIM2)")
parser.add_argument("-o", "--outpath", required=True, help="File Output (TIM2/TEX2)")
parser.add_argument("-sc", "--scan", action='store_true', help="Scans input file for TEX2 data and converts all found textures. Make sure the output folder exists.")
parser.add_argument("-ka", "--keepalpha", action='store_true', help="Keep original alpha range (0-128).")
parser.add_argument("-ks", "--keepswizz", action='store_true', help="Keep original swizzled/unswizzled pixel order.")
args = parser.parse_args()
if not args.repack_inpath is None: #Repack
print("hi")
with open(args.inpath, "rb") as input_file:
print(type(input_file))
input_file_buffer = bytearray( input_file.read() )
tm2_array = Get_Images(args.repack_inpath)
out_buffer = Inject_TM2s(input_file_buffer, tm2_array)
with open(args.outpath, "wb") as outfile:
#for byte in out_buffer:
# print(byte)
# print(byte.hex())
outfile.write(out_buffer)
print(f"Saved repacked file to {args.outpath}")
elif not args.inpath.endswith(".tm2"): #TEX2 or P2TX input is assumed
def save_tim2(input_file_buffer, output_tm2, outpath):
with open(outpath, "wb") as outfile:
for byte in output_tm2:
outfile.write(byte)
print(f"Saved TIM2 to {outpath}")
if args.scan == True: #TEX2 & P2TX filescan
with open(args.inpath, "rb") as input_file:
input_file_buffer = bytearray( input_file.read() )
tex2_offsets = find_STR(input_file_buffer, 'TEX2')
p2tx_offsets = find_STR(input_file_buffer, 'P2TX')
print("Scanning for TEX2 images...")
for offset in tex2_offsets:
tex2 = input_file_buffer[offset:]
output_tm2 = TEX2toTIM2(tex2, args.keepalpha, args.keepswizz)
outpath = args.outpath
outpath = (f"{Path(outpath).absolute()}\\{Path(args.inpath).stem}_TEX2_{hex(offset)}.tm2")
save_tim2(input_file_buffer, output_tm2, outpath)
print("Scanning for P2TX images...")
for offset in p2tx_offsets:
p2tx = input_file_buffer[offset:]
output_tm2 = P2TXtoTIM2(p2tx, args.keepalpha, args.keepswizz)
outpath = args.outpath
outpath = (f"{Path(outpath).absolute()}\\{Path(args.inpath).stem}_P2TX_{hex(offset)}.tm2")
save_tim2(input_file_buffer, output_tm2, outpath)
if args.outpath.endswith(".tm2"): #TEX2||P2TX TO TIM2
with open(args.inpath, "rb") as input_file:
input_file_buffer = bytearray( input_file.read() )
test_str = ru32(input_file_buffer, 0x0)
if test_str == 1481912912: #P2TX
output_tm2 = P2TXtoTIM2(input_file_buffer, args.keepalpha, args.keepswizz)
elif test_str == 844645716: #TEX2
output_tm2 = TEX2toTIM2(input_file_buffer, args.keepalpha, args.keepswizz)
else:
raise Exception(f"Unknown image type on input file. ({hex(test_str)})")
save_tim2(input_file_buffer, output_tm2, args.outpath)
elif args.inpath.endswith(".tm2"): #TEX2||P2TX output is assumed
with open(args.inpath, "rb") as input_file:
input_file_buffer = bytearray( input_file.read() )
test_str = ru32(input_file_buffer, 0x2A)
if test_str == 1481912912: # P2TX
output_tex = TIM2toP2TX(input_file_buffer, args.keepalpha, args.keepswizz)
elif test_str == 1869112185: # TEX2
output_tex = TIM2toTEX2(input_file_buffer, args.keepalpha, args.keepswizz)
else:
raise Exception(f"Missing output format from TIM2. ({hex(test_str)})")
with open(args.outpath, "wb") as outfile:
for byte in output_tex:
#print(byte)
#print(byte.hex())
outfile.write(byte)
print(f"Saved texture to {args.outpath}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment