From 15b23c13e7731302c6e46eaf2bfbfe1ea09ccd9e Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 8 Nov 2018 17:13:39 -0800 Subject: [PATCH] Updates delta cores + replaces ZipZap with ZIPFoundation Replaces ZipZap with ZIPFoundation due to DeltaCore now using ZIPFoundation --- Cores/DeltaCore | 2 +- Cores/GBADeltaCore | 2 +- Cores/GBCDeltaCore | 2 +- Cores/SNESDeltaCore | 2 +- Delta.xcodeproj/project.pbxproj | 8 - Delta/Database/DatabaseManager.swift | 71 +++------ Delta/Database/InputStreamOutputWriter.swift | 148 ------------------- 7 files changed, 22 insertions(+), 213 deletions(-) delete mode 100644 Delta/Database/InputStreamOutputWriter.swift diff --git a/Cores/DeltaCore b/Cores/DeltaCore index 4947d10..ec24fdb 160000 --- a/Cores/DeltaCore +++ b/Cores/DeltaCore @@ -1 +1 @@ -Subproject commit 4947d10713ba31017e40df275f40a315cf0da897 +Subproject commit ec24fdbd721a8a21636916797f65c2a97f1b0ac5 diff --git a/Cores/GBADeltaCore b/Cores/GBADeltaCore index 54d064d..f958a29 160000 --- a/Cores/GBADeltaCore +++ b/Cores/GBADeltaCore @@ -1 +1 @@ -Subproject commit 54d064d34a4d856fd34a49308f42f1aefac8eaad +Subproject commit f958a29481557135ffaf3d7f4c50b43edacffb7e diff --git a/Cores/GBCDeltaCore b/Cores/GBCDeltaCore index 9c295a2..1a45f45 160000 --- a/Cores/GBCDeltaCore +++ b/Cores/GBCDeltaCore @@ -1 +1 @@ -Subproject commit 9c295a2a287821544fec4b29d2b3f1205f965275 +Subproject commit 1a45f455c2fb89a81fad1eea4abb198084a4e41a diff --git a/Cores/SNESDeltaCore b/Cores/SNESDeltaCore index a3f0547..4641bfc 160000 --- a/Cores/SNESDeltaCore +++ b/Cores/SNESDeltaCore @@ -1 +1 @@ -Subproject commit a3f0547bc57c14b548105020136a9ead56f73470 +Subproject commit 4641bfc256383817cbded045eaf214aacea6ac6d diff --git a/Delta.xcodeproj/project.pbxproj b/Delta.xcodeproj/project.pbxproj index a541504..1fda36a 100644 --- a/Delta.xcodeproj/project.pbxproj +++ b/Delta.xcodeproj/project.pbxproj @@ -85,8 +85,6 @@ BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */; }; BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */; }; BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */; }; - BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; }; - BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BF71CF871FE90006001F1613 /* AppIconShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF71CF861FE90006001F1613 /* AppIconShortcutsViewController.swift */; }; BF71CF8A1FE904B1001F1613 /* GameTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF71CF891FE904B1001F1613 /* GameTableViewCell.xib */; }; BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; }; @@ -118,7 +116,6 @@ BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; }; BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BFF6452E1F7CC5060056533E /* GameControllerInputMappingTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */; }; - BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */; }; BFFA4C091E8A24D600D87934 /* GameTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */; }; BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; }; BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E01AAC406100EE9DD1 /* Main.storyboard */; }; @@ -140,7 +137,6 @@ BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */, BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */, BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */, - BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */, BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */, BF99C6951D0A9AA600BA92BC /* SNESDeltaCore.framework in Embed Frameworks */, ); @@ -250,7 +246,6 @@ BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveStateMigrationPolicy.swift; sourceTree = ""; }; BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBCDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputStreamOutputWriter.swift; sourceTree = ""; }; BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameTableViewCell.swift; sourceTree = ""; }; BFFA71D71AAC406100EE9DD1 /* Delta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Delta.app; sourceTree = BUILT_PRODUCTS_DIR; }; BFFA71DB1AAC406100EE9DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -273,7 +268,6 @@ BF9F4FCF1AAD7B87004C9500 /* DeltaCore.framework in Frameworks */, BFEC732D1AAECC4A00650035 /* Roxas.framework in Frameworks */, BF99C6941D0A9AA600BA92BC /* SNESDeltaCore.framework in Frameworks */, - BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */, BF0418141D01E93400E85BCF /* GBADeltaCore.framework in Frameworks */, BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */, 4FE8465FD28810191C3E5212 /* Pods_Delta.framework in Frameworks */, @@ -394,7 +388,6 @@ isa = PBXGroup; children = ( BF59426D1E09BC5D0051894B /* DatabaseManager.swift */, - BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */, BF5942711E09BC690051894B /* Model */, BF95E2751E49763D0030E7AD /* OpenVGDB */, ); @@ -968,7 +961,6 @@ BF696B801D9B2B02009639E0 /* Theme.swift in Sources */, BF7AE8081C2E858400B1B5BC /* GridMenuViewController.swift in Sources */, BF6BF31A1EB82146008E83CD /* ClipboardImportOption.swift in Sources */, - BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */, BF59427F1E09BC830051894B /* GameCollection.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Delta/Database/DatabaseManager.swift b/Delta/Database/DatabaseManager.swift index 0d7fbd9..a3fa711 100644 --- a/Delta/Database/DatabaseManager.swift +++ b/Delta/Database/DatabaseManager.swift @@ -11,7 +11,7 @@ import CoreData // Workspace import DeltaCore -import ZipZap +import ZIPFoundation // Pods import FileMD5Hash @@ -346,7 +346,6 @@ extension DatabaseManager { DispatchQueue.global().async { - var semaphores = Set() var outputURLs = Set() var errors = Set() @@ -354,17 +353,20 @@ extension DatabaseManager { var archiveContainsValidGameFile = false - do + guard let archive = Archive(url: url, accessMode: .read) else { + errors.insert(.invalid(url)) + continue + } + + for entry in archive { - let archive = try ZZArchive(url: url) - - for entry in archive.entries + do { // Ensure entry is not in a subdirectory - guard !entry.fileName.contains("/") else { continue } + guard !entry.path.contains("/") else { continue } + + let fileExtension = (entry.path as NSString).pathExtension - let fileExtension = (entry.fileName as NSString).pathExtension - guard GameType(fileExtension: fileExtension) != nil else { continue } // At least one entry is a valid game file, so we set archiveContainsValidGameFile to true @@ -372,54 +374,22 @@ extension DatabaseManager // However, if this game file does turn out to be invalid when extracting, we'll return an ImportError.invalid error specific to this game file archiveContainsValidGameFile = true - // ROMs may potentially be very large, so we extract using file streams and not raw Data - let inputStream = try entry.newStream() - // Must use temporary directory, and not the directory containing zip file, since the latter might be read-only (such as when importing from Safari) - let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(entry.fileName) + let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(entry.path) if FileManager.default.fileExists(atPath: outputURL.path) { try FileManager.default.removeItem(at: outputURL) } - guard let outputStream = OutputStream(url: outputURL, append: false) else { continue } + _ = try archive.extract(entry, to: outputURL) - let semaphore = DispatchSemaphore(value: 0) - semaphores.insert(semaphore) - - let outputWriter = InputStreamOutputWriter(inputStream: inputStream, outputStream: outputStream) - outputWriter.start { (error) in - if let error = error - { - if FileManager.default.fileExists(atPath: outputURL.path) - { - do - { - try FileManager.default.removeItem(at: outputURL) - } - catch - { - print(error) - } - } - - print(error) - - errors.insert(.invalid(outputURL)) - } - else - { - outputURLs.insert(outputURL) - } - - semaphore.signal() - } + outputURLs.insert(outputURL) + } + catch + { + print(error) } - } - catch - { - print(error) } if !archiveContainsValidGameFile @@ -428,11 +398,6 @@ extension DatabaseManager } } - for semaphore in semaphores - { - semaphore.wait() - } - for url in urls { if FileManager.default.fileExists(atPath: url.path) diff --git a/Delta/Database/InputStreamOutputWriter.swift b/Delta/Database/InputStreamOutputWriter.swift deleted file mode 100644 index 5ed98d8..0000000 --- a/Delta/Database/InputStreamOutputWriter.swift +++ /dev/null @@ -1,148 +0,0 @@ -// -// InputStreamOutputWriter.swift -// Delta -// -// Created by Riley Testut on 12/25/16. -// Copyright © 2016 Riley Testut. All rights reserved. -// - -import Foundation - -private let MaximumBufferLength = 4 * 1024 // 4 KB - -class InputStreamOutputWriter: NSObject -{ - let inputStream: InputStream - let outputStream: OutputStream - - private var completion: ((Error?) -> Void)? - - private var dataBuffer = Data(capacity: MaximumBufferLength * 2) - - init(inputStream: InputStream, outputStream: OutputStream) - { - self.inputStream = inputStream - self.outputStream = outputStream - - super.init() - - self.inputStream.delegate = self - self.outputStream.delegate = self - } - - func start(with completion: @escaping ((Error?) -> Void)) - { - guard self.completion == nil else { return } - - self.completion = completion - - let writingQueue = DispatchQueue(label: "com.rileytestut.InputStreamOutputWriter.writingQueue", qos: .userInitiated) - writingQueue.async { - self.inputStream.schedule(in: .current, forMode: .defaultRunLoopMode) - self.outputStream.schedule(in: .current, forMode: .defaultRunLoopMode) - - self.outputStream.open() - self.inputStream.open() - - RunLoop.current.run() - } - } -} - -private extension InputStreamOutputWriter -{ - func writeDataBuffer() - { - while self.outputStream.hasSpaceAvailable && self.dataBuffer.count > 0 - { - self.dataBuffer.withUnsafeMutableBytes { (buffer: UnsafeMutablePointer) -> Void in - let writtenBytesCount = self.outputStream.write(buffer, maxLength: self.dataBuffer.count) - if writtenBytesCount >= 0 - { - self.dataBuffer.removeSubrange(0 ..< writtenBytesCount) - } - } - } - } - - func finishWriting() - { - self.inputStream.close() - self.outputStream.close() - - self.inputStream.remove(from: .current, forMode: .commonModes) - self.outputStream.remove(from: .current, forMode: .commonModes) - - self.completion?(self.inputStream.streamError ?? self.outputStream.streamError) - - CFRunLoopStop(CFRunLoopGetCurrent()) - } -} - -extension InputStreamOutputWriter: StreamDelegate -{ - func stream(_ aStream: Stream, handle eventCode: Stream.Event) - { - if let inputStream = aStream as? InputStream - { - self.inputStream(inputStream, handle: eventCode) - } - else if let outputStream = aStream as? OutputStream - { - self.outputStream(outputStream, handle: eventCode) - } - } - - private func inputStream(_ inputStream: InputStream, handle eventCode: Stream.Event) - { - switch eventCode - { - case Stream.Event.hasBytesAvailable: - - guard inputStream.streamError == nil else { return } - - while inputStream.hasBytesAvailable - { - let buffer = UnsafeMutablePointer.allocate(capacity: MaximumBufferLength) - - let readBytesCount = inputStream.read(buffer, maxLength: MaximumBufferLength) - - guard readBytesCount >= 0 else { break } - - self.dataBuffer.append(buffer, count: readBytesCount) - - buffer.deallocate(capacity: MaximumBufferLength) - - self.writeDataBuffer() - } - - case Stream.Event.endEncountered: - if self.dataBuffer.count == 0 - { - self.finishWriting() - } - - case Stream.Event.errorOccurred: self.finishWriting() - - default: break - } - } - - private func outputStream(_ outputStream: OutputStream, handle eventCode: Stream.Event) - { - switch eventCode - { - case Stream.Event.hasSpaceAvailable: - self.writeDataBuffer() - - if self.inputStream.streamStatus == .atEnd - { - self.finishWriting() - } - - case Stream.Event.errorOccurred: self.finishWriting() - - default: break - } - } -}