add dds header parsing and info tool
This commit is contained in:
parent
e410f0c15c
commit
cdea4674f8
114
Scripts/dds.py
114
Scripts/dds.py
@ -1,27 +1,50 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
import os
|
||||||
|
import typing
|
||||||
|
import struct
|
||||||
import re
|
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):
|
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()
|
result.check_returncode()
|
||||||
return int(result.stdout)
|
return int(result.stdout)
|
||||||
|
|
||||||
|
|
||||||
def flip(file, output):
|
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()
|
result.check_returncode()
|
||||||
|
|
||||||
def nvcompress(format, file, output, mips = True):
|
|
||||||
|
def nvcompress(format, file, output, mips=True):
|
||||||
args = [format]
|
args = [format]
|
||||||
if not mips:
|
if not mips:
|
||||||
args.append('-nomips')
|
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()
|
result.check_returncode()
|
||||||
|
|
||||||
|
|
||||||
def nvdecompress(file, output):
|
def nvdecompress(file, output):
|
||||||
result = subprocess.run([decompresscmd, file, output], capture_output=True)
|
result = subprocess.run([decompresscmd, file, output], capture_output=True)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
|
|
||||||
|
|
||||||
def nvinfo(file):
|
def nvinfo(file):
|
||||||
result = subprocess.run([infocmd, file], capture_output=True)
|
result = subprocess.run([infocmd, file], capture_output=True)
|
||||||
result.check_returncode()
|
result.check_returncode()
|
||||||
@ -31,3 +54,86 @@ def nvinfo(file):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return info
|
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
|
Loading…
Reference in New Issue
Block a user