# Conflicts: # README.md # io_scene_psk_psa/__init__.py # io_scene_psk_psa/psk/data.py # io_scene_psk_psa/psk/importer.py # io_scene_psk_psa/psk/reader.py
82 lines
3.2 KiB
Python
82 lines
3.2 KiB
Python
import ctypes
|
|
import os
|
|
import re
|
|
import warnings
|
|
from pathlib import Path
|
|
|
|
from .data import *
|
|
|
|
|
|
def _read_types(fp, data_class, section: Section, data):
|
|
buffer_length = section.data_size * section.data_count
|
|
buffer = fp.read(buffer_length)
|
|
offset = 0
|
|
for _ in range(section.data_count):
|
|
data.append(data_class.from_buffer_copy(buffer, offset))
|
|
offset += section.data_size
|
|
|
|
|
|
def _read_material_references(path: str) -> List[str]:
|
|
property_file_path = Path(path).with_suffix('.props.txt')
|
|
if not property_file_path.is_file():
|
|
# Property file does not exist.
|
|
return []
|
|
# Do a crude regex match to find the Material list entries.
|
|
contents = property_file_path.read_text()
|
|
pattern = r"Material\s*=\s*([^\s^,]+)"
|
|
return re.findall(pattern, contents)
|
|
|
|
|
|
def read_psk(path: str) -> Psk:
|
|
|
|
psk = Psk()
|
|
|
|
# Read the PSK file sections.
|
|
with open(path, 'rb') as fp:
|
|
while fp.read(1):
|
|
fp.seek(-1, 1)
|
|
section = Section.from_buffer_copy(fp.read(ctypes.sizeof(Section)))
|
|
if section.name == b'ACTRHEAD':
|
|
pass
|
|
elif section.name == b'PNTS0000':
|
|
_read_types(fp, Vector3, section, psk.points)
|
|
elif section.name == b'VTXW0000':
|
|
if section.data_size == ctypes.sizeof(Psk.Wedge16):
|
|
_read_types(fp, Psk.Wedge16, section, psk.wedges)
|
|
elif section.data_size == ctypes.sizeof(Psk.Wedge32):
|
|
_read_types(fp, Psk.Wedge32, section, psk.wedges)
|
|
else:
|
|
raise RuntimeError('Unrecognized wedge format')
|
|
elif section.name == b'FACE0000':
|
|
_read_types(fp, Psk.Face, section, psk.faces)
|
|
elif section.name == b'MATT0000':
|
|
_read_types(fp, Psk.Material, section, psk.materials)
|
|
elif section.name == b'REFSKELT':
|
|
_read_types(fp, Psk.Bone, section, psk.bones)
|
|
elif section.name == b'RAWWEIGHTS':
|
|
_read_types(fp, Psk.Weight, section, psk.weights)
|
|
elif section.name == b'FACE3200':
|
|
_read_types(fp, Psk.Face32, section, psk.faces)
|
|
elif section.name == b'VERTEXCOLOR':
|
|
_read_types(fp, Color, section, psk.vertex_colors)
|
|
elif section.name.startswith(b'EXTRAUVS'):
|
|
_read_types(fp, Vector2, section, psk.extra_uvs)
|
|
elif section.name == b'VTXNORMS':
|
|
_read_types(fp, Vector3, section, psk.vertex_normals)
|
|
elif section.name == b'MRPHINFO':
|
|
_read_types(fp, Psk.MorphInfo, section, psk.morph_infos)
|
|
elif section.name == b'MRPHDATA':
|
|
_read_types(fp, Psk.MorphData, section, psk.morph_data)
|
|
else:
|
|
# Section is not handled, skip it.
|
|
fp.seek(section.data_size * section.data_count, os.SEEK_CUR)
|
|
warnings.warn(f'Unrecognized section "{section.name} at position {fp.tell():15}"')
|
|
|
|
'''
|
|
UEViewer exports a sidecar file (*.props.txt) with fully-qualified reference paths for each material
|
|
(e.g., Texture'Package.Group.Object').
|
|
'''
|
|
psk.material_references = _read_material_references(path)
|
|
|
|
return psk
|