Skip to content

Commit

Permalink
Merge pull request #17 from JayFoxRox/tex-model-xml
Browse files Browse the repository at this point in the history
Use texture information from modelblock
  • Loading branch information
JayFoxRox authored May 5, 2018
2 parents 1bd07bb + a44bc51 commit 6756098
Showing 1 changed file with 180 additions and 62 deletions.
242 changes: 180 additions & 62 deletions out_textureblock.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
#!/usr/bin/env python3

# This is a python script to export textures from out_textureblock.bin.
#
# As out_textureblock.bin only stores pixel data, but no information about
# the pixelformat or texture resolution, we need additional information.
#
# Additional texture information is being loaded from XML files which can be
# generated from out_modelblock.bin using the Sw_Racer XmlConverter tool.
# Sw_Racer can be found at https://github.com/Olganix/Sw_Racer

# Usage: out_textureblock.py <path to out_textureblock.bin> <path to XML> [<path to XML> ...]

import sys
import struct
from xml.dom import minidom
from PIL import Image

def get_value(s):
if s[0:2] == '0x':
return int(s[2:], 16)
else:
return int(s)

def read8(f):
return f.read(1)[0]
def read16(f):
Expand All @@ -19,89 +38,188 @@ def shifter(a1):
--v2
if v3 == 0:
break
result = 2 * v1
result = v1 << 1
if ( result < a1 ):
result *= 2
result <<= 1
if ( result < 16 ):
result = 16
return result


# Collect information about textures
texinfo = {}
texinfo_source = {}
history = ''
for path in sys.argv[2:]:
if history != '':
print('')
history = ''
print("Loading models from XML '%s'" % path)
mydoc = minidom.parse(path)
Swr_Model = mydoc.documentElement
#FIXME: Properly follow the structure of the XML
Section5_Collection = Swr_Model.getElementsByTagName("Section5")
for Section5 in Section5_Collection:
texmask = get_value(Section5.getElementsByTagName("textureMask")[0].attributes['u8'].value)
texindex = get_value(Section5.getElementsByTagName("textureIndex")[0].attributes['u24'].value)
if texmask != 0x0A:
print("Unknown texmask 0x%02X" % texmask)
continue

Section5b = Section5.getElementsByTagName("Section5b")
if (len(Section5b) == 0):
print(" Missing Section5b")
continue
#FIXME: assert(len(Section5b) == 1)
Section5b = Section5b[0]

t = {}
t['width'] = get_value(Section5.getElementsByTagName("unk16")[0].attributes['u16'].value)
t['height'] = get_value(Section5.getElementsByTagName("unk18")[0].attributes['u16'].value)
t['format_a'] = get_value(Section5.getElementsByTagName("unk12")[0].attributes['u8'].value)
t['format_b'] = get_value(Section5.getElementsByTagName("unk13")[0].attributes['u8'].value)
t['flags'] = get_value(Section5b.getElementsByTagName("unk3")[0].attributes['u8'].value)

if texindex in texinfo:
if (texinfo[texindex] != t):
if history != '':
print('')
print(" Skipping conflicting texture information for %d" % texindex)
print(" Old: %s" % (str(texinfo[texindex])))
print(" New: %s" % (str(t)))
history = ''
else:
if history != 'skipping':
if history != '':
print('')
print(" Skipping known texture information for", end='')
history = 'skipping'
print(" %d" % texindex, end='', flush=True)
else:
if history != 'adding':
if history != '':
print('')
print(" Adding texture information for", end='')
history = 'adding'
print(" %d" % texindex, end='', flush=True)
texinfo[texindex] = t
texinfo_source[texindex] = path

with open(sys.argv[1], 'rb') as f:
count = read32(f)

# Now dump all textures

for i in range(0, count - 1):
f.seek(4 + 8 * i)
a = read32(f)
b = read32(f)
c = read32(f)
length = c - a

if b != 0:
if i not in texinfo:
print("Unknown texture information for %d" % i)
continue

print("%d: a: 0x%08X b: 0x%08X (c: 0x%08X; length: %d or %d bytes)" % (i, a, b, c, b - a, length))
width = texinfo[i]['width']
height = texinfo[i]['height']
format_a = texinfo[i]['format_a']
format_b = texinfo[i]['format_b']
flags = texinfo[i]['flags']

f.seek(a)
buf = f.read(length)
#FIXME: POT width and height?!

with open("/tmp/swep1r/texture-%d.bin" % i, 'wb') as t:
f.read
t.write(buf)
f.seek(4 + 8 * i)
off_a = read32(f)
off_b = read32(f)
off_c = read32(f)
length = off_c - off_a

f.seek(a)
mode = read8(f)
print(" - mode: %d" % mode)
print("%d: a: 0x%08X b: 0x%08X (c: 0x%08X; length: %d or %d bytes)" % (i, off_a, off_b, off_c, off_b - off_a, length))

if False:
# Only flags 0x10 and 0x01 are known to exist
#FIXME: Also uses 0x20
print("Flags: 0x%02X " % flags)
#assert(flags & ~0x11 == 0)

if length != 2080:
continue

if mode != 1:
continue

#im = PIL.Image.frombytes('RGBA', (32, 32), buf[1:], decoder_name='raw')

if b == 0:
if length == 128:
width = 32 // 8
height = 32 // 4
elif length == 256:
width = 32 // 4
height = 32 // 4
elif length == 512:
width = 32 // 4
height = 32 // 2
elif length == 1024:
width = 32 // 2
height = 32 // 2
elif length == 2048:
width = 32
height = 32 // 2
elif length == 2800:
#FIXME!!!
width = 1
height = 1
elif length == 4096:
# Some of these are 64*16, others seem to be 32x32
width = 32 * 2
height = 32 // 2
#width = 32
#height = 32
else:
assert(False)
im = Image.new("RGBA", (width, height))
pixels = im.load()

im = Image.new("RGBA", (width, height))
pixels = im.load()

if format_a == 0 and format_b == 3:

f.seek(off_a)
for y in range(0, height):
for x in range(0, width):
#pixel = int.from_bytes(f.read(2), byteorder='big', signed=False) #read16(f)
r, g, b, a = f.read(4)
#r = ((pixel >> 0) & 0x1F) * 0xFF // 0x1F
#g = 0 #((pixel >> 5) & 0x1F) * 0xFF // 0x1F
#b = 0 #((pixel >> 10) & 0x1F) * 0xFF // 0x1F
pixels[x, y] = (a, r, g, b)
im.save("/tmp/swep1r/texture-%d.png" % i, 'PNG')
pixels[x, y] = (r, g, b, a)

elif format_a == 2 and format_b == 0:

f.seek(off_a)
for y in range(0, height):
for x in range(0, width):

if x % 2 == 0:
# Get index in palette for 2 pixels
indices = f.read(1)[0]
index = (indices & 0xF0) >> 4
indices <<= 4

# Read palette data
off = f.tell()
f.seek(off_b + 2 * index)
color = int.from_bytes(f.read(2), byteorder='big', signed=False)
a = (color >> 0) & 0x1
b = ((color >> 1) & 0x1F) / 0x1F
g = ((color >> 6) & 0x1F) / 0x1F
r = ((color >> 11) & 0x1F) / 0x1F
pixels[x, y] = (int(r * 255), int(g * 255), int(b * 255), a * 0xFF)
f.seek(off)

elif format_a == 2 and format_b == 1:

f.seek(off_a)
for y in range(0, height):
for x in range(0, width):

# Get index in palette
index = f.read(1)[0]

# Read palette data
off = f.tell()
f.seek(off_b + 2 * index)
color = int.from_bytes(f.read(2), byteorder='big', signed=False)
a = (color >> 0) & 0x1
b = ((color >> 1) & 0x1F) / 0x1F
g = ((color >> 6) & 0x1F) / 0x1F
r = ((color >> 11) & 0x1F) / 0x1F
pixels[x, y] = (int(r * 255), int(g * 255), int(b * 255), a * 0xFF)
f.seek(off)

elif format_a == 4 and format_b == 0:

f.seek(off_a)
for y in range(0, height):
for x in range(0, width):

if x % 2 == 0:
# Get color for 2 pixels
values = f.read(1)[0]
value = ((values & 0xF0) >> 4) * 0x11
values <<= 4

pixels[x, y] = (value, value, value, value)

elif format_a == 4 and format_b == 1:

f.seek(off_a)
for y in range(0, height):
for x in range(0, width):
value = f.read(1)[0]
pixels[x, y] = (value, value, value, 0xFF)

else:
print("Unhandled texture format %d / %d (%s)" % (format_a, format_b, texinfo_source[i]))
assert(False)

im = im.transpose(Image.FLIP_TOP_BOTTOM)
im.save("/tmp/swep1r/texture-0x%x.png" % (i), 'PNG')

if False:
if (a2 == 1257):
Expand Down

0 comments on commit 6756098

Please # to comment.