Skip to content

Commit

Permalink
update version info, use double quotes for all strings
Browse files Browse the repository at this point in the history
  • Loading branch information
matyalatte committed Apr 9, 2023
1 parent 37363f3 commit 8250737
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 347 deletions.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![test](https://github.com/matyalatte/UE4-DDS-tools/actions/workflows/test.yml/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

# UE4-DDS-Tools ver0.5.0
# UE4-DDS-Tools ver0.5.1

Texture modding tools for UE games.
You can inject texture files (.dds, .tga, .hdr, etc.) into UE assets.
Expand Down
1 change: 1 addition & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ver 0.5.1
- Added tooltips to GUI
- Fixed a bug that max size will be uexp's max size.
- Fixed a bug that ubulk flags won't be updated correctly.
- Enabled "skip_non_texture" option and cubic filter as default.

ver 0.5.0
- Supported Texture2DArray, TextureCubeArray, and VolumeTexture
Expand Down
52 changes: 26 additions & 26 deletions src/directx/dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


class PF_FLAGS(IntEnum):
'''dwFlags for DDS_PIXELFORMAT'''
"""dwFlags for DDS_PIXELFORMAT"""
# ALPHAPIXELS = 0x00000001
# ALPHA = 0x00000002
FOURCC = 0x00000004
Expand Down Expand Up @@ -67,7 +67,7 @@ def __init__(self):
self.bit_mask = (c.c_uint32 * 4)((0) * 4)

def get_dxgi(self) -> DXGI_FORMAT:
'''Similar method as GetDXGIFormat in DirectXTex/DDSTextureLoader/DDSTextureLoader12.cpp'''
"""Similar method as GetDXGIFormat in DirectXTex/DDSTextureLoader/DDSTextureLoader12.cpp"""

if not self.is_canonical():
raise RuntimeError(f"Non-standard fourCC detected. ({self.fourCC.decode()})")
Expand Down Expand Up @@ -249,7 +249,7 @@ def is_array(self):


def is_hdr(name: str):
return 'BC6' in name or 'FLOAT' in name or 'INT' in name or 'SNORM' in name
return "BC6" in name or "FLOAT" in name or "INT" in name or "SNORM" in name


def convertible_to_tga(name: str):
Expand All @@ -270,10 +270,10 @@ def read_buffer(f: IOBase, size: int, end_offset: int):


class DDSHeader(c.LittleEndianStructure):
MAGIC = b'DDS '
MAGIC = b"DDS "
_pack_ = 1
_fields_ = [
("magic", c.c_char * 4), # Magic == 'DDS '
("magic", c.c_char * 4), # Magic == "DDS "
("head_size", c.c_uint32), # Size == 124
("flags", c.c_uint32), # DDS_FLAGS
("height", c.c_uint32),
Expand Down Expand Up @@ -334,7 +334,7 @@ def read(f: IOBase) -> "DDSHeader":
@staticmethod
def read_from_file(file_name: str) -> "DDSHeader":
"""Read dds header from a file."""
with open(file_name, 'rb') as f:
with open(file_name, "rb") as f:
head = DDSHeader.read(f)
return head

Expand Down Expand Up @@ -390,16 +390,16 @@ def is_hdr(self):

def is_normals(self):
dxgi = self.get_format_as_str()
return 'BC5' in dxgi or dxgi == 'R8G8_UNORM'
return "BC5" in dxgi or dxgi == "R8G8_UNORM"

def get_format_as_str(self):
return self.dxgi_format.name

def is_srgb(self):
return 'SRGB' in self.dxgi_format.name
return "SRGB" in self.dxgi_format.name

def is_int(self):
return 'UINT' in self.dxgi_format.name or 'SINT' in self.dxgi_format.name
return "UINT" in self.dxgi_format.name or "SINT" in self.dxgi_format.name

def is_canonical(self):
return self.pixel_format.is_canonical()
Expand Down Expand Up @@ -456,24 +456,24 @@ def cail(val, unit):
slice_size += _width * _height * byte_per_pixel
if slice_size != int(slice_size):
raise RuntimeError(
'The size of mipmap data is not int. This is unexpected.'
"The size of mipmap data is not int. This is unexpected."
)
width, height = width // 2, height // 2
width, height = max(block_size, width), max(block_size, height)

return mipmap_size_list, int(slice_size)

def print(self):
print(f' type: {self.get_texture_type()}')
print(f' format: {self.get_format_as_str()}')
print(f' width: {self.width}')
print(f' height: {self.height}')
print(f" type: {self.get_texture_type()}")
print(f" format: {self.get_format_as_str()}")
print(f" width: {self.width}")
print(f" height: {self.height}")
if self.is_3d():
print(f' depth: {self.depth}')
print(f" depth: {self.depth}")
elif self.is_array():
print(f' array_size: {self.get_array_size()}')
print(f" array_size: {self.get_array_size()}")
else:
print(f' mipmaps: {self.mipmap_num}')
print(f" mipmaps: {self.mipmap_num}")

def disassemble(self):
self.update(self.width, self.height, 1, self.mipmap_num, self.dxgi_format, self.is_cube(), 1)
Expand All @@ -493,10 +493,10 @@ def __init__(self, header: DDSHeader, slices: list[bytes] = None, mipmap_sizes:

@staticmethod
def load(file: str, verbose=False):
if file[-3:] not in ['dds', 'DDS']:
raise RuntimeError(f'Not DDS. ({file})')
print('load: ' + file)
with open(file, 'rb') as f:
if file[-3:] not in ["dds", "DDS"]:
raise RuntimeError(f"Not DDS. ({file})")
print("load: " + file)
with open(file, "rb") as f:
end_offset = get_size(f)

# read header
Expand All @@ -521,12 +521,12 @@ def load(file: str, verbose=False):

# save as dds
def save(self, file: str):
print('save: {}'.format(file))
print("save: {}".format(file))
folder = os.path.dirname(file)
if folder not in ['.', ''] and not os.path.exists(folder):
if folder not in [".", ""] and not os.path.exists(folder):
mkdir(folder)

with open(file, 'wb') as f:
with open(file, "wb") as f:
# write header
self.header.write(f)

Expand Down Expand Up @@ -574,6 +574,6 @@ def print(self, verbose):
if verbose:
# print mipmap info
for i, size in zip(range(len(self.mipmap_size_list)), self.mipmap_size_list):
print(f' Mipmap {i}')
print(f" Mipmap {i}")
width, height = size
print(f' size (w, h): ({width}, {height})')
print(f" size (w, h): ({width}, {height})")
28 changes: 14 additions & 14 deletions src/directx/dxgi_format.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'''Constants for DXGI formats
"""Constants for DXGI formats
Notes:
- Official document for DXGI formats
https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
- Official repo for DDS
https://github.com/microsoft/DirectXTex
'''
"""
from enum import IntEnum


Expand Down Expand Up @@ -302,18 +302,18 @@ def int_to_byte(n):

# Used to detect DXGI format from fourCC
FOURCC_TO_DXGI = [
[[b'DXT1'], DXGI_FORMAT.BC1_UNORM],
[[b'DXT2', b'DXT3'], DXGI_FORMAT.BC2_UNORM],
[[b'DXT4', b'DXT5'], DXGI_FORMAT.BC3_UNORM],
[[b'ATI1', b'BC4U', b'3DC1'], DXGI_FORMAT.BC4_UNORM],
[[b'ATI2', b'BC5U', b'3DC2'], DXGI_FORMAT.BC5_UNORM],
[[b'BC4S'], DXGI_FORMAT.BC4_SNORM],
[[b'BC5S'], DXGI_FORMAT.BC5_SNORM],
[[b'BC6H'], DXGI_FORMAT.BC6H_UF16],
[[b'BC7L', b'BC7'], DXGI_FORMAT.BC7_UNORM],
[[b'RGBG'], DXGI_FORMAT.R8G8_B8G8_UNORM],
[[b'GRGB'], DXGI_FORMAT.G8R8_G8B8_UNORM],
[[b'YUY2', b'UYVY'], DXGI_FORMAT.YUY2],
[[b"DXT1"], DXGI_FORMAT.BC1_UNORM],
[[b"DXT2", b"DXT3"], DXGI_FORMAT.BC2_UNORM],
[[b"DXT4", b"DXT5"], DXGI_FORMAT.BC3_UNORM],
[[b"ATI1", b"BC4U", b"3DC1"], DXGI_FORMAT.BC4_UNORM],
[[b"ATI2", b"BC5U", b"3DC2"], DXGI_FORMAT.BC5_UNORM],
[[b"BC4S"], DXGI_FORMAT.BC4_SNORM],
[[b"BC5S"], DXGI_FORMAT.BC5_SNORM],
[[b"BC6H"], DXGI_FORMAT.BC6H_UF16],
[[b"BC7L", b"BC7"], DXGI_FORMAT.BC7_UNORM],
[[b"RGBG"], DXGI_FORMAT.R8G8_B8G8_UNORM],
[[b"GRGB"], DXGI_FORMAT.G8R8_G8B8_UNORM],
[[b"YUY2", b"UYVY"], DXGI_FORMAT.YUY2],
[[int_to_byte(36)], DXGI_FORMAT.R16G16B16A16_UNORM],
[[int_to_byte(110)], DXGI_FORMAT.R16G16B16A16_SNORM],
[[int_to_byte(111)], DXGI_FORMAT.R16_FLOAT],
Expand Down
62 changes: 31 additions & 31 deletions src/directx/texconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ def get_os_name():


def is_windows():
return get_os_name() == 'Windows'
return get_os_name() == "Windows"


def is_linux():
return get_os_name() == 'Linux'
return get_os_name() == "Linux"


def is_mac():
return get_os_name() == 'Darwin'
return get_os_name() == "Darwin"


class Texconv:
Expand All @@ -46,7 +46,7 @@ def load_dll(self, dll_path=None, com_initialized=False):
elif is_linux():
dll_name = "libtexconv.so"
else:
raise RuntimeError(f'This OS ({get_os_name()}) is unsupported.')
raise RuntimeError(f"This OS ({get_os_name()}) is unsupported.")
dirname = os.path.dirname(file_path)
dll_path = os.path.join(dirname, dll_name)
dll_path2 = os.path.join(os.path.dirname(dirname), dll_name) # allow ../texconv.dll
Expand All @@ -55,7 +55,7 @@ def load_dll(self, dll_path=None, com_initialized=False):
if os.path.exists(dll_path2):
dll_path = dll_path2
else:
raise RuntimeError(f'texconv not found. ({dll_path})')
raise RuntimeError(f"texconv not found. ({dll_path})")

self.dll = ctypes.cdll.LoadLibrary(dll_path)
self.com_initialized = com_initialized
Expand Down Expand Up @@ -85,35 +85,35 @@ def convert_dds_to(self, file: str, out=None, fmt="tga",
return name

if verbose:
print(f'DXGI_FORMAT: {dds_header.get_format_as_str()}')
print(f"DXGI_FORMAT: {dds_header.get_format_as_str()}")

args = []

if dds_header.is_hdr():
ext = 'hdr'
ext = "hdr"
if fmt == "tga":
fmt = ext
if not dds_header.convertible_to_hdr():
args += ['-f', 'fp32']
args += ["-f", "fp32"]
else:
ext = "tga"
if not dds_header.convertible_to_tga():
args += ['-f', 'rgba']
args += ["-f", "rgba"]

if dds_header.is_int():
msg = f'Int format detected. ({dds_header.get_format_as_str()})\n It might not be converted correctly.'
msg = f"Int format detected. ({dds_header.get_format_as_str()})\n It might not be converted correctly."
print(msg)

args2 = ['-ft', fmt]
args2 = ["-ft", fmt]

if dds_header.is_normals():
args2 += ['-reconstructz']
args2 += ["-reconstructz"]
if invert_normals:
args2 += ['-inverty']
args2 += ["-inverty"]

if dds_header.is_cube():
name = os.path.join(out, os.path.basename(file))
name = '.'.join(name.split('.')[:-1] + [fmt])
name = ".".join(name.split(".")[:-1] + [fmt])
temp = ".".join(file.split(".")[:-1] + [ext])
self.__cube_to_image(file, temp, args, cubemap_layout=cubemap_layout, verbose=verbose)
if fmt == ext:
Expand All @@ -123,7 +123,7 @@ def convert_dds_to(self, file: str, out=None, fmt="tga",
else:
out = self.__texconv(file, args + args2, out=out, verbose=verbose)
name = os.path.join(out, os.path.basename(file))
name = '.'.join(name.split('.')[:-1] + [fmt])
name = ".".join(name.split(".")[:-1] + [fmt])
return name

def convert_to_dds(self, file: str, dxgi_format: DXGI_FORMAT, out=None,
Expand All @@ -136,37 +136,37 @@ def convert_to_dds(self, file: str, dxgi_format: DXGI_FORMAT, out=None,

dds_fmt = dxgi_format.name

if ('BC6' in dds_fmt or 'BC7' in dds_fmt) and (not is_windows()) and (not allow_slow_codec):
raise RuntimeError(f'Can NOT use CPU codec for {dds_fmt}. Or enable the "Allow Slow Codec" option.')
if ("BC6" in dds_fmt or "BC7" in dds_fmt) and (not is_windows()) and (not allow_slow_codec):
raise RuntimeError(f"Can NOT use CPU codec for {dds_fmt}. Or enable the 'Allow Slow Codec' option.")
if dxgi_format.value > DXGI_FORMAT.get_max_canonical():
raise RuntimeError(
f"DDS converter does NOT support {dds_fmt}.\n"
"You should convert it to dds with another tool first."
)

if not DXGI_FORMAT.is_valid_format(dds_fmt):
raise RuntimeError(f'Not DXGI format. ({dds_fmt})')
raise RuntimeError(f"Not DXGI format. ({dds_fmt})")

if verbose:
print(f'DXGI_FORMAT: {dds_fmt}')
print(f"DXGI_FORMAT: {dds_fmt}")

base_name = os.path.basename(file)
base_name = '.'.join(base_name.split('.')[:-1] + ['dds'])
base_name = ".".join(base_name.split(".")[:-1] + ["dds"])

args = ['-f', dds_fmt]
args = ["-f", dds_fmt]
if no_mip:
args += ['-m', '1']
args += ["-m", "1"]
if image_filter.upper() != "LINEAR":
args += ["-if", image_filter.upper()]

if ("BC5" in dds_fmt or dds_fmt == "R8G8_UNORM") and invert_normals:
args += ['-inverty']
args += ["-inverty"]

if export_as_cubemap:
if is_hdr(dds_fmt):
temp_args = ['-f', 'fp32']
temp_args = ["-f", "fp32"]
else:
temp_args = ['-f', 'rgba']
temp_args = ["-f", "rgba"]
with tempfile.TemporaryDirectory() as temp_dir:
temp = os.path.join(temp_dir, base_name)
self.__image_to_cube(file, temp, temp_args, cubemap_layout=cubemap_layout, verbose=verbose)
Expand All @@ -178,20 +178,20 @@ def convert_to_dds(self, file: str, dxgi_format: DXGI_FORMAT, out=None,

def convert_nondds(self, file: str, out=None, fmt="tga", verbose=True):
"""Convert non-dds to non-dds."""
out = self.__texconv(file, ['-ft', fmt], out=out, verbose=verbose)
out = self.__texconv(file, ["-ft", fmt], out=out, verbose=verbose)
name = os.path.join(out, os.path.basename(file))
name = '.'.join(name.split('.')[:-1] + [fmt])
name = ".".join(name.split(".")[:-1] + [fmt])
return name

def __texconv(self, file: str, args: list[str],
out=None, verbose=True, allow_slow_codec=False):
"""Run texconv."""
if out is not None and isinstance(out, str):
args += ['-o', out]
args += ["-o", out]
else:
out = '.'
out = "."

if out not in ['.', ''] and not os.path.exists(out):
if out not in [".", ""] and not os.path.exists(out):
mkdir(out)

args += ["-y"]
Expand Down Expand Up @@ -226,7 +226,7 @@ def __image_to_cube(self, file: str, new_file: str, args: list[str],
def __texassemble(self, file: str, new_file: str, args: list[str], verbose=True):
"""Run texassemble."""
out = os.path.dirname(new_file)
if out not in ['.', ''] and not os.path.exists(out):
if out not in [".", ""] and not os.path.exists(out):
mkdir(out)
args += ["-y", "-o", new_file, file]

Expand Down
Loading

0 comments on commit 8250737

Please sign in to comment.