diff --git a/Scripts/dds.py b/Scripts/dds.py index 58ece7f..792e2fd 100644 --- a/Scripts/dds.py +++ b/Scripts/dds.py @@ -1,33 +1,139 @@ import subprocess +import os +import typing +import struct import re +import io +import enum + +if os.name == 'nt': + convertcmd = "convert.exe" + compresscmd = "nvcompress.exe" + decompresscmd = "nvdecompress.exe" + infocmd = "nvddsinfo.exe" +else: + convertcmd = "convert" + compresscmd = "nvcompress" + decompresscmd = "nvdecompress" + infocmd = "nvddsinfo" def alpha(file): - result = subprocess.run([convertcmd, file, "-resize", "1x1", "-format", "%[fx:int(255*a+.5)]", "info:-"], capture_output=True) + result = subprocess.run([convertcmd, file, "-resize", "1x1", + "-format", "%[fx:int(255*a+.5)]", "info:-"], capture_output=True) result.check_returncode() return int(result.stdout) + def flip(file, output): - result = subprocess.run([convertcmd, '-flip', file, output], capture_output=True) + result = subprocess.run( + [convertcmd, '-flip', file, output], capture_output=True) result.check_returncode() - -def nvcompress(format, file, output, mips = True): + + +def nvcompress(format, file, output, mips=True): args = [format] if not mips: args.append('-nomips') - - result = subprocess.run([compresscmd] + args + [file, output], capture_output=True) + + result = subprocess.run([compresscmd] + args + + [file, output], capture_output=True) result.check_returncode() - + + def nvdecompress(file, output): result = subprocess.run([decompresscmd, file, output], capture_output=True) result.check_returncode() + def nvinfo(file): result = subprocess.run([infocmd, file], capture_output=True) result.check_returncode() - + info = { "format": re.search(r"FourCC: '(.{4})'", str(result.stdout)).group(1) } - - return info \ No newline at end of file + + return info + +class DDSFlags(enum.IntFlag): + CAPS = 0x1 + HEIGHT = 0x2 + WIDTH = 0x4 + PITCH = 0x8 + PIXEL_FORMAT = 0x1000 + MIPMAPCOUNT = 0x20000 + LINEAR_SIZE = 0x80000 + DEPTH = 0x800000 + +class DDSPixelFlags(enum.IntFlag): + ALPHAPIXELS = 0x1 + ALPHA = 0x2 + FOURCC = 0x4 + RGB = 0x40 + YUV = 0x200 + LUMINANCE = 0x20000 + +class DDSHeader: + def __init__(self, f): + self.read(f) + + def read(self, f): + if f.read(4).decode() != "DDS ": + raise ValueError( + "File is not a valid DDS f: Incorrect magic bytes") + + dwords = struct.unpack('20I4s10I', f.read(124)) + + # check constants + if dwords[0] != 124: + raise ValueError( + "File is not a valid DDS file: Incorrect header size") + if dwords[18] != 32: + raise ValueError( + "File is not a valid DDS file: Incorrect pixelformat size") + + # read basic info + self.flags = DDSFlags(dwords[1]) + self.height, self.width = dwords[2:4] + + if DDSFlags.PITCH in self.flags: + self.pitch = dwords[4] + else: + self.linear_size = dwords[4] + + if DDSFlags.DEPTH in self.flags: + self.depth = dwords[5] + + if DDSFlags.MIPMAPCOUNT in self.flags: + self.mipmapcount = dwords[6] + + # read pixelformat + self.pixel_flags = DDSPixelFlags(dwords[19]) + + if DDSPixelFlags.ALPHAPIXELS in self.pixel_flags: + # texture has alpha data + self.a_bitmask = dwords[25] + + if DDSPixelFlags.FOURCC in self.pixel_flags: + # texture is compressed, four_cc contains the format + self.format = dwords[20].decode() + else: + self.format = 'uncompressed' + if (DDSPixelFlags.RGB | DDSPixelFlags.YUV) & self.pixel_flags: + # uncompressed RGB texture + self.rgb_bit_count, self.r_bitmask, self.g_bitmask, self.b_bitmask = dwords[21:25] + elif DDSPixelFlags.ALPHA in self.pixel_flags: + # uncompressed alpha-only texture + self.rgb_bit_count = dwords[21] + self.a_bitmask = dwords[25] + elif DDSPixelFlags.LUMINANCE in self.pixel_flags: + # uncompressed luminance-only texture + self.rgb_bit_count = dwords[21] + self.r_bitmask = dwords[22] + + # read caps + self.dw_caps, self.dw_caps2, self.dw_caps3, self.dw_caps4 = dwords[26:30] # read dwCaps flags + + if self.format == 'DX10': + dwords = f.read(20) + #TODO: actually read the DX10 data \ No newline at end of file