mirror of
https://github.com/OpenEmu/OpenEmu-Shaders.git
synced 2025-11-01 11:07:54 +00:00
114 lines
3.9 KiB
Swift
114 lines
3.9 KiB
Swift
//
|
|
// CompiledShaderContainer+Zip.swift
|
|
// OpenEmuShaders
|
|
//
|
|
// Created by Stuart Carnie on 19/10/2022.
|
|
// Copyright © 2022 OpenEmu. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import ZIPFoundation
|
|
|
|
public enum ZipCompiledShaderContainer {
|
|
enum Error: Swift.Error {
|
|
/// The specified path does not exist.
|
|
case pathNotExists
|
|
|
|
/// The specified path is not a valid archive.
|
|
case invalidArchive
|
|
|
|
/// The specified path is missing shader.json.
|
|
case missingCompiledShader
|
|
}
|
|
|
|
/// Encode a ``Compiled.Shader`` to a ZIP archive that may be distributed.
|
|
public static func encode(shader: Compiled.Shader, to path: URL) throws {
|
|
if FileManager.default.fileExists(atPath: path.path) {
|
|
try FileManager.default.removeItem(at: path)
|
|
}
|
|
|
|
let ar: Archive
|
|
do {
|
|
ar = try Archive(url: path, accessMode: .create)
|
|
} catch {
|
|
return
|
|
}
|
|
|
|
try shader.luts.forEach { lut in
|
|
// Duplicate names are not permitted during compilation, so there should never
|
|
// be conflicts.
|
|
// Compression is probably not necessary here, as most images are already compressed.
|
|
try ar.addEntry(with: "\(lut.name)__\(lut.url.lastPathComponent)", fileURL: lut.url, compressionMethod: .deflate)
|
|
}
|
|
|
|
let je = JSONEncoder()
|
|
je.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
let data = try je.encode(shader)
|
|
|
|
try ar.addEntry(with: "shader.json", type: .file, uncompressedSize: Int64(data.count), compressionMethod: .deflate) { position, size in
|
|
data.subdata(in: Int(position)..<Int(position) + size)
|
|
}
|
|
}
|
|
|
|
public final class Decoder: CompiledShaderContainer {
|
|
public let shader: Compiled.Shader
|
|
private let archive: Archive
|
|
|
|
public convenience init(url: URL) throws {
|
|
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
throw Error.pathNotExists
|
|
}
|
|
let archive: Archive
|
|
do {
|
|
archive = try Archive(url: url, accessMode: .read)
|
|
} catch {
|
|
throw Error.invalidArchive
|
|
}
|
|
try self.init(archive: archive)
|
|
}
|
|
|
|
public convenience init(data: Data) throws {
|
|
let archive: Archive
|
|
do {
|
|
archive = try Archive(data: data, accessMode: .read)
|
|
} catch {
|
|
throw Error.invalidArchive
|
|
}
|
|
try self.init(archive: archive)
|
|
}
|
|
|
|
private init(archive: Archive) throws {
|
|
self.archive = archive
|
|
if let entry = archive["shader.json"] {
|
|
var data = Data(capacity: Int(entry.uncompressedSize))
|
|
let crc = try archive.extract(entry, consumer: { block in
|
|
data.append(block)
|
|
})
|
|
if crc != entry.checksum {
|
|
throw Error.invalidArchive
|
|
}
|
|
|
|
let jd = JSONDecoder()
|
|
shader = try jd.decode(Compiled.Shader.self, from: data)
|
|
} else {
|
|
throw Error.missingCompiledShader
|
|
}
|
|
}
|
|
|
|
public func getLUTByName(_ name: String) throws -> Data {
|
|
let prefix = name + "__"
|
|
guard let entry = archive.first(where: { $0.path.hasPrefix(prefix) }) else {
|
|
throw CompiledShaderContainerError.invalidLUTName
|
|
}
|
|
var data = Data(capacity: Int(entry.uncompressedSize))
|
|
let crc = try archive.extract(entry, consumer: { block in
|
|
data.append(block)
|
|
})
|
|
if crc != entry.checksum {
|
|
throw Error.invalidArchive
|
|
}
|
|
return data
|
|
}
|
|
}
|
|
}
|