Skip to content

Commit

Permalink
Export/Replace textures
Browse files Browse the repository at this point in the history
  • Loading branch information
blank63 committed May 7, 2016
1 parent 7777958 commit f4dc19e
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 246 deletions.
85 changes: 25 additions & 60 deletions explorer_widget.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,18 @@
from PyQt4 import QtCore,QtGui
import qt
import gx


class TextureWrapper(qt.Wrapper):
name = qt.Wrapper.Property(str)
wrap_s = qt.Wrapper.Property(gx.WrapMode)
wrap_t = qt.Wrapper.Property(gx.WrapMode)
minification_filter = qt.Wrapper.Property(gx.FilterMode)
magnification_filter = qt.Wrapper.Property(gx.FilterMode)
minimum_lod = qt.Wrapper.Property(float)
maximum_lod = qt.Wrapper.Property(float)
lod_bias = qt.Wrapper.Property(float)
unknown0 = qt.Wrapper.Property(int)
unknown1 = qt.Wrapper.Property(int)
unknown2 = qt.Wrapper.Property(int)

def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)

self.wrap_s_changed.connect(self.on_wrap_s_changed)
self.wrap_t_changed.connect(self.on_wrap_t_changed)
self.minification_filter_changed.connect(self.on_minification_filter_changed)
self.magnification_filter_changed.connect(self.on_magnification_filter_changed)
self.minimum_lod_changed.connect(self.on_minimum_lod_changed)
self.maximum_lod_changed.connect(self.on_maximum_lod_changed)
self.lod_bias_changed.connect(self.on_lod_bias_changed)

@QtCore.pyqtSlot(gx.WrapMode)
def on_wrap_s_changed(self,value):
self.wrapped_object.gl_wrap_s_need_update = True

@QtCore.pyqtSlot(gx.WrapMode)
def on_wrap_t_changed(self,value):
self.wrapped_object.gl_wrap_t_need_update = True

@QtCore.pyqtSlot(gx.FilterMode)
def on_minification_filter_changed(self,value):
self.wrapped_object.gl_minification_filter_need_update = True

@QtCore.pyqtSlot(gx.FilterMode)
def on_magnification_filter_changed(self,value):
self.wrapped_object.gl_magnification_filter_need_update = True

@QtCore.pyqtSlot(float)
def on_minimum_lod_changed(self,value):
self.wrapped_object.gl_minimum_lod_need_update = True

@QtCore.pyqtSlot(float)
def on_maximum_lod_changed(self,value):
self.wrapped_object.gl_maximum_lod_need_update = True

@QtCore.pyqtSlot(float)
def on_lod_bias_changed(self,value):
self.wrapped_object.gl_lod_bias_need_update = True


class TextureItem(QtGui.QTreeWidgetItem):

def __init__(self,texture):
super().__init__([texture.name])
super().__init__()
self.texture = None
self.setTexture(texture)

def setTexture(self,texture):
if self.texture is not None:
self.texture.name_changed.disconnect(self.on_texture_name_changed)

self.setText(0,texture.name)
self.texture = texture
self.texture.name_changed.connect(self.on_texture_name_changed)

Expand All @@ -75,18 +28,30 @@ class ExplorerWidget(QtGui.QTreeWidget):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.header().close()
self.currentItemChanged.connect(self.on_currentItemChanged)

self.model = None
self.texture_list = QtGui.QTreeWidgetItem(['Textures'])
self.addTopLevelItem(self.texture_list)
self.currentItemChanged.connect(self.on_currentItemChanged)

def setModel(self,model):
if self.model is not None:
self.model.textures.entry_changed.disconnect(self.on_texture_changed)

self.model = model

self.texture_list.takeChildren()
for texture in model.textures:
self.texture_list.addChild(TextureItem(TextureWrapper(texture)))
self.texture_list.addChildren([TextureItem(texture) for texture in self.model.textures])
self.model.textures.entry_changed.connect(self.on_texture_changed)

@QtCore.pyqtSlot(QtGui.QTreeWidgetItem,QtGui.QTreeWidgetItem)
def on_currentItemChanged(self,current,previous):
if isinstance(current,TextureItem):
self.currentTextureChanged.emit(current.texture)

@QtCore.pyqtSlot(int,object)
def on_texture_changed(self,index,texture):
item = self.texture_list.child(index)
item.setTexture(texture)
if item is self.currentItem():
self.currentTextureChanged.emit(texture)

2 changes: 1 addition & 1 deletion gx/bti.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def pack_textures(stream,textures):
texture_offset = base + i*Texture.sizeof()
texture.image_offset = pack_images(texture.images) - texture_offset

end = stream.tell() #<-?
end = stream.tell()

stream.seek(base)
for texture in textures:
Expand Down
130 changes: 3 additions & 127 deletions j3d/tex1.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from btypes.big_endian import *
import gx
import gx.texture
import gx.bti
import j3d.string_table

import logging
logger = logging.getLogger(__name__)


class Header(Struct):
magic = ByteString(4)
Expand All @@ -26,88 +22,6 @@ def unpack(cls,stream):
return header


class Texture(gx.texture.Texture,Struct):
image_format = EnumConverter(uint8,gx.TextureFormat)
unknown0 = uint8
width = uint16
height = uint16
wrap_s = EnumConverter(uint8,gx.WrapMode)
wrap_t = EnumConverter(uint8,gx.WrapMode)
unknown1 = uint8
palette_format = EnumConverter(uint8,gx.PaletteFormat)
palette_entry_count = uint16
palette_offset = uint32
use_mipmapping = bool8
__padding__ = Padding(3,b'\x00')
minification_filter = EnumConverter(uint8,gx.FilterMode)
magnification_filter = EnumConverter(uint8,gx.FilterMode)
minimum_lod = FixedPointConverter(sint8,1/8)
maximum_lod = FixedPointConverter(sint8,1/8)
level_count = uint8
unknown2 = uint8
lod_bias = FixedPointConverter(sint16,1/100)
image_offset = uint32

def __init__(self):
super().__init__()
self.unknown0 = 1
self.unknown1 = 0
self.unknown2 = 0

@classmethod
def pack(cls,stream,texture):
texture.level_count = len(texture.images)
texture.use_mipmapping = texture.minification_filter in {gx.NEAR_MIP_NEAR,gx.LIN_MIP_NEAR,gx.NEAR_MIP_LIN,gx.LIN_MIP_LIN}
super().pack(stream,texture)

@classmethod
def unpack(cls,stream):
texture = super().unpack(stream)
if texture.unknown0 not in {0x00,0x01,0x02,0xCC}:
logger.warning('unknown0 different from default')
if texture.unknown1 not in {0,1}:
logger.warning('unknown1 different from default')
return texture


class CachedOffsetPacker:

def __init__(self,stream,pack_function,default_offset_table=None):
self.stream = stream
self.pack_function = pack_function
self.offset_table = default_offset_table if default_offset_table is not None else {}

def __call__(self,*args):
if args in self.offset_table:
return self.offset_table[args]

offset = self.stream.tell()
self.pack_function(self.stream,*args)
self.offset_table[args] = offset
return offset


class CachedOffsetUnpacker:

def __init__(self,stream,unpack_function):
self.stream = stream
self.unpack_function = unpack_function
self.argument_table = {}
self.value_table = {}

def __call__(self,offset,*args):
if offset in self.value_table:
if args != self.argument_table[offset]:
raise ValueError('inconsistent arguments for same offset')
return self.value_table[offset]

self.stream.seek(offset)
value = self.unpack_function(self.stream,*args)
self.argument_table[offset] = args
self.value_table[offset] = value
return value


def pack(stream,textures):
base = stream.tell()
header = Header()
Expand All @@ -116,23 +30,7 @@ def pack(stream,textures):

align(stream,0x20)
header.texture_offset = stream.tell() - base
stream.write(b'\x00'*Texture.sizeof()*len(textures))

pack_palette = CachedOffsetPacker(stream,gx.texture.pack_palette)
pack_images = CachedOffsetPacker(stream,gx.texture.pack_images)

for i,texture in enumerate(textures):
texture_offset = base + header.texture_offset + i*Texture.sizeof()

if texture.palette is None:
texture.palette_offset = stream.tell() - texture_offset
continue

texture.palette_offset = pack_palette(texture.palette) - texture_offset

for i,texture in enumerate(textures):
texture_offset = base + header.texture_offset + i*Texture.sizeof()
texture.image_offset = pack_images(texture.images) - texture_offset
gx.bti.pack_textures(stream,textures)

header.name_offset = stream.tell() - base
j3d.string_table.pack(stream,(texture.name for texture in textures))
Expand All @@ -141,11 +39,6 @@ def pack(stream,textures):
header.section_size = stream.tell() - base
stream.seek(base)
Header.pack(stream,header)

stream.seek(base + header.texture_offset)
for texture in textures:
Texture.pack(stream,texture)

stream.seek(base + header.section_size)


Expand All @@ -154,24 +47,7 @@ def unpack(stream):
header = Header.unpack(stream)

stream.seek(base + header.texture_offset)
textures = [Texture.unpack(stream) for _ in range(header.texture_count)]

unpack_palette = CachedOffsetUnpacker(stream,gx.texture.unpack_palette)
unpack_images = CachedOffsetUnpacker(stream,gx.texture.unpack_images)

for i,texture in enumerate(textures):
if texture.palette_entry_count == 0:
texture.palette = None
continue

texture_offset = base + header.texture_offset + i*Texture.sizeof()
palette_offset = texture_offset + texture.palette_offset
texture.palette = unpack_palette(palette_offset,texture.palette_format,texture.palette_entry_count)

for i,texture in enumerate(textures):
texture_offset = base + header.texture_offset + i*Texture.sizeof()
image_offset = texture_offset + texture.image_offset
texture.images = unpack_images(image_offset,texture.image_format,texture.width,texture.height,texture.level_count)
textures = gx.bti.unpack_textures(stream,header.texture_count)

stream.seek(base + header.name_offset)
names = j3d.string_table.unpack(stream)
Expand Down
Loading

0 comments on commit f4dc19e

Please sign in to comment.