diff --git a/GLLara.xcodeproj/project.pbxproj b/GLLara.xcodeproj/project.pbxproj index cd7f3dc..2e0709e 100644 --- a/GLLara.xcodeproj/project.pbxproj +++ b/GLLara.xcodeproj/project.pbxproj @@ -20,7 +20,7 @@ 5214470A16DBF206003E260F /* GLLItemMesh+MeshExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5214470916DBF206003E260F /* GLLItemMesh+MeshExport.swift */; }; 5214470D16DC2312003E260F /* GLLItem+MeshExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5214470C16DC2312003E260F /* GLLItem+MeshExport.swift */; }; 5214471116DE22E1003E260F /* GLLara.help in Resources */ = {isa = PBXBuildFile; fileRef = 5214471016DE22E0003E260F /* GLLara.help */; }; - 52152CEF16B66951001AE54C /* GLLDDSFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 52152CEE16B66951001AE54C /* GLLDDSFile.m */; }; + 52152CEF16B66951001AE54C /* GLLDDSFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52152CEE16B66951001AE54C /* GLLDDSFile.swift */; }; 52232EAE1EFC57D4007FE9AD /* XYAlignedSquare.obj in Resources */ = {isa = PBXBuildFile; fileRef = 52232EAD1EFC57D4007FE9AD /* XYAlignedSquare.obj */; }; 5224C8E615FA4F18002A6C76 /* GLLDirectionalLight.m in Sources */ = {isa = PBXBuildFile; fileRef = 5224C8E515FA4F18002A6C76 /* GLLDirectionalLight.m */; }; 5224C8EE15FA8FB0002A6C76 /* GLLAngleRangeValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5224C8ED15FA8FB0002A6C76 /* GLLAngleRangeValueTransformer.m */; }; @@ -406,8 +406,7 @@ 5214470916DBF206003E260F /* GLLItemMesh+MeshExport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GLLItemMesh+MeshExport.swift"; sourceTree = ""; }; 5214470C16DC2312003E260F /* GLLItem+MeshExport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GLLItem+MeshExport.swift"; sourceTree = ""; }; 5214471016DE22E0003E260F /* GLLara.help */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GLLara.help; sourceTree = ""; }; - 52152CED16B66951001AE54C /* GLLDDSFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLLDDSFile.h; sourceTree = ""; }; - 52152CEE16B66951001AE54C /* GLLDDSFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLLDDSFile.m; sourceTree = ""; }; + 52152CEE16B66951001AE54C /* GLLDDSFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GLLDDSFile.swift; sourceTree = ""; }; 52232EAD1EFC57D4007FE9AD /* XYAlignedSquare.obj */ = {isa = PBXFileReference; lastKnownFileType = text; name = XYAlignedSquare.obj; path = Shaders/XYAlignedSquare.obj; sourceTree = ""; }; 5224C8E415FA4F18002A6C76 /* GLLDirectionalLight.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLLDirectionalLight.h; sourceTree = ""; }; 5224C8E515FA4F18002A6C76 /* GLLDirectionalLight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLLDirectionalLight.m; sourceTree = ""; }; @@ -1404,8 +1403,7 @@ children = ( 529692E715F28BFB00DF2FA3 /* GLLTexture.h */, 529692E815F28BFB00DF2FA3 /* GLLTexture.m */, - 52152CED16B66951001AE54C /* GLLDDSFile.h */, - 52152CEE16B66951001AE54C /* GLLDDSFile.m */, + 52152CEE16B66951001AE54C /* GLLDDSFile.swift */, 52C516FE2871998C000EB8C2 /* GLLPipelineStateInformation.swift */, 52CDFEA3287369B100BC4298 /* GLLVertexAttribAccessor.swift */, 52C6115A2877080900ED8112 /* GLLResourceManager.swift */, @@ -1965,7 +1963,7 @@ 528934EE16A3593500F05312 /* GLLBoneListController.m in Sources */, 523BBB01287E94EF00B2D52E /* GLLControllerPreferencesView.swift in Sources */, 521102ED2897E9A8001BE4BC /* HUDBoneNames.swift in Sources */, - 52152CEF16B66951001AE54C /* GLLDDSFile.m in Sources */, + 52152CEF16B66951001AE54C /* GLLDDSFile.swift in Sources */, 52D8DDF72621E67F0006F0E5 /* GLLShaderDescription.swift in Sources */, 52C3AD8F29A224E2002EC334 /* GLLModelBone.swift in Sources */, 52653D8116BAB729001D802F /* GLLPoseExporter.m in Sources */, diff --git a/GLLara/GLLDDSFile.h b/GLLara/GLLDDSFile.h deleted file mode 100644 index 8625801..0000000 --- a/GLLara/GLLDDSFile.h +++ /dev/null @@ -1,49 +0,0 @@ -// -// GLLDDSFile.h -// GLLara -// -// Created by Torsten Kammer on 28.01.13. -// Copyright (c) 2013 Torsten Kammer. All rights reserved. -// - -#import - -enum GLLDDSDataFormat -{ - GLL_DDS_UNKNOWN, - GLL_DDS_DXT1, - GLL_DDS_DXT3, - GLL_DDS_DXT5, - GLL_DDS_ARGB_1555, - GLL_DDS_ARGB_4, - GLL_DDS_RGB_565, - GLL_DDS_BGR_8, - - GLL_DDS_BGRA_8, - GLL_DDS_RGBA_8, - GLL_DDS_BGRX_8 - -}; - -/*! - * @abstract Parses DDS files. - * @discussions Much of this is based on older code that was plain C and had - * become too hard to maintain, and especially add error support to. This class - * does not handle decompression and the like; it only provides the data to be - * loaded into OpenGL. - */ -@interface GLLDDSFile : NSObject - -- (id)initWithContentsOfURL:(NSURL *)url error:(NSError *__autoreleasing *)error; -- (id)initWithData:(NSData *)data error:(NSError *__autoreleasing*)error; - -@property (readonly, nonatomic) NSUInteger width; -@property (readonly, nonatomic) NSUInteger height; -@property (readonly, nonatomic) BOOL hasMipmaps; -@property (readonly, nonatomic) NSUInteger numMipmaps; -@property (readonly, nonatomic) BOOL isCompressed; -@property (readonly, nonatomic) enum GLLDDSDataFormat dataFormat; - -- (NSData *)dataForMipmapLevel:(NSUInteger)level; - -@end diff --git a/GLLara/GLLDDSFile.m b/GLLara/GLLDDSFile.m deleted file mode 100644 index 4852b0c..0000000 --- a/GLLara/GLLDDSFile.m +++ /dev/null @@ -1,325 +0,0 @@ -// -// GLLDDSFile.m -// GLLara -// -// Created by Torsten Kammer on 28.01.13. -// Copyright (c) 2013 Torsten Kammer. All rights reserved. -// - -#import "GLLDDSFile.h" - -// All information about the DDS file format is taken from -// http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/graphics/reference/ddsfilereference/ddsfileformat.asp - -struct __DDSFile -{ - enum GLLDDSDataFormat format; - CFIndex width; - CFIndex height; - CFIndex numMipmapLevels; - CFDataRef data; -}; - -struct DDSPixelFormat -{ - uint32_t size; - uint32_t flags; - union { - uint32_t fourCC; - uint8_t fourCCString[4]; - }; - uint32_t rgbBitCount; - uint32_t rBitMask; - uint32_t gBitMask; - uint32_t bBitMask; - uint32_t aBitMask; -}; - -struct DDSCaps -{ - uint32_t caps1; - uint32_t caps2; - uint32_t reserved[2]; -}; - -struct DDSFileHeader -{ - uint32_t size; - uint32_t flags; - uint32_t height; - uint32_t width; - uint32_t pitchOrLinearSize; - uint32_t depth; - uint32_t mipMapCount; - uint32_t reserved1[11]; - struct DDSPixelFormat pixelFormat; - struct DDSCaps caps; - uint32_t reserved2; -}; - -static NSString *ddsError = @"DDS File Loading"; - -@interface GLLDDSFile () -{ - NSData *fileData; -} - -@end - -@implementation GLLDDSFile - -- (id)initWithContentsOfURL:(NSURL *)url error:(NSError *__autoreleasing *)error -{ - NSData *data = [NSData dataWithContentsOfURL:url options:0 error:error]; - if (!data) return nil; - - return [self initWithData:data error:error]; -} - -- (id)initWithData:(NSData *)data error:(NSError *__autoreleasing *)error -{ - if (!(self = [super init])) return nil; - - const void *fileContents = data.bytes; - if (memcmp(fileContents, "DDS ", 4) != 0) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"This DDS file is corrupt.", @"DDS: Does not start with DDS"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file cannot be opened because it has an incorrect start sequence. This usually indicates that the file is damaged or not a DDS file at all.", @"DDS: Does not start with DDS")}]; - ; - return nil; - } - - struct DDSFileHeader header; - memcpy((void *) &header, fileContents + 4, sizeof(header)); - if (header.size != 124) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:2 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"This DDS file is not supported.", @"DDS: Header size wrong"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file cannot be read because it uses a different header size than normal. This may be because it uses a newer version of the file format, or because it is damaged.", @"DDS: Header size wrong")}]; - - return nil; - } - if (header.pixelFormat.size != 32) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:3 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"This DDS file is not supported.", @"DDS: Pixel format size wrong"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file cannot be read because it uses a different pixel format size than normal. This may be because it uses a newer version of the file format, or because it is damaged.", @"DDS: Pixel format size wrong")}]; - - return nil; - } - - // Find the file's format - if (header.pixelFormat.flags & 4) // Use the FourCC - { - if (header.pixelFormat.fourCC == NSSwapBigIntToHost('DXT1')) - _dataFormat = GLL_DDS_DXT1; - else if (header.pixelFormat.fourCC == NSSwapBigIntToHost('DXT3')) - _dataFormat = GLL_DDS_DXT3; - else if (header.pixelFormat.fourCC == NSSwapBigIntToHost('DXT5')) - _dataFormat = GLL_DDS_DXT5; - else - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"Graphics format %4s is not supported.", @"DDS: Unknown FourCC"), header.pixelFormat.fourCCString], - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file cannot be read because it uses a compressed data format that is not supported. Only DXT1, DXT3 and DXT5 formats are supported.", @"DDS: Unknown FourCC")}]; - - return nil; - } - } - else if (header.pixelFormat.flags & 64) // Use RGB - { - if (header.pixelFormat.flags & 1) // Contains alpha - { - if (header.pixelFormat.rgbBitCount == 32) - { - // Mentally swap the constants! - if ((header.pixelFormat.aBitMask == 0xff000000) && - (header.pixelFormat.rBitMask == 0x00ff0000) && - (header.pixelFormat.gBitMask == 0x0000ff00) && - (header.pixelFormat.bBitMask == 0x000000ff)) { - _dataFormat = GLL_DDS_BGRA_8; - } else if ((header.pixelFormat.aBitMask == 0xff000000) && - (header.pixelFormat.rBitMask == 0x000000ff) && - (header.pixelFormat.gBitMask == 0x0000ff00) && - (header.pixelFormat.bBitMask == 0x00ff0000)) { - _dataFormat = GLL_DDS_RGBA_8; - } else { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"Graphics format is not supported.", @"DDS: Unknown 32-bit alpha format"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Only ARGB or ABGR is supported for 32 bit uncompressed textures.", @"DDS: Unknown 32-bit format")}]; - return nil; - } - } - else if (header.pixelFormat.rgbBitCount == 16) - { - // It can be ARGB4 (what format is that? never heard of it) or ARGB1555. - if (header.pixelFormat.aBitMask == 0x8000 && header.pixelFormat.rBitMask == 0x7C00 && header.pixelFormat.gBitMask == 0x03E0 && header.pixelFormat.bBitMask == 0x001F) - _dataFormat = GLL_DDS_ARGB_1555; - else if (header.pixelFormat.aBitMask == 0xF000 && header.pixelFormat.rBitMask == 0x0F00 && header.pixelFormat.gBitMask == 0x00F0 && header.pixelFormat.bBitMask == 0x000F) - _dataFormat = GLL_DDS_ARGB_4; - else - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"Graphics format is not supported.", @"DDS: Unknown 16-bit alpha format"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Only ARGB4 and ARGB1555 are supported for 16 bit uncompressed textures with alpha channel.", @"DDS: Unknown 16-bit alpha format")}]; - return nil; - } - } - else - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"%u bits per pixel with alpha are not supported.", @"DDS: Unknown alpha bitcount"), header.pixelFormat.rgbBitCount], - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Uncompressed formats with alpha have to be 32 or 16 bits per pixel.", @"DDS: Unknown alpha bitcount")}]; - return nil; - } - - } - else // No alpha - { - if (header.pixelFormat.rgbBitCount == 32) - { - // To be supported, it has to be ?RGB32 now. - if (!((header.pixelFormat.rBitMask == 0x00ff0000) && - (header.pixelFormat.gBitMask == 0x0000ff00) && - (header.pixelFormat.bBitMask == 0x000000ff))) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"Graphics format is not supported.", @"DDS: Unknown 32-bit alpha format"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Only XRGB is supported for 32 bit uncompressed textures with alpha.", @"DDS: Unknown 32-bit alpha format")}]; - return nil; - } - _dataFormat = GLL_DDS_BGRX_8; - } - else if (header.pixelFormat.rgbBitCount == 24) - { - // To be supported, it has to be RGB32 now. - if (!((header.pixelFormat.rBitMask == 0x00ff0000) && - (header.pixelFormat.gBitMask == 0x0000ff00) && - (header.pixelFormat.bBitMask == 0x000000ff))) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"Graphics format is not supported.", @"DDS: Unknown 24-bit non-alpha format"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Only RGB is supported for 24 bit uncompressed textures.", @"DDS: Unknown 24-bit non-alpha format")}]; - return nil; - } - _dataFormat = GLL_DDS_BGR_8; - } - else if (header.pixelFormat.rgbBitCount == 16) - { - // To be supported, it has to be RGB565 now. - if (!((header.pixelFormat.rBitMask == 0xF800) && - (header.pixelFormat.gBitMask == 0x7E0) && - (header.pixelFormat.bBitMask == 0x001F))) - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString(@"Graphics format is not supported.", @"DDS: Unknown 16-bit non-alpha format"), - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Only RGB565 is supported for 16 bit uncompressed textures without alpha.", @"DDS: Unknown 16-bit non-alpha format")}]; - return nil; - } - _dataFormat = GLL_DDS_RGB_565; - } - else - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"%u bits per pixel without alpha are not supported.", @"DDS: Unknown non-alpha bitcount"), header.pixelFormat.rgbBitCount], - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"The file uses a data layout that is not supported. Uncompressed formats without alpha have to be 32, 24 or 16 bits per pixel.", @"DDS: Unknown non-alpha bitcount")}]; - return nil; - - }; - } - } - else - { - if (error) - *error = [NSError errorWithDomain:ddsError code:1 userInfo:@{ - NSLocalizedDescriptionKey : [NSString stringWithFormat:NSLocalizedString(@"The file's graphics format is not supported.", @"DDS: Unknown graphics format"), header.pixelFormat.rgbBitCount], - NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString(@"Only DXT1,3,5 and uncompressed (A)RGB formats are supported.", @"DDS: Unknown graphics format")}]; - return nil; - } - - _width = header.width; - _height = header.height; - _numMipmaps = header.mipMapCount; - - fileData = data; - - return self; -} - -- (NSData *)dataForMipmapLevel:(NSUInteger)level; -{ - NSUInteger size = 0; - NSUInteger offset = 0; - NSUInteger height = self.height; - NSUInteger width = self.width; - NSUInteger i; - for (i = 0; i <= level && (width || height); ++i) - { - if (width == 0) - width = 1; - if (height == 0) - height = 1; - - offset += size; - - switch(self.dataFormat) - { - case GLL_DDS_DXT1: - size = ((width+3)/4)*((height+3)/4)*8; - break; - case GLL_DDS_DXT3: - case GLL_DDS_DXT5: - size = ((width+3)/4)*((height+3)/4)*16; - break; - case GLL_DDS_ARGB_1555: - case GLL_DDS_ARGB_4: - case GLL_DDS_RGB_565: - size = (width * height) * 2; - break; - case GLL_DDS_BGR_8: - size = (width * height) * 3; - break; - case GLL_DDS_RGBA_8: - case GLL_DDS_BGRA_8: - case GLL_DDS_BGRX_8: - size = (width * height) * 4; - break; - default: - [NSException raise:NSInternalInconsistencyException format:@"Size for format %lu unknown", (NSUInteger) self.dataFormat]; - } - width >>= 1; - height >>= 1; - } - if (size == 0) return NULL; - - NSUInteger newDataStart = 4 + sizeof(struct DDSFileHeader) + offset; - - if (newDataStart + size > fileData.length) - return NULL; - - return [fileData subdataWithRange:NSMakeRange(newDataStart, size)]; -} - -- (BOOL)isCompressed -{ - return (self.dataFormat == GLL_DDS_DXT1 || self.dataFormat == GLL_DDS_DXT3 || self.dataFormat == GLL_DDS_DXT5); -} - -- (BOOL)hasMipmaps -{ - return self.numMipmaps > 0; -} - -@end diff --git a/GLLara/GLLDDSFile.swift b/GLLara/GLLDDSFile.swift new file mode 100644 index 0000000..a079164 --- /dev/null +++ b/GLLara/GLLDDSFile.swift @@ -0,0 +1,239 @@ +// +// GLLDDSFile.swift +// GLLara +// +// Created by Torsten Kammer on 28.01.13. +// Copyright (c) 2013 Torsten Kammer. All rights reserved. +// + +import Foundation + +// All information about the DDS file format is taken from +// http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/graphics/reference/ddsfilereference/ddsfileformat.asp + +@objc enum GLLDDSDataFormat: Int +{ + case dxt1 + case dxt3 + case dxt5 + case argb1555 + case argb4 + case rgb565 + case bgr8 + case bgra8 + case rgba8 + case bgrx8 +} + +/** + * # Parses DDS files. + * + * Much of this is based on older code that was plain C and had + * become too hard to maintain, and especially add error support to. This class + * does not handle decompression and the like; it only provides the data to be + * loaded into the GPU. + */ +@objc class GLLDDSFile: NSObject { + struct DDSPixelFormat { + var size: UInt32 = 0 + var flags: UInt32 = 0 + var fourCC: (UInt8, UInt8, UInt8, UInt8) = (0, 0, 0, 0) + var rgbBitCount: UInt32 = 0 + var rBitMask: UInt32 = 0 + var gBitMask: UInt32 = 0 + var bBitMask: UInt32 = 0 + var aBitMask: UInt32 = 0 + } + + struct DDSCaps { + var caps1: UInt32 = 0 + var caps2: UInt32 = 0 + var reserved1: UInt32 = 0 + var reserved2: UInt32 = 0 + } + + struct DDSFileHeader { + var size: UInt32 = 0 + var flags: UInt32 = 0 + var height: UInt32 = 0 + var width: UInt32 = 0 + var pitchOrLinearSize: UInt32 = 0 + var depth: UInt32 = 0 + var mipMapCount: UInt32 = 0 + var reserved1: (UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32) = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + var pixelFormat = DDSPixelFormat() + var caps = DDSCaps() + var reserved2: UInt32 = 0 + } + + @objc let fileData: Data + @objc let width: Int + @objc let height: Int + @objc let numMipmaps: Int + @objc let dataFormat: GLLDDSDataFormat + + @objc var hasMipmaps: Bool { + return numMipmaps > 0 + } + @objc var isCompressed: Bool { + return dataFormat == .dxt1 || dataFormat == .dxt3 || dataFormat == .dxt5 + } + + @objc convenience init(contentsOf: URL) throws { + let data = try Data(contentsOf: contentsOf, options: [.mappedIfSafe]) + try self.init(data: data) + } + + @objc init(data: Data) throws { + self.fileData = data + + guard data.count >= 128 && data[0] == Character("D").asciiValue! && data[1] == Character("D").asciiValue! && data[2] == Character("S").asciiValue! && data[3] == Character(" ").asciiValue! else { + throw NSError(domain:"ddsError", code:1, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("This DDS file is corrupt.", comment: "DDS: Does not start with DDS"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file cannot be opened because it has an incorrect start sequence. This usually indicates that the file is damaged or not a DDS file at all.", comment:"DDS: Does not start with DDS")]); + } + var header = DDSFileHeader() + _ = withUnsafeMutableBytes(of: &header) { + data.copyBytes(to: $0, from: 4 ..< MemoryLayout.size + 4) + } + if header.size != 124 { + throw NSError(domain:"ddsError", code:2, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("This DDS file is not supported.", comment: "DDS: Header size wrong"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file cannot be read because it uses a different header size than normal. This may be because it uses a newer version of the file format, or because it is damaged.", comment: "DDS: Header size wrong")]) + } + if header.pixelFormat.size != 32 { + throw NSError(domain:"ddsError", code:3, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("This DDS file is not supported.", comment: "DDS: Pixel format size wrong"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file cannot be read because it uses a different header size than normal. This may be because it uses a newer version of the file format, or because it is damaged.", comment: "DDS: Pixel format size wrong")]) + } + + if ((header.pixelFormat.flags & 4) != 0) { + // Use the FourCC + if header.pixelFormat.fourCC.0 == Character("D").asciiValue! && header.pixelFormat.fourCC.1 == Character("X").asciiValue! && header.pixelFormat.fourCC.2 == Character("T").asciiValue! { + if header.pixelFormat.fourCC.3 == Character("1").asciiValue! { + dataFormat = .dxt1 + } else if header.pixelFormat.fourCC.3 == Character("3").asciiValue! { + dataFormat = .dxt3 + } else if header.pixelFormat.fourCC.3 == Character("5").asciiValue! { + dataFormat = .dxt5 + } else { + throw NSError(domain:"ddsError", code:1, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("Graphics format is not supported.", comment:"DDS: Unknown FourCC"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file cannot be read because it uses a compressed data format that is not supported. Only DXT1, DXT3 and DXT5 formats are supported.", comment: "DDS: Unknown FourCC")]); + } + } else { + throw NSError(domain:"ddsError", code:1, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("Graphics format is not supported.", comment:"DDS: Unknown FourCC"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file cannot be read because it uses a compressed data format that is not supported. Only DXT1, DXT3 and DXT5 formats are supported.", comment: "DDS: Unknown FourCC")]); + } + } else if ((header.pixelFormat.flags & 64) != 0) { // Use RGB + + if header.pixelFormat.flags & 1 != 0 { + if header.pixelFormat.rgbBitCount == 32 { + // Mentally swap the constants! + if header.pixelFormat.aBitMask == 0xFF000000 && header.pixelFormat.rBitMask == 0x00FF0000 && header.pixelFormat.gBitMask == 0x0000FF00 && header.pixelFormat.bBitMask == 0x000000FF { + dataFormat = .bgra8 + } else if header.pixelFormat.aBitMask == 0xFF000000 && header.pixelFormat.rBitMask == 0x000000FF && header.pixelFormat.gBitMask == 0x0000FF00 && header.pixelFormat.bBitMask == 0x00FF0000 { + dataFormat = .rgba8 + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Graphics format is not supported.", comment: "DDS: Unknown 32-bit alpha format"), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Only ARGB or ABGR is supported for 32 bit uncompressed textures.", comment: "DDS: Unknown 32-bit format")]) + } + } else if header.pixelFormat.rgbBitCount == 16 { + if header.pixelFormat.aBitMask == 0x8000 && header.pixelFormat.rBitMask == 0x7C00 && header.pixelFormat.gBitMask == 0x03E0 && header.pixelFormat.bBitMask == 0x001F { + dataFormat = .argb1555 + } else if header.pixelFormat.aBitMask == 0xF000 && header.pixelFormat.rBitMask == 0x0F00 && header.pixelFormat.gBitMask == 0x00F0 && header.pixelFormat.bBitMask == 0x000F { + dataFormat = .argb4 + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Graphics format is not supported.", comment: "DDS: Unknown 16-bit alpha format"), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Only ARGB4 and ARGB1555 are supported for 16 bit uncompressed textures with alpha channel.", comment: "DDS: Unknown 16-bit alpha format")]) + } + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: String(format:NSLocalizedString("%u bits per pixel with alpha are not supported.", comment: "DDS: Unknown alpha bitcount"), header.pixelFormat.rgbBitCount), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Uncompressed formats with alpha have to be 32 or 16 bits per pixel.", comment: "DDS: Unknown alpha bitcount")]) + } + } else { + // No alpha + if header.pixelFormat.rgbBitCount == 32 { + if header.pixelFormat.rBitMask == 0x00FF0000 && header.pixelFormat.gBitMask == 0x0000FF00 && header.pixelFormat.bBitMask == 0x000000FF { + dataFormat = .bgrx8 + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Graphics format is not supported.", comment: "DDS: Unknown 32-bit non-alpha format"), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Only XRGB is supported for 32 bit uncompressed textures with alpha.", comment: "DDS: Unknown 32-bit non-alpha format")]) + } + } else if header.pixelFormat.rgbBitCount == 24 { + if header.pixelFormat.rBitMask == 0x00FF0000 && header.pixelFormat.gBitMask == 0x0000FF00 && header.pixelFormat.bBitMask == 0x000000FF { + dataFormat = .bgr8 + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Graphics format is not supported.", comment: "DDS: Unknown 24-bit non-alpha format"), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Only RGB is supported for 24 bit uncompressed textures.", comment: "DDS: Unknown 24-bit non-alpha format")]) + } + } else if header.pixelFormat.rgbBitCount == 16 { + if header.pixelFormat.rBitMask == 0xF800 && header.pixelFormat.gBitMask == 0x7E0 && header.pixelFormat.bBitMask == 0x001F { + dataFormat = .rgb565 + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Graphics format is not supported.", comment: "DDS: Unknown 16-bit non-alpha format"), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Only RGB565 is supported for 16 bit uncompressed textures.", comment: "DDS: Unknown 16-bit non-alpha format")]) + } + } else { + throw NSError(domain: "ddsError", code: 1, userInfo: [NSLocalizedDescriptionKey: String(format:NSLocalizedString("%u bits per pixel without alpha are not supported.", comment: "DDS: Unknown non-alpha bitcount"), header.pixelFormat.rgbBitCount), NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("The file uses a data layout that is not supported. Uncompressed formats without alpha have to be 32, 24 or 16 bits per pixel.", comment: "DDS: Unknown non-alpha bitcount")]) + } + } + } else { + throw NSError(domain:"ddsError", code:1, userInfo:[ + NSLocalizedDescriptionKey : NSLocalizedString("The file's graphics format is not supported.", comment:"DDS: Unknown graphics format"), + NSLocalizedRecoverySuggestionErrorKey : NSLocalizedString("Only DXT1,3,5 and uncompressed (A)RGB formats are supported.", comment: "DDS: Unknown graphics format")]); + } + + width = Int(header.width) + height = Int(header.height) + numMipmaps = Int(header.mipMapCount) + + super.init() + } + + @objc func data(mipmapLevel: Int) -> Data? { + // Stupid implementation because I have a headache + var size = 0 + var offset = 0 + var height = self.height + var width = self.width + var i = 0 + while i <= mipmapLevel && (width != 0 || height != 0) { + width = max(width, 1) + height = max(height, 1) + + offset += size + switch self.dataFormat { + case .dxt1: + size = (((width+3)/4) * ((height+3)/4)) * 8 + case .dxt3: + fallthrough + case .dxt5: + size = (((width+3)/4) * ((height+3)/4)) * 16 + case .argb1555: + fallthrough + case .argb4: + fallthrough + case .rgb565: + size = width * height * 2 + case .bgr8: + size = width * height * 3 + case .bgra8: + fallthrough + case .rgba8: + fallthrough + case .bgrx8: + size = width * height * 4 + } + + width >>= 1 + height >>= 1 + i += 1 + } + + if size == 0 { + return nil + } + let dataStart = 4 + MemoryLayout.size + offset + if dataStart + size > fileData.count { + return nil + } + + return fileData[dataStart ..< (dataStart + size)] + } +} diff --git a/GLLara/GLLTexture.m b/GLLara/GLLTexture.m index b908f0c..3bd61e2 100644 --- a/GLLara/GLLTexture.m +++ b/GLLara/GLLTexture.m @@ -13,7 +13,7 @@ #import #import -#import "GLLDDSFile.h" +#import "GLLara-Swift.h" #import "GLLNotifications.h" #import "GLLTiming.h" @@ -254,34 +254,36 @@ - (BOOL)_loadDDSTextureWithData:(NSData *)data error:(NSError *__autoreleasing*) } if (file.numMipmaps != 0) { - if (file.numMipmaps != descriptor.mipmapLevelCount) { + if (file.numMipmaps != (NSInteger) descriptor.mipmapLevelCount) { NSLog(@"Unexpectedly few mipmaps on %@", self.url.lastPathComponent); } descriptor.mipmapLevelCount = file.numMipmaps; + } else { + descriptor.mipmapLevelCount = 1; } BOOL expand24BitFormat = NO; switch (file.dataFormat) { - case GLL_DDS_DXT1: + case GLLDDSDataFormatDxt1: descriptor.pixelFormat = MTLPixelFormatBC1_RGBA; break; - case GLL_DDS_DXT3: + case GLLDDSDataFormatDxt3: descriptor.pixelFormat = MTLPixelFormatBC2_RGBA; break; - case GLL_DDS_DXT5: + case GLLDDSDataFormatDxt5: descriptor.pixelFormat = MTLPixelFormatBC3_RGBA; break; - case GLL_DDS_BGR_8: + case GLLDDSDataFormatBgr8: descriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; expand24BitFormat = YES; break; - case GLL_DDS_BGRA_8: + case GLLDDSDataFormatBgra8: descriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; break; - case GLL_DDS_RGBA_8: + case GLLDDSDataFormatRgba8: descriptor.pixelFormat = MTLPixelFormatRGBA8Unorm; break; - case GLL_DDS_ARGB_4: + case GLLDDSDataFormatArgb4: // TODO Does this need swizzling? Probably, right? descriptor.pixelFormat = MTLPixelFormatABGR4Unorm; MTLTextureSwizzleChannels channels = { @@ -292,13 +294,13 @@ - (BOOL)_loadDDSTextureWithData:(NSData *)data error:(NSError *__autoreleasing*) }; descriptor.swizzle = channels; break; - case GLL_DDS_RGB_565: + case GLLDDSDataFormatRgb565: descriptor.pixelFormat = MTLPixelFormatB5G6R5Unorm; break; - case GLL_DDS_ARGB_1555: + case GLLDDSDataFormatArgb1555: descriptor.pixelFormat = MTLPixelFormatBGR5A1Unorm; break; - case GLL_DDS_BGRX_8: + case GLLDDSDataFormatBgrx8: descriptor.pixelFormat = MTLPixelFormatBGRA8Unorm; break; default: @@ -318,7 +320,7 @@ - (BOOL)_loadDDSTextureWithData:(NSData *)data error:(NSError *__autoreleasing*) MTLRegion region = MTLRegionMake2D(0, 0, levelWidth, levelHeight); - NSData *data = [file dataForMipmapLevel:i]; + NSData *data = [file dataWithMipmapLevel:i]; if (expand24BitFormat) { // Metal does not support 24 bit texture formats, so we need to expand this data manually. // Grr